package cluster import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/go-chi/chi/v5" ) func TestProjectAdminRuntimeReturnsReadOnlyManifest(t *testing.T) { router := chi.NewRouter() module := &Module{} router.Post("/clusters/{clusterID}/nodes/{nodeID}/admin-runtime/projection", module.projectAdminRuntime) req := httptest.NewRequest(http.MethodPost, "/clusters/cluster-1/nodes/node-1/admin-runtime/projection", bytes.NewReader([]byte(`{ "schema_version":"rap.web_ingress.control_api_projection_request.v1", "method":"GET", "path":"/platform-admin/ui-manifest", "scope":"platform", "service_class":"platform_admin" }`))) rec := httptest.NewRecorder() router.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status = %d body=%s", rec.Code, rec.Body.String()) } var response struct { SchemaVersion string `json:"schema_version"` Status string `json:"status"` Reason string `json:"reason"` StatusCode int `json:"status_code"` Headers map[string]string `json:"headers"` Body json.RawMessage `json:"body"` } if err := json.Unmarshal(rec.Body.Bytes(), &response); err != nil { t.Fatalf("decode response: %v", err) } if response.SchemaVersion != "rap.web_ingress.control_api_projection_response.v1" || response.Status != "ready" || response.Reason != "ui_manifest_ready" || response.StatusCode != http.StatusOK || response.Headers["Content-Type"] != "application/json" { t.Fatalf("response = %+v", response) } var body struct { SchemaVersion string `json:"schema_version"` ClusterID string `json:"cluster_id"` NodeID string `json:"node_id"` Manifest map[string]any `json:"manifest"` } if err := json.Unmarshal(response.Body, &body); err != nil { t.Fatalf("decode body: %v", err) } if body.ClusterID != "cluster-1" || body.NodeID != "node-1" || body.Manifest["projection_binding"] != "control_api_read_only" || body.Manifest["mutation_enabled"] != false { t.Fatalf("body = %+v", body) } } func TestProjectAdminRuntimeRejectsMutations(t *testing.T) { router := chi.NewRouter() module := &Module{} router.Post("/clusters/{clusterID}/nodes/{nodeID}/admin-runtime/projection", module.projectAdminRuntime) req := httptest.NewRequest(http.MethodPost, "/clusters/cluster-1/nodes/node-1/admin-runtime/projection", bytes.NewReader([]byte(`{ "schema_version":"rap.web_ingress.control_api_projection_request.v1", "method":"POST", "path":"/platform-admin/nodes", "scope":"platform", "service_class":"platform_admin" }`))) rec := httptest.NewRecorder() router.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status = %d body=%s", rec.Code, rec.Body.String()) } var response struct { Status string `json:"status"` Reason string `json:"reason"` StatusCode int `json:"status_code"` } if err := json.Unmarshal(rec.Body.Bytes(), &response); err != nil { t.Fatalf("decode response: %v", err) } if response.Status != "blocked" || response.Reason != "control_api_mutation_rejected" || response.StatusCode != http.StatusForbidden { t.Fatalf("response = %+v", response) } } func TestProjectAdminRuntimeReturnsHealthProjection(t *testing.T) { router := chi.NewRouter() module := &Module{} router.Post("/clusters/{clusterID}/nodes/{nodeID}/admin-runtime/projection", module.projectAdminRuntime) req := httptest.NewRequest(http.MethodPost, "/clusters/cluster-1/nodes/node-1/admin-runtime/projection", bytes.NewReader([]byte(`{ "schema_version":"rap.web_ingress.control_api_projection_request.v1", "method":"GET", "path":"/readyz", "scope":"platform", "service_class":"platform_admin" }`))) rec := httptest.NewRecorder() router.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status = %d body=%s", rec.Code, rec.Body.String()) } var response struct { Status string `json:"status"` Reason string `json:"reason"` StatusCode int `json:"status_code"` Body json.RawMessage `json:"body"` } if err := json.Unmarshal(rec.Body.Bytes(), &response); err != nil { t.Fatalf("decode response: %v", err) } if response.Status != "ready" || response.Reason != "admin_runtime_projection_ready" || response.StatusCode != http.StatusOK { t.Fatalf("response = %+v", response) } var body struct { Projection string `json:"projection"` AuditRequired bool `json:"audit_required"` } if err := json.Unmarshal(response.Body, &body); err != nil { t.Fatalf("decode body: %v", err) } if body.Projection != "read_only" || !body.AuditRequired { t.Fatalf("body = %+v", body) } } func TestProjectAdminRuntimeBlocksUnknownReadProjection(t *testing.T) { router := chi.NewRouter() module := &Module{} router.Post("/clusters/{clusterID}/nodes/{nodeID}/admin-runtime/projection", module.projectAdminRuntime) req := httptest.NewRequest(http.MethodPost, "/clusters/cluster-1/nodes/node-1/admin-runtime/projection", bytes.NewReader([]byte(`{ "schema_version":"rap.web_ingress.control_api_projection_request.v1", "method":"GET", "path":"/platform-admin/nodes", "scope":"platform", "service_class":"platform_admin" }`))) rec := httptest.NewRecorder() router.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status = %d body=%s", rec.Code, rec.Body.String()) } var response struct { Status string `json:"status"` Reason string `json:"reason"` StatusCode int `json:"status_code"` } if err := json.Unmarshal(rec.Body.Bytes(), &response); err != nil { t.Fatalf("decode response: %v", err) } if response.Status != "blocked" || response.Reason != "control_api_projection_not_implemented" || response.StatusCode != http.StatusNotImplemented { t.Fatalf("response = %+v", response) } } func TestProjectAdminRuntimeRejectsScopeClassMismatch(t *testing.T) { router := chi.NewRouter() module := &Module{} router.Post("/clusters/{clusterID}/nodes/{nodeID}/admin-runtime/projection", module.projectAdminRuntime) req := httptest.NewRequest(http.MethodPost, "/clusters/cluster-1/nodes/node-1/admin-runtime/projection", bytes.NewReader([]byte(`{ "schema_version":"rap.web_ingress.control_api_projection_request.v1", "method":"GET", "path":"/platform-admin/ui-manifest", "scope":"organization", "service_class":"platform_admin" }`))) rec := httptest.NewRecorder() router.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status = %d body=%s", rec.Code, rec.Body.String()) } var response struct { Status string `json:"status"` Reason string `json:"reason"` StatusCode int `json:"status_code"` } if err := json.Unmarshal(rec.Body.Bytes(), &response); err != nil { t.Fatalf("decode response: %v", err) } if response.Status != "blocked" || response.Reason != "control_api_projection_scope_rejected" || response.StatusCode != http.StatusForbidden { t.Fatalf("response = %+v", response) } } func TestProjectAdminRuntimeRejectsInvalidSchema(t *testing.T) { router := chi.NewRouter() module := &Module{} router.Post("/clusters/{clusterID}/nodes/{nodeID}/admin-runtime/projection", module.projectAdminRuntime) req := httptest.NewRequest(http.MethodPost, "/clusters/cluster-1/nodes/node-1/admin-runtime/projection", bytes.NewReader([]byte(`{ "schema_version":"wrong.schema", "method":"GET", "path":"/readyz", "scope":"platform", "service_class":"platform_admin" }`))) rec := httptest.NewRecorder() router.ServeHTTP(rec, req) if rec.Code != http.StatusBadRequest { t.Fatalf("status = %d body=%s", rec.Code, rec.Body.String()) } }