Files
openchamber/packages/ui/src/sync/session-message-loader.test.ts
T
Bohdan Triapitsyn 85400459e9 perf: overhaul session loading, caching, and runtime isolation (#2360)
Improve OpenChamber responsiveness under large session workloads while fixing
cache, synchronization, and persistence correctness across runtimes, projects,
directories, and worktrees.

- prioritize selected and visible sessions during bootstrap and defer
  non-critical enrichment work
- reduce redundant message loading, event processing, store publication, and
  hidden sidebar work
- prevent stale session and message requests from overwriting newer
  authoritative state
- preserve existing data when authoritative fetches fail instead of treating
  failures as successful empty responses
- scope session materialization, messages, drafts, queues, todos, pins,
  permissions, folders, tabs, Git state, and pull request data by runtime and
  directory identity
- harden runtime switching, reconnect, cleanup, mutation reconciliation, and
  persisted-state ordering
- preserve live subagent Task linkage when metadata arrives after an older
  message request or while streaming parts are suspended
- coalesce overlapping tail refreshes without losing newer refresh demand
- improve cold-session loading by moving deferrable work out of the critical
  bootstrap path
- isolate URL authentication, mobile credentials, native secrets, and other
  runtime-owned state across endpoint changes
- bound long-lived caches and remove avoidable allocations from event and
  rendering hot paths
- limit virtualization to archive collections where it improves rendering
  without disrupting active sidebar layout
- stabilize session folders, pin ordering, expanded state, and persisted
  sidebar behavior
- open skill files through the same secure editor and outside-workspace grant
  flow used by file navigation, including worktree sessions
- expand regression coverage for stale completions, runtime collisions,
  reconnect behavior, persistence races, authoritative empty results, and
  subagent refresh ordering
- document the updated synchronization, cache ownership, performance, and
  runtime-isolation invariants
2026-07-21 20:52:20 +03:00

176 lines
6.7 KiB
TypeScript

import { describe, expect, test } from "bun:test"
import type { Message, OpencodeClient, Part } from "@opencode-ai/sdk/v2/client"
import { ChildStoreManager } from "./child-store"
import { SessionMessageLoader } from "./session-message-loader"
const createRecord = (sessionID: string, id = "msg_1") => ({
info: { id, sessionID, role: "user", time: { created: 1 } } as Message,
parts: [{ id: `part_${id}`, messageID: id, sessionID, type: "text", text: "hello" }] as Part[],
})
const deferred = <T>() => {
let resolve!: (value: T) => void
const promise = new Promise<T>((next) => {
resolve = next
})
return { promise, resolve }
}
const response = (data: ReturnType<typeof createRecord>[], cursor?: string) => ({
data,
response: { headers: { get: (name: string) => name === "x-next-cursor" ? cursor ?? null : null } },
})
const createLoader = (messages: (input: {
sessionID: string
directory?: string
limit?: number
before?: string
}) => Promise<unknown>) => {
const childStores = new ChildStoreManager()
const sdk = { session: { messages } } as unknown as OpencodeClient
const loader = new SessionMessageLoader(childStores, { sdk, runtimeKey: "runtime-a" })
return { childStores, loader }
}
describe("SessionMessageLoader", () => {
test("deduplicates navigation and reactive loading for the same target", async () => {
const pending = deferred<ReturnType<typeof response>>()
let calls = 0
const { childStores, loader } = createLoader(async () => {
calls += 1
return pending.promise
})
const target = { directory: "/repo", sessionID: "session-a" }
const navigation = loader.ensure(target, { reason: "navigation" })
const reactive = loader.ensure(target, { reason: "reactive" })
expect(calls).toBe(1)
pending.resolve(response([createRecord(target.sessionID)]))
await Promise.all([navigation, reactive])
expect(loader.getSnapshot(target).status).toBe("ready")
expect(childStores.getChild(target.directory)?.getState().message[target.sessionID]?.length).toBe(1)
loader.dispose()
childStores.disposeAll()
})
test("runs a requested tail refresh after an older in-flight load", async () => {
const initial = deferred<ReturnType<typeof response>>()
const refresh = deferred<ReturnType<typeof response>>()
let calls = 0
const limits: number[] = []
const { childStores, loader } = createLoader(async ({ limit }) => {
calls += 1
limits.push(limit ?? 0)
return calls === 1 ? initial.promise : refresh.promise
})
const target = { directory: "/repo", sessionID: "session-a" }
const loading = loader.ensure(target, { reason: "navigation" })
const refreshing = loader.refreshTail(target, 30)
const duplicateRefresh = loader.refreshTail(target, 80)
expect(calls).toBe(1)
expect(duplicateRefresh).toBe(refreshing)
initial.resolve(response([createRecord(target.sessionID, "msg_1")]))
await loading
await Promise.resolve()
expect(calls).toBe(2)
expect(limits).toEqual([50, 80])
refresh.resolve(response([createRecord(target.sessionID, "msg_2")]))
await Promise.all([refreshing, duplicateRefresh])
expect(childStores.getChild(target.directory)?.getState().message[target.sessionID]?.map((message) => message.id))
.toEqual(["msg_1", "msg_2"])
loader.dispose()
childStores.disposeAll()
})
test("does not deduplicate identical session IDs across directories", async () => {
const calls: string[] = []
const { childStores, loader } = createLoader(async ({ directory, sessionID }) => {
calls.push(directory ?? "")
return response([createRecord(sessionID)])
})
await Promise.all([
loader.ensure({ directory: "/repo-a", sessionID: "shared" }),
loader.ensure({ directory: "/repo-b", sessionID: "shared" }),
])
expect(calls.sort()).toEqual(["/repo-a", "/repo-b"])
loader.dispose()
childStores.disposeAll()
})
test("exposes a retryable error without clearing an existing snapshot", async () => {
let fail = true
const { childStores, loader } = createLoader(async ({ sessionID }) => {
if (fail) return { error: { message: "rejected" }, response: { status: 400 } }
return response([createRecord(sessionID)])
})
const target = { directory: "/repo", sessionID: "session-a" }
const store = childStores.ensureChild(target.directory, { bootstrap: false })
store.setState({ message: { [target.sessionID]: [{ id: "cached", sessionID: target.sessionID, role: "user", time: { created: 0 } } as Message] } })
await loader.ensure(target, { force: true })
expect(loader.getSnapshot(target).status).toBe("error")
expect(store.getState().message[target.sessionID]?.[0]?.id).toBe("cached")
fail = false
await loader.ensure(target, { force: true })
expect(loader.getSnapshot(target).status).toBe("ready")
loader.dispose()
childStores.disposeAll()
})
test("prevents an evicted in-flight request from repopulating the store", async () => {
const pending = deferred<ReturnType<typeof response>>()
const { childStores, loader } = createLoader(async () => pending.promise)
const target = { directory: "/repo", sessionID: "session-a" }
const loading = loader.ensure(target)
loader.invalidateSession(target)
pending.resolve(response([createRecord(target.sessionID)]))
await loading
expect(childStores.getChild(target.directory)?.getState().message[target.sessionID]).toBe(undefined)
expect(loader.getSnapshot(target).status).toBe("idle")
loader.dispose()
childStores.disposeAll()
})
test("treats an empty successful response as resolved authoritative state", async () => {
const { childStores, loader } = createLoader(async () => response([]))
const target = { directory: "/repo", sessionID: "empty" }
await loader.ensure(target)
expect(loader.getSnapshot(target).resolved).toBe(true)
expect(loader.getSnapshot(target).complete).toBe(true)
expect(childStores.getChild(target.directory)?.getState().message[target.sessionID]).toEqual([])
loader.dispose()
childStores.disposeAll()
})
test("retries a missing message payload instead of treating it as an empty snapshot", async () => {
let calls = 0
const { childStores, loader } = createLoader(async ({ sessionID }) => {
calls += 1
return calls === 1 ? {} : response([createRecord(sessionID)])
})
const target = { directory: "/repo", sessionID: "session-a" }
await loader.ensure(target)
expect(calls).toBe(2)
expect(loader.getSnapshot(target).status).toBe("ready")
expect(childStores.getChild(target.directory)?.getState().message[target.sessionID]?.length).toBe(1)
loader.dispose()
childStores.disposeAll()
})
})