package webingress import ( "context" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "time" ) func TestHTTPSHandlerBlocksUnknownServiceClass(t *testing.T) { runtime := Runtime{ Config: RuntimeConfig{ ServiceType: "public-ingress", Scope: "organization", ServiceClasses: []string{"public-ingress", "public-ingress"}, }, Now: fixedNow, } req := httptest.NewRequest(http.MethodGet, "https://org.example.test/admin/root", nil) rec := httptest.NewRecorder() runtime.HTTPSHandler().ServeHTTP(rec, req) if rec.Code != http.StatusForbidden { t.Fatalf("status = %d body=%s", rec.Code, rec.Body.String()) } var payload Response if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil { t.Fatalf("decode response: %v", err) } if payload.Reason != "service_class_not_allowed" || payload.ServiceClass != "admin-ingress" || payload.Scope != "organization" { t.Fatalf("payload = %+v", payload) } } func TestHTTPSHandlerRequiresFabricServiceChannelBinding(t *testing.T) { runtime := Runtime{ Config: RuntimeConfig{ ServiceType: "admin-ingress", Scope: "platform", ServiceClasses: []string{"admin-ingress", "admin-ingress"}, }, Now: fixedNow, } req := httptest.NewRequest(http.MethodPost, "https://admin.example.test/admin/root", nil) rec := httptest.NewRecorder() runtime.HTTPSHandler().ServeHTTP(rec, req) if rec.Code != http.StatusNotImplemented { t.Fatalf("status = %d body=%s", rec.Code, rec.Body.String()) } var payload Response if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil { t.Fatalf("decode response: %v", err) } if payload.Reason != "fabric_service_channel_binding_not_implemented" || payload.ServiceClass != "admin-ingress" || payload.ObservedAt != "2026-05-17T00:00:00Z" { t.Fatalf("payload = %+v", payload) } } func TestHTTPSHandlerForwardsAllowedRequestToBinder(t *testing.T) { binder := &recordingBinder{ response: FabricResponse{ StatusCode: http.StatusAccepted, Headers: http.Header{"X-RAP-Result": []string{"accepted"}}, Body: []byte(`{"ok":true}`), }, } runtime := Runtime{ Config: RuntimeConfig{ ServiceType: "admin-ingress", Scope: "platform", ServiceClasses: []string{"admin-ingress", "admin-ingress"}, }, Binder: binder, Now: fixedNow, } req := httptest.NewRequest(http.MethodPost, "https://admin.example.test/admin/root?tab=nodes", strings.NewReader(`{"hello":"world"}`)) req.Header.Set("X-RAP-Service-Class", "admin-ingress") req.Header.Set("Authorization", "Bearer secret") req.Header.Set("X-Trace-ID", "trace-1") rec := httptest.NewRecorder() runtime.HTTPSHandler().ServeHTTP(rec, req) if rec.Code != http.StatusAccepted { t.Fatalf("status = %d body=%s", rec.Code, rec.Body.String()) } if rec.Header().Get("X-RAP-Result") != "accepted" || rec.Body.String() != `{"ok":true}` { t.Fatalf("unexpected response headers=%v body=%s", rec.Header(), rec.Body.String()) } if binder.request.ServiceClass != "admin-ingress" || binder.request.Scope != "platform" || binder.request.Path != "/admin/root" || binder.request.Query != "tab=nodes" || string(binder.request.Body) != `{"hello":"world"}` { t.Fatalf("request = %+v", binder.request) } if binder.request.Headers.Get("Authorization") != "" || binder.request.Headers.Get("X-Trace-ID") != "trace-1" { t.Fatalf("headers = %#v", binder.request.Headers) } } func TestHTTPSHandlerDerivesFabricScopeFromServiceClass(t *testing.T) { binder := &recordingBinder{response: FabricResponse{StatusCode: http.StatusOK}} runtime := Runtime{ Config: RuntimeConfig{ ServiceType: "admin-ingress", Scope: "platform", ServiceClasses: []string{"admin-ingress", "admin-ingress"}, }, Binder: binder, Now: fixedNow, } req := httptest.NewRequest(http.MethodGet, "https://admin.example.test/clusters/ui-manifest", nil) rec := httptest.NewRecorder() runtime.HTTPSHandler().ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status = %d body=%s", rec.Code, rec.Body.String()) } if binder.request.ServiceClass != "admin-ingress" || binder.request.Scope != "cluster" { t.Fatalf("request = %+v", binder.request) } } func TestHTTPSHandlerReportsBinderFailure(t *testing.T) { runtime := Runtime{ Config: RuntimeConfig{ServiceType: "admin-ingress", Scope: "platform", ServiceClasses: []string{"admin-ingress"}}, Binder: failingBinder{}, Now: fixedNow, } req := httptest.NewRequest(http.MethodPost, "https://admin.example.test/admin/root", nil) rec := httptest.NewRecorder() runtime.HTTPSHandler().ServeHTTP(rec, req) if rec.Code != http.StatusBadGateway { t.Fatalf("status = %d body=%s", rec.Code, rec.Body.String()) } var payload Response if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil { t.Fatalf("decode response: %v", err) } if payload.Reason != "fabric_service_channel_forward_failed" { t.Fatalf("payload = %+v", payload) } } func TestHTTPSHandlerHealth(t *testing.T) { runtime := Runtime{Config: RuntimeConfig{ServiceType: "admin-ingress", Scope: "platform"}, Now: fixedNow} req := httptest.NewRequest(http.MethodGet, "https://admin.example.test/healthz", nil) rec := httptest.NewRecorder() runtime.HTTPSHandler().ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status = %d body=%s", rec.Code, rec.Body.String()) } } func fixedNow() time.Time { return time.Date(2026, 5, 17, 0, 0, 0, 0, time.UTC) } type recordingBinder struct { request FabricRequest response FabricResponse } func (b *recordingBinder) Forward(_ context.Context, request FabricRequest) (FabricResponse, error) { b.request = request return b.response, nil } type failingBinder struct{} func (failingBinder) Forward(context.Context, FabricRequest) (FabricResponse, error) { return FabricResponse{}, errTestBinderFailure{} } type errTestBinderFailure struct{} func (errTestBinderFailure) Error() string { return "binder failed" }