Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com> Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Co-authored-by: Affan Ali <93028901+affanali2k3@users.noreply.github.com> Co-authored-by: affanali2k3 <affanalikhanxx@gmail.com> Co-authored-by: Frank <frank@anoma.ly> Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com> Co-authored-by: James Long <longster@gmail.com> Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Jay V <air@live.ca> Co-authored-by: Dax Raad <d@ironbay.co> Co-authored-by: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Co-authored-by: OpeOginni <107570612+OpeOginni@users.noreply.github.com> Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com> Co-authored-by: Ben Guthrie <benjee.012@gmail.com> Co-authored-by: Dax <mail@thdxr.com> Co-authored-by: Filip <34747899+neriousy@users.noreply.github.com>
1127 lines
45 KiB
TypeScript
1127 lines
45 KiB
TypeScript
import { describe, expect, test } from "bun:test"
|
|
import type { retry } from "@opencode-ai/core/util/retry"
|
|
import type { Message, OpencodeClient, Part, Session } from "@opencode-ai/sdk/v2/client"
|
|
import { createServerSession } from "./server-session"
|
|
|
|
const session = (id: string, parentID?: string): Session => ({
|
|
id,
|
|
slug: id,
|
|
projectID: "project",
|
|
directory: "/repo",
|
|
title: id,
|
|
version: "1",
|
|
parentID,
|
|
time: { created: 1, updated: 1 },
|
|
})
|
|
|
|
type UserMessage = Extract<Message, { role: "user" }>
|
|
type TextPart = Extract<Part, { type: "text" }>
|
|
type MessageResponse = {
|
|
data: { info: Message; parts: Part[] }[]
|
|
response: { headers: Headers }
|
|
}
|
|
|
|
const userMessage = (id: string, input: Partial<UserMessage> = {}): UserMessage => ({
|
|
id,
|
|
sessionID: "child",
|
|
role: "user",
|
|
time: { created: 1 },
|
|
agent: "build",
|
|
model: { providerID: "provider", modelID: "model" },
|
|
...input,
|
|
})
|
|
|
|
const textPart = (messageID: string, input: Partial<TextPart> = {}): TextPart => ({
|
|
id: "part",
|
|
sessionID: "child",
|
|
messageID,
|
|
type: "text",
|
|
text: "text",
|
|
...input,
|
|
})
|
|
|
|
const response = (data: MessageResponse["data"] = [], cursor?: string): MessageResponse => ({
|
|
data,
|
|
response: { headers: new Headers(cursor ? { "x-next-cursor": cursor } : undefined) },
|
|
})
|
|
|
|
const deferredResponse = () => Promise.withResolvers<MessageResponse>()
|
|
|
|
function messageClient(...responses: Array<MessageResponse | Promise<MessageResponse>>) {
|
|
let index = 0
|
|
const requests: unknown[] = []
|
|
const waiting = new Map<number, () => void>()
|
|
const client = {
|
|
session: {
|
|
get: async () => ({ data: session("child", "root") }),
|
|
messages: (input: unknown) => {
|
|
requests.push(input)
|
|
waiting.get(requests.length)?.()
|
|
waiting.delete(requests.length)
|
|
return responses[index++]
|
|
},
|
|
},
|
|
} as unknown as OpencodeClient
|
|
return Object.assign(client, {
|
|
requests,
|
|
requested(count: number) {
|
|
if (requests.length >= count) return Promise.resolve()
|
|
return new Promise<void>((resolve) => waiting.set(count, resolve))
|
|
},
|
|
})
|
|
}
|
|
|
|
const retryImmediately: typeof retry = async (task, options = {}) => {
|
|
const attempts = options.attempts ?? 3
|
|
for (let attempt = 0; ; attempt++) {
|
|
try {
|
|
return await task()
|
|
} catch (error) {
|
|
if (attempt === attempts - 1) throw error
|
|
}
|
|
}
|
|
}
|
|
|
|
function setup(sessions: Record<string, Session>) {
|
|
const get: unknown[] = []
|
|
const messages: unknown[] = []
|
|
const client = {
|
|
session: {
|
|
get: async (input: unknown) => {
|
|
get.push(input)
|
|
const id = (input as { sessionID: string }).sessionID
|
|
return { data: sessions[id] }
|
|
},
|
|
messages: async (input: unknown) => {
|
|
messages.push(input)
|
|
return response()
|
|
},
|
|
diff: async () => ({ data: [] }),
|
|
todo: async () => ({ data: [] }),
|
|
},
|
|
} as unknown as OpencodeClient
|
|
return { get, messages, store: createServerSession(client) }
|
|
}
|
|
|
|
describe("server session", () => {
|
|
test("resolves lineage by session ID without directory", async () => {
|
|
const ctx = setup({ child: session("child", "root"), root: session("root") })
|
|
|
|
const result = await ctx.store.lineage.resolve("child")
|
|
|
|
expect(result.root.id).toBe("root")
|
|
expect(ctx.get).toEqual([{ sessionID: "child" }, { sessionID: "root" }])
|
|
expect(ctx.store.lineage.peek("child")).toEqual(result)
|
|
})
|
|
|
|
test("loads session content through the server client", async () => {
|
|
const ctx = setup({ root: session("root") })
|
|
|
|
await ctx.store.sync("root")
|
|
|
|
expect(ctx.get).toEqual([{ sessionID: "root" }])
|
|
expect(ctx.messages).toEqual([{ sessionID: "root", limit: 2, before: undefined }])
|
|
expect(ctx.store.data.message.root).toEqual([])
|
|
})
|
|
|
|
test("merges live events into the initial page", async () => {
|
|
const pending = deferredResponse()
|
|
const user = userMessage("message-1")
|
|
const live = userMessage("message-2", { time: { created: 2 } })
|
|
const livePart = textPart(live.id, { text: "live" })
|
|
const store = createServerSession(messageClient(pending.promise))
|
|
const loading = store.sync("child")
|
|
|
|
store.apply({ type: "message.updated", properties: { info: live } })
|
|
store.apply({ type: "message.part.updated", properties: { sessionID: "child", part: livePart, time: 2 } })
|
|
pending.resolve(response([{ info: user, parts: [] }]))
|
|
await loading
|
|
|
|
expect(store.data.message.child).toEqual([user, live])
|
|
expect(store.data.part[live.id]).toEqual([livePart])
|
|
})
|
|
|
|
test("preserves same-ID live updates over the initial page", async () => {
|
|
const pending = deferredResponse()
|
|
const fetched = userMessage("message")
|
|
const fetchedPart = textPart(fetched.id, { text: "fetched" })
|
|
const live = { ...fetched, time: { created: 2 } }
|
|
const livePart = { ...fetchedPart, text: "live" }
|
|
const store = createServerSession(messageClient(pending.promise))
|
|
const loading = store.sync("child")
|
|
|
|
store.apply({ type: "message.updated", properties: { info: live } })
|
|
store.apply({ type: "message.part.updated", properties: { sessionID: "child", part: livePart, time: 2 } })
|
|
pending.resolve(response([{ info: fetched, parts: [fetchedPart] }]))
|
|
await loading
|
|
|
|
expect(store.data.message.child).toEqual([live])
|
|
expect(store.data.part[live.id]).toEqual([livePart])
|
|
})
|
|
|
|
test("preserves removals received during the initial load", async () => {
|
|
const pending = deferredResponse()
|
|
const removed = userMessage("message-1")
|
|
const kept = { ...removed, id: "message-2" }
|
|
const part = textPart(kept.id, { text: "removed" })
|
|
const store = createServerSession(messageClient(pending.promise))
|
|
const loading = store.sync("child")
|
|
|
|
store.apply({ type: "message.removed", properties: { sessionID: "child", messageID: removed.id } })
|
|
store.apply({
|
|
type: "message.part.removed",
|
|
properties: { sessionID: "child", messageID: kept.id, partID: part.id },
|
|
})
|
|
pending.resolve(
|
|
response([
|
|
{ info: removed, parts: [] },
|
|
{ info: kept, parts: [part] },
|
|
]),
|
|
)
|
|
await loading
|
|
|
|
expect(store.data.message.child).toEqual([kept])
|
|
expect(store.data.part[kept.id]).toBeUndefined()
|
|
})
|
|
|
|
test("keeps removal tracking isolated across load generations", async () => {
|
|
const firstResponse = deferredResponse()
|
|
const secondResponse = deferredResponse()
|
|
const message = userMessage("message")
|
|
const store = createServerSession(messageClient(firstResponse.promise, secondResponse.promise))
|
|
const first = store.sync("child")
|
|
|
|
store.apply({ type: "message.removed", properties: { sessionID: "child", messageID: message.id } })
|
|
store.apply({
|
|
type: "session.deleted",
|
|
properties: { sessionID: "child", info: session("child", "root") },
|
|
})
|
|
const second = store.sync("child")
|
|
|
|
firstResponse.resolve(response())
|
|
await first
|
|
secondResponse.resolve(response([{ info: message, parts: [] }]))
|
|
await second
|
|
|
|
expect(store.data.message.child).toEqual([message])
|
|
})
|
|
|
|
test("tracks removals in a replacement load generation", async () => {
|
|
const firstResponse = deferredResponse()
|
|
const secondResponse = deferredResponse()
|
|
const message = userMessage("message")
|
|
const store = createServerSession(messageClient(firstResponse.promise, secondResponse.promise))
|
|
const first = store.sync("child")
|
|
store.apply({
|
|
type: "session.deleted",
|
|
properties: { sessionID: "child", info: session("child", "root") },
|
|
})
|
|
const second = store.sync("child")
|
|
|
|
store.apply({ type: "message.removed", properties: { sessionID: "child", messageID: message.id } })
|
|
firstResponse.resolve(response())
|
|
await first
|
|
secondResponse.resolve(response([{ info: message, parts: [] }]))
|
|
await second
|
|
|
|
expect(store.data.message.child).toEqual([])
|
|
})
|
|
|
|
test("preserves remove then re-add when a refresh omits the message", async () => {
|
|
const pending = deferredResponse()
|
|
const message = userMessage("message")
|
|
const store = createServerSession(messageClient(response([{ info: message, parts: [] }]), pending.promise))
|
|
await store.sync("child")
|
|
const refreshing = store.sync("child", { force: true })
|
|
|
|
store.apply({ type: "message.removed", properties: { sessionID: "child", messageID: message.id } })
|
|
store.apply({ type: "message.updated", properties: { info: message } })
|
|
pending.resolve(response())
|
|
await refreshing
|
|
|
|
expect(store.data.message.child).toEqual([message])
|
|
})
|
|
|
|
test("preserves a re-added message without restoring removed parts", async () => {
|
|
const pending = deferredResponse()
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id, { text: "stale" })
|
|
const store = createServerSession(messageClient(response([{ info: message, parts: [] }]), pending.promise))
|
|
await store.sync("child")
|
|
const refreshing = store.sync("child", { force: true })
|
|
|
|
store.apply({ type: "message.removed", properties: { sessionID: "child", messageID: message.id } })
|
|
store.apply({ type: "message.updated", properties: { info: message } })
|
|
pending.resolve(response([{ info: message, parts: [part] }]))
|
|
await refreshing
|
|
|
|
expect(store.data.message.child).toEqual([message])
|
|
expect(store.data.part[message.id]).toBeUndefined()
|
|
})
|
|
|
|
test("preserves optimistic parts re-added after removal during a refresh", async () => {
|
|
const pending = deferredResponse()
|
|
const message = userMessage("message")
|
|
const stale = textPart(message.id, { id: "stale", text: "stale" })
|
|
const part = textPart(message.id, { id: "optimistic", text: "optimistic" })
|
|
const store = createServerSession(
|
|
messageClient(response([{ info: message, parts: [] }]), pending.promise, response()),
|
|
)
|
|
await store.sync("child")
|
|
const refreshing = store.sync("child", { force: true })
|
|
|
|
store.apply({ type: "message.removed", properties: { sessionID: "child", messageID: message.id } })
|
|
store.optimistic.add({ sessionID: "child", message, parts: [part] })
|
|
pending.resolve(response([{ info: message, parts: [stale] }]))
|
|
await refreshing
|
|
|
|
expect(store.data.message.child).toEqual([message])
|
|
expect(store.data.part[message.id]).toEqual([part])
|
|
|
|
await store.sync("child", { force: true })
|
|
expect(store.data.message.child).toEqual([message])
|
|
expect(store.data.part[message.id]).toEqual([part])
|
|
})
|
|
|
|
test("drops stale event content omitted by a complete initial page", async () => {
|
|
const stale = userMessage("stale")
|
|
const store = createServerSession(messageClient(response()))
|
|
store.apply({ type: "message.updated", properties: { info: stale } })
|
|
|
|
await store.sync("child")
|
|
|
|
expect(store.data.message.child).toEqual([])
|
|
})
|
|
|
|
test("preserves event content outside an incomplete initial page", async () => {
|
|
const live = userMessage("message-1")
|
|
const fetched = userMessage("message-2", { time: { created: 2 } })
|
|
const store = createServerSession(messageClient(response([{ info: fetched, parts: [] }], "older")))
|
|
store.apply({ type: "message.updated", properties: { info: live } })
|
|
|
|
await store.sync("child")
|
|
|
|
expect(store.data.message.child).toEqual([live, fetched])
|
|
})
|
|
|
|
test("does not restore removed optimistic content on refresh", async () => {
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id, { text: "removed" })
|
|
const kept = { ...message, id: "kept" }
|
|
const keptPart = { ...part, id: "kept-part", messageID: kept.id }
|
|
const store = createServerSession(messageClient(response([{ info: kept, parts: [] }])))
|
|
store.optimistic.add({ sessionID: "child", message, parts: [part] })
|
|
store.optimistic.add({ sessionID: "child", message: kept, parts: [keptPart] })
|
|
|
|
store.apply({ type: "message.removed", properties: { sessionID: "child", messageID: message.id } })
|
|
store.apply({
|
|
type: "message.part.removed",
|
|
properties: { sessionID: "child", messageID: kept.id, partID: keptPart.id },
|
|
})
|
|
await store.sync("child", { force: true })
|
|
|
|
expect(store.data.message.child).toEqual([kept])
|
|
expect(store.data.part[message.id]).toBeUndefined()
|
|
expect(store.data.part[kept.id]).toBeUndefined()
|
|
})
|
|
|
|
test("replaces confirmed optimistic content with the initial page", async () => {
|
|
const optimistic = userMessage("message")
|
|
const fetched = { ...optimistic, time: { created: 2 } }
|
|
const store = createServerSession(messageClient(response([{ info: fetched, parts: [] }])))
|
|
store.optimistic.add({ sessionID: "child", message: optimistic, parts: [] })
|
|
|
|
await store.sync("child")
|
|
|
|
expect(store.data.message.child).toEqual([fetched])
|
|
})
|
|
|
|
test("replaces a confirmed optimistic part with fetched content", async () => {
|
|
const pending = deferredResponse()
|
|
const message = userMessage("message")
|
|
const optimistic = textPart(message.id, { text: "optimistic" })
|
|
const fetched = { ...optimistic, text: "fetched" }
|
|
const store = createServerSession(messageClient(pending.promise))
|
|
const loading = store.sync("child")
|
|
|
|
store.optimistic.add({ sessionID: "child", message, parts: [optimistic] })
|
|
pending.resolve(response([{ info: message, parts: [fetched] }]))
|
|
await loading
|
|
|
|
expect(store.data.part[message.id]).toEqual([fetched])
|
|
})
|
|
|
|
test("rolls back only unconfirmed optimistic parts", async () => {
|
|
const pending = deferredResponse()
|
|
const message = userMessage("message")
|
|
const confirmed = textPart(message.id, { id: "confirmed", text: "confirmed" })
|
|
const pendingPart = textPart(message.id, { id: "pending", text: "pending" })
|
|
const store = createServerSession(messageClient(pending.promise))
|
|
const loading = store.sync("child")
|
|
store.optimistic.add({ sessionID: "child", message, parts: [confirmed, pendingPart] })
|
|
|
|
pending.resolve(response([{ info: message, parts: [confirmed] }]))
|
|
await loading
|
|
store.optimistic.remove({ sessionID: "child", messageID: message.id })
|
|
|
|
expect(store.data.message.child).toEqual([message])
|
|
expect(store.data.part[message.id]).toEqual([confirmed])
|
|
})
|
|
|
|
test("updates confirmed optimistic parts from later pages", async () => {
|
|
const message = userMessage("message")
|
|
const confirmed = textPart(message.id, { id: "confirmed", text: "first" })
|
|
const updated = { ...confirmed, text: "updated" }
|
|
const pendingPart = textPart(message.id, { id: "pending", text: "pending" })
|
|
const store = createServerSession(
|
|
messageClient(response([{ info: message, parts: [confirmed] }]), response([{ info: message, parts: [updated] }])),
|
|
)
|
|
store.optimistic.add({ sessionID: "child", message, parts: [confirmed, pendingPart] })
|
|
await store.sync("child")
|
|
|
|
await store.sync("child", { force: true })
|
|
store.optimistic.remove({ sessionID: "child", messageID: message.id })
|
|
|
|
expect(store.data.part[message.id]).toEqual([updated])
|
|
})
|
|
|
|
test("does not restore a confirmed optimistic part after its removal event", async () => {
|
|
const message = userMessage("message")
|
|
const confirmed = textPart(message.id, { id: "confirmed", text: "confirmed" })
|
|
const pendingPart = textPart(message.id, { id: "pending", text: "pending" })
|
|
const store = createServerSession(
|
|
messageClient(response([{ info: message, parts: [confirmed] }]), response([{ info: message, parts: [] }])),
|
|
)
|
|
store.optimistic.add({ sessionID: "child", message, parts: [confirmed, pendingPart] })
|
|
await store.sync("child")
|
|
store.apply({
|
|
type: "message.part.removed",
|
|
properties: { sessionID: "child", messageID: message.id, partID: confirmed.id },
|
|
})
|
|
|
|
await store.sync("child", { force: true })
|
|
|
|
expect(store.data.part[message.id]).toEqual([pendingPart])
|
|
})
|
|
|
|
test("clears delta buffers when removing optimistic content", () => {
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id, { text: "optimistic" })
|
|
const store = setup({ child: session("child") }).store
|
|
store.optimistic.add({ sessionID: "child", message, parts: [part] })
|
|
store.apply({
|
|
type: "message.part.delta",
|
|
properties: { sessionID: "child", messageID: message.id, partID: part.id, field: "text", delta: " delta" },
|
|
})
|
|
|
|
store.optimistic.remove({ sessionID: "child", messageID: message.id })
|
|
|
|
expect(store.data.part[message.id]).toBeUndefined()
|
|
expect(store.data.part_text_accum_delta[part.id]).toBeUndefined()
|
|
})
|
|
|
|
test("does not remove content confirmed by a message event", () => {
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id)
|
|
const store = setup({ child: session("child") }).store
|
|
store.optimistic.add({ sessionID: "child", message, parts: [part] })
|
|
store.apply({ type: "message.updated", properties: { sessionID: "child", info: message } })
|
|
|
|
store.optimistic.remove({ sessionID: "child", messageID: message.id })
|
|
|
|
expect(store.data.message.child).toEqual([message])
|
|
expect(store.data.part[message.id]).toBeUndefined()
|
|
})
|
|
|
|
test("does not remove parts confirmed by part events", () => {
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id)
|
|
const store = setup({ child: session("child") }).store
|
|
store.optimistic.add({ sessionID: "child", message, parts: [part] })
|
|
store.apply({ type: "message.updated", properties: { sessionID: "child", info: message } })
|
|
store.apply({ type: "message.part.updated", properties: { sessionID: "child", part, time: 2 } })
|
|
|
|
store.optimistic.remove({ sessionID: "child", messageID: message.id })
|
|
|
|
expect(store.data.message.child).toEqual([message])
|
|
expect(store.data.part[message.id]).toEqual([part])
|
|
})
|
|
|
|
test("treats a part event as confirmation when it precedes the message event", () => {
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id)
|
|
const store = setup({ child: session("child") }).store
|
|
store.optimistic.add({ sessionID: "child", message, parts: [part] })
|
|
store.apply({ type: "message.part.updated", properties: { sessionID: "child", part, time: 2 } })
|
|
|
|
store.optimistic.remove({ sessionID: "child", messageID: message.id })
|
|
|
|
expect(store.data.message.child).toEqual([message])
|
|
expect(store.data.part[message.id]).toEqual([part])
|
|
})
|
|
|
|
test("clears stale parts when the initial page has none", async () => {
|
|
const pending = deferredResponse()
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id, { text: "stale" })
|
|
const store = createServerSession(messageClient(pending.promise))
|
|
store.apply({ type: "message.updated", properties: { info: message } })
|
|
store.apply({ type: "message.part.updated", properties: { sessionID: "child", part, time: 1 } })
|
|
const loading = store.sync("child")
|
|
|
|
pending.resolve(response([{ info: message, parts: [] }]))
|
|
await loading
|
|
|
|
expect(store.data.part[message.id]).toBeUndefined()
|
|
})
|
|
|
|
test("clears delta buffers for parts omitted by the initial page", async () => {
|
|
const pending = deferredResponse()
|
|
const message = userMessage("message")
|
|
const kept = textPart(message.id, { id: "part-1", text: "kept" })
|
|
const removed: Part = { ...kept, id: "part-2", text: "removed" }
|
|
const store = createServerSession(messageClient(pending.promise))
|
|
store.apply({ type: "message.updated", properties: { info: message } })
|
|
store.apply({ type: "message.part.updated", properties: { sessionID: "child", part: kept, time: 1 } })
|
|
store.apply({ type: "message.part.updated", properties: { sessionID: "child", part: removed, time: 1 } })
|
|
store.apply({
|
|
type: "message.part.delta",
|
|
properties: { sessionID: "child", messageID: message.id, partID: removed.id, field: "text", delta: " delta" },
|
|
})
|
|
const loading = store.sync("child")
|
|
|
|
pending.resolve(response([{ info: message, parts: [kept] }]))
|
|
await loading
|
|
|
|
expect(store.data.part[message.id]).toEqual([kept])
|
|
expect(store.data.part_text_accum_delta[removed.id]).toBeUndefined()
|
|
})
|
|
|
|
test("clears a stale delta buffer when a refresh replaces its part", async () => {
|
|
const message = userMessage("message")
|
|
const stale = textPart(message.id, { text: "stale" })
|
|
const fetched = { ...stale, text: "fetched" }
|
|
const store = createServerSession(
|
|
messageClient(response([{ info: message, parts: [stale] }]), response([{ info: message, parts: [fetched] }])),
|
|
)
|
|
await store.sync("child")
|
|
store.apply({
|
|
type: "message.part.delta",
|
|
properties: { sessionID: "child", messageID: message.id, partID: stale.id, field: "text", delta: " delta" },
|
|
})
|
|
|
|
await store.sync("child", { force: true })
|
|
|
|
expect(store.data.part[message.id]).toEqual([fetched])
|
|
expect(store.data.part_text_accum_delta[stale.id]).toBeUndefined()
|
|
})
|
|
|
|
test("preserves a non-durable delta received before refresh", async () => {
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id, { text: "stale" })
|
|
const store = createServerSession(
|
|
messageClient(response([{ info: message, parts: [part] }]), response([{ info: message, parts: [{ ...part }] }])),
|
|
)
|
|
await store.sync("child")
|
|
store.apply({
|
|
type: "message.part.delta",
|
|
properties: { sessionID: "child", messageID: message.id, partID: part.id, field: "text", delta: " delta" },
|
|
})
|
|
|
|
await store.sync("child", { force: true })
|
|
|
|
expect(store.data.part[message.id]).toEqual([{ ...part, text: "stale delta" }])
|
|
expect(store.data.part_text_accum_delta[part.id]).toBe("stale delta")
|
|
})
|
|
|
|
test("accepts fetched text that intentionally replaces an accumulated prefix", async () => {
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id, { text: "abc" })
|
|
const fetched = { ...part, text: "ab" }
|
|
const store = createServerSession(
|
|
messageClient(response([{ info: message, parts: [part] }]), response([{ info: message, parts: [fetched] }])),
|
|
)
|
|
await store.sync("child")
|
|
store.apply({
|
|
type: "message.part.delta",
|
|
properties: { sessionID: "child", messageID: message.id, partID: part.id, field: "text", delta: "def" },
|
|
})
|
|
|
|
await store.sync("child", { force: true })
|
|
|
|
expect(store.data.part[message.id]).toEqual([fetched])
|
|
expect(store.data.part_text_accum_delta[part.id]).toBeUndefined()
|
|
})
|
|
|
|
test("preserves an unpersisted delta suffix after partial server catch-up", async () => {
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id, { text: "a" })
|
|
const fetched = { ...part, text: "ab" }
|
|
const store = createServerSession(
|
|
messageClient(response([{ info: message, parts: [part] }]), response([{ info: message, parts: [fetched] }])),
|
|
)
|
|
await store.sync("child")
|
|
store.apply({
|
|
type: "message.part.delta",
|
|
properties: { sessionID: "child", messageID: message.id, partID: part.id, field: "text", delta: "bc" },
|
|
})
|
|
|
|
await store.sync("child", { force: true })
|
|
|
|
expect(store.data.part[message.id]).toEqual([{ ...part, text: "abc" }])
|
|
expect(store.data.part_text_accum_delta[part.id]).toBe("abc")
|
|
})
|
|
|
|
test("clears delta state after exact server catch-up", async () => {
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id, { text: "a" })
|
|
const fetched = { ...part, text: "ab" }
|
|
const store = createServerSession(
|
|
messageClient(response([{ info: message, parts: [part] }]), response([{ info: message, parts: [fetched] }])),
|
|
)
|
|
await store.sync("child")
|
|
store.apply({
|
|
type: "message.part.delta",
|
|
properties: { sessionID: "child", messageID: message.id, partID: part.id, field: "text", delta: "b" },
|
|
})
|
|
|
|
await store.sync("child", { force: true })
|
|
|
|
expect(store.data.part[message.id]).toEqual([fetched])
|
|
expect(store.data.part_text_accum_delta[part.id]).toBeUndefined()
|
|
})
|
|
|
|
test("uses the successful retry response over events from a failed attempt", async () => {
|
|
const failed = Promise.withResolvers<MessageResponse>()
|
|
const retried = Promise.withResolvers<MessageResponse>()
|
|
const message = userMessage("message")
|
|
const stale = textPart(message.id, { text: "stale" })
|
|
const intermediate = { ...stale, text: "intermediate" }
|
|
const fetched = { ...stale, text: "fetched" }
|
|
const client = messageClient(failed.promise, retried.promise)
|
|
const store = createServerSession(client, { retry: retryImmediately })
|
|
store.apply({ type: "message.updated", properties: { info: message } })
|
|
store.apply({ type: "message.part.updated", properties: { sessionID: "child", part: stale, time: 1 } })
|
|
const loading = store.sync("child")
|
|
|
|
store.apply({ type: "message.part.updated", properties: { sessionID: "child", part: intermediate, time: 2 } })
|
|
failed.reject(new Error("failed to fetch"))
|
|
await client.requested(2)
|
|
retried.resolve(response([{ info: message, parts: [fetched] }]))
|
|
await loading
|
|
|
|
expect(store.data.part[message.id]).toEqual([fetched])
|
|
})
|
|
|
|
test("preserves non-durable deltas across message retries", async () => {
|
|
const failed = Promise.withResolvers<MessageResponse>()
|
|
const retried = Promise.withResolvers<MessageResponse>()
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id, { text: "stale" })
|
|
const client = messageClient(failed.promise, retried.promise)
|
|
const store = createServerSession(client, { retry: retryImmediately })
|
|
store.apply({ type: "message.updated", properties: { info: message } })
|
|
store.apply({ type: "message.part.updated", properties: { sessionID: "child", part, time: 1 } })
|
|
const loading = store.sync("child")
|
|
|
|
store.apply({
|
|
type: "message.part.delta",
|
|
properties: { sessionID: "child", messageID: message.id, partID: part.id, field: "text", delta: " delta" },
|
|
})
|
|
failed.reject(new Error("failed to fetch"))
|
|
await client.requested(2)
|
|
retried.resolve(response([{ info: message, parts: [part] }]))
|
|
await loading
|
|
|
|
expect(store.data.part[message.id]).toEqual([{ ...part, text: "stale delta" }])
|
|
})
|
|
|
|
test("preserves part removals across message retries", async () => {
|
|
const failed = Promise.withResolvers<MessageResponse>()
|
|
const retried = Promise.withResolvers<MessageResponse>()
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id)
|
|
const client = messageClient(response([{ info: message, parts: [part] }]), failed.promise, retried.promise)
|
|
const store = createServerSession(client, { retry: retryImmediately })
|
|
await store.sync("child")
|
|
const loading = store.sync("child", { force: true })
|
|
|
|
store.apply({
|
|
type: "message.part.removed",
|
|
properties: { sessionID: "child", messageID: message.id, partID: part.id },
|
|
})
|
|
failed.reject(new Error("failed to fetch"))
|
|
await client.requested(3)
|
|
retried.resolve(response([{ info: message, parts: [part] }]))
|
|
await loading
|
|
|
|
expect(store.data.part[message.id]).toBeUndefined()
|
|
})
|
|
|
|
test("preserves message removals across message retries", async () => {
|
|
const failed = Promise.withResolvers<MessageResponse>()
|
|
const retried = Promise.withResolvers<MessageResponse>()
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id)
|
|
const client = messageClient(response([{ info: message, parts: [part] }]), failed.promise, retried.promise)
|
|
const store = createServerSession(client, { retry: retryImmediately })
|
|
await store.sync("child")
|
|
const loading = store.sync("child", { force: true })
|
|
|
|
store.apply({ type: "message.removed", properties: { sessionID: "child", messageID: message.id } })
|
|
failed.reject(new Error("failed to fetch"))
|
|
await client.requested(3)
|
|
retried.resolve(response([{ info: message, parts: [part] }]))
|
|
await loading
|
|
|
|
expect(store.data.message.child).toEqual([])
|
|
expect(store.data.part[message.id]).toBeUndefined()
|
|
})
|
|
|
|
test("preserves optimistic re-adds across message retries", async () => {
|
|
const failed = Promise.withResolvers<MessageResponse>()
|
|
const retried = Promise.withResolvers<MessageResponse>()
|
|
const message = userMessage("message")
|
|
const stale = textPart(message.id, { id: "stale", text: "stale" })
|
|
const optimistic = textPart(message.id, { id: "optimistic", text: "optimistic" })
|
|
const client = messageClient(response([{ info: message, parts: [stale] }]), failed.promise, retried.promise)
|
|
const store = createServerSession(client, { retry: retryImmediately })
|
|
await store.sync("child")
|
|
const loading = store.sync("child", { force: true })
|
|
|
|
store.apply({ type: "message.removed", properties: { sessionID: "child", messageID: message.id } })
|
|
store.optimistic.add({ sessionID: "child", message, parts: [optimistic] })
|
|
failed.reject(new Error("failed to fetch"))
|
|
await client.requested(3)
|
|
retried.resolve(response([{ info: message, parts: [stale] }]))
|
|
await loading
|
|
|
|
expect(store.data.message.child).toEqual([message])
|
|
expect(store.data.part[message.id]).toEqual([optimistic])
|
|
})
|
|
|
|
test("accepts part omission from a successful retry after an earlier delta", async () => {
|
|
const failed = Promise.withResolvers<MessageResponse>()
|
|
const retried = Promise.withResolvers<MessageResponse>()
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id)
|
|
const client = messageClient(response([{ info: message, parts: [part] }]), failed.promise, retried.promise)
|
|
const store = createServerSession(client, { retry: retryImmediately })
|
|
await store.sync("child")
|
|
const loading = store.sync("child", { force: true })
|
|
|
|
store.apply({
|
|
type: "message.part.delta",
|
|
properties: { sessionID: "child", messageID: message.id, partID: part.id, field: "text", delta: " delta" },
|
|
})
|
|
failed.reject(new Error("failed to fetch"))
|
|
await client.requested(3)
|
|
retried.resolve(response([{ info: message, parts: [] }]))
|
|
await loading
|
|
|
|
expect(store.data.part[message.id]).toBeUndefined()
|
|
expect(store.data.part_text_accum_delta[part.id]).toBeUndefined()
|
|
})
|
|
|
|
test("clears load-owned orphan parts when all retries fail", async () => {
|
|
const first = Promise.withResolvers<MessageResponse>()
|
|
const second = Promise.withResolvers<MessageResponse>()
|
|
const third = Promise.withResolvers<MessageResponse>()
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id)
|
|
const client = messageClient(first.promise, second.promise, third.promise)
|
|
const store = createServerSession(client, { retry: retryImmediately })
|
|
const loading = store.sync("child").catch((error) => error)
|
|
|
|
store.apply({ type: "message.part.updated", properties: { sessionID: "child", part, time: 2 } })
|
|
first.reject(new Error("failed to fetch"))
|
|
await client.requested(2)
|
|
second.reject(new Error("failed to fetch"))
|
|
await client.requested(3)
|
|
third.reject(new Error("failed to fetch"))
|
|
await loading
|
|
|
|
expect(store.data.part[message.id]).toBeUndefined()
|
|
})
|
|
|
|
test("preserves live updates during a forced refresh", async () => {
|
|
const pending = deferredResponse()
|
|
const stale = userMessage("message")
|
|
const stalePart = textPart(stale.id, { text: "stale" })
|
|
const store = createServerSession(messageClient(response([{ info: stale, parts: [stalePart] }]), pending.promise))
|
|
await store.sync("child")
|
|
const refreshing = store.sync("child", { force: true })
|
|
const live = { ...stale, time: { created: 2 } }
|
|
|
|
store.apply({ type: "message.updated", properties: { info: live } })
|
|
store.apply({
|
|
type: "message.part.delta",
|
|
properties: { sessionID: "child", messageID: stale.id, partID: stalePart.id, field: "text", delta: " live" },
|
|
})
|
|
pending.resolve(response([{ info: stale, parts: [stalePart] }]))
|
|
await refreshing
|
|
|
|
expect(store.data.message.child).toEqual([live])
|
|
expect(store.data.part[stale.id]).toEqual([{ ...stalePart, text: "stale live" }])
|
|
})
|
|
|
|
test("keeps fetched message metadata when only a part changes", async () => {
|
|
const pending = deferredResponse()
|
|
const stale = userMessage("message")
|
|
const fetched = { ...stale, time: { created: 2 } }
|
|
const part = textPart(stale.id, { text: "stale" })
|
|
const store = createServerSession(messageClient(response([{ info: stale, parts: [part] }]), pending.promise))
|
|
await store.sync("child")
|
|
const refreshing = store.sync("child", { force: true })
|
|
|
|
store.apply({
|
|
type: "message.part.delta",
|
|
properties: { sessionID: "child", messageID: stale.id, partID: part.id, field: "text", delta: " live" },
|
|
})
|
|
pending.resolve(response([{ info: fetched, parts: [part] }]))
|
|
await refreshing
|
|
|
|
expect(store.data.message.child).toEqual([fetched])
|
|
expect(store.data.part[stale.id]).toEqual([{ ...part, text: "stale live" }])
|
|
})
|
|
|
|
test("preserves a part update when a forced refresh omits its message", async () => {
|
|
const pending = deferredResponse()
|
|
const message = userMessage("message")
|
|
const stale = textPart(message.id, { text: "stale" })
|
|
const live = { ...stale, text: "live" }
|
|
const store = createServerSession(messageClient(response([{ info: message, parts: [stale] }]), pending.promise))
|
|
await store.sync("child")
|
|
const refreshing = store.sync("child", { force: true })
|
|
|
|
store.apply({ type: "message.part.updated", properties: { sessionID: "child", part: live, time: 2 } })
|
|
pending.resolve(response())
|
|
await refreshing
|
|
|
|
expect(store.data.message.child).toEqual([message])
|
|
expect(store.data.part[message.id]).toEqual([live])
|
|
})
|
|
|
|
test("ignores a late part update after its message is removed", async () => {
|
|
const pending = deferredResponse()
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id)
|
|
const store = createServerSession(messageClient(pending.promise))
|
|
const loading = store.sync("child")
|
|
|
|
store.apply({ type: "message.updated", properties: { info: message } })
|
|
store.apply({ type: "message.removed", properties: { sessionID: "child", messageID: message.id } })
|
|
store.apply({ type: "message.part.updated", properties: { sessionID: "child", part, time: 2 } })
|
|
pending.resolve(response([{ info: message, parts: [part] }]))
|
|
await loading
|
|
|
|
expect(store.data.message.child).toEqual([])
|
|
expect(store.data.part[message.id]).toBeUndefined()
|
|
})
|
|
|
|
test("ignores a late part update after a completed message removal", () => {
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id)
|
|
const store = setup({ child: session("child") }).store
|
|
store.apply({ type: "message.updated", properties: { info: message } })
|
|
store.apply({ type: "message.removed", properties: { sessionID: "child", messageID: message.id } })
|
|
|
|
store.apply({ type: "message.part.updated", properties: { sessionID: "child", part, time: 2 } })
|
|
|
|
expect(store.data.part[message.id]).toBeUndefined()
|
|
})
|
|
|
|
test("does not restore a completed message removal from a stale refresh", async () => {
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id)
|
|
const store = createServerSession(
|
|
messageClient(response([{ info: message, parts: [part] }]), response([{ info: message, parts: [part] }])),
|
|
)
|
|
await store.sync("child")
|
|
store.apply({ type: "message.removed", properties: { sessionID: "child", messageID: message.id } })
|
|
|
|
await store.sync("child", { force: true })
|
|
|
|
expect(store.data.message.child).toEqual([])
|
|
expect(store.data.part[message.id]).toBeUndefined()
|
|
})
|
|
|
|
test("does not restore a completed part removal from a stale refresh", async () => {
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id)
|
|
const store = createServerSession(
|
|
messageClient(response([{ info: message, parts: [part] }]), response([{ info: message, parts: [part] }])),
|
|
)
|
|
await store.sync("child")
|
|
store.apply({
|
|
type: "message.part.removed",
|
|
properties: { sessionID: "child", messageID: message.id, partID: part.id },
|
|
})
|
|
|
|
await store.sync("child", { force: true })
|
|
|
|
expect(store.data.part[message.id]).toBeUndefined()
|
|
})
|
|
|
|
test("does not cache skipped optimistic parts", () => {
|
|
const message = userMessage("message")
|
|
const part = { id: "part", sessionID: "child", messageID: message.id, type: "step-start" as const }
|
|
const store = setup({ child: session("child") }).store
|
|
|
|
store.optimistic.add({ sessionID: "child", message, parts: [part] })
|
|
|
|
expect(store.data.part[message.id]).toEqual([])
|
|
})
|
|
|
|
test("clears stale delta buffers when replacing optimistic parts", () => {
|
|
const message = userMessage("message")
|
|
const stale = textPart(message.id, { id: "stale", text: "stale" })
|
|
const optimistic = textPart(message.id, { id: "optimistic", text: "optimistic" })
|
|
const store = setup({ child: session("child") }).store
|
|
store.optimistic.add({ sessionID: "child", message, parts: [stale] })
|
|
store.apply({
|
|
type: "message.part.delta",
|
|
properties: { sessionID: "child", messageID: message.id, partID: stale.id, field: "text", delta: " delta" },
|
|
})
|
|
|
|
store.optimistic.add({ sessionID: "child", message, parts: [optimistic] })
|
|
|
|
expect(store.data.part_text_accum_delta[stale.id]).toBeUndefined()
|
|
expect(store.data.part_text_accum_delta[optimistic.id]).toBeUndefined()
|
|
})
|
|
|
|
test("preserves removals during history prepend", async () => {
|
|
const pending = deferredResponse()
|
|
const latest = userMessage("message-2", { time: { created: 2 } })
|
|
const older = { ...latest, id: "message-1", time: { created: 1 } }
|
|
const store = createServerSession(messageClient(response([{ info: latest, parts: [] }], "older"), pending.promise))
|
|
await store.sync("child")
|
|
const loading = store.history.loadMore("child")
|
|
|
|
store.apply({ type: "message.removed", properties: { sessionID: "child", messageID: older.id } })
|
|
pending.resolve(response([{ info: older, parts: [] }]))
|
|
await loading
|
|
|
|
expect(store.data.message.child).toEqual([latest])
|
|
})
|
|
|
|
test("preserves loaded history during an incomplete refresh", async () => {
|
|
const older = userMessage("message-1")
|
|
const latest = userMessage("message-2", { time: { created: 2 } })
|
|
const fresh = userMessage("message-3", { time: { created: 3 } })
|
|
const store = createServerSession(
|
|
messageClient(
|
|
response(
|
|
[
|
|
{ info: older, parts: [] },
|
|
{ info: latest, parts: [] },
|
|
],
|
|
"older",
|
|
),
|
|
response(
|
|
[
|
|
{ info: latest, parts: [] },
|
|
{ info: fresh, parts: [] },
|
|
],
|
|
"older",
|
|
),
|
|
),
|
|
)
|
|
await store.sync("child")
|
|
|
|
await store.sync("child", { force: true })
|
|
|
|
expect(store.data.message.child).toEqual([older, latest, fresh])
|
|
})
|
|
|
|
test("drops stale recent messages omitted by an incomplete refresh", async () => {
|
|
const third = userMessage("message-3", { time: { created: 3 } })
|
|
const fourth = userMessage("message-4", { time: { created: 4 } })
|
|
const stale = userMessage("message-5", { time: { created: 5 } })
|
|
const store = createServerSession(
|
|
messageClient(
|
|
response(
|
|
[
|
|
{ info: fourth, parts: [] },
|
|
{ info: stale, parts: [] },
|
|
],
|
|
"older",
|
|
),
|
|
response(
|
|
[
|
|
{ info: third, parts: [] },
|
|
{ info: fourth, parts: [] },
|
|
],
|
|
"older",
|
|
),
|
|
),
|
|
)
|
|
await store.sync("child")
|
|
|
|
await store.sync("child", { force: true })
|
|
|
|
expect(store.data.message.child).toEqual([third, fourth])
|
|
})
|
|
|
|
test("uses message creation time for incomplete refresh boundaries", async () => {
|
|
const older = userMessage("msg_z", { time: { created: 1 } })
|
|
const boundary = userMessage("msg_m", { time: { created: 2 } })
|
|
const stale = userMessage("msg_a", { time: { created: 3 } })
|
|
const store = createServerSession(
|
|
messageClient(
|
|
response(
|
|
[
|
|
{ info: older, parts: [] },
|
|
{ info: stale, parts: [] },
|
|
],
|
|
"older",
|
|
),
|
|
response([{ info: boundary, parts: [] }], "older"),
|
|
),
|
|
)
|
|
await store.sync("child")
|
|
|
|
await store.sync("child", { force: true })
|
|
|
|
expect(store.data.message.child).toEqual([boundary, older])
|
|
})
|
|
|
|
test("preserves a part update for a message being loaded from history", async () => {
|
|
const pending = deferredResponse()
|
|
const latest = userMessage("message-2", { time: { created: 2 } })
|
|
const older = userMessage("message-1")
|
|
const stale = textPart(older.id, { text: "stale" })
|
|
const live = { ...stale, text: "live" }
|
|
const store = createServerSession(messageClient(response([{ info: latest, parts: [] }], "older"), pending.promise))
|
|
await store.sync("child")
|
|
const loading = store.history.loadMore("child")
|
|
|
|
store.apply({ type: "message.part.updated", properties: { sessionID: "child", part: live, time: 2 } })
|
|
pending.resolve(response([{ info: older, parts: [stale] }]))
|
|
await loading
|
|
|
|
expect(store.data.part[older.id]).toEqual([live])
|
|
})
|
|
|
|
test("does not clear newer orphan parts after terminal history prepend", async () => {
|
|
const pending = deferredResponse()
|
|
const latest = userMessage("message-2", { time: { created: 2 } })
|
|
const older = userMessage("message-1")
|
|
const newer = userMessage("message-3", { time: { created: 3 } })
|
|
const part = textPart(newer.id, { text: "live" })
|
|
const store = createServerSession(messageClient(response([{ info: latest, parts: [] }], "older"), pending.promise))
|
|
await store.sync("child")
|
|
const loading = store.history.loadMore("child")
|
|
|
|
store.apply({ type: "message.part.updated", properties: { sessionID: "child", part, time: 3 } })
|
|
pending.resolve(response([{ info: older, parts: [] }]))
|
|
await loading
|
|
store.apply({ type: "message.updated", properties: { sessionID: "child", info: newer } })
|
|
|
|
expect(store.data.part[newer.id]).toEqual([part])
|
|
})
|
|
|
|
test("accepts an authoritative history part after an earlier unknown-parent update", async () => {
|
|
const pending = deferredResponse()
|
|
const history = deferredResponse()
|
|
const latest = userMessage("message-2", { time: { created: 2 } })
|
|
const older = userMessage("message-1")
|
|
const part = textPart(older.id, { text: "live" })
|
|
const store = createServerSession(messageClient(pending.promise, history.promise))
|
|
const loading = store.sync("child")
|
|
|
|
store.apply({ type: "message.part.updated", properties: { sessionID: "child", part, time: 2 } })
|
|
pending.resolve(response([{ info: latest, parts: [] }], "older"))
|
|
await loading
|
|
|
|
expect(store.data.part[older.id]).toEqual([part])
|
|
|
|
const loadingHistory = store.history.loadMore("child")
|
|
history.resolve(response([{ info: older, parts: [{ ...part, text: "stale" }] }]))
|
|
await loadingHistory
|
|
|
|
expect(store.data.part[older.id]).toEqual([{ ...part, text: "stale" }])
|
|
})
|
|
|
|
test("preserves an unknown-parent part removal across pages", async () => {
|
|
const initial = deferredResponse()
|
|
const history = deferredResponse()
|
|
const latest = userMessage("message-2", { time: { created: 2 } })
|
|
const older = userMessage("message-1")
|
|
const part = textPart(older.id)
|
|
const store = createServerSession(messageClient(initial.promise, history.promise))
|
|
const loading = store.sync("child")
|
|
|
|
store.apply({
|
|
type: "message.part.removed",
|
|
properties: { sessionID: "child", messageID: older.id, partID: part.id },
|
|
})
|
|
initial.resolve(response([{ info: latest, parts: [] }], "older"))
|
|
await loading
|
|
const loadingHistory = store.history.loadMore("child")
|
|
history.resolve(response([{ info: older, parts: [part] }]))
|
|
await loadingHistory
|
|
|
|
expect(store.data.part[older.id]).toBeUndefined()
|
|
})
|
|
|
|
test("clears orphaned parts when a refresh drops a message", async () => {
|
|
const message = userMessage("message")
|
|
const part = textPart(message.id, { text: "stale" })
|
|
const store = createServerSession(messageClient(response([{ info: message, parts: [part] }]), response()))
|
|
await store.sync("child")
|
|
store.apply({
|
|
type: "message.part.delta",
|
|
properties: { sessionID: "child", messageID: message.id, partID: part.id, field: "text", delta: " delta" },
|
|
})
|
|
await store.sync("child", { force: true })
|
|
|
|
expect(store.data.message.child).toEqual([])
|
|
expect(store.data.part[message.id]).toBeUndefined()
|
|
expect(store.data.part_text_accum_delta[part.id]).toBeUndefined()
|
|
})
|
|
|
|
test("applies events without a directory store", () => {
|
|
const ctx = setup({})
|
|
ctx.store.apply({ type: "session.created", properties: { sessionID: "root", info: session("root") } })
|
|
ctx.store.apply({ type: "session.status", properties: { sessionID: "root", status: { type: "busy" } } })
|
|
|
|
expect(ctx.store.get("root")?.directory).toBe("/repo")
|
|
expect(ctx.store.data.session_working("root")).toBe(true)
|
|
expect(ctx.get).toEqual([])
|
|
})
|
|
|
|
test("preserves pinned session content under server-wide cache pressure", () => {
|
|
const ctx = setup({})
|
|
ctx.store.pin("active")
|
|
ctx.store.optimistic.add({
|
|
sessionID: "active",
|
|
message: {
|
|
id: "message",
|
|
sessionID: "active",
|
|
role: "assistant",
|
|
time: { created: 1 },
|
|
parentID: "parent",
|
|
modelID: "model",
|
|
providerID: "provider",
|
|
mode: "build",
|
|
agent: "agent",
|
|
path: { cwd: "/repo", root: "/repo" },
|
|
cost: 0,
|
|
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
},
|
|
parts: [],
|
|
})
|
|
|
|
for (let index = 0; index < 50; index++) {
|
|
ctx.store.remember(session(`session-${index}`))
|
|
ctx.store.apply({
|
|
type: "session.status",
|
|
properties: { sessionID: `session-${index}`, status: { type: "idle" } },
|
|
})
|
|
}
|
|
|
|
expect(ctx.store.data.message.active?.map((message) => message.id)).toEqual(["message"])
|
|
expect(ctx.store.data.session_status["session-0"]).toBeUndefined()
|
|
})
|
|
})
|