All checks were successful
CI/CD / detect-changes (pull_request) Successful in 4s
CI/CD / test-runners (pull_request) Has been skipped
CI/CD / e2e-smoke (pull_request) Has been skipped
CI/CD / test-frontend (pull_request) Successful in 5s
CI/CD / test-go-pg (pull_request) Successful in 2m50s
CI/CD / test-sqlite (pull_request) Successful in 3m1s
CI/CD / build-and-deploy (pull_request) Successful in 1m27s
Hardens multi-surface routing: unified root/catch-all dispatcher, aggregateAccess() early auth short-circuit for all-authenticated packages, SDK history.replaceState() for back-button resilience. 13 new tests (5 aggregateAccess unit + 8 handler integration). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
227 lines
6.4 KiB
Go
227 lines
6.4 KiB
Go
package pages
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
// ── matchSurface tests ──────────────────────────────────────
|
|
|
|
func TestMatchSurface_StaticRoot(t *testing.T) {
|
|
surfaces := []any{
|
|
map[string]any{"path": "/", "title": "Dashboard"},
|
|
map[string]any{"path": "/submit", "title": "Submit"},
|
|
}
|
|
s, params := matchSurface(surfaces, "/")
|
|
if s == nil {
|
|
t.Fatal("expected match for /")
|
|
}
|
|
if s["title"] != "Dashboard" {
|
|
t.Errorf("expected Dashboard, got %v", s["title"])
|
|
}
|
|
if len(params) != 0 {
|
|
t.Errorf("expected no params, got %v", params)
|
|
}
|
|
}
|
|
|
|
func TestMatchSurface_StaticPath(t *testing.T) {
|
|
surfaces := []any{
|
|
map[string]any{"path": "/", "title": "Dashboard"},
|
|
map[string]any{"path": "/submit", "title": "Submit"},
|
|
map[string]any{"path": "/monitor", "title": "Monitor"},
|
|
}
|
|
s, _ := matchSurface(surfaces, "/submit")
|
|
if s == nil {
|
|
t.Fatal("expected match for /submit")
|
|
}
|
|
if s["title"] != "Submit" {
|
|
t.Errorf("expected Submit, got %v", s["title"])
|
|
}
|
|
}
|
|
|
|
func TestMatchSurface_ParamPath(t *testing.T) {
|
|
surfaces := []any{
|
|
map[string]any{"path": "/", "title": "Dashboard"},
|
|
map[string]any{"path": "/:id", "title": "Detail"},
|
|
}
|
|
s, params := matchSurface(surfaces, "/abc123")
|
|
if s == nil {
|
|
t.Fatal("expected match for /:id")
|
|
}
|
|
if s["title"] != "Detail" {
|
|
t.Errorf("expected Detail, got %v", s["title"])
|
|
}
|
|
if params["id"] != "abc123" {
|
|
t.Errorf("expected id=abc123, got %v", params["id"])
|
|
}
|
|
}
|
|
|
|
func TestMatchSurface_MultiSegmentParam(t *testing.T) {
|
|
surfaces := []any{
|
|
map[string]any{"path": "/monitor", "title": "Monitor"},
|
|
map[string]any{"path": "/monitor/:id", "title": "Instance Detail"},
|
|
}
|
|
s, params := matchSurface(surfaces, "/monitor/inst-42")
|
|
if s == nil {
|
|
t.Fatal("expected match for /monitor/:id")
|
|
}
|
|
if s["title"] != "Instance Detail" {
|
|
t.Errorf("expected Instance Detail, got %v", s["title"])
|
|
}
|
|
if params["id"] != "inst-42" {
|
|
t.Errorf("expected id=inst-42, got %v", params["id"])
|
|
}
|
|
}
|
|
|
|
func TestMatchSurface_NoMatch(t *testing.T) {
|
|
surfaces := []any{
|
|
map[string]any{"path": "/", "title": "Dashboard"},
|
|
map[string]any{"path": "/submit", "title": "Submit"},
|
|
}
|
|
s, _ := matchSurface(surfaces, "/nonexistent/deep")
|
|
if s != nil {
|
|
t.Errorf("expected no match, got %v", s)
|
|
}
|
|
}
|
|
|
|
func TestMatchSurface_StaticPreferredOverParam(t *testing.T) {
|
|
surfaces := []any{
|
|
map[string]any{"path": "/:id", "title": "Detail"},
|
|
map[string]any{"path": "/new", "title": "New"},
|
|
}
|
|
s, params := matchSurface(surfaces, "/new")
|
|
if s == nil {
|
|
t.Fatal("expected match for /new")
|
|
}
|
|
if s["title"] != "New" {
|
|
t.Errorf("expected static match 'New', got %v", s["title"])
|
|
}
|
|
if len(params) != 0 {
|
|
t.Errorf("expected no params for static match, got %v", params)
|
|
}
|
|
}
|
|
|
|
func TestMatchSurface_EmptySurfaces(t *testing.T) {
|
|
s, _ := matchSurface([]any{}, "/")
|
|
if s != nil {
|
|
t.Errorf("expected no match for empty surfaces, got %v", s)
|
|
}
|
|
}
|
|
|
|
// ── splitPath tests ─────────────────────────────────────────
|
|
|
|
func TestSplitPath(t *testing.T) {
|
|
tests := []struct {
|
|
input string
|
|
want int
|
|
}{
|
|
{"/", 0},
|
|
{"/submit", 1},
|
|
{"/monitor/:id", 2},
|
|
{"/:id/edit", 2},
|
|
}
|
|
for _, tt := range tests {
|
|
parts := splitPath(tt.input)
|
|
if len(parts) != tt.want {
|
|
t.Errorf("splitPath(%q) = %d parts, want %d", tt.input, len(parts), tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// ── findNavSurface tests ────────────────────────────────────
|
|
|
|
func TestFindNavSurface_ExplicitNav(t *testing.T) {
|
|
surfaces := []any{
|
|
map[string]any{"path": "/", "title": "Dashboard"},
|
|
map[string]any{"path": "/monitor", "title": "Monitor", "nav": true},
|
|
}
|
|
s := findNavSurface(surfaces)
|
|
if s == nil {
|
|
t.Fatal("expected nav surface")
|
|
}
|
|
if s["title"] != "Monitor" {
|
|
t.Errorf("expected Monitor, got %v", s["title"])
|
|
}
|
|
}
|
|
|
|
func TestFindNavSurface_FallbackRoot(t *testing.T) {
|
|
surfaces := []any{
|
|
map[string]any{"path": "/submit", "title": "Submit"},
|
|
map[string]any{"path": "/", "title": "Dashboard"},
|
|
}
|
|
s := findNavSurface(surfaces)
|
|
if s == nil {
|
|
t.Fatal("expected nav surface")
|
|
}
|
|
if s["title"] != "Dashboard" {
|
|
t.Errorf("expected Dashboard, got %v", s["title"])
|
|
}
|
|
}
|
|
|
|
func TestFindNavSurface_NoMatch(t *testing.T) {
|
|
surfaces := []any{
|
|
map[string]any{"path": "/submit", "title": "Submit"},
|
|
map[string]any{"path": "/admin", "title": "Admin"},
|
|
}
|
|
s := findNavSurface(surfaces)
|
|
if s != nil {
|
|
t.Errorf("expected nil, got %v", s)
|
|
}
|
|
}
|
|
|
|
// ── aggregateAccess tests ──────────────────────────────────
|
|
|
|
func TestAggregateAccess_AllPublic(t *testing.T) {
|
|
surfaces := []any{
|
|
map[string]any{"path": "/", "access": "public"},
|
|
map[string]any{"path": "/submit", "access": "public"},
|
|
}
|
|
if got := aggregateAccess(surfaces); got != "public" {
|
|
t.Errorf("expected public, got %s", got)
|
|
}
|
|
}
|
|
|
|
func TestAggregateAccess_AllAuthenticated(t *testing.T) {
|
|
surfaces := []any{
|
|
map[string]any{"path": "/", "access": "authenticated"},
|
|
map[string]any{"path": "/admin", "access": "admin"},
|
|
}
|
|
if got := aggregateAccess(surfaces); got != "authenticated" {
|
|
t.Errorf("expected authenticated, got %s", got)
|
|
}
|
|
}
|
|
|
|
func TestAggregateAccess_Mixed(t *testing.T) {
|
|
surfaces := []any{
|
|
map[string]any{"path": "/submit", "access": "public"},
|
|
map[string]any{"path": "/dashboard", "access": "authenticated"},
|
|
}
|
|
if got := aggregateAccess(surfaces); got != "mixed" {
|
|
t.Errorf("expected mixed, got %s", got)
|
|
}
|
|
}
|
|
|
|
func TestAggregateAccess_DefaultAccess(t *testing.T) {
|
|
// No access field → defaults to "authenticated"
|
|
surfaces := []any{
|
|
map[string]any{"path": "/"},
|
|
}
|
|
if got := aggregateAccess(surfaces); got != "authenticated" {
|
|
t.Errorf("expected authenticated (default), got %s", got)
|
|
}
|
|
}
|
|
|
|
func TestAggregateAccess_SinglePublic(t *testing.T) {
|
|
surfaces := []any{
|
|
map[string]any{"path": "/", "access": "public"},
|
|
}
|
|
if got := aggregateAccess(surfaces); got != "public" {
|
|
t.Errorf("expected public, got %s", got)
|
|
}
|
|
}
|
|
|
|
// ── evaluateAccess tests ────────────────────────────────────
|
|
// Note: evaluateAccess depends on gin.Context which is hard to unit test
|
|
// without a full Gin setup. These are covered via handler integration tests.
|
|
// The matchSurface + splitPath + findNavSurface functions above are the
|
|
// pure-logic components that benefit most from unit testing.
|