Total rebrand across all Go API source files: - Go module path: casera-api -> honeydue-api - All imports updated (130+ files) - Docker: containers, images, networks renamed - Email templates: support email, noreply, icon URL - Domains: casera.app/mycrib.treytartt.com -> honeyDue.treytartt.com - Bundle IDs: com.tt.casera -> com.tt.honeyDue - IAP product IDs updated - Landing page, admin panel, config defaults - Seeds, CI workflows, Makefile, docs - Database table names preserved (no migration needed) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
335 lines
13 KiB
Go
335 lines
13 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/treytartt/honeydue-api/internal/config"
|
|
"github.com/treytartt/honeydue-api/internal/repositories"
|
|
"github.com/treytartt/honeydue-api/internal/services"
|
|
"github.com/treytartt/honeydue-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)
|
|
})
|
|
}
|
|
}
|