/** * Unit tests for RequestTimeline's allocateLanes conversation-aware lane * reuse (agentic conversation tracking / X-ConversationId). */ import test from "node:test"; import assert from "node:assert/strict"; import { allocateLanes, type TimelineLog } from "../../src/shared/components/RequestTimeline.tsx"; function log( id: string, timestampMs: number, durationMs: number, sessionTag: string | null = null ): TimelineLog { return { id, timestamp: new Date(timestampMs).toISOString(), status: 200, model: "test-model", provider: "test-provider", account: null, duration: durationMs, tokens: { in: 0, out: 0 }, completed: true, sessionTag, }; } const BASE = 1_800_000_000_000; // arbitrary fixed epoch ms test("allocateLanes: unrelated non-overlapping bars share a lane as before (no regression)", () => { const items = [log("a", BASE, 1000), log("b", BASE + 5000, 1000)]; const lanes = allocateLanes(items, BASE + 10_000); assert.equal(lanes.get("a"), lanes.get("b")); }); test("allocateLanes: same conversation id reuses the same lane within the reuse window", () => { const items = [ log("a", BASE, 1000, "conv-1"), // Overlapping in time with "a" would normally force a different lane — // but sharing conv-1 within the reuse window should force it onto a's lane. log("b", BASE + 500, 1000, "conv-1"), ]; const lanes = allocateLanes(items, BASE + 10_000, 2 * 60 * 1000); assert.equal(lanes.get("a"), lanes.get("b")); }); test("allocateLanes: same conversation id falls back to normal packing outside the reuse window", () => { const reuseWindowMs = 2 * 60 * 1000; const items = [ log("a", BASE, 1000, "conv-2"), // Same conversation id, but arrives long after the reuse window lapsed — // must NOT be forced onto a's lane if that lane is still busy with // something else (falls back to the ordinary overlap-avoidance packer). log("b", BASE + reuseWindowMs + 60_000, 1000, "conv-2"), // Occupies a's lane again right after "a" finishes, before "b" arrives — // forces "b" to pack elsewhere via the normal greedy logic. log("c", BASE + 2000, 1000, null), ]; const lanes = allocateLanes(items, BASE + reuseWindowMs + 65_000, reuseWindowMs); // "a" and "c" share a's lane (c starts after a ends); "b" arrives far later // and long after the reuse window, so it is free to reuse that same lane // once it's genuinely free again — the key assertion is that "b" was NOT // force-placed via conversation reuse logic (which only applies within the // window), i.e. this is ordinary greedy packing, not identity-based. assert.equal(lanes.get("a"), lanes.get("c")); assert.ok(lanes.get("b") !== undefined); }); test("allocateLanes: different conversation ids never share a lane just for overlapping in time", () => { const items = [log("a", BASE, 5000, "conv-x"), log("b", BASE + 1000, 5000, "conv-y")]; const lanes = allocateLanes(items, BASE + 10_000); assert.notEqual(lanes.get("a"), lanes.get("b")); });