Skip to content

Commit 6d60f76

Browse files
committed
refactor template creation with stable IDs
1 parent 04f3cdd commit 6d60f76

File tree

3 files changed

+87
-81
lines changed

3 files changed

+87
-81
lines changed

coderd/database/queries.sql.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/insights.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ SELECT
133133
is_app,
134134
SUM(seconds) AS usage_seconds
135135
FROM app_stats_by_user_and_agent
136-
GROUP BY access_method, slug_or_port, display_name, icon, is_app;
136+
GROUP BY access_method, slug_or_port, display_name, icon, is_app
137+
ORDER BY access_method, slug_or_port, display_name, icon, is_app;
137138

138139
-- name: GetTemplateDailyInsights :many
139140
-- GetTemplateDailyInsights returns all daily intervals between start and end

coderd/insights_test.go

Lines changed: 84 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ import (
2222
"github.com/coder/coder/v2/agent"
2323
"github.com/coder/coder/v2/coderd/batchstats"
2424
"github.com/coder/coder/v2/coderd/coderdtest"
25+
"github.com/coder/coder/v2/coderd/database"
2526
"github.com/coder/coder/v2/coderd/database/dbauthz"
27+
"github.com/coder/coder/v2/coderd/database/dbgen"
28+
"github.com/coder/coder/v2/coderd/database/dbtestutil"
2629
"github.com/coder/coder/v2/coderd/rbac"
27-
"github.com/coder/coder/v2/coderd/util/slice"
2830
"github.com/coder/coder/v2/coderd/workspaceapps"
2931
"github.com/coder/coder/v2/codersdk"
3032
"github.com/coder/coder/v2/codersdk/agentsdk"
@@ -580,19 +582,6 @@ func TestTemplateInsights(t *testing.T) {
580582
func TestTemplateInsights_Golden(t *testing.T) {
581583
t.Parallel()
582584

583-
stabilizeReportForGoldenComparison := func(report *codersdk.TemplateInsightsResponse, toStableTemplateIDs func([]uuid.UUID)) {
584-
toStableTemplateIDs(report.Report.TemplateIDs)
585-
for _, param := range report.Report.ParametersUsage {
586-
toStableTemplateIDs(param.TemplateIDs)
587-
}
588-
for _, app := range report.Report.AppsUsage {
589-
toStableTemplateIDs(app.TemplateIDs)
590-
}
591-
for _, intervalReport := range report.IntervalReports {
592-
toStableTemplateIDs(intervalReport.TemplateIDs)
593-
}
594-
}
595-
596585
// Prepare test data types.
597586
type templateParameterOption struct {
598587
name string
@@ -613,7 +602,7 @@ func TestTemplateInsights_Golden(t *testing.T) {
613602
apps []templateApp
614603

615604
// Filled later.
616-
id uuid.UUID // Set to the created template ID.
605+
id uuid.UUID
617606
}
618607
type buildParameter struct {
619608
templateParameter *templateParameter
@@ -778,7 +767,17 @@ func TestTemplateInsights_Golden(t *testing.T) {
778767
},
779768
}
780769

781-
// Post-process workspaces.
770+
// Post-process.
771+
var stableIDs []uuid.UUID
772+
newStableUUID := func() uuid.UUID {
773+
stableIDs = append(stableIDs, uuid.MustParse(fmt.Sprintf("00000000-0000-0000-0000-%012d", len(stableIDs)+1)))
774+
stableID := stableIDs[len(stableIDs)-1]
775+
return stableID
776+
}
777+
778+
for _, template := range templates {
779+
template.id = newStableUUID()
780+
}
782781
for _, user := range users {
783782
for _, workspace := range user.workspaces {
784783
workspace.user = user
@@ -792,14 +791,16 @@ func TestTemplateInsights_Golden(t *testing.T) {
792791
return templates, users
793792
}
794793

795-
prepare := func(t *testing.T, templates []*testTemplate, users []*testUser, testData map[*testWorkspace]testDataGen) (*codersdk.Client, func([]uuid.UUID)) {
794+
prepare := func(t *testing.T, templates []*testTemplate, users []*testUser, testData map[*testWorkspace]testDataGen) *codersdk.Client {
796795
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: false}).Leveled(slog.LevelDebug)
797-
opts := &coderdtest.Options{
796+
db, pubsub := dbtestutil.NewDB(t)
797+
client := coderdtest.New(t, &coderdtest.Options{
798+
Database: db,
799+
Pubsub: pubsub,
798800
Logger: &logger,
799801
IncludeProvisionerDaemon: true,
800802
AgentStatsRefreshInterval: time.Hour, // Not relevant for this test.
801-
}
802-
client, _, coderdAPI := coderdtest.NewWithAPI(t, opts)
803+
})
803804
firstUser := coderdtest.CreateFirstUser(t, client)
804805

805806
// Prepare all test users.
@@ -926,11 +927,28 @@ func TestTemplateInsights_Golden(t *testing.T) {
926927
},
927928
}},
928929
})
929-
createdTemplate := coderdtest.CreateTemplate(t, client, firstUser.OrganizationID, version.ID)
930-
require.Empty(t, createdTemplate.BuildTimeStats[codersdk.WorkspaceTransitionStart])
931930
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
932931

933-
template.id = createdTemplate.ID
932+
// Create template, essentially a modified version of CreateTemplate
933+
// where we can control the template ID.
934+
// createdTemplate := coderdtest.CreateTemplate(t, client, firstUser.OrganizationID, version.ID)
935+
createdTemplate := dbgen.Template(t, db, database.Template{
936+
ID: template.id,
937+
ActiveVersionID: version.ID,
938+
OrganizationID: firstUser.OrganizationID,
939+
CreatedBy: firstUser.UserID,
940+
GroupACL: database.TemplateACL{
941+
firstUser.OrganizationID.String(): []rbac.Action{rbac.ActionRead},
942+
},
943+
})
944+
err := db.UpdateTemplateVersionByID(context.Background(), database.UpdateTemplateVersionByIDParams{
945+
ID: version.ID,
946+
TemplateID: uuid.NullUUID{
947+
UUID: createdTemplate.ID,
948+
Valid: true,
949+
},
950+
})
951+
require.NoError(t, err, "want no error updating template version")
934952

935953
// Create all workspaces and wait for them.
936954
for _, createWorkspace := range createWorkspaces {
@@ -949,7 +967,7 @@ func TestTemplateInsights_Golden(t *testing.T) {
949967
// the database.
950968
batcher, batcherCloser, err := batchstats.New(
951969
ctx,
952-
batchstats.WithStore(coderdAPI.Database),
970+
batchstats.WithStore(db),
953971
batchstats.WithLogger(logger.Named("batchstats")),
954972
batchstats.WithInterval(time.Hour),
955973
)
@@ -1000,80 +1018,57 @@ func TestTemplateInsights_Golden(t *testing.T) {
10001018
})
10011019
}
10021020
}
1003-
reporter := workspaceapps.NewStatsDBReporter(coderdAPI.Database, workspaceapps.DefaultStatsDBReporterBatchSize)
1021+
reporter := workspaceapps.NewStatsDBReporter(db, workspaceapps.DefaultStatsDBReporterBatchSize)
10041022
//nolint:gocritic // This is a test.
10051023
err = reporter.Report(dbauthz.AsSystemRestricted(ctx), stats)
10061024
require.NoError(t, err, "want no error inserting app stats")
10071025

1008-
var stableTemplateIDs []uuid.UUID
1009-
stableTemplateIDMap := make(map[uuid.UUID]uuid.UUID)
1010-
toStableTemplateID := func(id uuid.UUID) uuid.UUID {
1011-
if stableID, ok := stableTemplateIDMap[id]; ok {
1012-
return stableID
1013-
}
1014-
stableTemplateIDs = append(stableTemplateIDs, uuid.MustParse(fmt.Sprintf("00000000-0000-0000-0000-%012d", len(stableTemplateIDs)+1)))
1015-
stableID := stableTemplateIDs[len(stableTemplateIDs)-1]
1016-
stableTemplateIDMap[id] = stableID
1017-
return stableID
1018-
}
1019-
// Prime the map.
1020-
for _, template := range templates {
1021-
_ = toStableTemplateID(template.id)
1022-
}
1023-
1024-
return client, func(ids []uuid.UUID) {
1025-
for i, id := range ids {
1026-
ids[i] = toStableTemplateID(id)
1027-
}
1028-
slices.SortFunc(ids, func(a, b uuid.UUID) int {
1029-
return slice.Ascending(a.String(), b.String())
1030-
})
1031-
}
1026+
return client
10321027
}
10331028

