Skip to content

Commit 2874920

Browse files
authored
feat(cs-cloud): split reconnect and server restart, rename ownership/source enums (#1332)
1 parent 9c935b0 commit 2874920

7 files changed

Lines changed: 291 additions & 64 deletions

File tree

src/__tests__/assistant-ui-cscloud-service.spec.ts

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,14 +192,67 @@ describe("CsCloudService (refactored)", () => {
192192
expect(svc.state).toBe("error")
193193
})
194194

195-
it("restart clears state and re-resolves", async () => {
195+
it("reconnect clears state and re-resolves", async () => {
196196
setServerUrlFile("http://127.0.0.1:59249")
197197
mockHealthOk()
198198
const svc = new CsCloudService(createOutputChannel() as never)
199199
await svc.ensureStarted()
200200
expect(svc.state).toBe("running")
201201
setServerUrlFile("http://127.0.0.1:60000")
202-
await expect(svc.restart()).resolves.toBe("http://127.0.0.1:60000/api/v1")
202+
await expect(svc.reconnect()).resolves.toBe("http://127.0.0.1:60000/api/v1")
203+
expect(mockCrossSpawn).not.toHaveBeenCalled()
204+
})
205+
206+
it("restartServer uses bundled cs-cloud restart when bundled binary exists", async () => {
207+
setServerUrlFile("http://127.0.0.1:59249")
208+
mockHealthOk()
209+
210+
const svc = new CsCloudService(createOutputChannel() as never)
211+
await expect(svc.restartServer()).resolves.toBe("http://127.0.0.1:59249/api/v1")
212+
213+
expect(mockCrossSpawn).toHaveBeenCalledWith(
214+
"/home/testuser/.costrict/bin/cs-cloud",
215+
["restart"],
216+
expect.objectContaining({ env: expect.any(Object), stdio: "pipe" }),
217+
)
218+
expect(svc.processOwner).toBe("extension")
219+
expect(svc.connectionSource).toBe("bundledBinary")
220+
})
221+
222+
it("restartServer uses global csc cloud restart when bundled binary is missing", async () => {
223+
mockFs.existsSync.mockImplementation((p: string) => p.toString().endsWith("server_url"))
224+
mockFs.readFileSync.mockReturnValue("http://127.0.0.1:59249")
225+
mockWhich.mockResolvedValue("/usr/local/bin/csc")
226+
mockHealthOk()
227+
228+
const svc = new CsCloudService(createOutputChannel() as never)
229+
await expect(svc.restartServer()).resolves.toBe("http://127.0.0.1:59249/api/v1")
230+
231+
expect(mockCrossSpawn).toHaveBeenCalledWith(
232+
"/usr/local/bin/csc",
233+
["cloud", "restart"],
234+
expect.objectContaining({ env: expect.any(Object), stdio: "pipe" }),
235+
)
236+
expect(svc.processOwner).toBe("external")
237+
expect(svc.connectionSource).toBe("cliRestart")
238+
})
239+
240+
it("configured baseUrl reconnect keeps reconnect behavior", async () => {
241+
setConfigValues({ baseUrl: "http://custom:8080/api/v1", port: 45489 })
242+
243+
const svc = new CsCloudService(createOutputChannel() as never)
244+
await expect(svc.reconnect()).resolves.toBe("http://custom:8080/api/v1")
245+
246+
expect(mockCrossSpawn).not.toHaveBeenCalled()
247+
})
248+
249+
it("configured baseUrl restartServer is rejected", async () => {
250+
setConfigValues({ baseUrl: "http://custom:8080/api/v1", port: 45489 })
251+
252+
const svc = new CsCloudService(createOutputChannel() as never)
253+
await expect(svc.restartServer()).rejects.toThrow("Cannot restart configured external cs-cloud baseUrl")
254+
255+
expect(mockCrossSpawn).not.toHaveBeenCalled()
203256
})
204257

205258
it("deduplicates concurrent ensureStarted calls", async () => {
@@ -241,7 +294,7 @@ describe("CsCloudService (refactored)", () => {
241294
const svc = new CsCloudService(createOutputChannel() as never)
242295
await svc.ensureStarted()
243296
expect(svc.state).toBe("running")
244-
expect(svc.ownership).toBe("owned")
297+
expect(svc.processOwner).toBe("extension")
245298
expect(spawnExitRegistered).toBe(true)
246299
})
247300

@@ -252,7 +305,7 @@ describe("CsCloudService (refactored)", () => {
252305
const svc = new CsCloudService(createOutputChannel() as never)
253306
await svc.ensureStarted()
254307
expect(svc.state).toBe("running")
255-
expect(svc.ownership).toBe("unmanaged")
308+
expect(svc.processOwner).toBe("external")
256309

257310
const crashedPromise = new Promise<string>((resolve) => {
258311
svc.on("crashed", ({ reason }: { reason: string }) => resolve(reason))

src/__tests__/assistant-ui-panel.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
addNonceToScriptTags,
3737
injectIntoHead,
3838
buildAssistantUIFrameUrl,
39+
getAssistantUIIframeHtml,
3940
getAssistantUIStaticHtml,
4041
getAssistantUIStaticOutDir,
4142
rewriteStaticAssetUrls,
@@ -230,12 +231,31 @@ describe("AssistantUIPanel", () => {
230231

231232
expect(html).toContain("http://127.0.0.1:45489")
232233
expect(html).toContain(`href="vscode-resource:${outDir}/costrict/logo.png"`)
234+
expect(html).toContain('e.data?.type === "restartCsCloudServer"')
235+
expect(html).toContain('v.postMessage({ type: "restartCsCloudServer" })')
233236
expect(html).not.toContain('href="/costrict/logo.png"')
234237
} finally {
235238
fs.rmSync(extensionRoot, { recursive: true, force: true })
236239
}
237240
})
238241

242+
it("forwards server restart messages between iframe and extension host", () => {
243+
const webview = {
244+
cspSource: "vscode-webview://test-csp-source",
245+
}
246+
247+
const html = getAssistantUIIframeHtml(
248+
webview as never,
249+
{ extensionUri: { fsPath: "/tmp/test-extension" } } as never,
250+
"http://127.0.0.1:45489/api/v1",
251+
"http://127.0.0.1:3000",
252+
)
253+
254+
expect(html).toContain('event.data?.type === "restartCsCloudServer"')
255+
expect(html).toContain('vscodeApi.postMessage({ type: "restartCsCloudServer" })')
256+
expect(html).toContain('event.data?.type === "restartCsCloudServerFailed"')
257+
})
258+
239259
it("preserves Request method, headers, and body in the static Webview fetch proxy", () => {
240260
const extensionRoot = fs.mkdtempSync(path.join(os.tmpdir(), "cs-cloud-ui-fetch-proxy-"))
241261
try {

0 commit comments

Comments
 (0)