//go:build test // +build test package testutils import ( "context" "testing" "crussell/db" ) // txSemaphore limits concurrent test transactions per package to prevent // pgxpool connection exhaustion when many tests use t.Parallel(). // The cap (8) should match the -parallel N flag used in test invocations // and should be well below MaxConns per pool (currently 16). var txSemaphore = make(chan struct{}, 8) // SetupTestTx begins a transaction on the db.Conn pool, stores it in the // returned context (for PoolProxy routing), and returns the raw tx for use // with fixture functions (e.g. fixtures.CreateTestUser(tx)). // // The transaction is automatically rolled back in t.Cleanup. After calling // SetupTestTx, the test should call t.Parallel() and pass the returned context // to any request helper functions (MakeRequest, makeAdminRequest, etc.). // // Example: // // func TestMyHandler(t *testing.T) { // t.Parallel() // ctx, tx := testutils.SetupTestTx(t) // userID, _ := fixtures.CreateTestUser(tx) // w := testutils.MakeUserRequest(handler, "GET", "/api/foo", nil, userID, ctx) // assert.Equal(t, 200, w.Code) // } func SetupTestTx(t *testing.T) (context.Context, db.Querier) { t.Helper() txSemaphore <- struct{}{} pool := db.Conn.Pool() tx, err := pool.Begin(context.Background()) if err != nil { <-txSemaphore t.Fatalf("SetupTestTx: failed to begin transaction: %v", err) } ctx := db.ContextWithTx(context.Background(), tx) t.Cleanup(func() { tx.Rollback(context.Background()) <-txSemaphore }) return ctx, tx }