* perf(api): filter peer-run activity in the transcript page query The transcript loaded a raw page, dropped hidden peer-run rows in memory and rescanned when nothing visible was left, so a long delegated stretch cost two queries per hidden page. The page query now joins runs and excludes hidden peer activity in Postgres, so every page is filled with visible rows. The kept block kinds live in one list shared by paging, the in-memory filter and live event forwarding. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YTJRkJrXAS9e3aEFcFYTy8 * test: port the main-conversation journey fix from #126 Journey 5c fails on main since every space got its own main conversation: the lookup picked the Lab conversation instead of the home one. Same change as #126 (including its chart-links warm-up), so this PR can go green; it no-ops once #126 lands. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YTJRkJrXAS9e3aEFcFYTy8 --------- Co-authored-by: Claude <noreply@anthropic.com>
414 lines
12 KiB
TypeScript
414 lines
12 KiB
TypeScript
import type { PrismaClient } from "@rakazo/db";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import {
|
|
isPeerRun,
|
|
loadAllMessages,
|
|
loadMessagePage,
|
|
shouldForwardPeerThreadEvent,
|
|
} from "./thread-message-pages.js";
|
|
|
|
describe("thread message pages", () => {
|
|
it("caches peer-run classification for live events", async () => {
|
|
const findUnique = vi.fn(async () => ({ trigger: "bot_message" }));
|
|
const prisma = { run: { findUnique } } as unknown as PrismaClient;
|
|
const cache = new Map<string, Promise<boolean>>();
|
|
|
|
await expect(isPeerRun(prisma, "run-peer", cache)).resolves.toBe(true);
|
|
await expect(isPeerRun(prisma, "run-peer", cache)).resolves.toBe(true);
|
|
expect(findUnique).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("forwards peer waiting, ask, and text events on an open thread", () => {
|
|
expect(shouldForwardPeerThreadEvent({ type: "run.waiting_input", payload: {} })).toBe(true);
|
|
expect(shouldForwardPeerThreadEvent({ type: "computer.takeover.requested", payload: {} })).toBe(
|
|
true,
|
|
);
|
|
expect(
|
|
shouldForwardPeerThreadEvent({
|
|
type: "thread.message.created",
|
|
payload: { blocks: [{ kind: "ask", text: "Pick one" }] },
|
|
}),
|
|
).toBe(true);
|
|
expect(
|
|
shouldForwardPeerThreadEvent({
|
|
type: "thread.message.created",
|
|
payload: { blocks: [{ kind: "text", text: "peer body" }] },
|
|
}),
|
|
).toBe(true);
|
|
expect(
|
|
shouldForwardPeerThreadEvent({
|
|
type: "thread.progress",
|
|
payload: {},
|
|
}),
|
|
).toBe(false);
|
|
});
|
|
|
|
it("keeps peer text around-page targets and omits peer activity", async () => {
|
|
const findMany = vi.fn(async () => [
|
|
{
|
|
id: "message-user",
|
|
threadId: "thread-1",
|
|
seq: 4,
|
|
role: "bot",
|
|
blocks: [{ kind: "text", text: "Visible answer" }],
|
|
botId: "bot-1",
|
|
replyToMessageId: null,
|
|
runId: "run-user",
|
|
createdAt: new Date("2026-08-16T00:00:04.000Z"),
|
|
},
|
|
{
|
|
id: "message-peer-activity",
|
|
threadId: "thread-1",
|
|
seq: 5,
|
|
role: "bot",
|
|
blocks: [{ kind: "steps", steps: [{ label: "Message bot", count: 1 }] }],
|
|
botId: "bot-1",
|
|
replyToMessageId: null,
|
|
runId: "run-peer",
|
|
createdAt: new Date("2026-08-16T00:00:05.000Z"),
|
|
},
|
|
{
|
|
id: "message-peer-target",
|
|
threadId: "thread-1",
|
|
seq: 6,
|
|
role: "bot",
|
|
blocks: [{ kind: "text", text: "Peer reply" }],
|
|
botId: "bot-1",
|
|
replyToMessageId: null,
|
|
runId: "run-peer",
|
|
createdAt: new Date("2026-08-16T00:00:06.000Z"),
|
|
},
|
|
]);
|
|
const count = vi.fn(async () => 1);
|
|
const runFindMany = vi.fn(async () => [{ id: "run-peer" }]);
|
|
const prisma = {
|
|
message: { findMany, count },
|
|
run: { findMany: runFindMany },
|
|
} as unknown as PrismaClient;
|
|
|
|
const page = await loadMessagePage(prisma, "thread-1", undefined, 4, {
|
|
messageId: "message-peer-target",
|
|
seq: 6,
|
|
});
|
|
|
|
expect(page.messages.map((message) => message.id)).toEqual([
|
|
"message-user",
|
|
"message-peer-target",
|
|
]);
|
|
expect(runFindMany).toHaveBeenCalled();
|
|
});
|
|
|
|
it("keeps peer receipt and text around-page targets in the normal transcript page", async () => {
|
|
const findMany = vi.fn(async () => [
|
|
{
|
|
id: "message-user",
|
|
threadId: "thread-1",
|
|
seq: 4,
|
|
role: "bot",
|
|
blocks: [{ kind: "text", text: "Visible answer" }],
|
|
botId: "bot-1",
|
|
replyToMessageId: null,
|
|
runId: "run-user",
|
|
createdAt: new Date("2026-08-16T00:00:04.000Z"),
|
|
},
|
|
{
|
|
id: "message-peer-receipt",
|
|
threadId: "thread-1",
|
|
seq: 5,
|
|
role: "user",
|
|
blocks: [
|
|
{
|
|
kind: "bot_message_received",
|
|
fromBotId: "bot-2",
|
|
fromBotName: "Coder",
|
|
text: "Done.",
|
|
},
|
|
],
|
|
botId: null,
|
|
replyToMessageId: null,
|
|
runId: "run-peer",
|
|
createdAt: new Date("2026-08-16T00:00:05.000Z"),
|
|
},
|
|
{
|
|
id: "message-peer-text",
|
|
threadId: "thread-1",
|
|
seq: 6,
|
|
role: "bot",
|
|
blocks: [{ kind: "text", text: "Peer reply" }],
|
|
botId: "bot-1",
|
|
replyToMessageId: null,
|
|
runId: "run-peer",
|
|
createdAt: new Date("2026-08-16T00:00:06.000Z"),
|
|
},
|
|
]);
|
|
const count = vi.fn(async () => 0);
|
|
const prisma = {
|
|
message: { findMany, count },
|
|
run: { findMany: vi.fn(async () => [{ id: "run-peer" }]) },
|
|
} as unknown as PrismaClient;
|
|
|
|
const page = await loadMessagePage(prisma, "thread-1", undefined, 4, {
|
|
messageId: "message-peer-receipt",
|
|
seq: 5,
|
|
});
|
|
|
|
expect(page.messages.map((message) => message.id)).toEqual([
|
|
"message-user",
|
|
"message-peer-receipt",
|
|
"message-peer-text",
|
|
]);
|
|
});
|
|
|
|
it("returns peer-run output for the dedicated bot messages view", async () => {
|
|
const findMany = vi.fn(async () => [
|
|
{
|
|
id: "message-peer",
|
|
threadId: "thread-1",
|
|
seq: 1,
|
|
role: "bot",
|
|
blocks: [{ kind: "text", text: "Peer reply" }],
|
|
botId: "bot-1",
|
|
replyToMessageId: null,
|
|
runId: "run-peer",
|
|
createdAt: new Date("2026-08-16T00:00:01.000Z"),
|
|
},
|
|
]);
|
|
const prisma = {
|
|
message: { findMany, count: vi.fn(async () => 0) },
|
|
} as unknown as PrismaClient;
|
|
|
|
const page = await loadMessagePage(
|
|
prisma,
|
|
"thread-1",
|
|
undefined,
|
|
2,
|
|
{ messageId: "message-peer", seq: 1 },
|
|
true,
|
|
);
|
|
|
|
expect(page.messages.map((message) => message.id)).toEqual(["message-peer"]);
|
|
});
|
|
|
|
it("scans past a receipt-only page so web can reach older user-visible rows", async () => {
|
|
const receiptRows = [
|
|
{
|
|
id: "message-receipt-b",
|
|
threadId: "thread-1",
|
|
seq: 3,
|
|
role: "user",
|
|
blocks: [
|
|
{
|
|
kind: "bot_message_received",
|
|
fromBotId: "bot-2",
|
|
fromBotName: "Coder",
|
|
text: "Done.",
|
|
},
|
|
],
|
|
botId: null,
|
|
replyToMessageId: null,
|
|
runId: "run-peer",
|
|
createdAt: new Date("2026-08-16T00:00:03.000Z"),
|
|
},
|
|
{
|
|
id: "message-receipt-a",
|
|
threadId: "thread-1",
|
|
seq: 2,
|
|
role: "user",
|
|
blocks: [
|
|
{
|
|
kind: "bot_message_sent",
|
|
toBotId: "bot-2",
|
|
toBotName: "Coder",
|
|
text: "Check this.",
|
|
},
|
|
],
|
|
botId: null,
|
|
replyToMessageId: null,
|
|
runId: "run-user",
|
|
createdAt: new Date("2026-08-16T00:00:02.000Z"),
|
|
},
|
|
{
|
|
id: "message-lookahead",
|
|
threadId: "thread-1",
|
|
seq: 1,
|
|
role: "bot",
|
|
blocks: [{ kind: "text", text: "Older visible answer" }],
|
|
botId: "bot-1",
|
|
replyToMessageId: null,
|
|
runId: "run-user",
|
|
createdAt: new Date("2026-08-16T00:00:01.000Z"),
|
|
},
|
|
];
|
|
const olderRows = [
|
|
{
|
|
id: "message-user",
|
|
threadId: "thread-1",
|
|
seq: 1,
|
|
role: "bot",
|
|
blocks: [{ kind: "text", text: "Older visible answer" }],
|
|
botId: "bot-1",
|
|
replyToMessageId: null,
|
|
runId: "run-user",
|
|
createdAt: new Date("2026-08-16T00:00:01.000Z"),
|
|
},
|
|
];
|
|
const queryRaw = vi.fn().mockResolvedValueOnce(receiptRows).mockResolvedValueOnce(olderRows);
|
|
const prisma = { $queryRaw: queryRaw } as unknown as PrismaClient;
|
|
|
|
const page = await loadMessagePage(prisma, "thread-1", undefined, 2);
|
|
|
|
expect(page.messages.map((message) => message.id)).toEqual(["message-user"]);
|
|
expect(queryRaw).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it("returns a receipt-only page when the client displays peer receipts", async () => {
|
|
const receipt = (seq: number) => ({
|
|
id: `message-receipt-${seq}`,
|
|
threadId: "thread-1",
|
|
seq,
|
|
role: "user",
|
|
blocks: [
|
|
{
|
|
kind: "bot_message_received",
|
|
fromBotId: "bot-2",
|
|
fromBotName: "Coder",
|
|
text: "Done.",
|
|
},
|
|
],
|
|
botId: null,
|
|
replyToMessageId: null,
|
|
runId: "run-peer",
|
|
createdAt: new Date(`2026-08-16T00:00:0${seq}.000Z`),
|
|
});
|
|
const queryRaw = vi.fn(async () => [receipt(3), receipt(2), receipt(1)]);
|
|
const prisma = { $queryRaw: queryRaw } as unknown as PrismaClient;
|
|
|
|
const page = await loadMessagePage(prisma, "thread-1", undefined, 2, undefined, false, true);
|
|
|
|
expect(page.messages.map((message) => message.id)).toEqual([
|
|
"message-receipt-2",
|
|
"message-receipt-3",
|
|
]);
|
|
expect(page.olderCursor).toBe(2);
|
|
expect(queryRaw).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("queries the bot messages view before the cursor as an ascending bounded page", async () => {
|
|
const findMany = vi.fn(async () =>
|
|
[5, 4, 3].map((seq) => ({
|
|
id: `message-${seq}`,
|
|
threadId: "thread-1",
|
|
seq,
|
|
role: "bot",
|
|
blocks: [{ kind: "text", text: String(seq) }],
|
|
runId: null,
|
|
createdAt: new Date(`2026-08-16T00:00:0${seq}.000Z`),
|
|
})),
|
|
);
|
|
const prisma = { message: { findMany } } as unknown as PrismaClient;
|
|
|
|
const page = await loadMessagePage(prisma, "thread-1", 6, 2, undefined, true);
|
|
|
|
expect(findMany).toHaveBeenCalledWith({
|
|
where: { threadId: "thread-1", seq: { lt: 6 } },
|
|
orderBy: { seq: "desc" },
|
|
take: 3,
|
|
});
|
|
expect(page.messages.map((message) => message.seq)).toEqual([4, 5]);
|
|
expect(page.olderCursor).toBe(4);
|
|
});
|
|
|
|
it("ends pagination when the database returns no lookahead row", async () => {
|
|
const queryRaw = vi.fn(async () => [
|
|
{
|
|
id: "message-0",
|
|
threadId: "thread-1",
|
|
seq: 0,
|
|
role: "user",
|
|
blocks: [],
|
|
runId: null,
|
|
createdAt: new Date("2026-08-16T00:00:00.000Z"),
|
|
},
|
|
]);
|
|
const prisma = { $queryRaw: queryRaw } as unknown as PrismaClient;
|
|
|
|
const page = await loadMessagePage(prisma, "thread-1", 1, 2);
|
|
|
|
expect(page.messages.map((message) => message.seq)).toEqual([0]);
|
|
expect(page.olderCursor).toBeNull();
|
|
});
|
|
|
|
it("loads a page around a target sequence", async () => {
|
|
const findFirst = vi.fn(async () => ({ seq: 5 }));
|
|
const findMany = vi
|
|
.fn()
|
|
.mockResolvedValueOnce([
|
|
{
|
|
id: "message-3",
|
|
threadId: "thread-1",
|
|
seq: 3,
|
|
role: "bot",
|
|
blocks: [],
|
|
runId: null,
|
|
createdAt: new Date(),
|
|
},
|
|
{
|
|
id: "message-4",
|
|
threadId: "thread-1",
|
|
seq: 4,
|
|
role: "bot",
|
|
blocks: [],
|
|
runId: null,
|
|
createdAt: new Date(),
|
|
},
|
|
{
|
|
id: "message-5",
|
|
threadId: "thread-1",
|
|
seq: 5,
|
|
role: "bot",
|
|
blocks: [],
|
|
runId: null,
|
|
createdAt: new Date(),
|
|
},
|
|
])
|
|
.mockResolvedValueOnce(1);
|
|
const count = vi.fn(async () => 1);
|
|
const prisma = {
|
|
message: { findFirst, findMany, count },
|
|
} as unknown as PrismaClient;
|
|
|
|
const page = await loadMessagePage(prisma, "thread-1", undefined, 4, { seq: 5 });
|
|
|
|
expect(page.messages.map((message) => message.seq)).toEqual([3, 4, 5]);
|
|
expect(page.olderCursor).toBe(3);
|
|
expect(findMany).toHaveBeenCalledWith({
|
|
where: { threadId: "thread-1", seq: { gte: 3, lte: 7 } },
|
|
orderBy: { seq: "asc" },
|
|
take: 4,
|
|
});
|
|
});
|
|
|
|
it("collects bounded pages into chronological export order", async () => {
|
|
const row = (seq: number) => ({
|
|
id: `message-${seq}`,
|
|
threadId: "thread-1",
|
|
seq,
|
|
role: "bot",
|
|
blocks: [],
|
|
runId: null,
|
|
createdAt: new Date("2026-08-16T00:00:00.000Z"),
|
|
});
|
|
const findMany = vi
|
|
.fn()
|
|
.mockResolvedValueOnce([row(4), row(3), row(2)])
|
|
.mockResolvedValueOnce([row(2), row(1), row(0)])
|
|
.mockResolvedValueOnce([row(0)]);
|
|
const prisma = { message: { findMany } } as unknown as PrismaClient;
|
|
|
|
const messages = await loadAllMessages(prisma, "thread-1", 2);
|
|
|
|
expect(messages.map((message) => message.seq)).toEqual([0, 1, 2, 3, 4]);
|
|
expect(findMany.mock.calls.map(([query]) => query.where.seq?.lt)).toEqual([undefined, 3, 1]);
|
|
});
|
|
});
|