mirror of
https://github.com/stablyai/orca.git
synced 2026-09-26 08:02:38 +00:00
fix(mobile): degrade a null ui.get result the way the settings leg now does
One host answers both legs of useNewWorkspaceRuntimeContext, so fixing only settings.get left the likelier failure in place: a null or absent ui.get result still threw `reading 'ui'` out of the effect, skipping the provider commit. Review follow-ups on the same files: reply() returns the literal uncast and the stub client is FakeSession, dropping two assertions and their SAFETY disables; the degradation cases now assert absolute state instead of comparing mounts. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
This commit is contained in:
@@ -1,35 +1,41 @@
|
||||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { FakeSession } from '../transport/mobile-endpoint-supervisor-test-fakes'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { RpcResponse } from '../transport/types'
|
||||
import { useNewWorkspaceRuntimeContext } from './use-new-workspace-runtime-context'
|
||||
|
||||
type RuntimeContext = ReturnType<typeof useNewWorkspaceRuntimeContext>
|
||||
type PublishedState = Pick<
|
||||
RuntimeContext,
|
||||
'runtimeSettings' | 'trustedOrcaHooks' | 'availableProviders'
|
||||
>
|
||||
|
||||
const TRUSTED_HOOKS = { '/repo/orca.yaml': 'sha-1' }
|
||||
const UI_WITH_TRUST = { ui: { trustedOrcaHooks: TRUSTED_HOOKS } }
|
||||
const SETTINGS = { defaultTuiAgent: 'codex', visibleTaskProviders: ['github', 'linear'] }
|
||||
|
||||
function reply(result: unknown): RpcResponse {
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the test scripts raw host replies, not validated payloads.
|
||||
return { id: 'r', ok: true, result, _meta: { runtimeId: 'runtime-1' } } as RpcResponse
|
||||
return { id: 'r', ok: true, result, _meta: { runtimeId: 'runtime-1' } }
|
||||
}
|
||||
|
||||
/** Every prerequisite answers normally; only the settings result varies. */
|
||||
function clientAnsweringSettingsWith(settingsResult: unknown): RpcClient {
|
||||
const sendRequest = vi.fn(async (method: string) => {
|
||||
/** Every prerequisite answers normally; only the two reads under test vary. */
|
||||
function clientAnswering(settingsResult: unknown, uiResult: unknown): RpcClient {
|
||||
const client = new FakeSession('connected')
|
||||
client.sendRequest.mockImplementation(async (method: string) => {
|
||||
switch (method) {
|
||||
case 'settings.get':
|
||||
return reply(settingsResult)
|
||||
case 'ui.get':
|
||||
return reply({ ui: { trustedOrcaHooks: TRUSTED_HOOKS } })
|
||||
return reply(uiResult)
|
||||
case 'preflight.check':
|
||||
return reply({ glab: { installed: false } })
|
||||
default:
|
||||
return reply({ connected: false })
|
||||
}
|
||||
})
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the hook reaches only sendRequest on the client.
|
||||
return { sendRequest } as unknown as RpcClient
|
||||
return client
|
||||
}
|
||||
|
||||
describe('useNewWorkspaceRuntimeContext', () => {
|
||||
@@ -40,9 +46,9 @@ describe('useNewWorkspaceRuntimeContext', () => {
|
||||
renderer = null
|
||||
})
|
||||
|
||||
async function mount(settingsResult: unknown): Promise<RuntimeContext> {
|
||||
async function mount(settingsResult: unknown, uiResult: unknown): Promise<PublishedState> {
|
||||
// One client for the whole mount: the hook keys its effect on client identity.
|
||||
const client = clientAnsweringSettingsWith(settingsResult)
|
||||
const client = clientAnswering(settingsResult, uiResult)
|
||||
let context!: RuntimeContext
|
||||
function Harness(): null {
|
||||
context = useNewWorkspaceRuntimeContext(client, true)
|
||||
@@ -52,48 +58,43 @@ describe('useNewWorkspaceRuntimeContext', () => {
|
||||
renderer = create(createElement(Harness))
|
||||
})
|
||||
await act(async () => {})
|
||||
return context
|
||||
const { runtimeSettings, trustedOrcaHooks, availableProviders } = context
|
||||
return { runtimeSettings, trustedOrcaHooks, availableProviders }
|
||||
}
|
||||
|
||||
it('degrades a null result the way a reply without a settings member degrades', async () => {
|
||||
const absent = await mount({})
|
||||
const absentState = {
|
||||
runtimeSettings: absent.runtimeSettings,
|
||||
trustedOrcaHooks: absent.trustedOrcaHooks,
|
||||
availableProviders: absent.availableProviders
|
||||
}
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
|
||||
const nullResult = await mount(null)
|
||||
expect({
|
||||
runtimeSettings: nullResult.runtimeSettings,
|
||||
trustedOrcaHooks: nullResult.trustedOrcaHooks,
|
||||
availableProviders: nullResult.availableProviders
|
||||
}).toEqual(absentState)
|
||||
// The state the property-read TypeError used to skip on its way out of the effect.
|
||||
expect(absentState).toEqual({
|
||||
// A null result used to throw the `settings` property read out of the effect, skipping the
|
||||
// provider commit the absent case still reached.
|
||||
it.each([
|
||||
['null', null],
|
||||
['absent', undefined],
|
||||
['without a settings member', {}]
|
||||
])('degrades a %s settings result to absent settings', async (_label, settingsResult) => {
|
||||
expect(await mount(settingsResult, UI_WITH_TRUST)).toEqual({
|
||||
runtimeSettings: null,
|
||||
trustedOrcaHooks: TRUSTED_HOOKS,
|
||||
availableProviders: ['github']
|
||||
})
|
||||
})
|
||||
|
||||
it('degrades an absent result the same way', async () => {
|
||||
const context = await mount(undefined)
|
||||
expect(context.runtimeSettings).toBeNull()
|
||||
expect(context.trustedOrcaHooks).toEqual(TRUSTED_HOOKS)
|
||||
expect(context.availableProviders).toEqual(['github'])
|
||||
// Same defect on the sibling leg: `reading 'ui'` threw after the settings commit and before
|
||||
// the provider commit.
|
||||
it.each([
|
||||
['null', null],
|
||||
['absent', undefined],
|
||||
['without a ui member', {}]
|
||||
])('degrades a %s ui result to untrusted hooks', async (_label, uiResult) => {
|
||||
expect(await mount({ settings: SETTINGS }, uiResult)).toEqual({
|
||||
runtimeSettings: SETTINGS,
|
||||
trustedOrcaHooks: {},
|
||||
availableProviders: ['github']
|
||||
})
|
||||
})
|
||||
|
||||
it('publishes the settings a host does send', async () => {
|
||||
const context = await mount({
|
||||
settings: { defaultTuiAgent: 'codex', visibleTaskProviders: ['github', 'linear'] }
|
||||
it('publishes the settings and trust a host does send', async () => {
|
||||
expect(await mount({ settings: SETTINGS }, UI_WITH_TRUST)).toEqual({
|
||||
runtimeSettings: SETTINGS,
|
||||
trustedOrcaHooks: TRUSTED_HOOKS,
|
||||
availableProviders: ['github']
|
||||
})
|
||||
expect(context.runtimeSettings).toEqual({
|
||||
defaultTuiAgent: 'codex',
|
||||
visibleTaskProviders: ['github', 'linear']
|
||||
})
|
||||
expect(context.availableProviders).toEqual(['github'])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -60,7 +60,12 @@ export function useNewWorkspaceRuntimeContext(
|
||||
}
|
||||
const uiResult = settledSuccess(uiRes)
|
||||
if (uiResult) {
|
||||
const ui = (uiResult.result as { ui?: { trustedOrcaHooks?: PersistedTrustedOrcaHooks } }).ui
|
||||
const ui = (
|
||||
uiResult.result as
|
||||
| { ui?: { trustedOrcaHooks?: PersistedTrustedOrcaHooks } }
|
||||
| null
|
||||
| undefined
|
||||
)?.ui
|
||||
setTrustedOrcaHooks(ui?.trustedOrcaHooks ?? {})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user