fix(e2e): bound plugin update logs

This commit is contained in:
Vincent Koc
2026-06-07 10:26:50 +02:00
parent 248dfb22ec
commit 9fb8d87f91
3 changed files with 110 additions and 10 deletions
+44 -5
View File
@@ -10,6 +10,9 @@ import {
} from "../plugin-index-sqlite.mjs";
const home = os.homedir();
const OUTPUT_TAIL_BYTES = 64 * 1024;
const OUTPUT_TAIL_LINES = 120;
const OUTPUT_SCAN_WINDOW_BYTES = 8 * 1024;
const readJson = (file) => {
try {
@@ -106,15 +109,51 @@ function assertSnapshot(beforePath) {
}
}
function assertOutput(logPath) {
const output = fs.readFileSync(logPath, "utf8");
const failure = output.includes("Downloading @example/lossless-claw")
function appendBufferTail(tail, chunk, maxBytes) {
if (chunk.length >= maxBytes) {
return chunk.subarray(chunk.length - maxBytes);
}
if (tail.length + chunk.length <= maxBytes) {
return Buffer.concat([tail, chunk]);
}
return Buffer.concat([tail, chunk]).subarray(tail.length + chunk.length - maxBytes);
}
async function readOutputEvidence(logPath) {
let outputTail = Buffer.alloc(0);
let scanWindow = "";
let sawDownload = false;
let sawUpToDate = false;
for await (const chunk of fs.createReadStream(logPath)) {
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
const text = buffer.toString("utf8");
const searchable = `${scanWindow}${text}`;
outputTail = appendBufferTail(outputTail, buffer, OUTPUT_TAIL_BYTES);
sawDownload ||= searchable.includes("Downloading @example/lossless-claw");
sawUpToDate ||= searchable.includes("lossless-claw is up to date (0.9.0).");
scanWindow = searchable.slice(-OUTPUT_SCAN_WINDOW_BYTES);
}
return {
outputTail: outputTail
.toString("utf8")
.split(/\r?\n/u)
.slice(-OUTPUT_TAIL_LINES)
.join("\n")
.trimEnd(),
sawDownload,
sawUpToDate,
};
}
async function assertOutput(logPath) {
const evidence = await readOutputEvidence(logPath);
const failure = evidence.sawDownload
? "Unexpected npm download/reinstall path"
: !output.includes("lossless-claw is up to date (0.9.0).")
: !evidence.sawUpToDate
? "Expected up-to-date output missing"
: "";
if (failure) {
throw new Error(`${failure}\n${output}`);
throw new Error(`${failure}\nOutput tail:\n${evidence.outputTail}`);
}
}
@@ -23,7 +23,7 @@ trap 'openclaw_e2e_stop_process "${registry_pid:-}"' EXIT
if ! node "$probe" wait-registry; then
echo "Local npm metadata registry failed to start"
cat /tmp/openclaw-e2e-registry.log || true
openclaw_e2e_print_log /tmp/openclaw-e2e-registry.log
exit 1
fi
@@ -42,9 +42,9 @@ set -e
if [ "$plugin_update_status" -ne 0 ]; then
echo "Plugin update command failed or timed out after ${plugin_update_timeout_seconds}s (status ${plugin_update_status})"
echo "--- plugin update output ---"
cat /tmp/plugin-update-output.log || true
openclaw_e2e_print_log /tmp/plugin-update-output.log
echo "--- local registry output ---"
cat /tmp/openclaw-e2e-registry.log || true
openclaw_e2e_print_log /tmp/openclaw-e2e-registry.log
exit "$plugin_update_status"
fi
@@ -52,11 +52,11 @@ if [ -n "$before_config_hash" ]; then
after_config_hash="$(sha256sum "$OPENCLAW_CONFIG_PATH" | awk '{print $1}')"
if [ "$before_config_hash" != "$after_config_hash" ]; then
echo "Config changed unexpectedly for modern package $package_version"
cat /tmp/plugin-update-output.log
openclaw_e2e_print_log /tmp/plugin-update-output.log
exit 1
fi
fi
node "$probe" assert-snapshot /tmp/plugin-update-before.json
node "$probe" assert-output /tmp/plugin-update-output.log
cat /tmp/plugin-update-output.log
openclaw_e2e_print_log /tmp/plugin-update-output.log
@@ -47,6 +47,17 @@ function runProbeStatus(
}
}
function runProbeFileStatus(
command: string,
filePath: string,
): { status: number | null; stderr: string } {
const result = spawnSync("node", [PLUGIN_UPDATE_PROBE_SCRIPT, command, filePath], {
encoding: "utf8",
stdio: "pipe",
});
return { status: result.status, stderr: result.stderr };
}
describe("plugin update unchanged Docker E2E", () => {
it("seeds current plugin install ledger state before checking config stability", () => {
const runner = readFileSync(PLUGIN_UPDATE_DOCKER_SCRIPT, "utf8");
@@ -75,6 +86,56 @@ describe("plugin update unchanged Docker E2E", () => {
);
expect(script).toContain('"--- plugin update output ---"');
expect(script).toContain('"--- local registry output ---"');
expect(script).toContain("openclaw_e2e_print_log /tmp/plugin-update-output.log");
expect(script).toContain("openclaw_e2e_print_log /tmp/openclaw-e2e-registry.log");
expect(script).not.toContain("cat /tmp/plugin-update-output.log");
expect(script).not.toContain("cat /tmp/openclaw-e2e-registry.log");
});
it("bounds assert-output diagnostics to the saved command log tail", () => {
const root = mkdtempSync(path.join(tmpdir(), "openclaw-plugin-update-probe-"));
const logPath = path.join(root, "plugin-update-output.log");
try {
writeFileSync(
logPath,
`DO_NOT_PRINT_OLD_PLUGIN_UPDATE_LOG\n${"filler line\n".repeat(12 * 1024)}missing marker tail`,
"utf8",
);
const result = runProbeFileStatus("assert-output", logPath);
expect(result.status).toBe(1);
expect(result.stderr).toContain("Expected up-to-date output missing");
expect(result.stderr).toContain("Output tail:");
expect(result.stderr).toContain("missing marker tail");
expect(result.stderr).not.toContain("DO_NOT_PRINT_OLD_PLUGIN_UPDATE_LOG");
expect(result.stderr.length).toBeLessThan(80 * 1024);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
it("detects unexpected download output before a large log tail", () => {
const root = mkdtempSync(path.join(tmpdir(), "openclaw-plugin-update-probe-"));
const logPath = path.join(root, "plugin-update-output.log");
try {
writeFileSync(
logPath,
[
"Downloading @example/lossless-claw",
"filler line\n".repeat(12 * 1024),
"lossless-claw is up to date (0.9.0).",
].join("\n"),
"utf8",
);
const result = runProbeFileStatus("assert-output", logPath);
expect(result.status).toBe(1);
expect(result.stderr).toContain("Unexpected npm download/reinstall path");
} finally {
rmSync(root, { recursive: true, force: true });
}
});
it("waits for the local registry process during cleanup", () => {