| 1 | #!/usr/bin/env node |
| 2 | |
| 3 | const assert = require("node:assert/strict"); |
| 4 | const test = require("node:test"); |
| 5 | |
| 6 | const { |
| 7 | assertReleaseAssetsAbsent, |
| 8 | fetchRelease, |
| 9 | isNotFoundError, |
| 10 | validateTarget, |
| 11 | } = require("./ensure-release-assets-absent"); |
| 12 | |
| 13 | test("release immutability guard accepts a missing release or empty inventory", () => { |
| 14 | assert.doesNotThrow(() => assertReleaseAssetsAbsent(null, "v0.9.1")); |
| 15 | assert.doesNotThrow(() => assertReleaseAssetsAbsent({ assets: [] }, "v0.9.1")); |
| 16 | }); |
| 17 | |
| 18 | test("release immutability guard refuses any existing public asset", () => { |
| 19 | assert.throws( |
| 20 | () => |
| 21 | assertReleaseAssetsAbsent( |
| 22 | { |
| 23 | assets: [ |
| 24 | { name: "codewhale-linux-x64" }, |
| 25 | { name: "codewhale-artifacts-sha256.txt" }, |
| 26 | ], |
| 27 | }, |
| 28 | "v0.9.1", |
| 29 | ), |
| 30 | /Refusing to replace existing assets for v0\.9\.1: codewhale-linux-x64, codewhale-artifacts-sha256\.txt/, |
| 31 | ); |
| 32 | }); |
| 33 | |
| 34 | test("release immutability guard fails closed on malformed inventories", () => { |
| 35 | assert.throws( |
| 36 | () => assertReleaseAssetsAbsent({}, "v0.9.1"), |
| 37 | /did not provide an asset inventory/, |
| 38 | ); |
| 39 | }); |
| 40 | |
| 41 | test("release lookup treats only an explicit GitHub 404 as absent", () => { |
| 42 | const notFound = Object.assign(new Error("not found"), { |
| 43 | status: 1, |
| 44 | stderr: "gh: Not Found (HTTP 404)\n", |
| 45 | }); |
| 46 | assert.equal(isNotFoundError(notFound), true); |
| 47 | assert.equal( |
| 48 | fetchRelease("Hmbown/CodeWhale", "v0.9.1", "gh", () => { |
| 49 | throw notFound; |
| 50 | }), |
| 51 | null, |
| 52 | ); |
| 53 | |
| 54 | const forbidden = Object.assign(new Error("forbidden"), { |
| 55 | status: 1, |
| 56 | stderr: "gh: Resource not accessible (HTTP 403)\n", |
| 57 | }); |
| 58 | assert.throws( |
| 59 | () => |
| 60 | fetchRelease("Hmbown/CodeWhale", "v0.9.1", "gh", () => { |
| 61 | throw forbidden; |
| 62 | }), |
| 63 | /Could not inspect GitHub Release v0\.9\.1.*HTTP 403/, |
| 64 | ); |
| 65 | }); |
| 66 | |
| 67 | test("release lookup parses the exact tag endpoint and validates targets", () => { |
| 68 | let invocation; |
| 69 | const release = fetchRelease("Hmbown/CodeWhale", "v0.9.1", "/fake/gh", (...args) => { |
| 70 | invocation = args; |
| 71 | return JSON.stringify({ assets: [] }); |
| 72 | }); |
| 73 | assert.deepEqual(release, { assets: [] }); |
| 74 | assert.equal(invocation[0], "/fake/gh"); |
| 75 | assert.deepEqual(invocation[1], ["api", "repos/Hmbown/CodeWhale/releases/tags/v0.9.1"]); |
| 76 | assert.doesNotThrow(() => validateTarget("Hmbown/CodeWhale", "v0.9.1")); |
| 77 | assert.throws(() => validateTarget("bad repo", "v0.9.1"), /Invalid GitHub repository/); |
| 78 | assert.throws(() => validateTarget("Hmbown/CodeWhale", "main"), /Invalid release tag/); |
| 79 | }); |
| 80 | |
| 81 | test("release lookup rejects malformed successful API output", () => { |
| 82 | assert.throws( |
| 83 | () => fetchRelease("Hmbown/CodeWhale", "v0.9.1", "gh", () => "not-json"), |
| 84 | /returned invalid JSON/, |
| 85 | ); |
| 86 | }); |
| 87 |