Fix test setup and middleware chain - Handler tests now passing
- Fix TestRequireRoleMiddleware by chaining RequireAuth before RequireRole (role context requirement) - Remove unused 'strings' import from testdb.go - Create crussell_test database in Docker setup - Tests now properly initialize authentication context for role-based tests Result: handlers test suite passes (13/13 tests) Remaining failures in admin/auth/bookings/portfolio/scheduling/services/user packages need further investigation (environment setup, database constraints, endpoint initialization)
This commit is contained in:
@@ -0,0 +1,350 @@
|
||||
//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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user