package api import ( "net/http" "net/http/httptest" "strings" "sync" "testing" ) func TestAdmissionGateDefaultsToAllowAndBlocksOnlyNewWork(t *testing.T) { gate := NewAdmissionGate(false) for _, operation := range []string{"login", "queue", "proposal", "allocation", "result", "events"} { if !gate.Allow(operation) { t.Fatalf("normal mode rejected %q", operation) } } gate.SetDegraded(true) for _, operation := range []string{"login", "queue", "proposal", "allocation"} { if gate.Allow(operation) { t.Fatalf("degraded mode allowed %q", operation) } } for _, operation := range []string{"result", "events", "read", ""} { if !gate.Allow(operation) { t.Fatalf("degraded mode rejected live-safe operation %q", operation) } } } func TestAdmissionOperationClassifiesOnlyMutations(t *testing.T) { tests := []struct { path, method, want string }{ {"/v1/session/steam", http.MethodPost, "login"}, {"/api/v1/session/steam/", http.MethodPost, "login"}, {"/v1/queue", http.MethodPost, "queue"}, {"/api/v1/queue/tickets/abc/heartbeat", http.MethodPost, "queue"}, {"/v1/proposals/abc/accept", http.MethodPost, "proposal"}, {"/api/v1/proposals/abc", http.MethodDelete, "proposal"}, {"/v1/queue", http.MethodGet, ""}, {"/v1/queue-not-a-route", http.MethodPost, ""}, {"/v1/servers/abc/result", http.MethodPost, ""}, {"/v1/events", http.MethodPost, ""}, } for _, test := range tests { if got := admissionOperation(test.path, test.method); got != test.want { t.Errorf("admissionOperation(%q, %q) = %q, want %q", test.path, test.method, got, test.want) } } } func TestAdmissionGateConcurrentToggle(t *testing.T) { gate := NewAdmissionGate(false) var wg sync.WaitGroup for i := 0; i < 8; i++ { wg.Add(1) go func() { defer wg.Done() for j := 0; j < 1000; j++ { gate.SetDegraded(j%2 == 0) _ = gate.Allow("queue") } }() } wg.Wait() } func TestHandlerReturnsDegradedOnlyForNewMatchmakingMutations(t *testing.T) { service := &Service{Admission: NewAdmissionGate(true)} tests := []struct { path, want string }{ {"/v1/queue", "service_degraded"}, {"/api/v1/proposals/proposal-1/accept", "service_degraded"}, {"/v1/servers/server-1/result", "server_unavailable"}, {"/v1/events", ""}, } for _, test := range tests { req := httptest.NewRequest(http.MethodPost, test.path, strings.NewReader(`{}`)) rec := httptest.NewRecorder() service.Handler().ServeHTTP(rec, req) if test.want != "" && !strings.Contains(rec.Body.String(), test.want) { t.Errorf("%s body = %q, want %q", test.path, rec.Body.String(), test.want) } if test.want == "service_degraded" && rec.Code != http.StatusServiceUnavailable { t.Errorf("%s status = %d, want 503", test.path, rec.Code) } } }