blob: 179167766068996e93e8785a5d7b27171449035e [file] [log] [blame]
Earl Lee2e463fb2025-04-17 11:22:22 -07001package dockerimg
2
3import (
4 "context"
5 "flag"
6 "io/fs"
7 "net/http"
8 "os"
9 "strings"
10 "testing"
11 "testing/fstest"
12
13 "github.com/google/go-cmp/cmp"
14 "sketch.dev/httprr"
15)
16
17var flagRewriteWant = flag.Bool("rewritewant", false, "rewrite the dockerfiles we want from the model")
18
19func TestCreateDockerfile(t *testing.T) {
20 ctx := context.Background()
21
22 tests := []struct {
23 name string
24 fsys fs.FS
25 }{
26 {
27 name: "Basic repo with README",
28 fsys: fstest.MapFS{
29 "README.md": &fstest.MapFile{Data: []byte("# Test Project\nA Go project for testing.")},
30 },
31 },
32 {
33 // TODO: this looks bogus.
34 name: "Repo with README and workflow",
35 fsys: fstest.MapFS{
36 "README.md": &fstest.MapFile{Data: []byte("# Test Project\nA Go project for testing.")},
37 ".github/workflows/test.yml": &fstest.MapFile{Data: []byte(`name: Test
38on: [push]
39jobs:
40 test:
41 runs-on: ubuntu-latest
42 steps:
43 - uses: actions/checkout@v2
44 - uses: actions/setup-node@v3
45 with:
46 node-version: '18'
47 - name: Install and activate corepack
48 run: |
49 npm install -g corepack
50 corepack enable
51 - run: go test ./...`)},
52 },
53 },
54 {
55 name: "mention a devtool in the readme",
56 fsys: fstest.MapFS{
57 "readme.md": &fstest.MapFile{Data: []byte("# Test Project\nYou must install `dot` to run the tests.")},
58 },
59 },
60 {
61 name: "empty repo",
62 fsys: fstest.MapFS{
63 "main.go": &fstest.MapFile{Data: []byte("package main\n\nfunc main() {}")},
64 },
65 },
66 {
67 name: "python misery",
68 fsys: fstest.MapFS{
69 "README.md": &fstest.MapFile{Data: []byte("# Our amazing repo\n\nTo use this project you need python 3.11 and the dvc tool")},
70 },
71 },
72 }
73
74 for _, tt := range tests {
75 t.Run(tt.name, func(t *testing.T) {
76 basePath := "testdata/" + strings.ToLower(strings.Replace(t.Name(), "/", "_", -1))
77 rrPath := basePath + ".httprr"
78 rr, err := httprr.Open(rrPath, http.DefaultTransport)
79 if err != nil && !os.IsNotExist(err) {
80 t.Fatal(err)
81 }
82 rr.ScrubReq(func(req *http.Request) error {
83 req.Header.Del("x-api-key")
84 return nil
85 })
86 initFiles, err := readInitFiles(tt.fsys)
87 if err != nil {
88 t.Fatal(err)
89 }
90 result, err := createDockerfile(ctx, rr.Client(), "", os.Getenv("ANTHROPIC_API_KEY"), initFiles, "")
91 if err != nil {
92 t.Fatal(err)
93 }
94
95 wantPath := basePath + ".dockerfile"
96
97 if *flagRewriteWant {
98 if err := os.WriteFile(wantPath, []byte(result), 0o666); err != nil {
99 t.Fatal(err)
100 }
101 return
102 }
103
104 wantBytes, err := os.ReadFile(wantPath)
105 if err != nil {
106 t.Fatal(err)
107 }
108 want := string(wantBytes)
109 if diff := cmp.Diff(want, result); diff != "" {
110 t.Errorf("dockerfile does not match. got:\n----\n%s\n----\n\ndiff: %s", result, diff)
111 }
112 })
113 }
114}
115
116func TestReadInitFiles(t *testing.T) {
117 testFS := fstest.MapFS{
118 "README.md": &fstest.MapFile{Data: []byte("# Test Repo")},
119 ".github/workflows/test.yml": &fstest.MapFile{Data: []byte("name: Test Workflow")},
120 "main.go": &fstest.MapFile{Data: []byte("package main")},
121 ".git/HEAD": &fstest.MapFile{Data: []byte("ref: refs/heads/main")},
122 "random/README.md": &fstest.MapFile{Data: []byte("ignore me")},
123 }
124
125 files, err := readInitFiles(testFS)
126 if err != nil {
127 t.Fatalf("readInitFiles failed: %v", err)
128 }
129
130 // Should have 2 files: README.md and .github/workflows/test.yml
131 if len(files) != 2 {
132 t.Errorf("Expected 2 files, got %d", len(files))
133 }
134
135 if content, ok := files["README.md"]; !ok {
136 t.Error("README.md not found")
137 } else if content != "# Test Repo" {
138 t.Errorf("README.md has incorrect content: %q", content)
139 }
140
141 if content, ok := files[".github/workflows/test.yml"]; !ok {
142 t.Error(".github/workflows/test.yml not found")
143 } else if content != "name: Test Workflow" {
144 t.Errorf("Workflow file has incorrect content: %q", content)
145 }
146
147 if _, ok := files["main.go"]; ok {
148 t.Error("main.go should not be included")
149 }
150
151 if _, ok := files[".git/HEAD"]; ok {
152 t.Error(".git/HEAD should not be included")
153 }
154}
155
156func TestReadInitFilesWithSubdir(t *testing.T) {
157 // Create a file system with files in a subdirectory
158 testFS := fstest.MapFS{
159 "subdir/README.md": &fstest.MapFile{Data: []byte("# Test Repo")},
160 "subdir/.github/workflows/test.yml": &fstest.MapFile{Data: []byte("name: Test Workflow")},
161 "subdir/main.go": &fstest.MapFile{Data: []byte("package main")},
162 }
163
164 // Use fs.Sub to get a sub-filesystem
165 subFS, err := fs.Sub(testFS, "subdir")
166 if err != nil {
167 t.Fatalf("fs.Sub failed: %v", err)
168 }
169
170 files, err := readInitFiles(subFS)
171 if err != nil {
172 t.Fatalf("readInitFiles failed: %v", err)
173 }
174
175 // Should have 2 files: README.md and .github/workflows/test.yml
176 if len(files) != 2 {
177 t.Errorf("Expected 2 files, got %d", len(files))
178 }
179
180 // Verify README.md was found
181 if content, ok := files["README.md"]; !ok {
182 t.Error("README.md not found")
183 } else if content != "# Test Repo" {
184 t.Errorf("README.md has incorrect content: %q", content)
185 }
186
187 // Verify workflow file was found
188 if content, ok := files[".github/workflows/test.yml"]; !ok {
189 t.Error(".github/workflows/test.yml not found")
190 } else if content != "name: Test Workflow" {
191 t.Errorf("Workflow file has incorrect content: %q", content)
192 }
193}
David Crawshaw11129492025-04-25 20:41:53 -0700194
195// TestDockerHashIsPushed ensures that any changes made to the
196// dockerfile template have been pushed to the default image.
197func TestDockerHashIsPushed(t *testing.T) {
David Crawshaw2a5bd6d2025-04-30 14:29:46 -0700198 name, _, tag := DefaultImage()
David Crawshaw11129492025-04-25 20:41:53 -0700199
David Crawshaw2a5bd6d2025-04-30 14:29:46 -0700200 if err := checkTagExists(tag); err != nil {
201 if strings.Contains(err.Error(), "not found") {
202 t.Fatalf(`Currently released docker image %s does not match dockerfileCustomTmpl.
David Crawshaw11129492025-04-25 20:41:53 -0700203
David Crawshaw2a5bd6d2025-04-30 14:29:46 -0700204Inspecting the docker image shows the current hash of dockerfileBase is %s,
205but it is not published in the GitHub container registry.
David Crawshaw11129492025-04-25 20:41:53 -0700206
207This means the template constants in createdockerfile.go have been
David Crawshaw2a5bd6d2025-04-30 14:29:46 -0700208edited (e.g. dockerfileBase changed), but a new version
David Crawshaw11129492025-04-25 20:41:53 -0700209of the public default docker image has not been built and pushed.
210
211To do so:
212
213 go run ./dockerimg/pushdockerimg.go
214
David Crawshaw2a5bd6d2025-04-30 14:29:46 -0700215`, name, tag)
216 } else {
217 t.Fatalf("checkTagExists: %v", err)
218 }
David Crawshaw11129492025-04-25 20:41:53 -0700219 }
220}