refactor(backend): update test files for PoolProxy and per-test transactions
Migrate all test files from SetupTestDB/db.DB pattern to per-test transactions: - Replace SetupTestDB(t) with SetupTestTx(t) for context + transaction - Replace db.DB.Query/QueryRow/Exec with tx.Query/QueryRow/Exec - Replace context.Background() with context from SetupTestTx - Replace defer rows.Close() pattern with explicit rows.Close() - Add testdb.SeedBaseline(pool) to all TestMain functions - Wire db.Conn = db.NewPoolProxy(pool) in all TestMain functions Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -19,7 +19,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"crussell/db"
|
||||
"crussell/testutils"
|
||||
"crussell/mw"
|
||||
"crussell/testutils/fixtures"
|
||||
@@ -29,7 +28,7 @@ import (
|
||||
|
||||
// makeAdminReserveRequest creates a request with admin context for admin reserve slot handler
|
||||
// It sets mw.UserIDKey and mw.UserRoleKey to "admin" in the context
|
||||
func makeAdminReserveRequest(handler http.Handler, body interface{}, adminID string) *httptest.ResponseRecorder {
|
||||
func makeAdminReserveRequest(handler http.Handler, body interface{}, adminID string, requestCtx ...context.Context) *httptest.ResponseRecorder {
|
||||
var req *http.Request
|
||||
if body != nil {
|
||||
bodyBytes, _ := json.Marshal(body)
|
||||
@@ -39,8 +38,13 @@ func makeAdminReserveRequest(handler http.Handler, body interface{}, adminID str
|
||||
req = httptest.NewRequest("POST", "/api/admin/bookings/reserve", nil)
|
||||
}
|
||||
|
||||
baseCtx := req.Context()
|
||||
if len(requestCtx) > 0 {
|
||||
baseCtx = requestCtx[0]
|
||||
}
|
||||
|
||||
rctx := chi.NewRouteContext()
|
||||
ctx := context.WithValue(req.Context(), chi.RouteCtxKey, rctx)
|
||||
ctx := context.WithValue(baseCtx, chi.RouteCtxKey, rctx)
|
||||
|
||||
ctx = context.WithValue(ctx, mw.UserIDKey, adminID)
|
||||
ctx = context.WithValue(ctx, mw.UserRoleKey, "admin")
|
||||
@@ -60,15 +64,15 @@ func makeAdminReserveRequest(handler http.Handler, body interface{}, adminID str
|
||||
// create a walk-in reservation with a valid duration. The test verifies
|
||||
// the reservation is created in the database with the correct duration.
|
||||
func TestAdminReserveSlot_WalkIn_Success(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
adminID, err := fixtures.CreateTestAdminUser(db.DB)
|
||||
adminID, err := fixtures.CreateTestAdminUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, adminID)
|
||||
defer fixtures.DeleteUser(tx, adminID)
|
||||
|
||||
tomorrow := time.Now().Add(24 * time.Hour)
|
||||
now := time.Date(tomorrow.Year(), tomorrow.Month(), tomorrow.Day(), 12, 0, 0, 0, tomorrow.Location())
|
||||
@@ -80,7 +84,7 @@ func TestAdminReserveSlot_WalkIn_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
handler := http.HandlerFunc(AdminReserveSlotHandler)
|
||||
w := makeAdminReserveRequest(handler, req, adminID)
|
||||
w := makeAdminReserveRequest(handler, req, adminID, ctx)
|
||||
|
||||
if w.Code != http.StatusCreated {
|
||||
t.Errorf("expected status 201, got %d. body: %s", w.Code, w.Body.String())
|
||||
@@ -98,7 +102,7 @@ func TestAdminReserveSlot_WalkIn_Success(t *testing.T) {
|
||||
|
||||
// Verify time_blocker was created with correct description pattern
|
||||
var desc string
|
||||
err = db.DB.QueryRow(context.Background(),
|
||||
err = tx.QueryRow(ctx,
|
||||
"SELECT description FROM time_blockers WHERE description LIKE 'RESERVATION:admin:walkin:%'",
|
||||
).Scan(&desc)
|
||||
if err != nil {
|
||||
@@ -118,29 +122,29 @@ func TestAdminReserveSlot_WalkIn_Success(t *testing.T) {
|
||||
// create a call-in reservation with valid service IDs. The test verifies
|
||||
// the reservation duration matches the service duration.
|
||||
func TestAdminReserveSlot_CallIn_Success(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
adminID, err := fixtures.CreateTestAdminUser(db.DB)
|
||||
adminID, err := fixtures.CreateTestAdminUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, adminID)
|
||||
defer fixtures.DeleteUser(tx, adminID)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, userID)
|
||||
defer fixtures.DeleteUser(tx, userID)
|
||||
|
||||
serviceID, err := fixtures.CreateTestService(db.DB)
|
||||
serviceID, err := fixtures.CreateTestService(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test service: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteService(db.DB, serviceID)
|
||||
defer fixtures.DeleteService(tx, serviceID)
|
||||
|
||||
_, err = db.DB.Exec(context.Background(), "UPDATE services SET duration_minutes = 30 WHERE id = $1", serviceID)
|
||||
_, err = tx.Exec(ctx, "UPDATE services SET duration_minutes = 30 WHERE id = $1", serviceID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to update service duration: %v", err)
|
||||
}
|
||||
@@ -157,7 +161,7 @@ func TestAdminReserveSlot_CallIn_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
handler := http.HandlerFunc(AdminReserveSlotHandler)
|
||||
w := makeAdminReserveRequest(handler, req, adminID)
|
||||
w := makeAdminReserveRequest(handler, req, adminID, ctx)
|
||||
|
||||
if w.Code != http.StatusCreated {
|
||||
t.Errorf("expected status 201, got %d. body: %s", w.Code, w.Body.String())
|
||||
@@ -175,7 +179,7 @@ func TestAdminReserveSlot_CallIn_Success(t *testing.T) {
|
||||
|
||||
// Verify time_blocker was created with correct description pattern
|
||||
var desc string
|
||||
err = db.DB.QueryRow(context.Background(),
|
||||
err = tx.QueryRow(ctx,
|
||||
"SELECT description FROM time_blockers WHERE description LIKE 'RESERVATION:admin:callin:%'",
|
||||
).Scan(&desc)
|
||||
if err != nil {
|
||||
@@ -194,15 +198,16 @@ func TestAdminReserveSlot_CallIn_Success(t *testing.T) {
|
||||
// TestAdminReserveSlot_WalkIn_MissingDuration tests that walk-in reservations
|
||||
// fail with HTTP 400 when duration_minutes is missing or zero.
|
||||
func TestAdminReserveSlot_WalkIn_MissingDuration(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
|
||||
adminID, err := fixtures.CreateTestAdminUser(db.DB)
|
||||
adminID, err := fixtures.CreateTestAdminUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, adminID)
|
||||
defer fixtures.DeleteUser(tx, adminID)
|
||||
|
||||
now := time.Now()
|
||||
req := AdminReserveSlotRequest{
|
||||
@@ -212,7 +217,7 @@ func TestAdminReserveSlot_WalkIn_MissingDuration(t *testing.T) {
|
||||
}
|
||||
|
||||
handler := http.HandlerFunc(AdminReserveSlotHandler)
|
||||
w := makeAdminReserveRequest(handler, req, adminID)
|
||||
w := makeAdminReserveRequest(handler, req, adminID, ctx)
|
||||
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("expected status 400, got %d. body: %s", w.Code, w.Body.String())
|
||||
@@ -232,15 +237,16 @@ func TestAdminReserveSlot_WalkIn_MissingDuration(t *testing.T) {
|
||||
// TestAdminReserveSlot_CallIn_MissingServices tests that call-in reservations
|
||||
// fail with HTTP 400 when service_ids is empty.
|
||||
func TestAdminReserveSlot_CallIn_MissingServices(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
|
||||
adminID, err := fixtures.CreateTestAdminUser(db.DB)
|
||||
adminID, err := fixtures.CreateTestAdminUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, adminID)
|
||||
defer fixtures.DeleteUser(tx, adminID)
|
||||
|
||||
tomorrow := time.Now().Add(24 * time.Hour).Truncate(time.Second)
|
||||
tomorrow = time.Date(tomorrow.Year(), tomorrow.Month(), tomorrow.Day(), 10, 0, 0, 0, tomorrow.Location())
|
||||
@@ -253,7 +259,7 @@ func TestAdminReserveSlot_CallIn_MissingServices(t *testing.T) {
|
||||
}
|
||||
|
||||
handler := http.HandlerFunc(AdminReserveSlotHandler)
|
||||
w := makeAdminReserveRequest(handler, req, adminID)
|
||||
w := makeAdminReserveRequest(handler, req, adminID, ctx)
|
||||
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("expected status 400, got %d. body: %s", w.Code, w.Body.String())
|
||||
@@ -273,15 +279,16 @@ func TestAdminReserveSlot_CallIn_MissingServices(t *testing.T) {
|
||||
// TestAdminReserveSlot_InvalidReservationType tests that reservations
|
||||
// fail with HTTP 400 when reservation_type is invalid.
|
||||
func TestAdminReserveSlot_InvalidReservationType(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
|
||||
adminID, err := fixtures.CreateTestAdminUser(db.DB)
|
||||
adminID, err := fixtures.CreateTestAdminUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, adminID)
|
||||
defer fixtures.DeleteUser(tx, adminID)
|
||||
|
||||
req := AdminReserveSlotRequest{
|
||||
ReservationType: "invalid",
|
||||
@@ -290,7 +297,7 @@ func TestAdminReserveSlot_InvalidReservationType(t *testing.T) {
|
||||
}
|
||||
|
||||
handler := http.HandlerFunc(AdminReserveSlotHandler)
|
||||
w := makeAdminReserveRequest(handler, req, adminID)
|
||||
w := makeAdminReserveRequest(handler, req, adminID, ctx)
|
||||
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("expected status 400, got %d. body: %s", w.Code, w.Body.String())
|
||||
@@ -310,32 +317,33 @@ func TestAdminReserveSlot_InvalidReservationType(t *testing.T) {
|
||||
// TestAdminReserveSlot_SlotOverlap tests that a reservation fails
|
||||
// with HTTP 409 when the slot overlaps with an existing booking.
|
||||
func TestAdminReserveSlot_SlotOverlap(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
|
||||
// Create test admin user (for the booking)
|
||||
adminID, err := fixtures.CreateTestAdminUser(db.DB)
|
||||
adminID, err := fixtures.CreateTestAdminUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, adminID)
|
||||
defer fixtures.DeleteUser(tx, adminID)
|
||||
|
||||
// Create test regular user
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, userID)
|
||||
defer fixtures.DeleteUser(tx, userID)
|
||||
|
||||
serviceID, err := fixtures.CreateTestService(db.DB)
|
||||
serviceID, err := fixtures.CreateTestService(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test service: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteService(db.DB, serviceID)
|
||||
defer fixtures.DeleteService(tx, serviceID)
|
||||
|
||||
// Set deposits_required=0 for test user
|
||||
_, err = db.DB.Exec(context.Background(), "UPDATE users SET deposits_required = 0 WHERE id = $1", userID)
|
||||
_, err = tx.Exec(ctx, "UPDATE users SET deposits_required = 0 WHERE id = $1", userID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to set deposits_required: %v", err)
|
||||
}
|
||||
@@ -343,13 +351,13 @@ func TestAdminReserveSlot_SlotOverlap(t *testing.T) {
|
||||
tomorrow := time.Now().Add(24 * time.Hour).Truncate(time.Second)
|
||||
tomorrow = time.Date(tomorrow.Year(), tomorrow.Month(), tomorrow.Day(), 10, 0, 0, 0, tomorrow.Location())
|
||||
|
||||
bookingID, err := fixtures.CreateTestBooking(db.DB, userID, serviceID)
|
||||
bookingID, err := fixtures.CreateTestBooking(tx, userID, serviceID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test booking: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteBooking(db.DB, bookingID)
|
||||
defer fixtures.DeleteBooking(tx, bookingID)
|
||||
|
||||
_, err = db.DB.Exec(context.Background(),
|
||||
_, err = tx.Exec(ctx,
|
||||
"UPDATE bookings SET start_time = $1, status = 'confirmed' WHERE id = $2",
|
||||
tomorrow, bookingID)
|
||||
if err != nil {
|
||||
@@ -367,7 +375,7 @@ func TestAdminReserveSlot_SlotOverlap(t *testing.T) {
|
||||
}
|
||||
|
||||
handler := http.HandlerFunc(AdminReserveSlotHandler)
|
||||
w := makeAdminReserveRequest(handler, req, adminID)
|
||||
w := makeAdminReserveRequest(handler, req, adminID, ctx)
|
||||
|
||||
// Should return 409 Conflict due to overlap
|
||||
if w.Code != http.StatusConflict {
|
||||
@@ -382,15 +390,16 @@ func TestAdminReserveSlot_SlotOverlap(t *testing.T) {
|
||||
// TestAdminReserveSlot_ReplacesExisting tests that reserving twice
|
||||
// on the same admin replaces the previous reservation.
|
||||
func TestAdminReserveSlot_ReplacesExisting(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
|
||||
adminID, err := fixtures.CreateTestAdminUser(db.DB)
|
||||
adminID, err := fixtures.CreateTestAdminUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, adminID)
|
||||
defer fixtures.DeleteUser(tx, adminID)
|
||||
|
||||
tomorrow := time.Now().Add(24 * time.Hour)
|
||||
now := time.Date(tomorrow.Year(), tomorrow.Month(), tomorrow.Day(), 12, 0, 0, 0, tomorrow.Location())
|
||||
@@ -404,7 +413,7 @@ func TestAdminReserveSlot_ReplacesExisting(t *testing.T) {
|
||||
|
||||
// First reservation
|
||||
handler := http.HandlerFunc(AdminReserveSlotHandler)
|
||||
w := makeAdminReserveRequest(handler, req, adminID)
|
||||
w := makeAdminReserveRequest(handler, req, adminID, ctx)
|
||||
|
||||
if w.Code != http.StatusCreated {
|
||||
t.Errorf("expected status 201, got %d. body: %s", w.Code, w.Body.String())
|
||||
@@ -418,7 +427,7 @@ func TestAdminReserveSlot_ReplacesExisting(t *testing.T) {
|
||||
// Count reservations before second request
|
||||
var countBefore int
|
||||
var qerr error
|
||||
qerr = db.DB.QueryRow(context.Background(),
|
||||
qerr = tx.QueryRow(ctx,
|
||||
"SELECT COUNT(*) FROM time_blockers WHERE description LIKE 'RESERVATION:admin:%'",
|
||||
).Scan(&countBefore)
|
||||
if qerr != nil {
|
||||
@@ -434,7 +443,7 @@ func TestAdminReserveSlot_ReplacesExisting(t *testing.T) {
|
||||
TTLMinutes: 15,
|
||||
}
|
||||
|
||||
w = makeAdminReserveRequest(handler, req2, adminID)
|
||||
w = makeAdminReserveRequest(handler, req2, adminID, ctx)
|
||||
|
||||
if w.Code != http.StatusCreated {
|
||||
t.Errorf("expected status 201, got %d. body: %s", w.Code, w.Body.String())
|
||||
@@ -447,7 +456,7 @@ func TestAdminReserveSlot_ReplacesExisting(t *testing.T) {
|
||||
|
||||
// Count reservations after second request - should still be 1
|
||||
var countAfter int
|
||||
qerr = db.DB.QueryRow(context.Background(),
|
||||
qerr = tx.QueryRow(ctx,
|
||||
"SELECT COUNT(*) FROM time_blockers WHERE description LIKE 'RESERVATION:admin:%'",
|
||||
).Scan(&countAfter)
|
||||
if qerr != nil {
|
||||
@@ -470,15 +479,16 @@ func TestAdminReserveSlot_ReplacesExisting(t *testing.T) {
|
||||
|
||||
// TestAdminReserveSlot_WalkIn_PastStart tests that walk-in reservations
|
||||
func TestAdminReserveSlot_WalkIn_PastStart(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
seedDefaultWorkingHours(t)
|
||||
|
||||
|
||||
adminID, err := fixtures.CreateTestAdminUser(db.DB)
|
||||
adminID, err := fixtures.CreateTestAdminUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin: %v", err)
|
||||
}
|
||||
defer fixtures.DeleteUser(db.DB, adminID)
|
||||
defer fixtures.DeleteUser(tx, adminID)
|
||||
|
||||
pastTime := time.Now().Add(-5 * time.Minute)
|
||||
req := AdminReserveSlotRequest{
|
||||
@@ -489,7 +499,7 @@ func TestAdminReserveSlot_WalkIn_PastStart(t *testing.T) {
|
||||
}
|
||||
|
||||
handler := http.HandlerFunc(AdminReserveSlotHandler)
|
||||
w := makeAdminReserveRequest(handler, req, adminID)
|
||||
w := makeAdminReserveRequest(handler, req, adminID, ctx)
|
||||
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("expected status 400, got %d. body: %s", w.Code, w.Body.String())
|
||||
|
||||
Reference in New Issue
Block a user