import { describe, expect, it } from "vitest"; import type { StreamItem } from "@/types/stream"; import { buildAgentStreamRenderModel } from "./agent-stream-render-model"; function createTimestamp(seed: number): Date { return new Date(`2026-01-01T00:00:${seed.toString().padStart(2, "0")}.000Z`); } function userMessage(id: string, seed: number): StreamItem { return { kind: "user_message", id, text: id, timestamp: createTimestamp(seed), }; } function assistantMessage(id: string, seed: number): StreamItem { return { kind: "assistant_message", id, text: id, timestamp: createTimestamp(seed), }; } describe("buildAgentStreamRenderModel", () => { it("keeps head separate from committed history on desktop web", () => { const tail: StreamItem[] = []; for (let index = 0; index < 60; index += 1) { const seed = index * 2; tail.push(userMessage(`u${index}`, seed + 1)); tail.push(assistantMessage(`a${index}`, seed + 2)); } const head = [assistantMessage("live-a", 121)]; const model = buildAgentStreamRenderModel({ tail, head, platform: "web", isMobileBreakpoint: false, }); expect(model.segments.historyVirtualized.length).toBeGreaterThan(0); expect(model.segments.historyMounted.length).toBeGreaterThan(0); expect(model.segments.liveHead.map((item) => item.id)).toEqual(["live-a"]); expect(model.history).not.toContain(head[0]); }); it("keeps the full committed tail mounted on mobile web", () => { const tail = [userMessage("u1", 1), assistantMessage("a1", 2)]; const head = [assistantMessage("live-a", 3)]; const model = buildAgentStreamRenderModel({ tail, head, platform: "web", isMobileBreakpoint: true, }); expect(model.segments.historyVirtualized).toHaveLength(0); expect(model.segments.historyMounted).toBe(tail); expect(model.segments.liveHead).toBe(head); }); it("reuses ordered committed history when only the live head changes", () => { const tail = [userMessage("u1", 1), assistantMessage("a1", 2)]; const firstHead = [assistantMessage("live-a", 3)]; const secondHead = [assistantMessage("live-b", 4)]; const first = buildAgentStreamRenderModel({ tail, head: firstHead, platform: "native", isMobileBreakpoint: false, }); const second = buildAgentStreamRenderModel({ tail, head: secondHead, platform: "native", isMobileBreakpoint: false, }); expect(first.history).toBe(second.history); expect(first.segments.historyMounted).toBe(second.segments.historyMounted); expect(second.segments.liveHead.map((item) => item.id)).toEqual(["live-b"]); }); });