10341029
// Time range for report, test data will be generated within and
10351030
// outside this range, but only data within the range should be
10361031
// included in the report.
1037-
lastNight := time.Now().UTC().Truncate(24 * time.Hour)
1038-
weekAgo := lastNight.AddDate(0, 0, -7)
1032+
frozenLastNight := time.Date(2023, 8, 22, 0, 0, 0, 0, time.UTC)
1033+
frozenWeekAgo := frozenLastNight.AddDate(0, 0, -7)
10391034

10401035
saoPaulo, err := time.LoadLocation("America/Sao_Paulo")
10411036
require.NoError(t, err)
1042-
weekAgoSaoPaulo, err := time.ParseInLocation(time.DateTime, weekAgo.Format(time.DateTime), saoPaulo)
1037+
frozenWeekAgoSaoPaulo, err := time.ParseInLocation(time.DateTime, frozenWeekAgo.Format(time.DateTime), saoPaulo)
10431038
require.NoError(t, err)
10441039

10451040
makeBaseTestData := func(templates []*testTemplate, users []*testUser) map[*testWorkspace]testDataGen {
10461041
return map[*testWorkspace]testDataGen{
10471042
users[0].workspaces[0]: {
10481043
agentStats: []agentStat{
10491044
{ // One hour of usage.
1050-
startedAt: weekAgo,
1051-
endedAt: weekAgo.Add(time.Hour),
1045+
startedAt: frozenWeekAgo,
1046+
endedAt: frozenWeekAgo.Add(time.Hour),
10521047
sessionCountVSCode: 1,
10531048
sessionCountSSH: 1,
10541049
},
10551050
{ // 12 minutes of usage -> 15 minutes.
1056-
startedAt: weekAgo.AddDate(0, 0, 1),
1057-
endedAt: weekAgo.AddDate(0, 0, 1).Add(12 * time.Minute),
1051+
startedAt: frozenWeekAgo.AddDate(0, 0, 1),
1052+
endedAt: frozenWeekAgo.AddDate(0, 0, 1).Add(12 * time.Minute),
10581053
sessionCountSSH: 1,
10591054
},
10601055
{ // 2 minutes of usage -> 10 minutes because it crosses the 5 minute interval boundary.
1061-
startedAt: weekAgo.AddDate(0, 0, 2).Add(4 * time.Minute),
1062-
endedAt: weekAgo.AddDate(0, 0, 2).Add(6 * time.Minute),
1056+
startedAt: frozenWeekAgo.AddDate(0, 0, 2).Add(4 * time.Minute),
1057+
endedAt: frozenWeekAgo.AddDate(0, 0, 2).Add(6 * time.Minute),
10631058
sessionCountJetBrains: 1,
10641059
},
10651060
},
10661061
appUsage: []appUsage{
10671062
{ // One hour of usage.
10681063
app: users[0].workspaces[0].apps[0],
1069-
startedAt: weekAgo,
1070-
endedAt: weekAgo.Add(time.Hour),
1064+
startedAt: frozenWeekAgo,
1065+
endedAt: frozenWeekAgo.Add(time.Hour),
10711066
requests: 1,
10721067
},
10731068
{ // used an app on the last day, counts as active user, 12m -> 15m rounded.
10741069
app: users[0].workspaces[0].apps[2],
1075-
startedAt: weekAgo.AddDate(0, 0, 6),
1076-
endedAt: weekAgo.AddDate(0, 0, 6).Add(12 * time.Minute),
1070+
startedAt: frozenWeekAgo.AddDate(0, 0, 6),
1071+
endedAt: frozenWeekAgo.AddDate(0, 0, 6).Add(12 * time.Minute),
10771072
requests: 1,
10781073
},
10791074
},
@@ -1085,8 +1080,8 @@ func TestTemplateInsights_Golden(t *testing.T) {
10851080
// as in first template. When selecting both templates
10861081
// this user and their app usage will only be counted
10871082
// once but the template ID will show up in the data.
1088-
startedAt: weekAgo,
1089-
endedAt: weekAgo.Add(time.Hour),
1083+
startedAt: frozenWeekAgo,
1084+
endedAt: frozenWeekAgo.Add(time.Hour),
10901085
sessionCountVSCode: 1,
10911086
sessionCountSSH: 1,
10921087
},
@@ -1105,8 +1100,8 @@ func TestTemplateInsights_Golden(t *testing.T) {
11051100
// Different templates but identical apps, apps will be
11061101
// combined and usage will be summed.
11071102
app: users[0].workspaces[1].apps[0],
1108-
startedAt: weekAgo.AddDate(0, 0, 2),
1109-
endedAt: weekAgo.AddDate(0, 0, 2).Add(6 * time.Hour),
1103+
startedAt: frozenWeekAgo.AddDate(0, 0, 2),
1104+
endedAt: frozenWeekAgo.AddDate(0, 0, 2).Add(6 * time.Hour),
11101105
requests: 1,
11111106
},
11121107
},
@@ -1128,6 +1123,7 @@ func TestTemplateInsights_Golden(t *testing.T) {
11281123
type testRequest struct {
11291124
name string
11301125
makeRequest func([]*testTemplate) codersdk.TemplateInsightsRequest
1126+
ignoreTimes bool
11311127
}
11321128
tests := []struct {
11331129
name string
@@ -1142,8 +1138,8 @@ func TestTemplateInsights_Golden(t *testing.T) {
11421138
name: "week deployment wide",
11431139
makeRequest: func(templates []*testTemplate) codersdk.TemplateInsightsRequest {
11441140
return codersdk.TemplateInsightsRequest{
1145-
StartTime: weekAgo,
1146-
EndTime: weekAgo.AddDate(0, 0, 7),
1141+
StartTime: frozenWeekAgo,
1142+
EndTime: frozenWeekAgo.AddDate(0, 0, 7),
11471143
Interval: codersdk.InsightsReportIntervalDay,
11481144
}
11491145
},
@@ -1153,8 +1149,8 @@ func TestTemplateInsights_Golden(t *testing.T) {
11531149
makeRequest: func(templates []*testTemplate) codersdk.TemplateInsightsRequest {
11541150
return codersdk.TemplateInsightsRequest{
11551151
TemplateIDs: []uuid.UUID{templates[0].id, templates[1].id, templates[2].id},
1156-
StartTime: weekAgo,
1157-
EndTime: weekAgo.AddDate(0, 0, 7),
1152+
StartTime: frozenWeekAgo,
1153+
EndTime: frozenWeekAgo.AddDate(0, 0, 7),
11581154
Interval: codersdk.InsightsReportIntervalDay,
11591155
}
11601156
},
@@ -1164,8 +1160,8 @@ func TestTemplateInsights_Golden(t *testing.T) {
11641160
makeRequest: func(templates []*testTemplate) codersdk.TemplateInsightsRequest {
11651161
return codersdk.TemplateInsightsRequest{
11661162
TemplateIDs: []uuid.UUID{templates[0].id},
1167-
StartTime: weekAgo,
1168-
EndTime: weekAgo.AddDate(0, 0, 7),
1163+
StartTime: frozenWeekAgo,
1164+
EndTime: frozenWeekAgo.AddDate(0, 0, 7),
11691165
Interval: codersdk.InsightsReportIntervalDay,
11701166
}
11711167
},
@@ -1175,8 +1171,8 @@ func TestTemplateInsights_Golden(t *testing.T) {
11751171
makeRequest: func(templates []*testTemplate) codersdk.TemplateInsightsRequest {
11761172
return codersdk.TemplateInsightsRequest{
11771173
TemplateIDs: []uuid.UUID{templates[1].id},
1178-
StartTime: weekAgo,
1179-
EndTime: weekAgo.AddDate(0, 0, 7),
1174+
StartTime: frozenWeekAgo,
1175+
EndTime: frozenWeekAgo.AddDate(0, 0, 7),
11801176
Interval: codersdk.InsightsReportIntervalDay,
11811177
}
11821178
},
@@ -1186,8 +1182,8 @@ func TestTemplateInsights_Golden(t *testing.T) {
11861182
makeRequest: func(templates []*testTemplate) codersdk.TemplateInsightsRequest {
11871183
return codersdk.TemplateInsightsRequest{
11881184
TemplateIDs: []uuid.UUID{templates[2].id},
1189-
StartTime: weekAgo,
1190-
EndTime: weekAgo.AddDate(0, 0, 7),
1185+
StartTime: frozenWeekAgo,
1186+
EndTime: frozenWeekAgo.AddDate(0, 0, 7),
11911187
Interval: codersdk.InsightsReportIntervalDay,
11921188
}
11931189
},
@@ -1198,8 +1194,8 @@ func TestTemplateInsights_Golden(t *testing.T) {
11981194
name: "week other timezone (São Paulo)",
11991195
makeRequest: func(templates []*testTemplate) codersdk.TemplateInsightsRequest {
12001196
return codersdk.TemplateInsightsRequest{
1201-
StartTime: weekAgoSaoPaulo,
1202-
EndTime: weekAgoSaoPaulo.AddDate(0, 0, 7),
1197+
StartTime: frozenWeekAgoSaoPaulo,
1198+
EndTime: frozenWeekAgoSaoPaulo.AddDate(0, 0, 7),
12031199
Interval: codersdk.InsightsReportIntervalDay,
12041200
}
12051201
},
@@ -1233,7 +1229,7 @@ func TestTemplateInsights_Golden(t *testing.T) {
12331229
}
12341230
}
12351231

1236-
client, toStableTemplateIDs := prepare(t, templates, users, testData)
1232+
client := prepare(t, templates, users, testData)
12371233

12381234
for _, req := range tt.requests {
12391235
req := req
@@ -1245,7 +1241,15 @@ func TestTemplateInsights_Golden(t *testing.T) {
12451241
report, err := client.TemplateInsights(ctx, req.makeRequest(templates))
12461242
require.NoError(t, err, "want no error getting template insights")
12471243

1248-
stabilizeReportForGoldenComparison(&report, toStableTemplateIDs)
1244+
if req.ignoreTimes {
1245+
// Ignore times, we're only interested in the data.
1246+
report.Report.StartTime = time.Time{}
1247+
report.Report.EndTime = time.Time{}
1248+
for i := range report.IntervalReports {
1249+
report.IntervalReports[i].StartTime = time.Time{}
1250+
report.IntervalReports[i].EndTime = time.Time{}
1251+
}
1252+
}
12491253

12501254
partialName := strings.Join(strings.Split(t.Name(), "/")[1:], "_")
12511255
goldenFile := filepath.Join("testdata", "insights", partialName+".json.golden")

0 commit comments

Comments
 (0)