Skip to content

Commit 07ae594

Browse files
committed
test(coderd/database/dbpurge): convert audit logs tests to table-driven
1 parent 3f24dd9 commit 07ae594

File tree

1 file changed

+111
-167
lines changed

1 file changed

+111
-167
lines changed

coderd/database/dbpurge/dbpurge_test.go

Lines changed: 111 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,192 +1148,135 @@ func TestDeleteOldAIBridgeRecords(t *testing.T) {
11481148
func TestDeleteOldAuditLogs(t *testing.T) {
11491149
t.Parallel()
11501150

1151-
t.Run("RetentionEnabled", func(t *testing.T) {
1152-
t.Parallel()
1153-
1154-
ctx := testutil.Context(t, testutil.WaitShort)
1155-
1156-
clk := quartz.NewMock(t)
1157-
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
1158-
retentionPeriod := 30 * 24 * time.Hour // 30 days
1159-
afterThreshold := now.Add(-retentionPeriod).Add(-24 * time.Hour) // 31 days ago (older than threshold)
1160-
beforeThreshold := now.Add(-15 * 24 * time.Hour) // 15 days ago (newer than threshold)
1161-
clk.Set(now).MustWait(ctx)
1162-
1163-
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
1164-
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
1165-
user := dbgen.User(t, db, database.User{})
1166-
org := dbgen.Organization(t, db, database.Organization{})
1167-
1168-
// Create old audit log (should be deleted)
1169-
oldLog := dbgen.AuditLog(t, db, database.AuditLog{
1170-
UserID: user.ID,
1171-
OrganizationID: org.ID,
1172-
Time: afterThreshold,
1173-
Action: database.AuditActionCreate,
1174-
ResourceType: database.ResourceTypeWorkspace,
1175-
})
1176-
1177-
// Create recent audit log (should be kept)
1178-
recentLog := dbgen.AuditLog(t, db, database.AuditLog{
1179-
UserID: user.ID,
1180-
OrganizationID: org.ID,
1181-
Time: beforeThreshold,
1182-
Action: database.AuditActionCreate,
1183-
ResourceType: database.ResourceTypeWorkspace,
1184-
})
1185-
1186-
// Run the purge with configured retention period
1187-
done := awaitDoTick(ctx, t, clk)
1188-
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
1189-
Retention: codersdk.RetentionConfig{
1151+
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
1152+
retentionPeriod := 30 * 24 * time.Hour
1153+
afterThreshold := now.Add(-retentionPeriod).Add(-24 * time.Hour) // 31 days ago (older than threshold)
1154+
beforeThreshold := now.Add(-15 * 24 * time.Hour) // 15 days ago (newer than threshold)
1155+
1156+
testCases := []struct {
1157+
name string
1158+
retentionConfig codersdk.RetentionConfig
1159+
oldLogTime time.Time
1160+
recentLogTime *time.Time // nil means no recent log created
1161+
expectOldDeleted bool
1162+
expectedLogsRemaining int
1163+
}{
1164+
{
1165+
name: "RetentionEnabled",
1166+
retentionConfig: codersdk.RetentionConfig{
11901167
AuditLogs: serpent.Duration(retentionPeriod),
11911168
},
1192-
}, clk)
1193-
defer closer.Close()
1194-
testutil.TryReceive(ctx, t, done)
1195-
1196-
// Verify results by querying all audit logs
1197-
logs, err := db.GetAuditLogsOffset(ctx, database.GetAuditLogsOffsetParams{
1198-
LimitOpt: 100,
1199-
})
1200-
require.NoError(t, err)
1201-
1202-
logIDs := make([]uuid.UUID, len(logs))
1203-
for i, log := range logs {
1204-
logIDs[i] = log.AuditLog.ID
1205-
}
1206-
1207-
require.NotContains(t, logIDs, oldLog.ID, "old audit log should be deleted")
1208-
require.Contains(t, logIDs, recentLog.ID, "recent audit log should be kept")
1209-
})
1210-
1211-
t.Run("RetentionDisabled", func(t *testing.T) {
1212-
t.Parallel()
1213-
1214-
ctx := testutil.Context(t, testutil.WaitShort)
1215-
1216-
clk := quartz.NewMock(t)
1217-
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
1218-
oldTime := now.Add(-365 * 24 * time.Hour) // 1 year ago
1219-
clk.Set(now).MustWait(ctx)
1220-
1221-
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
1222-
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
1223-
user := dbgen.User(t, db, database.User{})
1224-
org := dbgen.Organization(t, db, database.Organization{})
1225-
1226-
// Create old audit log (should NOT be deleted when retention is 0)
1227-
oldLog := dbgen.AuditLog(t, db, database.AuditLog{
1228-
UserID: user.ID,
1229-
OrganizationID: org.ID,
1230-
Time: oldTime,
1231-
Action: database.AuditActionCreate,
1232-
ResourceType: database.ResourceTypeWorkspace,
1233-
})
1234-
1235-
// Run the purge with retention disabled (0)
1236-
done := awaitDoTick(ctx, t, clk)
1237-
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
1238-
Retention: codersdk.RetentionConfig{
1239-
AuditLogs: serpent.Duration(0), // disabled
1169+
oldLogTime: afterThreshold,
1170+
recentLogTime: &beforeThreshold,
1171+
expectOldDeleted: true,
1172+
expectedLogsRemaining: 1, // only recent log remains
1173+
},
1174+
{
1175+
name: "RetentionDisabled",
1176+
retentionConfig: codersdk.RetentionConfig{
1177+
AuditLogs: serpent.Duration(0),
12401178
},
1241-
}, clk)
1242-
defer closer.Close()
1243-
testutil.TryReceive(ctx, t, done)
1244-
1245-
// Verify old log is still present
1246-
logs, err := db.GetAuditLogsOffset(ctx, database.GetAuditLogsOffsetParams{
1247-
LimitOpt: 100,
1248-
})
1249-
require.NoError(t, err)
1250-
1251-
logIDs := make([]uuid.UUID, len(logs))
1252-
for i, log := range logs {
1253-
logIDs[i] = log.AuditLog.ID
1254-
}
1255-
1256-
require.Contains(t, logIDs, oldLog.ID, "old audit log should NOT be deleted when retention is disabled")
1257-
})
1258-
1259-
t.Run("GlobalRetentionFallback", func(t *testing.T) {
1260-
t.Parallel()
1179+
oldLogTime: now.Add(-365 * 24 * time.Hour), // 1 year ago
1180+
recentLogTime: nil,
1181+
expectOldDeleted: false,
1182+
expectedLogsRemaining: 1, // old log is kept
1183+
},
1184+
{
1185+
name: "GlobalRetentionFallback",
1186+
retentionConfig: codersdk.RetentionConfig{
1187+
Global: serpent.Duration(retentionPeriod),
1188+
AuditLogs: serpent.Duration(0), // Not set, should fall back to global
1189+
},
1190+
oldLogTime: afterThreshold,
1191+
recentLogTime: &beforeThreshold,
1192+
expectOldDeleted: true,
1193+
expectedLogsRemaining: 1, // only recent log remains
1194+
},
1195+
}
12611196

1262-
ctx := testutil.Context(t, testutil.WaitShort)
1197+
for _, tc := range testCases {
1198+
t.Run(tc.name, func(t *testing.T) {
1199+
t.Parallel()
12631200

1264-
clk := quartz.NewMock(t)
1265-
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
1266-
retentionPeriod := 30 * 24 * time.Hour // 30 days
1267-
afterThreshold := now.Add(-retentionPeriod).Add(-24 * time.Hour) // 31 days ago (older than threshold)
1268-
beforeThreshold := now.Add(-15 * 24 * time.Hour) // 15 days ago (newer than threshold)
1269-
clk.Set(now).MustWait(ctx)
1201+
ctx := testutil.Context(t, testutil.WaitShort)
1202+
clk := quartz.NewMock(t)
1203+
clk.Set(now).MustWait(ctx)
1204+
1205+
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
1206+
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
1207+
1208+
// Setup test fixtures.
1209+
user := dbgen.User(t, db, database.User{})
1210+
org := dbgen.Organization(t, db, database.Organization{})
1211+
1212+
// Create old audit log.
1213+
oldLog := dbgen.AuditLog(t, db, database.AuditLog{
1214+
UserID: user.ID,
1215+
OrganizationID: org.ID,
1216+
Time: tc.oldLogTime,
1217+
Action: database.AuditActionCreate,
1218+
ResourceType: database.ResourceTypeWorkspace,
1219+
})
12701220

1271-
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
1272-
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
1273-
user := dbgen.User(t, db, database.User{})
1274-
org := dbgen.Organization(t, db, database.Organization{})
1221+
// Create recent audit log if specified.
1222+
var recentLog database.AuditLog
1223+
if tc.recentLogTime != nil {
1224+
recentLog = dbgen.AuditLog(t, db, database.AuditLog{
1225+
UserID: user.ID,
1226+
OrganizationID: org.ID,
1227+
Time: *tc.recentLogTime,
1228+
Action: database.AuditActionCreate,
1229+
ResourceType: database.ResourceTypeWorkspace,
1230+
})
1231+
}
12751232

1276-
// Create old audit log (should be deleted)
1277-
oldLog := dbgen.AuditLog(t, db, database.AuditLog{
1278-
UserID: user.ID,
1279-
OrganizationID: org.ID,
1280-
Time: afterThreshold,
1281-
Action: database.AuditActionCreate,
1282-
ResourceType: database.ResourceTypeWorkspace,
1283-
})
1233+
// Run the purge.
1234+
done := awaitDoTick(ctx, t, clk)
1235+
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
1236+
Retention: tc.retentionConfig,
1237+
}, clk)
1238+
defer closer.Close()
1239+
testutil.TryReceive(ctx, t, done)
1240+
1241+
// Verify results.
1242+
logs, err := db.GetAuditLogsOffset(ctx, database.GetAuditLogsOffsetParams{
1243+
LimitOpt: 100,
1244+
})
1245+
require.NoError(t, err)
1246+
require.Len(t, logs, tc.expectedLogsRemaining, "unexpected number of logs remaining")
12841247

1285-
// Create recent audit log (should be kept)
1286-
recentLog := dbgen.AuditLog(t, db, database.AuditLog{
1287-
UserID: user.ID,
1288-
OrganizationID: org.ID,
1289-
Time: beforeThreshold,
1290-
Action: database.AuditActionCreate,
1291-
ResourceType: database.ResourceTypeWorkspace,
1292-
})
1248+
logIDs := make([]uuid.UUID, len(logs))
1249+
for i, log := range logs {
1250+
logIDs[i] = log.AuditLog.ID
1251+
}
12931252

1294-
// Run the purge with global retention (audit logs retention is 0, so it falls back)
1295-
done := awaitDoTick(ctx, t, clk)
1296-
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
1297-
Retention: codersdk.RetentionConfig{
1298-
Global: serpent.Duration(retentionPeriod), // Use global
1299-
AuditLogs: serpent.Duration(0), // Not set, should fall back to global
1300-
},
1301-
}, clk)
1302-
defer closer.Close()
1303-
testutil.TryReceive(ctx, t, done)
1253+
if tc.expectOldDeleted {
1254+
require.NotContains(t, logIDs, oldLog.ID, "old audit log should be deleted")
1255+
} else {
1256+
require.Contains(t, logIDs, oldLog.ID, "old audit log should NOT be deleted")
1257+
}
13041258

1305-
// Verify results
1306-
logs, err := db.GetAuditLogsOffset(ctx, database.GetAuditLogsOffsetParams{
1307-
LimitOpt: 100,
1259+
if tc.recentLogTime != nil {
1260+
require.Contains(t, logIDs, recentLog.ID, "recent audit log should be kept")
1261+
}
13081262
})
1309-
require.NoError(t, err)
1310-
1311-
logIDs := make([]uuid.UUID, len(logs))
1312-
for i, log := range logs {
1313-
logIDs[i] = log.AuditLog.ID
1314-
}
1315-
1316-
require.NotContains(t, logIDs, oldLog.ID, "old audit log should be deleted via global retention")
1317-
require.Contains(t, logIDs, recentLog.ID, "recent audit log should be kept")
1318-
})
1263+
}
13191264

