Files
Crussell/backend/testutils/fixtures/fixtures.go
T
popertots df3439bd70 fix: improve test infrastructure and add ID validation
- Add TestMain to set test env vars and testdb.TruncateTables for test
  isolation
- Add chi routing context to test helpers for path parameter extraction
- Fix SQL error handling to use errors.Is() instead of ==
- Add validators package with ID validation
- Fix admin test middleware chain (RequireAdmin wrapper)
- Update test user inserts to include phone and date_of_birth fields
- Update service delete test to check soft-delete (is_active=false)
- Update holiday hours test to use new schema (weekday, is_open)
- Add phone number validation tests for UK mobile numbers
2026-02-23 00:59:32 +00:00

157 lines
4.8 KiB
Go

//go:build test
// +build test
package fixtures
import (
"context"
"fmt"
"github.com/jackc/pgx/v5/pgxpool"
"golang.org/x/crypto/bcrypt"
)
// Global counter for unique emails in tests
var testEmailCounter int64
func CreateTestAdminUser(pool *pgxpool.Pool) (string, error) {
return createTestUser(pool, "Admin", "User", "", "admin")
}
func CreateTestUser(pool *pgxpool.Pool) (string, error) {
return createTestUser(pool, "Test", "User", "", "verified_email")
}
func CreateTestUserWithEmail(pool *pgxpool.Pool, email, role string) (string, error) {
return createTestUser(pool, "Test", "User", email, role)
}
func createTestUser(pool *pgxpool.Pool, firstName, lastName, email, role string) (string, error) {
passwordHash, err := bcrypt.GenerateFromPassword([]byte("testpassword123"), bcrypt.DefaultCost)
if err != nil {
return "", fmt.Errorf("failed to hash password: %w", err)
}
// Generate unique email if not provided
if email == "" {
testEmailCounter++
email = fmt.Sprintf("%s.%s.%d@test.com", firstName, lastName, testEmailCounter)
}
ctx := context.Background()
var userID string
err = pool.QueryRow(ctx, `
INSERT INTO users (n_first_name, n_last_name, email, phone, date_of_birth, password_hash, account_role, account_type)
VALUES ($1, $2, $3, $4, $5, $6, $7, 'email')
RETURNING id
`, firstName, lastName, email, "+447123456789", "1990-01-01", string(passwordHash), role).Scan(&userID)
if err != nil {
return "", fmt.Errorf("failed to create user: %w", err)
}
return userID, nil
}
func CreateTestService(pool *pgxpool.Pool) (string, error) {
ctx := context.Background()
var serviceID string
err := pool.QueryRow(ctx, `
INSERT INTO services (name, description, price, duration_minutes, is_active, patch_test_duration_hours, minimum_age_required)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING id
`, "Test Service", "A test service for unit tests", 50.00, 60, true, 0, 16).Scan(&serviceID)
if err != nil {
return "", fmt.Errorf("failed to create service: %w", err)
}
return serviceID, nil
}
func CreateTestServiceWithPatchTest(pool *pgxpool.Pool) (string, error) {
ctx := context.Background()
var serviceID string
err := pool.QueryRow(ctx, `
INSERT INTO services (name, description, price, duration_minutes, is_active, patch_test_duration_hours, minimum_age_required)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING id
`, "Test Patch Test Service", "A test service requiring patch test", 75.00, 90, true, 48, 18).Scan(&serviceID)
if err != nil {
return "", fmt.Errorf("failed to create service: %w", err)
}
return serviceID, nil
}
func CreateTestBooking(pool *pgxpool.Pool, userID, serviceID string) (string, error) {
ctx := context.Background()
var bookingID string
err := pool.QueryRow(ctx, `
INSERT INTO bookings (user_id, start_time, status, notes)
VALUES ($1, $2, $3, $4)
RETURNING id
`, userID, "2099-12-31 10:00:00+00", "pending", "Test booking").Scan(&bookingID)
if err != nil {
return "", fmt.Errorf("failed to create booking: %w", err)
}
_, err = pool.Exec(ctx, `
INSERT INTO booking_services (booking_id, service_id)
VALUES ($1, $2)
`, bookingID, serviceID)
if err != nil {
return "", fmt.Errorf("failed to link service to booking: %w", err)
}
return bookingID, nil
}
func CreateTestVerifiedUser(pool *pgxpool.Pool) (string, error) {
return createTestUser(pool, "Verified", "User", "verified@test.com", "verified_email")
}
func CreateTestUnverifiedUser(pool *pgxpool.Pool) (string, error) {
return createTestUser(pool, "Unverified", "User", "unverified@test.com", "unverified_email")
}
func CreateTestGuestUser(pool *pgxpool.Pool) (string, error) {
return createTestUser(pool, "Guest", "User", "guest@test.com", "guest")
}
func DeleteUser(pool *pgxpool.Pool, userID string) error {
ctx := context.Background()
_, err := pool.Exec(ctx, "DELETE FROM users WHERE id = $1", userID)
return err
}
func DeleteService(pool *pgxpool.Pool, serviceID string) error {
ctx := context.Background()
_, err := pool.Exec(ctx, "DELETE FROM services WHERE id = $1", serviceID)
return err
}
func DeleteBooking(pool *pgxpool.Pool, bookingID string) error {
ctx := context.Background()
_, err := pool.Exec(ctx, "DELETE FROM bookings WHERE id = $1", bookingID)
return err
}
// SafeDeleteUser wraps DeleteUser and returns error (for tests that care about cleanup failure)
func SafeDeleteUser(db *pgxpool.Pool, userID string) error {
return DeleteUser(db, userID)
}
// SafeDeleteService wraps DeleteService and returns error (for tests that care about cleanup failure)
func SafeDeleteService(db *pgxpool.Pool, serviceID string) error {
return DeleteService(db, serviceID)
}
// SafeDeleteBooking wraps DeleteBooking and returns error (for tests that care about cleanup failure)
func SafeDeleteBooking(db *pgxpool.Pool, bookingID string) error {
return DeleteBooking(db, bookingID)
}