Fixed user cancelation logic
This commit is contained in:
@@ -816,6 +816,285 @@ func TestAdminBookings_Cancel_NotFound(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestAdminBookings_Cancel_PendingStatus verifies that admin cancellations of pending bookings
|
||||
// do NOT create admin notifications (pending cancellations don't require staff attention).
|
||||
func TestAdminBookings_Cancel_PendingStatus(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
adminID, err := fixtures.CreateTestAdminUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, adminID)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, userID)
|
||||
|
||||
serviceID, err := fixtures.CreateTestService(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test service: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteService(db.DB, serviceID)
|
||||
|
||||
ctx := context.Background()
|
||||
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)
|
||||
|
||||
// Booking stays in 'pending' status (no confirmation)
|
||||
|
||||
handler := http.HandlerFunc(bookings.AdminCancelBookingHandler)
|
||||
w := makeAdminRequest(handler, "POST", "/api/admin/bookings/"+bookingID+"/cancel", nil)
|
||||
|
||||
if w.Code != http.StatusNoContent {
|
||||
t.Errorf("expected status 204, got %d. body: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
// Verify status changed to we_cancelled
|
||||
var dbStatus string
|
||||
err = db.DB.QueryRow(ctx,
|
||||
"SELECT status FROM bookings WHERE id = $1", bookingID).Scan(&dbStatus)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query booking: %v", err)
|
||||
}
|
||||
if dbStatus != "we_cancelled" {
|
||||
t.Errorf("expected status 'we_cancelled' in DB, got %s", dbStatus)
|
||||
}
|
||||
|
||||
// Verify NO admin notification was created for pending cancellations
|
||||
var notifCount int
|
||||
err = db.DB.QueryRow(ctx,
|
||||
`SELECT COUNT(*) FROM admin_notifications WHERE booking_id = $1`,
|
||||
bookingID).Scan(¬ifCount)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query notifications: %v", err)
|
||||
}
|
||||
if notifCount != 0 {
|
||||
t.Errorf("expected 0 admin notifications for pending cancel, got %d", notifCount)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAdminBookings_Cancel_ConfirmedCreatesNotification verifies that cancelling a confirmed
|
||||
// booking creates an admin notification for staff awareness.
|
||||
func TestAdminBookings_Cancel_ConfirmedCreatesNotification(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
adminID, err := fixtures.CreateTestAdminUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, adminID)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, userID)
|
||||
|
||||
serviceID, err := fixtures.CreateTestService(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test service: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteService(db.DB, serviceID)
|
||||
|
||||
ctx := context.Background()
|
||||
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)
|
||||
|
||||
// Confirm the booking
|
||||
_, err = db.DB.Exec(ctx, "UPDATE bookings SET status = 'confirmed' WHERE id = $1", bookingID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to confirm booking: %v", err)
|
||||
}
|
||||
|
||||
handler := http.HandlerFunc(bookings.AdminCancelBookingHandler)
|
||||
w := makeAdminRequest(handler, "POST", "/api/admin/bookings/"+bookingID+"/cancel", nil)
|
||||
|
||||
if w.Code != http.StatusNoContent {
|
||||
t.Errorf("expected status 204, got %d. body: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
// Verify status changed to we_cancelled
|
||||
var dbStatus string
|
||||
err = db.DB.QueryRow(ctx,
|
||||
"SELECT status FROM bookings WHERE id = $1", bookingID).Scan(&dbStatus)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query booking: %v", err)
|
||||
}
|
||||
if dbStatus != "we_cancelled" {
|
||||
t.Errorf("expected status 'we_cancelled' in DB, got %s", dbStatus)
|
||||
}
|
||||
|
||||
// Verify admin notification WAS created for confirmed->cancelled
|
||||
var notifCount int
|
||||
err = db.DB.QueryRow(ctx,
|
||||
`SELECT COUNT(*) FROM admin_notifications WHERE booking_id = $1 AND reason = 'cancelled_booking'`,
|
||||
bookingID).Scan(¬ifCount)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query notifications: %v", err)
|
||||
}
|
||||
if notifCount != 1 {
|
||||
t.Errorf("expected 1 admin notification for confirmed cancel, got %d", notifCount)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAdminBookings_Cancel_InProgressStatus verifies cancellation of in-progress bookings.
|
||||
func TestAdminBookings_Cancel_InProgressStatus(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
adminID, err := fixtures.CreateTestAdminUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, adminID)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, userID)
|
||||
|
||||
serviceID, err := fixtures.CreateTestService(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test service: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteService(db.DB, serviceID)
|
||||
|
||||
ctx := context.Background()
|
||||
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)
|
||||
|
||||
// Set booking to in-progress status
|
||||
_, err = db.DB.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to set in_progress status: %v", err)
|
||||
}
|
||||
|
||||
handler := http.HandlerFunc(bookings.AdminCancelBookingHandler)
|
||||
w := makeAdminRequest(handler, "POST", "/api/admin/bookings/"+bookingID+"/cancel", nil)
|
||||
|
||||
if w.Code != http.StatusNoContent {
|
||||
t.Errorf("expected status 204, got %d. body: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
// Verify status changed to we_cancelled
|
||||
var dbStatus string
|
||||
err = db.DB.QueryRow(ctx,
|
||||
"SELECT status FROM bookings WHERE id = $1", bookingID).Scan(&dbStatus)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query booking: %v", err)
|
||||
}
|
||||
if dbStatus != "we_cancelled" {
|
||||
t.Errorf("expected status 'we_cancelled' in DB, got %s", dbStatus)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAdminBookings_Cancel_AlreadyCancelledRejectsCancellation verifies that attempting to
|
||||
// cancel an already-cancelled booking returns 404 Not Found (idempotency guard).
|
||||
func TestAdminBookings_Cancel_AlreadyCancelledRejectsCancellation(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
adminID, err := fixtures.CreateTestAdminUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, adminID)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, userID)
|
||||
|
||||
serviceID, err := fixtures.CreateTestService(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test service: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteService(db.DB, serviceID)
|
||||
|
||||
ctx := context.Background()
|
||||
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)
|
||||
|
||||
// Set to already cancelled
|
||||
_, err = db.DB.Exec(ctx, "UPDATE bookings SET status = 'we_cancelled' WHERE id = $1", bookingID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to set we_cancelled status: %v", err)
|
||||
}
|
||||
|
||||
// Try to cancel again
|
||||
handler := http.HandlerFunc(bookings.AdminCancelBookingHandler)
|
||||
w := makeAdminRequest(handler, "POST", "/api/admin/bookings/"+bookingID+"/cancel", nil)
|
||||
|
||||
if w.Code != http.StatusNotFound {
|
||||
t.Errorf("expected status 404 for already-cancelled booking, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAdminBookings_Cancel_CompletedRejectsCancellation verifies that attempting to cancel
|
||||
// a completed booking returns 404 Not Found (cannot cancel finished appointments).
|
||||
func TestAdminBookings_Cancel_CompletedRejectsCancellation(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
adminID, err := fixtures.CreateTestAdminUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, adminID)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, userID)
|
||||
|
||||
serviceID, err := fixtures.CreateTestService(db.DB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test service: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteService(db.DB, serviceID)
|
||||
|
||||
ctx := context.Background()
|
||||
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)
|
||||
|
||||
// Set to completed
|
||||
_, err = db.DB.Exec(ctx, "UPDATE bookings SET status = 'completed' WHERE id = $1", bookingID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to set completed status: %v", err)
|
||||
}
|
||||
|
||||
// Try to cancel
|
||||
handler := http.HandlerFunc(bookings.AdminCancelBookingHandler)
|
||||
w := makeAdminRequest(handler, "POST", "/api/admin/bookings/"+bookingID+"/cancel", nil)
|
||||
|
||||
if w.Code != http.StatusNotFound {
|
||||
t.Errorf("expected status 404 for completed booking, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Non-Admin Tests
|
||||
// =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user