Files
openchamber/packages/ui/src/sync/reconnect-recovery.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

160 lines
5.2 KiB
TypeScript

import { describe, expect, test } from "bun:test"
import type { Message, Part, SessionStatus } from "@opencode-ai/sdk/v2/client"
import type { Session } from "@opencode-ai/sdk/v2"
import { getReconnectCandidateSessionIds, mergeBootstrapSessions } from "./reconnect-recovery"
function createSession(id: string, overrides: Partial<Session> = {}): Session {
return {
id,
title: id,
time: { created: 1, updated: 1 },
version: "1",
...overrides,
} as Session
}
function createAssistantMessage(id: string, sessionID: string, completed?: number): Message {
return {
id,
sessionID,
role: "assistant",
time: completed ? { created: 1, updated: 1, completed } : { created: 1, updated: 1 },
parts: [],
} as unknown as Message
}
function createPart(id: string, messageID: string): Part {
return { id, messageID, sessionID: "active", type: "text", text: "done" } as Part
}
describe("getReconnectCandidateSessionIds", () => {
test("includes non-idle, incomplete assistant, and parent sessions", () => {
const busyStatus = { type: "busy" } as SessionStatus
expect(getReconnectCandidateSessionIds({
session: [
createSession("busy"),
createSession("child", { parentID: "parent" }),
createSession("parent"),
createSession("incomplete"),
],
session_status: { busy: busyStatus },
message: {
incomplete: [createAssistantMessage("m-1", "incomplete")],
},
}).sort()).toEqual(["busy", "incomplete", "parent"])
})
test("includes the currently viewed session even when it looks idle and complete", () => {
expect(getReconnectCandidateSessionIds({
session: [createSession("active")],
session_status: { active: { type: "idle" } as SessionStatus },
message: {
active: [createAssistantMessage("m-1", "active", 1)],
},
part: {
"m-1": [createPart("p-1", "m-1")],
},
}, {
directory: "/repo",
viewedSession: { directory: "/repo", sessionId: "active" },
}).sort()).toContain("active")
})
test("includes completed assistant sessions when the latest assistant parts are missing", () => {
expect(getReconnectCandidateSessionIds({
session: [createSession("blank")],
session_status: { blank: { type: "idle" } as SessionStatus },
message: {
blank: [createAssistantMessage("m-1", "blank", 1)],
},
part: {},
})).toEqual(["blank"])
})
test("does not include a viewed session from another directory", () => {
expect(getReconnectCandidateSessionIds({
session: [createSession("active")],
session_status: { active: { type: "idle" } as SessionStatus },
message: {
active: [createAssistantMessage("m-1", "active", 1)],
},
part: {
"m-1": [createPart("p-1", "m-1")],
},
}, {
directory: "/repo-a",
viewedSession: { directory: "/repo-b", sessionId: "active" },
}).sort()).not.toContain("active")
})
})
describe("mergeBootstrapSessions", () => {
test("recovers a referenced parent when the roots response is temporarily empty", () => {
const parent = createSession("parent")
const child = createSession("child", { parentID: "parent" })
expect(mergeBootstrapSessions([], [child], [parent])).toEqual({
sessions: [child, parent],
rootCount: 1,
})
})
test("recovers referenced parents from the broader response without retaining stale roots", () => {
const parent = createSession("parent")
const stale = createSession("stale")
const child = createSession("child", { parentID: "parent" })
expect(mergeBootstrapSessions([], [parent, child], [stale])).toEqual({
sessions: [child, parent],
rootCount: 1,
})
})
test("treats a successful empty response as authoritative", () => {
const persisted = createSession("persisted")
expect(mergeBootstrapSessions([], [], [persisted])).toEqual({
sessions: [],
rootCount: 0,
})
})
test("preserves known children when the child-session request fails", () => {
const cachedParent = createSession("parent")
const authoritativeParent = createSession("parent", { title: "Current" })
const cachedChild = createSession("child", { parentID: "parent" })
expect(mergeBootstrapSessions([authoritativeParent], null, [cachedChild, cachedParent])).toEqual({
sessions: [cachedChild, authoritativeParent],
rootCount: 1,
})
})
test("overlays live session events that arrive after the request starts", () => {
const staleResponse = createSession("existing", { title: "Stale" })
const liveUpdate = createSession("existing", { title: "Live" })
const liveCreate = createSession("new")
expect(mergeBootstrapSessions([staleResponse], [], [liveUpdate, liveCreate], {
baselineRevision: 4,
eventRevision: { existing: 5, new: 6 },
})).toEqual({
sessions: [liveUpdate, liveCreate],
rootCount: 2,
})
})
test("does not resurrect a session deleted after the request starts", () => {
const deleted = createSession("deleted")
expect(mergeBootstrapSessions([deleted], [], [], {
baselineRevision: 2,
deletedRevision: { deleted: 3 },
})).toEqual({
sessions: [],
rootCount: 0,
})
})
})