Skip to content

Commit e7f80c5

Browse files
committed
test(app): cover malformed session diff recovery
1 parent 77f9eed commit e7f80c5

1 file changed

Lines changed: 132 additions & 0 deletions

File tree

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import { beforeAll, describe, expect, mock, test } from "bun:test"
2+
import { createRoot } from "solid-js"
3+
import { createStore } from "solid-js/store"
4+
import type { FileDiff } from "@opencode-ai/sdk/v2/client"
5+
import type { State } from "./global-sync/types"
6+
7+
type SyncValue = {
8+
session: {
9+
diff: (sessionID: string, opts?: { force?: boolean }) => Promise<void> | undefined
10+
}
11+
}
12+
13+
let initSyncForTest: () => SyncValue
14+
let currentGlobalSync: unknown
15+
let currentSDK: unknown
16+
17+
beforeAll(async () => {
18+
let capturedInit: (() => SyncValue) | undefined
19+
20+
mock.module("@opencode-ai/ui/context", () => ({
21+
createSimpleContext: (input: { init: () => SyncValue }) => {
22+
capturedInit = input.init
23+
return {
24+
use: () => undefined,
25+
provider: () => undefined,
26+
}
27+
},
28+
}))
29+
30+
mock.module("./global-sync", () => ({
31+
useGlobalSync: () => currentGlobalSync,
32+
}))
33+
34+
mock.module("./sdk", () => ({
35+
useSDK: () => currentSDK,
36+
}))
37+
38+
await import("./sync")
39+
if (!capturedInit) throw new Error("Failed to capture sync context init")
40+
initSyncForTest = capturedInit
41+
})
42+
43+
function baseState(): State {
44+
return {
45+
status: "complete",
46+
agent: [],
47+
command: [],
48+
project: "",
49+
projectMeta: undefined,
50+
icon: undefined,
51+
provider_ready: false,
52+
provider: { all: [], connected: [], default: {} },
53+
config: {},
54+
path: { state: "", config: "", worktree: "", directory: "/tmp/project", home: "" },
55+
session: [],
56+
sessionTotal: 0,
57+
session_status: {},
58+
session_diff: {},
59+
todo: {},
60+
permission: {},
61+
question: {},
62+
mcp_ready: false,
63+
mcp: {},
64+
lsp_ready: false,
65+
lsp: [],
66+
vcs: undefined,
67+
limit: 5,
68+
message: {},
69+
part: {},
70+
}
71+
}
72+
73+
describe("sync session diff recovery", () => {
74+
test("re-fetches malformed cached diffs until a valid array is stored", async () => {
75+
const sessionID = "ses_1"
76+
const valid = [{ file: "a.ts", before: "", after: "", additions: 1, deletions: 0 }] as FileDiff[]
77+
const [store, setStore] = createStore({
78+
...baseState(),
79+
session_diff: { [sessionID]: {} as State["session_diff"][string] },
80+
})
81+
82+
const diff = mock(async () => {
83+
const count = diff.mock.calls.length
84+
return count === 1 ? ({ data: {} } as const) : ({ data: valid } as const)
85+
})
86+
87+
currentGlobalSync = {
88+
child: () => [store, setStore],
89+
data: {
90+
project: [],
91+
session_todo: {},
92+
},
93+
todo: {
94+
set() {},
95+
},
96+
}
97+
98+
currentSDK = {
99+
directory: "/tmp/project",
100+
client: {
101+
session: {
102+
diff,
103+
},
104+
},
105+
}
106+
107+
await new Promise<void>((resolve, reject) => {
108+
createRoot((dispose) => {
109+
const sync = initSyncForTest()
110+
void (async () => {
111+
try {
112+
await sync.session.diff(sessionID)
113+
expect(diff).toHaveBeenCalledTimes(1)
114+
expect(store.session_diff[sessionID]).toBeUndefined()
115+
116+
await sync.session.diff(sessionID)
117+
expect(diff).toHaveBeenCalledTimes(2)
118+
expect(store.session_diff[sessionID]).toEqual(valid)
119+
120+
await sync.session.diff(sessionID)
121+
expect(diff).toHaveBeenCalledTimes(2)
122+
resolve()
123+
} catch (error) {
124+
reject(error)
125+
} finally {
126+
dispose()
127+
}
128+
})()
129+
})
130+
})
131+
})
132+
})

0 commit comments

Comments
 (0)