| Earl Lee | 2e463fb | 2025-04-17 11:22:22 -0700 | [diff] [blame] | 1 | package dockerimg |
| 2 | |
| 3 | import ( |
| Josh Bleecher Snyder | 1c18ec9 | 2025-07-08 10:55:54 -0700 | [diff] [blame] | 4 | "bytes" |
| Earl Lee | 2e463fb | 2025-04-17 11:22:22 -0700 | [diff] [blame] | 5 | "context" |
| Josh Bleecher Snyder | 1c18ec9 | 2025-07-08 10:55:54 -0700 | [diff] [blame] | 6 | "crypto/sha256" |
| 7 | "encoding/hex" |
| Earl Lee | 2e463fb | 2025-04-17 11:22:22 -0700 | [diff] [blame] | 8 | "os" |
| Josh Bleecher Snyder | 1c18ec9 | 2025-07-08 10:55:54 -0700 | [diff] [blame] | 9 | "path/filepath" |
| Earl Lee | 2e463fb | 2025-04-17 11:22:22 -0700 | [diff] [blame] | 10 | "testing" |
| Earl Lee | 2e463fb | 2025-04-17 11:22:22 -0700 | [diff] [blame] | 11 | ) |
| 12 | |
| Philip Zeyliger | 983b58a | 2025-07-02 19:42:08 -0700 | [diff] [blame] | 13 | // TestDockerHashIsPushed tests that the published image hash is available |
| David Crawshaw | 1112949 | 2025-04-25 20:41:53 -0700 | [diff] [blame] | 14 | func TestDockerHashIsPushed(t *testing.T) { |
| Philip Zeyliger | 983b58a | 2025-07-02 19:42:08 -0700 | [diff] [blame] | 15 | // Skip this test if we can't reach the internet |
| 16 | if os.Getenv("CI") == "" { |
| 17 | t.Skip("Skipping test that requires internet access") |
| 18 | } |
| David Crawshaw | 1112949 | 2025-04-25 20:41:53 -0700 | [diff] [blame] | 19 | |
| Philip Zeyliger | 983b58a | 2025-07-02 19:42:08 -0700 | [diff] [blame] | 20 | if err := checkTagExists(dockerfileBaseHash()); err != nil { |
| 21 | t.Errorf("Docker image tag %s not found: %v", dockerfileBaseHash(), err) |
| 22 | } |
| David Crawshaw | 1112949 | 2025-04-25 20:41:53 -0700 | [diff] [blame] | 23 | |
| Philip Zeyliger | 983b58a | 2025-07-02 19:42:08 -0700 | [diff] [blame] | 24 | // Test that the default image components are reasonable |
| 25 | name, dockerfile, tag := DefaultImage() |
| 26 | if name == "" { |
| 27 | t.Error("DefaultImage name is empty") |
| 28 | } |
| 29 | if dockerfile == "" { |
| 30 | t.Error("DefaultImage dockerfile is empty") |
| 31 | } |
| 32 | if tag == "" { |
| 33 | t.Error("DefaultImage tag is empty") |
| 34 | } |
| 35 | if len(tag) < 10 { |
| 36 | t.Errorf("DefaultImage tag suspiciously short: %s", tag) |
| David Crawshaw | 1112949 | 2025-04-25 20:41:53 -0700 | [diff] [blame] | 37 | } |
| 38 | } |
| Josh Bleecher Snyder | 3e6a4c4 | 2025-05-23 17:29:57 +0000 | [diff] [blame] | 39 | |
| Philip Zeyliger | 983b58a | 2025-07-02 19:42:08 -0700 | [diff] [blame] | 40 | // TestGetHostGoCacheDirs tests that we can get the host Go cache directories |
| Josh Bleecher Snyder | 3e6a4c4 | 2025-05-23 17:29:57 +0000 | [diff] [blame] | 41 | func TestGetHostGoCacheDirs(t *testing.T) { |
| Philip Zeyliger | 983b58a | 2025-07-02 19:42:08 -0700 | [diff] [blame] | 42 | if !RaceEnabled() { |
| 43 | t.Skip("Race detector not enabled, skipping test") |
| 44 | } |
| 45 | |
| Josh Bleecher Snyder | 3e6a4c4 | 2025-05-23 17:29:57 +0000 | [diff] [blame] | 46 | ctx := context.Background() |
| 47 | |
| Josh Bleecher Snyder | 3e6a4c4 | 2025-05-23 17:29:57 +0000 | [diff] [blame] | 48 | goCacheDir, err := getHostGoCacheDir(ctx) |
| 49 | if err != nil { |
| 50 | t.Fatalf("getHostGoCacheDir failed: %v", err) |
| 51 | } |
| 52 | if goCacheDir == "" { |
| Philip Zeyliger | 983b58a | 2025-07-02 19:42:08 -0700 | [diff] [blame] | 53 | t.Error("GOCACHE is empty") |
| Josh Bleecher Snyder | 3e6a4c4 | 2025-05-23 17:29:57 +0000 | [diff] [blame] | 54 | } |
| Josh Bleecher Snyder | 3e6a4c4 | 2025-05-23 17:29:57 +0000 | [diff] [blame] | 55 | |
| Josh Bleecher Snyder | 3e6a4c4 | 2025-05-23 17:29:57 +0000 | [diff] [blame] | 56 | goModCacheDir, err := getHostGoModCacheDir(ctx) |
| 57 | if err != nil { |
| 58 | t.Fatalf("getHostGoModCacheDir failed: %v", err) |
| 59 | } |
| 60 | if goModCacheDir == "" { |
| Philip Zeyliger | 983b58a | 2025-07-02 19:42:08 -0700 | [diff] [blame] | 61 | t.Error("GOMODCACHE is empty") |
| Josh Bleecher Snyder | 3e6a4c4 | 2025-05-23 17:29:57 +0000 | [diff] [blame] | 62 | } |
| Philip Zeyliger | 983b58a | 2025-07-02 19:42:08 -0700 | [diff] [blame] | 63 | |
| 64 | t.Logf("GOCACHE: %s", goCacheDir) |
| Josh Bleecher Snyder | 3e6a4c4 | 2025-05-23 17:29:57 +0000 | [diff] [blame] | 65 | t.Logf("GOMODCACHE: %s", goModCacheDir) |
| Philip Zeyliger | 983b58a | 2025-07-02 19:42:08 -0700 | [diff] [blame] | 66 | } |
| Josh Bleecher Snyder | 3e6a4c4 | 2025-05-23 17:29:57 +0000 | [diff] [blame] | 67 | |
| Philip Zeyliger | 983b58a | 2025-07-02 19:42:08 -0700 | [diff] [blame] | 68 | // TestCreateCacheKey tests the cache key generation |
| 69 | func TestCreateCacheKey(t *testing.T) { |
| 70 | key1 := createCacheKey("image1", "/path1") |
| 71 | key2 := createCacheKey("image2", "/path1") |
| 72 | key3 := createCacheKey("image1", "/path2") |
| 73 | key4 := createCacheKey("image1", "/path1") |
| 74 | |
| 75 | // Different inputs should produce different keys |
| 76 | if key1 == key2 { |
| 77 | t.Error("Different base images should produce different cache keys") |
| Josh Bleecher Snyder | 3e6a4c4 | 2025-05-23 17:29:57 +0000 | [diff] [blame] | 78 | } |
| Philip Zeyliger | 983b58a | 2025-07-02 19:42:08 -0700 | [diff] [blame] | 79 | if key1 == key3 { |
| 80 | t.Error("Different paths should produce different cache keys") |
| 81 | } |
| 82 | |
| 83 | // Same inputs should produce same key |
| 84 | if key1 != key4 { |
| 85 | t.Error("Same inputs should produce same cache key") |
| 86 | } |
| 87 | |
| 88 | // Keys should be reasonably short |
| 89 | if len(key1) != 12 { |
| 90 | t.Errorf("Cache key length should be 12, got %d", len(key1)) |
| Josh Bleecher Snyder | 3e6a4c4 | 2025-05-23 17:29:57 +0000 | [diff] [blame] | 91 | } |
| 92 | } |
| Jon Friesen | d27921f | 2025-06-05 13:15:56 +0000 | [diff] [blame] | 93 | |
| Philip Zeyliger | 983b58a | 2025-07-02 19:42:08 -0700 | [diff] [blame] | 94 | // TestEnsureBaseImageExists tests the base image existence check and pull logic |
| 95 | func TestEnsureBaseImageExists(t *testing.T) { |
| 96 | // This test would require Docker to be running and would make network calls |
| 97 | // So we'll skip it unless we're in an integration test environment |
| 98 | if testing.Short() { |
| 99 | t.Skip("Skipping integration test that requires Docker") |
| Jon Friesen | d27921f | 2025-06-05 13:15:56 +0000 | [diff] [blame] | 100 | } |
| 101 | |
| Philip Zeyliger | 983b58a | 2025-07-02 19:42:08 -0700 | [diff] [blame] | 102 | ctx := context.Background() |
| Jon Friesen | d27921f | 2025-06-05 13:15:56 +0000 | [diff] [blame] | 103 | |
| Philip Zeyliger | 983b58a | 2025-07-02 19:42:08 -0700 | [diff] [blame] | 104 | // Test with a non-existent image (should fail gracefully) |
| 105 | err := ensureBaseImageExists(ctx, "nonexistent/image:tag") |
| 106 | if err == nil { |
| 107 | t.Error("Expected error for nonexistent image, got nil") |
| Philip Zeyliger | 2343f8a | 2025-06-17 06:16:19 -0700 | [diff] [blame] | 108 | } |
| 109 | } |
| Josh Bleecher Snyder | 1c18ec9 | 2025-07-08 10:55:54 -0700 | [diff] [blame] | 110 | |
| 111 | // TestBinaryCaching tests the content-addressable binary caching functionality |
| 112 | func TestBinaryCaching(t *testing.T) { |
| 113 | // Mock the embedded binary |
| 114 | testBinary := []byte("fake binary content for testing") |
| 115 | |
| 116 | // Calculate expected hash |
| 117 | hash := sha256.Sum256(testBinary) |
| 118 | hashHex := hex.EncodeToString(hash[:]) |
| 119 | |
| 120 | // Create a temporary directory for this test |
| 121 | tempDir := t.TempDir() |
| 122 | cacheDir := filepath.Join(tempDir, "sketch-binary-cache") |
| 123 | binaryPath := filepath.Join(cacheDir, hashHex) |
| 124 | |
| 125 | // First, create the cache directory |
| 126 | err := os.MkdirAll(cacheDir, 0o755) |
| 127 | if err != nil { |
| 128 | t.Fatalf("Failed to create cache directory: %v", err) |
| 129 | } |
| 130 | |
| 131 | // Verify the binary doesn't exist initially |
| 132 | if _, err := os.Stat(binaryPath); !os.IsNotExist(err) { |
| 133 | t.Fatalf("Binary should not exist initially, but stat returned: %v", err) |
| 134 | } |
| 135 | |
| 136 | // Write the binary (simulating first time) |
| 137 | err = os.WriteFile(binaryPath, testBinary, 0o700) |
| 138 | if err != nil { |
| 139 | t.Fatalf("Failed to write binary: %v", err) |
| 140 | } |
| 141 | |
| 142 | // Verify the binary now exists and has correct permissions |
| 143 | info, err := os.Stat(binaryPath) |
| 144 | if err != nil { |
| 145 | t.Fatalf("Failed to stat cached binary: %v", err) |
| 146 | } |
| 147 | |
| 148 | if info.Mode().Perm() != 0o700 { |
| 149 | t.Errorf("Expected permissions 0700, got %o", info.Mode().Perm()) |
| 150 | } |
| 151 | |
| 152 | // Verify content matches |
| 153 | cachedContent, err := os.ReadFile(binaryPath) |
| 154 | if err != nil { |
| 155 | t.Fatalf("Failed to read cached binary: %v", err) |
| 156 | } |
| 157 | |
| 158 | if !bytes.Equal(testBinary, cachedContent) { |
| 159 | t.Error("Cached binary content doesn't match original") |
| 160 | } |
| 161 | |
| 162 | // Test that the same hash produces the same path |
| 163 | hash2 := sha256.Sum256(testBinary) |
| 164 | hashHex2 := hex.EncodeToString(hash2[:]) |
| 165 | |
| 166 | if hashHex != hashHex2 { |
| 167 | t.Error("Same content should produce same hash") |
| 168 | } |
| 169 | |
| 170 | // Test that different content produces different hash |
| 171 | differentBinary := []byte("different fake binary content") |
| 172 | differentHash := sha256.Sum256(differentBinary) |
| 173 | differentHashHex := hex.EncodeToString(differentHash[:]) |
| 174 | |
| 175 | if hashHex == differentHashHex { |
| 176 | t.Error("Different content should produce different hash") |
| 177 | } |
| 178 | } |