"use strict";
const test = require("node:test");
const assert = require("node:assert");
const fs = require("fs");
const os = require("os");
const path = require("path");
const { spawnSync } = require("child_process");
const pkgName = require("../package.json").name;
const {
CHECK_ATTEMPTS, acquireLock, compareVersions, latestVersion, npmCommand, parseArgs, stableVersion, summarizeError,
updateMode, updaterEnvironment, withNpmIsolation,
} = require("../lib/bootstrap-update-helper");
test("bootstrap updater accepts only stable versions", () => {
assert.deepStrictEqual(stableVersion("1.2.3"), [1, 2, 3]);
assert.strictEqual(stableVersion("1.2.3-beta.1"), null);
assert.strictEqual(compareVersions("1.2.3", "1.2.4"), -1);
});
test("bootstrap updater parses detached update arguments", () => {
const manifest = path.join(fs.mkdtempSync(path.join(os.tmpdir(), "gc-manifest-")), "install.json");
assert.deepStrictEqual(
parseArgs(["--background", "--force", "--parent-pid", "42", "--manifest", manifest]),
{ background: true, check: false, force: true, json: false, manifest, parentPid: 42 }
);
});
test("bootstrap updater honors enterprise update mode", () => {
assert.strictEqual(updateMode({ GC_CONFIG_DIR: fs.mkdtempSync(path.join(os.tmpdir(), "gc-config-")) }), "notify");
assert.strictEqual(updateMode({ GC_UPDATE_MODE: "notify" }), "notify");
assert.strictEqual(updateMode({ GC_UPDATE_MODE: "off" }), "off");
});
test("bootstrap updater uses a minimal child environment", () => {
const clean = updaterEnvironment({ PATH: "/bin", GH_TOKEN: "secret", npm_config_registry: "mirror" });
assert.strictEqual(clean.PATH, "/bin");
assert.strictEqual(clean.GH_TOKEN, undefined);
assert.strictEqual(clean.npm_config_registry, undefined);
});
test("bootstrap npm exec isolates config before the command separator", () => {
const args = withNpmIsolation(["exec", "--yes", "--", "gitcode", "install"], "user.npmrc", "global.npmrc");
const separator = args.indexOf("--");
assert.ok(args.slice(0, separator).includes("--userconfig=user.npmrc"));
if (pkgName.startsWith("@")) {
assert.ok(args.slice(0, separator).includes(`--${pkgName.split("/")[0]}:registry=https://registry.npmjs.org`));
} else {
assert.ok(!args.some((a) => a.includes(":registry=")), "unscoped package names must not carry a scope-registry flag");
}
assert.ok(args.slice(0, separator).includes("--registry=https://registry.npmjs.org"));
assert.deepStrictEqual(args.slice(separator + 1), ["gitcode", "install"]);
});
test("bootstrap updater falls back to PATH when recorded npm is stale", () => {
const missing = path.join(os.tmpdir(), "missing-npm-cli.js");
const command = npmCommand({ npm: missing });
assert.ok(command.command);
assert.notDeepStrictEqual(command.prefix, [missing]);
});
function flakyNpmStub(dir, failures, version = "0.0.2") {
const attemptsFile = path.join(dir, "attempts");
const stub = path.join(dir, "flaky-npm-cli.js");
fs.writeFileSync(stub, [
"const fs = require('fs');",
`const attempts = fs.existsSync(${JSON.stringify(attemptsFile)}) ? Number(fs.readFileSync(${JSON.stringify(attemptsFile)}, "utf8")) : 0;`,
`fs.writeFileSync(${JSON.stringify(attemptsFile)}, String(attempts + 1));`,
`if (attempts < ${failures}) { process.stderr.write("network ECONNRESET from stub\\n"); process.exit(1); }`,
`process.stdout.write(${JSON.stringify(`"${version}"\n`)});`,
"",
].join("\n"));
return { stub, attemptsFile };
}
test("bootstrap registry checks retry transient failures before giving up", () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "gc-bootstrap-retry-"));
const { stub, attemptsFile } = flakyNpmStub(dir, CHECK_ATTEMPTS - 1);
assert.strictEqual(latestVersion({ npm: stub }), "0.0.2");
assert.strictEqual(Number(fs.readFileSync(attemptsFile, "utf8")), CHECK_ATTEMPTS);
});
test("bootstrap registry checks fail after exhausting the retry budget", () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "gc-bootstrap-retry-exhaust-"));
const { stub, attemptsFile } = flakyNpmStub(dir, CHECK_ATTEMPTS);
assert.throws(() => latestVersion({ npm: stub }), /ECONNRESET/);
assert.strictEqual(Number(fs.readFileSync(attemptsFile, "utf8")), CHECK_ATTEMPTS);
});
test("bootstrap registry checks fail fast on deterministic errors", () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "gc-bootstrap-e404-"));
const attemptsFile = path.join(dir, "attempts");
const stub = path.join(dir, "e404-npm-cli.js");
fs.writeFileSync(stub, [
"const fs = require('fs');",
`const n = fs.existsSync(${JSON.stringify(attemptsFile)}) ? Number(fs.readFileSync(${JSON.stringify(attemptsFile)}, "utf8")) : 0;`,
`fs.writeFileSync(${JSON.stringify(attemptsFile)}, String(n + 1));`,
'process.stderr.write("npm error code E404\\n");',
"process.exit(1);",
"",
].join("\n"));
assert.throws(() => latestVersion({ npm: stub }), /E404/);
assert.strictEqual(Number(fs.readFileSync(attemptsFile, "utf8")), 1, "E404 must not be retried");
});
test("a prerelease bootstrap current is reported as available", { timeout: 30000 }, () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "gc-bootstrap-prerelease-"));
const { stub } = flakyNpmStub(root, 0, "0.0.2");
const manifestFile = path.join(root, "install.json");
fs.writeFileSync(manifestFile, JSON.stringify({
distribution: "npm-bootstrap",
version: "0.0.1-rc.1",
targetDir: root,
npm: stub,
helper: path.join(root, "helper.js"),
}));
const result = spawnSync(process.execPath, [path.join(__dirname, "..", "lib", "bootstrap-update-helper.js"), "--check", "--json", "--manifest", manifestFile], {
encoding: "utf8",
timeout: 20000,
env: { PATH: process.env.PATH, HOME: process.env.HOME, GC_STATE_DIR: path.join(root, "state") },
});
assert.strictEqual(result.status, 0, result.stderr);
const payload = JSON.parse(result.stdout);
assert.strictEqual(payload.status, "available");
assert.match(payload.message, /0\.0\.2 is available/);
assert.match(payload.message, /prerelease/);
});
test("bootstrap background current check queues no summary and clears stale notices", { timeout: 30000 }, () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "gc-bootstrap-current-"));
const stateDir = path.join(root, "state");
fs.mkdirSync(stateDir, { recursive: true });
const { stub } = flakyNpmStub(root, 0, "0.0.1");
const manifestFile = path.join(root, "install.json");
fs.writeFileSync(manifestFile, JSON.stringify({
distribution: "npm-bootstrap",
version: "0.0.1",
targetDir: root,
npm: stub,
helper: path.join(root, "helper.js"),
}));
fs.writeFileSync(path.join(stateDir, "update-state.json"), JSON.stringify({
summary: { message: "GitCode CLI 9.9.9 is available.", shown: false },
}));
const result = spawnSync(process.execPath, [path.join(__dirname, "..", "lib", "bootstrap-update-helper.js"), "--background", "--force", "--manifest", manifestFile], {
encoding: "utf8",
timeout: 20000,
env: { PATH: process.env.PATH, HOME: process.env.HOME, GC_STATE_DIR: stateDir },
});
assert.strictEqual(result.status, 0, result.stderr);
const state = JSON.parse(fs.readFileSync(path.join(stateDir, "update-state.json"), "utf8"));
assert.strictEqual(state.summary, undefined, "current results must not queue a daily notice");
assert.ok(state.nextCheck > Date.now());
});
test("bootstrap state lands under the package/npm-bootstrap directory", { timeout: 30000 }, () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "gc-bootstrap-scoped-"));
const { stub } = flakyNpmStub(root, 0, "0.0.1");
const manifestFile = path.join(root, "install.json");
fs.writeFileSync(manifestFile, JSON.stringify({
distribution: "npm-bootstrap",
version: "0.0.1",
targetDir: root,
npm: stub,
helper: path.join(root, "helper.js"),
package: pkgName,
}));
const result = spawnSync(process.execPath, [path.join(__dirname, "..", "lib", "bootstrap-update-helper.js"), "--background", "--force", "--manifest", manifestFile], {
encoding: "utf8",
timeout: 20000,
env: {
PATH: process.env.PATH,
HOME: process.env.HOME,
XDG_STATE_HOME: root,
LOCALAPPDATA: path.join(root, "la"),
},
});
assert.strictEqual(result.status, 0, result.stderr);
const base = process.platform === "win32"
? path.join(root, "la", "gitcode-cli")
: path.join(root, "gitcode-cli");
const stateFile = path.join(base, pkgName, "npm-bootstrap", "update-state.json");
const state = JSON.parse(fs.readFileSync(stateFile, "utf8"));
assert.ok(state.nextCheck > Date.now());
});
test("an invalid manifest is recorded as a permanent failure", { timeout: 30000 }, () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "gc-bootstrap-permanent-"));
const stateDir = path.join(root, "state");
fs.mkdirSync(stateDir, { recursive: true });
const manifestFile = path.join(root, "install.json");
fs.writeFileSync(manifestFile, JSON.stringify({ distribution: "bogus" }));
const helper = path.join(__dirname, "..", "lib", "bootstrap-update-helper.js");
const first = spawnSync(process.execPath, [helper, "--background", "--force", "--manifest", manifestFile], {
encoding: "utf8",
timeout: 20000,
env: { PATH: process.env.PATH, HOME: process.env.HOME, GC_STATE_DIR: stateDir },
});
assert.strictEqual(first.status, 1);
const stateFile = path.join(stateDir, "update-state.json");
const state = JSON.parse(fs.readFileSync(stateFile, "utf8"));
assert.strictEqual(state.permanentError, true);
assert.strictEqual(state.failureStreak, 1);
assert.ok(state.nextCheck > Date.now(), "backoff still applies to manual retries");
assert.match(state.summary.message, /invalid npm-bootstrap install manifest/);
assert.match(state.summary.message, /Background checks are paused/);
});
test("a permanent failure stops scheduled helper runs even when due", { timeout: 30000 }, () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "gc-bootstrap-perm-gate-"));
const stateDir = path.join(root, "state");
fs.mkdirSync(stateDir, { recursive: true });
const { stub } = flakyNpmStub(root, 99, "9.9.9");
const manifestFile = path.join(root, "install.json");
fs.writeFileSync(manifestFile, JSON.stringify({
distribution: "npm-bootstrap",
version: "0.0.1",
targetDir: root,
npm: stub,
helper: path.join(root, "helper.js"),
}));
const stateFile = path.join(stateDir, "update-state.json");
fs.writeFileSync(stateFile, JSON.stringify({
permanentError: true,
failureStreak: 3,
nextCheck: 1,
summary: { message: "Automatic update failed: earlier.", shown: true },
}));
const helper = path.join(__dirname, "..", "lib", "bootstrap-update-helper.js");
const result = spawnSync(process.execPath, [helper, "--background", "--manifest", manifestFile], {
encoding: "utf8",
timeout: 20000,
env: { PATH: process.env.PATH, HOME: process.env.HOME, GC_STATE_DIR: stateDir },
});
assert.strictEqual(result.status, 0, result.stderr);
assert.strictEqual(JSON.parse(fs.readFileSync(stateFile, "utf8")).nextCheck, 1, "state must be untouched");
assert.strictEqual(fs.existsSync(path.join(root, "attempts")), false, "the flaky npm stub must not be invoked");
});
test("an unwritable update.log never turns a successful bootstrap check into a failure", { timeout: 30000 }, () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "gc-bootstrap-log-fail-"));
const stateDir = path.join(root, "state");
fs.mkdirSync(stateDir, { recursive: true });
fs.mkdirSync(path.join(stateDir, "update.log"));
const { stub } = flakyNpmStub(root, 0, "0.0.1");
const manifestFile = path.join(root, "install.json");
fs.writeFileSync(manifestFile, JSON.stringify({
distribution: "npm-bootstrap",
version: "0.0.1",
targetDir: root,
npm: stub,
helper: path.join(root, "helper.js"),
}));
const result = spawnSync(process.execPath, [path.join(__dirname, "..", "lib", "bootstrap-update-helper.js"), "--check", "--json", "--manifest", manifestFile], {
encoding: "utf8",
timeout: 20000,
env: { PATH: process.env.PATH, HOME: process.env.HOME, GC_STATE_DIR: stateDir },
});
assert.strictEqual(result.status, 0, result.stderr);
const payload = JSON.parse(result.stdout);
assert.strictEqual(payload.status, "current");
});
test("bootstrap --json reports argument errors as JSON", () => {
const result = spawnSync(process.execPath, [
path.join(__dirname, "..", "lib", "bootstrap-update-helper.js"),
"--json", "--bogus-flag", "--manifest", "/nonexistent",
], { encoding: "utf8", timeout: 20000, env: { PATH: process.env.PATH, HOME: process.env.HOME } });
assert.strictEqual(result.status, 1);
const payload = JSON.parse(result.stdout);
assert.strictEqual(payload.status, "error");
assert.match(payload.message, /unknown updater argument/);
});
test("a background bootstrap update re-checks the TTL under the lock", { timeout: 30000 }, () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "gc-bootstrap-cached-"));
const stateDir = path.join(root, "state");
fs.mkdirSync(stateDir, { recursive: true });
const { stub, attemptsFile } = flakyNpmStub(root, 0, "9.9.9");
const manifestFile = path.join(root, "install.json");
fs.writeFileSync(manifestFile, JSON.stringify({
distribution: "npm-bootstrap",
version: "0.0.1",
targetDir: root,
npm: stub,
helper: path.join(root, "helper.js"),
}));
fs.writeFileSync(path.join(stateDir, "update-state.json"), JSON.stringify({
nextCheck: Date.now() + 60 * 60 * 1000,
}));
const result = spawnSync(process.execPath, [path.join(__dirname, "..", "lib", "bootstrap-update-helper.js"), "--background", "--manifest", manifestFile], {
encoding: "utf8",
timeout: 20000,
env: { PATH: process.env.PATH, HOME: process.env.HOME, GC_STATE_DIR: stateDir },
});
assert.strictEqual(result.status, 0, result.stderr);
const state = JSON.parse(fs.readFileSync(path.join(stateDir, "update-state.json"), "utf8"));
assert.strictEqual(state.status, undefined, "a cached run must not touch the state");
assert.strictEqual(fs.existsSync(attemptsFile), false, "the npm stub must not be invoked");
});
test("an explicit --force update after a foreground check still installs (Go update chain)", { timeout: 30000 }, () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "gc-bootstrap-force-chain-"));
const stateDir = path.join(root, "state");
fs.mkdirSync(stateDir, { recursive: true });
const { stub, attemptsFile } = flakyNpmStub(root, 0, "9.9.9");
if (process.platform !== "win32") {
fs.writeFileSync(path.join(root, "gitcode"), "#!/bin/sh\necho '{\"version\":\"9.9.9\"}'\n", { mode: 0o755 });
}
const manifestFile = path.join(root, "install.json");
fs.writeFileSync(manifestFile, JSON.stringify({
distribution: "npm-bootstrap",
version: "0.0.1",
targetDir: root,
npm: stub,
helper: path.join(root, "helper.js"),
}));
const helperPath = path.join(__dirname, "..", "lib", "bootstrap-update-helper.js");
const env = { PATH: process.env.PATH, HOME: process.env.HOME, GC_STATE_DIR: stateDir };
const check = spawnSync(process.execPath, [helperPath, "--check", "--json", "--manifest", manifestFile], {
encoding: "utf8",
timeout: 20000,
env,
});
assert.strictEqual(check.status, 0, check.stderr);
assert.strictEqual(JSON.parse(check.stdout).status, "available");
const forced = spawnSync(process.execPath, [helperPath, "--background", "--force", "--manifest", manifestFile], {
encoding: "utf8",
timeout: 20000,
env,
});
const attempts = Number(fs.readFileSync(attemptsFile, "utf8"));
assert.ok(attempts >= 3, `the forced update must run the install (attempts: ${attempts})`);
const state = JSON.parse(fs.readFileSync(path.join(stateDir, "update-state.json"), "utf8"));
if (process.platform === "win32") {
assert.strictEqual(forced.status, 1, "the health check fails without a runnable gitcode.exe");
assert.match(state.summary.message, /gitcode\.exe ENOENT|health check/, "the install path must have run to the health check");
} else {
assert.strictEqual(forced.status, 0, forced.stderr);
assert.match(state.summary.message, /Updated GitCode CLI 0\.0\.1 -> 9\.9\.9/);
assert.strictEqual(state.summary.shown, false, "the update summary is queued for the next launch");
}
});
test("bootstrap stale-lock reclamation stays single-owner under a reclaim race", () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "gc-bootstrap-lock-race-"));
const stateDir = path.join(root, "state");
fs.mkdirSync(stateDir, { recursive: true });
const lockFile = path.join(stateDir, "update-state.json.lock");
const stale = new Date(Date.now() - 16 * 60 * 1000);
fs.writeFileSync(lockFile, "");
fs.utimesSync(lockFile, stale, stale);
const originalRenameSync = fs.renameSync;
try {
fs.renameSync = () => {
const error = new Error("ENOENT: no such file or directory");
error.code = "ENOENT";
throw error;
};
assert.strictEqual(acquireLock(lockFile), null, "the loser must not hold the lock");
} finally {
fs.renameSync = originalRenameSync;
}
const reclaimed = acquireLock(lockFile);
assert.notStrictEqual(reclaimed, null);
fs.closeSync(reclaimed);
fs.unlinkSync(lockFile);
});
test("bootstrap failure state writes respect the cross-process lock", () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "gc-bootstrap-lock-fail-"));
const stateDir = path.join(root, "state");
fs.mkdirSync(stateDir, { recursive: true });
const stateFile = path.join(stateDir, "update-state.json");
const original = JSON.stringify({ nextCheck: 123, noticeShown: true, summary: { message: "pending", shown: false } });
fs.writeFileSync(stateFile, original);
const lock = fs.openSync(`${stateFile}.lock`, "wx", 0o600);
const manifestFile = path.join(root, "install.json");
fs.writeFileSync(manifestFile, JSON.stringify({ distribution: "bogus" }));
try {
const result = spawnSync(process.execPath, [path.join(__dirname, "..", "lib", "bootstrap-update-helper.js"), "--background", "--manifest", manifestFile], {
encoding: "utf8",
timeout: 20000,
env: { PATH: process.env.PATH, HOME: process.env.HOME, GC_STATE_DIR: stateDir },
});
assert.strictEqual(result.status, 1, result.stderr);
assert.strictEqual(fs.readFileSync(stateFile, "utf8"), original, "state must stay untouched while another updater holds the lock");
const log = fs.readFileSync(path.join(stateDir, "update.log"), "utf8");
assert.match(log, /state locked; summary skipped/);
} finally {
fs.closeSync(lock);
fs.unlinkSync(`${stateFile}.lock`);
}
});
test("bootstrap failure summaries carry the real error instead of a dead-end notice", { timeout: 30000 }, () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "gc-bootstrap-bg-fail-"));
const stateDir = path.join(root, "state");
const { stub } = flakyNpmStub(root, Infinity);
const manifestFile = path.join(root, "install.json");
fs.writeFileSync(manifestFile, JSON.stringify({
distribution: "npm-bootstrap",
version: "0.0.1",
targetDir: root,
npm: stub,
helper: path.join(root, "helper.js"),
}));
const childEnv = { PATH: process.env.PATH, HOME: process.env.HOME, GC_STATE_DIR: stateDir };
for (const key of ["TEMP", "TMP", "SYSTEMROOT"]) {
if (process.env[key]) childEnv[key] = process.env[key];
}
const result = spawnSync(process.execPath, [path.join(__dirname, "..", "lib", "bootstrap-update-helper.js"), "--background", "--manifest", manifestFile], {
encoding: "utf8",
timeout: 20000,
env: childEnv,
});
assert.strictEqual(result.status, 1, result.stderr);
const state = JSON.parse(fs.readFileSync(path.join(stateDir, "update-state.json"), "utf8"));
assert.match(state.summary.message, /^Automatic update failed: /);
assert.match(state.summary.message, /ECONNRESET/);
assert.ok(!state.summary.message.includes("run gitcode update for details"), "dead-end notice must be gone");
assert.strictEqual(state.summary.shown, false);
const log = fs.readFileSync(path.join(stateDir, "update.log"), "utf8");
assert.match(log, /package=\S+ channel=npm-bootstrap status=error detail=.*ECONNRESET/);
});
test("bootstrap summarizeError collapses and bounds error text", () => {
assert.strictEqual(summarizeError(new Error("boom")), "boom");
assert.strictEqual(summarizeError(null), "unknown error");
assert.strictEqual(summarizeError(new Error(" \n\t ")), "unknown error");
assert.strictEqual(summarizeError({ code: "ECONNRESET" }), "ECONNRESET");
const bounded = summarizeError(new Error(`x${"😀".repeat(150)}`));
assert.ok(bounded.endsWith("..."));
const body = bounded.slice(0, -3);
const last = body.charCodeAt(body.length - 1);
assert.ok(!(last >= 0xd800 && last <= 0xdbff), "cut must not end on a lone high surrogate");
const long = new Error(`x${"a".repeat(400)}`);
assert.strictEqual(summarizeError(long).length, 203);
assert.ok(summarizeError(new Error("line1\nline2")).includes("line1 line2"));
});
test("bootstrap updater parseArgs accepts =value forms for all its flags", () => {
const manifestPath = path.resolve(os.tmpdir(), "gc-test-manifest.json");
assert.deepStrictEqual(parseArgs(["--check=true", "--json=1", `--manifest=${manifestPath}`, "--parent-pid=42"]), {
background: false, check: true, force: false, json: true, manifest: manifestPath, parentPid: 42,
});
assert.deepStrictEqual(parseArgs(["--force=false", `--manifest=${manifestPath}`]), {
background: false, check: false, force: false, json: false, manifest: manifestPath, parentPid: 0,
});
assert.deepStrictEqual(parseArgs(["--background=1", `--manifest=${manifestPath}`]), {
background: true, check: false, force: false, json: false, manifest: manifestPath, parentPid: 0,
});
assert.throws(() => parseArgs(["--check=banana"]), /unknown updater argument/);
});
test("a transient bootstrap failure over an existing pause keeps the remain-paused guidance", { timeout: 30000 }, () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "gc-bootstrap-sticky-"));
const stateDir = path.join(root, "state");
fs.mkdirSync(stateDir, { recursive: true });
fs.writeFileSync(path.join(stateDir, "update-state.json"), JSON.stringify({
permanentError: true,
lastErrorFingerprint: "old-fingerprint",
summary: { message: "Automatic update failed: earlier.", shown: true },
}));
const { stub } = flakyNpmStub(root, 99, "0.0.2");
const manifestFile = path.join(root, "install.json");
fs.writeFileSync(manifestFile, JSON.stringify({
distribution: "npm-bootstrap",
version: "0.0.1",
targetDir: root,
npm: stub,
helper: path.join(root, "helper.js"),
}));
const result = spawnSync(process.execPath, [path.join(__dirname, "..", "lib", "bootstrap-update-helper.js"), "--background", "--force", "--manifest", manifestFile], {
encoding: "utf8",
timeout: 20000,
env: { PATH: process.env.PATH, HOME: process.env.HOME, GC_STATE_DIR: stateDir },
});
assert.strictEqual(result.status, 1);
const state = JSON.parse(fs.readFileSync(path.join(stateDir, "update-state.json"), "utf8"));
assert.strictEqual(state.permanentError, true, "the pause is sticky");
assert.match(state.summary.message, /remain paused from an earlier unrepaired failure/);
});