package handlers import ( "net/http" "testing" "github.com/treytartt/casera-api/internal/config" "github.com/treytartt/casera-api/internal/repositories" "github.com/treytartt/casera-api/internal/services" "github.com/treytartt/casera-api/internal/testutil" ) // TestTaskHandler_NoAuth_Returns401 verifies that task handler endpoints return // 401 Unauthorized when no auth user is set in the context (e.g., auth middleware // misconfigured or bypassed). This is a regression test for P1-1 (SEC-19). func TestTaskHandler_NoAuth_Returns401(t *testing.T) { db := testutil.SetupTestDB(t) taskRepo := repositories.NewTaskRepository(db) residenceRepo := repositories.NewResidenceRepository(db) taskService := services.NewTaskService(taskRepo, residenceRepo) handler := NewTaskHandler(taskService, nil) e := testutil.SetupTestRouter() // Register routes WITHOUT auth middleware e.GET("/api/tasks/", handler.ListTasks) e.GET("/api/tasks/:id/", handler.GetTask) e.POST("/api/tasks/", handler.CreateTask) e.PUT("/api/tasks/:id/", handler.UpdateTask) e.DELETE("/api/tasks/:id/", handler.DeleteTask) e.POST("/api/tasks/:id/cancel/", handler.CancelTask) e.POST("/api/tasks/:id/mark-in-progress/", handler.MarkInProgress) e.GET("/api/task-completions/", handler.ListCompletions) e.POST("/api/task-completions/", handler.CreateCompletion) tests := []struct { name string method string path string }{ {"ListTasks", "GET", "/api/tasks/"}, {"GetTask", "GET", "/api/tasks/1/"}, {"CreateTask", "POST", "/api/tasks/"}, {"UpdateTask", "PUT", "/api/tasks/1/"}, {"DeleteTask", "DELETE", "/api/tasks/1/"}, {"CancelTask", "POST", "/api/tasks/1/cancel/"}, {"MarkInProgress", "POST", "/api/tasks/1/mark-in-progress/"}, {"ListCompletions", "GET", "/api/task-completions/"}, {"CreateCompletion", "POST", "/api/task-completions/"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { w := testutil.MakeRequest(e, tt.method, tt.path, nil, "") testutil.AssertStatusCode(t, w, http.StatusUnauthorized) }) } } // TestResidenceHandler_NoAuth_Returns401 verifies that residence handler endpoints // return 401 Unauthorized when no auth user is set in the context. func TestResidenceHandler_NoAuth_Returns401(t *testing.T) { db := testutil.SetupTestDB(t) residenceRepo := repositories.NewResidenceRepository(db) userRepo := repositories.NewUserRepository(db) cfg := &config.Config{} residenceService := services.NewResidenceService(residenceRepo, userRepo, cfg) handler := NewResidenceHandler(residenceService, nil, nil, true) e := testutil.SetupTestRouter() // Register routes WITHOUT auth middleware e.GET("/api/residences/", handler.ListResidences) e.GET("/api/residences/my/", handler.GetMyResidences) e.GET("/api/residences/summary/", handler.GetSummary) e.GET("/api/residences/:id/", handler.GetResidence) e.POST("/api/residences/", handler.CreateResidence) e.PUT("/api/residences/:id/", handler.UpdateResidence) e.DELETE("/api/residences/:id/", handler.DeleteResidence) e.POST("/api/residences/:id/generate-share-code/", handler.GenerateShareCode) e.POST("/api/residences/join-with-code/", handler.JoinWithCode) e.GET("/api/residences/:id/users/", handler.GetResidenceUsers) tests := []struct { name string method string path string }{ {"ListResidences", "GET", "/api/residences/"}, {"GetMyResidences", "GET", "/api/residences/my/"}, {"GetSummary", "GET", "/api/residences/summary/"}, {"GetResidence", "GET", "/api/residences/1/"}, {"CreateResidence", "POST", "/api/residences/"}, {"UpdateResidence", "PUT", "/api/residences/1/"}, {"DeleteResidence", "DELETE", "/api/residences/1/"}, {"GenerateShareCode", "POST", "/api/residences/1/generate-share-code/"}, {"JoinWithCode", "POST", "/api/residences/join-with-code/"}, {"GetResidenceUsers", "GET", "/api/residences/1/users/"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { w := testutil.MakeRequest(e, tt.method, tt.path, nil, "") testutil.AssertStatusCode(t, w, http.StatusUnauthorized) }) } } // TestNotificationHandler_NoAuth_Returns401 verifies that notification handler // endpoints return 401 Unauthorized when no auth user is set in the context. func TestNotificationHandler_NoAuth_Returns401(t *testing.T) { db := testutil.SetupTestDB(t) notificationRepo := repositories.NewNotificationRepository(db) notificationService := services.NewNotificationService(notificationRepo, nil) handler := NewNotificationHandler(notificationService) e := testutil.SetupTestRouter() // Register routes WITHOUT auth middleware e.GET("/api/notifications/", handler.ListNotifications) e.GET("/api/notifications/unread-count/", handler.GetUnreadCount) e.POST("/api/notifications/:id/read/", handler.MarkAsRead) e.POST("/api/notifications/mark-all-read/", handler.MarkAllAsRead) e.GET("/api/notifications/preferences/", handler.GetPreferences) e.PUT("/api/notifications/preferences/", handler.UpdatePreferences) e.POST("/api/notifications/devices/", handler.RegisterDevice) e.GET("/api/notifications/devices/", handler.ListDevices) tests := []struct { name string method string path string }{ {"ListNotifications", "GET", "/api/notifications/"}, {"GetUnreadCount", "GET", "/api/notifications/unread-count/"}, {"MarkAsRead", "POST", "/api/notifications/1/read/"}, {"MarkAllAsRead", "POST", "/api/notifications/mark-all-read/"}, {"GetPreferences", "GET", "/api/notifications/preferences/"}, {"UpdatePreferences", "PUT", "/api/notifications/preferences/"}, {"RegisterDevice", "POST", "/api/notifications/devices/"}, {"ListDevices", "GET", "/api/notifications/devices/"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { w := testutil.MakeRequest(e, tt.method, tt.path, nil, "") testutil.AssertStatusCode(t, w, http.StatusUnauthorized) }) } } // TestDocumentHandler_NoAuth_Returns401 verifies that document handler endpoints // return 401 Unauthorized when no auth user is set in the context. func TestDocumentHandler_NoAuth_Returns401(t *testing.T) { db := testutil.SetupTestDB(t) documentRepo := repositories.NewDocumentRepository(db) residenceRepo := repositories.NewResidenceRepository(db) documentService := services.NewDocumentService(documentRepo, residenceRepo) handler := NewDocumentHandler(documentService, nil) e := testutil.SetupTestRouter() // Register routes WITHOUT auth middleware e.GET("/api/documents/", handler.ListDocuments) e.GET("/api/documents/:id/", handler.GetDocument) e.GET("/api/documents/warranties/", handler.ListWarranties) e.POST("/api/documents/", handler.CreateDocument) e.PUT("/api/documents/:id/", handler.UpdateDocument) e.DELETE("/api/documents/:id/", handler.DeleteDocument) e.POST("/api/documents/:id/activate/", handler.ActivateDocument) e.POST("/api/documents/:id/deactivate/", handler.DeactivateDocument) tests := []struct { name string method string path string }{ {"ListDocuments", "GET", "/api/documents/"}, {"GetDocument", "GET", "/api/documents/1/"}, {"ListWarranties", "GET", "/api/documents/warranties/"}, {"CreateDocument", "POST", "/api/documents/"}, {"UpdateDocument", "PUT", "/api/documents/1/"}, {"DeleteDocument", "DELETE", "/api/documents/1/"}, {"ActivateDocument", "POST", "/api/documents/1/activate/"}, {"DeactivateDocument", "POST", "/api/documents/1/deactivate/"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { w := testutil.MakeRequest(e, tt.method, tt.path, nil, "") testutil.AssertStatusCode(t, w, http.StatusUnauthorized) }) } } // TestContractorHandler_NoAuth_Returns401 verifies that contractor handler endpoints // return 401 Unauthorized when no auth user is set in the context. func TestContractorHandler_NoAuth_Returns401(t *testing.T) { db := testutil.SetupTestDB(t) contractorRepo := repositories.NewContractorRepository(db) residenceRepo := repositories.NewResidenceRepository(db) contractorService := services.NewContractorService(contractorRepo, residenceRepo) handler := NewContractorHandler(contractorService) e := testutil.SetupTestRouter() // Register routes WITHOUT auth middleware e.GET("/api/contractors/", handler.ListContractors) e.GET("/api/contractors/:id/", handler.GetContractor) e.POST("/api/contractors/", handler.CreateContractor) e.PUT("/api/contractors/:id/", handler.UpdateContractor) e.DELETE("/api/contractors/:id/", handler.DeleteContractor) e.POST("/api/contractors/:id/toggle-favorite/", handler.ToggleFavorite) e.GET("/api/contractors/:id/tasks/", handler.GetContractorTasks) tests := []struct { name string method string path string }{ {"ListContractors", "GET", "/api/contractors/"}, {"GetContractor", "GET", "/api/contractors/1/"}, {"CreateContractor", "POST", "/api/contractors/"}, {"UpdateContractor", "PUT", "/api/contractors/1/"}, {"DeleteContractor", "DELETE", "/api/contractors/1/"}, {"ToggleFavorite", "POST", "/api/contractors/1/toggle-favorite/"}, {"GetContractorTasks", "GET", "/api/contractors/1/tasks/"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { w := testutil.MakeRequest(e, tt.method, tt.path, nil, "") testutil.AssertStatusCode(t, w, http.StatusUnauthorized) }) } } // TestSubscriptionHandler_NoAuth_Returns401 verifies that subscription handler // endpoints return 401 Unauthorized when no auth user is set in the context. func TestSubscriptionHandler_NoAuth_Returns401(t *testing.T) { db := testutil.SetupTestDB(t) subscriptionRepo := repositories.NewSubscriptionRepository(db) residenceRepo := repositories.NewResidenceRepository(db) taskRepo := repositories.NewTaskRepository(db) contractorRepo := repositories.NewContractorRepository(db) documentRepo := repositories.NewDocumentRepository(db) subscriptionService := services.NewSubscriptionService(subscriptionRepo, residenceRepo, taskRepo, contractorRepo, documentRepo) handler := NewSubscriptionHandler(subscriptionService, nil) e := testutil.SetupTestRouter() // Register routes WITHOUT auth middleware e.GET("/api/subscription/", handler.GetSubscription) e.GET("/api/subscription/status/", handler.GetSubscriptionStatus) e.GET("/api/subscription/promotions/", handler.GetPromotions) e.POST("/api/subscription/purchase/", handler.ProcessPurchase) e.POST("/api/subscription/cancel/", handler.CancelSubscription) e.POST("/api/subscription/restore/", handler.RestoreSubscription) tests := []struct { name string method string path string }{ {"GetSubscription", "GET", "/api/subscription/"}, {"GetSubscriptionStatus", "GET", "/api/subscription/status/"}, {"GetPromotions", "GET", "/api/subscription/promotions/"}, {"ProcessPurchase", "POST", "/api/subscription/purchase/"}, {"CancelSubscription", "POST", "/api/subscription/cancel/"}, {"RestoreSubscription", "POST", "/api/subscription/restore/"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { w := testutil.MakeRequest(e, tt.method, tt.path, nil, "") testutil.AssertStatusCode(t, w, http.StatusUnauthorized) }) } } // TestMediaHandler_NoAuth_Returns401 verifies that media handler endpoints return // 401 Unauthorized when no auth user is set in the context. func TestMediaHandler_NoAuth_Returns401(t *testing.T) { handler := NewMediaHandler(nil, nil, nil, nil) e := testutil.SetupTestRouter() // Register routes WITHOUT auth middleware e.GET("/api/media/document/:id", handler.ServeDocument) e.GET("/api/media/document-image/:id", handler.ServeDocumentImage) e.GET("/api/media/completion-image/:id", handler.ServeCompletionImage) tests := []struct { name string method string path string }{ {"ServeDocument", "GET", "/api/media/document/1"}, {"ServeDocumentImage", "GET", "/api/media/document-image/1"}, {"ServeCompletionImage", "GET", "/api/media/completion-image/1"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { w := testutil.MakeRequest(e, tt.method, tt.path, nil, "") testutil.AssertStatusCode(t, w, http.StatusUnauthorized) }) } } // TestUserHandler_NoAuth_Returns401 verifies that user handler endpoints return // 401 Unauthorized when no auth user is set in the context. func TestUserHandler_NoAuth_Returns401(t *testing.T) { db := testutil.SetupTestDB(t) userRepo := repositories.NewUserRepository(db) userService := services.NewUserService(userRepo) handler := NewUserHandler(userService) e := testutil.SetupTestRouter() // Register routes WITHOUT auth middleware e.GET("/api/users/", handler.ListUsers) e.GET("/api/users/:id/", handler.GetUser) e.GET("/api/users/profiles/", handler.ListProfiles) tests := []struct { name string method string path string }{ {"ListUsers", "GET", "/api/users/"}, {"GetUser", "GET", "/api/users/1/"}, {"ListProfiles", "GET", "/api/users/profiles/"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { w := testutil.MakeRequest(e, tt.method, tt.path, nil, "") testutil.AssertStatusCode(t, w, http.StatusUnauthorized) }) } }