Skip to content

Commit 782f1f7

Browse files
committed
test(coderd/database/dbpurge): convert connection logs tests to table-driven
1 parent 0fb3b51 commit 782f1f7

File tree

1 file changed

+117
-200
lines changed

1 file changed

+117
-200
lines changed

coderd/database/dbpurge/dbpurge_test.go

Lines changed: 117 additions & 200 deletions
Original file line numberDiff line numberDiff line change
@@ -762,218 +762,135 @@ func TestDeleteOldTelemetryHeartbeats(t *testing.T) {
762762
func TestDeleteOldConnectionLogs(t *testing.T) {
763763
t.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{
820781
ConnectionLogs: 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

979896
func TestDeleteOldAIBridgeRecords(t *testing.T) {

0 commit comments

Comments
 (0)