package backendclient_test import ( "context" "encoding/json" "net/http" "net/http/httptest" "testing" "time" "scrabble/gateway/internal/backendclient" "scrabble/gateway/internal/ratelimit" ) // TestReportRateLimited verifies the rejection report reaches the backend's // internal endpoint with the agreed JSON shape and no user identity. func TestReportRateLimited(t *testing.T) { var got struct { WindowSeconds int `json:"window_seconds"` Entries []ratelimit.Rejection `json:"entries"` } srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost || r.URL.Path != "/api/v1/internal/ratelimit/report" { t.Errorf("call = %s %s, want POST /api/v1/internal/ratelimit/report", r.Method, r.URL.Path) } if uid := r.Header.Get("X-User-ID"); uid != "" { t.Errorf("X-User-ID = %q, want empty", uid) } if err := json.NewDecoder(r.Body).Decode(&got); err != nil { t.Errorf("decode report: %v", err) } })) defer srv.Close() c, err := backendclient.New(srv.URL, "localhost:9090", 2*time.Second) if err != nil { t.Fatalf("backendclient: %v", err) } defer func() { _ = c.Close() }() entries := []ratelimit.Rejection{{Class: "user", Key: "u-1", Rejected: 5}} if err := c.ReportRateLimited(context.Background(), 30, entries); err != nil { t.Fatalf("ReportRateLimited: %v", err) } if got.WindowSeconds != 30 || len(got.Entries) != 1 || got.Entries[0] != entries[0] { t.Fatalf("backend received %+v, want window 30 + %+v", got, entries[0]) } }