101 lines
4.0 KiB
TypeScript
101 lines
4.0 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
|
|
import { createWorkbenchKafkaRailProjectionStream } from "./workbench-kafka-rail-stream.ts";
|
|
|
|
test("rail stream emits scoped snapshots and only semantic live deltas", async () => {
|
|
const summaries = new Map([
|
|
["ses_rail_a", railSummary("ses_rail_a", "1", "running")],
|
|
["ses_rail_b", railSummary("ses_rail_b", "2", "completed")]
|
|
]);
|
|
const subscribers = new Set<(envelope: unknown) => void>();
|
|
const delivered: any[] = [];
|
|
const bridge = {
|
|
async waitWorkbenchSessionRailReady() { return { ready: true }; },
|
|
workbenchSessionSummaries({ sessionIds = [] } = {}) {
|
|
return { ok: true, sessions: sessionIds.map((sessionId: string) => summaries.get(sessionId)) };
|
|
},
|
|
subscribeLiveHwlabEvents(listener: (envelope: unknown) => void) {
|
|
subscribers.add(listener);
|
|
return () => subscribers.delete(listener);
|
|
}
|
|
};
|
|
const stream = createWorkbenchKafkaRailProjectionStream({
|
|
bridge,
|
|
sessionIds: ["ses_rail_b", "ses_rail_a"],
|
|
deliver(payload: unknown) { delivered.push(payload); return true; }
|
|
});
|
|
|
|
await stream.start();
|
|
expect(delivered.map((item) => [item.phase, item.sessionId, item.session.status])).toEqual([
|
|
["snapshot", "ses_rail_a", "running"],
|
|
["snapshot", "ses_rail_b", "completed"]
|
|
]);
|
|
|
|
for (const listener of subscribers) listener({ schema: "hwlab.event.v1", sessionId: "ses_rail_a", event: { type: "assistant" } });
|
|
await Promise.resolve();
|
|
expect(delivered).toHaveLength(2);
|
|
|
|
summaries.set("ses_rail_a", railSummary("ses_rail_a", "4", "completed"));
|
|
for (const listener of subscribers) listener({ schema: "hwlab.event.v1", sessionId: "ses_rail_a", event: { type: "result", terminal: true } });
|
|
await Promise.resolve();
|
|
await Promise.resolve();
|
|
expect(delivered.map((item) => [item.phase, item.sessionId, item.session.status])).toEqual([
|
|
["snapshot", "ses_rail_a", "running"],
|
|
["snapshot", "ses_rail_b", "completed"],
|
|
["live", "ses_rail_a", "completed"]
|
|
]);
|
|
expect(stream.status()).toMatchObject({ live: true, snapshotCount: 2, liveCount: 1 });
|
|
stream.stop();
|
|
expect(subscribers.size).toBe(0);
|
|
});
|
|
|
|
test("rail stream waits for the first user-derived summary without failing the active session", async () => {
|
|
const sessionId = "ses_new_without_user";
|
|
const summaries = new Map<string, ReturnType<typeof railSummary>>();
|
|
const subscribers = new Set<(envelope: unknown) => void>();
|
|
const delivered: any[] = [];
|
|
const failures: unknown[] = [];
|
|
const bridge = {
|
|
async waitWorkbenchSessionRailReady() { return { ready: true }; },
|
|
workbenchSessionSummaries({ sessionIds = [] } = {}) {
|
|
return { ok: true, sessions: sessionIds.map((id: string) => summaries.get(id)).filter(Boolean) };
|
|
},
|
|
subscribeLiveHwlabEvents(listener: (envelope: unknown) => void) {
|
|
subscribers.add(listener);
|
|
return () => subscribers.delete(listener);
|
|
}
|
|
};
|
|
const stream = createWorkbenchKafkaRailProjectionStream({
|
|
bridge,
|
|
sessionIds: [sessionId],
|
|
deliver(payload: unknown) { delivered.push(payload); return true; },
|
|
onFailure(error: unknown) { failures.push(error); }
|
|
});
|
|
|
|
await stream.start();
|
|
expect(delivered).toEqual([]);
|
|
expect(failures).toEqual([]);
|
|
|
|
summaries.set(sessionId, railSummary(sessionId, "7", "running"));
|
|
for (const listener of subscribers) listener({ schema: "hwlab.event.v1", sessionId, event: { type: "user" } });
|
|
await Promise.resolve();
|
|
await Promise.resolve();
|
|
|
|
expect(delivered).toHaveLength(1);
|
|
expect(delivered[0]).toMatchObject({ phase: "live", sessionId, session: { firstUserMessagePreview: `title ${sessionId}` } });
|
|
expect(failures).toEqual([]);
|
|
stream.stop();
|
|
});
|
|
|
|
function railSummary(sessionId: string, offset: string, status: string) {
|
|
return {
|
|
sessionId,
|
|
firstUserMessagePreview: `title ${sessionId}`,
|
|
lastUserMessageAt: "2026-07-22T08:00:00.000Z",
|
|
lastTraceId: `trc_${sessionId}`,
|
|
status,
|
|
revision: { topic: "hwlab.event.v1", partition: 0, offset, valuesRedacted: true },
|
|
valuesRedacted: true
|
|
};
|
|
}
|