@@ -762,218 +762,135 @@ func TestDeleteOldTelemetryHeartbeats(t *testing.T) {
762762func TestDeleteOldConnectionLogs (t * testing.T ) {
763763t .Parallel ()
764764
765- t .Run ("RetentionEnabled" ,func (t * testing.T ) {
766- t .Parallel ()
767-
768- ctx := testutil .Context (t ,testutil .WaitShort )
769-
770- clk := quartz .NewMock (t )
771- now := time .Date (2025 ,1 ,15 ,7 ,30 ,0 ,0 ,time .UTC )
772- retentionPeriod := 30 * 24 * time .Hour // 30 days
773- afterThreshold := now .Add (- retentionPeriod ).Add (- 24 * time .Hour )// 31 days ago (older than threshold)
774- beforeThreshold := now .Add (- 15 * 24 * time .Hour )// 15 days ago (newer than threshold)
775- clk .Set (now ).MustWait (ctx )
776-
777- db ,_ := dbtestutil .NewDB (t ,dbtestutil .WithDumpOnFailure ())
778- logger := slogtest .Make (t ,& slogtest.Options {IgnoreErrors :true })
779- user := dbgen .User (t ,db , database.User {})
780- org := dbgen .Organization (t ,db , database.Organization {})
781- _ = dbgen .OrganizationMember (t ,db , database.OrganizationMember {UserID :user .ID ,OrganizationID :org .ID })
782- tv := dbgen .TemplateVersion (t ,db , database.TemplateVersion {OrganizationID :org .ID ,CreatedBy :user .ID })
783- tmpl := dbgen .Template (t ,db , database.Template {OrganizationID :org .ID ,ActiveVersionID :tv .ID ,CreatedBy :user .ID })
784- workspace := dbgen .Workspace (t ,db , database.WorkspaceTable {
785- OwnerID :user .ID ,
786- OrganizationID :org .ID ,
787- TemplateID :tmpl .ID ,
788- })
789-
790- // Create old connection log (should be deleted)
791- oldLog := dbgen .ConnectionLog (t ,db , database.UpsertConnectionLogParams {
792- ID :uuid .New (),
793- Time :afterThreshold ,
794- OrganizationID :org .ID ,
795- WorkspaceOwnerID :user .ID ,
796- WorkspaceID :workspace .ID ,
797- WorkspaceName :workspace .Name ,
798- AgentName :"agent1" ,
799- Type :database .ConnectionTypeSsh ,
800- ConnectionStatus :database .ConnectionStatusConnected ,
801- })
802-
803- // Create recent connection log (should be kept)
804- recentLog := dbgen .ConnectionLog (t ,db , database.UpsertConnectionLogParams {
805- ID :uuid .New (),
806- Time :beforeThreshold ,
807- OrganizationID :org .ID ,
808- WorkspaceOwnerID :user .ID ,
809- WorkspaceID :workspace .ID ,
810- WorkspaceName :workspace .Name ,
811- AgentName :"agent2" ,
812- Type :database .ConnectionTypeSsh ,
813- ConnectionStatus :database .ConnectionStatusConnected ,
814- })
815-
816- // Run the purge with configured retention period
817- done := awaitDoTick (ctx ,t ,clk )
818- closer := dbpurge .New (ctx ,logger ,db ,& codersdk.DeploymentValues {
819- Retention : codersdk.RetentionConfig {
765+ now := time .Date (2025 ,1 ,15 ,7 ,30 ,0 ,0 ,time .UTC )
766+ retentionPeriod := 30 * 24 * time .Hour
767+ afterThreshold := now .Add (- retentionPeriod ).Add (- 24 * time .Hour )// 31 days ago (older than threshold)
768+ beforeThreshold := now .Add (- 15 * 24 * time .Hour )// 15 days ago (newer than threshold)
769+
770+ testCases := []struct {
771+ name string
772+ retentionConfig codersdk.RetentionConfig
773+ oldLogTime time.Time
774+ recentLogTime * time.Time // nil means no recent log created
775+ expectOldDeleted bool
776+ expectedLogsRemaining int
777+ }{
778+ {
779+ name :"RetentionEnabled" ,
780+ retentionConfig : codersdk.RetentionConfig {
820781ConnectionLogs :serpent .Duration (retentionPeriod ),
821782},
822- },clk )
823- defer closer .Close ()
824- testutil .TryReceive (ctx ,t ,done )
825-
826- // Verify results by querying all connection logs
827- logs ,err := db .GetConnectionLogsOffset (ctx , database.GetConnectionLogsOffsetParams {
828- LimitOpt :100 ,
829- })
830- require .NoError (t ,err )
831-
832- logIDs := make ([]uuid.UUID ,len (logs ))
833- for i ,log := range logs {
834- logIDs [i ]= log .ConnectionLog .ID
835- }
836-
837- require .NotContains (t ,logIDs ,oldLog .ID ,"old connection log should be deleted" )
838- require .Contains (t ,logIDs ,recentLog .ID ,"recent connection log should be kept" )
839- })
840-
841- t .Run ("RetentionDisabled" ,func (t * testing.T ) {
842- t .Parallel ()
843-
844- ctx := testutil .Context (t ,testutil .WaitShort )
845-
846- clk := quartz .NewMock (t )
847- now := time .Date (2025 ,1 ,15 ,7 ,30 ,0 ,0 ,time .UTC )
848- oldTime := now .Add (- 365 * 24 * time .Hour )// 1 year ago
849- clk .Set (now ).MustWait (ctx )
850-
851- db ,_ := dbtestutil .NewDB (t ,dbtestutil .WithDumpOnFailure ())
852- logger := slogtest .Make (t ,& slogtest.Options {IgnoreErrors :true })
853- user := dbgen .User (t ,db , database.User {})
854- org := dbgen .Organization (t ,db , database.Organization {})
855- _ = dbgen .OrganizationMember (t ,db , database.OrganizationMember {UserID :user .ID ,OrganizationID :org .ID })
856- tv := dbgen .TemplateVersion (t ,db , database.TemplateVersion {OrganizationID :org .ID ,CreatedBy :user .ID })
857- tmpl := dbgen .Template (t ,db , database.Template {OrganizationID :org .ID ,ActiveVersionID :tv .ID ,CreatedBy :user .ID })
858- workspace := dbgen .Workspace (t ,db , database.WorkspaceTable {
859- OwnerID :user .ID ,
860- OrganizationID :org .ID ,
861- TemplateID :tmpl .ID ,
862- })
863-
864- // Create old connection log (should NOT be deleted when retention is 0)
865- oldLog := dbgen .ConnectionLog (t ,db , database.UpsertConnectionLogParams {
866- ID :uuid .New (),
867- Time :oldTime ,
868- OrganizationID :org .ID ,
869- WorkspaceOwnerID :user .ID ,
870- WorkspaceID :workspace .ID ,
871- WorkspaceName :workspace .Name ,
872- AgentName :"agent1" ,
873- Type :database .ConnectionTypeSsh ,
874- ConnectionStatus :database .ConnectionStatusConnected ,
875- })
876-
877- // Run the purge with retention disabled (0)
878- done := awaitDoTick (ctx ,t ,clk )
879- closer := dbpurge .New (ctx ,logger ,db ,& codersdk.DeploymentValues {
880- Retention : codersdk.RetentionConfig {
881- ConnectionLogs :serpent .Duration (0 ),// disabled
783+ oldLogTime :afterThreshold ,
784+ recentLogTime :& beforeThreshold ,
785+ expectOldDeleted :true ,
786+ expectedLogsRemaining :1 ,// only recent log remains
787+ },
788+ {
789+ name :"RetentionDisabled" ,
790+ retentionConfig : codersdk.RetentionConfig {
791+ ConnectionLogs :serpent .Duration (0 ),
882792},
883- },clk )
884- defer closer .Close ()
885- testutil .TryReceive (ctx ,t ,done )
793+ oldLogTime :now .Add (- 365 * 24 * time .Hour ),// 1 year ago
794+ recentLogTime :nil ,
795+ expectOldDeleted :false ,
796+ expectedLogsRemaining :1 ,// old log is kept
797+ },
798+ {
799+ name :"GlobalRetentionFallback" ,
800+ retentionConfig : codersdk.RetentionConfig {
801+ Global :serpent .Duration (retentionPeriod ),
802+ ConnectionLogs :serpent .Duration (0 ),// Not set, should fall back to global
803+ },
804+ oldLogTime :afterThreshold ,
805+ recentLogTime :& beforeThreshold ,
806+ expectOldDeleted :true ,
807+ expectedLogsRemaining :1 ,// only recent log remains
808+ },
809+ }
886810
887- // Verify old log is still present
888- logs ,err := db .GetConnectionLogsOffset (ctx , database.GetConnectionLogsOffsetParams {
889- LimitOpt :100 ,
890- })
891- require .NoError (t ,err )
811+ for _ ,tc := range testCases {
812+ t .Run (tc .name ,func (t * testing.T ) {
813+ t .Parallel ()
892814
893- logIDs := make ([]uuid.UUID ,len (logs ))
894- for i ,log := range logs {
895- logIDs [i ]= log .ConnectionLog .ID
896- }
815+ ctx := testutil .Context (t ,testutil .WaitShort )
816+ clk := quartz .NewMock (t )
817+ clk .Set (now ).MustWait (ctx )
818+
819+ db ,_ := dbtestutil .NewDB (t ,dbtestutil .WithDumpOnFailure ())
820+ logger := slogtest .Make (t ,& slogtest.Options {IgnoreErrors :true })
821+
822+ // Setup test fixtures.
823+ user := dbgen .User (t ,db , database.User {})
824+ org := dbgen .Organization (t ,db , database.Organization {})
825+ _ = dbgen .OrganizationMember (t ,db , database.OrganizationMember {UserID :user .ID ,OrganizationID :org .ID })
826+ tv := dbgen .TemplateVersion (t ,db , database.TemplateVersion {OrganizationID :org .ID ,CreatedBy :user .ID })
827+ tmpl := dbgen .Template (t ,db , database.Template {OrganizationID :org .ID ,ActiveVersionID :tv .ID ,CreatedBy :user .ID })
828+ workspace := dbgen .Workspace (t ,db , database.WorkspaceTable {
829+ OwnerID :user .ID ,
830+ OrganizationID :org .ID ,
831+ TemplateID :tmpl .ID ,
832+ })
897833
898- require .Contains (t ,logIDs ,oldLog .ID ,"old connection log should NOT be deleted when retention is disabled" )
899- })
834+ // Create old connection log.
835+ oldLog := dbgen .ConnectionLog (t ,db , database.UpsertConnectionLogParams {
836+ ID :uuid .New (),
837+ Time :tc .oldLogTime ,
838+ OrganizationID :org .ID ,
839+ WorkspaceOwnerID :user .ID ,
840+ WorkspaceID :workspace .ID ,
841+ WorkspaceName :workspace .Name ,
842+ AgentName :"agent1" ,
843+ Type :database .ConnectionTypeSsh ,
844+ ConnectionStatus :database .ConnectionStatusConnected ,
845+ })
900846
901- t .Run ("GlobalRetentionFallback" ,func (t * testing.T ) {
902- t .Parallel ()
903-
904- ctx := testutil .Context (t ,testutil .WaitShort )
905-
906- clk := quartz .NewMock (t )
907- now := time .Date (2025 ,1 ,15 ,7 ,30 ,0 ,0 ,time .UTC )
908- retentionPeriod := 30 * 24 * time .Hour // 30 days
909- afterThreshold := now .Add (- retentionPeriod ).Add (- 24 * time .Hour )// 31 days ago (older than threshold)
910- beforeThreshold := now .Add (- 15 * 24 * time .Hour )// 15 days ago (newer than threshold)
911- clk .Set (now ).MustWait (ctx )
912-
913- db ,_ := dbtestutil .NewDB (t ,dbtestutil .WithDumpOnFailure ())
914- logger := slogtest .Make (t ,& slogtest.Options {IgnoreErrors :true })
915- user := dbgen .User (t ,db , database.User {})
916- org := dbgen .Organization (t ,db , database.Organization {})
917- _ = dbgen .OrganizationMember (t ,db , database.OrganizationMember {UserID :user .ID ,OrganizationID :org .ID })
918- tv := dbgen .TemplateVersion (t ,db , database.TemplateVersion {OrganizationID :org .ID ,CreatedBy :user .ID })
919- tmpl := dbgen .Template (t ,db , database.Template {OrganizationID :org .ID ,ActiveVersionID :tv .ID ,CreatedBy :user .ID })
920- workspace := dbgen .Workspace (t ,db , database.WorkspaceTable {
921- OwnerID :user .ID ,
922- OrganizationID :org .ID ,
923- TemplateID :tmpl .ID ,
924- })
847+ // Create recent connection log if specified.
848+ var recentLog database.ConnectionLog
849+ if tc .recentLogTime != nil {
850+ recentLog = dbgen .ConnectionLog (t ,db , database.UpsertConnectionLogParams {
851+ ID :uuid .New (),
852+ Time :* tc .recentLogTime ,
853+ OrganizationID :org .ID ,
854+ WorkspaceOwnerID :user .ID ,
855+ WorkspaceID :workspace .ID ,
856+ WorkspaceName :workspace .Name ,
857+ AgentName :"agent2" ,
858+ Type :database .ConnectionTypeSsh ,
859+ ConnectionStatus :database .ConnectionStatusConnected ,
860+ })
861+ }
925862
926- // Create old connection log (should be deleted)
927- oldLog := dbgen .ConnectionLog (t ,db , database.UpsertConnectionLogParams {
928- ID :uuid .New (),
929- Time :afterThreshold ,
930- OrganizationID :org .ID ,
931- WorkspaceOwnerID :user .ID ,
932- WorkspaceID :workspace .ID ,
933- WorkspaceName :workspace .Name ,
934- AgentName :"agent1" ,
935- Type :database .ConnectionTypeSsh ,
936- ConnectionStatus :database .ConnectionStatusConnected ,
937- })
863+ // Run the purge.
864+ done := awaitDoTick (ctx ,t ,clk )
865+ closer := dbpurge .New (ctx ,logger ,db ,& codersdk.DeploymentValues {
866+ Retention :tc .retentionConfig ,
867+ },clk )
868+ defer closer .Close ()
869+ testutil .TryReceive (ctx ,t ,done )
870+
871+ // Verify results.
872+ logs ,err := db .GetConnectionLogsOffset (ctx , database.GetConnectionLogsOffsetParams {
873+ LimitOpt :100 ,
874+ })
875+ require .NoError (t ,err )
876+ require .Len (t ,logs ,tc .expectedLogsRemaining ,"unexpected number of logs remaining" )
938877
939- // Create recent connection log (should be kept)
940- recentLog := dbgen .ConnectionLog (t ,db , database.UpsertConnectionLogParams {
941- ID :uuid .New (),
942- Time :beforeThreshold ,
943- OrganizationID :org .ID ,
944- WorkspaceOwnerID :user .ID ,
945- WorkspaceID :workspace .ID ,
946- WorkspaceName :workspace .Name ,
947- AgentName :"agent2" ,
948- Type :database .ConnectionTypeSsh ,
949- ConnectionStatus :database .ConnectionStatusConnected ,
950- })
878+ logIDs := make ([]uuid.UUID ,len (logs ))
879+ for i ,log := range logs {
880+ logIDs [i ]= log .ConnectionLog .ID
881+ }
951882
952- // Run the purge with global retention (connection logs retention is 0, so it falls back)
953- done := awaitDoTick (ctx ,t ,clk )
954- closer := dbpurge .New (ctx ,logger ,db ,& codersdk.DeploymentValues {
955- Retention : codersdk.RetentionConfig {
956- Global :serpent .Duration (retentionPeriod ),// Use global
957- ConnectionLogs :serpent .Duration (0 ),// Not set, should fall back to global
958- },
959- },clk )
960- defer closer .Close ()
961- testutil .TryReceive (ctx ,t ,done )
883+ if tc .expectOldDeleted {
884+ require .NotContains (t ,logIDs ,oldLog .ID ,"old connection log should be deleted" )
885+ }else {
886+ require .Contains (t ,logIDs ,oldLog .ID ,"old connection log should NOT be deleted" )
887+ }
962888
963- // Verify results
964- logs , err := db . GetConnectionLogsOffset ( ctx , database. GetConnectionLogsOffsetParams {
965- LimitOpt : 100 ,
889+ if tc . recentLogTime != nil {
890+ require . Contains ( t , logIDs , recentLog . ID , "recent connection log should be kept" )
891+ }
966892})
967- require .NoError (t ,err )
968-
969- logIDs := make ([]uuid.UUID ,len (logs ))
970- for i ,log := range logs {
971- logIDs [i ]= log .ConnectionLog .ID
972- }
973-
974- require .NotContains (t ,logIDs ,oldLog .ID ,"old connection log should be deleted via global retention" )
975- require .Contains (t ,logIDs ,recentLog .ID ,"recent connection log should be kept" )
976- })
893+ }
977894}
978895
979896func TestDeleteOldAIBridgeRecords (t * testing.T ) {