fix(test): run the server suite on a private tmux server
Several suites start real `agenthub-lane-<id>` tmux sessions and then kill or reset them, with lane ids from their own throwaway database. On the user's default tmux server those names collide with the dashboard's real lane panes, so a test run — including the one the pre-commit hook runs on every commit — killed whichever live session shared an id, the pane the commit was made from included. `npm run test:server` now goes through scripts/test-server.js, which drops TMUX, points TMUX_TMPDIR at a fresh directory and kills that server after. Also fixes the ERUNTIMEOUT test's tmux mock, which had the wrong (async, cmd+args) signature and only passed by accident.
This commit is contained in:
+2
-2
@@ -62,8 +62,8 @@
|
|||||||
"import-history": "node scripts/import-history.js",
|
"import-history": "node scripts/import-history.js",
|
||||||
"reconcile-tokens": "node scripts/import-history.js --reconcile-tokens",
|
"reconcile-tokens": "node scripts/import-history.js --reconcile-tokens",
|
||||||
"clear-data": "node scripts/clear-data.js",
|
"clear-data": "node scripts/clear-data.js",
|
||||||
"test": "node --test server/__tests__/*.test.js && cd client && npm test",
|
"test": "node scripts/test-server.js && cd client && npm test",
|
||||||
"test:server": "node --test server/__tests__/*.test.js",
|
"test:server": "node scripts/test-server.js",
|
||||||
"test:client": "cd client && npm test",
|
"test:client": "cd client && npm test",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"format:check": "prettier --check .",
|
"format:check": "prettier --check .",
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* @file test-server.js
|
||||||
|
* @description Runs the server test suite against a PRIVATE tmux server.
|
||||||
|
* Several suites start real `agenthub-lane-<id>` tmux sessions and then kill
|
||||||
|
* or reset them, with lane ids from their own throwaway database — on the
|
||||||
|
* user's default tmux server those names collide with the dashboard's real
|
||||||
|
* lane panes, and a test run kills whichever live session shares an id
|
||||||
|
* (including the pane the test was launched from). Dropping `TMUX` and
|
||||||
|
* pointing `TMUX_TMPDIR` at a fresh directory gives every tmux call — the
|
||||||
|
* app's own and the tests' direct `execFileSync("tmux", …)` — its own socket.
|
||||||
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { spawnSync } = require("node:child_process");
|
||||||
|
const fs = require("node:fs");
|
||||||
|
const os = require("node:os");
|
||||||
|
const path = require("node:path");
|
||||||
|
|
||||||
|
const root = path.join(__dirname, "..");
|
||||||
|
const dir = path.join(root, "server", "__tests__");
|
||||||
|
const files = fs
|
||||||
|
.readdirSync(dir)
|
||||||
|
.filter((f) => f.endsWith(".test.js"))
|
||||||
|
.map((f) => path.join(dir, f));
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
|
||||||
|
const tmuxDir = fs.mkdtempSync(path.join(os.tmpdir(), "agenthub-test-tmux-"));
|
||||||
|
const env = { ...process.env, TMUX_TMPDIR: tmuxDir };
|
||||||
|
delete env.TMUX;
|
||||||
|
delete env.TMUX_PANE;
|
||||||
|
|
||||||
|
const result = spawnSync(process.execPath, ["--test", ...args, ...(args.length ? [] : files)], {
|
||||||
|
stdio: "inherit",
|
||||||
|
env,
|
||||||
|
cwd: root,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Whatever a test leaked dies with the private server, never a real pane.
|
||||||
|
spawnSync("tmux", ["kill-server"], { env, stdio: "ignore" });
|
||||||
|
fs.rmSync(tmuxDir, { recursive: true, force: true });
|
||||||
|
process.exit(result.status ?? 1);
|
||||||
@@ -848,17 +848,12 @@ describe("destructive lane lifecycle actions", () => {
|
|||||||
const runId = started.body.lane.run_id;
|
const runId = started.body.lane.run_id;
|
||||||
|
|
||||||
// Mock tmux so has-session always returns 0 (session exists), simulating a stuck session
|
// Mock tmux so has-session always returns 0 (session exists), simulating a stuck session
|
||||||
tmux.__setExecImpl(async (cmd, args) => {
|
// Same shape as tmux.js's own exec: sync, argv only, throws on failure.
|
||||||
if (cmd === "tmux" && args[0] === "has-session" && args[1] === "-t" && args[2] === runId) {
|
tmux.__setExecImpl((args) => {
|
||||||
return { code: 0, stdout: "", stderr: "" };
|
if (args[0] === "has-session" && args[2] === runId) return "";
|
||||||
}
|
if (args[0] === "list-sessions") return `${runId}\n`;
|
||||||
if (cmd === "tmux" && args[0] === "list-sessions") {
|
if (args[0] === "kill-session" && args[2] === runId) return "";
|
||||||
return { code: 0, stdout: "", stderr: "" };
|
throw Object.assign(new Error("mocked tmux failure"), { status: 1 });
|
||||||
}
|
|
||||||
if (cmd === "tmux" && args[0] === "kill-session" && args[1] === "-t" && args[2] === runId) {
|
|
||||||
return { code: 0, stdout: "", stderr: "" };
|
|
||||||
}
|
|
||||||
return { code: 1, stdout: "", stderr: "" };
|
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user