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,276 @@
|
||||
//go:build test
|
||||
// +build test
|
||||
|
||||
package services
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"crussell/db"
|
||||
"crussell/handlers/user"
|
||||
"crussell/testutils/jwt"
|
||||
"crussell/testutils/testdb"
|
||||
)
|
||||
|
||||
func setupTestDB(t *testing.T) func() {
|
||||
t.Helper()
|
||||
|
||||
pool := testdb.Pool(t)
|
||||
testdb.Migrate(t, pool)
|
||||
|
||||
originalDB := db.DB
|
||||
db.DB = pool
|
||||
|
||||
jwt.Init()
|
||||
|
||||
return func() {
|
||||
db.DB = originalDB
|
||||
pool.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func makeRequest(handler http.HandlerFunc, method, path string, body interface{}) *httptest.ResponseRecorder {
|
||||
var req *http.Request
|
||||
if body != nil {
|
||||
bodyBytes, _ := json.Marshal(body)
|
||||
req = httptest.NewRequest(method, path, bytes.NewReader(bodyBytes))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
} else {
|
||||
req = httptest.NewRequest(method, path, nil)
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
handler.ServeHTTP(w, req)
|
||||
return w
|
||||
}
|
||||
|
||||
func TestServices_ListAll(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
_, err := db.DB.Exec(context.Background(), `
|
||||
INSERT INTO services (name, description, price, duration_minutes, is_active, patch_test_duration_hours, minimum_age_required)
|
||||
VALUES
|
||||
('Manicure', 'Basic manicure', 25.00, 30, true, 0, 0),
|
||||
('Pedicure', 'Basic pedicure', 30.00, 45, true, 0, 0),
|
||||
('Inactive Service', 'Should not appear', 50.00, 60, false, 0, 0)
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create services: %v", err)
|
||||
}
|
||||
|
||||
handler := http.HandlerFunc(ServicesHandler)
|
||||
w := makeRequest(handler, "GET", "/api/services", nil)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected status 200, got %d. body: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
var response []ServiceResponse
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
|
||||
t.Fatalf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
|
||||
if len(response) != 2 {
|
||||
t.Errorf("expected 2 services, got %d", len(response))
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
if found["Inactive Service"] {
|
||||
t.Error("should not include inactive service")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServices_EligibleForUser_AgeFilter(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
dob := "2005-01-01"
|
||||
userID, err := createUserWithDOB(dob)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create user: %v", err)
|
||||
}
|
||||
|
||||
_, err = db.DB.Exec(context.Background(), `
|
||||
INSERT INTO services (name, description, price, duration_minutes, is_active, patch_test_duration_hours, minimum_age_required)
|
||||
VALUES
|
||||
('Under 18 Service', 'For minors', 20.00, 30, true, 0, 16),
|
||||
('Adult Only Service', 'For adults only', 50.00, 60, true, 0, 21),
|
||||
('No Age Restriction', 'Everyone welcome', 30.00, 45, true, 0, 0)
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create services: %v", err)
|
||||
}
|
||||
|
||||
handler := http.HandlerFunc(ServicesEligibleForUserHandler)
|
||||
req := httptest.NewRequest("GET", "/api/services/eligible-for/"+userID, nil)
|
||||
w := httptest.NewRecorder()
|
||||
handler.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected status 200, got %d. body: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
var response []ServiceResponse
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
|
||||
t.Fatalf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
|
||||
if len(response) != 2 {
|
||||
t.Errorf("expected 2 services, got %d. Response: %s", len(response), w.Body.String())
|
||||
}
|
||||
|
||||
found := map[string]bool{}
|
||||
for _, s := range response {
|
||||
found[s.Name] = true
|
||||
}
|
||||
if !found["Under 18 Service"] {
|
||||
t.Error("expected Under 18 Service in response (age 20 >= 16)")
|
||||
}
|
||||
if !found["No Age Restriction"] {
|
||||
t.Error("expected No Age Restriction in response")
|
||||
}
|
||||
if found["Adult Only Service"] {
|
||||
t.Error("should not include Adult Only Service (age 20 < 21)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServices_EligibleForUser_PatchTest(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
dob := "2000-01-01"
|
||||
userID, err := createUserWithDOB(dob)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create user: %v", err)
|
||||
}
|
||||
|
||||
_, err = db.DB.Exec(context.Background(), `
|
||||
INSERT INTO services (name, description, price, duration_minutes, is_active, patch_test_duration_hours, minimum_age_required)
|
||||
VALUES
|
||||
('Regular Service', 'No patch test needed', 30.00, 30, true, 0, 0),
|
||||
('Patch Test Required', 'Requires patch test', 75.00, 60, true, 48, 0)
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create services: %v", err)
|
||||
}
|
||||
|
||||
var patchTestSvcID string
|
||||
err = db.DB.QueryRow(context.Background(), "SELECT id FROM services WHERE name = 'Patch Test Required'").Scan(&patchTestSvcID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get patch test service: %v", err)
|
||||
}
|
||||
|
||||
_, err = db.DB.Exec(context.Background(), `
|
||||
INSERT INTO user_service_patch_tests (user_id, service_id, last_time)
|
||||
VALUES ($1, $2, NOW() - INTERVAL '24 hours')
|
||||
`, userID, patchTestSvcID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create patch test record: %v", err)
|
||||
}
|
||||
|
||||
handler := http.HandlerFunc(ServicesEligibleForUserHandler)
|
||||
req := httptest.NewRequest("GET", "/api/services/eligible-for/"+userID, nil)
|
||||
w := httptest.NewRecorder()
|
||||
handler.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected status 200, got %d. body: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
var response []ServiceResponse
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
|
||||
t.Fatalf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
|
||||
if len(response) != 2 {
|
||||
t.Errorf("expected 2 services, got %d. Response: %s", len(response), w.Body.String())
|
||||
}
|
||||
|
||||
var patchTestSvc *ServiceResponse
|
||||
var regularSvc *ServiceResponse
|
||||
for i := range response {
|
||||
if response[i].Name == "Patch Test Required" {
|
||||
patchTestSvc = &response[i]
|
||||
}
|
||||
if response[i].Name == "Regular Service" {
|
||||
regularSvc = &response[i]
|
||||
}
|
||||
}
|
||||
|
||||
if patchTestSvc == nil {
|
||||
t.Fatal("Patch Test Required service not found in response")
|
||||
}
|
||||
if patchTestSvc.PatchTestStatus == nil || *patchTestSvc.PatchTestStatus != "ok" {
|
||||
t.Errorf("expected patch test status 'ok', got %v", patchTestSvc.PatchTestStatus)
|
||||
}
|
||||
|
||||
if regularSvc == nil {
|
||||
t.Fatal("Regular Service not found in response")
|
||||
}
|
||||
if regularSvc.PatchTestStatus != nil {
|
||||
t.Errorf("expected no patch test status for regular service, got %v", regularSvc.PatchTestStatus)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContact_ReturnsInfo(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
_, err := db.DB.Exec(context.Background(), `
|
||||
INSERT INTO users (n_first_name, n_last_name, email, phone, password_hash, account_role, account_type)
|
||||
VALUES ('John', 'Smith', 'john@test.com', '07700900001', 'hash', 'admin', 'email')
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin user: %v", err)
|
||||
}
|
||||
|
||||
handler := http.HandlerFunc(user.GetContactInfoHandler)
|
||||
w := makeRequest(handler, "GET", "/api/contact", nil)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected status 200, got %d. body: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
var response user.ContactInfo
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
|
||||
t.Fatalf("failed to unmarshal response: %v", err)
|
||||
}
|
||||
|
||||
if response.Name == "" {
|
||||
t.Error("expected name in response")
|
||||
}
|
||||
if response.Phone == "" {
|
||||
t.Error("expected phone in response")
|
||||
}
|
||||
if response.Email == "" {
|
||||
t.Error("expected email in response")
|
||||
}
|
||||
if response.Role == "" {
|
||||
t.Error("expected role in response")
|
||||
}
|
||||
}
|
||||
|
||||
func createUserWithDOB(dob string) (string, error) {
|
||||
ctx := context.Background()
|
||||
var userID string
|
||||
err := db.DB.QueryRow(ctx, `
|
||||
INSERT INTO users (n_first_name, n_last_name, email, password_hash, account_role, account_type, date_of_birth)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
RETURNING id
|
||||
`, "Test", "User", "testuser@test.com", "hash", "verified_email", "email", dob).Scan(&userID)
|
||||
return userID, err
|
||||
}
|
||||
Reference in New Issue
Block a user