/** * @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ĩ */ 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); }); }); 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"] ); }); });