Files
kandev/scripts/release/npm-view-version.test.mjs
2026-08-04 00:45:50 +01:00

108 lines
3.8 KiB
JavaScript

import assert from "node:assert/strict";
import { chmod, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { spawnSync } from "node:child_process";
import test from "node:test";
const repoRoot = path.resolve(import.meta.dirname, "../..");
const script = path.join(repoRoot, "scripts/release/npm-view-version.sh");
async function runView(mode) {
const fixtureDir = await mkdtemp(path.join(tmpdir(), "kandev-npm-view-"));
const npm = path.join(fixtureDir, "npm");
const sleep = path.join(fixtureDir, "sleep");
const countFile = path.join(fixtureDir, "attempts");
const sleepFile = path.join(fixtureDir, "sleeps");
await writeFile(
npm,
`#!/usr/bin/env bash
count=0
if [[ -f "$MOCK_NPM_COUNT_FILE" ]]; then read -r count < "$MOCK_NPM_COUNT_FILE"; fi
count=$((count + 1))
printf '%s\n' "$count" > "$MOCK_NPM_COUNT_FILE"
case "$MOCK_NPM_MODE" in
found) printf '%s\\n' '1.2.4-nightly.shaabc123def456' ;;
missing) printf '%s\\n' 'npm error code E404' 'npm error No match found for version nightly' >&2; exit 1 ;;
transient)
if (( count < 3 )); then
printf '%s\\n' 'npm error code EAI_AGAIN' >&2
exit 1
fi
printf '%s\\n' '1.2.4-nightly.shaabc123def456'
;;
failure) printf '%s\\n' 'npm error code EAI_AGAIN' 'registry-secret-detail' >&2; exit 1 ;;
misleading) printf '%s\\n' 'npm error code EAI_AGAIN' 'npm error request to https://registry.test/e404/404-Not-Found/is-not-in-this-registry failed' >&2; exit 1 ;;
esac
`,
);
await writeFile(sleepFile, "");
await writeFile(
sleep,
'#!/usr/bin/env bash\nprintf \'%s\\n\' "$1" >> "$MOCK_SLEEP_FILE"\n',
);
await chmod(npm, 0o755);
await chmod(sleep, 0o755);
try {
const result = spawnSync("bash", [script, "kandev@nightly"], {
encoding: "utf8",
env: {
...process.env,
MOCK_NPM_COUNT_FILE: countFile,
MOCK_NPM_MODE: mode,
MOCK_SLEEP_FILE: sleepFile,
PATH: `${fixtureDir}${path.delimiter}${process.env.PATH ?? ""}`,
},
});
result.attempts = Number(await readFile(countFile, "utf8"));
result.sleeps = (await readFile(sleepFile, "utf8"))
.trim()
.split("\n")
.filter(Boolean);
return result;
} finally {
await rm(fixtureDir, { recursive: true, force: true });
}
}
test("prints a resolved npm version", async () => {
const result = await runView("found");
assert.equal(result.status, 0);
assert.equal(result.stdout.trim(), "1.2.4-nightly.shaabc123def456");
assert.deepEqual(result.sleeps, []);
});
test("treats a missing version or dist-tag as an empty result", async () => {
const result = await runView("missing");
assert.equal(result.status, 0);
assert.equal(result.stdout, "");
assert.equal(result.stderr, "");
assert.equal(result.attempts, 1);
assert.deepEqual(result.sleeps, []);
});
test("retries transient registry failures before succeeding", async () => {
const result = await runView("transient");
assert.equal(result.status, 0);
assert.equal(result.stdout.trim(), "1.2.4-nightly.shaabc123def456");
assert.equal(result.attempts, 3);
assert.deepEqual(result.sleeps, ["2", "2"]);
});
test("fails closed on registry and network errors", async () => {
const result = await runView("failure");
assert.notEqual(result.status, 0);
assert.match(result.stderr, /npm view failed for kandev@nightly/);
assert.doesNotMatch(result.stderr, /registry-secret-detail/);
assert.equal(result.attempts, 3);
assert.deepEqual(result.sleeps, ["2", "2"]);
});
test("does not mistake missing-looking request URLs for npm E404 diagnostics", async () => {
const result = await runView("misleading");
assert.notEqual(result.status, 0);
assert.match(result.stderr, /npm view failed for kandev@nightly/);
assert.equal(result.attempts, 3);
assert.deepEqual(result.sleeps, ["2", "2"]);
});