Skip to content

Commit bca5eea

Browse files
committed
test(coderd/database/dbpurge): convert audit logs tests to table-driven
1 parent 93f3a80 commit bca5eea

File tree

1 file changed

+106
-162
lines changed

1 file changed

+106
-162
lines changed

coderd/database/dbpurge/dbpurge_test.go

Lines changed: 106 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,192 +1054,135 @@ func TestDeleteOldAIBridgeRecords(t *testing.T) {
10541054
func TestDeleteOldAuditLogs(t *testing.T) {
10551055
t.Parallel()
10561056

1057-
t.Run("RetentionEnabled", func(t *testing.T) {
1058-
t.Parallel()
1059-
1060-
ctx := testutil.Context(t, testutil.WaitShort)
1061-
1062-
clk := quartz.NewMock(t)
1063-
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
1064-
retentionPeriod := 30 * 24 * time.Hour // 30 days
1065-
afterThreshold := now.Add(-retentionPeriod).Add(-24 * time.Hour) // 31 days ago (older than threshold)
1066-
beforeThreshold := now.Add(-15 * 24 * time.Hour) // 15 days ago (newer than threshold)
1067-
clk.Set(now).MustWait(ctx)
1068-
1069-
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
1070-
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
1071-
user := dbgen.User(t, db, database.User{})
1072-
org := dbgen.Organization(t, db, database.Organization{})
1073-
1074-
// Create old audit log (should be deleted)
1075-
oldLog := dbgen.AuditLog(t, db, database.AuditLog{
1076-
UserID: user.ID,
1077-
OrganizationID: org.ID,
1078-
Time: afterThreshold,
1079-
Action: database.AuditActionCreate,
1080-
ResourceType: database.ResourceTypeWorkspace,
1081-
})
1082-
1083-
// Create recent audit log (should be kept)
1084-
recentLog := dbgen.AuditLog(t, db, database.AuditLog{
1085-
UserID: user.ID,
1086-
OrganizationID: org.ID,
1087-
Time: beforeThreshold,
1088-
Action: database.AuditActionCreate,
1089-
ResourceType: database.ResourceTypeWorkspace,
1090-
})
1057+
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
1058+
retentionPeriod := 30 * 24 * time.Hour
1059+
afterThreshold := now.Add(-retentionPeriod).Add(-24 * time.Hour) // 31 days ago (older than threshold)
1060+
beforeThreshold := now.Add(-15 * 24 * time.Hour) // 15 days ago (newer than threshold)
10911061

1092-
// Run the purge with configured retention period
1093-
done := awaitDoTick(ctx, t, clk)
1094-
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
1095-
Retention: codersdk.RetentionConfig{
1062+
testCases := []struct {
1063+
name string
1064+
retentionConfig codersdk.RetentionConfig
1065+
oldLogTime time.Time
1066+
recentLogTime *time.Time // nil means no recent log created
1067+
expectOldDeleted bool
1068+
expectedLogsRemaining int
1069+
}{
1070+
{
1071+
name: "RetentionEnabled",
1072+
retentionConfig: codersdk.RetentionConfig{
10961073
AuditLogs: serpent.Duration(retentionPeriod),
10971074
},
1098-
}, clk)
1099-
defer closer.Close()
1100-
testutil.TryReceive(ctx, t, done)
1101-
1102-
// Verify results by querying all audit logs
1103-
logs, err := db.GetAuditLogsOffset(ctx, database.GetAuditLogsOffsetParams{
1104-
LimitOpt: 100,
1105-
})
1106-
require.NoError(t, err)
1107-
1108-
logIDs := make([]uuid.UUID, len(logs))
1109-
for i, log := range logs {
1110-
logIDs[i] = log.AuditLog.ID
1111-
}
1112-
1113-
require.NotContains(t, logIDs, oldLog.ID, "old audit log should be deleted")
1114-
require.Contains(t, logIDs, recentLog.ID, "recent audit log should be kept")
1115-
})
1116-
1117-
t.Run("RetentionDisabled", func(t *testing.T) {
1118-
t.Parallel()
1119-
1120-
ctx := testutil.Context(t, testutil.WaitShort)
1121-
1122-
clk := quartz.NewMock(t)
1123-
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
1124-
oldTime := now.Add(-365 * 24 * time.Hour) // 1 year ago
1125-
clk.Set(now).MustWait(ctx)
1126-
1127-
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
1128-
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
1129-
user := dbgen.User(t, db, database.User{})
1130-
org := dbgen.Organization(t, db, database.Organization{})
1131-
1132-
// Create old audit log (should NOT be deleted when retention is 0)
1133-
oldLog := dbgen.AuditLog(t, db, database.AuditLog{
1134-
UserID: user.ID,
1135-
OrganizationID: org.ID,
1136-
Time: oldTime,
1137-
Action: database.AuditActionCreate,
1138-
ResourceType: database.ResourceTypeWorkspace,
1139-
})
1140-
1141-
// Run the purge with retention disabled (0)
1142-
done := awaitDoTick(ctx, t, clk)
1143-
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
1144-
Retention: codersdk.RetentionConfig{
1145-
AuditLogs: serpent.Duration(0), // disabled
1075+
oldLogTime: afterThreshold,
1076+
recentLogTime: &beforeThreshold,
1077+
expectOldDeleted: true,
1078+
expectedLogsRemaining: 1, // only recent log remains
1079+
},
1080+
{
1081+
name: "RetentionDisabled",
1082+
retentionConfig: codersdk.RetentionConfig{
1083+
AuditLogs: serpent.Duration(0),
11461084
},
1147-
}, clk)
1148-
defer closer.Close()
1149-
testutil.TryReceive(ctx, t, done)
1085+
oldLogTime: now.Add(-365 * 24 * time.Hour), // 1 year ago
1086+
recentLogTime: nil,
1087+
expectOldDeleted: false,
1088+
expectedLogsRemaining: 1, // old log is kept
1089+
},
1090+
{
1091+
name: "GlobalRetentionFallback",
1092+
retentionConfig: codersdk.RetentionConfig{
1093+
Global: serpent.Duration(retentionPeriod),
1094+
AuditLogs: serpent.Duration(0), // Not set, should fall back to global
1095+
},
1096+
oldLogTime: afterThreshold,
1097+
recentLogTime: &beforeThreshold,
1098+
expectOldDeleted: true,
1099+
expectedLogsRemaining: 1, // only recent log remains
1100+
},
1101+
}
11501102

1151-
// Verify old log is still present
1152-
logs, err := db.GetAuditLogsOffset(ctx, database.GetAuditLogsOffsetParams{
1153-
LimitOpt: 100,
1154-
})
1155-
require.NoError(t, err)
1103+
for _, tc := range testCases {
1104+
t.Run(tc.name, func(t *testing.T) {
1105+
t.Parallel()
11561106

1157-
logIDs := make([]uuid.UUID, len(logs))
1158-
for i, log := range logs {
1159-
logIDs[i] = log.AuditLog.ID
1160-
}
1107+
ctx := testutil.Context(t, testutil.WaitShort)
1108+
clk := quartz.NewMock(t)
1109+
clk.Set(now).MustWait(ctx)
11611110

1162-
require.Contains(t, logIDs, oldLog.ID, "old audit log should NOT be deleted when retention is disabled")
1163-
})
1111+
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
1112+
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
11641113

1165-
t.Run("GlobalRetentionFallback", func(t *testing.T) {
1166-
t.Parallel()
1114+
// Setup test fixtures.
1115+
user := dbgen.User(t, db, database.User{})
1116+
org := dbgen.Organization(t, db, database.Organization{})
11671117

1168-
ctx := testutil.Context(t, testutil.WaitShort)
1118+
// Create old audit log.
1119+
oldLog := dbgen.AuditLog(t, db, database.AuditLog{
1120+
UserID: user.ID,
1121+
OrganizationID: org.ID,
1122+
Time: tc.oldLogTime,
1123+
Action: database.AuditActionCreate,
1124+
ResourceType: database.ResourceTypeWorkspace,
1125+
})
11691126

1170-
clk := quartz.NewMock(t)
1171-
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
1172-
retentionPeriod := 30 * 24 * time.Hour // 30 days
1173-
afterThreshold := now.Add(-retentionPeriod).Add(-24 * time.Hour) // 31 days ago (older than threshold)
1174-
beforeThreshold := now.Add(-15 * 24 * time.Hour) // 15 days ago (newer than threshold)
1175-
clk.Set(now).MustWait(ctx)
1127+
// Create recent audit log if specified.
1128+
var recentLog database.AuditLog
1129+
if tc.recentLogTime != nil {
1130+
recentLog = dbgen.AuditLog(t, db, database.AuditLog{
1131+
UserID: user.ID,
1132+
OrganizationID: org.ID,
1133+
Time: *tc.recentLogTime,
1134+
Action: database.AuditActionCreate,
1135+
ResourceType: database.ResourceTypeWorkspace,
1136+
})
1137+
}
11761138

1177-
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
1178-
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
1179-
user := dbgen.User(t, db, database.User{})
1180-
org := dbgen.Organization(t, db, database.Organization{})
1139+
// Run the purge.
1140+
done := awaitDoTick(ctx, t, clk)
1141+
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
1142+
Retention: tc.retentionConfig,
1143+
}, clk)
1144+
defer closer.Close()
1145+
testutil.TryReceive(ctx, t, done)
11811146

1182-
// Create old audit log (should be deleted)
1183-
oldLog := dbgen.AuditLog(t, db, database.AuditLog{
1184-
UserID: user.ID,
1185-
OrganizationID: org.ID,
1186-
Time: afterThreshold,
1187-
Action: database.AuditActionCreate,
1188-
ResourceType: database.ResourceTypeWorkspace,
1189-
})
1147+
// Verify results.
1148+
logs, err := db.GetAuditLogsOffset(ctx, database.GetAuditLogsOffsetParams{
1149+
LimitOpt: 100,
1150+
})
1151+
require.NoError(t, err)
1152+
require.Len(t, logs, tc.expectedLogsRemaining, "unexpected number of logs remaining")
11901153

1191-
// Create recent audit log (should be kept)
1192-
recentLog := dbgen.AuditLog(t, db, database.AuditLog{
1193-
UserID: user.ID,
1194-
OrganizationID: org.ID,
1195-
Time: beforeThreshold,
1196-
Action: database.AuditActionCreate,
1197-
ResourceType: database.ResourceTypeWorkspace,
1198-
})
1154+
logIDs := make([]uuid.UUID, len(logs))
1155+
for i, log := range logs {
1156+
logIDs[i] = log.AuditLog.ID
1157+
}
11991158

1200-
// Run the purge with global retention (audit logs retention is 0, so it falls back)
1201-
done := awaitDoTick(ctx, t, clk)
1202-
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
1203-
Retention: codersdk.RetentionConfig{
1204-
Global: serpent.Duration(retentionPeriod), // Use global
1205-
AuditLogs: serpent.Duration(0), // Not set, should fall back to global
1206-
},
1207-
}, clk)
1208-
defer closer.Close()
1209-
testutil.TryReceive(ctx, t, done)
1159+
if tc.expectOldDeleted {
1160+
require.NotContains(t, logIDs, oldLog.ID, "old audit log should be deleted")
1161+
} else {
1162+
require.Contains(t, logIDs, oldLog.ID, "old audit log should NOT be deleted")
1163+
}
12101164

1211-
// Verify results
1212-
logs, err := db.GetAuditLogsOffset(ctx, database.GetAuditLogsOffsetParams{
1213-
LimitOpt: 100,
1165+
if tc.recentLogTime != nil {
1166+
require.Contains(t, logIDs, recentLog.ID, "recent audit log should be kept")
1167+
}
12141168
})
1215-
require.NoError(t, err)
1216-
1217-
logIDs := make([]uuid.UUID, len(logs))
1218-
for i, log := range logs {
1219-
logIDs[i] = log.AuditLog.ID
1220-
}
1221-
1222-
require.NotContains(t, logIDs, oldLog.ID, "old audit log should be deleted via global retention")
1223-
require.Contains(t, logIDs, recentLog.ID, "recent audit log should be kept")
1224-
})
1169+
}
12251170

