refactor: optimize test DB setup — TestMain per package, truncate-only between tests
- Add TestMain to all 10 test packages (schema DROP+CREATE runs once per package) - Convert per-test setupTestDB to resetTestData (TRUNCATE only, ~60% faster) - Add 3 missing tables to TruncateTables (booking_edit_requests, exceptional_group_applications, business_settings) - Remove dead truncateDiscountTables helper - Consolidate discount_test.go into package bookings (was external test package) - Update testutils.SetupTestDB to truncate-only - Fix unused imports across user, bookings, and handlers packages - Verify: 286 passing, 2 skipped, 0 failures with -count=2 (no state leakage)
This commit is contained in:
@@ -39,25 +39,9 @@ import (
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
// setupTestDB replaces the global db.DB with a test pool and returns a cleanup function
|
||||
func setupTestDB(t *testing.T) func() {
|
||||
func resetTestData(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
pool := testdb.Pool(t)
|
||||
testdb.Migrate(t, pool)
|
||||
testdb.TruncateTables(t, pool) // Clear data between tests
|
||||
|
||||
// Replace global db.DB with test pool
|
||||
originalDB := db.DB
|
||||
db.DB = pool
|
||||
|
||||
// Initialize JWT for tests
|
||||
jwt.Init()
|
||||
|
||||
return func() {
|
||||
db.DB = originalDB
|
||||
pool.Close()
|
||||
}
|
||||
testdb.TruncateTables(t, db.DB)
|
||||
}
|
||||
|
||||
// seedDefaultWorkingHours seeds default working hours for tests
|
||||
@@ -242,8 +226,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
// with a valid future time and at least one service. The test verifies the
|
||||
// booking is created in the database and associated with the correct user.
|
||||
func TestBookings_Create(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Seed working hours for booking tests
|
||||
seedDefaultWorkingHours(t)
|
||||
@@ -311,8 +294,7 @@ func TestBookings_Create(t *testing.T) {
|
||||
// TestBookings_Create_InvalidInput verifies that booking creation fails
|
||||
// with HTTP 400 when required fields are missing: start time or service IDs.
|
||||
func TestBookings_Create_InvalidInput(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -375,8 +357,7 @@ func TestBookings_Create_InvalidInput(t *testing.T) {
|
||||
// The test verifies the response includes the correct total count and that
|
||||
// bookings are properly returned.
|
||||
func TestBookings_List(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user and service
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -431,8 +412,7 @@ func TestBookings_List(t *testing.T) {
|
||||
// by status (e.g., pending, completed). It verifies that non-matching statuses
|
||||
// return empty results.
|
||||
func TestBookings_List_FilterByStatus(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user and service
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -502,8 +482,7 @@ func TestBookings_List_FilterByStatus(t *testing.T) {
|
||||
// TestBookings_Get tests that a user can retrieve a single booking by its ID.
|
||||
// The test verifies the booking details including services are returned.
|
||||
func TestBookings_Get(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user and service
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -557,8 +536,7 @@ func TestBookings_Get(t *testing.T) {
|
||||
// TestBookings_Get_NotFound verifies that requesting a non-existent booking
|
||||
// returns HTTP 404 Not Found.
|
||||
func TestBookings_Get_NotFound(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -587,8 +565,7 @@ func TestBookings_Get_NotFound(t *testing.T) {
|
||||
// booking. The test creates two users, one creates a booking, and the other
|
||||
// attempts to access it - expecting HTTP 404 (not found/access denied).
|
||||
func TestBookings_Get_AccessDenied(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create two test users
|
||||
userID1, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -636,8 +613,7 @@ func TestBookings_Get_AccessDenied(t *testing.T) {
|
||||
// as an ICS calendar file. It verifies the response has the correct
|
||||
// text/calendar Content-Type and contains ICS-formatted data.
|
||||
func TestBookings_GetCalendar(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user and service
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -698,8 +674,7 @@ func TestBookings_GetCalendar(t *testing.T) {
|
||||
// TestBookings_GetCalendar_NotFound verifies that attempting to export
|
||||
// a non-existent booking to calendar returns HTTP 404.
|
||||
func TestBookings_GetCalendar_NotFound(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -731,8 +706,7 @@ func TestBookings_GetCalendar_NotFound(t *testing.T) {
|
||||
// TestBookings_Edit tests that a user can modify the start time of
|
||||
// their existing booking. The test verifies the time is updated in the DB.
|
||||
func TestBookings_Edit(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user and service
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -797,8 +771,7 @@ func TestBookings_Edit(t *testing.T) {
|
||||
// TestBookings_Edit_InvalidInput verifies that editing fails with HTTP 400
|
||||
// when the start time is missing or is in the past.
|
||||
func TestBookings_Edit_InvalidInput(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user and service
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -859,8 +832,7 @@ func TestBookings_Edit_InvalidInput(t *testing.T) {
|
||||
// TestBookings_Edit_NotFound verifies that editing a non-existent
|
||||
// booking returns HTTP 404.
|
||||
func TestBookings_Edit_NotFound(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -897,8 +869,7 @@ func TestBookings_Edit_NotFound(t *testing.T) {
|
||||
// For bookings without payments, it performs a hard delete. The test verifies
|
||||
// the booking is removed from the database.
|
||||
func TestBookings_Delete(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user and service
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -952,8 +923,7 @@ func TestBookings_Delete(t *testing.T) {
|
||||
// the request fails with HTTP 400. With a reason, the booking is soft-deleted
|
||||
// (status changed to client_cancelled).
|
||||
func TestBookings_Delete_WithReason(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user and service
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -1022,8 +992,7 @@ func TestBookings_Delete_WithReason(t *testing.T) {
|
||||
// TestBookings_Delete_NotFound verifies that deleting a non-existent
|
||||
// booking returns HTTP 404.
|
||||
func TestBookings_Delete_NotFound(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -1052,8 +1021,7 @@ func TestBookings_Delete_NotFound(t *testing.T) {
|
||||
// - Cancellation < 24 hours before appointment: treated as no-show (deposits = 3)
|
||||
// - Cancellation >= 24 hours before appointment: treated as late_cancellation
|
||||
func TestBookings_Delete_NoShow24hThreshold(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -1115,8 +1083,7 @@ func TestBookings_Delete_NoShow24hThreshold(t *testing.T) {
|
||||
|
||||
// TestBookings_Delete_NoShow_WithForgiveness tests that admin can forgive a no-show
|
||||
func TestBookings_Delete_NoShow_WithForgiveness(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -1185,8 +1152,7 @@ func TestBookings_Delete_NoShow_WithForgiveness(t *testing.T) {
|
||||
// authentication. It verifies that requests without a token are rejected with
|
||||
// HTTP 401 for protected endpoints.
|
||||
func TestBookings_Unauthorized(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user and service
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -1296,8 +1262,7 @@ func TestBookings_Unauthorized(t *testing.T) {
|
||||
// TestBookings_List_Empty tests that listing bookings for a user with no
|
||||
// bookings returns an empty list with total 0.
|
||||
func TestBookings_List_Empty(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user (with no bookings)
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -1338,8 +1303,7 @@ func TestBookings_List_Empty(t *testing.T) {
|
||||
// TestBookings_Get_InvalidBookingID verifies that using an invalid
|
||||
// booking ID format returns HTTP 404 or 400.
|
||||
func TestBookings_Get_InvalidBookingID(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -1368,8 +1332,7 @@ func TestBookings_Get_InvalidBookingID(t *testing.T) {
|
||||
// TestBookings_Create_PastDate verifies that creating a booking with a
|
||||
// past start time fails with HTTP 400 Bad Request.
|
||||
func TestBookings_Create_PastDate(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
@@ -1408,8 +1371,7 @@ func TestBookings_Create_PastDate(t *testing.T) {
|
||||
// TestBookings_Create_MinimumAdvance tests that bookings must be made at least
|
||||
// 1 hour in advance (changed from 48h deposit requirement to universal 1h rule).
|
||||
func TestBookings_Create_MinimumAdvance(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Seed working hours for booking tests
|
||||
seedDefaultWorkingHours(t)
|
||||
@@ -1462,8 +1424,7 @@ func TestBookings_Create_MinimumAdvance(t *testing.T) {
|
||||
// TestBookings_Create_WithNotes_StatusPending tests that when a booking is created with notes,
|
||||
// the booking status is automatically set to 'pending' (requires admin approval).
|
||||
func TestBookings_Create_WithNotes_StatusPending(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Seed working hours
|
||||
seedDefaultWorkingHours(t)
|
||||
@@ -1516,8 +1477,7 @@ func TestBookings_Create_WithNotes_StatusPending(t *testing.T) {
|
||||
// TestBookings_Create_WithoutNotes_StatusConfirmed tests that when a booking is created without notes,
|
||||
// the booking status is automatically set to 'confirmed' (auto-approved).
|
||||
func TestBookings_Create_WithoutNotes_StatusConfirmed(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Seed working hours
|
||||
seedDefaultWorkingHours(t)
|
||||
@@ -1564,8 +1524,7 @@ func TestBookings_Create_WithoutNotes_StatusConfirmed(t *testing.T) {
|
||||
|
||||
// TestBookings_Create_Within1Hour_ShouldFail tests that bookings less than 1 hour in advance are rejected.
|
||||
func TestBookings_Create_Within1Hour_ShouldFail(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Seed working hours
|
||||
seedDefaultWorkingHours(t)
|
||||
@@ -1603,8 +1562,7 @@ func TestBookings_Create_Within1Hour_ShouldFail(t *testing.T) {
|
||||
// multiple services at once, and all services are properly associated with
|
||||
// the booking in the database.
|
||||
func TestBookings_Create_MultipleServices(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Seed working hours for booking tests
|
||||
seedDefaultWorkingHours(t)
|
||||
@@ -1668,8 +1626,7 @@ func TestBookings_Create_MultipleServices(t *testing.T) {
|
||||
// deleted as no-show with less than 24 hours notice (and no forgiveness), the
|
||||
// user's deposits_required is set to 3 and the booking status becomes "no_show".
|
||||
func TestDeleteBooking_NoShowUnder24h_SetsDepositsTo3(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -1752,8 +1709,7 @@ func TestDeleteBooking_NoShowUnder24h_SetsDepositsTo3(t *testing.T) {
|
||||
// cancelled with more than 24 hours notice (client_cancelled), no deposit penalty
|
||||
// is applied and deposits_required remains 0.
|
||||
func TestDeleteBooking_CancelOver24h_NoDepositPenalty(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -1837,8 +1793,7 @@ func TestDeleteBooking_CancelOver24h_NoDepositPenalty(t *testing.T) {
|
||||
// deleted as no-show with forgiveness (forgive_no_show: true), no deposit penalty
|
||||
// is applied and the booking status becomes "client_cancelled".
|
||||
func TestDeleteBooking_NoShowWithForgiveness_NoPenalty(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -1922,8 +1877,7 @@ func TestDeleteBooking_NoShowWithForgiveness_NoPenalty(t *testing.T) {
|
||||
// no-show, the deposits_required stays at 3 (not 6). The handler sets deposits to 3
|
||||
// on the first no-show and doesn't increment on subsequent no-shows.
|
||||
func TestDeleteBooking_SecondNoShow_StaysAt3(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -2033,8 +1987,7 @@ func TestDeleteBooking_SecondNoShow_StaysAt3(t *testing.T) {
|
||||
// TestCountUnforgivenNoShows_ExcludesForgiven tests that CountUnforgivenNoShows
|
||||
// excludes bookings that have been forgiven (in forgiven_no_shows table).
|
||||
func TestCountUnforgivenNoShows_ExcludesForgiven(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -2097,8 +2050,7 @@ func TestCountUnforgivenNoShows_ExcludesForgiven(t *testing.T) {
|
||||
// TestCountUnforgivenNoShows_ExcludesOld tests that CountUnforgivenNoShows
|
||||
// excludes no-shows older than 6 months.
|
||||
func TestCountUnforgivenNoShows_ExcludesOld(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -2160,8 +2112,7 @@ func TestCountUnforgivenNoShows_ExcludesOld(t *testing.T) {
|
||||
// TestApplyDepositsIfNeeded_AppliesAt2Plus tests that ApplyDepositsIfNeeded
|
||||
// applies 3 deposits when user has 2 or more unforgiven no-shows in last 6 months.
|
||||
func TestApplyDepositsIfNeeded_AppliesAt2Plus(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user with deposits_required = 0
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -2230,8 +2181,7 @@ func TestApplyDepositsIfNeeded_AppliesAt2Plus(t *testing.T) {
|
||||
// TestApplyDepositsIfNeeded_DoesNotApplyAt1 tests that ApplyDepositsIfNeeded
|
||||
// does NOT apply deposits when user has only 1 unforgiven no-show.
|
||||
func TestApplyDepositsIfNeeded_DoesNotApplyAt1(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user with deposits_required = 0
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -2307,8 +2257,7 @@ var _ = mw.UserIDKey
|
||||
// TestBookings_Get_NoAuthHeader confirms that accessing a booking without
|
||||
// an Authorization header returns HTTP 401 Unauthorized.
|
||||
func TestBookings_Get_NoAuthHeader(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
@@ -2351,8 +2300,7 @@ func TestBookings_Get_NoAuthHeader(t *testing.T) {
|
||||
// contains all required fields: BEGIN:VCALENDAR, END:VCALENDAR, BEGIN:VEVENT,
|
||||
// END:VEVENT, DTSTART, DTEND, and SUMMARY.
|
||||
func TestBookings_GetCalendar_ValidICS(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
@@ -2425,8 +2373,7 @@ func TestBookings_GetCalendar_ValidICS(t *testing.T) {
|
||||
// user cancels a confirmed booking (one with payments), an admin notification
|
||||
// is created to alert staff of the cancellation.
|
||||
func TestUserCancelBooking_ConfirmedCreatesNotification(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
@@ -2497,8 +2444,7 @@ func TestUserCancelBooking_ConfirmedCreatesNotification(t *testing.T) {
|
||||
// pending booking (one without payments) does NOT create an admin notification,
|
||||
// as pending cancellations don't require staff attention.
|
||||
func TestUserCancelBooking_PendingNoNotification(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
@@ -2563,8 +2509,7 @@ func TestUserCancelBooking_PendingNoNotification(t *testing.T) {
|
||||
// TestUserCancelBooking_TransactionIntegrity verifies that if any part of the
|
||||
// cancellation transaction fails, the booking status is NOT changed (rollback behavior)
|
||||
func TestUserCancelBooking_TransactionIntegrity(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
@@ -2638,8 +2583,7 @@ func TestUserCancelBooking_TransactionIntegrity(t *testing.T) {
|
||||
// confirmed booking (e.g., change time). This creates a booking_edit_request record
|
||||
// and generates an admin notification for staff review.
|
||||
func TestCreateEditRequest(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
@@ -2713,8 +2657,7 @@ func TestCreateEditRequest(t *testing.T) {
|
||||
// TestCreateEditRequest_WithTimeChange verifies that a user can request an edit to
|
||||
// change the booking time, and a time_blocker is created to reserve the new slot.
|
||||
func TestCreateEditRequest_WithTimeChange(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -2800,8 +2743,7 @@ func TestCreateEditRequest_WithTimeChange(t *testing.T) {
|
||||
|
||||
// TestDeleteEditRequest tests that user deleting their edit request deletes the admin notification
|
||||
func TestDeleteEditRequest(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
@@ -2891,8 +2833,7 @@ func TestDeleteEditRequest(t *testing.T) {
|
||||
|
||||
// TestAdminApproveEditRequest tests that admin approving acknowledges the notification (not deletes)
|
||||
func TestAdminApproveEditRequest(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
@@ -3008,8 +2949,7 @@ func TestAdminApproveEditRequest(t *testing.T) {
|
||||
|
||||
// TestAdminRejectEditRequest tests that admin rejecting acknowledges the notification (not deletes)
|
||||
func TestAdminRejectEditRequest(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
@@ -3121,8 +3061,7 @@ func TestAdminRejectEditRequest(t *testing.T) {
|
||||
// TestAdminApproveEditRequest_DeletesTimeBlocker verifies that when admin approves
|
||||
// an edit request, the associated time_blocker reservation is deleted.
|
||||
func TestAdminApproveEditRequest_DeletesTimeBlocker(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -3217,8 +3156,7 @@ func TestAdminApproveEditRequest_DeletesTimeBlocker(t *testing.T) {
|
||||
// TestAdminRejectEditRequest_DeletesTimeBlocker verifies that when admin rejects
|
||||
// an edit request, the associated time_blocker reservation is deleted.
|
||||
func TestAdminRejectEditRequest_DeletesTimeBlocker(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -3313,8 +3251,7 @@ func TestAdminRejectEditRequest_DeletesTimeBlocker(t *testing.T) {
|
||||
// TestDeleteEditRequest_DeletesTimeBlocker verifies that when user cancels their
|
||||
// own edit request, the associated time_blocker reservation is deleted.
|
||||
func TestDeleteEditRequest_DeletesTimeBlocker(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -3393,8 +3330,7 @@ func TestDeleteEditRequest_DeletesTimeBlocker(t *testing.T) {
|
||||
// TestAdminApproveEditRequest_TimeBlockerOverlap tests that approving an edit
|
||||
// request fails when the new time conflicts with an existing time_blocker.
|
||||
func TestAdminApproveEditRequest_TimeBlockerOverlap(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -3474,8 +3410,7 @@ func TestAdminApproveEditRequest_TimeBlockerOverlap(t *testing.T) {
|
||||
|
||||
// TestBookings_RequestEdit_BookingNotFound tests that requesting an edit for a non-existent booking returns 404
|
||||
func TestBookings_RequestEdit_BookingNotFound(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create test user
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
@@ -3505,8 +3440,7 @@ func TestBookings_RequestEdit_BookingNotFound(t *testing.T) {
|
||||
|
||||
// TestBookings_RequestEdit_AlreadyHasPending tests that a user cannot create a second edit request while one already exists
|
||||
func TestBookings_RequestEdit_AlreadyHasPending(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
@@ -3602,8 +3536,7 @@ func TestBookings_RequestEdit_AlreadyHasPending(t *testing.T) {
|
||||
// TestBookings_Create_PatchTestRequired_NoRecord verifies that a user without a patch test record
|
||||
// cannot book a service that requires a patch test. The booking should be rejected with 400.
|
||||
func TestBookings_Create_PatchTestRequired_NoRecord(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -3652,8 +3585,7 @@ func TestBookings_Create_PatchTestRequired_NoRecord(t *testing.T) {
|
||||
// TestBookings_Create_PatchTestRequired_WithinNoticePeriod verifies that a user
|
||||
// cannot book within the notice period after completing a patch test (e.g., 24h wait).
|
||||
func TestBookings_Create_PatchTestRequired_WithinNoticePeriod(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -3706,8 +3638,7 @@ func TestBookings_Create_PatchTestRequired_WithinNoticePeriod(t *testing.T) {
|
||||
// TestBookings_Create_PatchTestRequired_Expired verifies that a user
|
||||
// with an expired patch test cannot book services requiring patch test.
|
||||
func TestBookings_Create_PatchTestRequired_Expired(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -3760,8 +3691,7 @@ func TestBookings_Create_PatchTestRequired_Expired(t *testing.T) {
|
||||
// TestBookings_Create_PatchTestRequired_ValidRecord verifies that a user
|
||||
// with a valid patch test record can successfully book services requiring patch test.
|
||||
func TestBookings_Create_PatchTestRequired_ValidRecord(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -3826,8 +3756,7 @@ func TestBookings_Create_PatchTestRequired_ValidRecord(t *testing.T) {
|
||||
// cannot book within 24 hours notice (deposit payment window). They must complete more appointments
|
||||
// to remove this restriction.
|
||||
func TestBookings_Create_DepositRequired_Within24Hours(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -3874,8 +3803,7 @@ func TestBookings_Create_DepositRequired_Within24Hours(t *testing.T) {
|
||||
// TestBookings_Create_DepositRequired_After48Hours verifies that a user with deposits_required > 0
|
||||
// CAN book if the start time is at least 48 hours in the future.
|
||||
func TestBookings_Create_DepositRequired_After48Hours(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -3929,8 +3857,7 @@ func TestBookings_Create_DepositRequired_After48Hours(t *testing.T) {
|
||||
// TestBookings_Create_NoDepositRequired_Within48Hours verifies that a user with deposits_required=0
|
||||
// can book at any time (no 48h restriction).
|
||||
func TestBookings_Create_NoDepositRequired_Within48Hours(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -3977,8 +3904,7 @@ func TestBookings_Create_NoDepositRequired_Within48Hours(t *testing.T) {
|
||||
// TestBookings_Create_DepositSnapshot verifies that deposit_required is snapshotted
|
||||
// at booking creation time from user's current deposits_required value.
|
||||
func TestBookings_Create_DepositSnapshot(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -4048,8 +3974,7 @@ func TestBookings_Create_DepositSnapshot(t *testing.T) {
|
||||
// TestBookings_Create_DepositRequired_OneActiveBookingLimit verifies that a user
|
||||
// with deposits_required > 0 can only have ONE active booking at a time.
|
||||
func TestBookings_Create_DepositRequired_OneActiveBookingLimit(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -4110,8 +4035,7 @@ func TestBookings_Create_DepositRequired_OneActiveBookingLimit(t *testing.T) {
|
||||
// TestBookings_Get_DepositFieldsReturned verifies that GET /api/bookings returns
|
||||
// the deposit-related fields (deposit_required, deposit_amount, deposit_paid, deposit_deadline).
|
||||
func TestBookings_Get_DepositFieldsReturned(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -4187,8 +4111,7 @@ func TestBookings_Get_DepositFieldsReturned(t *testing.T) {
|
||||
// TestBookings_Edit_ClosedDay_UserBlocked verifies that a regular user cannot edit a booking
|
||||
// to fall on a closed day (exceptional hours marked as is_open=false).
|
||||
func TestBookings_Edit_ClosedDay_UserBlocked(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -4274,8 +4197,7 @@ func TestBookings_Edit_ClosedDay_UserBlocked(t *testing.T) {
|
||||
// TestBookings_Edit_OpenDay_UserAllowed verifies that a user CAN edit a booking
|
||||
// to a day that is marked as open in exceptional hours.
|
||||
func TestBookings_Edit_OpenDay_UserAllowed(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -4361,8 +4283,7 @@ func TestBookings_Edit_OpenDay_UserAllowed(t *testing.T) {
|
||||
// TestBookings_Create_OverlappingBlocker_UserBlocked verifies that a regular user
|
||||
// CANNOT create a booking that overlaps with a time blocker. They receive 409 Conflict.
|
||||
func TestBookings_Create_OverlappingBlocker_UserBlocked(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -4431,8 +4352,7 @@ func TestBookings_Create_OverlappingBlocker_UserBlocked(t *testing.T) {
|
||||
// TestBookings_Edit_OverlappingBlocker_UserBlocked verifies that a regular user
|
||||
// CANNOT edit a booking to a time that overlaps with a time blocker.
|
||||
func TestBookings_Edit_OverlappingBlocker_UserBlocked(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
@@ -4496,8 +4416,7 @@ func TestBookings_Edit_OverlappingBlocker_UserBlocked(t *testing.T) {
|
||||
// --- Guest Booking Tests ---
|
||||
|
||||
func TestGuestUser_Create_Success(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
req := map[string]string{
|
||||
"firstName": "Jane",
|
||||
@@ -4534,8 +4453,7 @@ func TestGuestUser_Create_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGuestUser_Create_DuplicateEmail(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// First guest creation
|
||||
req := map[string]string{
|
||||
@@ -4580,8 +4498,7 @@ func TestGuestUser_Create_DuplicateEmail(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGuestUser_Create_RegisteredEmailCollision(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create a registered user with a known email
|
||||
registeredEmail := "registered@example.com"
|
||||
@@ -4610,8 +4527,7 @@ func TestGuestUser_Create_RegisteredEmailCollision(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGuestBooking_Create_Success(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
// Create guest user
|
||||
@@ -4657,8 +4573,7 @@ func TestGuestBooking_Create_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGuestBooking_Create_WithoutUserID(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Attempt booking without auth AND without user_id
|
||||
serviceID, _ := fixtures.CreateTestService(db.DB)
|
||||
@@ -4678,8 +4593,7 @@ func TestGuestBooking_Create_WithoutUserID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGuestBooking_Create_NonGuestUserID(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
|
||||
// Create a registered (non-guest) user
|
||||
userID, _ := fixtures.CreateTestUser(db.DB)
|
||||
@@ -4706,8 +4620,7 @@ func TestGuestBooking_Create_NonGuestUserID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGuestBooking_SkipsDepositCheck(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
resetTestData(t)
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
// Create guest user
|
||||
|
||||
Reference in New Issue
Block a user