//go:build test // +build test package admin import ( "context" "encoding/json" "net/http" "testing" "crussell/db" "crussell/handlers/user" "crussell/mw" ) func TestAdminUsers_List(t *testing.T) { cleanup := setupTestDB(t) defer cleanup() // Create test users _, err := db.DB.Exec(context.Background(), ` INSERT INTO users (n_first_name, n_last_name, email, password_hash, account_role, account_type) VALUES ('Alice', 'Smith', 'alice@test.com', 'hash1', 'admin', 'standard'), ('Bob', 'Jones', 'bob@test.com', 'hash2', 'verified_email', 'standard'), ('Charlie', 'Brown', 'charlie@test.com', 'hash3', 'verified_email', 'vip') `) if err != nil { t.Fatalf("failed to create users: %v", err) } handler := http.HandlerFunc(user.ListAdminUsersHandler) w := makeAdminRequest(handler, "GET", "/api/admin/users", nil) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d. body: %s", w.Code, w.Body.String()) } var response user.UserListResponse if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil { t.Fatalf("failed to unmarshal response: %v", err) } if response.Total != 3 { t.Errorf("expected 3 users, got %d", response.Total) } if len(response.Users) != 3 { t.Errorf("expected 3 users in list, got %d", len(response.Users)) } } func TestAdminUsers_Get(t *testing.T) { cleanup := setupTestDB(t) defer cleanup() // Create test user var userID string err := db.DB.QueryRow(context.Background(), ` INSERT INTO users (n_first_name, n_last_name, email, password_hash, account_role, account_type) VALUES ('Test', 'User', 'testuser@test.com', 'hash', 'verified_email', 'vip') RETURNING id `).Scan(&userID) if err != nil { t.Fatalf("failed to create user: %v", err) } handler := http.HandlerFunc(user.GetAdminUserHandler) w := makeAdminRequest(handler, "GET", "/api/admin/users/"+userID, nil) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d. body: %s", w.Code, w.Body.String()) } var response user.AdminUserDetail if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil { t.Fatalf("failed to unmarshal response: %v", err) } if response.ID != userID { t.Errorf("expected user ID %s, got %s", userID, response.ID) } if response.AccountType != "vip" { t.Errorf("expected account type 'vip', got %s", response.AccountType) } } func TestAdminUsers_Get_NotFound(t *testing.T) { cleanup := setupTestDB(t) defer cleanup() handler := http.HandlerFunc(user.GetAdminUserHandler) w := makeAdminRequest(handler, "GET", "/api/admin/users/nonexistent-id", nil) if w.Code != http.StatusNotFound { t.Errorf("expected status 404, got %d", w.Code) } } func TestAdminUsers_PatchTests_Eligible(t *testing.T) { cleanup := setupTestDB(t) defer cleanup() // Create test user var userID string err := db.DB.QueryRow(context.Background(), ` INSERT INTO users (n_first_name, n_last_name, email, password_hash, account_role, account_type) VALUES ('Test', 'User', 'testuser@test.com', 'hash', 'verified_email', 'standard') RETURNING id `).Scan(&userID) if err != nil { t.Fatalf("failed to create user: %v", err) } // Create services - some with patch test, some without _, err = db.DB.Exec(context.Background(), ` INSERT INTO services (name, description, price, duration_minutes, is_active, patch_test_duration_hours, minimum_age_required) VALUES ('Basic Manicure', 'Basic manicure', 25.00, 30, true, 0, 0), ('Gel Polish Full Set', 'Gel polish service', 45.00, 60, true, 48, 16), ('Luxury Gel Manicure', 'Luxury gel', 55.00, 75, true, 48, 16), ('Inactive Service', 'Inactive', 30.00, 30, false, 48, 16) `) if err != nil { t.Fatalf("failed to create services: %v", err) } handler := http.HandlerFunc(user.GetEligiblePatchTestServicesHandler) w := makeAdminRequest(handler, "GET", "/api/admin/users/"+userID+"/patch-tests/eligible", nil) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d. body: %s", w.Code, w.Body.String()) } var response []user.ServiceForPatchTest if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil { t.Fatalf("failed to unmarshal response: %v", err) } // Should return 2 services (the two with patch test duration > 0 that are active) if len(response) != 2 { t.Errorf("expected 2 eligible services, got %d. body: %s", len(response), w.Body.String()) } } func TestAdminUsers_PatchTests_Eligible_WithExisting(t *testing.T) { cleanup := setupTestDB(t) defer cleanup() // Create test user var userID string err := db.DB.QueryRow(context.Background(), ` INSERT INTO users (n_first_name, n_last_name, email, password_hash, account_role, account_type) VALUES ('Test', 'User', 'testuser@test.com', 'hash', 'verified_email', 'standard') RETURNING id `).Scan(&userID) if err != nil { t.Fatalf("failed to create user: %v", err) } // Create services with patch test var serviceID1, serviceID2 string err = db.DB.QueryRow(context.Background(), ` INSERT INTO services (name, description, price, duration_minutes, is_active, patch_test_duration_hours, minimum_age_required) VALUES ('Gel Polish Full Set', 'Gel polish service', 45.00, 60, true, 48, 16) RETURNING id `).Scan(&serviceID1) if err != nil { t.Fatalf("failed to create service 1: %v", err) } err = db.DB.QueryRow(context.Background(), ` INSERT INTO services (name, description, price, duration_minutes, is_active, patch_test_duration_hours, minimum_age_required) VALUES ('Luxury Gel Manicure', 'Luxury gel', 55.00, 75, true, 48, 16) RETURNING id `).Scan(&serviceID2) if err != nil { t.Fatalf("failed to create service 2: %v", err) } // Add one patch test for the user _, err = db.DB.Exec(context.Background(), ` INSERT INTO user_service_patch_tests (user_id, service_id, last_time) VALUES ($1, $2, NOW()) `, userID, serviceID1) if err != nil { t.Fatalf("failed to add patch test: %v", err) } handler := http.HandlerFunc(user.GetEligiblePatchTestServicesHandler) w := makeAdminRequest(handler, "GET", "/api/admin/users/"+userID+"/patch-tests/eligible", nil) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d. body: %s", w.Code, w.Body.String()) } var response []user.ServiceForPatchTest if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil { t.Fatalf("failed to unmarshal response: %v", err) } // Should return only 1 service (the one not already added) if len(response) != 1 { t.Errorf("expected 1 eligible service, got %d. body: %s", len(response), w.Body.String()) } if len(response) > 0 && response[0].ID != serviceID2 { t.Errorf("expected service %s, got %s", serviceID2, response[0].ID) } } func TestAdminUsers_AddPatchTest(t *testing.T) { cleanup := setupTestDB(t) defer cleanup() // Create test user var userID string err := db.DB.QueryRow(context.Background(), ` INSERT INTO users (n_first_name, n_last_name, email, password_hash, account_role, account_type) VALUES ('Test', 'User', 'testuser@test.com', 'hash', 'verified_email', 'standard') RETURNING id `).Scan(&userID) if err != nil { t.Fatalf("failed to create user: %v", err) } // Create service with patch test var serviceID string err = db.DB.QueryRow(context.Background(), ` INSERT INTO services (name, description, price, duration_minutes, is_active, patch_test_duration_hours, minimum_age_required) VALUES ('Gel Polish Full Set', 'Gel polish service', 45.00, 60, true, 48, 16) RETURNING id `).Scan(&serviceID) if err != nil { t.Fatalf("failed to create service: %v", err) } handler := http.HandlerFunc(user.AddPatchTestHandler) reqBody := user.AddPatchTestRequest{ServiceID: serviceID} w := makeAdminRequest(handler, "POST", "/api/admin/users/"+userID+"/patch-tests", reqBody) if w.Code != http.StatusCreated { t.Errorf("expected status 201, got %d. body: %s", w.Code, w.Body.String()) } // Verify patch test was added var count int err = db.DB.QueryRow(context.Background(), ` SELECT COUNT(*) FROM user_service_patch_tests WHERE user_id = $1 AND service_id = $2 `, userID, serviceID).Scan(&count) if err != nil { t.Fatalf("failed to check patch test: %v", err) } if count != 1 { t.Errorf("expected 1 patch test record, got %d", count) } } func TestAdminUsers_AddPatchTest_InvalidService(t *testing.T) { cleanup := setupTestDB(t) defer cleanup() // Create test user var userID string err := db.DB.QueryRow(context.Background(), ` INSERT INTO users (n_first_name, n_last_name, email, password_hash, account_role, account_type) VALUES ('Test', 'User', 'testuser@test.com', 'hash', 'verified_email', 'standard') RETURNING id `).Scan(&userID) if err != nil { t.Fatalf("failed to create user: %v", err) } // Create service without patch test requirement var serviceID string err = db.DB.QueryRow(context.Background(), ` INSERT INTO services (name, description, price, duration_minutes, is_active, patch_test_duration_hours, minimum_age_required) VALUES ('Basic Manicure', 'Basic manicure', 25.00, 30, true, 0, 0) RETURNING id `).Scan(&serviceID) if err != nil { t.Fatalf("failed to create service: %v", err) } handler := http.HandlerFunc(user.AddPatchTestHandler) reqBody := user.AddPatchTestRequest{ServiceID: serviceID} w := makeAdminRequest(handler, "POST", "/api/admin/users/"+userID+"/patch-tests", reqBody) if w.Code != http.StatusBadRequest { t.Errorf("expected status 400, got %d. body: %s", w.Code, w.Body.String()) } } func TestAdminUsers_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, password_hash, account_role, account_type) VALUES ('Regular', 'User', 'user@test.com', 'hash', 'verified_email', 'standard') `) if err != nil { t.Fatalf("failed to create user: %v", err) } // Create test user for GET var targetUserID string err = db.DB.QueryRow(context.Background(), ` INSERT INTO users (n_first_name, n_last_name, email, password_hash, account_role, account_type) VALUES ('Target', 'User', 'target@test.com', 'hash', 'verified_email', 'standard') RETURNING id `).Scan(&targetUserID) if err != nil { t.Fatalf("failed to create target user: %v", err) } // Test LIST - should get 403 when using middleware listHandler := mw.RequireAdmin(http.HandlerFunc(user.ListAdminUsersHandler)) w := makeUserRequest(listHandler, "GET", "/api/admin/users", nil) if w.Code != http.StatusForbidden { t.Errorf("LIST: expected status 403, got %d", w.Code) } // Test GET - should get 403 when using middleware getHandler := mw.RequireAdmin(http.HandlerFunc(user.GetAdminUserHandler)) w = makeUserRequest(getHandler, "GET", "/api/admin/users/"+targetUserID, nil) if w.Code != http.StatusForbidden { t.Errorf("GET: expected status 403, got %d", w.Code) } // Test eligible patch tests - should get 403 when using middleware eligibleHandler := mw.RequireAdmin(http.HandlerFunc(user.GetEligiblePatchTestServicesHandler)) w = makeUserRequest(eligibleHandler, "GET", "/api/admin/users/"+targetUserID+"/patch-tests/eligible", nil) if w.Code != http.StatusForbidden { t.Errorf("ELIGIBLE: expected status 403, got %d", w.Code) } // Test add patch test - should get 403 when using middleware addHandler := mw.RequireAdmin(http.HandlerFunc(user.AddPatchTestHandler)) w = makeUserRequest(addHandler, "POST", "/api/admin/users/"+targetUserID+"/patch-tests", map[string]string{"service_id": "some-service-id"}) if w.Code != http.StatusForbidden { t.Errorf("ADD: expected status 403, got %d", w.Code) } }