diff --git a/backend/handlers/bookings/edit_requests_test.go b/backend/handlers/bookings/edit_requests_test.go index f027f05..7296018 100644 --- a/backend/handlers/bookings/edit_requests_test.go +++ b/backend/handlers/bookings/edit_requests_test.go @@ -2387,3 +2387,93 @@ func TestAdminApproveEditRequest_ClosedExceptionalHours_Rejected(t *testing.T) { t.Errorf("expected status 409 (conflict), got %d. body: %s", w.Code, w.Body.String()) } } + +// TestRequestEditHandler_PastClosing_Blocked verifies that proposing a time +// whose end extends past closing hours returns a 400 error. +// Uses the bookings test DB hours (all days 08:00-20:00). +func TestRequestEditHandler_PastClosing_Blocked(t *testing.T) { + ctx, tx := testutils.SetupTestTx(t) + _, _, bookingID, token := setupEditRequestTest(t, ctx, tx) + + // Test DB seed data: all days 08:00-20:00. Propose 19:30 which ends at + // 20:30 for a 60-min service — past the 20:00 closing. + nextDay := clock.Now().In(clock.London).AddDate(0, 0, 1) + pastClose := time.Date(nextDay.Year(), nextDay.Month(), nextDay.Day(), 19, 30, 0, 0, clock.London).UTC() + + handler := http.HandlerFunc(RequestEditHandler) + w := makeRequest(handler, "POST", "/api/bookings/"+bookingID+"/edit-request", map[string]interface{}{ + "new_start_time": pastClose.Format(time.RFC3339), + }, token, ctx) + + if w.Code != http.StatusBadRequest { + t.Errorf("expected 400 for past-closing time, got %d: %s", w.Code, w.Body.String()) + } +} + +// TestRequestEditHandler_StagedHoursClosed_Blocked verifies that proposing a +// time on a day that becomes closed under a staged default hours change is +// blocked with a 400 error. +func TestRequestEditHandler_StagedHoursClosed_Blocked(t *testing.T) { + ctx, tx := testutils.SetupTestTx(t) + _, serviceID, bookingID, token := setupEditRequestTest(t, ctx, tx) + _ = serviceID + + // Insert a staged change closing a weekday that is normally open + adminID, err := fixtures.CreateTestAdminUser(tx) + if err != nil { + t.Fatalf("failed to create admin user: %v", err) + } + defer fixtures.DeleteUser(tx, adminID) + + tomorrow := clock.Now().In(clock.London).AddDate(0, 0, 1).Format("2006-01-02") + // Stage: all days closed + hoursJSON := `[{"weekday":0,"startTime":"00:00","endTime":"00:00","isOpen":false},{"weekday":1,"startTime":"00:00","endTime":"00:00","isOpen":false},{"weekday":2,"startTime":"00:00","endTime":"00:00","isOpen":false},{"weekday":3,"startTime":"00:00","endTime":"00:00","isOpen":false},{"weekday":4,"startTime":"00:00","endTime":"00:00","isOpen":false},{"weekday":5,"startTime":"00:00","endTime":"00:00","isOpen":false},{"weekday":6,"startTime":"00:00","endTime":"00:00","isOpen":false}]` + _, err = tx.Exec(ctx, ` + INSERT INTO default_hours_scheduled_changes (effective_date, created_by, hours) + VALUES ($1, $2, $3) + `, tomorrow, adminID, hoursJSON) + if err != nil { + t.Fatalf("failed to insert staged change: %v", err) + } + + // Propose a time on a day that is now closed under the staged change + londonNow := clock.Now().In(clock.London) + target := londonNow.AddDate(0, 0, 1) + for target.Weekday() == time.Sunday { + target = target.AddDate(0, 0, 1) + } + proposedTime := time.Date(target.Year(), target.Month(), target.Day(), 10, 0, 0, 0, clock.London).UTC() + + handler := http.HandlerFunc(RequestEditHandler) + w := makeRequest(handler, "POST", "/api/bookings/"+bookingID+"/edit-request", map[string]interface{}{ + "new_start_time": proposedTime.Format(time.RFC3339), + }, token, ctx) + + if w.Code != http.StatusBadRequest { + t.Errorf("expected 400 for staged-closed day, got %d: %s", w.Code, w.Body.String()) + } +} + +// TestRequestEditHandler_ValidTime_Succeeds verifies that proposing a time +// within open hours succeeds (returns 201). +func TestRequestEditHandler_ValidTime_Succeeds(t *testing.T) { + ctx, tx := testutils.SetupTestTx(t) + _, _, bookingID, token := setupEditRequestTest(t, ctx, tx) + + // Propose 10:00 on a weekday that is open + londonNow := clock.Now().In(clock.London) + target := londonNow.AddDate(0, 0, 1) + for target.Weekday() == time.Sunday { + target = target.AddDate(0, 0, 1) + } + validTime := time.Date(target.Year(), target.Month(), target.Day(), 10, 0, 0, 0, clock.London).UTC() + + handler := http.HandlerFunc(RequestEditHandler) + w := makeRequest(handler, "POST", "/api/bookings/"+bookingID+"/edit-request", map[string]interface{}{ + "new_start_time": validTime.Format(time.RFC3339), + }, token, ctx) + + if w.Code != http.StatusCreated { + t.Errorf("expected 201 for valid time, got %d: %s", w.Code, w.Body.String()) + } +}