package api import ( "net/http" "net/http/httptest" "testing" "github.com/go-chi/chi/v5" ) func buildMountedRouter() http.Handler { r := chi.NewRouter() Mount(r) return r } func TestRoutes_7_1_1_AnalyzeEndpointExists(t *testing.T) { resetRateLimiter() r := buildMountedRouter() req := httptest.NewRequest(http.MethodPost, "/api/analyze", nil) rr := httptest.NewRecorder() r.ServeHTTP(rr, req) if rr.Code == http.StatusNotFound { t.Fatalf("expected /api/analyze endpoint to exist") } } func TestRoutes_7_1_2_POSTMethodWorks(t *testing.T) { resetRateLimiter() r := buildMountedRouter() req := httptest.NewRequest(http.MethodPost, "/api/analyze", nil) rr := httptest.NewRecorder() r.ServeHTTP(rr, req) if rr.Code != http.StatusBadRequest && rr.Code != http.StatusOK && rr.Code != http.StatusInternalServerError { t.Fatalf("expected handled POST status, got %d", rr.Code) } } func TestRoutes_7_1_3_GETMethodReturnsMethodNotAllowed(t *testing.T) { resetRateLimiter() r := buildMountedRouter() req := httptest.NewRequest(http.MethodGet, "/api/analyze", nil) rr := httptest.NewRecorder() r.ServeHTTP(rr, req) if rr.Code != http.StatusMethodNotAllowed { t.Fatalf("expected 405 for GET /api/analyze, got %d", rr.Code) } } func TestRoutes_7_1_4_UnknownRoutesReturn404(t *testing.T) { resetRateLimiter() r := buildMountedRouter() req := httptest.NewRequest(http.MethodPost, "/api/unknown", nil) rr := httptest.NewRecorder() r.ServeHTTP(rr, req) if rr.Code != http.StatusNotFound { t.Fatalf("expected 404 for unknown route, got %d", rr.Code) } } func TestRoutes_7_2_1_CORSAppliedBeforeRateLimit(t *testing.T) { resetRateLimiter() withFixedNow(t, timeNow()) r := buildMountedRouter() for i := 0; i < 10; i++ { req := httptest.NewRequest(http.MethodPost, "/api/analyze", nil) req.Header.Set("Origin", "http://localhost:5173") req.RemoteAddr = "127.0.0.1:12345" rr := httptest.NewRecorder() r.ServeHTTP(rr, req) } blockedReq := httptest.NewRequest(http.MethodPost, "/api/analyze", nil) blockedReq.Header.Set("Origin", "http://localhost:5173") blockedReq.RemoteAddr = "127.0.0.1:12345" blockedResp := httptest.NewRecorder() r.ServeHTTP(blockedResp, blockedReq) if blockedResp.Code != http.StatusTooManyRequests { t.Fatalf("expected 429 after limit, got %d", blockedResp.Code) } if got := blockedResp.Header().Get("Access-Control-Allow-Origin"); got != "http://localhost:5173" { t.Fatalf("expected CORS header on rate-limited response, got %q", got) } } func TestRoutes_7_2_2_RateLimitingAppliedToAPIRoutes(t *testing.T) { resetRateLimiter() r := buildMountedRouter() for i := 0; i < 10; i++ { req := httptest.NewRequest(http.MethodPost, "/api/analyze", nil) req.RemoteAddr = "127.0.0.1:12345" rr := httptest.NewRecorder() r.ServeHTTP(rr, req) } req := httptest.NewRequest(http.MethodPost, "/api/analyze", nil) req.RemoteAddr = "127.0.0.1:12345" rr := httptest.NewRecorder() r.ServeHTTP(rr, req) if rr.Code != http.StatusTooManyRequests { t.Fatalf("expected 429 on 11th /api/analyze request, got %d", rr.Code) } }