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:
@@ -29,10 +29,9 @@ import (
|
||||
)
|
||||
|
||||
// createUserWithDOB creates a test user with specified date of birth
|
||||
func createUserWithDOB(dob string) (string, error) {
|
||||
ctx := context.Background()
|
||||
func createUserWithDOB(ctx context.Context, q db.Querier, dob string) (string, error) {
|
||||
var userID string
|
||||
err := db.DB.QueryRow(ctx, `
|
||||
err := q.QueryRow(ctx, `
|
||||
INSERT INTO users (n_first_name, n_last_name, email, phone, date_of_birth, password_hash, account_role, account_type)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
RETURNING id
|
||||
@@ -40,7 +39,7 @@ func createUserWithDOB(dob string) (string, error) {
|
||||
return userID, err
|
||||
}
|
||||
|
||||
func makeRequest(handler http.HandlerFunc, method, path string, body interface{}) *httptest.ResponseRecorder {
|
||||
func makeRequest(handler http.HandlerFunc, method, path string, body interface{}, ctx context.Context) *httptest.ResponseRecorder {
|
||||
var req *http.Request
|
||||
if body != nil {
|
||||
bodyBytes, _ := json.Marshal(body)
|
||||
@@ -49,18 +48,19 @@ func makeRequest(handler http.HandlerFunc, method, path string, body interface{}
|
||||
} else {
|
||||
req = httptest.NewRequest(method, path, nil)
|
||||
}
|
||||
return makeRequestWithContext(handler, req)
|
||||
return makeRequestWithContext(handler, req, ctx)
|
||||
}
|
||||
|
||||
// makeRequestWithContext executes request with chi routing context for path params
|
||||
func makeRequestWithContext(handler http.HandlerFunc, req *http.Request) *httptest.ResponseRecorder {
|
||||
// Set up chi routing context for path params
|
||||
func makeRequestWithContext(handler http.HandlerFunc, req *http.Request, ctx context.Context) *httptest.ResponseRecorder {
|
||||
// Set up chi routing context for path params on top of the tx context
|
||||
rctx := chi.NewRouteContext()
|
||||
if id, paramName := extractIDFromPath(req.URL.Path); id != "" {
|
||||
rctx.URLParams.Add(paramName, id)
|
||||
}
|
||||
ctx := context.WithValue(req.Context(), chi.RouteCtxKey, rctx)
|
||||
req = req.WithContext(ctx)
|
||||
chiCtx := context.WithValue(ctx, chi.RouteCtxKey, rctx)
|
||||
req = req.WithContext(chiCtx)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
handler.ServeHTTP(w, req)
|
||||
@@ -95,9 +95,10 @@ func findLastSegment(path, prefix string) int {
|
||||
// TestServices_ListAll verifies that listing all services returns only active services,
|
||||
// filtering out inactive services from the response.
|
||||
func TestServices_ListAll(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
_, err := db.DB.Exec(context.Background(), `
|
||||
_, err := tx.Exec(ctx, `
|
||||
INSERT INTO services (name, description, price, duration_minutes, is_active, minimum_age_required)
|
||||
VALUES
|
||||
('Manicure', 'Basic manicure', 25.00, 30, true, 0),
|
||||
@@ -109,7 +110,7 @@ func TestServices_ListAll(t *testing.T) {
|
||||
}
|
||||
|
||||
handler := http.HandlerFunc(ServicesHandler)
|
||||
w := makeRequest(handler, "GET", "/api/services", nil)
|
||||
w := makeRequest(handler, "GET", "/api/services", nil, ctx)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected status 200, got %d. body: %s", w.Code, w.Body.String())
|
||||
@@ -142,15 +143,16 @@ func TestServices_ListAll(t *testing.T) {
|
||||
// TestServices_EligibleForUser_AgeFilter verifies that eligible services are filtered based on the user's age,
|
||||
// excluding services with minimum_age_required higher than the user's age.
|
||||
func TestServices_EligibleForUser_AgeFilter(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
dob := "2006-01-01" // Age 20 in Feb 2026
|
||||
userID, err := createUserWithDOB(dob)
|
||||
userID, err := createUserWithDOB(ctx, tx, dob)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create user: %v", err)
|
||||
}
|
||||
|
||||
_, err = db.DB.Exec(context.Background(), `
|
||||
_, err = tx.Exec(ctx, `
|
||||
INSERT INTO services (name, description, price, duration_minutes, is_active, minimum_age_required)
|
||||
VALUES
|
||||
('Under 18 Service', 'For minors', 20.00, 30, true, 16),
|
||||
@@ -163,7 +165,7 @@ func TestServices_EligibleForUser_AgeFilter(t *testing.T) {
|
||||
|
||||
handler := http.HandlerFunc(ServicesEligibleForUserHandler)
|
||||
req := httptest.NewRequest("GET", "/api/services/eligible-for/"+userID, nil)
|
||||
w := makeRequestWithContext(handler, req)
|
||||
w := makeRequestWithContext(handler, req, ctx)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected status 200, got %d. body: %s", w.Code, w.Body.String())
|
||||
@@ -196,16 +198,17 @@ func TestServices_EligibleForUser_AgeFilter(t *testing.T) {
|
||||
// TestServices_EligibleForUser_PatchTest verifies that services requiring patch tests include a
|
||||
// PatchTestStatus field set to 'ok' when the user has completed a valid patch test for that service.
|
||||
func TestServices_EligibleForUser_PatchTest(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
dob := "2000-01-01"
|
||||
userID, err := createUserWithDOB(dob)
|
||||
userID, err := createUserWithDOB(ctx, tx, dob)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create user: %v", err)
|
||||
}
|
||||
|
||||
// Create a regular service (no patch test required)
|
||||
_, err = db.DB.Exec(context.Background(), `
|
||||
_, err = tx.Exec(ctx, `
|
||||
INSERT INTO services (name, description, price, duration_minutes, is_active, minimum_age_required)
|
||||
VALUES ('Regular Service', 'No patch test needed', 30.00, 30, true, 0)
|
||||
`)
|
||||
@@ -215,7 +218,7 @@ func TestServices_EligibleForUser_PatchTest(t *testing.T) {
|
||||
|
||||
// Create a service that will require a patch test
|
||||
var patchTestSvcID string
|
||||
err = db.DB.QueryRow(context.Background(), `
|
||||
err = tx.QueryRow(ctx, `
|
||||
INSERT INTO services (name, description, price, duration_minutes, is_active, minimum_age_required)
|
||||
VALUES ('Patch Test Required', 'Requires patch test', 75.00, 60, true, 0)
|
||||
RETURNING id
|
||||
@@ -226,7 +229,7 @@ func TestServices_EligibleForUser_PatchTest(t *testing.T) {
|
||||
|
||||
// Create a patch test that links to this service
|
||||
var patchTestID string
|
||||
err = db.DB.QueryRow(context.Background(), `
|
||||
err = tx.QueryRow(ctx, `
|
||||
INSERT INTO patch_tests (name, description, notice_duration_hours, expiry_months, service_ids)
|
||||
VALUES ('Allergy Test', 'Patch test for gel products', 24, 6, $1)
|
||||
RETURNING id
|
||||
@@ -236,7 +239,7 @@ func TestServices_EligibleForUser_PatchTest(t *testing.T) {
|
||||
}
|
||||
|
||||
// Create a valid user patch test record (tested 24+ hours ago, within expiry)
|
||||
_, err = db.DB.Exec(context.Background(), `
|
||||
_, err = tx.Exec(ctx, `
|
||||
INSERT INTO user_patch_tests (user_id, patch_test_id, tested_at)
|
||||
VALUES ($1, $2, NOW() - INTERVAL '48 hours')
|
||||
`, userID, patchTestID)
|
||||
@@ -246,7 +249,7 @@ func TestServices_EligibleForUser_PatchTest(t *testing.T) {
|
||||
|
||||
handler := http.HandlerFunc(ServicesEligibleForUserHandler)
|
||||
req := httptest.NewRequest("GET", "/api/services/eligible-for/"+userID, nil)
|
||||
w := makeRequestWithContext(handler, req)
|
||||
w := makeRequestWithContext(handler, req, ctx)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected status 200, got %d. body: %s", w.Code, w.Body.String())
|
||||
@@ -290,9 +293,10 @@ func TestServices_EligibleForUser_PatchTest(t *testing.T) {
|
||||
// TestContact_ReturnsInfo verifies that the contact info endpoint returns the salon's contact
|
||||
// details (name, phone, email, role) from the first admin user in the database.
|
||||
func TestContact_ReturnsInfo(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
_, err := db.DB.Exec(context.Background(), `
|
||||
_, err := tx.Exec(ctx, `
|
||||
INSERT INTO users (n_first_name, n_last_name, email, phone, date_of_birth, password_hash, account_role, account_type)
|
||||
VALUES ('John', 'Smith', 'john@test.com', '+447700000001', '1990-01-01', 'hash', 'admin', 'email')
|
||||
`)
|
||||
@@ -301,7 +305,7 @@ func TestContact_ReturnsInfo(t *testing.T) {
|
||||
}
|
||||
|
||||
handler := http.HandlerFunc(user.GetContactInfoHandler)
|
||||
w := makeRequest(handler, "GET", "/api/contact", nil)
|
||||
w := makeRequest(handler, "GET", "/api/contact", nil, ctx)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected status 200, got %d. body: %s", w.Code, w.Body.String())
|
||||
|
||||
@@ -14,8 +14,9 @@ import (
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
pool := testdb.CreateTestDatabase("crussell_test_handlers_services")
|
||||
db.DB = pool
|
||||
db.Conn = db.NewPoolProxy(pool)
|
||||
jwt.Init()
|
||||
testdb.SeedBaseline(pool)
|
||||
code := m.Run()
|
||||
testdb.DestroyTestDatabase(pool, "crussell_test_handlers_services")
|
||||
os.Exit(code)
|
||||
|
||||
Reference in New Issue
Block a user