mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-31 12:28:35 +00:00
116 lines
4.1 KiB
TypeScript
116 lines
4.1 KiB
TypeScript
import { spawnSync } from "node:child_process";
|
|
import { EventEmitter } from "node:events";
|
|
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import path from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
import { runCase } from "../../scripts/profile-extension-memory.mjs";
|
|
|
|
const SCRIPT_PATH = path.resolve("scripts/profile-extension-memory.mjs");
|
|
|
|
function runProfileExtensionMemory(args: string[], cwd = process.cwd()) {
|
|
return spawnSync(process.execPath, [SCRIPT_PATH, ...args], {
|
|
cwd,
|
|
encoding: "utf8",
|
|
});
|
|
}
|
|
|
|
describe("scripts/profile-extension-memory", () => {
|
|
it("prints help without requiring built plugin artifacts", () => {
|
|
const result = runProfileExtensionMemory(["--help"]);
|
|
|
|
expect(result.status).toBe(0);
|
|
expect(result.stderr).toBe("");
|
|
expect(result.stdout).toContain("Usage: node scripts/profile-extension-memory.mjs");
|
|
});
|
|
|
|
it("rejects loose numeric flags before scanning built plugin artifacts", () => {
|
|
const cases = [
|
|
["--concurrency", "2abc"],
|
|
["--timeout-ms", "1e3"],
|
|
["--combined-timeout-ms", "90000ms"],
|
|
["--top", "0x10"],
|
|
];
|
|
|
|
for (const [flag, value] of cases) {
|
|
const result = runProfileExtensionMemory([flag, value]);
|
|
|
|
expect(result.status).toBe(1);
|
|
expect(result.stdout).toBe("");
|
|
expect(result.stderr).toContain(`[extension-memory] ${flag} must be a positive integer`);
|
|
expect(result.stderr).not.toContain("dist/extensions");
|
|
expect(result.stderr).not.toContain("at ");
|
|
}
|
|
});
|
|
|
|
it("bounds noisy child output without losing RSS samples", () => {
|
|
const root = mkdtempSync(path.join(tmpdir(), "openclaw-extension-memory-test-"));
|
|
try {
|
|
const extensionDir = path.join(root, "dist", "extensions", "noisy");
|
|
const reportPath = path.join(root, "report.json");
|
|
mkdirSync(extensionDir, { recursive: true });
|
|
writeFileSync(
|
|
path.join(extensionDir, "index.js"),
|
|
[
|
|
`const fs = require("node:fs");`,
|
|
`fs.writeSync(2, "old stderr " + "x".repeat(160000) + "\\n");`,
|
|
`fs.writeSync(1, "old stdout " + "y".repeat(160000) + "\\n");`,
|
|
`process.on("exit", () => fs.writeSync(2, "exit tail\\n"));`,
|
|
].join("\n"),
|
|
"utf8",
|
|
);
|
|
|
|
const result = runProfileExtensionMemory(
|
|
["--extension", "noisy", "--skip-combined", "--concurrency", "1", "--json", reportPath],
|
|
root,
|
|
);
|
|
|
|
expect(result.status, result.stderr).toBe(0);
|
|
const report = JSON.parse(readFileSync(reportPath, "utf8"));
|
|
expect(report.results).toHaveLength(1);
|
|
expect(report.results[0].status).toBe("ok");
|
|
expect(report.results[0].maxRssMb).toEqual(expect.any(Number));
|
|
expect(report.results[0].stderrPreview).toContain("[output truncated");
|
|
expect(report.results[0].stderrPreview).toContain("[stderr preview truncated");
|
|
expect(report.results[0].stderrPreview).toContain("exit tail");
|
|
expect(report.results[0].stderrPreview).not.toContain("old stderr");
|
|
expect(report.results[0].stderrPreview.length).toBeLessThan(9_000);
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("resolves spawn errors without waiting for the timeout", async () => {
|
|
const startedAt = Date.now();
|
|
const result = await runCase({
|
|
repoRoot: process.cwd(),
|
|
env: process.env,
|
|
hookPath: "missing-hook.mjs",
|
|
name: "spawn-error",
|
|
body: "",
|
|
timeoutMs: 30_000,
|
|
spawnImpl: () => {
|
|
const child = new EventEmitter() as EventEmitter & {
|
|
kill: () => boolean;
|
|
stderr: EventEmitter;
|
|
stdout: EventEmitter;
|
|
};
|
|
child.stderr = new EventEmitter();
|
|
child.stdout = new EventEmitter();
|
|
child.kill = () => true;
|
|
queueMicrotask(() => child.emit("error", new Error("spawn denied")));
|
|
return child;
|
|
},
|
|
});
|
|
|
|
expect(Date.now() - startedAt).toBeLessThan(1000);
|
|
expect(result).toMatchObject({
|
|
code: null,
|
|
error: "spawn denied",
|
|
name: "spawn-error",
|
|
signal: null,
|
|
timedOut: false,
|
|
});
|
|
});
|
|
});
|