Large manual tests corruption fix
This commit is contained in:
@@ -4,8 +4,8 @@ import (
|
||||
"crussell/db"
|
||||
"crussell/handlers/notifications"
|
||||
"crussell/internal/dav"
|
||||
"crussell/mw"
|
||||
"crussell/internal/validators"
|
||||
"crussell/mw"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -1240,7 +1240,6 @@ func CreateBookingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if req.StartTime.Before(time.Now()) {
|
||||
http.Error(w, "Start time cannot be in the past", http.StatusBadRequest)
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
// Validate booking fits within operating hours for regular users
|
||||
@@ -1279,8 +1278,6 @@ func CreateBookingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Get created by from context (if available)
|
||||
var createdBy *string
|
||||
if creatorID, ok := r.Context().Value(mw.UserIDKey).(string); ok {
|
||||
@@ -1761,7 +1758,6 @@ func ConfirmBookingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
tx, err := db.DB.Begin(r.Context())
|
||||
if err != nil {
|
||||
log.Printf("Failed to start transaction: %v", err)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
//go:build test
|
||||
// +build test
|
||||
|
||||
package bookings
|
||||
|
||||
// Package bookings contains tests for user-facing booking endpoints.
|
||||
//
|
||||
// Test Coverage:
|
||||
@@ -13,12 +15,6 @@
|
||||
// - GetBookingCalendarHandler: GET /api/bookings/calendar - Export bookings as ICS
|
||||
//
|
||||
// Validation: Tests cover patch test requirements, deposit rules, time slot conflicts.
|
||||
package bookings
|
||||
//go:build test
|
||||
// +build test
|
||||
|
||||
package bookings
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
@@ -38,7 +34,6 @@ import (
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
// setupTestDB replaces the global db.DB with a test pool and returns a cleanup function
|
||||
@@ -210,6 +205,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
// TestBookings_Create tests that a user can successfully create a new booking
|
||||
// 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()
|
||||
|
||||
@@ -273,6 +269,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
|
||||
// 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()
|
||||
|
||||
@@ -336,6 +333,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
// TestBookings_List tests that a user can retrieve their list of bookings.
|
||||
// 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()
|
||||
|
||||
@@ -391,6 +389,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
// TestBookings_List_FilterByStatus tests that booking list can be filtered
|
||||
// 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()
|
||||
|
||||
@@ -461,6 +460,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
|
||||
// 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()
|
||||
|
||||
@@ -515,6 +515,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
|
||||
// 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()
|
||||
|
||||
@@ -544,6 +545,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
// TestBookings_Get_AccessDenied tests that a user cannot access another user's
|
||||
// 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()
|
||||
|
||||
@@ -592,6 +594,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
// TestBookings_GetCalendar tests that a user can export their booking
|
||||
// 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()
|
||||
|
||||
@@ -653,6 +656,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
|
||||
// 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()
|
||||
|
||||
@@ -685,6 +689,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
|
||||
// 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()
|
||||
|
||||
@@ -750,6 +755,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
|
||||
// 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()
|
||||
|
||||
@@ -811,6 +817,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
|
||||
// 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()
|
||||
|
||||
@@ -848,6 +855,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
// TestBookings_Delete tests that a user can delete (cancel) their booking.
|
||||
// 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()
|
||||
|
||||
@@ -902,6 +910,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
// an associated payment requires a reason (client_cancelled). Without a reason,
|
||||
// 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()
|
||||
|
||||
@@ -971,6 +980,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
|
||||
// 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()
|
||||
|
||||
@@ -1004,6 +1014,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
// TestBookings_Unauthorized tests that all booking endpoints require
|
||||
// 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()
|
||||
|
||||
@@ -1120,6 +1131,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
|
||||
// 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()
|
||||
|
||||
@@ -1161,6 +1173,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
|
||||
// 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()
|
||||
|
||||
@@ -1190,6 +1203,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
|
||||
// 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()
|
||||
|
||||
@@ -1230,6 +1244,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
// TestBookings_Create_Within48HourDepositRequired tests that when a booking
|
||||
// is made within 48 hours and the user has deposits_required > 0, the booking
|
||||
// should have deposit_required=true. With deposits_required=0, no deposit needed.
|
||||
func TestBookings_Create_Within48HourDepositRequired(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
@@ -1279,6 +1294,7 @@ func parseResponseBody(w *httptest.ResponseRecorder, dest interface{}) error {
|
||||
// TestBookings_Create_MultipleServices verifies that a booking can include
|
||||
// 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()
|
||||
|
||||
@@ -1343,6 +1359,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()
|
||||
|
||||
@@ -1386,6 +1403,7 @@ var _ = mw.UserIDKey
|
||||
// TestBookings_GetCalendar_ValidICS validates that the ICS calendar export
|
||||
// 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()
|
||||
|
||||
@@ -1459,6 +1477,7 @@ var _ = mw.UserIDKey
|
||||
// TestUserCancelBooking_ConfirmedCreatesNotification verifies that when a
|
||||
// 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()
|
||||
|
||||
@@ -1530,6 +1549,7 @@ var _ = mw.UserIDKey
|
||||
// TestUserCancelBooking_PendingNoNotification verifies that cancelling a
|
||||
// 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()
|
||||
|
||||
@@ -1593,11 +1613,10 @@ var _ = mw.UserIDKey
|
||||
// Transaction and Error Handling Tests
|
||||
// =============================================================================
|
||||
|
||||
// TestUserCancelBooking_TransactionIntegrity verifies that if any part of the
|
||||
// cancellation transaction fails, the booking status is NOT changed (rollback behavior)
|
||||
// TestUserCancelBooking_TransactionIntegrity tests that the cancellation
|
||||
// transaction properly commits - verifying the booking status actually changes
|
||||
// after a successful cancellation request.
|
||||
func TestUserCancelBooking_TransactionIntegrity(t *testing.T) {
|
||||
cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
@@ -1674,6 +1693,7 @@ var _ = mw.UserIDKey
|
||||
// TestCreateEditRequest verifies that a user can request an edit to their
|
||||
// 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()
|
||||
|
||||
|
||||
@@ -373,15 +373,19 @@ func AdminEditBookingHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Clear any pending edit requests for this booking (admin edit takes priority)
|
||||
_, err = db.DB.Exec(r.Context(), `
|
||||
DELETE FROM booking_edit_requests
|
||||
DELETE FROM booking_edit_requests
|
||||
WHERE booking_id = $1
|
||||
`, bookingID)
|
||||
if err != nil {
|
||||
log.Printf("Failed to clear edit requests for booking %s: %v", bookingID, err)
|
||||
// Don't fail the request, just log the error
|
||||
}
|
||||
|
||||
// Return warnings if any
|
||||
if len(warnings) > 0 {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"message": "Booking updated",
|
||||
"message": "Booking updated",
|
||||
"warnings": warnings,
|
||||
})
|
||||
return
|
||||
@@ -537,7 +541,6 @@ func AdminCreateBookingForUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if isClosed {
|
||||
http.Error(w, "Cannot book during holiday hours when the salon is closed", http.StatusConflict)
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
// Check for overlapping confirmed/in_progress/completed bookings
|
||||
@@ -555,7 +558,6 @@ func AdminCreateBookingForUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
tx, err := db.DB.Begin(r.Context())
|
||||
if err != nil {
|
||||
log.Printf("Failed to start transaction: %v", err)
|
||||
@@ -700,18 +702,19 @@ func AdminCreateBookingForUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// BookingEditRequest represents a user's request to edit a booking
|
||||
type BookingEditRequest struct {
|
||||
ID string `json:"id"`
|
||||
BookingID string `json:"booking_id"`
|
||||
RequestedBy string `json:"requested_by"`
|
||||
NewStartTime *time.Time `json:"new_start_time,omitempty"`
|
||||
NewServices []string `json:"new_services"`
|
||||
Notes *string `json:"notes,omitempty"`
|
||||
HasOverrides bool `json:"has_overrides"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
ID string `json:"id"`
|
||||
BookingID string `json:"booking_id"`
|
||||
RequestedBy string `json:"requested_by"`
|
||||
NewStartTime *time.Time `json:"new_start_time,omitempty"`
|
||||
NewServices []string `json:"new_services"`
|
||||
Notes *string `json:"notes,omitempty"`
|
||||
HasOverrides bool `json:"has_overrides"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
// Joined fields
|
||||
Booking *Booking `json:"booking,omitempty"`
|
||||
Booking *Booking `json:"booking,omitempty"`
|
||||
User *UserSummary `json:"user,omitempty"`
|
||||
}
|
||||
|
||||
// DeleteEditRequestHandler allows a user to delete/cancel their pending edit request
|
||||
func DeleteEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
bookingID := chi.URLParam(r, "id")
|
||||
@@ -755,7 +758,7 @@ func DeleteEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Delete the edit request for this booking
|
||||
res, err := tx.Exec(r.Context(), `
|
||||
DELETE FROM booking_edit_requests
|
||||
DELETE FROM booking_edit_requests
|
||||
WHERE booking_id = $1 AND requested_by = $2
|
||||
`, bookingID, userID)
|
||||
if err != nil {
|
||||
@@ -790,7 +793,6 @@ func DeleteEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
|
||||
// RequestEditHandler allows a user to request an edit to their booking
|
||||
func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
|
||||
bookingID := chi.URLParam(r, "id")
|
||||
@@ -855,7 +857,7 @@ func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if len(req.NewServices) > 0 {
|
||||
var overrideCount int
|
||||
err = db.DB.QueryRow(r.Context(), `
|
||||
SELECT COUNT(*) FROM booking_services
|
||||
SELECT COUNT(*) FROM booking_services
|
||||
WHERE booking_id = $1 AND (override_price IS NOT NULL OR override_duration_minutes IS NOT NULL)
|
||||
`, bookingID).Scan(&overrideCount)
|
||||
if err != nil {
|
||||
@@ -879,7 +881,7 @@ func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Delete any existing edit request for this booking (upsert behavior)
|
||||
_, err = tx.Exec(r.Context(), `
|
||||
DELETE FROM booking_edit_requests
|
||||
DELETE FROM booking_edit_requests
|
||||
WHERE booking_id = $1 AND requested_by = $2
|
||||
`, bookingID, userID)
|
||||
if err != nil {
|
||||
@@ -910,7 +912,6 @@ func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Delete existing admin notification for edit_request before creating new one (refreshes timestamp)
|
||||
_, err = tx.Exec(r.Context(), `
|
||||
DELETE FROM admin_notifications
|
||||
@@ -937,8 +938,8 @@ func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if currentStatus == "pending" {
|
||||
// Acknowledge existing pending_booking notification
|
||||
_, err = tx.Exec(r.Context(), `
|
||||
UPDATE admin_notifications
|
||||
SET acknowledged_at = NOW()
|
||||
UPDATE admin_notifications
|
||||
SET acknowledged_at = NOW()
|
||||
WHERE booking_id = $1 AND reason = 'pending_booking' AND acknowledged_at IS NULL
|
||||
`, bookingID)
|
||||
if err != nil {
|
||||
@@ -969,7 +970,7 @@ func RequestEditHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// AdminListEditRequestsHandler returns all edit requests
|
||||
func AdminListEditRequestsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
baseQuery := `
|
||||
SELECT ber.id, ber.booking_id, ber.requested_by, ber.new_start_time,
|
||||
SELECT ber.id, ber.booking_id, ber.requested_by, ber.new_start_time,
|
||||
ber.new_services, ber.notes, ber.has_overrides, ber.updated_at,
|
||||
b.start_time as original_start_time, b.status as booking_status,
|
||||
u.fn as user_name
|
||||
@@ -1007,7 +1008,6 @@ func AdminListEditRequestsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var origStartTime time.Time
|
||||
var bookingStatus string
|
||||
var userName string
|
||||
var newServices []string
|
||||
|
||||
err := rows.Scan(
|
||||
&req.ID,
|
||||
@@ -1071,18 +1071,17 @@ func AdminApproveEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Get the edit request
|
||||
var bookingID string
|
||||
var newStartTime *time.Time
|
||||
var newServices []string
|
||||
var notes *string
|
||||
var hasOverrides bool
|
||||
err = tx.QueryRow(r.Context(), `
|
||||
SELECT booking_id, new_start_time, new_services, notes, has_overrides
|
||||
FROM booking_edit_requests
|
||||
SELECT booking_id, new_start_time, new_services, notes, has_overrides
|
||||
FROM booking_edit_requests
|
||||
WHERE id = $1
|
||||
`, requestID).Scan(&bookingID, &newStartTime, pq.Array(&newServices), ¬es, &hasOverrides)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
http.Error(w, "Edit request not found", http.StatusNotFound)
|
||||
http.Error(w, "Edit request not found or already processed", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
log.Printf("Failed to get edit request %s: %v", requestID, err)
|
||||
@@ -1229,6 +1228,7 @@ func AdminApproveEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if err := tx.Commit(r.Context()); err != nil {
|
||||
log.Printf("Failed to commit: %v", err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
@@ -1272,7 +1272,7 @@ func AdminRejectEditRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Delete the edit request
|
||||
_, err = tx.Exec(r.Context(), `
|
||||
DELETE FROM booking_edit_requests
|
||||
DELETE FROM booking_edit_requests
|
||||
WHERE id = $1
|
||||
`, requestID)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user