Files
pikasTech-HWLAB/internal/cloud/workbench-kafka-session-index.test.ts
T

328 lines
11 KiB
TypeScript

import assert from "node:assert/strict";
import { expect, test } from "bun:test";
import { createWorkbenchKafkaSessionIndex, workbenchKafkaRailSummary } from "./workbench-kafka-session-index.ts";
test("process session index bootstraps once and serves repeated scoped queries from memory", async () => {
let queryCount = 0;
let now = 1000;
const index = createWorkbenchKafkaSessionIndex({
topic: "hwlab.event.v1",
maxEvents: 20,
maxEventsPerSession: 10,
bootstrapTimeoutMs: 5000,
rebuildIntervalMs: 60000,
rebuildCooldownMs: 1000,
scanLimit: 100,
nowMs: () => now++,
logger: quietLogger(),
queryAll: async () => {
queryCount += 1;
return completeResult([
record(0, "ses_a", "trc_a"),
record(1, "ses_b", "trc_b"),
record(2, "ses_a", "trc_a")
], "3");
}
});
assert.equal(await index.start(), true);
const first = index.query({ sessionId: "ses_a", partitionKey: "ses_a", limit: 10, scanLimit: 100, timeoutMs: 5000 });
const second = index.query({ sessionId: "ses_a", partitionKey: "ses_a", limit: 10, scanLimit: 100, timeoutMs: 5000 });
assert.equal(first.hit, true);
assert.equal(second.hit, true);
assert.equal(first.result.events.length, 2);
assert.equal(first.result.scannedCount, 2);
assert.equal(first.result.index.hit, true);
assert.equal(first.result.index.indexedEventCount, 3);
assert.equal(queryCount, 1);
expect(index.sessionSummaries().sessions).toMatchObject([
{ sessionId: "ses_a", firstUserMessagePreview: "prompt ses_a", lastUserMessageAt: "2026-07-20T00:00:01.000Z", lastTraceId: "trc_a", status: "running", valuesRedacted: true }
]);
index.observeLive(envelope("ses_a", "trc_a", 4), { topic: "hwlab.event.v1", partition: 0, offset: "3" });
const live = index.query({ sessionId: "ses_a", limit: 10 });
assert.equal(live.hit, true);
assert.equal(live.result.events.length, 3);
assert.equal(live.result.endOffsets[0].endOffset, "4");
index.stop();
});
test("process session index returns one transport-ordered result for a session set", async () => {
const index = createWorkbenchKafkaSessionIndex({
topic: "hwlab.event.v1",
maxEvents: 20,
maxEventsPerSession: 10,
bootstrapTimeoutMs: 5000,
rebuildIntervalMs: 60000,
rebuildCooldownMs: 1000,
scanLimit: 100,
logger: quietLogger(),
queryAll: async () => completeResult([
record(0, "ses_a", "trc_a"),
record(1, "ses_c", "trc_c"),
record(2, "ses_b", "trc_b")
], "3")
});
assert.equal(await index.start(), true);
const result = index.query({ sessionIds: ["ses_b", "ses_a", "ses_b"], limit: 10 });
assert.equal(result.hit, true);
assert.deepEqual(result.result.events.map((event) => event.value.sessionId), ["ses_a", "ses_b"]);
assert.deepEqual(result.result.filters.sessionIds, ["ses_a", "ses_b"]);
assert.equal(result.result.partitionKeyScoped, false);
index.stop();
});
test("single-session query reads its bucket without scanning unrelated envelopes", async () => {
let unrelatedSessionReads = 0;
const unrelated = envelope("ses_unrelated", "trc_unrelated", 1);
Object.defineProperty(unrelated, "sessionId", {
configurable: true,
enumerable: true,
get() {
unrelatedSessionReads += 1;
return "ses_unrelated";
}
});
const index = createWorkbenchKafkaSessionIndex({
topic: "hwlab.event.v1",
maxEvents: 20,
maxEventsPerSession: 10,
bootstrapTimeoutMs: 5000,
rebuildIntervalMs: 60000,
rebuildCooldownMs: 1000,
scanLimit: 100,
logger: quietLogger(),
queryAll: async () => completeResult([
record(0, "ses_target", "trc_target"),
{
topic: "hwlab.event.v1",
partition: 0,
offset: "1",
key: "ses_target",
value: {
schema: "hwlab.event.v1",
sessionId: "ses_target",
traceId: "trc_steer",
event: { type: "user", traceId: "trc_steer", targetTraceId: "trc_target", commandType: "steer" }
}
},
{
topic: "hwlab.event.v1",
partition: 0,
offset: "2",
key: "ses_unrelated",
timestamp: unrelated.event.createdAt,
valueSha256: "sha-unrelated",
value: unrelated
}
], "3")
});
assert.equal(await index.start(), true);
unrelatedSessionReads = 0;
const result = index.query({ sessionId: "ses_target", traceId: "trc_target", limit: 10 });
assert.equal(result.hit, true);
assert.equal(result.result.events.length, 2);
assert.equal(unrelatedSessionReads, 0);
index.stop();
});
test("process session index falls back instead of returning an incomplete bounded bucket", async () => {
const index = createWorkbenchKafkaSessionIndex({
topic: "hwlab.event.v1",
maxEvents: 10,
maxEventsPerSession: 1,
bootstrapTimeoutMs: 5000,
rebuildIntervalMs: 60000,
rebuildCooldownMs: 1000,
scanLimit: 100,
logger: quietLogger(),
queryAll: async () => completeResult([
record(0, "ses_overflow", "trc_overflow"),
record(1, "ses_overflow", "trc_overflow")
], "2")
});
assert.equal(await index.start(), true);
const result = index.query({ sessionId: "ses_overflow", limit: 10 });
assert.equal(result.hit, false);
assert.equal(result.fallbackReason, "session-capacity-exceeded");
assert.equal(result.warning.blocking, false);
index.stop();
});
test("process session index readiness recovers after a later successful rebuild", async () => {
let queryCount = 0;
let now = 1000;
const index = createWorkbenchKafkaSessionIndex({
topic: "hwlab.event.v1",
maxEvents: 20,
maxEventsPerSession: 10,
bootstrapTimeoutMs: 5000,
rebuildIntervalMs: 60000,
rebuildCooldownMs: 1000,
scanLimit: 100,
nowMs: () => now,
logger: quietLogger(),
queryAll: async () => {
queryCount += 1;
if (queryCount === 1) return {
completionReason: "timeout",
completion: { complete: false, barrierReached: false, retentionStartVerified: true },
reachedEndOffsets: false,
endOffsetsAvailable: true,
endOffsets: [{ partition: 0, startOffset: "0", endOffset: "1" }],
events: []
};
return completeResult([record(0, "ses_recovered", "trc_recovered")], "1");
}
});
assert.equal(await index.start(), false);
assert.equal(index.status().ready, false);
now += 1000;
assert.equal(await index.rebuild("interval"), true);
assert.equal(index.status().ready, true);
assert.equal(await index.start(), true);
assert.equal(index.sessionSummaries({ sessionIds: ["ses_recovered"] }).ok, true);
assert.equal(queryCount, 2);
index.stop();
});
test("Kafka rail projection derives title, user clock and latest-trace terminal status in partition order", () => {
const sessionId = "ses_rail";
const record = (offset, traceId, event) => ({
topic: "hwlab.event.v1",
partition: 2,
offset: String(offset),
key: sessionId,
value: { schema: "hwlab.event.v1", sessionId, hwlabSessionId: sessionId, traceId, event }
});
const summary = workbenchKafkaRailSummary(sessionId, [
record(1, "trc_old", { type: "user", text: "first title", submittedAt: "2026-07-20T00:00:01.000Z" }),
record(2, "trc_old", { type: "result", eventType: "terminal", terminal: true, terminalStatus: "completed", createdAt: "2026-07-20T00:00:05.000Z" }),
record(3, "trc_new", { type: "user", text: "second turn", submittedAt: "2026-07-20T00:01:00.000Z" }),
record(4, "trc_old", { type: "result", eventType: "terminal", terminal: true, terminalStatus: "failed" }),
record(5, "trc_new", { type: "assistant_progress", createdAt: "2026-07-20T00:02:00.000Z" }),
record(6, "trc_new", { type: "result", eventType: "terminal", terminal: true, terminalStatus: "cancelled" })
]);
expect(summary).toMatchObject({
sessionId,
firstUserMessagePreview: "first title",
lastUserMessageAt: "2026-07-20T00:01:00.000Z",
lastTraceId: "trc_new",
status: "canceled",
revision: { topic: "hwlab.event.v1", partition: 2, offset: "6" }
});
});
test("Kafka rail summaries omit sessions until their first valid user event", async () => {
const sessionId = "ses_admission_only";
const index = createWorkbenchKafkaSessionIndex({
topic: "hwlab.event.v1",
maxEvents: 20,
maxEventsPerSession: 10,
bootstrapTimeoutMs: 5000,
rebuildIntervalMs: 60000,
rebuildCooldownMs: 1000,
scanLimit: 100,
logger: quietLogger(),
queryAll: async () => completeResult([{
topic: "hwlab.event.v1",
partition: 0,
offset: "0",
key: sessionId,
value: {
schema: "hwlab.event.v1",
sessionId,
traceId: "trc_admission_only",
event: { type: "backend", text: "admission accepted" }
}
}], "1")
});
expect(await index.start()).toBe(true);
expect(index.sessionSummaries().sessions).toEqual([]);
expect(index.sessionSummaries({ sessionIds: [sessionId] }).sessions).toEqual([]);
index.stop();
});
test("Kafka rail projection follows actual turn delivery when a requested steer arrives after terminal", () => {
const sessionId = "ses_rail_steer";
const record = (offset, traceId, event) => ({
topic: "hwlab.event.v1",
partition: 2,
offset: String(offset),
key: sessionId,
value: { schema: "hwlab.event.v1", sessionId, hwlabSessionId: sessionId, traceId, event }
});
const summary = workbenchKafkaRailSummary(sessionId, [
record(1, "trc_target", { type: "user", text: "initial turn", submittedAt: "2026-07-20T00:00:01.000Z" }),
record(2, "trc_target", { type: "result", eventType: "terminal", terminal: true, terminalStatus: "completed" }),
record(3, "trc_steer", { type: "user", delivery: "turn", requestedDelivery: "steer", targetTraceId: "trc_target", text: "continue as a turn", submittedAt: "2026-07-20T00:00:03.000Z" }),
record(4, "trc_steer", { type: "assistant_progress", createdAt: "2026-07-20T00:00:04.000Z" }),
record(5, "trc_steer", { type: "result", eventType: "terminal", terminal: true, terminalStatus: "completed" })
]);
expect(summary).toMatchObject({
sessionId,
firstUserMessagePreview: "initial turn",
lastUserMessageAt: "2026-07-20T00:00:03.000Z",
lastTraceId: "trc_steer",
status: "completed",
revision: { topic: "hwlab.event.v1", partition: 2, offset: "5" }
});
});
function completeResult(events: ReturnType<typeof record>[], endOffset: string) {
return {
completionReason: "end-offset",
completion: {
complete: true,
barrierReached: true,
retentionStartVerified: true
},
reachedEndOffsets: true,
endOffsetsAvailable: true,
endOffsets: [{ partition: 0, startOffset: "0", endOffset }],
events
};
}
function record(offset: number, sessionId: string, traceId: string) {
const value = envelope(sessionId, traceId, offset + 1);
return {
topic: "hwlab.event.v1",
partition: 0,
offset: String(offset),
key: sessionId,
timestamp: value.event.createdAt,
valueSha256: `sha-${offset}`,
value
};
}
function envelope(sessionId: string, traceId: string, seq: number) {
return {
schema: "hwlab.event.v1",
eventId: `evt_${sessionId}_${seq}`,
sourceEventId: `src_${sessionId}_${seq}`,
sessionId,
traceId,
event: {
type: seq === 1 ? "user" : "assistant",
text: seq === 1 ? `prompt ${sessionId}` : "reply",
sessionId,
traceId,
createdAt: `2026-07-20T00:00:0${seq}.000Z`
}
};
}
function quietLogger() {
return { log() {}, warn() {} };
}