Skip to content

Commit 8d8689d

Browse files
committed
dbmem/golden
1 parent 9a8adf3 commit 8d8689d

File tree

14 files changed

+180
-83
lines changed

14 files changed

+180
-83
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,7 @@ update-golden-files: \
711711
.PHONY: update-golden-files
712712

713713
cli/testdata/.gen-golden: $(wildcard cli/testdata/*.golden) $(wildcard cli/*.tpl) $(GO_SRC_FILES) $(wildcard cli/*_test.go)
714-
go test ./cli -run="Test(CommandHelp|ServerYAML|ErrorExamples)" -update
714+
go test ./cli -run="Test(CommandHelp|ServerYAML|ErrorExamples|.*Golden)" -update
715715
touch "$@"
716716

717717
enterprise/cli/testdata/.gen-golden: $(wildcard enterprise/cli/testdata/*.golden) $(wildcard cli/*.tpl) $(GO_SRC_FILES) $(wildcard enterprise/cli/*_test.go)

cli/clitest/golden.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"strings"
1212
"testing"
1313

14+
"github.com/google/go-cmp/cmp"
1415
"github.com/google/uuid"
1516
"github.com/stretchr/testify/require"
1617

@@ -95,32 +96,32 @@ ExtractCommandPathsLoop:
9596

9697
// TestGoldenFile will test the given bytes slice input against the
9798
// golden file with the given file name, optionally using the given replacements.
98-
func TestGoldenFile(t *testing.T, fileName string, actual []byte, replacements map[string]string) {
99-
if len(actual) == 0 {
99+
func TestGoldenFile(t *testing.T, fileName string, got []byte, replacements map[string]string) {
100+
t.Helper()
101+
102+
if len(got) == 0 {
100103
t.Fatal("no output")
101104
}
102105

103106
for k, v := range replacements {
104-
actual = bytes.ReplaceAll(actual, []byte(k), []byte(v))
107+
got = bytes.ReplaceAll(got, []byte(k), []byte(v))
105108
}
106109

107-
actual = normalizeGoldenFile(t, actual)
110+
got = normalizeGoldenFile(t, got)
108111
goldenPath := filepath.Join("testdata", strings.ReplaceAll(fileName, " ", "_")+".golden")
109112
if *UpdateGoldenFiles {
110113
t.Logf("update golden file for: %q: %s", fileName, goldenPath)
111-
err := os.WriteFile(goldenPath, actual, 0o600)
114+
err := os.WriteFile(goldenPath, got, 0o600)
112115
require.NoError(t, err, "update golden file")
113116
}
114117

115-
expected, err := os.ReadFile(goldenPath)
118+
want, err := os.ReadFile(goldenPath)
116119
require.NoError(t, err, "read golden file, run \"make update-golden-files\" and commit the changes")
117120

118-
expected = normalizeGoldenFile(t, expected)
119-
require.Equal(
120-
t, string(expected), string(actual),
121-
"golden file mismatch: %s, run \"make update-golden-files\", verify and commit the changes",
122-
goldenPath,
123-
)
121+
want = normalizeGoldenFile(t, want)
122+
if diff := cmp.Diff(string(want), string(got)); diff != "" {
123+
require.Failf(t, "golden file mismatch, run \"make update-golden-files\", verify and commit the changes", "%s: diff (-want +got)\n%s", goldenPath, diff)
124+
}
124125
}
125126

126127
// normalizeGoldenFile replaces any strings that are system or timing dependent

cli/provisionerjobs.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
func (r *RootCmd) provisionerJobs() *serpent.Command {
1515
cmd := &serpent.Command{
1616
Use: "jobs",
17-
Short: "View and manage provisioner jobs.",
17+
Short: "View and manage provisioner jobs",
1818
Handler: func(inv *serpent.Invocation) error {
1919
return inv.Command.HelpHandler(inv)
2020
},

cli/provisioners_test.go

Lines changed: 75 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,68 @@
11
package cli_test
22

33
import (
4+
"bytes"
5+
"context"
46
"database/sql"
57
"encoding/json"
8+
"fmt"
9+
"slices"
610
"testing"
711
"time"
812

913
"github.com/google/uuid"
1014
"github.com/stretchr/testify/require"
1115

1216
"github.com/coder/coder/v2/cli/clitest"
17+
"github.com/coder/coder/v2/coderd"
1318
"github.com/coder/coder/v2/coderd/coderdtest"
1419
"github.com/coder/coder/v2/coderd/database"
20+
"github.com/coder/coder/v2/coderd/database/dbauthz"
1521
"github.com/coder/coder/v2/coderd/database/dbgen"
1622
"github.com/coder/coder/v2/coderd/database/dbtestutil"
23+
"github.com/coder/coder/v2/coderd/database/dbtime"
1724
"github.com/coder/coder/v2/codersdk"
1825
)
1926

20-
func TestProvisioners(t *testing.T) {
27+
func TestProvisioners_Golden(t *testing.T) {
2128
t.Parallel()
2229

23-
db, ps := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
30+
// Replace UUIDs with predictable values for golden files.
31+
replace := make(map[string]string)
32+
updateReplaceUUIDs := func(coderdAPI *coderd.API) {
33+
//nolint:gocritic // This is a test.
34+
systemCtx := dbauthz.AsSystemRestricted(context.Background())
35+
provisioners, err := coderdAPI.Database.GetProvisionerDaemons(systemCtx)
36+
require.NoError(t, err)
37+
slices.SortFunc(provisioners, func(a, b database.ProvisionerDaemon) int {
38+
return a.CreatedAt.Compare(b.CreatedAt)
39+
})
40+
pIdx := 0
41+
for _, p := range provisioners {
42+
if _, ok := replace[p.ID.String()]; !ok {
43+
replace[p.ID.String()] = fmt.Sprintf("00000000-0000-0000-aaaa-%012d", pIdx)
44+
pIdx++
45+
}
46+
}
47+
jobs, err := coderdAPI.Database.GetProvisionerJobsCreatedAfter(systemCtx, time.Time{})
48+
require.NoError(t, err)
49+
slices.SortFunc(jobs, func(a, b database.ProvisionerJob) int {
50+
return a.CreatedAt.Compare(b.CreatedAt)
51+
})
52+
jIdx := 0
53+
for _, j := range jobs {
54+
if _, ok := replace[j.ID.String()]; !ok {
55+
replace[j.ID.String()] = fmt.Sprintf("00000000-0000-0000-bbbb-%012d", jIdx)
56+
jIdx++
57+
}
58+
}
59+
}
60+
61+
db, ps := dbtestutil.NewDB(t,
62+
dbtestutil.WithDumpOnFailure(),
63+
//nolint:gocritic // Use UTC for consistent timestamp length in golden files.
64+
dbtestutil.WithTimezone("UTC"),
65+
)
2466
client, _, coderdAPI := coderdtest.NewWithAPI(t, &coderdtest.Options{
2567
IncludeProvisionerDaemon: false,
2668
Database: db,
@@ -36,27 +78,32 @@ func TestProvisioners(t *testing.T) {
3678
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
3779
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
3880

81+
time.Sleep(1500 * time.Millisecond) // Ensure the workspace build job has a different timestamp for sorting.
3982
workspace := coderdtest.CreateWorkspace(t, client, template.ID)
4083
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
4184

42-
// Stop the provisioner so it doesn't grab jobs.
85+
// Stop the provisioner so it doesn't grab any more jobs.
4386
firstProvisioner.Close()
4487

88+
// Sanitize the UUIDs for the initial resources.
89+
replace[version.ID.String()] = "00000000-0000-0000-cccc-000000000000"
90+
replace[workspace.LatestBuild.ID.String()] = "00000000-0000-0000-dddd-000000000000"
91+
4592
// Create a provisioner that's working on a job.
4693
pd1 := dbgen.ProvisionerDaemon(t, coderdAPI.Database, database.ProvisionerDaemon{
4794
Name: "provisioner-1",
48-
CreatedAt: timeParse(t, "2006-01-02", "2024-12-20"),
95+
CreatedAt: dbtime.Now().Add(1 * time.Second),
4996
KeyID: uuid.MustParse(codersdk.ProvisionerKeyIDBuiltIn),
5097
})
5198
w1 := dbgen.Workspace(t, coderdAPI.Database, database.WorkspaceTable{
5299
OwnerID: memberUser.ID,
53100
TemplateID: template.ID,
54101
})
55-
wb1ID := uuid.MustParse("00000000-0000-0000-bbbb-000000000001")
102+
wb1ID := uuid.MustParse("00000000-0000-0000-dddd-000000000001")
56103
job1 := dbgen.ProvisionerJob(t, db, coderdAPI.Pubsub, database.ProvisionerJob{
57-
ID: uuid.MustParse("00000000-0000-0000-cccc-000000000001"),
58104
WorkerID: uuid.NullUUID{UUID: pd1.ID, Valid: true},
59105
Input: json.RawMessage(`{"workspace_build_id":"` + wb1ID.String() + `"}`),
106+
CreatedAt: dbtime.Now().Add(2 * time.Second),
60107
StartedAt: sql.NullTime{Time: coderdAPI.Clock.Now(), Valid: true},
61108
Tags: database.StringMap{"owner": "", "scope": "organization"},
62109
})
@@ -67,22 +114,22 @@ func TestProvisioners(t *testing.T) {
67114
TemplateVersionID: version.ID,
68115
})
69116

70-
// Create another provisioner that completed a job and is offline.
117+
// Create a provisioner that completed a job previously and is offline.
71118
pd2 := dbgen.ProvisionerDaemon(t, coderdAPI.Database, database.ProvisionerDaemon{
72119
Name: "provisioner-2",
73-
CreatedAt: timeParse(t, "2006-01-02", "2024-12-20"),
120+
CreatedAt: dbtime.Now().Add(2 * time.Second),
74121
LastSeenAt: sql.NullTime{Time: coderdAPI.Clock.Now().Add(-time.Hour), Valid: true},
75122
KeyID: uuid.MustParse(codersdk.ProvisionerKeyIDBuiltIn),
76123
})
77124
w2 := dbgen.Workspace(t, coderdAPI.Database, database.WorkspaceTable{
78125
OwnerID: memberUser.ID,
79126
TemplateID: template.ID,
80127
})
81-
wb2ID := uuid.MustParse("00000000-0000-0000-bbbb-000000000002")
128+
wb2ID := uuid.MustParse("00000000-0000-0000-dddd-000000000002")
82129
job2 := dbgen.ProvisionerJob(t, db, coderdAPI.Pubsub, database.ProvisionerJob{
83-
ID: uuid.MustParse("00000000-0000-0000-cccc-000000000002"),
84130
WorkerID: uuid.NullUUID{UUID: pd2.ID, Valid: true},
85131
Input: json.RawMessage(`{"workspace_build_id":"` + wb2ID.String() + `"}`),
132+
CreatedAt: dbtime.Now().Add(3 * time.Second),
86133
StartedAt: sql.NullTime{Time: coderdAPI.Clock.Now().Add(-2 * time.Hour), Valid: true},
87134
CompletedAt: sql.NullTime{Time: coderdAPI.Clock.Now().Add(-time.Hour), Valid: true},
88135
Tags: database.StringMap{"owner": "", "scope": "organization"},
@@ -99,11 +146,11 @@ func TestProvisioners(t *testing.T) {
99146
OwnerID: memberUser.ID,
100147
TemplateID: template.ID,
101148
})
102-
wb3ID := uuid.MustParse("00000000-0000-0000-bbbb-000000000003")
149+
wb3ID := uuid.MustParse("00000000-0000-0000-dddd-000000000003")
103150
job3 := dbgen.ProvisionerJob(t, db, coderdAPI.Pubsub, database.ProvisionerJob{
104-
ID: uuid.MustParse("00000000-0000-0000-cccc-000000000003"),
105-
Input: json.RawMessage(`{"workspace_build_id":"` + wb3ID.String() + `"}`),
106-
Tags: database.StringMap{"owner": "", "scope": "organization"},
151+
Input: json.RawMessage(`{"workspace_build_id":"` + wb3ID.String() + `"}`),
152+
CreatedAt: dbtime.Now().Add(4 * time.Second),
153+
Tags: database.StringMap{"owner": "", "scope": "organization"},
107154
})
108155
dbgen.WorkspaceBuild(t, coderdAPI.Database, database.WorkspaceBuild{
109156
ID: wb3ID,
@@ -113,48 +160,50 @@ func TestProvisioners(t *testing.T) {
113160
})
114161

115162
// Create a provisioner that is idle.
116-
pd3 := dbgen.ProvisionerDaemon(t, coderdAPI.Database, database.ProvisionerDaemon{
163+
_ = dbgen.ProvisionerDaemon(t, coderdAPI.Database, database.ProvisionerDaemon{
117164
Name: "provisioner-3",
118-
CreatedAt: timeParse(t, "2006-01-02", "2024-12-20"),
165+
CreatedAt: dbtime.Now().Add(3 * time.Second),
119166
KeyID: uuid.MustParse(codersdk.ProvisionerKeyIDBuiltIn),
120167
})
121-
_ = pd3
168+
169+
updateReplaceUUIDs(coderdAPI)
170+
171+
for id, replaceID := range replace {
172+
t.Logf("replace[%q] = %q", id, replaceID)
173+
}
122174

123175
t.Run("list", func(t *testing.T) {
124176
t.Parallel()
125177

178+
var got bytes.Buffer
126179
inv, root := clitest.New(t,
127180
"provisioners",
128181
"list",
129182
"--column", "id,created at,last seen at,name,version,api version,tags,status,current job id,previous job id,previous job status,organization",
130183
)
184+
inv.Stdout = &got
131185
clitest.SetupConfig(t, member, root)
132186
err := inv.Run()
133187
require.NoError(t, err)
134188

135-
// TODO(mafredri): Verify golden output.
189+
clitest.TestGoldenFile(t, t.Name(), got.Bytes(), replace)
136190
})
137191

138192
t.Run("jobs list", func(t *testing.T) {
139193
t.Parallel()
140194

195+
var got bytes.Buffer
141196
inv, root := clitest.New(t,
142197
"provisioners",
143198
"jobs",
144199
"list",
145200
"--column", "id,created at,status,worker id,tags,template version id,workspace build id,type,available workers,organization,queue",
146201
)
202+
inv.Stdout = &got
147203
clitest.SetupConfig(t, member, root)
148204
err := inv.Run()
149205
require.NoError(t, err)
150206

151-
// TODO(mafredri): Verify golden output.
207+
clitest.TestGoldenFile(t, t.Name(), got.Bytes(), replace)
152208
})
153209
}
154-
155-
func timeParse(t *testing.T, layout, s string) time.Time {
156-
t.Helper()
157-
tm, err := time.Parse(layout, s)
158-
require.NoError(t, err)
159-
return tm
160-
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
ID CREATED AT STATUS WORKER ID TAGS TEMPLATE VERSION ID WORKSPACE BUILD ID TYPE AVAILABLE WORKERS ORGANIZATION QUEUE
2+
00000000-0000-0000-bbbb-000000000000 ====[timestamp]===== succeeded 00000000-0000-0000-aaaa-000000000000 map[owner: scope:organization] 00000000-0000-0000-cccc-000000000000 <nil> template_version_import [] Coder
3+
00000000-0000-0000-bbbb-000000000001 ====[timestamp]===== succeeded 00000000-0000-0000-aaaa-000000000000 map[owner: scope:organization] <nil> 00000000-0000-0000-dddd-000000000000 workspace_build [] Coder
4+
00000000-0000-0000-bbbb-000000000002 ====[timestamp]===== running 00000000-0000-0000-aaaa-000000000001 map[owner: scope:organization] <nil> 00000000-0000-0000-dddd-000000000001 workspace_build [] Coder
5+
00000000-0000-0000-bbbb-000000000003 ====[timestamp]===== succeeded 00000000-0000-0000-aaaa-000000000002 map[owner: scope:organization] <nil> 00000000-0000-0000-dddd-000000000002 workspace_build [] Coder
6+
00000000-0000-0000-bbbb-000000000004 ====[timestamp]===== pending <nil> map[owner: scope:organization] <nil> 00000000-0000-0000-dddd-000000000003 workspace_build [00000000-0000-0000-aaaa-000000000000, 00000000-0000-0000-aaaa-000000000001, 00000000-0000-0000-aaaa-000000000002, 00000000-0000-0000-aaaa-000000000003] Coder 1/1
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
ID CREATED AT LAST SEEN AT NAME VERSION API VERSION TAGS STATUS CURRENT JOB ID PREVIOUS JOB ID PREVIOUS JOB STATUS ORGANIZATION
2+
00000000-0000-0000-aaaa-000000000001 ====[timestamp]===== ====[timestamp]===== provisioner-1 v0.0.0 1.1 map[owner: scope:organization] busy 00000000-0000-0000-bbbb-000000000002 <nil> <nil> Coder
3+
00000000-0000-0000-aaaa-000000000002 ====[timestamp]===== ====[timestamp]===== provisioner-2 v0.0.0 1.1 map[owner: scope:organization] offline <nil> 00000000-0000-0000-bbbb-000000000003 succeeded Coder
4+
00000000-0000-0000-aaaa-000000000003 ====[timestamp]===== ====[timestamp]===== provisioner-3 v0.0.0 1.1 map[owner: scope:organization] idle <nil> <nil> <nil> Coder
5+
00000000-0000-0000-aaaa-000000000000 ====[timestamp]===== ====[timestamp]===== test v0.0.0-devel 1.1 map[owner: scope:organization] idle <nil> 00000000-0000-0000-bbbb-000000000001 succeeded Coder

cli/testdata/coder_provisioners_jobs_list_--output_json.golden

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[
22
{
3-
"id": "==========[version job ID]==========",
3+
"id": "======[workspace build job ID]======",
44
"created_at": "====[timestamp]=====",
55
"started_at": "====[timestamp]=====",
66
"completed_at": "====[timestamp]=====",
@@ -15,13 +15,13 @@
1515
"queue_size": 0,
1616
"organization_id": "===========[first org ID]===========",
1717
"input": {
18-
"template_version_id": "============[version ID]============"
18+
"workspace_build_id": "========[workspace build ID]========"
1919
},
20-
"type": "template_version_import",
20+
"type": "workspace_build",
2121
"organization_name": "Coder"
2222
},
2323
{
24-
"id": "======[workspace build job ID]======",
24+
"id": "==========[version job ID]==========",
2525
"created_at": "====[timestamp]=====",
2626
"started_at": "====[timestamp]=====",
2727
"completed_at": "====[timestamp]=====",
@@ -36,9 +36,9 @@
3636
"queue_size": 0,
3737
"organization_id": "===========[first org ID]===========",
3838
"input": {
39-
"workspace_build_id": "========[workspace build ID]========"
39+
"template_version_id": "============[version ID]============"
4040
},
41-
"type": "workspace_build",
41+
"type": "template_version_import",
4242
"organization_name": "Coder"
4343
}
4444
]

0 commit comments

Comments
 (0)