blob: e8a5d5af67ed9926c51e77fed74c34ba6e370a17 [file] [log] [blame]
Earl Lee2e463fb2025-04-17 11:22:22 -07001package dockerimg
2
3import (
Josh Bleecher Snyder1c18ec92025-07-08 10:55:54 -07004 "bytes"
Earl Lee2e463fb2025-04-17 11:22:22 -07005 "context"
Josh Bleecher Snyder1c18ec92025-07-08 10:55:54 -07006 "crypto/sha256"
7 "encoding/hex"
Earl Lee2e463fb2025-04-17 11:22:22 -07008 "os"
Josh Bleecher Snyderfa424f52025-07-11 18:43:55 +00009 "os/exec"
Josh Bleecher Snyder1c18ec92025-07-08 10:55:54 -070010 "path/filepath"
Earl Lee2e463fb2025-04-17 11:22:22 -070011 "testing"
Earl Lee2e463fb2025-04-17 11:22:22 -070012)
13
Philip Zeyliger983b58a2025-07-02 19:42:08 -070014// TestDockerHashIsPushed tests that the published image hash is available
David Crawshaw11129492025-04-25 20:41:53 -070015func TestDockerHashIsPushed(t *testing.T) {
Philip Zeyliger983b58a2025-07-02 19:42:08 -070016 // Skip this test if we can't reach the internet
17 if os.Getenv("CI") == "" {
18 t.Skip("Skipping test that requires internet access")
19 }
David Crawshaw11129492025-04-25 20:41:53 -070020
Philip Zeyliger983b58a2025-07-02 19:42:08 -070021 if err := checkTagExists(dockerfileBaseHash()); err != nil {
22 t.Errorf("Docker image tag %s not found: %v", dockerfileBaseHash(), err)
23 }
David Crawshaw11129492025-04-25 20:41:53 -070024
Philip Zeyliger983b58a2025-07-02 19:42:08 -070025 // Test that the default image components are reasonable
26 name, dockerfile, tag := DefaultImage()
27 if name == "" {
28 t.Error("DefaultImage name is empty")
29 }
30 if dockerfile == "" {
31 t.Error("DefaultImage dockerfile is empty")
32 }
33 if tag == "" {
34 t.Error("DefaultImage tag is empty")
35 }
36 if len(tag) < 10 {
37 t.Errorf("DefaultImage tag suspiciously short: %s", tag)
David Crawshaw11129492025-04-25 20:41:53 -070038 }
39}
Josh Bleecher Snyder3e6a4c42025-05-23 17:29:57 +000040
Philip Zeyliger983b58a2025-07-02 19:42:08 -070041// TestCreateCacheKey tests the cache key generation
42func TestCreateCacheKey(t *testing.T) {
43 key1 := createCacheKey("image1", "/path1")
44 key2 := createCacheKey("image2", "/path1")
45 key3 := createCacheKey("image1", "/path2")
46 key4 := createCacheKey("image1", "/path1")
47
48 // Different inputs should produce different keys
49 if key1 == key2 {
50 t.Error("Different base images should produce different cache keys")
Josh Bleecher Snyder3e6a4c42025-05-23 17:29:57 +000051 }
Philip Zeyliger983b58a2025-07-02 19:42:08 -070052 if key1 == key3 {
53 t.Error("Different paths should produce different cache keys")
54 }
55
56 // Same inputs should produce same key
57 if key1 != key4 {
58 t.Error("Same inputs should produce same cache key")
59 }
60
61 // Keys should be reasonably short
62 if len(key1) != 12 {
63 t.Errorf("Cache key length should be 12, got %d", len(key1))
Josh Bleecher Snyder3e6a4c42025-05-23 17:29:57 +000064 }
65}
Jon Friesend27921f2025-06-05 13:15:56 +000066
Philip Zeyliger983b58a2025-07-02 19:42:08 -070067// TestEnsureBaseImageExists tests the base image existence check and pull logic
68func TestEnsureBaseImageExists(t *testing.T) {
69 // This test would require Docker to be running and would make network calls
70 // So we'll skip it unless we're in an integration test environment
71 if testing.Short() {
72 t.Skip("Skipping integration test that requires Docker")
Jon Friesend27921f2025-06-05 13:15:56 +000073 }
74
Philip Zeyliger983b58a2025-07-02 19:42:08 -070075 ctx := context.Background()
Jon Friesend27921f2025-06-05 13:15:56 +000076
Philip Zeyliger983b58a2025-07-02 19:42:08 -070077 // Test with a non-existent image (should fail gracefully)
78 err := ensureBaseImageExists(ctx, "nonexistent/image:tag")
79 if err == nil {
80 t.Error("Expected error for nonexistent image, got nil")
Philip Zeyliger2343f8a2025-06-17 06:16:19 -070081 }
82}
Josh Bleecher Snyder1c18ec92025-07-08 10:55:54 -070083
84// TestBinaryCaching tests the content-addressable binary caching functionality
85func TestBinaryCaching(t *testing.T) {
86 // Mock the embedded binary
87 testBinary := []byte("fake binary content for testing")
88
89 // Calculate expected hash
90 hash := sha256.Sum256(testBinary)
91 hashHex := hex.EncodeToString(hash[:])
92
93 // Create a temporary directory for this test
94 tempDir := t.TempDir()
95 cacheDir := filepath.Join(tempDir, "sketch-binary-cache")
96 binaryPath := filepath.Join(cacheDir, hashHex)
97
98 // First, create the cache directory
99 err := os.MkdirAll(cacheDir, 0o755)
100 if err != nil {
101 t.Fatalf("Failed to create cache directory: %v", err)
102 }
103
104 // Verify the binary doesn't exist initially
105 if _, err := os.Stat(binaryPath); !os.IsNotExist(err) {
106 t.Fatalf("Binary should not exist initially, but stat returned: %v", err)
107 }
108
109 // Write the binary (simulating first time)
110 err = os.WriteFile(binaryPath, testBinary, 0o700)
111 if err != nil {
112 t.Fatalf("Failed to write binary: %v", err)
113 }
114
115 // Verify the binary now exists and has correct permissions
116 info, err := os.Stat(binaryPath)
117 if err != nil {
118 t.Fatalf("Failed to stat cached binary: %v", err)
119 }
120
121 if info.Mode().Perm() != 0o700 {
122 t.Errorf("Expected permissions 0700, got %o", info.Mode().Perm())
123 }
124
125 // Verify content matches
126 cachedContent, err := os.ReadFile(binaryPath)
127 if err != nil {
128 t.Fatalf("Failed to read cached binary: %v", err)
129 }
130
131 if !bytes.Equal(testBinary, cachedContent) {
132 t.Error("Cached binary content doesn't match original")
133 }
134
135 // Test that the same hash produces the same path
136 hash2 := sha256.Sum256(testBinary)
137 hashHex2 := hex.EncodeToString(hash2[:])
138
139 if hashHex != hashHex2 {
140 t.Error("Same content should produce same hash")
141 }
142
143 // Test that different content produces different hash
144 differentBinary := []byte("different fake binary content")
145 differentHash := sha256.Sum256(differentBinary)
146 differentHashHex := hex.EncodeToString(differentHash[:])
147
148 if hashHex == differentHashHex {
149 t.Error("Different content should produce different hash")
150 }
151}
Josh Bleecher Snyderfa424f52025-07-11 18:43:55 +0000152
153func TestCollectGoModules(t *testing.T) {
154 // Create a temporary directory with test files
155 tempDir := t.TempDir()
156
157 // Initialize a git repository
158 cmd := exec.Command("git", "init", ".")
159 cmd.Dir = tempDir
160 if err := cmd.Run(); err != nil {
161 t.Fatalf("Failed to init git repo: %v", err)
162 }
163
164 // Create test go.mod files
165 modContent := "module test\n\ngo 1.19\n"
166 sumContent := "example.com/test v1.0.0 h1:abc\n"
167
168 // Root go.mod
169 if err := os.WriteFile(filepath.Join(tempDir, "go.mod"), []byte(modContent), 0o644); err != nil {
170 t.Fatalf("Failed to create go.mod: %v", err)
171 }
172 if err := os.WriteFile(filepath.Join(tempDir, "go.sum"), []byte(sumContent), 0o644); err != nil {
173 t.Fatalf("Failed to create go.sum: %v", err)
174 }
175
176 // Subdirectory go.mod
177 subDir := filepath.Join(tempDir, "subdir")
178 if err := os.MkdirAll(subDir, 0o755); err != nil {
179 t.Fatalf("Failed to create subdir: %v", err)
180 }
181 if err := os.WriteFile(filepath.Join(subDir, "go.mod"), []byte(modContent), 0o644); err != nil {
182 t.Fatalf("Failed to create subdir/go.mod: %v", err)
183 }
184 // No go.sum for subdir to test the case where go.sum is missing
185
186 // Add files to git
187 cmd = exec.Command("git", "add", ".")
188 cmd.Dir = tempDir
189 if err := cmd.Run(); err != nil {
190 t.Fatalf("Failed to add files to git: %v", err)
191 }
192
193 // Configure git user for the test repo
194 cmd = exec.Command("git", "config", "user.email", "test@example.com")
195 cmd.Dir = tempDir
196 if err := cmd.Run(); err != nil {
197 t.Fatalf("Failed to set git user email: %v", err)
198 }
199 cmd = exec.Command("git", "config", "user.name", "Test User")
200 cmd.Dir = tempDir
201 if err := cmd.Run(); err != nil {
202 t.Fatalf("Failed to set git user name: %v", err)
203 }
204
205 // Commit the files
206 cmd = exec.Command("git", "commit", "-m", "test commit")
207 cmd.Dir = tempDir
208 if err := cmd.Run(); err != nil {
209 t.Fatalf("Failed to commit files: %v", err)
210 }
211
212 // Collect go modules
213 ctx := context.Background()
214 modules, err := collectGoModules(ctx, tempDir)
215 if err != nil {
216 t.Fatalf("collectGoModules failed: %v", err)
217 }
218
219 // Verify results
220 if len(modules) != 2 {
221 t.Fatalf("Expected 2 modules, got %d", len(modules))
222 }
223
224 // Check root module
225 root := modules[0]
226 if root.modPath != "go.mod" {
227 t.Errorf("Expected root modPath to be 'go.mod', got %s", root.modPath)
228 }
229 if root.modSHA == "" {
230 t.Errorf("Expected root modSHA to be non-empty")
231 }
232 if root.sumSHA == "" {
233 t.Errorf("Expected root sumSHA to be non-empty")
234 }
235
236 // Check subdir module
237 sub := modules[1]
238 if sub.modPath != "subdir/go.mod" {
239 t.Errorf("Expected subdir modPath to be 'subdir/go.mod', got %s", sub.modPath)
240 }
241 if sub.modSHA == "" {
242 t.Errorf("Expected subdir modSHA to be non-empty")
243 }
244 if sub.sumSHA != "" {
245 t.Errorf("Expected subdir sumSHA to be empty, got %s", sub.sumSHA)
246 }
247}
248
249func TestCollectGoModulesNoModFiles(t *testing.T) {
250 // Create a temporary directory with no go.mod files
251 tempDir := t.TempDir()
252
253 // Initialize a git repository
254 cmd := exec.Command("git", "init", ".")
255 cmd.Dir = tempDir
256 if err := cmd.Run(); err != nil {
257 t.Fatalf("Failed to init git repo: %v", err)
258 }
259
260 // Create a non-go.mod file
261 if err := os.WriteFile(filepath.Join(tempDir, "README.md"), []byte("# Test"), 0o644); err != nil {
262 t.Fatalf("Failed to create README.md: %v", err)
263 }
264
265 // Add files to git
266 cmd = exec.Command("git", "add", ".")
267 cmd.Dir = tempDir
268 if err := cmd.Run(); err != nil {
269 t.Fatalf("Failed to add files to git: %v", err)
270 }
271
272 // Configure git user for the test repo
273 cmd = exec.Command("git", "config", "user.email", "test@example.com")
274 cmd.Dir = tempDir
275 if err := cmd.Run(); err != nil {
276 t.Fatalf("Failed to set git user email: %v", err)
277 }
278 cmd = exec.Command("git", "config", "user.name", "Test User")
279 cmd.Dir = tempDir
280 if err := cmd.Run(); err != nil {
281 t.Fatalf("Failed to set git user name: %v", err)
282 }
283
284 // Commit the files
285 cmd = exec.Command("git", "commit", "-m", "test commit")
286 cmd.Dir = tempDir
287 if err := cmd.Run(); err != nil {
288 t.Fatalf("Failed to commit files: %v", err)
289 }
290
291 // Collect go modules
292 ctx := context.Background()
293 modules, err := collectGoModules(ctx, tempDir)
294 if err != nil {
295 t.Fatalf("collectGoModules failed: %v", err)
296 }
297
298 // Verify no modules found
299 if len(modules) != 0 {
300 t.Fatalf("Expected 0 modules, got %d", len(modules))
301 }
302}