198fbb7980
- 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
73 lines
2.7 KiB
JavaScript
73 lines
2.7 KiB
JavaScript
/**
|
|
* @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" });
|
|
});
|
|
});
|