1171+
// ConnectionEventsNotDeleted is a special case that tests multiple audit
1172+
// action types, so it's kept as a separate subtest.
12261173
t.Run("ConnectionEventsNotDeleted", func(t *testing.T) {
12271174
t.Parallel()
12281175

12291176
ctx := testutil.Context(t, testutil.WaitShort)
1230-
12311177
clk := quartz.NewMock(t)
1232-
now := time.Date(2025, 1, 15, 7, 30, 0, 0, time.UTC)
1233-
retentionPeriod := 30 * 24 * time.Hour // 30 days
1234-
afterThreshold := now.Add(-retentionPeriod).Add(-24 * time.Hour) // 31 days ago (older than threshold)
12351178
clk.Set(now).MustWait(ctx)
12361179

12371180
db, _ := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
12381181
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
12391182
user := dbgen.User(t, db, database.User{})
12401183
org := dbgen.Organization(t, db, database.Organization{})
12411184

1242-
// Create old connection events (should NOT be deleted by audit logs retention)
1185+
// Create old connection events (should NOT be deleted by audit logs retention).
12431186
oldConnectLog := dbgen.AuditLog(t, db, database.AuditLog{
12441187
UserID: user.ID,
12451188
OrganizationID: org.ID,
@@ -1272,7 +1215,7 @@ func TestDeleteOldAuditLogs(t *testing.T) {
12721215
ResourceType: database.ResourceTypeWorkspace,
12731216
})
12741217

1275-
// Create old non-connection audit log (should be deleted)
1218+
// Create old non-connection audit log (should be deleted).
12761219
oldCreateLog := dbgen.AuditLog(t, db, database.AuditLog{
12771220
UserID: user.ID,
12781221
OrganizationID: org.ID,
@@ -1281,7 +1224,7 @@ func TestDeleteOldAuditLogs(t *testing.T) {
12811224
ResourceType: database.ResourceTypeWorkspace,
12821225
})
12831226

1284-
// Run the purge with audit logs retention enabled
1227+
// Run the purge with audit logs retention enabled.
12851228
done := awaitDoTick(ctx, t, clk)
12861229
closer := dbpurge.New(ctx, logger, db, &codersdk.DeploymentValues{
12871230
Retention: codersdk.RetentionConfig{
@@ -1291,24 +1234,25 @@ func TestDeleteOldAuditLogs(t *testing.T) {
12911234
defer closer.Close()
12921235
testutil.TryReceive(ctx, t, done)
12931236

1294-
// Verify results
1237+
// Verify results.
12951238
logs, err := db.GetAuditLogsOffset(ctx, database.GetAuditLogsOffsetParams{
12961239
LimitOpt: 100,
12971240
})
12981241
require.NoError(t, err)
1242+
require.Len(t, logs, 4, "should have 4 connection event logs remaining")
12991243

13001244
logIDs := make([]uuid.UUID, len(logs))
13011245
for i, log := range logs {
13021246
logIDs[i] = log.AuditLog.ID
13031247
}
13041248

1305-
// Connection events should NOT be deleted by audit logs retention
1249+
// Connection events should NOT be deleted by audit logs retention.
13061250
require.Contains(t, logIDs, oldConnectLog.ID, "old connect log should NOT be deleted by audit logs retention")
13071251
require.Contains(t, logIDs, oldDisconnectLog.ID, "old disconnect log should NOT be deleted by audit logs retention")
13081252
require.Contains(t, logIDs, oldOpenLog.ID, "old open log should NOT be deleted by audit logs retention")
13091253
require.Contains(t, logIDs, oldCloseLog.ID, "old close log should NOT be deleted by audit logs retention")
13101254

1311-
// Non-connection event should be deleted
1255+
// Non-connection event should be deleted.
13121256
require.NotContains(t, logIDs, oldCreateLog.ID, "old create log should be deleted by audit logs retention")
13131257
})
13141258
}

0 commit comments

Comments
 (0)