|
1 | | -import { resolve } from "path"; |
| 1 | +import { createHash } from "crypto"; |
| 2 | +import { join, relative, resolve } from "path"; |
| 3 | +import { |
| 4 | + createReadStream, |
| 5 | + pathExists, |
| 6 | + readdir, |
| 7 | + readFile, |
| 8 | + stat, |
| 9 | +} from "fs-extra"; |
| 10 | +import { dir, DirectoryResult } from "tmp-promise"; |
2 | 11 | import { |
3 | 12 | excludeDirectories, |
4 | 13 | openZip, |
5 | 14 | openZipBuffer, |
6 | 15 | readZipEntries, |
| 16 | + unzipToDirectory, |
7 | 17 | } from "../../../src/common/unzip"; |
| 18 | +import { walkDirectory } from "../../../src/common/files"; |
8 | 19 |
|
9 | 20 | const zipPath = resolve(__dirname, "../data/unzip/test-zip.zip"); |
10 | 21 |
|
@@ -81,3 +92,109 @@ describe("openZipBuffer", () => { |
81 | 92 | expect(buffer.toString("utf8")).toEqual("I am a file\n\n"); |
82 | 93 | }); |
83 | 94 | }); |
| 95 | + |
| 96 | +describe("unzipToDirectory", () => { |
| 97 | + let tmpDir: DirectoryResult; |
| 98 | + |
| 99 | + beforeEach(async () => { |
| 100 | + tmpDir = await dir({ |
| 101 | + unsafeCleanup: true, |
| 102 | + }); |
| 103 | + }); |
| 104 | + |
| 105 | + afterEach(async () => { |
| 106 | + await tmpDir?.cleanup(); |
| 107 | + }); |
| 108 | + |
| 109 | + it("extracts all files", async () => { |
| 110 | + await unzipToDirectory(zipPath, tmpDir.path); |
| 111 | + |
| 112 | + const allFiles = []; |
| 113 | + for await (const file of walkDirectory(tmpDir.path, true)) { |
| 114 | + allFiles.push(file); |
| 115 | + } |
| 116 | + |
| 117 | + expect( |
| 118 | + allFiles.map((filePath) => relative(tmpDir.path, filePath)).sort(), |
| 119 | + ).toEqual([ |
| 120 | + "directory", |
| 121 | + "directory/file.txt", |
| 122 | + "directory/file2.txt", |
| 123 | + "directory2", |
| 124 | + "directory2/file.txt", |
| 125 | + "empty-directory", |
| 126 | + "tools", |
| 127 | + "tools/osx64", |
| 128 | + "tools/osx64/java-aarch64", |
| 129 | + "tools/osx64/java-aarch64/bin", |
| 130 | + "tools/osx64/java-aarch64/bin/java", |
| 131 | + ]); |
| 132 | + |
| 133 | + await expectFile(join(tmpDir.path, "directory", "file.txt"), { |
| 134 | + mode: 0o100644, |
| 135 | + contents: "I am a file\n\n", |
| 136 | + }); |
| 137 | + await expectFile(join(tmpDir.path, "directory", "file2.txt"), { |
| 138 | + mode: 0o100644, |
| 139 | + contents: "I am another file\n\n", |
| 140 | + }); |
| 141 | + await expectFile(join(tmpDir.path, "directory2", "file.txt"), { |
| 142 | + mode: 0o100600, |
| 143 | + contents: "I am secret\n", |
| 144 | + }); |
| 145 | + await expectFile( |
| 146 | + join(tmpDir.path, "tools", "osx64", "java-aarch64", "bin", "java"), |
| 147 | + { |
| 148 | + mode: 0o100755, |
| 149 | + hash: "68b832b5c0397c5baddbbb0a76cf5407b7ea5eee8f84f9ab9488f04a52e529eb", |
| 150 | + }, |
| 151 | + ); |
| 152 | + |
| 153 | + expect(await pathExists(join(tmpDir.path, "empty-directory"))).toBe(true); |
| 154 | + expect(await readdir(join(tmpDir.path, "empty-directory"))).toEqual([]); |
| 155 | + }); |
| 156 | +}); |
| 157 | + |
| 158 | +async function expectFile( |
| 159 | + filePath: string, |
| 160 | + { |
| 161 | + mode: expectedMode, |
| 162 | + hash: expectedHash, |
| 163 | + contents: expectedContents, |
| 164 | + }: { |
| 165 | + mode: number; |
| 166 | + hash?: string; |
| 167 | + contents?: string; |
| 168 | + }, |
| 169 | +) { |
| 170 | + const stats = await stat(filePath); |
| 171 | + expect(stats.mode).toEqual(expectedMode); |
| 172 | + |
| 173 | + if (expectedHash) { |
| 174 | + const hash = await computeHash(filePath); |
| 175 | + expect(hash).toEqual(expectedHash); |
| 176 | + } |
| 177 | + |
| 178 | + if (expectedContents) { |
| 179 | + const contents = await readFile(filePath); |
| 180 | + expect(contents.toString("utf-8")).toEqual(expectedContents); |
| 181 | + } |
| 182 | +} |
| 183 | + |
| 184 | +async function computeHash(filePath: string) { |
| 185 | + const input = createReadStream(filePath); |
| 186 | + const hash = createHash("sha256"); |
| 187 | + |
| 188 | + input.pipe(hash); |
| 189 | + |
| 190 | + await new Promise((resolve, reject) => { |
| 191 | + input.on("error", (err) => { |
| 192 | + reject(err); |
| 193 | + }); |
| 194 | + input.on("end", () => { |
| 195 | + resolve(undefined); |
| 196 | + }); |
| 197 | + }); |
| 198 | + |
| 199 | + return hash.digest("hex"); |
| 200 | +} |
0 commit comments