//go:build test // +build test package fixtures import ( "context" "fmt" "time" "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, minimum_age_required) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id `, "Test Service", "A test service for unit tests", 50.00, 60, true, 16).Scan(&serviceID) if err != nil { return "", fmt.Errorf("failed to create service: %w", err) } return serviceID, nil } // CreateTestServiceWithPatchTest creates a service and a patch test that links to it // Returns serviceID, patchTestID func CreateTestServiceWithPatchTest(pool *pgxpool.Pool) (string, string, error) { ctx := context.Background() // First create the service var serviceID string err := pool.QueryRow(ctx, ` INSERT INTO services (name, description, price, duration_minutes, is_active, minimum_age_required) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id `, "Test Patch Test Service", "A test service requiring patch test", 75.00, 90, true, 18).Scan(&serviceID) if err != nil { return "", "", fmt.Errorf("failed to create service: %w", err) } // Now create a patch test that links to this service var patchTestID string err = pool.QueryRow(ctx, ` INSERT INTO patch_tests (name, description, notice_duration_hours, expiry_months, service_ids) VALUES ($1, $2, $3, $4, $5) RETURNING id `, "Test Patch Test", "A patch test for testing", 24, 6, []string{serviceID}).Scan(&patchTestID) if err != nil { return "", "", fmt.Errorf("failed to create patch test: %w", err) } return serviceID, patchTestID, nil } // CreateTestPatchTest creates a patch test definition func CreateTestPatchTest(pool *pgxpool.Pool, serviceIDs []string) (string, error) { ctx := context.Background() var patchTestID string err := pool.QueryRow(ctx, ` INSERT INTO patch_tests (name, description, notice_duration_hours, expiry_months, service_ids) VALUES ($1, $2, $3, $4, $5) RETURNING id `, "Test Patch Test", "A patch test for testing", 24, 6, serviceIDs).Scan(&patchTestID) if err != nil { return "", fmt.Errorf("failed to create patch test: %w", err) } return patchTestID, nil } // CreateUserPatchTest creates a user patch test record func CreateUserPatchTest(pool *pgxpool.Pool, userID, patchTestID string, testedAt string) error { ctx := context.Background() _, err := pool.Exec(ctx, ` INSERT INTO user_patch_tests (user_id, patch_test_id, tested_at) VALUES ($1, $2, $3) `, userID, patchTestID, testedAt) if err != nil { return fmt.Errorf("failed to create user patch test: %w", err) } return 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) } // CreateTestTimeBlocker creates a time blocker for testing // Returns the blocker ID func CreateTestTimeBlocker(pool *pgxpool.Pool, startTime time.Time, durationMinutes int, description string) (string, error) { ctx := context.Background() var blockerID string err := pool.QueryRow(ctx, ` INSERT INTO time_blockers (start_time, duration_minutes, description) VALUES ($1, $2, $3) RETURNING id `, startTime, durationMinutes, description).Scan(&blockerID) if err != nil { return "", fmt.Errorf("failed to create time blocker: %w", err) } return blockerID, nil } // DeleteTimeBlocker removes a time blocker from the database func DeleteTimeBlocker(pool *pgxpool.Pool, blockerID string) error { ctx := context.Background() _, err := pool.Exec(ctx, "DELETE FROM time_blockers WHERE id = $1", blockerID) return err } // CreateTestPayment creates a payment record for testing // Returns payment ID func CreateTestPayment(db *pgxpool.Pool, bookingID string, amount float64, method string, ptype string, status string) (string, error) { ctx := context.Background() var paymentID string err := db.QueryRow(ctx, ` INSERT INTO payments (booking_id, payment_type, payment_method, status, amount, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, NOW(), NOW()) RETURNING id `, bookingID, ptype, method, status, amount).Scan(&paymentID) if err != nil { return "", fmt.Errorf("failed to create payment: %w", err) } return paymentID, nil } // CreateTestRefund creates a refund record for testing // Returns refund ID func CreateTestRefund(db *pgxpool.Pool, paymentID string, bookingID string, amount float64) (string, error) { ctx := context.Background() var refundID string err := db.QueryRow(ctx, ` INSERT INTO refunds (payment_id, booking_id, amount, status, reason, created_at) VALUES ($1, $2, $3, 'completed', 'test refund', NOW()) RETURNING id `, paymentID, bookingID, amount).Scan(&refundID) if err != nil { return "", fmt.Errorf("failed to create refund: %w", err) } return refundID, nil } // CreateTestPaymentMethod creates a saved card for a user // Returns card ID func CreateTestPaymentMethod(db *pgxpool.Pool, userID string, squareCardID string, brand string, last4 string) (string, error) { ctx := context.Background() var cardID string err := db.QueryRow(ctx, ` INSERT INTO user_saved_cards (user_id, square_card_id, brand, last_4, exp_month, exp_year, fingerprint, is_default, created_at) VALUES ($1, $2, $3, $4, 12, 2030, 'test_fp', false, NOW()) RETURNING id `, userID, squareCardID, brand, last4).Scan(&cardID) if err != nil { return "", fmt.Errorf("failed to create payment method: %w", err) } return cardID, nil } // DeletePayment deletes a payment from the database func DeletePayment(pool *pgxpool.Pool, paymentID string) error { ctx := context.Background() _, err := pool.Exec(ctx, "DELETE FROM payments WHERE id = $1", paymentID) return err } // DeleteRefund deletes a refund from the database func DeleteRefund(pool *pgxpool.Pool, refundID string) error { ctx := context.Background() _, err := pool.Exec(ctx, "DELETE FROM refunds WHERE id = $1", refundID) return err } // DeletePaymentMethod deletes a saved card from the database func DeletePaymentMethod(pool *pgxpool.Pool, cardID string) error { ctx := context.Background() _, err := pool.Exec(ctx, "DELETE FROM user_saved_cards WHERE id = $1", cardID) return err }