Fix nbtb, add tests

This commit is contained in:
2026-03-04 20:11:51 +00:00
parent 29b9776a93
commit 7c3f922725
7 changed files with 522 additions and 12 deletions
+139
View File
@@ -3012,4 +3012,143 @@ func TestBookings_Edit_OpenDay_UserAllowed(t *testing.T) {
if w.Code != http.StatusOK {
t.Errorf("expected status 200 for open day edit, got %d. body: %s", w.Code, w.Body.String())
}
}
// =============================================================================
// Time Blocker Tests for User Bookings
// =============================================================================
// 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()
seedDefaultWorkingHours(t)
userID, err := fixtures.CreateTestUser(db.DB)
if err != nil {
t.Fatalf("failed to create test user: %v", err)
}
defer fixtures.DeleteUser(db.DB, userID)
// Set deposits_required=0 to avoid 48h advance booking requirement
_, err = db.DB.Exec(context.Background(), "UPDATE users SET deposits_required = 0 WHERE id = $1", userID)
if err != nil {
t.Fatalf("failed to set deposits_required: %v", err)
}
serviceID, err := fixtures.CreateTestService(db.DB)
if err != nil {
t.Fatalf("failed to create test service: %v", err)
}
defer fixtures.DeleteService(db.DB, serviceID)
// Create a time blocker for a specific time
ukLocation, _ := time.LoadLocation("Europe/London")
blockerTime := time.Date(2099, 12, 31, 10, 0, 0, 0, ukLocation)
_, err = db.DB.Exec(context.Background(), `
INSERT INTO time_blockers (start_time, duration_minutes, description, created_by)
VALUES ($1, 60, 'Staff meeting', NULL)
`, blockerTime)
if err != nil {
t.Fatalf("failed to create time blocker: %v", err)
}
token := jwt.GenerateUserToken(userID)
// User tries to create booking overlapping the blocker
req := CreateBookingRequest{
StartTime: blockerTime,
ServiceIDs: []string{serviceID},
}
handler := http.HandlerFunc(CreateBookingHandler)
w := makeRequest(handler, "POST", "/api/bookings", req, token)
// User should get 409 Conflict (not 201 Created)
if w.Code != http.StatusConflict {
t.Errorf("expected status 409, got %d. body: %s", w.Code, w.Body.String())
}
// Verify error message mentions the blocker
if !bytes.Contains(w.Body.Bytes(), []byte("blocked")) {
t.Errorf("expected error message to mention 'blocked', got: %s", w.Body.String())
}
// Verify NO booking was created
var count int
err = db.DB.QueryRow(context.Background(),
"SELECT COUNT(*) FROM bookings WHERE user_id = $1", userID).Scan(&count)
if err != nil {
t.Fatalf("failed to query bookings: %v", err)
}
if count != 0 {
t.Errorf("expected 0 bookings (user should be blocked), got %d", count)
}
}
// 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()
seedDefaultWorkingHours(t)
userID, err := fixtures.CreateTestUser(db.DB)
if err != nil {
t.Fatalf("failed to create test user: %v", err)
}
defer fixtures.DeleteUser(db.DB, userID)
// Set deposits_required=0 to avoid 48h advance booking requirement
_, err = db.DB.Exec(context.Background(), "UPDATE users SET deposits_required = 0 WHERE id = $1", userID)
if err != nil {
t.Fatalf("failed to set deposits_required: %v", err)
}
serviceID, err := fixtures.CreateTestService(db.DB)
if err != nil {
t.Fatalf("failed to create test service: %v", err)
}
defer fixtures.DeleteService(db.DB, serviceID)
// Create a booking first
bookingID, err := fixtures.CreateTestBooking(db.DB, userID, serviceID)
if err != nil {
t.Fatalf("failed to create test booking: %v", err)
}
defer fixtures.DeleteBooking(db.DB, bookingID)
// Create a time blocker for a specific time
ukLocation, _ := time.LoadLocation("Europe/London")
blockerTime := time.Date(2099, 12, 31, 10, 0, 0, 0, ukLocation)
_, err = db.DB.Exec(context.Background(), `
INSERT INTO time_blockers (start_time, duration_minutes, description, created_by)
VALUES ($1, 60, 'Staff meeting', NULL)
`, blockerTime)
if err != nil {
t.Fatalf("failed to create time blocker: %v", err)
}
token := jwt.GenerateUserToken(userID)
// User tries to edit booking to overlap the blocker
req := EditBookingRequest{
StartTime: blockerTime,
}
handler := http.HandlerFunc(EditBookingHandler)
w := makeRequest(handler, "PUT", "/api/bookings/"+bookingID, req, token)
// User should get 409 Conflict
if w.Code != http.StatusConflict {
t.Errorf("expected status 409, got %d. body: %s", w.Code, w.Body.String())
}
// Verify error message mentions the blocker
if !bytes.Contains(w.Body.Bytes(), []byte("blocked")) {
t.Errorf("expected error message to mention 'blocked', got: %s", w.Body.String())
}
}