//go:build test // +build test package admin import ( "context" "encoding/json" "net/http" "testing" "crussell/db" "crussell/handlers/services" "crussell/mw" ) func TestAdminServices_Create(t *testing.T) { cleanup := setupTestDB(t) defer cleanup() // Create admin user in DB first _, err := db.DB.Exec(context.Background(), ` INSERT INTO users (n_first_name, n_last_name, email, phone, date_of_birth, password_hash, account_role, account_type) VALUES ('Admin', 'User', 'admin@test.com', '+447123456789', '1990-01-01', 'hash', 'admin', 'email') `) if err != nil { t.Fatalf("failed to create admin user: %v", err) } handler := http.HandlerFunc(services.CreateServiceHandler) createReq := services.CreateServiceRequest{ Name: "Test Manicure", Description: stringPtr("A test manicure service"), Price: 35.00, DurationMinutes: 45, MinimumAgeRequired: 16, } w := makeAdminRequest(handler, "POST", "/api/admin/services", createReq) if w.Code != http.StatusCreated { t.Errorf("expected status 201, got %d. body: %s", w.Code, w.Body.String()) } var response services.Service if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil { t.Fatalf("failed to unmarshal response: %v", err) } if response.Name != "Test Manicure" { t.Errorf("expected name 'Test Manicure', got %s", response.Name) } if response.Price != 35.00 { t.Errorf("expected price 35.00, got %f", response.Price) } if !response.IsActive { t.Error("expected new service to be active by default") } } func TestAdminServices_List(t *testing.T) { cleanup := setupTestDB(t) defer cleanup() // Insert test services _, err := db.DB.Exec(context.Background(), ` INSERT INTO services (name, description, price, duration_minutes, is_active, minimum_age_required) VALUES ('Manicure', 'Basic manicure', 25.00, 30, true, 0), ('Pedicure', 'Basic pedicure', 30.00, 45, false, 0), ('Gel Polish', 'Gel polish service', 40.00, 60, true, 16) `) if err != nil { t.Fatalf("failed to create services: %v", err) } handler := http.HandlerFunc(services.AllServicesHandler) w := makeAdminRequest(handler, "GET", "/api/admin/services", nil) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d. body: %s", w.Code, w.Body.String()) } var response []services.Service if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil { t.Fatalf("failed to unmarshal response: %v", err) } if len(response) != 3 { t.Errorf("expected 3 services, got %d", len(response)) } // Verify all services including inactive are returned found := map[string]bool{} for _, s := range response { found[s.Name] = true } if !found["Manicure"] { t.Error("expected Manicure in response") } if !found["Pedicure"] { t.Error("expected Pedicure in response (including inactive)") } if !found["Gel Polish"] { t.Error("expected Gel Polish in response") } } func TestAdminServices_Toggle(t *testing.T) { cleanup := setupTestDB(t) defer cleanup() // Create a service var serviceID string err := db.DB.QueryRow(context.Background(), ` INSERT INTO services (name, description, price, duration_minutes, is_active, minimum_age_required) VALUES ('Test Service', 'A test service', 50.00, 60, true, 16) RETURNING id `).Scan(&serviceID) if err != nil { t.Fatalf("failed to create service: %v", err) } handler := http.HandlerFunc(services.ToggleService) w := makeAdminRequest(handler, "PUT", "/api/admin/services/"+serviceID+"/toggle", nil) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d. body: %s", w.Code, w.Body.String()) } // Verify service is now inactive var isActive bool err = db.DB.QueryRow(context.Background(), "SELECT is_active FROM services WHERE id = $1", serviceID).Scan(&isActive) if err != nil { t.Fatalf("failed to check service: %v", err) } if isActive { t.Error("expected service to be inactive after toggle") } // Toggle again w = makeAdminRequest(handler, "PUT", "/api/admin/services/"+serviceID+"/toggle", nil) if w.Code != http.StatusOK { t.Errorf("expected status 200 on second toggle, got %d", w.Code) } // Verify service is active again err = db.DB.QueryRow(context.Background(), "SELECT is_active FROM services WHERE id = $1", serviceID).Scan(&isActive) if err != nil { t.Fatalf("failed to check service: %v", err) } if !isActive { t.Error("expected service to be active after second toggle") } } func TestAdminServices_Delete(t *testing.T) { cleanup := setupTestDB(t) defer cleanup() // Create a service var serviceID string err := db.DB.QueryRow(context.Background(), ` INSERT INTO services (name, description, price, duration_minutes, is_active, minimum_age_required) VALUES ('Test Service', 'A test service', 50.00, 60, true, 16) RETURNING id `).Scan(&serviceID) if err != nil { t.Fatalf("failed to create service: %v", err) } handler := http.HandlerFunc(services.DeleteServiceHandler) w := makeAdminRequest(handler, "DELETE", "/api/admin/services/"+serviceID, nil) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d. body: %s", w.Code, w.Body.String()) } // Verify service is soft deleted (is_active = false) var isActive bool err = db.DB.QueryRow(context.Background(), "SELECT is_active FROM services WHERE id = $1", serviceID).Scan(&isActive) if err != nil { t.Fatalf("failed to check service: %v", err) } if isActive { t.Error("expected service to be soft deleted (is_active = false)") } } func TestAdminServices_NonAdmin(t *testing.T) { cleanup := setupTestDB(t) defer cleanup() // Create regular user in DB _, err := db.DB.Exec(context.Background(), ` INSERT INTO users (n_first_name, n_last_name, email, phone, date_of_birth, password_hash, account_role, account_type) VALUES ('Regular', 'User', 'user@test.com', '+447123456789', '1990-01-01', 'hash', 'verified_email', 'email') `) if err != nil { t.Fatalf("failed to create user: %v", err) } // Test CREATE - should get 403 when using middleware createHandler := mw.RequireAdmin(http.HandlerFunc(services.CreateServiceHandler)) createReq := services.CreateServiceRequest{ Name: "Test Service", Description: stringPtr("Test"), Price: 50.00, DurationMinutes: 60, MinimumAgeRequired: 16, } w := makeUserRequest(createHandler, "POST", "/api/admin/services", createReq) if w.Code != http.StatusForbidden { t.Errorf("CREATE: expected status 403, got %d", w.Code) } // Test LIST - should get 403 when using middleware listHandler := mw.RequireAdmin(http.HandlerFunc(services.AllServicesHandler)) w = makeUserRequest(listHandler, "GET", "/api/admin/services", nil) if w.Code != http.StatusForbidden { t.Errorf("LIST: expected status 403, got %d", w.Code) } // Test TOGGLE - should get 403 when using middleware var serviceID string err = db.DB.QueryRow(context.Background(), ` INSERT INTO services (name, description, price, duration_minutes, is_active, minimum_age_required) VALUES ('Test Service', 'A test service', 50.00, 60, true, 16) RETURNING id `).Scan(&serviceID) if err != nil { t.Fatalf("failed to create service: %v", err) } toggleHandler := mw.RequireAdmin(http.HandlerFunc(services.ToggleService)) w = makeUserRequest(toggleHandler, "PUT", "/api/admin/services/"+serviceID+"/toggle", nil) if w.Code != http.StatusForbidden { t.Errorf("TOGGLE: expected status 403, got %d", w.Code) } // Test DELETE - should get 403 when using middleware deleteHandler := mw.RequireAdmin(http.HandlerFunc(services.DeleteServiceHandler)) w = makeUserRequest(deleteHandler, "DELETE", "/api/admin/services/"+serviceID, nil) if w.Code != http.StatusForbidden { t.Errorf("DELETE: expected status 403, got %d", w.Code) } } func stringPtr(s string) *string { return &s }