From 89ee54ebb7e094314530272ff13ea9470b5e1549 Mon Sep 17 00:00:00 2001 From: imccyu <276526105+imccyu@users.noreply.github.com> Date: Thu, 27 Aug 2026 20:00:42 +0800 Subject: [PATCH] test(api): align migrated client contracts --- .../session-open-workspace-path.host.spec.ts | 8 +++---- .../tests/session-skills.host.spec.ts | 21 ++++++++----------- packages/api/settings-controller/src/index.ts | 13 ++++++++---- .../tests/settings-controller.host.spec.ts | 12 ++++++----- .../client/connection/src/client/fixture.ts | 6 ++---- .../tests/stores.client.spec.ts | 8 +++---- 6 files changed, 35 insertions(+), 33 deletions(-) diff --git a/packages/api/session-controller/tests/session-open-workspace-path.host.spec.ts b/packages/api/session-controller/tests/session-open-workspace-path.host.spec.ts index ca7a78c589..2727fb96c2 100644 --- a/packages/api/session-controller/tests/session-open-workspace-path.host.spec.ts +++ b/packages/api/session-controller/tests/session-open-workspace-path.host.spec.ts @@ -114,11 +114,11 @@ describe('session/openWorkspacePath', () => { }, aborted.signal)).rejects.toMatchObject({ failure: { code: 'cancelled' } }) inspect.mockRejectedValueOnce('storage offline') - await expect(controller.openWorkspacePath({ + const failed = controller.openWorkspacePath({ sessionId: SessionId('inspection-failed'), path: 'result.html', - }, new AbortController().signal)).rejects.toMatchObject({ - failure: { code: 'internal', message: expect.stringContaining('storage offline') }, - }) + }, new AbortController().signal) + await expect(failed).rejects.toMatchObject({ failure: { code: 'internal' } }) + await expect(failed).rejects.toThrow('storage offline') }) it('classifies opener cancellation and non-Error failures', async () => { diff --git a/packages/api/session-controller/tests/session-skills.host.spec.ts b/packages/api/session-controller/tests/session-skills.host.spec.ts index 32d9957d4b..5b5e1b2a5a 100644 --- a/packages/api/session-controller/tests/session-skills.host.spec.ts +++ b/packages/api/session-controller/tests/session-skills.host.spec.ts @@ -183,10 +183,9 @@ describe('SessionSkillCatalog', () => { } as never) const catalog = new SessionSkillCatalog(ctx) - await expect(catalog.list({ sessionId }, new AbortController().signal)) - .rejects.toMatchObject({ - failure: { code: 'internal', message: expect.stringContaining('skill registry is absent') }, - }) + const failed = catalog.list({ sessionId }, new AbortController().signal) + await expect(failed).rejects.toMatchObject({ failure: { code: 'internal' } }) + await expect(failed).rejects.toThrow('skill registry is absent') }) it('rejects observations without projections or a project cwd', async () => { @@ -199,14 +198,12 @@ describe('SessionSkillCatalog', () => { ctx.provide('sessionQuery', { observeSession } as never) const catalog = new SessionSkillCatalog(ctx) - await expect(catalog.list({ sessionId }, new AbortController().signal)) - .rejects.toMatchObject({ - failure: { code: 'internal', message: expect.stringContaining('projected Session observation') }, - }) - await expect(catalog.list({ sessionId }, new AbortController().signal)) - .rejects.toMatchObject({ - failure: { code: 'internal', message: expect.stringContaining('has no project cwd') }, - }) + const unprojected = catalog.list({ sessionId }, new AbortController().signal) + await expect(unprojected).rejects.toMatchObject({ failure: { code: 'internal' } }) + await expect(unprojected).rejects.toThrow('projected Session observation') + const cwdless = catalog.list({ sessionId }, new AbortController().signal) + await expect(cwdless).rejects.toMatchObject({ failure: { code: 'internal' } }) + await expect(cwdless).rejects.toThrow('has no project cwd') }) it('classifies a provider listing failure', async () => { diff --git a/packages/api/settings-controller/src/index.ts b/packages/api/settings-controller/src/index.ts index a61dfd329e..393a1dec81 100644 --- a/packages/api/settings-controller/src/index.ts +++ b/packages/api/settings-controller/src/index.ts @@ -37,6 +37,11 @@ export type * from './types.ts' const settingsNamespaceRequestSchema = z.object({ ns: z.string().min(1) }) +/** Read abort state afresh after an awaited provider or opener call. */ +function isAborted(signal: AbortSignal): boolean { + return signal.aborted +} + /** Native document-opening policy. */ export interface Config { /** Override platform desktop-opener detection. */ @@ -185,23 +190,23 @@ export class SettingsController extends TypertRemoteService { @Remote async openSettingsDocument(signal: AbortSignal): Promise { const settings = this.provider() - if (signal.aborted) throw cancelled('settings document open was aborted') + if (isAborted(signal)) throw cancelled('settings document open was aborted') let path: string | undefined try { path = await settings.prepareDocument() } catch (error: unknown) { - if (signal.aborted) throw cancelled('settings document preparation was aborted') + if (isAborted(signal)) throw cancelled('settings document preparation was aborted') throw internal(`settings document preparation failed: ${messageOf(error)}`) } if (path === undefined) { throw internal('settings provider has no local document to open') } - if (signal.aborted) throw cancelled('settings document open was aborted') + if (isAborted(signal)) throw cancelled('settings document open was aborted') try { await this.openTextFile(path, signal) return { opened: true } } catch (error: unknown) { - if (signal.aborted) throw cancelled('settings document open was aborted') + if (isAborted(signal)) throw cancelled('settings document open was aborted') throw internal(`path open failed: ${messageOf(error)}`) } } diff --git a/packages/api/settings-controller/tests/settings-controller.host.spec.ts b/packages/api/settings-controller/tests/settings-controller.host.spec.ts index d43232ae62..aa8945b4fc 100644 --- a/packages/api/settings-controller/tests/settings-controller.host.spec.ts +++ b/packages/api/settings-controller/tests/settings-controller.host.spec.ts @@ -263,13 +263,15 @@ describe('the settings Remote namespace a configuration page calls', () => { it('preserves settings-document absence, failure, and cancellation', async () => { const absent = await boot() - await expect(absent.controller.openSettingsDocument(new AbortController().signal)) - .rejects.toMatchObject({ failure: { code: 'internal', message: expect.stringContaining('no local document') } }) + const missingDocument = absent.controller.openSettingsDocument(new AbortController().signal) + await expect(missingDocument).rejects.toMatchObject({ failure: { code: 'internal' } }) + await expect(missingDocument).rejects.toThrow('no local document') const failed = await boot(DocumentSettings) vi.spyOn(failed.ctx.settings, 'prepareDocument').mockRejectedValue(new Error('read failed')) - await expect(failed.controller.openSettingsDocument(new AbortController().signal)) - .rejects.toMatchObject({ failure: { code: 'internal', message: expect.stringContaining('read failed') } }) + const failedRead = failed.controller.openSettingsDocument(new AbortController().signal) + await expect(failedRead).rejects.toMatchObject({ failure: { code: 'internal' } }) + await expect(failedRead).rejects.toThrow('read failed') const cancelled = new AbortController() cancelled.abort(new Error('cancelled')) @@ -412,7 +414,7 @@ describe('the settings Remote namespace a configuration page calls', () => { ['unexpected preset failure', 'internal'], ] as const)('maps Agent preset resolution failure %#', async (error, code) => { const ctx = new Context() - ctx.provide('agentPresets', { resolve: () => Promise.reject(error) } as never) + ctx.provide('agentPresets', { resolve: async () => { throw error } } as never) const controller = new SettingsController(ctx) await expect(controller.openAgentPresetDirectory('mine', new AbortController().signal)) diff --git a/packages/client/connection/src/client/fixture.ts b/packages/client/connection/src/client/fixture.ts index a5689069c2..e39c39db01 100644 --- a/packages/client/connection/src/client/fixture.ts +++ b/packages/client/connection/src/client/fixture.ts @@ -3657,12 +3657,10 @@ export class FixtureApiClient extends AbstractApiClient { /** Method-key dispatch into the in-memory contract impl (a real carrier routes by URL path instead). */ private dispatch( - method: keyof RpcMethodMap, + _method: keyof RpcMethodMap, request: RpcRequest, ): Promise> { - switch (method) { - case 'host.describe': return this.api.host.describe(request) - } + return this.api.host.describe(request) } } diff --git a/packages/client/ui-settings-plugins/tests/stores.client.spec.ts b/packages/client/ui-settings-plugins/tests/stores.client.spec.ts index 2fefbb0cc8..ffdb0d8cf3 100644 --- a/packages/client/ui-settings-plugins/tests/stores.client.spec.ts +++ b/packages/client/ui-settings-plugins/tests/stores.client.spec.ts @@ -668,7 +668,7 @@ describe('SubagentModelSelectionCardController', () => { }) .mockImplementationOnce(() => refreshed.promise) const controller = new SubagentModelSelectionCardController( - host.scope, { modelCatalog: models } as never, + host.scope, { modelCatalog: models }, ) const face = controller.inject() const state = () => face.hooks.subagentModelSelectionCard.getSnapshot() @@ -747,7 +747,7 @@ describe('SubagentModelSelectionCardController', () => { }, }) const controller = new SubagentModelSelectionCardController( - host.scope, { modelCatalog: models } as never, + host.scope, { modelCatalog: models }, ) const state = () => controller.inject().hooks.subagentModelSelectionCard.getSnapshot() await vi.waitFor(() => { expect(state().candidates[0]?.provider).toBe('alpha') }) @@ -802,7 +802,7 @@ describe('SubagentModelSelectionCardController', () => { const pending = deferred() const models = vi.fn(() => pending.promise) - const controller = new SubagentModelSelectionCardController(host.scope, { modelCatalog: models } as never) + const controller = new SubagentModelSelectionCardController(host.scope, { modelCatalog: models }) const face = controller.inject() face.toggleEnabled() face.retryCatalog() @@ -814,7 +814,7 @@ describe('SubagentModelSelectionCardController', () => { const pendingResolve = deferred() const resolving = new SubagentModelSelectionCardController( host.scope, - { modelCatalog: () => pendingResolve.promise } as never, + { modelCatalog: () => pendingResolve.promise }, ) const resolvingFace = resolving.inject() resolvingFace.toggleEnabled()