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:
2026-06-21 19:29:24 +01:00
co-authored by Sisyphus
parent 3d0e2afc4c
commit 220a0ef6e8
57 changed files with 5911 additions and 6235 deletions
+23 -31
View File
@@ -18,19 +18,9 @@ import (
"testing"
"time"
"crussell/db"
"crussell/testutils/testtx"
)
// requiresDB skips the test if the database is not available (e.g. running
// tests standalone without test DB setup). DB-backed JTI revocation tests
// need a connection to the revoked_jtis table.
func requiresDB(t *testing.T) {
t.Helper()
if db.DB == nil {
t.Skip("skipping: no database connection")
}
}
// =============================================================================
// generateJTI Tests
// =============================================================================
@@ -139,12 +129,13 @@ func TestGenerateToken_JTIInClaims(t *testing.T) {
// TestVerifyToken_ReturnsJTI creates a token, verifies it, and checks the
// returned user_id, role, and JTI match the expected values.
func TestVerifyToken_ReturnsJTI(t *testing.T) {
ctx, _ := testtx.SetupTestTx(t)
token, jti, err := GenerateToken("user-003", "verified_email")
if err != nil {
t.Fatalf("GenerateToken() failed: %v", err)
}
userID, role, returnedJTI, err := VerifyToken(token, context.Background())
userID, role, returnedJTI, err := VerifyToken(token, ctx)
if err != nil {
t.Fatalf("VerifyToken() failed: %v", err)
}
@@ -163,15 +154,15 @@ func TestVerifyToken_ReturnsJTI(t *testing.T) {
// TestVerifyToken_RevokedJTI creates a token, revokes its JTI, and verifies
// that VerifyToken returns an error containing "token revoked".
func TestVerifyToken_RevokedJTI(t *testing.T) {
requiresDB(t)
ctx, _ := testtx.SetupTestTx(t)
token, jti, err := GenerateToken("user-004", "verified_email")
if err != nil {
t.Fatalf("GenerateToken() failed: %v", err)
}
RevokeJTI(jti, time.Now().Add(30*24*time.Hour))
RevokeJTI(ctx, jti, time.Now().Add(30*24*time.Hour))
_, _, _, err = VerifyToken(token, context.Background())
_, _, _, err = VerifyToken(token, ctx)
if err == nil {
t.Fatal("expected error for revoked JTI, got nil")
}
@@ -209,19 +200,19 @@ func TestVerifyToken_MissingJTI(t *testing.T) {
// TestRevokeJTI_AddsToSet verifies that calling RevokeJTI adds the JTI to the
// revoked set, and IsJTIRevoked returns true for it.
func TestRevokeJTI_AddsToSet(t *testing.T) {
requiresDB(t)
ctx, _ := testtx.SetupTestTx(t)
_, jti, err := GenerateToken("user-006", "verified_email")
if err != nil {
t.Fatalf("GenerateToken() failed: %v", err)
}
if IsJTIRevoked(jti) {
if IsJTIRevoked(ctx, jti) {
t.Fatal("JTI should not be revoked before calling RevokeJTI")
}
RevokeJTI(jti, time.Now().Add(30*24*time.Hour))
RevokeJTI(ctx, jti, time.Now().Add(30*24*time.Hour))
if !IsJTIRevoked(jti) {
if !IsJTIRevoked(ctx, jti) {
t.Error("expected IsJTIRevoked to return true after RevokeJTI")
}
}
@@ -229,7 +220,8 @@ func TestRevokeJTI_AddsToSet(t *testing.T) {
// TestIsJTIRevoked_NonExistent verifies that checking a non-existent JTI
// returns false.
func TestIsJTIRevoked_NonExistent(t *testing.T) {
if IsJTIRevoked("nonexistent-jti-12345") {
ctx, _ := testtx.SetupTestTx(t)
if IsJTIRevoked(ctx, "nonexistent-jti-12345") {
t.Error("expected IsJTIRevoked to return false for non-existent JTI")
}
}
@@ -241,29 +233,29 @@ func TestIsJTIRevoked_NonExistent(t *testing.T) {
// TestCleanupRevokedJTIs_RemovesExpired adds a JTI with a past expiry time,
// runs CleanupRevokedJTIs, and verifies the JTI is removed from the set.
func TestCleanupRevokedJTIs_RemovesExpired(t *testing.T) {
requiresDB(t)
ctx, tx := testtx.SetupTestTx(t)
_, jti, err := GenerateToken("user-007", "verified_email")
if err != nil {
t.Fatalf("GenerateToken() failed: %v", err)
}
// Add with future expiry so IsJTIRevoked sees it
RevokeJTI(jti, time.Now().Add(1*time.Hour))
RevokeJTI(ctx, jti, time.Now().Add(1*time.Hour))
if !IsJTIRevoked(jti) {
if !IsJTIRevoked(ctx, jti) {
t.Fatal("JTI should be in revoked set after RevokeJTI")
}
// Directly update the DB to set expiry in the past
_, err = db.DB.Exec(context.Background(),
_, err = tx.Exec(ctx,
"UPDATE revoked_jtis SET expires_at = NOW() - INTERVAL '1 hour' WHERE jti = $1", jti)
if err != nil {
t.Fatalf("failed to expire JTI: %v", err)
}
CleanupRevokedJTIs()
CleanupRevokedJTIs(ctx)
if IsJTIRevoked(jti) {
if IsJTIRevoked(ctx, jti) {
t.Error("expected expired JTI to be removed after cleanup")
}
}
@@ -271,22 +263,22 @@ func TestCleanupRevokedJTIs_RemovesExpired(t *testing.T) {
// TestCleanupRevokedJTIs_KeepsValid adds a JTI with a future expiry time,
// runs CleanupRevokedJTIs, and verifies the JTI is still in the set.
func TestCleanupRevokedJTIs_KeepsValid(t *testing.T) {
requiresDB(t)
ctx, _ := testtx.SetupTestTx(t)
_, jti, err := GenerateToken("user-008", "verified_email")
if err != nil {
t.Fatalf("GenerateToken() failed: %v", err)
}
// Add with future expiry
RevokeJTI(jti, time.Now().Add(30*24*time.Hour))
RevokeJTI(ctx, jti, time.Now().Add(30*24*time.Hour))
if !IsJTIRevoked(jti) {
if !IsJTIRevoked(ctx, jti) {
t.Fatal("JTI should be in revoked set before cleanup")
}
CleanupRevokedJTIs()
CleanupRevokedJTIs(ctx)
if !IsJTIRevoked(jti) {
if !IsJTIRevoked(ctx, jti) {
t.Error("expected valid (future expiry) JTI to remain after cleanup")
}
}