



















@@ -1,9 +1,49 @@
1-import { readFileSync } from "node:fs";
1+import { execFileSync, spawnSync } from "node:child_process";
2+import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3+import { tmpdir } from "node:os";
4+import path from "node:path";
25import { describe, expect, it } from "vitest";
3647const PLUGIN_UPDATE_DOCKER_SCRIPT = "scripts/e2e/plugin-update-unchanged-docker.sh";
58const PLUGIN_UPDATE_SCENARIO_SCRIPT = "scripts/e2e/lib/plugin-update/unchanged-scenario.sh";
69const PLUGIN_UPDATE_PROBE_SCRIPT = "scripts/e2e/lib/plugin-update/probe.mjs";
10+const CORRUPT_PLUGIN_ID = "demo-corrupt-plugin";
11+12+function runProbe(command: string, payload: unknown): void {
13+const root = mkdtempSync(path.join(tmpdir(), "openclaw-plugin-update-probe-"));
14+const payloadPath = path.join(root, "payload.json");
15+try {
16+writeFileSync(payloadPath, `${JSON.stringify(payload, null, 2)}\n`);
17+execFileSync("node", [PLUGIN_UPDATE_PROBE_SCRIPT, command, payloadPath, CORRUPT_PLUGIN_ID], {
18+encoding: "utf8",
19+stdio: "pipe",
20+});
21+} finally {
22+rmSync(root, { recursive: true, force: true });
23+}
24+}
25+26+function runProbeStatus(
27+command: string,
28+payload: unknown,
29+): { status: number | null; stderr: string } {
30+const root = mkdtempSync(path.join(tmpdir(), "openclaw-plugin-update-probe-"));
31+const payloadPath = path.join(root, "payload.json");
32+try {
33+writeFileSync(payloadPath, `${JSON.stringify(payload, null, 2)}\n`);
34+const result = spawnSync(
35+"node",
36+[PLUGIN_UPDATE_PROBE_SCRIPT, command, payloadPath, CORRUPT_PLUGIN_ID],
37+{
38+encoding: "utf8",
39+stdio: "pipe",
40+},
41+);
42+return { status: result.status, stderr: result.stderr };
43+} finally {
44+rmSync(root, { recursive: true, force: true });
45+}
46+}
747848describe("plugin update unchanged Docker E2E", () => {
949it("seeds current plugin install ledger state before checking config stability", () => {
@@ -31,4 +71,41 @@ describe("plugin update unchanged Docker E2E", () => {
3171expect(script).toContain('"--- plugin update output ---"');
3272expect(script).toContain('"--- local registry output ---"');
3373});
74+75+it("requires disabled-after-failure corrupt plugin updates to stay warnings", () => {
76+const disabledAfterFailure = {
77+status: "ok",
78+npm: {
79+outcomes: [
80+{
81+pluginId: CORRUPT_PLUGIN_ID,
82+status: "skipped",
83+message:
84+`Disabled "${CORRUPT_PLUGIN_ID}" after plugin update failure; OpenClaw will continue without it. Failed to update ${CORRUPT_PLUGIN_ID}: registry timeout`,
85+},
86+],
87+},
88+};
89+90+const acceptedOkResult = runProbeStatus("assert-corrupt-plugin-result", disabledAfterFailure);
91+92+expect(acceptedOkResult.status).not.toBe(0);
93+expect(acceptedOkResult.stderr).toContain("expected clean or repaired corrupt plugin state");
94+expect(() =>
95+runProbe("assert-corrupt-plugin-result", {
96+ ...disabledAfterFailure,
97+status: "warning",
98+warnings: [
99+{
100+pluginId: CORRUPT_PLUGIN_ID,
101+message:
102+`Plugin "${CORRUPT_PLUGIN_ID}" could not be processed after the core update: ` +
103+disabledAfterFailure.npm.outcomes[0].message +
104+" Run openclaw doctor --fix to attempt automatic repair. " +
105+`Run openclaw plugins inspect ${CORRUPT_PLUGIN_ID} --runtime --json for details.`,
106+},
107+],
108+}),
109+).not.toThrow();
110+});
34111});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。