diff --git a/src/main/browser/browser-session-registry.persistence.test.ts b/src/main/browser/browser-session-registry.persistence.test.ts index f95469b58c8..9c20fdc0d2c 100644 --- a/src/main/browser/browser-session-registry.persistence.test.ts +++ b/src/main/browser/browser-session-registry.persistence.test.ts @@ -164,10 +164,9 @@ function installModuleMocks( fsState.files.set(targetKey, value) } }), + // Nothing on this path calls writeFileAtomically; it is here only to keep the module shape + // complete. The identity write goes through node:fs above, which is where failure is injected. writeFileAtomically: vi.fn((pathValue: string, data: string) => { - if (failIdentityWrite && pathValue.endsWith('browser-identity-mode.json')) { - throw new Error('read-only userData') - } const key = fsKey(pathValue) fsState.files.set(key, data) fsState.present.add(key) diff --git a/src/main/runtime/orca-runtime-tests/browser-capabilities.spec.ts b/src/main/runtime/orca-runtime-tests/browser-capabilities.spec.ts index 6b09d1263b1..ffe98483585 100644 --- a/src/main/runtime/orca-runtime-tests/browser-capabilities.spec.ts +++ b/src/main/runtime/orca-runtime-tests/browser-capabilities.spec.ts @@ -23,8 +23,29 @@ import { attachClientBrowserHost, publishClientHostedPage } from '../orca-runtime-test-scenario-builders.spec' +import { mkdtempSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { + initializeBrowserIdentityModeStore, + resetBrowserIdentityModeStoreForTests +} from '../../browser/browser-identity-mode-store' describe('OrcaRuntimeService', () => { + // The mixed-version guarantee: a host that never initialized the identity store must not + // advertise a method that can only throw there. + it('advertises the browser identity capability only where an identity store exists', () => { + resetBrowserIdentityModeStoreForTests() + expect(createRuntime().getStatus().capabilities).not.toContain('browser.identity.v1') + + initializeBrowserIdentityModeStore(mkdtempSync(join(tmpdir(), 'orca-identity-capability-'))) + try { + expect(createRuntime().getStatus().capabilities).toContain('browser.identity.v1') + } finally { + resetBrowserIdentityModeStoreForTests() + } + }) + it('advertises headless browser capability when an offscreen backend backs a windowless host', () => { const runtime = createRuntime() runtime.setOffscreenBrowserBackend({ createTab: vi.fn(), closeTab: vi.fn() }) diff --git a/src/main/runtime/rpc/methods/browser.test.ts b/src/main/runtime/rpc/methods/browser.test.ts index e6b9d8dd891..b5df3a6616b 100644 --- a/src/main/runtime/rpc/methods/browser.test.ts +++ b/src/main/runtime/rpc/methods/browser.test.ts @@ -77,8 +77,12 @@ describe('browser RPC methods', () => { // The schema check above proves the shape; this proves an older client actually gets the // rejection over the wire instead of a success with the field quietly dropped. it('rejects the retired profile user-agent field through the dispatcher', async () => { - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: params parsing fails before any runtime member is read. - const runtime = { getRuntimeId: () => 'test-runtime' } as unknown as OrcaRuntimeService + const browserProfileCreate = vi.fn().mockResolvedValue({ id: 'profile-1' }) + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the dispatcher reads only getRuntimeId and the single browser method stubbed here. + const runtime = { + getRuntimeId: () => 'test-runtime', + browserProfileCreate + } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: BROWSER_CORE_METHODS }) const response = await dispatcher.dispatch( @@ -89,8 +93,11 @@ describe('browser RPC methods', () => { }) ) + // Why a working runtime stub: if the field were accepted and stripped again the call would + // succeed, so every assertion below is load-bearing rather than passing on a missing method. expect(response).toMatchObject({ ok: false }) expect(JSON.stringify(response)).toContain('browser_profile_user_agent_mode_is_now_app_wide') + expect(browserProfileCreate).not.toHaveBeenCalled() }) it('routes core browser automation commands to the runtime server', async () => {