Skip to content

Commit 9fb8d87

Browse files
committed
fix(e2e): bound plugin update logs
1 parent 248dfb2 commit 9fb8d87

3 files changed

Lines changed: 110 additions & 10 deletions

File tree

scripts/e2e/lib/plugin-update/probe.mjs

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import {
1010
} from "../plugin-index-sqlite.mjs";
1111

1212
const home = os.homedir();
13+
const OUTPUT_TAIL_BYTES = 64 * 1024;
14+
const OUTPUT_TAIL_LINES = 120;
15+
const OUTPUT_SCAN_WINDOW_BYTES = 8 * 1024;
1316

1417
const readJson = (file) => {
1518
try {
@@ -106,15 +109,51 @@ function assertSnapshot(beforePath) {
106109
}
107110
}
108111

109-
function assertOutput(logPath) {
110-
const output = fs.readFileSync(logPath, "utf8");
111-
const failure = output.includes("Downloading @example/lossless-claw")
112+
function appendBufferTail(tail, chunk, maxBytes) {
113+
if (chunk.length >= maxBytes) {
114+
return chunk.subarray(chunk.length - maxBytes);
115+
}
116+
if (tail.length + chunk.length <= maxBytes) {
117+
return Buffer.concat([tail, chunk]);
118+
}
119+
return Buffer.concat([tail, chunk]).subarray(tail.length + chunk.length - maxBytes);
120+
}
121+
122+
async function readOutputEvidence(logPath) {
123+
let outputTail = Buffer.alloc(0);
124+
let scanWindow = "";
125+
let sawDownload = false;
126+
let sawUpToDate = false;
127+
for await (const chunk of fs.createReadStream(logPath)) {
128+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
129+
const text = buffer.toString("utf8");
130+
const searchable = `${scanWindow}${text}`;
131+
outputTail = appendBufferTail(outputTail, buffer, OUTPUT_TAIL_BYTES);
132+
sawDownload ||= searchable.includes("Downloading @example/lossless-claw");
133+
sawUpToDate ||= searchable.includes("lossless-claw is up to date (0.9.0).");
134+
scanWindow = searchable.slice(-OUTPUT_SCAN_WINDOW_BYTES);
135+
}
136+
return {
137+
outputTail: outputTail
138+
.toString("utf8")
139+
.split(/\r?\n/u)
140+
.slice(-OUTPUT_TAIL_LINES)
141+
.join("\n")
142+
.trimEnd(),
143+
sawDownload,
144+
sawUpToDate,
145+
};
146+
}
147+
148+
async function assertOutput(logPath) {
149+
const evidence = await readOutputEvidence(logPath);
150+
const failure = evidence.sawDownload
112151
? "Unexpected npm download/reinstall path"
113-
: !output.includes("lossless-claw is up to date (0.9.0).")
152+
: !evidence.sawUpToDate
114153
? "Expected up-to-date output missing"
115154
: "";
116155
if (failure) {
117-
throw new Error(`${failure}\n${output}`);
156+
throw new Error(`${failure}\nOutput tail:\n${evidence.outputTail}`);
118157
}
119158
}
120159

scripts/e2e/lib/plugin-update/unchanged-scenario.sh

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ trap 'openclaw_e2e_stop_process "${registry_pid:-}"' EXIT
2323

2424
if ! node "$probe" wait-registry; then
2525
echo "Local npm metadata registry failed to start"
26-
cat /tmp/openclaw-e2e-registry.log || true
26+
openclaw_e2e_print_log /tmp/openclaw-e2e-registry.log
2727
exit 1
2828
fi
2929

@@ -42,21 +42,21 @@ set -e
4242
if [ "$plugin_update_status" -ne 0 ]; then
4343
echo "Plugin update command failed or timed out after ${plugin_update_timeout_seconds}s (status ${plugin_update_status})"
4444
echo "--- plugin update output ---"
45-
cat /tmp/plugin-update-output.log || true
45+
openclaw_e2e_print_log /tmp/plugin-update-output.log
4646
echo "--- local registry output ---"
47-
cat /tmp/openclaw-e2e-registry.log || true
47+
openclaw_e2e_print_log /tmp/openclaw-e2e-registry.log
4848
exit "$plugin_update_status"
4949
fi
5050

5151
if [ -n "$before_config_hash" ]; then
5252
after_config_hash="$(sha256sum "$OPENCLAW_CONFIG_PATH" | awk '{print $1}')"
5353
if [ "$before_config_hash" != "$after_config_hash" ]; then
5454
echo "Config changed unexpectedly for modern package $package_version"
55-
cat /tmp/plugin-update-output.log
55+
openclaw_e2e_print_log /tmp/plugin-update-output.log
5656
exit 1
5757
fi
5858
fi
5959

6060
node "$probe" assert-snapshot /tmp/plugin-update-before.json
6161
node "$probe" assert-output /tmp/plugin-update-output.log
62-
cat /tmp/plugin-update-output.log
62+
openclaw_e2e_print_log /tmp/plugin-update-output.log

test/scripts/plugin-update-unchanged-docker.test.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,17 @@ function runProbeStatus(
4747
}
4848
}
4949

50+
function runProbeFileStatus(
51+
command: string,
52+
filePath: string,
53+
): { status: number | null; stderr: string } {
54+
const result = spawnSync("node", [PLUGIN_UPDATE_PROBE_SCRIPT, command, filePath], {
55+
encoding: "utf8",
56+
stdio: "pipe",
57+
});
58+
return { status: result.status, stderr: result.stderr };
59+
}
60+
5061
describe("plugin update unchanged Docker E2E", () => {
5162
it("seeds current plugin install ledger state before checking config stability", () => {
5263
const runner = readFileSync(PLUGIN_UPDATE_DOCKER_SCRIPT, "utf8");
@@ -75,6 +86,56 @@ describe("plugin update unchanged Docker E2E", () => {
7586
);
7687
expect(script).toContain('"--- plugin update output ---"');
7788
expect(script).toContain('"--- local registry output ---"');
89+
expect(script).toContain("openclaw_e2e_print_log /tmp/plugin-update-output.log");
90+
expect(script).toContain("openclaw_e2e_print_log /tmp/openclaw-e2e-registry.log");
91+
expect(script).not.toContain("cat /tmp/plugin-update-output.log");
92+
expect(script).not.toContain("cat /tmp/openclaw-e2e-registry.log");
93+
});
94+
95+
it("bounds assert-output diagnostics to the saved command log tail", () => {
96+
const root = mkdtempSync(path.join(tmpdir(), "openclaw-plugin-update-probe-"));
97+
const logPath = path.join(root, "plugin-update-output.log");
98+
try {
99+
writeFileSync(
100+
logPath,
101+
`DO_NOT_PRINT_OLD_PLUGIN_UPDATE_LOG\n${"filler line\n".repeat(12 * 1024)}missing marker tail`,
102+
"utf8",
103+
);
104+
105+
const result = runProbeFileStatus("assert-output", logPath);
106+
107+
expect(result.status).toBe(1);
108+
expect(result.stderr).toContain("Expected up-to-date output missing");
109+
expect(result.stderr).toContain("Output tail:");
110+
expect(result.stderr).toContain("missing marker tail");
111+
expect(result.stderr).not.toContain("DO_NOT_PRINT_OLD_PLUGIN_UPDATE_LOG");
112+
expect(result.stderr.length).toBeLessThan(80 * 1024);
113+
} finally {
114+
rmSync(root, { recursive: true, force: true });
115+
}
116+
});
117+
118+
it("detects unexpected download output before a large log tail", () => {
119+
const root = mkdtempSync(path.join(tmpdir(), "openclaw-plugin-update-probe-"));
120+
const logPath = path.join(root, "plugin-update-output.log");
121+
try {
122+
writeFileSync(
123+
logPath,
124+
[
125+
"Downloading @example/lossless-claw",
126+
"filler line\n".repeat(12 * 1024),
127+
"lossless-claw is up to date (0.9.0).",
128+
].join("\n"),
129+
"utf8",
130+
);
131+
132+
const result = runProbeFileStatus("assert-output", logPath);
133+
134+
expect(result.status).toBe(1);
135+
expect(result.stderr).toContain("Unexpected npm download/reinstall path");
136+
} finally {
137+
rmSync(root, { recursive: true, force: true });
138+
}
78139
});
79140

80141
it("waits for the local registry process during cleanup", () => {

0 commit comments

Comments
 (0)