mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-28 03:00:34 +00:00
fix(e2e): reject corrupt plugin update false greens
This commit is contained in:
@@ -132,9 +132,6 @@ function assertCorruptPluginResult(pluginJsonPath, pluginId) {
|
||||
function assertCorruptPluginTolerated(plugins, pluginId) {
|
||||
const evidence = collectPluginEvidence(plugins, pluginId);
|
||||
if (plugins.status === "ok") {
|
||||
if (isCorruptPluginDisabledAfterUpdate(evidence, pluginId)) {
|
||||
return;
|
||||
}
|
||||
assertCorruptPluginCleanOrRepaired(evidence);
|
||||
return;
|
||||
}
|
||||
@@ -174,9 +171,10 @@ function assertCorruptPluginCleanOrRepaired(evidence) {
|
||||
function assertCorruptPluginDetails(plugins, pluginId) {
|
||||
const evidence = collectPluginEvidence(plugins, pluginId);
|
||||
const outcome = evidence.outcome;
|
||||
const disabledAfterFailure = isCorruptPluginDisabledAfterUpdate(evidence, pluginId);
|
||||
if (
|
||||
!outcome ||
|
||||
(outcome.status !== "error" && !isCorruptPluginDisabledAfterUpdate(evidence, pluginId))
|
||||
(outcome.status !== "error" && !disabledAfterFailure)
|
||||
) {
|
||||
throw new Error(
|
||||
`expected error or disabled-after-failure outcome for ${pluginId}, got ${JSON.stringify({
|
||||
@@ -187,21 +185,28 @@ function assertCorruptPluginDetails(plugins, pluginId) {
|
||||
})}`,
|
||||
);
|
||||
}
|
||||
if (isCorruptPluginDisabledAfterUpdate(evidence, pluginId)) {
|
||||
return;
|
||||
}
|
||||
const warning = evidence.warning;
|
||||
if (!warning) {
|
||||
throw new Error(
|
||||
`expected warning for ${pluginId}, got ${JSON.stringify(plugins.warnings ?? [])}`,
|
||||
);
|
||||
}
|
||||
const text = JSON.stringify({ outcome, warning });
|
||||
for (const expected of [
|
||||
"package.json is missing",
|
||||
"Run openclaw doctor --fix to attempt automatic repair.",
|
||||
`Run openclaw plugins inspect ${pluginId} --runtime --json for details.`,
|
||||
]) {
|
||||
const text = [outcome.message, warning.reason, warning.message, ...(warning.guidance ?? [])]
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
const expectedFragments = disabledAfterFailure
|
||||
? [
|
||||
`Disabled "${pluginId}" after plugin update failure`,
|
||||
"OpenClaw will continue without it",
|
||||
"Run openclaw doctor --fix to attempt automatic repair.",
|
||||
`Run openclaw plugins inspect ${pluginId} --runtime --json for details.`,
|
||||
]
|
||||
: [
|
||||
"package.json is missing",
|
||||
"Run openclaw doctor --fix to attempt automatic repair.",
|
||||
`Run openclaw plugins inspect ${pluginId} --runtime --json for details.`,
|
||||
];
|
||||
for (const expected of expectedFragments) {
|
||||
if (!text.includes(expected)) {
|
||||
throw new Error(`expected update output to include ${expected}: ${text}`);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,49 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { execFileSync, spawnSync } from "node:child_process";
|
||||
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const PLUGIN_UPDATE_DOCKER_SCRIPT = "scripts/e2e/plugin-update-unchanged-docker.sh";
|
||||
const PLUGIN_UPDATE_SCENARIO_SCRIPT = "scripts/e2e/lib/plugin-update/unchanged-scenario.sh";
|
||||
const PLUGIN_UPDATE_PROBE_SCRIPT = "scripts/e2e/lib/plugin-update/probe.mjs";
|
||||
const CORRUPT_PLUGIN_ID = "demo-corrupt-plugin";
|
||||
|
||||
function runProbe(command: string, payload: unknown): void {
|
||||
const root = mkdtempSync(path.join(tmpdir(), "openclaw-plugin-update-probe-"));
|
||||
const payloadPath = path.join(root, "payload.json");
|
||||
try {
|
||||
writeFileSync(payloadPath, `${JSON.stringify(payload, null, 2)}\n`);
|
||||
execFileSync("node", [PLUGIN_UPDATE_PROBE_SCRIPT, command, payloadPath, CORRUPT_PLUGIN_ID], {
|
||||
encoding: "utf8",
|
||||
stdio: "pipe",
|
||||
});
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
function runProbeStatus(
|
||||
command: string,
|
||||
payload: unknown,
|
||||
): { status: number | null; stderr: string } {
|
||||
const root = mkdtempSync(path.join(tmpdir(), "openclaw-plugin-update-probe-"));
|
||||
const payloadPath = path.join(root, "payload.json");
|
||||
try {
|
||||
writeFileSync(payloadPath, `${JSON.stringify(payload, null, 2)}\n`);
|
||||
const result = spawnSync(
|
||||
"node",
|
||||
[PLUGIN_UPDATE_PROBE_SCRIPT, command, payloadPath, CORRUPT_PLUGIN_ID],
|
||||
{
|
||||
encoding: "utf8",
|
||||
stdio: "pipe",
|
||||
},
|
||||
);
|
||||
return { status: result.status, stderr: result.stderr };
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
describe("plugin update unchanged Docker E2E", () => {
|
||||
it("seeds current plugin install ledger state before checking config stability", () => {
|
||||
@@ -31,4 +71,41 @@ describe("plugin update unchanged Docker E2E", () => {
|
||||
expect(script).toContain('"--- plugin update output ---"');
|
||||
expect(script).toContain('"--- local registry output ---"');
|
||||
});
|
||||
|
||||
it("requires disabled-after-failure corrupt plugin updates to stay warnings", () => {
|
||||
const disabledAfterFailure = {
|
||||
status: "ok",
|
||||
npm: {
|
||||
outcomes: [
|
||||
{
|
||||
pluginId: CORRUPT_PLUGIN_ID,
|
||||
status: "skipped",
|
||||
message:
|
||||
`Disabled "${CORRUPT_PLUGIN_ID}" after plugin update failure; OpenClaw will continue without it. Failed to update ${CORRUPT_PLUGIN_ID}: registry timeout`,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const acceptedOkResult = runProbeStatus("assert-corrupt-plugin-result", disabledAfterFailure);
|
||||
|
||||
expect(acceptedOkResult.status).not.toBe(0);
|
||||
expect(acceptedOkResult.stderr).toContain("expected clean or repaired corrupt plugin state");
|
||||
expect(() =>
|
||||
runProbe("assert-corrupt-plugin-result", {
|
||||
...disabledAfterFailure,
|
||||
status: "warning",
|
||||
warnings: [
|
||||
{
|
||||
pluginId: CORRUPT_PLUGIN_ID,
|
||||
message:
|
||||
`Plugin "${CORRUPT_PLUGIN_ID}" could not be processed after the core update: ` +
|
||||
disabledAfterFailure.npm.outcomes[0].message +
|
||||
" Run openclaw doctor --fix to attempt automatic repair. " +
|
||||
`Run openclaw plugins inspect ${CORRUPT_PLUGIN_ID} --runtime --json for details.`,
|
||||
},
|
||||
],
|
||||
}),
|
||||
).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user