package cabana import ( "encoding/json" "os" "path/filepath" "runtime" "strings" "testing" ) // TestPhase09ContractInventory fails when the committed framework admin // OpenAPI document (admin/openapi/admin.json, D-15) drops an admin API route // or a protected route's 401 response. Paths are prefix-relative (D-03). func TestPhase09ContractInventory(t *testing.T) { _, file, _, ok := runtime.Caller(0) if !ok { t.Fatal("caller") } specPath := filepath.Clean(filepath.Join(filepath.Dir(file), "..", "..", "admin", "openapi", "admin.json")) raw, err := os.ReadFile(specPath) if err != nil { t.Fatalf("read %s: %v", specPath, err) } var spec struct { Paths map[string]map[string]struct { Responses map[string]json.RawMessage `json:"responses"` Security []map[string]json.RawMessage `json:"security"` } `json:"paths"` Components struct { SecuritySchemes map[string]json.RawMessage `json:"securitySchemes"` } `json:"components"` } if err := json.Unmarshal(raw, &spec); err != nil { t.Fatal(err) } if _, ok := spec.Components.SecuritySchemes["BackendBearer"]; !ok { t.Fatal("openapi is missing the BackendBearer scheme") } public := map[string]bool{ "POST /auth/login": true, "POST /auth/refresh": true, } seen := map[string]bool{} apiRoutes := 0 for _, route := range phase09Routes { if route.spa { continue } apiRoutes++ method, path, ok := splitRoute(route.key) if !ok { t.Fatalf("bad route key %s", route.key) } method = strings.ToLower(method) ops, ok := spec.Paths[path] if !ok { t.Fatalf("openapi missing %s", path) } op, ok := ops[method] if !ok { t.Fatalf("openapi missing %s %s", method, path) } key := route.key if seen[key] { t.Fatalf("duplicate contract route %s", key) } seen[key] = true _, ok200 := op.Responses["200"] _, ok201 := op.Responses["201"] if !ok200 && !ok201 { t.Fatalf("%s has no 200 or 201 response", key) } if route.public { // Login and refresh answer 401; the public string bundle does not. if _, ok := op.Responses["401"]; public[key] && !ok { t.Fatalf("%s has no 401 response", key) } continue } if len(op.Security) == 0 { t.Fatalf("%s has no BackendBearer security requirement", key) } if _, ok := op.Responses["401"]; !ok { t.Fatalf("%s has no 401 response", key) } } if len(seen) != apiRoutes { t.Fatalf("contract routes=%d want %d", len(seen), apiRoutes) } if len(spec.Paths) != len(pathsOf(phase09Routes)) { t.Fatalf("openapi lists %d paths, the mounted API has %d", len(spec.Paths), len(pathsOf(phase09Routes))) } } func splitRoute(key string) (method, path string, ok bool) { for i := 0; i < len(key); i++ { if key[i] == ' ' { return key[:i], key[i+1:], key[i+1:] != "" } } return "", "", false } func pathsOf(routes []adminRoute) map[string]bool { out := map[string]bool{} for _, route := range routes { if route.spa { continue } if _, path, ok := splitRoute(route.key); ok { out[path] = true } } return out }