Files
AgentHub/server/__tests__/ui-audit-routes.test.js
T
nntrivi2001 d060d288fb fix(sessions): Top tools counts each tool call once
The session analysis page's Top tools list counted every event carrying a
tool_name, so each call was counted twice (its PreToolUse and its
PostToolUse) while the Tool calls total beside it counted once — a session
with 840 calls showed Bash at 1,533. The list now counts the same way as
the total: PreToolUse, or PostToolUse in a session with none (imported).
2026-10-29 14:53:45 +07:00

135 lines
4.9 KiB
JavaScript

/**
* @file ui-audit-routes.test.js
* @description Contract tests for the read-route additions the dashboard UI audit
* needed: batched agents by `session_ids`, exact tool-call totals on session
* stats, session/agent labels joined onto the events list, and the
* `awaiting`/`from`/`to` filters on the sessions list.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
const { describe, it, before, after } = require("node:test");
const assert = require("node:assert/strict");
const path = require("path");
const fs = require("fs");
const os = require("os");
const TEST_DB = path.join(os.tmpdir(), `ui-audit-test-${Date.now()}-${process.pid}.db`);
process.env.DASHBOARD_DB_PATH = TEST_DB;
process.env.DASHBOARD_LIVENESS_PROBE = "0";
const { createApp, startServer } = require("../index");
const { db } = require("../db");
let server;
let BASE;
async function get(p) {
const res = await fetch(BASE + p);
return { status: res.status, body: await res.json() };
}
before(async () => {
server = await startServer(createApp(), 0);
BASE = `http://127.0.0.1:${server.address().port}`;
const s = db.prepare(
"INSERT INTO sessions (id, name, status, cwd, started_at, awaiting_input_since) VALUES (?, ?, ?, ?, ?, ?)"
);
s.run("ua-1", "First", "active", "/tmp/ua", "2026-01-10T10:00:00.000Z", "2026-01-10T10:05:00Z");
s.run("ua-2", "Second", "completed", "/tmp/ua", "2026-02-10T10:00:00.000Z", null);
s.run("ua-3", "Third", "active", "/tmp/ua", "2026-03-10T10:00:00.000Z", null);
const a = db.prepare(
"INSERT INTO agents (id, session_id, name, type, subagent_type) VALUES (?, ?, ?, ?, ?)"
);
a.run("ua-1-main", "ua-1", "Main", "main", null);
a.run("ua-2-main", "ua-2", "Main", "main", null);
a.run("ua-2-sub", "ua-2", "Explorer", "subagent", "Explore");
a.run("ua-3-main", "ua-3", "Main", "main", null);
const e = db.prepare(
"INSERT INTO events (session_id, agent_id, event_type, tool_name) VALUES (?, ?, ?, ?)"
);
// ua-1: 20 distinct tools, one PreToolUse + one PostToolUse each.
for (let i = 0; i < 20; i++) {
e.run("ua-1", "ua-1-main", "PreToolUse", `Tool${i}`);
e.run("ua-1", "ua-1-main", "PostToolUse", `Tool${i}`);
}
// ua-2: imported shape, PostToolUse only.
e.run("ua-2", "ua-2-sub", "PostToolUse", "Read");
e.run("ua-2", "ua-2-sub", "PostToolUse", "Read");
});
after(() => {
if (server) server.close();
if (db) db.close();
for (const f of [TEST_DB, TEST_DB + "-wal", TEST_DB + "-shm"]) {
try {
fs.unlinkSync(f);
} catch {
// ignore
}
}
});
describe("GET /api/agents?session_ids=", () => {
it("returns every agent of the named sessions in one call", async () => {
const { status, body } = await get("/api/agents?session_ids=ua-1,ua-2");
assert.equal(status, 200);
assert.deepEqual(body.agents.map((a) => a.id).sort(), ["ua-1-main", "ua-2-main", "ua-2-sub"]);
});
it("an empty list returns no agents", async () => {
const { body } = await get("/api/agents?session_ids=");
assert.deepEqual(body.agents, []);
});
});
describe("GET /api/sessions/:id/stats tool totals", () => {
it("counts every PreToolUse, not the top-15 list", async () => {
const { body } = await get("/api/sessions/ua-1/stats");
assert.equal(body.tools_used.length, 15);
assert.equal(body.tool_call_count, 20);
assert.equal(body.distinct_tool_count, 20);
});
it("falls back to PostToolUse when a session has no PreToolUse", async () => {
const { body } = await get("/api/sessions/ua-2/stats");
assert.equal(body.tool_call_count, 2);
assert.equal(body.distinct_tool_count, 1);
});
it("counts each call once in tools_used, not its Pre and Post events", async () => {
const one = (await get("/api/sessions/ua-1/stats")).body.tools_used;
assert.ok(one.every((t) => t.count === 1));
const two = (await get("/api/sessions/ua-2/stats")).body.tools_used;
assert.deepEqual(two, [{ tool_name: "Read", count: 2 }]);
});
});
describe("GET /api/events labels", () => {
it("joins session name/cwd and agent name/type onto each row", async () => {
const { body } = await get("/api/events?session_id=ua-2");
assert.equal(body.total, 2);
const ev = body.events[0];
assert.equal(ev.session_name, "Second");
assert.equal(ev.session_cwd, "/tmp/ua");
assert.equal(ev.agent_name, "Explorer");
assert.equal(ev.agent_subagent_type, "Explore");
});
});
describe("GET /api/sessions filters", () => {
it("awaiting=1 keeps only active sessions waiting on the user", async () => {
const { body } = await get("/api/sessions?awaiting=1&q=ua-");
assert.deepEqual(
body.sessions.map((s) => s.id),
["ua-1"]
);
assert.equal(body.total, 1);
});
it("from/to bound started_at", async () => {
const { body } = await get(
"/api/sessions?q=ua-&from=2026-02-01T00:00:00Z&to=2026-02-28T23:59:59Z"
);
assert.deepEqual(
body.sessions.map((s) => s.id),
["ua-2"]
);
});
});