refactor(cli): consolidate security-sensitive regression fixtures (#114411)

* refactor(cli): consolidate security-sensitive regression fixtures

* refactor(cli): refresh fixture cleanup against main
This commit is contained in:
Peter Steinberger
2026-08-02 01:19:59 -07:00
committed by GitHub
parent 76ec39556e
commit 6b625357fc
4 changed files with 504 additions and 1218 deletions
+61 -129
View File
@@ -592,6 +592,20 @@ describe("update-cli", () => {
});
};
const primeNpmChannelTag = (tag: string, version: string | null): void => {
vi.mocked(resolveNpmChannelTag).mockResolvedValue({ tag, version });
};
const primeServiceCommand = (
programArguments: Array<string | undefined>,
environment?: NodeJS.ProcessEnv,
): void => {
serviceReadCommand.mockResolvedValue({
programArguments,
...(environment === undefined ? {} : { environment }),
});
};
const expectUpdateCallChannel = (channel: string) => {
const call = vi.mocked(runGatewayUpdate).mock.calls[0]?.[0];
expect(call?.channel).toBe(channel);
@@ -845,9 +859,7 @@ describe("update-cli", () => {
const mockOwnedGitService = () => {
const serviceEntrypoint = path.join(process.cwd(), "dist", "index.js");
serviceReadCommand.mockResolvedValue({
programArguments: ["node", serviceEntrypoint, "gateway", "run"],
});
primeServiceCommand(["node", serviceEntrypoint, "gateway", "run"]);
pathExists.mockImplementation(
async (candidate: string) => candidate === path.join(process.cwd(), "package.json"),
);
@@ -886,10 +898,7 @@ describe("update-cli", () => {
readPackageVersion.mockResolvedValue("2.0.0");
mockPackageInstallStatus(tempDir);
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "0.0.1",
});
primeNpmChannelTag("latest", "0.0.1");
vi.mocked(runGatewayUpdate).mockResolvedValue({
status: "ok",
mode: "npm",
@@ -940,9 +949,7 @@ describe("update-cli", () => {
});
}
serviceLoaded.mockResolvedValue(true);
serviceReadCommand.mockResolvedValue({
programArguments: ["node", entrypoints[0], "gateway", "run"],
});
primeServiceCommand(["node", entrypoints[0], "gateway", "run"]);
return { root, entrypoints };
};
@@ -1337,10 +1344,7 @@ describe("update-cli", () => {
version: "2026.6.33",
packageSpec: "openclaw@2026.6.33",
});
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "9999.0.0",
});
primeNpmChannelTag("latest", "9999.0.0");
nodeVersionSatisfiesEngine.mockReturnValue(true);
vi.mocked(checkUpdateStatus).mockResolvedValue({
root: "/test/path",
@@ -1879,10 +1883,7 @@ describe("update-cli", () => {
}),
});
readPackageVersion.mockResolvedValue("2026.4.14");
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "2026.4.10",
});
primeNpmChannelTag("latest", "2026.4.10");
mockCurrentProcessFreshDoctor();
mockGatewayProbe("2026.4.10", "downgraded-gateway");
@@ -1908,7 +1909,7 @@ describe("update-cli", () => {
}),
});
readPackageVersion.mockResolvedValue("2026.4.14");
vi.mocked(resolveNpmChannelTag).mockResolvedValue({ tag: "latest", version: "2026.4.10" });
primeNpmChannelTag("latest", "2026.4.10");
mockCurrentProcessFreshDoctor();
await updateCommand({ yes: true, tag: "2026.4.10", restart: false });
@@ -1936,7 +1937,7 @@ describe("update-cli", () => {
readPackageVersion.mockImplementation(async (pkgRoot: string) =>
pkgRoot === downgradedRoot ? "2026.4.10" : "2026.4.14",
);
vi.mocked(resolveNpmChannelTag).mockResolvedValue({ tag: "latest", version: "2026.4.10" });
primeNpmChannelTag("latest", "2026.4.10");
delete process.env.OPENCLAW_COMPATIBILITY_HOST_VERSION;
let hostVersionDuringPluginUpdate: string | undefined = "unset";
@@ -3105,9 +3106,8 @@ describe("update-cli", () => {
it("refuses a package target that changes after the service stops", async () => {
mockPackageInstallStatus(process.cwd());
mockOwnedGitService();
serviceReadCommand.mockResolvedValue({
programArguments: ["node", path.join(process.cwd(), "dist", "index.js"), "gateway", "run"],
environment: { OPENCLAW_STATE_DIR: "/managed/state" },
primeServiceCommand(["node", path.join(process.cwd(), "dist", "index.js"), "gateway", "run"], {
OPENCLAW_STATE_DIR: "/managed/state",
});
serviceLoaded.mockResolvedValue(true);
vi.mocked(fetchNpmPackageTargetStatus).mockResolvedValue({
@@ -3345,10 +3345,7 @@ describe("update-cli", () => {
...baseSnapshot,
config: { update: { channel: "beta" } } as OpenClawConfig,
});
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "1.2.3-1",
});
primeNpmChannelTag("latest", "1.2.3-1");
await updateCommand({});
expectPackageInstallSpec("openclaw@1.2.3-1");
@@ -3528,10 +3525,7 @@ describe("update-cli", () => {
const tempDir = createCaseDir("openclaw-update");
mockPackageInstallStatus(tempDir);
readPackageVersion.mockResolvedValue("2026.4.22");
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "2026.4.22",
});
primeNpmChannelTag("latest", "2026.4.22");
await updateCommand({ yes: true });
@@ -3551,10 +3545,7 @@ describe("update-cli", () => {
setTty(false);
mockPackageInstallStatus(tempDir);
readPackageVersion.mockResolvedValue("2026.4.22");
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: null,
});
primeNpmChannelTag("latest", null);
mockCurrentProcessFreshDoctor();
await updateCommand({});
@@ -3633,12 +3624,9 @@ describe("update-cli", () => {
it("refuses package updates from inherited gateway service env when --no-restart leaves the gateway running", async () => {
mockPackageInstallStatus(createCaseDir("openclaw-update"));
serviceReadCommand.mockResolvedValue({
programArguments: ["openclaw", "gateway", "run"],
environment: {
OPENCLAW_SERVICE_MARKER: "openclaw",
OPENCLAW_SERVICE_KIND: "gateway",
},
primeServiceCommand(["openclaw", "gateway", "run"], {
OPENCLAW_SERVICE_MARKER: "openclaw",
OPENCLAW_SERVICE_KIND: "gateway",
});
serviceLoaded.mockResolvedValue(true);
@@ -3670,12 +3658,9 @@ describe("update-cli", () => {
"refuses package updates from inherited gateway service env when $name",
async ({ setupRuntime }) => {
mockPackageInstallStatus(createCaseDir("openclaw-update"));
serviceReadCommand.mockResolvedValue({
programArguments: ["openclaw", "gateway", "run"],
environment: {
OPENCLAW_SERVICE_MARKER: "openclaw",
OPENCLAW_SERVICE_KIND: "gateway",
},
primeServiceCommand(["openclaw", "gateway", "run"], {
OPENCLAW_SERVICE_MARKER: "openclaw",
OPENCLAW_SERVICE_KIND: "gateway",
});
setupRuntime();
@@ -3758,10 +3743,7 @@ describe("update-cli", () => {
it("blocks package updates when the target requires a newer Node runtime", async () => {
mockPackageInstallStatus(createCaseDir("openclaw-update"));
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "2026.3.23-2",
});
primeNpmChannelTag("latest", "2026.3.23-2");
vi.mocked(fetchNpmPackageTargetStatus).mockResolvedValue({
target: "latest",
version: "2026.3.23-2",
@@ -3894,10 +3876,7 @@ describe("update-cli", () => {
const nodeModules = path.join(prefix, "lib", "node_modules");
const { pkgRoot } = await setupInstalledPackageAtNodeModules(nodeModules, "2026.4.20");
readPackageVersion.mockResolvedValue("2026.4.20");
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "2026.4.25",
});
primeNpmChannelTag("latest", "2026.4.25");
vi.mocked(runCommandWithTimeout).mockImplementation(async (argv) => {
if (Array.isArray(argv) && argv[0] === "npm" && argv[1] === "root" && argv[2] === "-g") {
return commandResult({ stdout: `${nodeModules}\n` });
@@ -3942,18 +3921,13 @@ describe("update-cli", () => {
it("runs old package doctors without fix mode when service ownership is unknown", async () => {
const tempDir = await createTrackedTempDir("openclaw-update-package-");
const { nodeModules, pkgRoot, entryPath } = await setupInstalledPackageRoot(tempDir);
serviceReadCommand.mockResolvedValue({
programArguments: ["openclaw-wrapper", "gateway", "run"],
});
primeServiceCommand(["openclaw-wrapper", "gateway", "run"]);
serviceLoaded.mockResolvedValue(true);
serviceReadRuntime.mockResolvedValue({ status: "stopped", state: "stopped" });
readPackageVersion.mockImplementation(async (packageRoot: string) =>
packageRoot === pkgRoot ? "2026.4.21" : "1.0.0",
);
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "2026.4.21",
});
primeNpmChannelTag("latest", "2026.4.21");
mockFileBackedPathExists();
mockNpmGlobalRoot(nodeModules);
@@ -3991,10 +3965,7 @@ describe("update-cli", () => {
it("continues package post-core work for explicit post-update doctor advisories", async () => {
const tempDir = await createTrackedTempDir("openclaw-update-package-doctor-warning-");
const { nodeModules, entryPath } = await setupInstalledPackageRoot(tempDir);
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "2026.4.21",
});
primeNpmChannelTag("latest", "2026.4.21");
mockFileBackedPathExists();
vi.mocked(runCommandWithTimeout).mockImplementation(async (argv, options) => {
if (Array.isArray(argv) && argv[0] === "npm" && argv[1] === "root" && argv[2] === "-g") {
@@ -4060,10 +4031,7 @@ describe("update-cli", () => {
it("fails package updates when the post-update doctor is killed after verification", async () => {
const tempDir = await createTrackedTempDir("openclaw-update-package-doctor-timeout-");
const { nodeModules, entryPath } = await setupInstalledPackageRoot(tempDir);
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "2026.4.21",
});
primeNpmChannelTag("latest", "2026.4.21");
mockFileBackedPathExists();
vi.mocked(runCommandWithTimeout).mockImplementation(async (argv) => {
if (Array.isArray(argv) && argv[0] === "npm" && argv[1] === "root" && argv[2] === "-g") {
@@ -4103,10 +4071,7 @@ describe("update-cli", () => {
const { nodeModules, pkgRoot, entryPath } = await setupInstalledPackageAtNodeModules(
path.join(tempDir, "lib", "node_modules"),
);
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "2026.5.14",
});
primeNpmChannelTag("latest", "2026.5.14");
mockFileBackedPathExists();
vi.mocked(runCommandWithTimeout).mockImplementation(async (argv) => {
if (!Array.isArray(argv)) {
@@ -4351,12 +4316,9 @@ describe("update-cli", () => {
it("preserves both the update and Scheduled Task recovery failures", async () => {
const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
mockPackageInstallStatus(createCaseDir("openclaw-update-recovery-failure"));
serviceReadCommand.mockResolvedValue({
programArguments: ["openclaw", "gateway", "run"],
environment: {
OPENCLAW_SERVICE_MARKER: "openclaw",
OPENCLAW_SERVICE_KIND: "gateway",
},
primeServiceCommand(["openclaw", "gateway", "run"], {
OPENCLAW_SERVICE_MARKER: "openclaw",
OPENCLAW_SERVICE_KIND: "gateway",
});
serviceReadRuntime.mockResolvedValue({ status: "stopped", state: "stopped" });
suspendScheduledTaskAutoStartForUpdate.mockResolvedValue(true);
@@ -4399,12 +4361,9 @@ describe("update-cli", () => {
);
resumeScheduledTaskAutoStartAfterUpdate.mockResolvedValue(true);
mockPackageInstallStatus(createCaseDir("openclaw-update-suspension-signal"));
serviceReadCommand.mockResolvedValue({
programArguments: ["openclaw", "gateway", "run"],
environment: {
OPENCLAW_SERVICE_MARKER: "openclaw",
OPENCLAW_SERVICE_KIND: "gateway",
},
primeServiceCommand(["openclaw", "gateway", "run"], {
OPENCLAW_SERVICE_MARKER: "openclaw",
OPENCLAW_SERVICE_KIND: "gateway",
});
serviceReadRuntime.mockResolvedValue({ status: "stopped", state: "stopped" });
@@ -4439,12 +4398,9 @@ describe("update-cli", () => {
async (runtimeStatus) => {
const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
mockPackageInstallStatus(createCaseDir("openclaw-update-stopped-task"));
serviceReadCommand.mockResolvedValue({
programArguments: ["openclaw", "gateway", "run"],
environment: {
OPENCLAW_SERVICE_MARKER: "openclaw",
OPENCLAW_SERVICE_KIND: "gateway",
},
primeServiceCommand(["openclaw", "gateway", "run"], {
OPENCLAW_SERVICE_MARKER: "openclaw",
OPENCLAW_SERVICE_KIND: "gateway",
});
serviceReadRuntime.mockResolvedValue(
runtimeStatus === "running"
@@ -5025,9 +4981,7 @@ describe("update-cli", () => {
"utf-8",
);
mockPackageInstallStatus(shellRoot);
serviceReadCommand.mockResolvedValue({
programArguments: [serviceNode, path.join(serviceRoot, "dist", "index.js"), "gateway"],
});
primeServiceCommand([serviceNode, path.join(serviceRoot, "dist", "index.js"), "gateway"]);
await updateCommand({ dryRun: true });
@@ -5053,10 +5007,8 @@ describe("update-cli", () => {
"utf-8",
);
mockPackageInstallStatus(shellRoot);
serviceReadCommand.mockResolvedValue({
programArguments: [serviceNode, path.join(serviceRoot, "dist", "index.js"), "gateway"],
});
vi.mocked(resolveNpmChannelTag).mockResolvedValue({ tag: "latest", version: "2026.5.20" });
primeServiceCommand([serviceNode, path.join(serviceRoot, "dist", "index.js"), "gateway"]);
primeNpmChannelTag("latest", "2026.5.20");
vi.mocked(fetchNpmPackageTargetStatus).mockResolvedValue({
target: "latest",
version: "2026.5.20",
@@ -5101,14 +5053,9 @@ describe("update-cli", () => {
await fs.writeFile(entrypoint, "", "utf-8");
await writePackageDistInventory(serviceRoot);
mockPackageInstallStatus(shellRoot);
serviceReadCommand.mockResolvedValue({
programArguments: [serviceNode, entrypoint, "gateway"],
});
primeServiceCommand([serviceNode, entrypoint, "gateway"]);
serviceLoaded.mockResolvedValue(true);
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "2026.5.20",
});
primeNpmChannelTag("latest", "2026.5.20");
mockFileBackedPathExists();
vi.mocked(runCommandWithTimeout).mockImplementation(async (argv) => {
if (Array.isArray(argv) && argv[0] === serviceNode && argv[1] === "--version") {
@@ -5157,9 +5104,7 @@ describe("update-cli", () => {
const serviceNode = "/opt/other-node/bin/node";
const entrypoint = path.join(root, "dist", "index.js");
mockPackageInstallStatus(root);
serviceReadCommand.mockResolvedValue({
programArguments: [serviceNode, entrypoint, "gateway"],
});
primeServiceCommand([serviceNode, entrypoint, "gateway"]);
await updateCommand({ dryRun: true });
@@ -5195,11 +5140,9 @@ describe("update-cli", () => {
await writePackageDistInventory(root);
// Same package root for both shell and service.
mockPackageInstallStatus(root);
serviceReadCommand.mockResolvedValue({
programArguments: [serviceNode, entrypoint, "gateway"],
});
primeServiceCommand([serviceNode, entrypoint, "gateway"]);
serviceLoaded.mockResolvedValue(true);
vi.mocked(resolveNpmChannelTag).mockResolvedValue({ tag: "latest", version: "2026.7.1" });
primeNpmChannelTag("latest", "2026.7.1");
vi.mocked(fetchNpmPackageTargetStatus).mockResolvedValue({
target: "latest",
version: "2026.7.1",
@@ -5270,14 +5213,9 @@ describe("update-cli", () => {
await fs.writeFile(entrypoint, "", "utf-8");
await writePackageDistInventory(root);
mockPackageInstallStatus(root);
serviceReadCommand.mockResolvedValue({
programArguments: [serviceNode, entrypoint, "gateway"],
});
primeServiceCommand([serviceNode, entrypoint, "gateway"]);
serviceLoaded.mockResolvedValue(true);
vi.mocked(resolveNpmChannelTag).mockResolvedValue({
tag: "latest",
version: "2026.5.20",
});
primeNpmChannelTag("latest", "2026.5.20");
mockFileBackedPathExists();
// The PATH npm returns a DIFFERENT global root (simulates Node-B's npm).
const nodeBGlobalRoot = path.join(
@@ -6247,9 +6185,7 @@ describe("update-cli", () => {
it("tries the updated install restart when package service refresh fails", async () => {
const { updatedRoot, updatedEntrypoint } = setupNpmUpdatedRootRefresh();
serviceLoaded.mockResolvedValue(true);
serviceReadCommand.mockResolvedValue({
programArguments: ["node", updatedEntrypoint, "gateway", "run"],
});
primeServiceCommand(["node", updatedEntrypoint, "gateway", "run"]);
mockGatewayInstallFailure(updatedEntrypoint);
mockGatewayProbe("2026.4.24", "updated-gateway");
@@ -6289,9 +6225,7 @@ describe("update-cli", () => {
candidate === updatedEntrypoint || candidate === updatedPackageJson,
);
serviceLoaded.mockResolvedValue(true);
serviceReadCommand.mockResolvedValue({
programArguments: ["node", updatedEntrypoint, "gateway", "run"],
});
primeServiceCommand(["node", updatedEntrypoint, "gateway", "run"]);
mockGatewayInstallFailure(updatedEntrypoint);
mockGatewayProbe("2026.4.24", "matching-old-gateway");
@@ -6340,9 +6274,7 @@ describe("update-cli", () => {
[oldEntrypoint, updatedEntrypoint, oldPackageJson, updatedPackageJson].includes(candidate),
);
serviceLoaded.mockResolvedValue(true);
serviceReadCommand.mockResolvedValue({
programArguments: ["node", oldEntrypoint, "gateway", "run"],
});
primeServiceCommand(["node", oldEntrypoint, "gateway", "run"]);
mockGatewayInstallFailure(updatedEntrypoint);
mockGatewayProbe("2026.4.24", "matching-old-service");