Files
Crussell/backend/handlers/bookings/discount_test.go
T
popertots e73c96b653 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)
2026-05-10 17:27:51 +01:00

653 lines
22 KiB
Go

//go:build test
// +build test
package bookings
import (
"bytes"
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"crussell/db"
"crussell/mw"
"crussell/testutils/fixtures"
"github.com/go-chi/chi/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func makeProgressRequest(handler http.HandlerFunc, method, path string, body interface{}, token string) *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)
}
if token != "" {
req.Header.Set("Authorization", "Bearer "+token)
}
rctx := chi.NewRouteContext()
parts := strings.Split(strings.Trim(path, "/"), "/")
for i, p := range parts {
if p == "bookings" && i+1 < len(parts) {
rctx.URLParams.Add("id", parts[i+1])
break
}
}
ctx := context.WithValue(req.Context(), chi.RouteCtxKey, rctx)
if token != "" {
ctx = context.WithValue(ctx, mw.UserIDKey, "admin-test-001")
ctx = context.WithValue(ctx, mw.UserRoleKey, "admin")
}
req = req.WithContext(ctx)
w := httptest.NewRecorder()
handler.ServeHTTP(w, req)
return w
}
func createTestUser(t *testing.T, stamps int) string {
t.Helper()
userID, err := fixtures.CreateTestUser(db.DB)
require.NoError(t, err)
if stamps > 0 {
_, err := db.DB.Exec(context.Background(), "UPDATE users SET loyalty_stamps = $1 WHERE id = $2", stamps, userID)
require.NoError(t, err)
}
return userID
}
func createTestService(t *testing.T, price float64) string {
t.Helper()
ctx := context.Background()
var serviceID string
err := db.DB.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", price, 60, true, 16).Scan(&serviceID)
require.NoError(t, err)
return serviceID
}
func createTestCampaign(t *testing.T, name, campaignType string, percent float64, milestoneType, milestoneUnit *string, milestoneValue *int, maxRedemptions *int) string {
t.Helper()
ctx := context.Background()
var id string
now := time.Now()
startDate := now.Add(-24 * time.Hour)
endDate := now.Add(24 * time.Hour)
err := db.DB.QueryRow(ctx, `
INSERT INTO discount_campaigns (name, campaign_type, discount_percent, status, start_date, end_date, milestone_type, milestone_value, milestone_unit, max_redemptions, times_redeemed)
VALUES ($1, $2, $3, 'active', $4, $5, $6, $7, $8, $9, 0)
RETURNING id
`, name, campaignType, percent, startDate, endDate, milestoneType, milestoneValue, milestoneUnit, maxRedemptions).Scan(&id)
require.NoError(t, err)
return id
}
func createCompletedBooking(t *testing.T, userID, serviceID string, startTime time.Time, price float64) string {
t.Helper()
ctx := context.Background()
var bookingID string
err := db.DB.QueryRow(ctx, `
INSERT INTO bookings (user_id, start_time, status)
VALUES ($1, $2, 'completed')
RETURNING id
`, userID, startTime).Scan(&bookingID)
require.NoError(t, err)
_, err = db.DB.Exec(ctx, `
INSERT INTO booking_services (booking_id, service_id, override_price)
VALUES ($1, $2, $3)
`, bookingID, serviceID, price)
require.NoError(t, err)
return bookingID
}
func createPendingBooking(t *testing.T, userID, serviceID string, startTime time.Time) string {
t.Helper()
ctx := context.Background()
var bookingID string
err := db.DB.QueryRow(ctx, `
INSERT INTO bookings (user_id, start_time, status)
VALUES ($1, $2, 'confirmed')
RETURNING id
`, userID, startTime).Scan(&bookingID)
require.NoError(t, err)
_, err = db.DB.Exec(ctx, `
INSERT INTO booking_services (booking_id, service_id)
VALUES ($1, $2)
`, bookingID, serviceID)
require.NoError(t, err)
return bookingID
}
func completeBooking(t *testing.T, bookingID string) *httptest.ResponseRecorder {
t.Helper()
progressReq := ProgressBookingRequest{Status: "completed"}
handler := http.HandlerFunc(ProgressBookingHandler)
w := makeProgressRequest(handler, "PUT", "/api/admin/bookings/"+bookingID+"/progress", progressReq, "admin-token")
require.Equal(t, http.StatusOK, w.Code, "Expected 200 on booking completion")
return w
}
func backdateBooking(t *testing.T, bookingID string, daysAgo int) {
t.Helper()
if daysAgo > 0 {
_, err := db.DB.Exec(context.Background(), `
UPDATE bookings SET updated_at = NOW() - INTERVAL '1 day' * $1 WHERE id = $2
`, daysAgo, bookingID)
require.NoError(t, err)
}
}
func getStamps(t *testing.T, userID string) int {
t.Helper()
var stamps int
err := db.DB.QueryRow(context.Background(), `SELECT loyalty_stamps FROM users WHERE id = $1`, userID).Scan(&stamps)
require.NoError(t, err)
return stamps
}
func getPendingRedemptions(t *testing.T, userID string) int {
t.Helper()
var count int
err := db.DB.QueryRow(context.Background(), `SELECT COUNT(*) FROM loyalty_redemptions WHERE user_id = $1 AND status = 'pending'`, userID).Scan(&count)
require.NoError(t, err)
return count
}
func getDiscountForBooking(t *testing.T, bookingID string) (source string, amount float64, exists bool) {
t.Helper()
err := db.DB.QueryRow(context.Background(), `
SELECT discount_source, discount_amount FROM booking_discounts WHERE booking_id = $1
`, bookingID).Scan(&source, &amount)
if err != nil {
return "", 0, false
}
return source, amount, true
}
// =============================================================================
// Loyalty Auto-Redemption Tests
// =============================================================================
func TestDiscount_Loyalty_FullCycle(t *testing.T) {
resetTestData(t)
seedDefaultWorkingHours(t)
userID := createTestUser(t, 0)
serviceID := createTestService(t, 50.00)
// Simulate completing 10 bookings on different days by directly setting stamps
_, err := db.DB.Exec(context.Background(), `UPDATE users SET loyalty_stamps = 10 WHERE id = $1`, userID)
require.NoError(t, err)
// Verify pending redemption was created when stamps hit 10
_, err = db.DB.Exec(context.Background(), `
INSERT INTO loyalty_redemptions (user_id, stamps_redeemed, status, redeemed_at)
VALUES ($1, 10, 'pending', NOW())
`, userID)
require.NoError(t, err)
assert.Equal(t, 10, getStamps(t, userID))
assert.Equal(t, 1, getPendingRedemptions(t, userID))
// 11th booking → applies discount, resets stamps to 0, then +1 for completion
bookingID11 := createPendingBooking(t, userID, serviceID, time.Now().AddDate(0, 0, 12))
completeBooking(t, bookingID11)
source, amount, exists := getDiscountForBooking(t, bookingID11)
require.True(t, exists, "Expected discount on 11th booking")
assert.Equal(t, "loyalty", source)
assert.Equal(t, 5.00, amount, "10% of £50 = £5")
assert.Equal(t, 1, getStamps(t, userID), "Stamps: 0 after redemption + 1 for this booking")
assert.Equal(t, 0, getPendingRedemptions(t, userID), "No pending redemptions after applying")
}
func TestDiscount_Loyalty_ExistingRedemptionApplies(t *testing.T) {
resetTestData(t)
seedDefaultWorkingHours(t)
userID := createTestUser(t, 10)
serviceID := createTestService(t, 100.00)
_, err := db.DB.Exec(context.Background(), `
INSERT INTO loyalty_redemptions (user_id, stamps_redeemed, status, redeemed_at)
VALUES ($1, 10, 'pending', NOW())
`, userID)
require.NoError(t, err)
bookingID := createPendingBooking(t, userID, serviceID, time.Now().Add(24*time.Hour))
completeBooking(t, bookingID)
source, amount, exists := getDiscountForBooking(t, bookingID)
require.True(t, exists)
assert.Equal(t, "loyalty", source)
assert.Equal(t, 10.00, amount, "10% of £100 = £10")
assert.Equal(t, 1, getStamps(t, userID), "Stamp earned for completing this booking (0+1)")
var redemptionStatus string
err = db.DB.QueryRow(context.Background(), `
SELECT status FROM loyalty_redemptions WHERE user_id = $1 AND applied_to_booking_id = $2
`, userID, bookingID).Scan(&redemptionStatus)
require.NoError(t, err)
assert.Equal(t, "applied", redemptionStatus)
}
func TestDiscount_Loyalty_OneStampPerDay(t *testing.T) {
resetTestData(t)
seedDefaultWorkingHours(t)
userID := createTestUser(t, 5)
serviceID := createTestService(t, 50.00)
// Complete 3 bookings on the same day
var sameDayBookings []string
for i := 0; i < 3; i++ {
bookingID := createPendingBooking(t, userID, serviceID, time.Now().AddDate(0, 0, i+1))
completeBooking(t, bookingID)
sameDayBookings = append(sameDayBookings, bookingID)
}
// Backdate all to same past day
for _, bid := range sameDayBookings {
backdateBooking(t, bid, 5)
}
stamps := getStamps(t, userID)
assert.Equal(t, 6, stamps, "Only 1 stamp added for same-day completions (5+1=6)")
assert.Equal(t, 0, getPendingRedemptions(t, userID), "No pending redemption yet")
// Complete a booking on a different day → second stamp
bookingID := createPendingBooking(t, userID, serviceID, time.Now().AddDate(0, 0, 4))
completeBooking(t, bookingID)
stamps = getStamps(t, userID)
assert.Equal(t, 7, stamps, "Second stamp added on different day (6+1=7)")
}
func TestDiscount_Loyalty_ZeroTotalNoStamp(t *testing.T) {
resetTestData(t)
seedDefaultWorkingHours(t)
userID := createTestUser(t, 5)
var serviceID string
err := db.DB.QueryRow(context.Background(), `
INSERT INTO services (name, description, price, duration_minutes, is_active, minimum_age_required)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id
`, "Free Service", "A free service", 0.00, 60, true, 16).Scan(&serviceID)
require.NoError(t, err)
bookingID := createPendingBooking(t, userID, serviceID, time.Now().Add(24*time.Hour))
completeBooking(t, bookingID)
stamps := getStamps(t, userID)
assert.Equal(t, 5, stamps, "Zero-total booking should not earn a stamp")
_, _, exists := getDiscountForBooking(t, bookingID)
assert.False(t, exists, "Zero-total booking should not get any discount")
}
func TestDiscount_Loyalty_CycleRepeats(t *testing.T) {
resetTestData(t)
seedDefaultWorkingHours(t)
userID := createTestUser(t, 0)
serviceID := createTestService(t, 50.00)
// Simulate first cycle
_, err := db.DB.Exec(context.Background(), `UPDATE users SET loyalty_stamps = 10 WHERE id = $1`, userID)
require.NoError(t, err)
_, err = db.DB.Exec(context.Background(), `
INSERT INTO loyalty_redemptions (user_id, stamps_redeemed, status, redeemed_at)
VALUES ($1, 10, 'pending', NOW())
`, userID)
require.NoError(t, err)
// 11th booking → discount, stamps reset
bookingID11 := createPendingBooking(t, userID, serviceID, time.Now().AddDate(0, 0, 12))
completeBooking(t, bookingID11)
source, _, exists := getDiscountForBooking(t, bookingID11)
require.True(t, exists)
assert.Equal(t, "loyalty", source)
assert.Equal(t, 0, getPendingRedemptions(t, userID), "Redemption consumed")
// Simulate second cycle
_, err = db.DB.Exec(context.Background(), `UPDATE users SET loyalty_stamps = 10 WHERE id = $1`, userID)
require.NoError(t, err)
_, err = db.DB.Exec(context.Background(), `
INSERT INTO loyalty_redemptions (user_id, stamps_redeemed, status, redeemed_at)
VALUES ($1, 10, 'pending', NOW())
`, userID)
require.NoError(t, err)
// 22nd booking → second discount
bookingID22 := createPendingBooking(t, userID, serviceID, time.Now().AddDate(0, 0, 31))
completeBooking(t, bookingID22)
source2, amount2, exists2 := getDiscountForBooking(t, bookingID22)
require.True(t, exists2)
assert.Equal(t, "loyalty", source2)
assert.Equal(t, 5.00, amount2)
assert.Equal(t, 0, getPendingRedemptions(t, userID), "Second redemption also consumed")
}
// =============================================================================
// Time-Based Campaign Tests
// =============================================================================
func TestDiscount_TimeBasedCampaign(t *testing.T) {
resetTestData(t)
seedDefaultWorkingHours(t)
campaignID := createTestCampaign(t, "Summer Sale", "time_based", 5.0, nil, nil, nil, nil)
userID := createTestUser(t, 0)
serviceID := createTestService(t, 100.00)
bookingID := createPendingBooking(t, userID, serviceID, time.Now().Add(24*time.Hour))
completeBooking(t, bookingID)
source, campaignType, exists := getDiscountSourceAndType(t, bookingID)
require.True(t, exists)
assert.Equal(t, "campaign", source)
assert.Equal(t, "time_based", campaignType)
var timesRedeemed int
err := db.DB.QueryRow(context.Background(), `SELECT times_redeemed FROM discount_campaigns WHERE id = $1`, campaignID).Scan(&timesRedeemed)
require.NoError(t, err)
assert.Equal(t, 1, timesRedeemed)
var paymentAmount float64
err = db.DB.QueryRow(context.Background(), `
SELECT amount FROM payments WHERE booking_id = $1 AND payment_method = 'discount'
`, bookingID).Scan(&paymentAmount)
require.NoError(t, err)
assert.Equal(t, 5.00, paymentAmount)
}
func getDiscountSourceAndType(t *testing.T, bookingID string) (source, campaignType string, exists bool) {
t.Helper()
err := db.DB.QueryRow(context.Background(), `
SELECT discount_source, campaign_type FROM booking_discounts WHERE booking_id = $1
`, bookingID).Scan(&source, &campaignType)
if err != nil {
return "", "", false
}
return source, campaignType, true
}
// =============================================================================
// Per-User Milestone Tests
// =============================================================================
func TestDiscount_PerUserMilestone(t *testing.T) {
resetTestData(t)
seedDefaultWorkingHours(t)
milestoneValue := 10
milestoneType := "per_user_booking_count"
_ = createTestCampaign(t, "10th Visit Bonus", "milestone", 15.0, &milestoneType, nil, &milestoneValue, nil)
userID := createTestUser(t, 0)
serviceID := createTestService(t, 80.00)
for i := 0; i < 9; i++ {
startTime := time.Now().AddDate(0, -1, -i*7)
_ = createCompletedBooking(t, userID, serviceID, startTime, 80.00)
}
bookingID := createPendingBooking(t, userID, serviceID, time.Now().Add(24*time.Hour))
completeBooking(t, bookingID)
source, milestoneTypeResult, exists := getDiscountSourceAndMilestone(t, bookingID)
require.True(t, exists)
assert.Equal(t, "campaign", source)
assert.Equal(t, "per_user_booking_count", milestoneTypeResult)
var discountAmount float64
err := db.DB.QueryRow(context.Background(), `SELECT discount_amount FROM booking_discounts WHERE booking_id = $1`, bookingID).Scan(&discountAmount)
require.NoError(t, err)
assert.Equal(t, 12.00, discountAmount, "15% of £80 = £12")
bookingID2 := createPendingBooking(t, userID, serviceID, time.Now().Add(48*time.Hour))
completeBooking(t, bookingID2)
var discountCount int
err = db.DB.QueryRow(context.Background(), `SELECT COUNT(*) FROM booking_discounts WHERE booking_id = $1`, bookingID2).Scan(&discountCount)
require.NoError(t, err)
assert.Equal(t, 0, discountCount, "No second discount (dedup)")
}
func getDiscountSourceAndMilestone(t *testing.T, bookingID string) (source, milestoneType string, exists bool) {
t.Helper()
err := db.DB.QueryRow(context.Background(), `
SELECT discount_source, milestone_type FROM booking_discounts WHERE booking_id = $1
`, bookingID).Scan(&source, &milestoneType)
if err != nil {
return "", "", false
}
return source, milestoneType, true
}
// =============================================================================
// Global Milestone Tests
// =============================================================================
func TestDiscount_GlobalMilestone(t *testing.T) {
resetTestData(t)
seedDefaultWorkingHours(t)
milestoneValue := 5
milestoneType := "global_booking_count"
_ = createTestCampaign(t, "5th Customer Milestone", "milestone", 20.0, &milestoneType, nil, &milestoneValue, nil)
userID1 := createTestUser(t, 0)
userID2 := createTestUser(t, 0)
serviceID := createTestService(t, 50.00)
for i := 0; i < 4; i++ {
startTime := time.Now().AddDate(0, 0, -i-1)
_ = createCompletedBooking(t, userID1, serviceID, startTime, 50.00)
}
bookingID := createPendingBooking(t, userID2, serviceID, time.Now().Add(24*time.Hour))
completeBooking(t, bookingID)
_, milestoneTypeResult, exists := getDiscountSourceAndMilestone(t, bookingID)
require.True(t, exists)
assert.Equal(t, "global_booking_count", milestoneTypeResult)
var discountAmount float64
err := db.DB.QueryRow(context.Background(), `SELECT discount_amount FROM booking_discounts WHERE booking_id = $1`, bookingID).Scan(&discountAmount)
require.NoError(t, err)
assert.Equal(t, 10.00, discountAmount, "20% of £50 = £10")
}
// =============================================================================
// Anniversary Milestone Tests
// =============================================================================
func TestDiscount_AnniversaryMilestone(t *testing.T) {
resetTestData(t)
seedDefaultWorkingHours(t)
milestoneValue := 6
milestoneType := "anniversary"
milestoneUnit := "months"
_ = createTestCampaign(t, "6 Month Anniversary", "milestone", 10.0, &milestoneType, &milestoneUnit, &milestoneValue, nil)
userID := createTestUser(t, 0)
serviceID := createTestService(t, 60.00)
ctx := context.Background()
sevenMonthsAgo := time.Now().AddDate(0, -7, 0)
var firstBookingID string
err := db.DB.QueryRow(ctx, `
INSERT INTO bookings (user_id, start_time, status)
VALUES ($1, $2, 'completed')
RETURNING id
`, userID, sevenMonthsAgo).Scan(&firstBookingID)
require.NoError(t, err)
_, err = db.DB.Exec(ctx, `
INSERT INTO booking_services (booking_id, service_id, override_price)
VALUES ($1, $2, $3)
`, firstBookingID, serviceID, 60.00)
require.NoError(t, err)
bookingID := createPendingBooking(t, userID, serviceID, time.Now().Add(24*time.Hour))
completeBooking(t, bookingID)
_, milestoneTypeResult, exists := getDiscountSourceAndMilestone(t, bookingID)
require.True(t, exists)
assert.Equal(t, "anniversary", milestoneTypeResult)
bookingID2 := createPendingBooking(t, userID, serviceID, time.Now().Add(48*time.Hour))
completeBooking(t, bookingID2)
var discountCount int
err = db.DB.QueryRow(context.Background(), `SELECT COUNT(*) FROM booking_discounts WHERE booking_id = $1`, bookingID2).Scan(&discountCount)
require.NoError(t, err)
assert.Equal(t, 0, discountCount, "No second discount (dedup)")
}
// =============================================================================
// Loyalty Priority Tests
// =============================================================================
func TestDiscount_LoyaltyPriority(t *testing.T) {
resetTestData(t)
seedDefaultWorkingHours(t)
_ = createTestCampaign(t, "Summer Sale", "time_based", 5.0, nil, nil, nil, nil)
userID := createTestUser(t, 10)
ctx := context.Background()
_, err := db.DB.Exec(ctx, `
INSERT INTO loyalty_redemptions (user_id, stamps_redeemed, status, redeemed_at)
VALUES ($1, 10, 'pending', NOW())
`, userID)
require.NoError(t, err)
serviceID := createTestService(t, 100.00)
bookingID := createPendingBooking(t, userID, serviceID, time.Now().Add(24*time.Hour))
completeBooking(t, bookingID)
source, _, exists := getDiscountForBooking(t, bookingID)
require.True(t, exists)
assert.Equal(t, "loyalty", source)
var campaignDiscountCount int
err = db.DB.QueryRow(context.Background(), `
SELECT COUNT(*) FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'campaign'
`, bookingID).Scan(&campaignDiscountCount)
require.NoError(t, err)
assert.Equal(t, 0, campaignDiscountCount, "No campaign discount when loyalty applies")
}
// =============================================================================
// Zero Total Edge Case Tests
// =============================================================================
func TestDiscount_NoDiscountOnZeroTotal(t *testing.T) {
resetTestData(t)
seedDefaultWorkingHours(t)
userID := createTestUser(t, 10)
ctx := context.Background()
_, err := db.DB.Exec(ctx, `
INSERT INTO loyalty_redemptions (user_id, stamps_redeemed, status, redeemed_at)
VALUES ($1, 10, 'pending', NOW())
`, userID)
require.NoError(t, err)
var serviceID string
err = db.DB.QueryRow(ctx, `
INSERT INTO services (name, description, price, duration_minutes, is_active, minimum_age_required)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id
`, "Free Service", "A free service", 0.00, 60, true, 16).Scan(&serviceID)
require.NoError(t, err)
bookingID := createPendingBooking(t, userID, serviceID, time.Now().Add(24*time.Hour))
completeBooking(t, bookingID)
var paymentCount int
err = db.DB.QueryRow(context.Background(), `
SELECT COUNT(*) FROM payments WHERE booking_id = $1 AND payment_method = 'discount'
`, bookingID).Scan(&paymentCount)
require.NoError(t, err)
assert.Equal(t, 0, paymentCount, "No discount payment (total is 0)")
var redemptionStatus string
err = db.DB.QueryRow(context.Background(), `
SELECT status FROM loyalty_redemptions WHERE user_id = $1 AND status = 'pending'
`, userID).Scan(&redemptionStatus)
require.NoError(t, err)
assert.Equal(t, "pending", redemptionStatus, "Redemption stays pending")
assert.Equal(t, 10, getStamps(t, userID), "Stamps unchanged")
}
// =============================================================================
// Max Redemptions Tests
// =============================================================================
func TestDiscount_CampaignMaxRedemptions(t *testing.T) {
resetTestData(t)
seedDefaultWorkingHours(t)
maxRedemptions := 1
campaignID := createTestCampaign(t, "Limited Time Offer", "time_based", 10.0, nil, nil, nil, &maxRedemptions)
userID1 := createTestUser(t, 0)
userID2 := createTestUser(t, 0)
serviceID := createTestService(t, 50.00)
bookingID1 := createPendingBooking(t, userID1, serviceID, time.Now().Add(24*time.Hour))
completeBooking(t, bookingID1)
var discountCount1 int
err := db.DB.QueryRow(context.Background(), `SELECT COUNT(*) FROM booking_discounts WHERE booking_id = $1`, bookingID1).Scan(&discountCount1)
require.NoError(t, err)
assert.Equal(t, 1, discountCount1)
var timesRedeemed int
err = db.DB.QueryRow(context.Background(), `SELECT times_redeemed FROM discount_campaigns WHERE id = $1`, campaignID).Scan(&timesRedeemed)
require.NoError(t, err)
assert.Equal(t, 1, timesRedeemed)
bookingID2 := createPendingBooking(t, userID2, serviceID, time.Now().Add(48*time.Hour))
completeBooking(t, bookingID2)
var discountCount2 int
err = db.DB.QueryRow(context.Background(), `SELECT COUNT(*) FROM booking_discounts WHERE booking_id = $1`, bookingID2).Scan(&discountCount2)
require.NoError(t, err)
assert.Equal(t, 0, discountCount2, "No discount (max reached)")
}