1265+
// ConnectionEventsNotDeleted is a special case that tests multiple audit
1266+
// action types, so it's kept as a separate subtest.
13201267
t.Run("ConnectionEventsNotDeleted", func(t *testing.T) {
13211268
t.Parallel()
13221269

13231270
ctx := testutil.Context(t, testutil.WaitShort)
1324-
13251271
clk := quartz.NewMock(t)
1326-
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
1327-
retentionPeriod := 30 * 24 * time.Hour // 30 days
1328-
afterThreshold := now.Add(-retentionPeriod).Add(-24 * time.Hour) // 31 days ago (older than threshold)
13291272
clk.Set(now).MustWait(ctx)
13301273

13311274
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
13321275
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
13331276
user := dbgen.User(t, db, database.User{})
13341277
org := dbgen.Organization(t, db, database.Organization{})
13351278

1336-
// Create old connection events (should NOT be deleted by audit logs retention)
1279+
// Create old connection events (should NOT be deleted by audit logs retention).
13371280
oldConnectLog := dbgen.AuditLog(t, db, database.AuditLog{
13381281
UserID: user.ID,
13391282
OrganizationID: org.ID,
@@ -1366,7 +1309,7 @@ func TestDeleteOldAuditLogs(t *testing.T) {
13661309
ResourceType: database.ResourceTypeWorkspace,
13671310
})
13681311

1369-
// Create old non-connection audit log (should be deleted)
1312+
// Create old non-connection audit log (should be deleted).
13701313
oldCreateLog := dbgen.AuditLog(t, db, database.AuditLog{
13711314
UserID: user.ID,
13721315
OrganizationID: org.ID,
@@ -1375,7 +1318,7 @@ func TestDeleteOldAuditLogs(t *testing.T) {
13751318
ResourceType: database.ResourceTypeWorkspace,
13761319
})
13771320

1378-
// Run the purge with audit logs retention enabled
1321+
// Run the purge with audit logs retention enabled.
13791322
done := awaitDoTick(ctx, t, clk)
13801323
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
13811324
Retention: codersdk.RetentionConfig{
@@ -1385,24 +1328,25 @@ func TestDeleteOldAuditLogs(t *testing.T) {
13851328
defer closer.Close()
13861329
testutil.TryReceive(ctx, t, done)
13871330

1388-
// Verify results
1331+
// Verify results.
13891332
logs, err := db.GetAuditLogsOffset(ctx, database.GetAuditLogsOffsetParams{
13901333
LimitOpt: 100,
13911334
})
13921335
require.NoError(t, err)
1336+
require.Len(t, logs, 4, "should have 4 connection event logs remaining")
13931337

13941338
logIDs := make([]uuid.UUID, len(logs))
13951339
for i, log := range logs {
13961340
logIDs[i] = log.AuditLog.ID
13971341
}
13981342

1399-
// Connection events should NOT be deleted by audit logs retention
1343+
// Connection events should NOT be deleted by audit logs retention.
14001344
require.Contains(t, logIDs, oldConnectLog.ID, "old connect log should NOT be deleted by audit logs retention")
14011345
require.Contains(t, logIDs, oldDisconnectLog.ID, "old disconnect log should NOT be deleted by audit logs retention")
14021346
require.Contains(t, logIDs, oldOpenLog.ID, "old open log should NOT be deleted by audit logs retention")
14031347
require.Contains(t, logIDs, oldCloseLog.ID, "old close log should NOT be deleted by audit logs retention")
14041348

1405-
// Non-connection event should be deleted
1349+
// Non-connection event should be deleted.
14061350
require.NotContains(t, logIDs, oldCreateLog.ID, "old create log should be deleted by audit logs retention")
14071351
})
14081352
}

0 commit comments

Comments
 (0)