Skip to content

Commit 62fbfd1

Browse files
authored
Merge branch 'main' into dean/skip-tests-fe-be
2 parents 77162d9 + 175561b commit 62fbfd1

31 files changed

+1015
-235
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ jobs:
213213
run: ./scripts/check_unstaged.sh
214214

215215
test-go:
216-
runs-on: ${{ matrix.os == 'ubuntu-latest' && github.repository_owner == 'coder' && 'buildjet-4vcpu-ubuntu-2204' || matrix.os == 'windows-2019' && github.repository_owner == 'coder' && 'windows-latest-8-cores'|| matrix.os }}
216+
runs-on: ${{ matrix.os == 'ubuntu-latest' && github.repository_owner == 'coder' && 'buildjet-4vcpu-ubuntu-2204' || matrix.os == 'macos-latest' && github.repository_owner == 'coder' && 'macos-latest-xl' || matrix.os == 'windows-2019' && github.repository_owner == 'coder' && 'windows-latest-8-cores' || matrix.os }}
217217
needs: changes
218218
if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main'
219219
timeout-minutes: 20

Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -525,13 +525,17 @@ coderd/apidoc/swagger.json: $(shell find ./scripts/apidocgen $(FIND_EXCLUSIONS)
525525
./scripts/apidocgen/generate.sh
526526
yarn run --cwd=site format:write:only ../docs/api ../docs/manifest.json ../coderd/apidoc/swagger.json
527527

528-
update-golden-files: cli/testdata/.gen-golden helm/tests/testdata/.gen-golden scripts/ci-report/testdata/.gen-golden
528+
update-golden-files: cli/testdata/.gen-golden helm/tests/testdata/.gen-golden scripts/ci-report/testdata/.gen-golden enterprise/cli/testdata/.gen-golden
529529
.PHONY: update-golden-files
530530

531531
cli/testdata/.gen-golden: $(wildcard cli/testdata/*.golden) $(wildcard cli/*.tpl) $(GO_SRC_FILES)
532532
go test ./cli -run="Test(CommandHelp|ServerYAML)" -update
533533
touch "$@"
534534

535+
enterprise/cli/testdata/.gen-golden: $(wildcard enterprise/cli/testdata/*.golden) $(wildcard cli/*.tpl) $(GO_SRC_FILES)
536+
go test ./enterprise/cli -run="TestEnterpriseCommandHelp" -update
537+
touch "$@"
538+
535539
helm/tests/testdata/.gen-golden: $(wildcard helm/tests/testdata/*.yaml) $(wildcard helm/tests/testdata/*.golden) $(GO_SRC_FILES)
536540
go test ./helm/tests -run=TestUpdateGoldenFiles -update
537541
touch "$@"
@@ -630,7 +634,7 @@ test-postgres-docker:
630634
--name test-postgres-docker \
631635
--restart no \
632636
--detach \
633-
postgres:13 \
637+
gcr.io/coder-dev-1/postgres:13 \
634638
-c shared_buffers=1GB \
635639
-c work_mem=1GB \
636640
-c effective_cache_size=1GB \

cli/clitest/golden.go

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
package clitest
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"flag"
7+
"fmt"
8+
"os"
9+
"path/filepath"
10+
"regexp"
11+
"strings"
12+
"testing"
13+
14+
"github.com/charmbracelet/lipgloss"
15+
"github.com/muesli/termenv"
16+
"github.com/stretchr/testify/require"
17+
18+
"github.com/coder/coder/cli/clibase"
19+
"github.com/coder/coder/cli/config"
20+
"github.com/coder/coder/coderd/coderdtest"
21+
"github.com/coder/coder/coderd/database/dbtestutil"
22+
"github.com/coder/coder/codersdk"
23+
"github.com/coder/coder/testutil"
24+
)
25+
26+
// UpdateGoldenFiles indicates golden files should be updated.
27+
// To update the golden files:
28+
// make update-golden-files
29+
var UpdateGoldenFiles = flag.Bool("update", false, "update .golden files")
30+
31+
var timestampRegex = regexp.MustCompile(`(?i)\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(.\d+)?Z`)
32+
33+
type CommandHelpCase struct {
34+
Name string
35+
Cmd []string
36+
}
37+
38+
func DefaultCases() []CommandHelpCase {
39+
return []CommandHelpCase{
40+
{
41+
Name: "coder --help",
42+
Cmd: []string{"--help"},
43+
},
44+
{
45+
Name: "coder server --help",
46+
Cmd: []string{"server", "--help"},
47+
},
48+
}
49+
}
50+
51+
// TestCommandHelp will test the help output of the given commands
52+
// using golden files.
53+
//
54+
//nolint:tparallel,paralleltest
55+
func TestCommandHelp(t *testing.T, getRoot func(t *testing.T) *clibase.Cmd, cases []CommandHelpCase) {
56+
ogColorProfile := lipgloss.ColorProfile()
57+
// ANSI256 escape codes are far easier for humans to parse in a diff,
58+
// but TrueColor is probably more popular with modern terminals.
59+
lipgloss.SetColorProfile(termenv.ANSI)
60+
t.Cleanup(func() {
61+
lipgloss.SetColorProfile(ogColorProfile)
62+
})
63+
rootClient, replacements := prepareTestData(t)
64+
65+
root := getRoot(t)
66+
67+
ExtractCommandPathsLoop:
68+
for _, cp := range extractVisibleCommandPaths(nil, root.Children) {
69+
name := fmt.Sprintf("coder %s --help", strings.Join(cp, " "))
70+
cmd := append(cp, "--help")
71+
for _, tt := range cases {
72+
if tt.Name == name {
73+
continue ExtractCommandPathsLoop
74+
}
75+
}
76+
cases = append(cases, CommandHelpCase{Name: name, Cmd: cmd})
77+
}
78+
79+
for _, tt := range cases {
80+
tt := tt
81+
t.Run(tt.Name, func(t *testing.T) {
82+
t.Parallel()
83+
ctx := testutil.Context(t, testutil.WaitLong)
84+
85+
var outBuf bytes.Buffer
86+
87+
caseCmd := getRoot(t)
88+
89+
inv, cfg := NewWithCommand(t, caseCmd, tt.Cmd...)
90+
inv.Stderr = &outBuf
91+
inv.Stdout = &outBuf
92+
inv.Environ.Set("CODER_URL", rootClient.URL.String())
93+
inv.Environ.Set("CODER_SESSION_TOKEN", rootClient.SessionToken())
94+
inv.Environ.Set("CODER_CACHE_DIRECTORY", "~/.cache")
95+
96+
SetupConfig(t, rootClient, cfg)
97+
98+
StartWithWaiter(t, inv.WithContext(ctx)).RequireSuccess()
99+
100+
actual := outBuf.Bytes()
101+
if len(actual) == 0 {
102+
t.Fatal("no output")
103+
}
104+
105+
for k, v := range replacements {
106+
actual = bytes.ReplaceAll(actual, []byte(k), []byte(v))
107+
}
108+
109+
actual = NormalizeGoldenFile(t, actual)
110+
goldenPath := filepath.Join("testdata", strings.Replace(tt.Name, " ", "_", -1)+".golden")
111+
if *UpdateGoldenFiles {
112+
t.Logf("update golden file for: %q: %s", tt.Name, goldenPath)
113+
err := os.WriteFile(goldenPath, actual, 0o600)
114+
require.NoError(t, err, "update golden file")
115+
}
116+
117+
expected, err := os.ReadFile(goldenPath)
118+
require.NoError(t, err, "read golden file, run \"make update-golden-files\" and commit the changes")
119+
120+
expected = NormalizeGoldenFile(t, expected)
121+
require.Equal(
122+
t, string(expected), string(actual),
123+
"golden file mismatch: %s, run \"make update-golden-files\", verify and commit the changes",
124+
goldenPath,
125+
)
126+
})
127+
}
128+
}
129+
130+
// NormalizeGoldenFile replaces any strings that are system or timing dependent
131+
// with a placeholder so that the golden files can be compared with a simple
132+
// equality check.
133+
func NormalizeGoldenFile(t *testing.T, byt []byte) []byte {
134+
// Replace any timestamps with a placeholder.
135+
byt = timestampRegex.ReplaceAll(byt, []byte("[timestamp]"))
136+
137+
homeDir, err := os.UserHomeDir()
138+
require.NoError(t, err)
139+
140+
configDir := config.DefaultDir()
141+
byt = bytes.ReplaceAll(byt, []byte(configDir), []byte("~/.config/coderv2"))
142+
143+
byt = bytes.ReplaceAll(byt, []byte(codersdk.DefaultCacheDir()), []byte("[cache dir]"))
144+
145+
// The home directory changes depending on the test environment.
146+
byt = bytes.ReplaceAll(byt, []byte(homeDir), []byte("~"))
147+
for _, r := range []struct {
148+
old string
149+
new string
150+
}{
151+
{"\r\n", "\n"},
152+
{`~\.cache\coder`, "~/.cache/coder"},
153+
{`C:\Users\RUNNER~1\AppData\Local\Temp`, "/tmp"},
154+
{os.TempDir(), "/tmp"},
155+
} {
156+
byt = bytes.ReplaceAll(byt, []byte(r.old), []byte(r.new))
157+
}
158+
return byt
159+
}
160+
161+
func extractVisibleCommandPaths(cmdPath []string, cmds []*clibase.Cmd) [][]string {
162+
var cmdPaths [][]string
163+
for _, c := range cmds {
164+
if c.Hidden {
165+
continue
166+
}
167+
cmdPath := append(cmdPath, c.Name())
168+
cmdPaths = append(cmdPaths, cmdPath)
169+
cmdPaths = append(cmdPaths, extractVisibleCommandPaths(cmdPath, c.Children)...)
170+
}
171+
return cmdPaths
172+
}
173+
174+
func prepareTestData(t *testing.T) (*codersdk.Client, map[string]string) {
175+
t.Helper()
176+
177+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
178+
defer cancel()
179+
180+
db, pubsub := dbtestutil.NewDB(t)
181+
rootClient := coderdtest.New(t, &coderdtest.Options{
182+
Database: db,
183+
Pubsub: pubsub,
184+
IncludeProvisionerDaemon: true,
185+
})
186+
firstUser := coderdtest.CreateFirstUser(t, rootClient)
187+
secondUser, err := rootClient.CreateUser(ctx, codersdk.CreateUserRequest{
188+
Email: "testuser2@coder.com",
189+
Username: "testuser2",
190+
Password: coderdtest.FirstUserParams.Password,
191+
OrganizationID: firstUser.OrganizationID,
192+
})
193+
require.NoError(t, err)
194+
version := coderdtest.CreateTemplateVersion(t, rootClient, firstUser.OrganizationID, nil)
195+
version = coderdtest.AwaitTemplateVersionJob(t, rootClient, version.ID)
196+
template := coderdtest.CreateTemplate(t, rootClient, firstUser.OrganizationID, version.ID, func(req *codersdk.CreateTemplateRequest) {
197+
req.Name = "test-template"
198+
})
199+
workspace := coderdtest.CreateWorkspace(t, rootClient, firstUser.OrganizationID, template.ID, func(req *codersdk.CreateWorkspaceRequest) {
200+
req.Name = "test-workspace"
201+
})
202+
workspaceBuild := coderdtest.AwaitWorkspaceBuildJob(t, rootClient, workspace.LatestBuild.ID)
203+
204+
replacements := map[string]string{
205+
firstUser.UserID.String(): "[first user ID]",
206+
secondUser.ID.String(): "[second user ID]",
207+
firstUser.OrganizationID.String(): "[first org ID]",
208+
version.ID.String(): "[version ID]",
209+
version.Name: "[version name]",
210+
version.Job.ID.String(): "[version job ID]",
211+
version.Job.FileID.String(): "[version file ID]",
212+
version.Job.WorkerID.String(): "[version worker ID]",
213+
template.ID.String(): "[template ID]",
214+
workspace.ID.String(): "[workspace ID]",
215+
workspaceBuild.ID.String(): "[workspace build ID]",
216+
workspaceBuild.Job.ID.String(): "[workspace build job ID]",
217+
workspaceBuild.Job.FileID.String(): "[workspace build file ID]",
218+
workspaceBuild.Job.WorkerID.String(): "[workspace build worker ID]",
219+
}
220+
221+
return rootClient, replacements
222+
}

0 commit comments

Comments
 (0)