|
4 | 4 | "archive/tar" |
5 | 5 | "archive/zip" |
6 | 6 | "bytes" |
7 | | - "io" |
8 | 7 | "os" |
9 | 8 | "path/filepath" |
10 | 9 | "testing" |
@@ -91,195 +90,6 @@ func TestComputeArchiveHash(t *testing.T) { |
91 | 90 | }) |
92 | 91 | } |
93 | 92 |
|
94 | | -func TestNormalizeZip(t *testing.T) { |
95 | | - t.Parallel() |
96 | | - |
97 | | - t.Run("AddsMissingDirectoryEntries", func(t *testing.T) { |
98 | | - t.Parallel() |
99 | | - // Create a zip with only file entries (no directory entries), |
100 | | - // mimicking hashicorp/archive's archive_file data source. |
101 | | - zipPath := filepath.Join(t.TempDir(), "nodirs.zip") |
102 | | - var buf bytes.Buffer |
103 | | - zw := zip.NewWriter(&buf) |
104 | | - |
105 | | - // Top-level regular file. |
106 | | - fw, err := zw.Create("main.tf") |
107 | | - require.NoError(t, err) |
108 | | - _, err = fw.Write([]byte("resource {}")) |
109 | | - require.NoError(t, err) |
110 | | - |
111 | | - // Top-level hidden file (dotfile at root). |
112 | | - fw, err = zw.Create(".env") |
113 | | - require.NoError(t, err) |
114 | | - _, err = fw.Write([]byte("SECRET=value")) |
115 | | - require.NoError(t, err) |
116 | | - |
117 | | - // Top-level hidden directory with a file inside. |
118 | | - fw, err = zw.Create(".config/settings.json") |
119 | | - require.NoError(t, err) |
120 | | - _, err = fw.Write([]byte(`{"key": "value"}`)) |
121 | | - require.NoError(t, err) |
122 | | - |
123 | | - // Hidden file inside a regular directory. |
124 | | - fw, err = zw.Create("subdir/.hidden") |
125 | | - require.NoError(t, err) |
126 | | - _, err = fw.Write([]byte("hidden content")) |
127 | | - require.NoError(t, err) |
128 | | - |
129 | | - // Deeply nested hidden directory with a hidden file inside. |
130 | | - fw, err = zw.Create("a/b/.secret-dir/.credentials") |
131 | | - require.NoError(t, err) |
132 | | - _, err = fw.Write([]byte("token=abc123")) |
133 | | - require.NoError(t, err) |
134 | | - |
135 | | - require.NoError(t, zw.Close()) |
136 | | - err = os.WriteFile(zipPath, buf.Bytes(), 0644) |
137 | | - require.NoError(t, err) |
138 | | - |
139 | | - // Verify original has no directory entries. |
140 | | - origReader, err := zip.OpenReader(zipPath) |
141 | | - require.NoError(t, err) |
142 | | - for _, f := range origReader.File { |
143 | | - require.False(t, f.FileInfo().IsDir(), "expected no dir entries in original, got %q", f.Name) |
144 | | - } |
145 | | - require.NoError(t, origReader.Close()) |
146 | | - |
147 | | - // Normalize. |
148 | | - data, err := normalizeZip(zipPath) |
149 | | - require.NoError(t, err) |
150 | | - |
151 | | - // Read normalized zip and collect entries. |
152 | | - normReader, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) |
153 | | - require.NoError(t, err) |
154 | | - |
155 | | - dirs := make(map[string]bool) |
156 | | - files := make(map[string]bool) |
157 | | - for _, f := range normReader.File { |
158 | | - if f.FileInfo().IsDir() { |
159 | | - dirs[f.Name] = true |
160 | | - } else { |
161 | | - files[f.Name] = true |
162 | | - } |
163 | | - } |
164 | | - |
165 | | - // All intermediate directories should exist, including hidden ones. |
166 | | - require.True(t, dirs[".config/"], "missing .config/") |
167 | | - require.True(t, dirs["subdir/"], "missing subdir/") |
168 | | - require.True(t, dirs["a/"], "missing a/") |
169 | | - require.True(t, dirs["a/b/"], "missing a/b/") |
170 | | - require.True(t, dirs["a/b/.secret-dir/"], "missing a/b/.secret-dir/") |
171 | | - |
172 | | - // All original files should still be present. |
173 | | - require.True(t, files["main.tf"], "missing main.tf") |
174 | | - require.True(t, files[".env"], "missing .env") |
175 | | - require.True(t, files[".config/settings.json"], "missing .config/settings.json") |
176 | | - require.True(t, files["subdir/.hidden"], "missing subdir/.hidden") |
177 | | - require.True(t, files["a/b/.secret-dir/.credentials"], "missing a/b/.secret-dir/.credentials") |
178 | | - }) |
179 | | - |
180 | | - t.Run("PreservesDirectoryPermissions", func(t *testing.T) { |
181 | | - t.Parallel() |
182 | | - // Verify that synthesized directory entries have proper mode bits |
183 | | - // so the server-side extractor can create them. |
184 | | - zipPath := filepath.Join(t.TempDir(), "needsperms.zip") |
185 | | - var buf bytes.Buffer |
186 | | - zw := zip.NewWriter(&buf) |
187 | | - |
188 | | - fw, err := zw.Create("deep/nested/dir/file.txt") |
189 | | - require.NoError(t, err) |
190 | | - _, err = fw.Write([]byte("content")) |
191 | | - require.NoError(t, err) |
192 | | - require.NoError(t, zw.Close()) |
193 | | - err = os.WriteFile(zipPath, buf.Bytes(), 0644) |
194 | | - require.NoError(t, err) |
195 | | - |
196 | | - data, err := normalizeZip(zipPath) |
197 | | - require.NoError(t, err) |
198 | | - |
199 | | - normReader, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) |
200 | | - require.NoError(t, err) |
201 | | - |
202 | | - for _, f := range normReader.File { |
203 | | - if f.FileInfo().IsDir() { |
204 | | - mode := f.Mode() |
205 | | - require.NotZero(t, mode&0700, |
206 | | - "directory %q should have owner rwx bits set, got %s", f.Name, mode) |
207 | | - } |
208 | | - } |
209 | | - }) |
210 | | - |
211 | | - t.Run("NoChangeWhenDirsExist", func(t *testing.T) { |
212 | | - t.Parallel() |
213 | | - zipPath := filepath.Join(t.TempDir(), "withdirs.zip") |
214 | | - var buf bytes.Buffer |
215 | | - zw := zip.NewWriter(&buf) |
216 | | - |
217 | | - // Add directory entry explicitly. |
218 | | - _, err := zw.Create("subdir/") |
219 | | - require.NoError(t, err) |
220 | | - fw, err := zw.Create("subdir/file.txt") |
221 | | - require.NoError(t, err) |
222 | | - _, err = fw.Write([]byte("content")) |
223 | | - require.NoError(t, err) |
224 | | - require.NoError(t, zw.Close()) |
225 | | - |
226 | | - origData := buf.Bytes() |
227 | | - err = os.WriteFile(zipPath, origData, 0644) |
228 | | - require.NoError(t, err) |
229 | | - |
230 | | - // Normalize should return original bytes unchanged. |
231 | | - data, err := normalizeZip(zipPath) |
232 | | - require.NoError(t, err) |
233 | | - require.Equal(t, origData, data) |
234 | | - }) |
235 | | - |
236 | | - t.Run("PreservesFileContent", func(t *testing.T) { |
237 | | - t.Parallel() |
238 | | - // Verify normalization doesn't corrupt file contents, |
239 | | - // including hidden files at various levels. |
240 | | - zipPath := filepath.Join(t.TempDir(), "content.zip") |
241 | | - var buf bytes.Buffer |
242 | | - zw := zip.NewWriter(&buf) |
243 | | - |
244 | | - fileContents := map[string]string{ |
245 | | - ".gitignore": "node_modules/\n.env\n", |
246 | | - "main.tf": "resource \"null_resource\" \"test\" {}", |
247 | | - ".vscode/settings.json": `{"editor.formatOnSave": true}`, |
248 | | - "scripts/.local/bin/deploy": "#!/bin/bash\necho deploy", |
249 | | - } |
250 | | - for name, content := range fileContents { |
251 | | - fw, err := zw.Create(name) |
252 | | - require.NoError(t, err) |
253 | | - _, err = fw.Write([]byte(content)) |
254 | | - require.NoError(t, err) |
255 | | - } |
256 | | - require.NoError(t, zw.Close()) |
257 | | - err := os.WriteFile(zipPath, buf.Bytes(), 0644) |
258 | | - require.NoError(t, err) |
259 | | - |
260 | | - data, err := normalizeZip(zipPath) |
261 | | - require.NoError(t, err) |
262 | | - |
263 | | - normReader, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) |
264 | | - require.NoError(t, err) |
265 | | - |
266 | | - for _, f := range normReader.File { |
267 | | - if f.FileInfo().IsDir() { |
268 | | - continue |
269 | | - } |
270 | | - expected, ok := fileContents[f.Name] |
271 | | - require.True(t, ok, "unexpected file in normalized zip: %q", f.Name) |
272 | | - rc, err := f.Open() |
273 | | - require.NoError(t, err) |
274 | | - actual, err := io.ReadAll(rc) |
275 | | - require.NoError(t, rc.Close()) |
276 | | - require.NoError(t, err) |
277 | | - require.Equal(t, expected, string(actual), |
278 | | - "content mismatch for %q", f.Name) |
279 | | - } |
280 | | - }) |
281 | | -} |
282 | | - |
283 | 93 | func TestArchiveContentType(t *testing.T) { |
284 | 94 | t.Parallel() |
285 | 95 |
|
|
0 commit comments