Skip to content

Commit 71ebede

Browse files
committed
test: tighten update cli post core assertions
1 parent 1f99cdd commit 71ebede

1 file changed

Lines changed: 50 additions & 66 deletions

File tree

src/cli/update-cli.test.ts

Lines changed: 50 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,23 @@ describe("update-cli", () => {
380380
return calls[index];
381381
};
382382

383+
const syncPluginCall = (index = 0) => {
384+
const calls = syncPluginsForUpdateChannel.mock.calls as unknown as Array<
385+
[{ channel?: string; config?: OpenClawConfig }]
386+
>;
387+
return calls[index]?.[0];
388+
};
389+
390+
const npmPluginUpdateCall = (index = 0) => {
391+
const calls = updateNpmInstalledPlugins.mock.calls as unknown as Array<
392+
[{ timeoutMs?: number }]
393+
>;
394+
return calls[index]?.[0];
395+
};
396+
397+
const pluginWarning = (result?: UpdateRunResult) => result?.postUpdate?.plugins?.warnings?.[0];
398+
const pluginOutcome = (result?: UpdateRunResult) => result?.postUpdate?.plugins?.npm.outcomes[0];
399+
383400
const expectPackageInstallSpec = (spec: string) => {
384401
expect(runGatewayUpdate).not.toHaveBeenCalled();
385402
const call = (
@@ -799,18 +816,13 @@ describe("update-cli", () => {
799816

800817
await updateCommand({ channel: "dev", yes: true, restart: false });
801818

802-
expect(spawn).toHaveBeenCalledWith(
803-
expect.stringMatching(/node/),
804-
[entrypoints[0], "update", "--no-restart", "--yes"],
805-
expect.objectContaining({
806-
stdio: "inherit",
807-
env: expect.objectContaining({
808-
OPENCLAW_UPDATE_POST_CORE: "1",
809-
OPENCLAW_UPDATE_POST_CORE_CHANNEL: "dev",
810-
OPENCLAW_UPDATE_POST_CORE_REQUESTED_CHANNEL: "dev",
811-
}),
812-
}),
813-
);
819+
const call = spawnCall();
820+
expect(call?.[0]).toMatch(/node/);
821+
expect(call?.[1]).toEqual([entrypoints[0], "update", "--no-restart", "--yes"]);
822+
expect(call?.[2]?.stdio).toBe("inherit");
823+
expect(call?.[2]?.env?.OPENCLAW_UPDATE_POST_CORE).toBe("1");
824+
expect(call?.[2]?.env?.OPENCLAW_UPDATE_POST_CORE_CHANNEL).toBe("dev");
825+
expect(call?.[2]?.env?.OPENCLAW_UPDATE_POST_CORE_REQUESTED_CHANNEL).toBe("dev");
814826
expect(replaceConfigFile).not.toHaveBeenCalled();
815827
expect(syncPluginsForUpdateChannel).not.toHaveBeenCalled();
816828
expect(updateNpmInstalledPlugins).not.toHaveBeenCalled();
@@ -891,10 +903,10 @@ describe("update-cli", () => {
891903
);
892904

893905
expect(runGatewayUpdate).not.toHaveBeenCalled();
894-
expect(runCommandWithTimeout).not.toHaveBeenCalledWith(
895-
["npm", "i", "-g", expect.any(String)],
896-
expect.anything(),
897-
);
906+
const installCall = (
907+
vi.mocked(runCommandWithTimeout).mock.calls as unknown as Array<[string[], unknown]>
908+
).find(([argv]) => argv[0] === "npm" && argv[1] === "i" && argv[2] === "-g");
909+
expect(installCall).toBeUndefined();
898910
expect(defaultRuntime.exit).toHaveBeenCalledWith(0);
899911
expect(syncPluginsForUpdateChannel).toHaveBeenCalledTimes(1);
900912
expect(updateNpmInstalledPlugins).toHaveBeenCalledTimes(1);
@@ -999,14 +1011,8 @@ describe("update-cli", () => {
9991011
},
10001012
baseHash: "stable-hash",
10011013
});
1002-
expect(syncPluginsForUpdateChannel).toHaveBeenCalledWith(
1003-
expect.objectContaining({
1004-
channel: "dev",
1005-
config: expect.objectContaining({
1006-
update: expect.objectContaining({ channel: "dev" }),
1007-
}),
1008-
}),
1009-
);
1014+
expect(syncPluginCall()?.channel).toBe("dev");
1015+
expect(syncPluginCall()?.config?.update?.channel).toBe("dev");
10101016
});
10111017

10121018
it("post-core resume mode retries update channel persistence after config hash drift", async () => {
@@ -1071,14 +1077,8 @@ describe("update-cli", () => {
10711077
},
10721078
baseHash: "newer-hash",
10731079
});
1074-
expect(syncPluginsForUpdateChannel).toHaveBeenCalledWith(
1075-
expect.objectContaining({
1076-
config: expect.objectContaining({
1077-
meta: expect.objectContaining({ lastTouchedVersion: "2026.4.30" }),
1078-
update: expect.objectContaining({ channel: "dev" }),
1079-
}),
1080-
}),
1081-
);
1080+
expect(syncPluginCall()?.config?.meta?.lastTouchedVersion).toBe("2026.4.30");
1081+
expect(syncPluginCall()?.config?.update?.channel).toBe("dev");
10821082
});
10831083

10841084
it("passes the update timeout budget into post-core plugin updates", async () => {
@@ -1092,9 +1092,7 @@ describe("update-cli", () => {
10921092
},
10931093
);
10941094

1095-
expect(updateNpmInstalledPlugins).toHaveBeenCalledWith(
1096-
expect.objectContaining({ timeoutMs: 1_800_000 }),
1097-
);
1095+
expect(npmPluginUpdateCall()?.timeoutMs).toBe(1_800_000);
10981096
});
10991097

11001098
it("uses a fail-closed integrity policy for post-core plugin updates", async () => {
@@ -1200,16 +1198,12 @@ describe("update-cli", () => {
12001198
},
12011199
]);
12021200
expect(jsonOutput?.postUpdate?.plugins?.status).toBe("warning");
1203-
expect(jsonOutput?.postUpdate?.plugins?.warnings?.[0]).toMatchObject({
1204-
pluginId: "demo",
1205-
guidance: [
1206-
"Run openclaw doctor --fix to attempt automatic repair.",
1207-
"Run openclaw plugins inspect demo --runtime --json for details.",
1208-
],
1209-
});
1210-
expect(jsonOutput?.postUpdate?.plugins?.warnings?.[0]?.reason).toContain(
1211-
"npm package integrity drift",
1212-
);
1201+
expect(pluginWarning(jsonOutput)?.pluginId).toBe("demo");
1202+
expect(pluginWarning(jsonOutput)?.guidance).toEqual([
1203+
"Run openclaw doctor --fix to attempt automatic repair.",
1204+
"Run openclaw plugins inspect demo --runtime --json for details.",
1205+
]);
1206+
expect(pluginWarning(jsonOutput)?.reason).toContain("npm package integrity drift");
12131207
expect(jsonOutput?.postUpdate?.plugins?.npm.outcomes[0]?.status).toBe("error");
12141208
expect(jsonOutput?.postUpdate?.plugins?.npm.outcomes[0]?.message).toContain(
12151209
"Run openclaw doctor --fix to attempt automatic repair.",
@@ -1268,16 +1262,10 @@ describe("update-cli", () => {
12681262
| undefined;
12691263
expect(jsonOutput?.status).toBe("ok");
12701264
expect(jsonOutput?.postUpdate?.plugins?.status).toBe("warning");
1271-
expect(jsonOutput?.postUpdate?.plugins?.warnings?.[0]).toMatchObject({
1272-
pluginId: "demo",
1273-
});
1274-
expect(jsonOutput?.postUpdate?.plugins?.warnings?.[0]?.reason).toContain(
1275-
"package.json is missing",
1276-
);
1277-
expect(jsonOutput?.postUpdate?.plugins?.npm.outcomes[0]).toMatchObject({
1278-
pluginId: "demo",
1279-
status: "error",
1280-
});
1265+
expect(pluginWarning(jsonOutput)?.pluginId).toBe("demo");
1266+
expect(pluginWarning(jsonOutput)?.reason).toContain("package.json is missing");
1267+
expect(pluginOutcome(jsonOutput)?.pluginId).toBe("demo");
1268+
expect(pluginOutcome(jsonOutput)?.status).toBe("error");
12811269
});
12821270

12831271
it("prints non-fatal plugin warnings in human update output", async () => {
@@ -1335,17 +1323,13 @@ describe("update-cli", () => {
13351323
| UpdateRunResult
13361324
| undefined;
13371325
expect(jsonOutput?.postUpdate?.plugins?.status).toBe("warning");
1338-
expect(jsonOutput?.postUpdate?.plugins?.warnings?.[0]).toMatchObject({
1339-
pluginId: "demo",
1340-
guidance: [
1341-
"Run openclaw doctor --fix to attempt automatic repair.",
1342-
"Run openclaw plugins inspect demo --runtime --json for details.",
1343-
],
1344-
});
1345-
expect(jsonOutput?.postUpdate?.plugins?.npm.outcomes[0]).toMatchObject({
1346-
pluginId: "demo",
1347-
status: "skipped",
1348-
});
1326+
expect(pluginWarning(jsonOutput)?.pluginId).toBe("demo");
1327+
expect(pluginWarning(jsonOutput)?.guidance).toEqual([
1328+
"Run openclaw doctor --fix to attempt automatic repair.",
1329+
"Run openclaw plugins inspect demo --runtime --json for details.",
1330+
]);
1331+
expect(pluginOutcome(jsonOutput)?.pluginId).toBe("demo");
1332+
expect(pluginOutcome(jsonOutput)?.status).toBe("skipped");
13491333
});
13501334

13511335
it("fails unexpected post-core plugin sync exceptions", async () => {

0 commit comments

Comments
 (0)