@@ -1148,192 +1148,130 @@ func TestDeleteOldAIBridgeRecords(t *testing.T) {
11481148func TestDeleteOldAuditLogs (t * testing.T ) {
11491149t .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+ }{
1163+ {
1164+ name :"RetentionEnabled" ,
1165+ retentionConfig : codersdk.RetentionConfig {
11901166AuditLogs :serpent .Duration (retentionPeriod ),
11911167},
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
1168+ oldLogTime :afterThreshold ,
1169+ recentLogTime :& beforeThreshold ,
1170+ expectOldDeleted :true ,
1171+ },
1172+ {
1173+ name :"RetentionDisabled" ,
1174+ retentionConfig : codersdk.RetentionConfig {
1175+ AuditLogs :serpent .Duration (0 ),
12401176},
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 ()
1177+ oldLogTime :now .Add (- 365 * 24 * time .Hour ),// 1 year ago
1178+ recentLogTime :nil ,
1179+ expectOldDeleted :false ,
1180+ },
1181+ {
1182+ name :"GlobalRetentionFallback" ,
1183+ retentionConfig : codersdk.RetentionConfig {
1184+ Global :serpent .Duration (retentionPeriod ),
1185+ AuditLogs :serpent .Duration (0 ),// Not set, should fall back to global
1186+ },
1187+ oldLogTime :afterThreshold ,
1188+ recentLogTime :& beforeThreshold ,
1189+ expectOldDeleted :true ,
1190+ },
1191+ }
12611192
1262- ctx := testutil .Context (t ,testutil .WaitShort )
1193+ for _ ,tc := range testCases {
1194+ t .Run (tc .name ,func (t * testing.T ) {
1195+ t .Parallel ()
12631196
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 )
1197+ ctx := testutil .Context (t ,testutil .WaitShort )
1198+ clk := quartz .NewMock (t )
1199+ clk .Set (now ).MustWait (ctx )
1200+
1201+ db ,_ := dbtestutil .NewDB (t ,dbtestutil .WithDumpOnFailure ())
1202+ logger := slogtest .Make (t ,& slogtest.Options {IgnoreErrors :true })
1203+
1204+ // Setup test fixtures.
1205+ user := dbgen .User (t ,db , database.User {})
1206+ org := dbgen .Organization (t ,db , database.Organization {})
1207+
1208+ // Create old audit log.
1209+ oldLog := dbgen .AuditLog (t ,db , database.AuditLog {
1210+ UserID :user .ID ,
1211+ OrganizationID :org .ID ,
1212+ Time :tc .oldLogTime ,
1213+ Action :database .AuditActionCreate ,
1214+ ResourceType :database .ResourceTypeWorkspace ,
1215+ })
12701216
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 {})
1217+ // Create recent audit log if specified.
1218+ var recentLog database.AuditLog
1219+ if tc .recentLogTime != nil {
1220+ recentLog = dbgen .AuditLog (t ,db , database.AuditLog {
1221+ UserID :user .ID ,
1222+ OrganizationID :org .ID ,
1223+ Time :* tc .recentLogTime ,
1224+ Action :database .AuditActionCreate ,
1225+ ResourceType :database .ResourceTypeWorkspace ,
1226+ })
1227+ }
12751228
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- })
1229+ // Run the purge.
1230+ done := awaitDoTick (ctx ,t ,clk )
1231+ closer := dbpurge .New (ctx ,logger ,db ,& codersdk.DeploymentValues {
1232+ Retention :tc .retentionConfig ,
1233+ },clk )
1234+ defer closer .Close ()
1235+ testutil .TryReceive (ctx ,t ,done )
1236+
1237+ // Verify results.
1238+ logs ,err := db .GetAuditLogsOffset (ctx , database.GetAuditLogsOffsetParams {
1239+ LimitOpt :100 ,
1240+ })
1241+ require .NoError (t ,err )
12841242
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- })
1243+ logIDs := make ([]uuid.UUID ,len (logs ))
1244+ for i ,log := range logs {
1245+ logIDs [i ]= log .AuditLog .ID
1246+ }
12931247
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 )
1248+ if tc .expectOldDeleted {
1249+ require .NotContains (t ,logIDs ,oldLog .ID ,"old audit log should be deleted" )
1250+ }else {
1251+ require .Contains (t ,logIDs ,oldLog .ID ,"old audit log should NOT be deleted" )
1252+ }
13041253
1305- // Verify results
1306- logs , err := db . GetAuditLogsOffset ( ctx , database. GetAuditLogsOffsetParams {
1307- LimitOpt : 100 ,
1254+ if tc . recentLogTime != nil {
1255+ require . Contains ( t , logIDs , recentLog . ID , "recent audit log should be kept" )
1256+ }
13081257})
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- })
1258+ }
13191259
1260+ // ConnectionEventsNotDeleted is a special case that tests multiple audit
1261+ // action types, so it's kept as a separate subtest.
13201262t .Run ("ConnectionEventsNotDeleted" ,func (t * testing.T ) {
13211263t .Parallel ()
13221264
13231265ctx := testutil .Context (t ,testutil .WaitShort )
1324-
13251266clk := 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)
13291267clk .Set (now ).MustWait (ctx )
13301268
13311269db ,_ := dbtestutil .NewDB (t ,dbtestutil .WithDumpOnFailure ())
13321270logger := slogtest .Make (t ,& slogtest.Options {IgnoreErrors :true })
13331271user := dbgen .User (t ,db , database.User {})
13341272org := dbgen .Organization (t ,db , database.Organization {})
13351273
1336- // Create old connection events (should NOT be deleted by audit logs retention)
1274+ // Create old connection events (should NOT be deleted by audit logs retention).
13371275oldConnectLog := dbgen .AuditLog (t ,db , database.AuditLog {
13381276UserID :user .ID ,
13391277OrganizationID :org .ID ,
@@ -1366,7 +1304,7 @@ func TestDeleteOldAuditLogs(t *testing.T) {
13661304ResourceType :database .ResourceTypeWorkspace ,
13671305})
13681306
1369- // Create old non-connection audit log (should be deleted)
1307+ // Create old non-connection audit log (should be deleted).
13701308oldCreateLog := dbgen .AuditLog (t ,db , database.AuditLog {
13711309UserID :user .ID ,
13721310OrganizationID :org .ID ,
@@ -1375,7 +1313,7 @@ func TestDeleteOldAuditLogs(t *testing.T) {
13751313ResourceType :database .ResourceTypeWorkspace ,
13761314})
13771315
1378- // Run the purge with audit logs retention enabled
1316+ // Run the purge with audit logs retention enabled.
13791317done := awaitDoTick (ctx ,t ,clk )
13801318closer := dbpurge .New (ctx ,logger ,db ,& codersdk.DeploymentValues {
13811319Retention : codersdk.RetentionConfig {
@@ -1385,7 +1323,7 @@ func TestDeleteOldAuditLogs(t *testing.T) {
13851323defer closer .Close ()
13861324testutil .TryReceive (ctx ,t ,done )
13871325
1388- // Verify results
1326+ // Verify results.
13891327logs ,err := db .GetAuditLogsOffset (ctx , database.GetAuditLogsOffsetParams {
13901328LimitOpt :100 ,
13911329})
@@ -1396,13 +1334,13 @@ func TestDeleteOldAuditLogs(t *testing.T) {
13961334logIDs [i ]= log .AuditLog .ID
13971335}
13981336
1399- // Connection events should NOT be deleted by audit logs retention
1337+ // Connection events should NOT be deleted by audit logs retention.
14001338require .Contains (t ,logIDs ,oldConnectLog .ID ,"old connect log should NOT be deleted by audit logs retention" )
14011339require .Contains (t ,logIDs ,oldDisconnectLog .ID ,"old disconnect log should NOT be deleted by audit logs retention" )
14021340require .Contains (t ,logIDs ,oldOpenLog .ID ,"old open log should NOT be deleted by audit logs retention" )
14031341require .Contains (t ,logIDs ,oldCloseLog .ID ,"old close log should NOT be deleted by audit logs retention" )
14041342
1405- // Non-connection event should be deleted
1343+ // Non-connection event should be deleted.
14061344require .NotContains (t ,logIDs ,oldCreateLog .ID ,"old create log should be deleted by audit logs retention" )
14071345})
14081346}