Skip to content

Commit e16d01f

Browse files
committed
test(coderd/database/dbpurge): convert API keys tests to table-driven
1 parent 318a984 commit e16d01f

File tree

1 file changed

+120
-177
lines changed

1 file changed

+120
-177
lines changed

coderd/database/dbpurge/dbpurge_test.go

Lines changed: 120 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,195 +1410,138 @@ func TestDeleteOldAuditLogs(t *testing.T) {
14101410
func TestDeleteExpiredAPIKeys(t *testing.T) {
14111411
t.Parallel()
14121412

1413-
t.Run("RetentionEnabled", func(t *testing.T) {
1414-
t.Parallel()
1415-
1416-
ctx := testutil.Context(t, testutil.WaitShort)
1417-
1418-
clk := quartz.NewMock(t)
1419-
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
1420-
retentionPeriod := 7 * 24 * time.Hour // 7 days
1421-
expiredLongAgo := now.Add(-retentionPeriod).Add(-24 * time.Hour) // Expired 8 days ago (should be deleted)
1422-
expiredRecently := now.Add(-retentionPeriod).Add(24 * time.Hour) // Expired 6 days ago (should be kept)
1423-
notExpired := now.Add(24 * time.Hour) // Expires tomorrow (should be kept)
1424-
clk.Set(now).MustWait(ctx)
1425-
1426-
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
1427-
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
1428-
user := dbgen.User(t, db, database.User{})
1429-
1430-
// Create API key that expired long ago (should be deleted)
1431-
oldExpiredKey, _ := dbgen.APIKey(t, db, database.APIKey{
1432-
UserID: user.ID,
1433-
ExpiresAt: expiredLongAgo,
1434-
TokenName: "old-expired-key",
1435-
})
1436-
1437-
// Create API key that expired recently (should be kept)
1438-
recentExpiredKey, _ := dbgen.APIKey(t, db, database.APIKey{
1439-
UserID: user.ID,
1440-
ExpiresAt: expiredRecently,
1441-
TokenName: "recent-expired-key",
1442-
})
1443-
1444-
// Create API key that hasn't expired yet (should be kept)
1445-
activeKey, _ := dbgen.APIKey(t, db, database.APIKey{
1446-
UserID: user.ID,
1447-
ExpiresAt: notExpired,
1448-
TokenName: "active-key",
1449-
})
1413+
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
14501414

1451-
// Run the purge with configured retention period
1452-
done := awaitDoTick(ctx, t, clk)
1453-
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
1454-
Retention: codersdk.RetentionConfig{
1455-
APIKeys: serpent.Duration(retentionPeriod),
1415+
testCases := []struct {
1416+
name string
1417+
retentionConfig codersdk.RetentionConfig
1418+
oldExpiredTime time.Time
1419+
recentExpiredTime *time.Time // nil means no recent expired key created
1420+
activeTime *time.Time // nil means no active key created
1421+
expectOldExpiredDeleted bool
1422+
expectedKeysRemaining int
1423+
}{
1424+
{
1425+
name: "RetentionEnabled",
1426+
retentionConfig: codersdk.RetentionConfig{
1427+
APIKeys: serpent.Duration(7 * 24 * time.Hour), // 7 days
14561428
},
1457-
}, clk)
1458-
defer closer.Close()
1459-
testutil.TryReceive(ctx, t, done)
1460-
1461-
// Verify results
1462-
_, err := db.GetAPIKeyByID(ctx, oldExpiredKey.ID)
1463-
require.Error(t, err, "old expired key should be deleted")
1464-
1465-
_, err = db.GetAPIKeyByID(ctx, recentExpiredKey.ID)
1466-
require.NoError(t, err, "recently expired key should be kept")
1467-
1468-
_, err = db.GetAPIKeyByID(ctx, activeKey.ID)
1469-
require.NoError(t, err, "active key should be kept")
1470-
})
1471-
1472-
t.Run("RetentionDisabled", func(t *testing.T) {
1473-
t.Parallel()
1474-
1475-
ctx := testutil.Context(t, testutil.WaitShort)
1476-
1477-
clk := quartz.NewMock(t)
1478-
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
1479-
expiredLongAgo := now.Add(-365 * 24 * time.Hour) // Expired 1 year ago
1480-
clk.Set(now).MustWait(ctx)
1481-
1482-
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
1483-
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
1484-
user := dbgen.User(t, db, database.User{})
1485-
1486-
// Create API key that expired long ago (should NOT be deleted when retention is 0)
1487-
oldExpiredKey, _ := dbgen.APIKey(t, db, database.APIKey{
1488-
UserID: user.ID,
1489-
ExpiresAt: expiredLongAgo,
1490-
TokenName: "old-expired-key",
1491-
})
1492-
1493-
// Run the purge with retention disabled (0)
1494-
done := awaitDoTick(ctx, t, clk)
1495-
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
1496-
Retention: codersdk.RetentionConfig{
1497-
APIKeys: serpent.Duration(0), // disabled
1429+
oldExpiredTime: now.Add(-8 * 24 * time.Hour), // Expired 8 days ago
1430+
recentExpiredTime: ptr(now.Add(-6 * 24 * time.Hour)), // Expired 6 days ago
1431+
activeTime: ptr(now.Add(24 * time.Hour)), // Expires tomorrow
1432+
expectOldExpiredDeleted: true,
1433+
expectedKeysRemaining: 2, // recent expired + active
1434+
},
1435+
{
1436+
name: "RetentionDisabled",
1437+
retentionConfig: codersdk.RetentionConfig{
1438+
APIKeys: serpent.Duration(0),
14981439
},
1499-
}, clk)
1500-
defer closer.Close()
1501-
testutil.TryReceive(ctx, t, done)
1502-
1503-
// Verify old expired key is still present
1504-
_, err := db.GetAPIKeyByID(ctx, oldExpiredKey.ID)
1505-
require.NoError(t, err, "old expired key should NOT be deleted when retention is disabled")
1506-
})
1507-
1508-
t.Run("GlobalRetentionFallback", func(t *testing.T) {
1509-
t.Parallel()
1510-
1511-
ctx := testutil.Context(t, testutil.WaitShort)
1512-
1513-
clk := quartz.NewMock(t)
1514-
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
1515-
retentionPeriod := 14 * 24 * time.Hour // 14 days global
1516-
expiredLongAgo := now.Add(-retentionPeriod).Add(-24 * time.Hour) // Expired 15 days ago (should be deleted)
1517-
expiredRecently := now.Add(-retentionPeriod).Add(24 * time.Hour) // Expired 13 days ago (should be kept)
1518-
clk.Set(now).MustWait(ctx)
1519-
1520-
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
1521-
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
1522-
user := dbgen.User(t, db, database.User{})
1523-
1524-
// Create API key that expired long ago (should be deleted)
1525-
oldExpiredKey, _ := dbgen.APIKey(t, db, database.APIKey{
1526-
UserID: user.ID,
1527-
ExpiresAt: expiredLongAgo,
1528-
TokenName: "old-expired-key",
1529-
})
1530-
1531-
// Create API key that expired recently (should be kept)
1532-
recentExpiredKey, _ := dbgen.APIKey(t, db, database.APIKey{
1533-
UserID: user.ID,
1534-
ExpiresAt: expiredRecently,
1535-
TokenName: "recent-expired-key",
1536-
})
1537-
1538-
// Run the purge with global retention (API keys retention is 0, so it falls back)
1539-
done := awaitDoTick(ctx, t, clk)
1540-
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
1541-
Retention: codersdk.RetentionConfig{
1542-
Global: serpent.Duration(retentionPeriod), // Use global
1543-
APIKeys: serpent.Duration(0), // Not set, should fall back to global
1440+
oldExpiredTime: now.Add(-365 * 24 * time.Hour), // Expired 1 year ago
1441+
recentExpiredTime: nil,
1442+
activeTime: nil,
1443+
expectOldExpiredDeleted: false,
1444+
expectedKeysRemaining: 1, // old expired is kept
1445+
},
1446+
{
1447+
name: "GlobalRetentionFallback",
1448+
retentionConfig: codersdk.RetentionConfig{
1449+
Global: serpent.Duration(14 * 24 * time.Hour), // 14 days global
1450+
APIKeys: serpent.Duration(0), // Not set, should fall back to global
15441451
},
1545-
}, clk)
1546-
defer closer.Close()
1547-
testutil.TryReceive(ctx, t, done)
1452+
oldExpiredTime: now.Add(-15 * 24 * time.Hour), // Expired 15 days ago
1453+
recentExpiredTime: ptr(now.Add(-13 * 24 * time.Hour)), // Expired 13 days ago
1454+
activeTime: nil,
1455+
expectOldExpiredDeleted: true,
1456+
expectedKeysRemaining: 1, // only recent expired remains
1457+
},
1458+
{
1459+
name: "CustomRetention30Days",
1460+
retentionConfig: codersdk.RetentionConfig{
1461+
APIKeys: serpent.Duration(30 * 24 * time.Hour), // 30 days
1462+
},
1463+
oldExpiredTime: now.Add(-31 * 24 * time.Hour), // Expired 31 days ago
1464+
recentExpiredTime: ptr(now.Add(-29 * 24 * time.Hour)), // Expired 29 days ago
1465+
activeTime: nil,
1466+
expectOldExpiredDeleted: true,
1467+
expectedKeysRemaining: 1, // only recent expired remains
1468+
},
1469+
}
15481470

1549-
// Verify results
1550-
_, err := db.GetAPIKeyByID(ctx, oldExpiredKey.ID)
1551-
require.Error(t, err, "old expired key should be deleted via global retention")
1471+
for _, tc := range testCases {
1472+
t.Run(tc.name, func(t *testing.T) {
1473+
t.Parallel()
15521474

1553-
_, err = db.GetAPIKeyByID(ctx, recentExpiredKey.ID)
1554-
require.NoError(t, err, "recently expired key should be kept")
1555-
})
1475+
ctx := testutil.Context(t, testutil.WaitShort)
1476+
clk := quartz.NewMock(t)
1477+
clk.Set(now).MustWait(ctx)
1478+
1479+
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
1480+
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
1481+
user := dbgen.User(t, db, database.User{})
1482+
1483+
// Create API key that expired long ago.
1484+
oldExpiredKey, _ := dbgen.APIKey(t, db, database.APIKey{
1485+
UserID: user.ID,
1486+
ExpiresAt: tc.oldExpiredTime,
1487+
TokenName: "old-expired-key",
1488+
})
15561489

1557-
t.Run("CustomRetention30Days", func(t *testing.T) {
1558-
t.Parallel()
1490+
// Create API key that expired recently if specified.
1491+
var recentExpiredKey database.APIKey
1492+
if tc.recentExpiredTime != nil {
1493+
recentExpiredKey, _ = dbgen.APIKey(t, db, database.APIKey{
1494+
UserID: user.ID,
1495+
ExpiresAt: *tc.recentExpiredTime,
1496+
TokenName: "recent-expired-key",
1497+
})
1498+
}
15591499

1560-
ctx := testutil.Context(t, testutil.WaitShort)
1500+
// Create API key that hasn't expired yet if specified.
1501+
var activeKey database.APIKey
1502+
if tc.activeTime != nil {
1503+
activeKey, _ = dbgen.APIKey(t, db, database.APIKey{
1504+
UserID: user.ID,
1505+
ExpiresAt: *tc.activeTime,
1506+
TokenName: "active-key",
1507+
})
1508+
}
15611509

1562-
clk := quartz.NewMock(t)
1563-
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
1564-
retentionPeriod := 30 * 24 * time.Hour // 30 days
1565-
expiredLongAgo := now.Add(-retentionPeriod).Add(-24 * time.Hour) // Expired 31 days ago (should be deleted)
1566-
expiredRecently := now.Add(-retentionPeriod).Add(24 * time.Hour) // Expired 29 days ago (should be kept)
1567-
clk.Set(now).MustWait(ctx)
1510+
// Run the purge.
1511+
done := awaitDoTick(ctx, t, clk)
1512+
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
1513+
Retention: tc.retentionConfig,
1514+
}, clk)
1515+
defer closer.Close()
1516+
testutil.TryReceive(ctx, t, done)
15681517

1569-
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
1570-
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
1571-
user := dbgen.User(t, db, database.User{})
1518+
// Verify total keys remaining.
1519+
keys, err := db.GetAPIKeysLastUsedAfter(ctx, time.Time{})
1520+
require.NoError(t, err)
1521+
require.Len(t, keys, tc.expectedKeysRemaining, "unexpected number of keys remaining")
1522+
1523+
// Verify results.
1524+
_, err = db.GetAPIKeyByID(ctx, oldExpiredKey.ID)
1525+
if tc.expectOldExpiredDeleted {
1526+
require.Error(t, err, "old expired key should be deleted")
1527+
} else {
1528+
require.NoError(t, err, "old expired key should NOT be deleted")
1529+
}
15721530

1573-
// Create API key that expired long ago (should be deleted)
1574-
oldExpiredKey, _ := dbgen.APIKey(t, db, database.APIKey{
1575-
UserID: user.ID,
1576-
ExpiresAt: expiredLongAgo,
1577-
TokenName: "old-expired-key",
1578-
})
1531+
if tc.recentExpiredTime != nil {
1532+
_, err = db.GetAPIKeyByID(ctx, recentExpiredKey.ID)
1533+
require.NoError(t, err, "recently expired key should be kept")
1534+
}
15791535

1580-
// Create API key that expired recently (should be kept)
1581-
recentExpiredKey, _ := dbgen.APIKey(t, db, database.APIKey{
1582-
UserID: user.ID,
1583-
ExpiresAt: expiredRecently,
1584-
TokenName: "recent-expired-key",
1536+
if tc.activeTime != nil {
1537+
_, err = db.GetAPIKeyByID(ctx, activeKey.ID)
1538+
require.NoError(t, err, "active key should be kept")
1539+
}
15851540
})
1541+
}
1542+
}
15861543

1587-
// Run the purge with 30-day retention
1588-
done := awaitDoTick(ctx, t, clk)
1589-
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
1590-
Retention: codersdk.RetentionConfig{
1591-
APIKeys: serpent.Duration(retentionPeriod),
1592-
},
1593-
}, clk)
1594-
defer closer.Close()
1595-
testutil.TryReceive(ctx, t, done)
1596-
1597-
// Verify results
1598-
_, err := db.GetAPIKeyByID(ctx, oldExpiredKey.ID)
1599-
require.Error(t, err, "old expired key should be deleted with 30-day retention")
1600-
1601-
_, err = db.GetAPIKeyByID(ctx, recentExpiredKey.ID)
1602-
require.NoError(t, err, "recently expired key should be kept with 30-day retention")
1603-
})
1544+
// ptr is a helper to create a pointer to a value.
1545+
func ptr[T any](v T) *T {
1546+
return &v
16041547
}

0 commit comments

Comments
 (0)