package routes import ( "authorization/db" "bytes" "database/sql" "net/http" "net/http/httptest" "os" "testing" "github.com/DATA-DOG/go-sqlmock" "github.com/gorilla/mux" ) func TestMain(m *testing.M) { os.Setenv("GO_ENV", "development") code := m.Run() os.Unsetenv("GO_ENV") os.Exit(code) } func setupMockDB(t *testing.T) (*sql.DB, sqlmock.Sqlmock, func()) { mockDB, mock, err := sqlmock.New() if err != nil { t.Fatalf("Failed to create mock database: %v", err) } originalDB := db.DB db.DB = mockDB cleanup := func() { db.DB = originalDB mockDB.Close() } return mockDB, mock, cleanup } func TestSetupRoutes_HealthEndpoint(t *testing.T) { mockDB, _, cleanup := setupMockDB(t) defer cleanup() router := mux.NewRouter() SetupRoutes(router, mockDB) req := httptest.NewRequest("GET", "/health", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("Expected status %d for /health, got %d", http.StatusOK, w.Code) } } func TestSetupRoutes_ReadyEndpoint(t *testing.T) { mockDB, _, cleanup := setupMockDB(t) defer cleanup() router := mux.NewRouter() SetupRoutes(router, mockDB) req := httptest.NewRequest("GET", "/ready", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) // The ready endpoint returns 503 when db.DB global is not initialized // This is expected behavior in unit tests where we don't initialize global state if w.Code != http.StatusServiceUnavailable && w.Code != http.StatusOK { t.Errorf("Expected status 503 or 200 for /ready, got %d", w.Code) } } func TestSetupRoutes_SwaggerEndpoint(t *testing.T) { mockDB, _, cleanup := setupMockDB(t) defer cleanup() router := mux.NewRouter() SetupRoutes(router, mockDB) req := httptest.NewRequest("GET", "/swagger/", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) // Swagger endpoint should be registered (might return various status codes depending on setup) // We just check that it doesn't return 404 if w.Code == http.StatusNotFound { t.Error("Swagger endpoint should be registered") } } func TestSetupRoutes_AuthCheckEndpoint(t *testing.T) { // Test that the auth check endpoint is properly registered // We can test the routing without full DB initialization router := mux.NewRouter() SetupRoutes(router, nil) // Pass nil DB, handlers should handle it gracefully req := httptest.NewRequest("POST", "/v1/auth/check", bytes.NewBufferString(`{"userId":"user123","resource":"doc","action":"read"}`)) w := httptest.NewRecorder() router.ServeHTTP(w, req) // Endpoint is registered (status won't be 404) if w.Code == http.StatusNotFound { t.Error("Auth check endpoint should be registered") } // Will likely return 401 (no JWT) or 500 (no DB) but that's OK - route exists if w.Code != http.StatusUnauthorized && w.Code != http.StatusInternalServerError && w.Code != http.StatusForbidden { t.Logf("Auth check returned status %d (expected 401, 403, or 500 without proper setup)", w.Code) } } func TestSetupRoutes_MethodRestrictions(t *testing.T) { mockDB, _, cleanup := setupMockDB(t) defer cleanup() router := mux.NewRouter() SetupRoutes(router, mockDB) tests := []struct { name string method string path string expectNotFound bool }{ { name: "GET on health is allowed", method: "GET", path: "/health", expectNotFound: false, }, { name: "POST on health is not allowed", method: "POST", path: "/health", expectNotFound: true, }, { name: "GET on ready is allowed", method: "GET", path: "/ready", expectNotFound: false, }, { name: "POST on auth/check is allowed", method: "POST", path: "/v1/auth/check", expectNotFound: false, }, { name: "GET on auth/check is not allowed", method: "GET", path: "/v1/auth/check", expectNotFound: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req := httptest.NewRequest(tt.method, tt.path, nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if tt.expectNotFound && w.Code != http.StatusNotFound && w.Code != http.StatusMethodNotAllowed { t.Errorf("Expected 404 or 405, got %d", w.Code) } if !tt.expectNotFound && (w.Code == http.StatusNotFound || w.Code == http.StatusMethodNotAllowed) { t.Errorf("Expected route to exist, got %d", w.Code) } }) } } func TestSetupRoutes_RouterConfiguration(t *testing.T) { mockDB, _, cleanup := setupMockDB(t) defer cleanup() router := mux.NewRouter() // Before setup, routes should not exist req := httptest.NewRequest("GET", "/health", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusNotFound { t.Log("Route might already exist before setup (not necessarily an error)") } // After setup, health route should exist SetupRoutes(router, mockDB) req = httptest.NewRequest("GET", "/health", nil) w = httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code == http.StatusNotFound { t.Error("Health route should exist after SetupRoutes") } } func TestSetupRoutes_WithNilDB(t *testing.T) { router := mux.NewRouter() // Should not panic with nil DB defer func() { if r := recover(); r != nil { t.Errorf("SetupRoutes should not panic with nil DB: %v", r) } }() SetupRoutes(router, nil) } func TestSetupRoutes_PathPrefix(t *testing.T) { mockDB, _, cleanup := setupMockDB(t) defer cleanup() router := mux.NewRouter() SetupRoutes(router, mockDB) // Test that auth routes use /v1/auth prefix req := httptest.NewRequest("POST", "/v1/auth/check", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) // Should not be 404 (route exists, even if auth fails) if w.Code == http.StatusNotFound { t.Error("Auth route with /v1/auth prefix should exist") } } func TestSetupRoutes_MultipleInitializations(t *testing.T) { mockDB, _, cleanup := setupMockDB(t) defer cleanup() router := mux.NewRouter() // Setup routes twice SetupRoutes(router, mockDB) SetupRoutes(router, mockDB) // Should still work req := httptest.NewRequest("GET", "/health", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("Expected status %d after multiple setups, got %d", http.StatusOK, w.Code) } } func TestSetupRoutes_AllEndpoints(t *testing.T) { // Test that all endpoints are properly registered router := mux.NewRouter() SetupRoutes(router, nil) endpoints := []struct { method string path string }{ {"GET", "/health"}, {"GET", "/ready"}, {"POST", "/v1/auth/check"}, {"GET", "/swagger/"}, } for _, ep := range endpoints { t.Run(ep.method+" "+ep.path, func(t *testing.T) { req := httptest.NewRequest(ep.method, ep.path, nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) // Endpoint should be registered (not 404 or 405) if w.Code == http.StatusNotFound { t.Errorf("Endpoint %s %s should be registered", ep.method, ep.path) } }) } } func TestSetupRoutes_DBParameter(t *testing.T) { // Test that SetupRoutes accepts *sql.DB parameter router := mux.NewRouter() var mockDB *sql.DB = nil // Should compile and not panic defer func() { if r := recover(); r != nil { t.Errorf("SetupRoutes should accept *sql.DB parameter: %v", r) } }() SetupRoutes(router, mockDB) }