fix: event-loop freezes, plugin-install boot crash, tmux attach/scrollbar, Codex transcripts
- GET /api/workflows: one LEAD() window pass in a read worker (read-pool.js, workflow-stats.js); idx_events_session restored, idx_events_tool added - compat-sqlite gains function/pluck/readonly so a plugin install without a better-sqlite3 build boots again; antigravity-usage uses db.js's Database - install-hooks adds no checkout hooks while the agenthub plugin is enabled (events were doubled) - terminal attach runs the live tmux server's own binary; the pane scrollbar gets a visible 2-column style, and claude panes start with CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN=1 so tmux keeps history to scroll - Codex sessions' conversation in GET /api/sessions/:id/transcript - Codex/Antigravity sessions claimed by their workspace without hooks - UI-audit follow-ups, docs and OpenAPI
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* @file Tests server/lib/read-pool.js: a read job runs in the worker on its own
|
||||
* connection, identical calls in flight share one run, a job over the timeout
|
||||
* is refused with EREADTIMEOUT and the next one gets a fresh worker, and
|
||||
* AGENTHUB_READ_WORKER=0 runs the job inline.
|
||||
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||||
*/
|
||||
|
||||
const { describe, it, before, after, afterEach } = require("node:test");
|
||||
const assert = require("node:assert/strict");
|
||||
const fs = require("fs");
|
||||
const os = require("os");
|
||||
const path = require("path");
|
||||
|
||||
const TEST_DB = path.join(os.tmpdir(), `dashboard-readpool-test-${Date.now()}-${process.pid}.db`);
|
||||
process.env.DASHBOARD_DB_PATH = TEST_DB;
|
||||
|
||||
const { db } = require("../db");
|
||||
const { runRead } = require("../lib/read-pool");
|
||||
|
||||
before(() => {
|
||||
db.prepare("INSERT INTO sessions (id, status) VALUES ('rp-1', 'completed')").run();
|
||||
const ins = db.prepare(
|
||||
"INSERT INTO events (session_id, event_type, tool_name) VALUES ('rp-1', 'PreToolUse', ?)"
|
||||
);
|
||||
for (const t of ["Read", "Edit", "Read", "Edit"]) ins.run(t);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
delete process.env.AGENTHUB_READ_TIMEOUT_MS;
|
||||
delete process.env.AGENTHUB_READ_WORKER;
|
||||
});
|
||||
|
||||
after(() => {
|
||||
try {
|
||||
db.close();
|
||||
} catch {}
|
||||
for (const s of ["", "-wal", "-shm"]) fs.rmSync(TEST_DB + s, { force: true });
|
||||
});
|
||||
|
||||
describe("read-pool", () => {
|
||||
it("runs a job in the worker and sees the main connection's committed rows", async () => {
|
||||
const data = await runRead("workflows", { sections: ["toolFlow"] });
|
||||
assert.deepEqual(data.toolFlow.transitions.map((t) => [t.source, t.target, t.value]).sort(), [
|
||||
["Edit", "Read", 1],
|
||||
["Read", "Edit", 2],
|
||||
]);
|
||||
});
|
||||
|
||||
it("shares one run between identical calls in flight", async () => {
|
||||
const a = runRead("workflows", { sections: ["stats"] });
|
||||
const b = runRead("workflows", { sections: ["stats"] });
|
||||
assert.equal(a, b);
|
||||
assert.notEqual(a, runRead("workflows", { sections: ["toolFlow"] }));
|
||||
await Promise.all([a, b]);
|
||||
});
|
||||
|
||||
it("refuses a job over the timeout, then recovers with a fresh worker", async () => {
|
||||
process.env.AGENTHUB_READ_TIMEOUT_MS = "1";
|
||||
await assert.rejects(runRead("workflows", { tag: "slow" }), { code: "EREADTIMEOUT" });
|
||||
delete process.env.AGENTHUB_READ_TIMEOUT_MS;
|
||||
const data = await runRead("workflows", { sections: ["stats"] });
|
||||
assert.equal(data.stats.totalSessions, 1);
|
||||
});
|
||||
|
||||
it("runs inline with AGENTHUB_READ_WORKER=0, and rejects an unknown job", async () => {
|
||||
process.env.AGENTHUB_READ_WORKER = "0";
|
||||
const data = await runRead("workflows", { sections: ["stats"], tag: "inline" });
|
||||
assert.equal(data.stats.topFlow.source, "Read");
|
||||
await assert.rejects(runRead("nope"), { code: "EBADJOB" });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user