/** * Activity-feed tests — the dock's composition (server/observability.ts). * * Real stores with fixture chats/jobs, cleaned up in a top-level test. * Verifies the cross-chat gathering, running-only filtering, title joins, * stable oldest-first ordering, and busy-chat reporting. */ import assert from "node:assert/strict"; import test from "node:test"; import * as fs from "node:fs"; import * as path from "node:path"; import { chatStore } from "../src/store/chats.ts"; import { jobStore } from "../src/store/jobs.ts"; import { PATHS } from "../config.ts"; import { activityFeed } from "../src/server/observability.ts"; test("activityFeed: running jobs across ALL chats, running-only, title-joined, oldest first", () => { const chatA = chatStore.createChat("feed chat A"); const chatB = chatStore.createChat("feed chat B"); // job ids tracked as created — the finally cleanup stays scoped to them even // if the test throws mid-way (test files run as concurrent processes over // the shared data/ scratch; wiping the whole jobs dir would delete other // processes' live fixtures — the wave-4 flake lesson) const createdJobs: string[] = []; try { const jobA = jobStore.createJob({ kind: "research", chatId: chatA.id, toolCallId: "tc_a" }); const jobB = jobStore.createJob({ kind: "demo", chatId: chatB.id, toolCallId: "tc_b" }); const jobDone = jobStore.createJob({ kind: "research", chatId: chatA.id, toolCallId: "tc_done" }); createdJobs.push(jobA.id, jobB.id, jobDone.id); jobStore.updateJob(jobDone.id, { status: "done" }); const jobB2 = jobStore.createJob({ kind: "research", chatId: chatB.id, toolCallId: "tc_b2" }); createdJobs.push(jobB2.id); // make jobB2 the MOST recently active → should still sort AFTER jobA/jobB (oldest-first). // Explicit later timestamp: the real bump can land in the same ms as creation. jobStore.updateJob(jobB2.id, { activity: "second step", updatedAt: new Date(Date.now() + 10).toISOString() }); const feed = activityFeed(); const feedJobs = feed.jobs.filter((j) => [jobA.id, jobB.id, jobB2.id].includes(j.id)); assert.equal(feedJobs.length, 3); // done job filtered out for (const j of feedJobs) { assert.ok(j.title.length > 0, "title joined onto every job"); assert.equal(typeof j.chatId, "string"); } // title join is the chat's title assert.equal(feedJobs.find((j) => j.id === jobA.id)?.title, "feed chat A"); // oldest-first on updatedAt: jobA/jobB created before jobB2's bump const order = feedJobs.map((j) => j.id); assert.equal(order.indexOf(jobB2.id), order.length - 1); // extraction runs are absent when the pipeline is idle (registry empty) assert.deepEqual(feed.extractions, []); } finally { fs.unlinkSync(`${PATHS.chats}/${chatA.id}.json`); fs.unlinkSync(`${PATHS.chats}/${chatB.id}.json`); const jobsDir = path.join(PATHS.data, "jobs"); for (const id of createdJobs) { fs.unlinkSync(path.join(jobsDir, `${id}.json`)); } } });