package router import ( "net/http" "net/http/httptest" "testing" ) // TestPublicRoutes verifies that public routes are accessible without authentication func TestPublicRoutes(t *testing.T) { // Note: This test requires a running router. // For isolation, we test the expected behavior without needing full DB setup. t.Run("GET /health returns 200", func(t *testing.T) { // Simple health endpoint test req := httptest.NewRequest("GET", "/health", nil) w := httptest.NewRecorder() // Simulate the health handler directly handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "text/plain") w.Write([]byte("OK")) }) handler.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("Expected 200, got %d", w.Code) } if w.Body.String() != "OK" { t.Errorf("Expected 'OK', got '%s'", w.Body.String()) } }) t.Run("Root route returns API info", func(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) w := httptest.NewRecorder() // Simulate root handler handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { http.NotFound(w, r) return } w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"message":"🐴 GoHorseJobs API is running!"}`)) }) handler.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("Expected 200, got %d", w.Code) } }) } // TestPublicRoutePatterns documents the expected public routes in the system func TestPublicRoutePatterns(t *testing.T) { // This is a documentation test to verify route patterns publicRoutes := []struct { method string path string }{ // Health & Root {"GET", "/health"}, {"GET", "/"}, // Auth {"POST", "/api/v1/auth/login"}, {"POST", "/api/v1/auth/logout"}, {"POST", "/api/v1/auth/register"}, {"POST", "/api/v1/auth/register/candidate"}, {"POST", "/api/v1/auth/register/company"}, // Jobs (public read) {"GET", "/api/v1/jobs"}, {"GET", "/api/v1/jobs/{id}"}, // Companies (public read single) {"POST", "/api/v1/companies"}, {"GET", "/api/v1/companies/{id}"}, // Locations (all public) {"GET", "/api/v1/locations/countries"}, {"GET", "/api/v1/locations/countries/{id}/states"}, {"GET", "/api/v1/locations/states/{id}/cities"}, {"GET", "/api/v1/locations/search"}, // Applications (public create) {"POST", "/api/v1/applications"}, {"GET", "/api/v1/applications"}, {"GET", "/api/v1/applications/{id}"}, {"PUT", "/api/v1/applications/{id}/status"}, {"DELETE", "/api/v1/applications/{id}"}, // Payments {"POST", "/api/v1/payments/webhook"}, {"GET", "/api/v1/payments/status/{id}"}, // Swagger {"GET", "/docs/"}, } t.Logf("Total public routes: %d", len(publicRoutes)) for _, route := range publicRoutes { t.Logf(" %s %s", route.method, route.path) } // Verify we have the expected count if len(publicRoutes) < 20 { t.Error("Expected at least 20 public routes") } } // TestProtectedRoutePatterns documents routes that require authentication func TestProtectedRoutePatterns(t *testing.T) { protectedRoutes := []struct { method string path string roles []string // empty = any authenticated, non-empty = specific roles }{ // Users {"POST", "/api/v1/users", nil}, {"GET", "/api/v1/users", []string{"ADMIN", "SUPERADMIN"}}, {"PATCH", "/api/v1/users/{id}", nil}, {"DELETE", "/api/v1/users/{id}", nil}, {"GET", "/api/v1/users/me", nil}, {"PATCH", "/api/v1/users/me/profile", nil}, // Jobs (write) {"POST", "/api/v1/jobs", nil}, {"PUT", "/api/v1/jobs/{id}", nil}, {"DELETE", "/api/v1/jobs/{id}", nil}, {"GET", "/api/v1/jobs/moderation", []string{"ADMIN", "SUPERADMIN"}}, {"PATCH", "/api/v1/jobs/{id}/status", []string{"ADMIN", "SUPERADMIN"}}, {"POST", "/api/v1/jobs/{id}/duplicate", []string{"ADMIN", "SUPERADMIN"}}, // Admin {"GET", "/api/v1/users/roles", []string{"ADMIN", "SUPERADMIN"}}, {"GET", "/api/v1/audit/logins", []string{"ADMIN", "SUPERADMIN"}}, // Companies (admin) {"PATCH", "/api/v1/companies/{id}/status", []string{"ADMIN", "SUPERADMIN"}}, {"PATCH", "/api/v1/companies/{id}", []string{"ADMIN", "SUPERADMIN"}}, {"DELETE", "/api/v1/companies/{id}", []string{"ADMIN", "SUPERADMIN"}}, // Tags {"GET", "/api/v1/tags", nil}, {"POST", "/api/v1/tags", []string{"ADMIN", "SUPERADMIN"}}, {"PATCH", "/api/v1/tags/{id}", []string{"ADMIN", "SUPERADMIN"}}, // Candidates {"GET", "/api/v1/candidates", []string{"ADMIN", "SUPERADMIN"}}, // Notifications {"GET", "/api/v1/notifications", nil}, {"POST", "/api/v1/tokens", nil}, // Support Tickets {"GET", "/api/v1/support/tickets", nil}, {"POST", "/api/v1/support/tickets", nil}, {"GET", "/api/v1/support/tickets/all", nil}, {"GET", "/api/v1/support/tickets/{id}", nil}, {"POST", "/api/v1/support/tickets/{id}/messages", nil}, {"PATCH", "/api/v1/support/tickets/{id}", nil}, {"PATCH", "/api/v1/support/tickets/{id}/close", nil}, {"DELETE", "/api/v1/support/tickets/{id}", nil}, // System {"POST", "/api/v1/system/settings/{key}", []string{"ADMIN", "SUPERADMIN"}}, {"GET", "/api/v1/storage/upload-url", nil}, {"POST", "/api/v1/system/cloudflare/purge", []string{"ADMIN", "SUPERADMIN"}}, // Email Admin {"GET", "/api/v1/admin/email-templates", []string{"ADMIN", "SUPERADMIN"}}, {"POST", "/api/v1/admin/email-templates", []string{"ADMIN", "SUPERADMIN"}}, {"GET", "/api/v1/admin/email-templates/{slug}", []string{"ADMIN", "SUPERADMIN"}}, {"PUT", "/api/v1/admin/email-templates/{slug}", []string{"ADMIN", "SUPERADMIN"}}, {"DELETE", "/api/v1/admin/email-templates/{slug}", []string{"ADMIN", "SUPERADMIN"}}, {"GET", "/api/v1/admin/email-settings", []string{"ADMIN", "SUPERADMIN"}}, {"PUT", "/api/v1/admin/email-settings", []string{"ADMIN", "SUPERADMIN"}}, // Chat {"GET", "/api/v1/conversations", nil}, {"GET", "/api/v1/conversations/{id}/messages", nil}, {"POST", "/api/v1/conversations/{id}/messages", nil}, // Payments {"POST", "/api/v1/payments/create-checkout", nil}, } t.Logf("Total protected routes: %d", len(protectedRoutes)) adminOnlyCount := 0 for _, route := range protectedRoutes { if len(route.roles) > 0 { adminOnlyCount++ } } t.Logf("Admin-only routes: %d", adminOnlyCount) if len(protectedRoutes) < 30 { t.Error("Expected at least 30 protected routes") } }