Skip to content

Commit e74e489

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

File tree

1 file changed

+118
-175
lines changed

1 file changed

+118
-175
lines changed

coderd/database/dbpurge/dbpurge_test.go

Lines changed: 118 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,195 +1249,138 @@ func TestDeleteOldAuditLogs(t *testing.T) {
12491249
func TestDeleteExpiredAPIKeys(t *testing.T) {
12501250
t.Parallel()
12511251

1252-
t.Run("RetentionEnabled", func(t *testing.T) {
1253-
t.Parallel()
1254-
1255-
ctx := testutil.Context(t, testutil.WaitShort)
1256-
1257-
clk := quartz.NewMock(t)
1258-
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
1259-
retentionPeriod := 7 * 24 * time.Hour // 7 days
1260-
expiredLongAgo := now.Add(-retentionPeriod).Add(-24 * time.Hour) // Expired 8 days ago (should be deleted)
1261-
expiredRecently := now.Add(-retentionPeriod).Add(24 * time.Hour) // Expired 6 days ago (should be kept)
1262-
notExpired := now.Add(24 * time.Hour) // Expires tomorrow (should be kept)
1263-
clk.Set(now).MustWait(ctx)
1264-
1265-
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
1266-
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
1267-
user := dbgen.User(t, db, database.User{})
1268-
1269-
// Create API key that expired long ago (should be deleted)
1270-
oldExpiredKey, _ := dbgen.APIKey(t, db, database.APIKey{
1271-
UserID: user.ID,
1272-
ExpiresAt: expiredLongAgo,
1273-
TokenName: "old-expired-key",
1274-
})
1275-
1276-
// Create API key that expired recently (should be kept)
1277-
recentExpiredKey, _ := dbgen.APIKey(t, db, database.APIKey{
1278-
UserID: user.ID,
1279-
ExpiresAt: expiredRecently,
1280-
TokenName: "recent-expired-key",
1281-
})
1282-
1283-
// Create API key that hasn't expired yet (should be kept)
1284-
activeKey, _ := dbgen.APIKey(t, db, database.APIKey{
1285-
UserID: user.ID,
1286-
ExpiresAt: notExpired,
1287-
TokenName: "active-key",
1288-
})
1252+
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
12891253

1290-
// Run the purge with configured retention period
1291-
done := awaitDoTick(ctx, t, clk)
1292-
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
1293-
Retention: codersdk.RetentionConfig{
1294-
APIKeys: serpent.Duration(retentionPeriod),
1254+
testCases := []struct {
1255+
name string
1256+
retentionConfig codersdk.RetentionConfig
1257+
oldExpiredTime time.Time
1258+
recentExpiredTime *time.Time // nil means no recent expired key created
1259+
activeTime *time.Time // nil means no active key created
1260+
expectOldExpiredDeleted bool
1261+
expectedKeysRemaining int
1262+
}{
1263+
{
1264+
name: "RetentionEnabled",
1265+
retentionConfig: codersdk.RetentionConfig{
1266+
APIKeys: serpent.Duration(7 * 24 * time.Hour), // 7 days
12951267
},
1296-
}, clk)
1297-
defer closer.Close()
1298-
testutil.TryReceive(ctx, t, done)
1299-
1300-
// Verify results
1301-
_, err := db.GetAPIKeyByID(ctx, oldExpiredKey.ID)
1302-
require.Error(t, err, "old expired key should be deleted")
1303-
1304-
_, err = db.GetAPIKeyByID(ctx, recentExpiredKey.ID)
1305-
require.NoError(t, err, "recently expired key should be kept")
1306-
1307-
_, err = db.GetAPIKeyByID(ctx, activeKey.ID)
1308-
require.NoError(t, err, "active key should be kept")
1309-
})
1310-
1311-
t.Run("RetentionDisabled", func(t *testing.T) {
1312-
t.Parallel()
1313-
1314-
ctx := testutil.Context(t, testutil.WaitShort)
1315-
1316-
clk := quartz.NewMock(t)
1317-
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
1318-
expiredLongAgo := now.Add(-365 * 24 * time.Hour) // Expired 1 year ago
1319-
clk.Set(now).MustWait(ctx)
1320-
1321-
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
1322-
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
1323-
user := dbgen.User(t, db, database.User{})
1324-
1325-
// Create API key that expired long ago (should NOT be deleted when retention is 0)
1326-
oldExpiredKey, _ := dbgen.APIKey(t, db, database.APIKey{
1327-
UserID: user.ID,
1328-
ExpiresAt: expiredLongAgo,
1329-
TokenName: "old-expired-key",
1330-
})
1331-
1332-
// Run the purge with retention disabled (0)
1333-
done := awaitDoTick(ctx, t, clk)
1334-
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
1335-
Retention: codersdk.RetentionConfig{
1336-
APIKeys: serpent.Duration(0), // disabled
1268+
oldExpiredTime: now.Add(-8 * 24 * time.Hour), // Expired 8 days ago
1269+
recentExpiredTime: ptr(now.Add(-6 * 24 * time.Hour)), // Expired 6 days ago
1270+
activeTime: ptr(now.Add(24 * time.Hour)), // Expires tomorrow
1271+
expectOldExpiredDeleted: true,
1272+
expectedKeysRemaining: 2, // recent expired + active
1273+
},
1274+
{
1275+
name: "RetentionDisabled",
1276+
retentionConfig: codersdk.RetentionConfig{
1277+
APIKeys: serpent.Duration(0),
13371278
},
1338-
}, clk)
1339-
defer closer.Close()
1340-
testutil.TryReceive(ctx, t, done)
1341-
1342-
// Verify old expired key is still present
1343-
_, err := db.GetAPIKeyByID(ctx, oldExpiredKey.ID)
1344-
require.NoError(t, err, "old expired key should NOT be deleted when retention is disabled")
1345-
})
1346-
1347-
t.Run("GlobalRetentionFallback", func(t *testing.T) {
1348-
t.Parallel()
1349-
1350-
ctx := testutil.Context(t, testutil.WaitShort)
1351-
1352-
clk := quartz.NewMock(t)
1353-
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
1354-
retentionPeriod := 14 * 24 * time.Hour // 14 days global
1355-
expiredLongAgo := now.Add(-retentionPeriod).Add(-24 * time.Hour) // Expired 15 days ago (should be deleted)
1356-
expiredRecently := now.Add(-retentionPeriod).Add(24 * time.Hour) // Expired 13 days ago (should be kept)
1357-
clk.Set(now).MustWait(ctx)
1358-
1359-
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
1360-
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
1361-
user := dbgen.User(t, db, database.User{})
1279+
oldExpiredTime: now.Add(-365 * 24 * time.Hour), // Expired 1 year ago
1280+
recentExpiredTime: nil,
1281+
activeTime: nil,
1282+
expectOldExpiredDeleted: false,
1283+
expectedKeysRemaining: 1, // old expired is kept
1284+
},
1285+
{
1286+
name: "GlobalRetentionFallback",
1287+
retentionConfig: codersdk.RetentionConfig{
1288+
Global: serpent.Duration(14 * 24 * time.Hour), // 14 days global
1289+
APIKeys: serpent.Duration(0), // Not set, should fall back to global
1290+
},
1291+
oldExpiredTime: now.Add(-15 * 24 * time.Hour), // Expired 15 days ago
1292+
recentExpiredTime: ptr(now.Add(-13 * 24 * time.Hour)), // Expired 13 days ago
1293+
activeTime: nil,
1294+
expectOldExpiredDeleted: true,
1295+
expectedKeysRemaining: 1, // only recent expired remains
1296+
},
1297+
{
1298+
name: "CustomRetention30Days",
1299+
retentionConfig: codersdk.RetentionConfig{
1300+
APIKeys: serpent.Duration(30 * 24 * time.Hour), // 30 days
1301+
},
1302+
oldExpiredTime: now.Add(-31 * 24 * time.Hour), // Expired 31 days ago
1303+
recentExpiredTime: ptr(now.Add(-29 * 24 * time.Hour)), // Expired 29 days ago
1304+
activeTime: nil,
1305+
expectOldExpiredDeleted: true,
1306+
expectedKeysRemaining: 1, // only recent expired remains
1307+
},
1308+
}
13621309

1363-
// Create API key that expired long ago (should be deleted)
1364-
oldExpiredKey, _ := dbgen.APIKey(t, db, database.APIKey{
1365-
UserID: user.ID,
1366-
ExpiresAt: expiredLongAgo,
1367-
TokenName: "old-expired-key",
1368-
})
1310+
for _, tc := range testCases {
1311+
t.Run(tc.name, func(t *testing.T) {
1312+
t.Parallel()
13691313

1370-
// Create API key that expired recently (should be kept)
1371-
recentExpiredKey, _ := dbgen.APIKey(t, db, database.APIKey{
1372-
UserID: user.ID,
1373-
ExpiresAt: expiredRecently,
1374-
TokenName: "recent-expired-key",
1375-
})
1314+
ctx := testutil.Context(t, testutil.WaitShort)
1315+
clk := quartz.NewMock(t)
1316+
clk.Set(now).MustWait(ctx)
13761317

1377-
// Run the purge with global retention (API keys retention is 0, so it falls back)
1378-
done := awaitDoTick(ctx, t, clk)
1379-
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
1380-
Retention: codersdk.RetentionConfig{
1381-
Global: serpent.Duration(retentionPeriod), // Use global
1382-
APIKeys: serpent.Duration(0), // Not set, should fall back to global
1383-
},
1384-
}, clk)
1385-
defer closer.Close()
1386-
testutil.TryReceive(ctx, t, done)
1318+
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
1319+
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
1320+
user := dbgen.User(t, db, database.User{})
13871321

1388-
// Verify results
1389-
_, err := db.GetAPIKeyByID(ctx, oldExpiredKey.ID)
1390-
require.Error(t, err, "old expired key should be deleted via global retention")
1322+
// Create API key that expired long ago.
1323+
oldExpiredKey, _ := dbgen.APIKey(t, db, database.APIKey{
1324+
UserID: user.ID,
1325+
ExpiresAt: tc.oldExpiredTime,
1326+
TokenName: "old-expired-key",
1327+
})
13911328

1392-
_, err = db.GetAPIKeyByID(ctx, recentExpiredKey.ID)
1393-
require.NoError(t, err, "recently expired key should be kept")
1394-
})
1329+
// Create API key that expired recently if specified.
1330+
var recentExpiredKey database.APIKey
1331+
if tc.recentExpiredTime != nil {
1332+
recentExpiredKey, _ = dbgen.APIKey(t, db, database.APIKey{
1333+
UserID: user.ID,
1334+
ExpiresAt: *tc.recentExpiredTime,
1335+
TokenName: "recent-expired-key",
1336+
})
1337+
}
13951338

1396-
t.Run("CustomRetention30Days", func(t *testing.T) {
1397-
t.Parallel()
1339+
// Create API key that hasn't expired yet if specified.
1340+
var activeKey database.APIKey
1341+
if tc.activeTime != nil {
1342+
activeKey, _ = dbgen.APIKey(t, db, database.APIKey{
1343+
UserID: user.ID,
1344+
ExpiresAt: *tc.activeTime,
1345+
TokenName: "active-key",
1346+
})
1347+
}
13981348

1399-
ctx := testutil.Context(t, testutil.WaitShort)
1349+
// Run the purge.
1350+
done := awaitDoTick(ctx, t, clk)
1351+
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
1352+
Retention: tc.retentionConfig,
1353+
}, clk)
1354+
defer closer.Close()
1355+
testutil.TryReceive(ctx, t, done)
14001356

1401-
clk := quartz.NewMock(t)
1402-
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
1403-
retentionPeriod := 30 * 24 * time.Hour // 30 days
1404-
expiredLongAgo := now.Add(-retentionPeriod).Add(-24 * time.Hour) // Expired 31 days ago (should be deleted)
1405-
expiredRecently := now.Add(-retentionPeriod).Add(24 * time.Hour) // Expired 29 days ago (should be kept)
1406-
clk.Set(now).MustWait(ctx)
1357+
// Verify total keys remaining.
1358+
keys, err := db.GetAPIKeysLastUsedAfter(ctx, time.Time{})
1359+
require.NoError(t, err)
1360+
require.Len(t, keys, tc.expectedKeysRemaining, "unexpected number of keys remaining")
14071361

1408-
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
1409-
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
1410-
user := dbgen.User(t, db, database.User{})
1362+
// Verify results.
1363+
_, err = db.GetAPIKeyByID(ctx, oldExpiredKey.ID)
1364+
if tc.expectOldExpiredDeleted {
1365+
require.Error(t, err, "old expired key should be deleted")
1366+
} else {
1367+
require.NoError(t, err, "old expired key should NOT be deleted")
1368+
}
14111369

1412-
// Create API key that expired long ago (should be deleted)
1413-
oldExpiredKey, _ := dbgen.APIKey(t, db, database.APIKey{
1414-
UserID: user.ID,
1415-
ExpiresAt: expiredLongAgo,
1416-
TokenName: "old-expired-key",
1417-
})
1370+
if tc.recentExpiredTime != nil {
1371+
_, err = db.GetAPIKeyByID(ctx, recentExpiredKey.ID)
1372+
require.NoError(t, err, "recently expired key should be kept")
1373+
}
14181374

1419-
// Create API key that expired recently (should be kept)
1420-
recentExpiredKey, _ := dbgen.APIKey(t, db, database.APIKey{
1421-
UserID: user.ID,
1422-
ExpiresAt: expiredRecently,
1423-
TokenName: "recent-expired-key",
1375+
if tc.activeTime != nil {
1376+
_, err = db.GetAPIKeyByID(ctx, activeKey.ID)
1377+
require.NoError(t, err, "active key should be kept")
1378+
}
14241379
})
1380+
}
1381+
}
14251382

1426-
// Run the purge with 30-day retention
1427-
done := awaitDoTick(ctx, t, clk)
1428-
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
1429-
Retention: codersdk.RetentionConfig{
1430-
APIKeys: serpent.Duration(retentionPeriod),
1431-
},
1432-
}, clk)
1433-
defer closer.Close()
1434-
testutil.TryReceive(ctx, t, done)
1435-
1436-
// Verify results
1437-
_, err := db.GetAPIKeyByID(ctx, oldExpiredKey.ID)
1438-
require.Error(t, err, "old expired key should be deleted with 30-day retention")
1439-
1440-
_, err = db.GetAPIKeyByID(ctx, recentExpiredKey.ID)
1441-
require.NoError(t, err, "recently expired key should be kept with 30-day retention")
1442-
})
1383+
// ptr is a helper to create a pointer to a value.
1384+
func ptr[T any](v T) *T {
1385+
return &v
14431386
}

0 commit comments

Comments
 (0)