@@ -762,218 +762,130 @@ 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+ }{
777+ {
778+ name :"RetentionEnabled" ,
779+ retentionConfig : codersdk.RetentionConfig {
820780ConnectionLogs :serpent .Duration (retentionPeriod ),
821781},
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
782+ oldLogTime :afterThreshold ,
783+ recentLogTime :& beforeThreshold ,
784+ expectOldDeleted :true ,
785+ },
786+ {
787+ name :"RetentionDisabled" ,
788+ retentionConfig : codersdk.RetentionConfig {
789+ ConnectionLogs :serpent .Duration (0 ),
882790},
883- },clk )
884- defer closer .Close ()
885- testutil .TryReceive (ctx ,t ,done )
791+ oldLogTime :now .Add (- 365 * 24 * time .Hour ),// 1 year ago
792+ recentLogTime :nil ,
793+ expectOldDeleted :false ,
794+ },
795+ {
796+ name :"GlobalRetentionFallback" ,
797+ retentionConfig : codersdk.RetentionConfig {
798+ Global :serpent .Duration (retentionPeriod ),
799+ ConnectionLogs :serpent .Duration (0 ),// Not set, should fall back to global
800+ },
801+ oldLogTime :afterThreshold ,
802+ recentLogTime :& beforeThreshold ,
803+ expectOldDeleted :true ,
804+ },
805+ }
886806
887- // Verify old log is still present
888- logs ,err := db .GetConnectionLogsOffset (ctx , database.GetConnectionLogsOffsetParams {
889- LimitOpt :100 ,
890- })
891- require .NoError (t ,err )
807+ for _ ,tc := range testCases {
808+ t .Run (tc .name ,func (t * testing.T ) {
809+ t .Parallel ()
892810
893- logIDs := make ([]uuid.UUID ,len (logs ))
894- for i ,log := range logs {
895- logIDs [i ]= log .ConnectionLog .ID
896- }
811+ ctx := testutil .Context (t ,testutil .WaitShort )
812+ clk := quartz .NewMock (t )
813+ clk .Set (now ).MustWait (ctx )
814+
815+ db ,_ := dbtestutil .NewDB (t ,dbtestutil .WithDumpOnFailure ())
816+ logger := slogtest .Make (t ,& slogtest.Options {IgnoreErrors :true })
817+
818+ // Setup test fixtures.
819+ user := dbgen .User (t ,db , database.User {})
820+ org := dbgen .Organization (t ,db , database.Organization {})
821+ _ = dbgen .OrganizationMember (t ,db , database.OrganizationMember {UserID :user .ID ,OrganizationID :org .ID })
822+ tv := dbgen .TemplateVersion (t ,db , database.TemplateVersion {OrganizationID :org .ID ,CreatedBy :user .ID })
823+ tmpl := dbgen .Template (t ,db , database.Template {OrganizationID :org .ID ,ActiveVersionID :tv .ID ,CreatedBy :user .ID })
824+ workspace := dbgen .Workspace (t ,db , database.WorkspaceTable {
825+ OwnerID :user .ID ,
826+ OrganizationID :org .ID ,
827+ TemplateID :tmpl .ID ,
828+ })
897829
898- require .Contains (t ,logIDs ,oldLog .ID ,"old connection log should NOT be deleted when retention is disabled" )
899- })
830+ // Create old connection log.
831+ oldLog := dbgen .ConnectionLog (t ,db , database.UpsertConnectionLogParams {
832+ ID :uuid .New (),
833+ Time :tc .oldLogTime ,
834+ OrganizationID :org .ID ,
835+ WorkspaceOwnerID :user .ID ,
836+ WorkspaceID :workspace .ID ,
837+ WorkspaceName :workspace .Name ,
838+ AgentName :"agent1" ,
839+ Type :database .ConnectionTypeSsh ,
840+ ConnectionStatus :database .ConnectionStatusConnected ,
841+ })
900842
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- })
843+ // Create recent connection log if specified.
844+ var recentLog database.ConnectionLog
845+ if tc .recentLogTime != nil {
846+ recentLog = dbgen .ConnectionLog (t ,db , database.UpsertConnectionLogParams {
847+ ID :uuid .New (),
848+ Time :* tc .recentLogTime ,
849+ OrganizationID :org .ID ,
850+ WorkspaceOwnerID :user .ID ,
851+ WorkspaceID :workspace .ID ,
852+ WorkspaceName :workspace .Name ,
853+ AgentName :"agent2" ,
854+ Type :database .ConnectionTypeSsh ,
855+ ConnectionStatus :database .ConnectionStatusConnected ,
856+ })
857+ }
925858
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- })
859+ // Run the purge.
860+ done := awaitDoTick (ctx ,t ,clk )
861+ closer := dbpurge .New (ctx ,logger ,db ,& codersdk.DeploymentValues {
862+ Retention :tc .retentionConfig ,
863+ },clk )
864+ defer closer .Close ()
865+ testutil .TryReceive (ctx ,t ,done )
866+
867+ // Verify results.
868+ logs ,err := db .GetConnectionLogsOffset (ctx , database.GetConnectionLogsOffsetParams {
869+ LimitOpt :100 ,
870+ })
871+ require .NoError (t ,err )
938872
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- })
873+ logIDs := make ([]uuid.UUID ,len (logs ))
874+ for i ,log := range logs {
875+ logIDs [i ]= log .ConnectionLog .ID
876+ }
951877
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 )
878+ if tc .expectOldDeleted {
879+ require .NotContains (t ,logIDs ,oldLog .ID ,"old connection log should be deleted" )
880+ }else {
881+ require .Contains (t ,logIDs ,oldLog .ID ,"old connection log should NOT be deleted" )
882+ }
962883
963- // Verify results
964- logs , err := db . GetConnectionLogsOffset ( ctx , database. GetConnectionLogsOffsetParams {
965- LimitOpt : 100 ,
884+ if tc . recentLogTime != nil {
885+ require . Contains ( t , logIDs , recentLog . ID , "recent connection log should be kept" )
886+ }
966887})
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- })
888+ }
977889}
978890
979891func TestDeleteOldAIBridgeRecords (t * testing.T ) {