@@ -1054,192 +1054,135 @@ func TestDeleteOldAIBridgeRecords(t *testing.T) {
10541054func 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