57dc91585d
Internal SmartGift build of a Claude Code monitoring dashboard. Lanes: a durable unit of parallel agent work, one per working directory, tracked across session restarts. Managed lanes are git worktrees the dashboard provisions and can reset or remove behind a three-check destroy guard and a counted preflight; adopted lanes are directories you already own and are never destroyable. Pipelines: a lane moves through pipeline stages. A stage the agent declares with evidence renders green; a stage inferred from the tool-event stream renders dashed amber and never counts as done. Detection is forward-only within a 30-minute window, and never writes the declared stage. Workspace: one page at /run with a lane grid, the selected lane's pipeline, and a full Claude console behind a disclosure.
128 lines
4.0 KiB
JavaScript
128 lines
4.0 KiB
JavaScript
/**
|
|
* @file Tests for multi-server discovery and hook-ingest deduplication by
|
|
* SQLite data directory (`server/lib/server-info.js`).
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
|
|
const { describe, it, beforeEach, afterEach } = require("node:test");
|
|
const assert = require("node:assert/strict");
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const os = require("os");
|
|
|
|
const STAMP = `server-info-${Date.now()}-${process.pid}`;
|
|
const TMP = path.join(os.tmpdir(), STAMP);
|
|
const CLAUDE_HOME = path.join(TMP, "home");
|
|
const DATA_DIR_A = path.join(TMP, "data-a");
|
|
const DATA_DIR_B = path.join(TMP, "data-b");
|
|
|
|
function freshModule(dataDir) {
|
|
delete require.cache[require.resolve("../lib/server-info")];
|
|
delete require.cache[require.resolve("../lib/claude-home")];
|
|
process.env.CLAUDE_HOME = CLAUDE_HOME;
|
|
process.env.DASHBOARD_DATA_DIR = dataDir;
|
|
return require("../lib/server-info");
|
|
}
|
|
|
|
function writeDiscovery(servers) {
|
|
const infoPath = path.join(CLAUDE_HOME, ".agent-dashboard.json");
|
|
fs.mkdirSync(CLAUDE_HOME, { recursive: true });
|
|
const recent = servers.reduce((a, b) =>
|
|
Date.parse(b.startedAt) > Date.parse(a.startedAt) ? b : a
|
|
);
|
|
fs.writeFileSync(
|
|
infoPath,
|
|
JSON.stringify(
|
|
{ port: recent.port, pid: recent.pid, startedAt: recent.startedAt, servers },
|
|
null,
|
|
2
|
|
)
|
|
);
|
|
}
|
|
|
|
describe("server-info hook ingest deduplication", () => {
|
|
beforeEach(() => {
|
|
fs.rmSync(TMP, { recursive: true, force: true });
|
|
fs.mkdirSync(DATA_DIR_A, { recursive: true });
|
|
fs.mkdirSync(DATA_DIR_B, { recursive: true });
|
|
});
|
|
|
|
afterEach(() => {
|
|
delete process.env.CLAUDE_DASHBOARD_PORT;
|
|
fs.rmSync(TMP, { recursive: true, force: true });
|
|
delete require.cache[require.resolve("../lib/server-info")];
|
|
delete require.cache[require.resolve("../lib/claude-home")];
|
|
});
|
|
|
|
it("dedupes hook targets when two live servers share one dataDir", () => {
|
|
const mod = freshModule(DATA_DIR_A);
|
|
writeDiscovery([
|
|
{
|
|
port: 4820,
|
|
pid: process.pid,
|
|
startedAt: "2026-01-01T00:00:00.000Z",
|
|
dataDir: DATA_DIR_A,
|
|
},
|
|
{
|
|
port: 4821,
|
|
pid: process.pid,
|
|
startedAt: "2026-01-02T00:00:00.000Z",
|
|
dataDir: DATA_DIR_A,
|
|
},
|
|
]);
|
|
assert.deepEqual(mod.resolveHookIngestPorts(), [4820]);
|
|
assert.deepEqual(mod.resolveAllDashboardPorts(), [4820, 4821]);
|
|
});
|
|
|
|
it("fans out when live servers use different data directories", () => {
|
|
const mod = freshModule(DATA_DIR_A);
|
|
writeDiscovery([
|
|
{
|
|
port: 4820,
|
|
pid: process.pid,
|
|
startedAt: "2026-01-01T00:00:00.000Z",
|
|
dataDir: DATA_DIR_A,
|
|
},
|
|
{
|
|
port: 4900,
|
|
pid: process.pid,
|
|
startedAt: "2026-01-02T00:00:00.000Z",
|
|
dataDir: DATA_DIR_B,
|
|
},
|
|
]);
|
|
assert.deepEqual(mod.resolveHookIngestPorts(), [4820, 4900]);
|
|
});
|
|
|
|
it("treats legacy entries without dataDir as unique per port", () => {
|
|
const mod = freshModule(DATA_DIR_A);
|
|
writeDiscovery([
|
|
{ port: 4820, pid: process.pid, startedAt: "2026-01-01T00:00:00.000Z" },
|
|
{ port: 4821, pid: process.pid, startedAt: "2026-01-02T00:00:00.000Z" },
|
|
]);
|
|
assert.deepEqual(mod.resolveHookIngestPorts(), [4820, 4821]);
|
|
});
|
|
|
|
it("records dataDir when writing server info", () => {
|
|
const mod = freshModule(DATA_DIR_A);
|
|
mod.writeServerInfo(4999);
|
|
const raw = JSON.parse(fs.readFileSync(mod.getServerInfoPath(), "utf8"));
|
|
const entry = raw.servers.find((s) => s.port === 4999);
|
|
assert.ok(entry);
|
|
assert.equal(mod.normalizeDataDir(entry.dataDir), mod.normalizeDataDir(DATA_DIR_A));
|
|
});
|
|
|
|
it("honors CLAUDE_DASHBOARD_PORT for hook ingest", () => {
|
|
const mod = freshModule(DATA_DIR_A);
|
|
process.env.CLAUDE_DASHBOARD_PORT = "7777";
|
|
writeDiscovery([
|
|
{
|
|
port: 4820,
|
|
pid: process.pid,
|
|
startedAt: "2026-01-01T00:00:00.000Z",
|
|
dataDir: DATA_DIR_A,
|
|
},
|
|
]);
|
|
assert.deepEqual(mod.resolveHookIngestPorts(), [7777]);
|
|
});
|
|
});
|