192 lines
5.9 KiB
Go
192 lines
5.9 KiB
Go
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" }
|