mirror of
https://github.com/stablyai/orca.git
synced 2026-11-14 01:47:24 +00:00
170 lines
5.1 KiB
TypeScript
170 lines
5.1 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import { getOpenCode2SetupSource } from './status-plugin-setup-source'
|
|
|
|
type Hooks = { event: (event: unknown) => Promise<void>; dispose?: () => Promise<void> }
|
|
|
|
function createSetup(factory: () => Promise<Hooks>) {
|
|
const setup: unknown = new Function(
|
|
'OrcaOpenCodeStatusPlugin',
|
|
'AbortController',
|
|
'console',
|
|
`${getOpenCode2SetupSource().join('\n')}\nreturn setupOpenCode2Status;`
|
|
)(factory, AbortController, { warn: vi.fn() })
|
|
if (typeof setup !== 'function') {
|
|
throw new Error('Missing generated setup')
|
|
}
|
|
return async (context: unknown) => {
|
|
const cleanup: unknown = await setup(context)
|
|
if (typeof cleanup !== 'function') {
|
|
throw new Error('Missing generated cleanup')
|
|
}
|
|
return cleanup
|
|
}
|
|
}
|
|
|
|
function deferred() {
|
|
let finish = () => {}
|
|
const promise = new Promise<void>((resolve) => {
|
|
finish = resolve
|
|
})
|
|
return { promise, finish }
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn(async () => new Response('{}', { status: 200 }))
|
|
)
|
|
})
|
|
afterEach(() => {
|
|
expect(fetch).not.toHaveBeenCalled()
|
|
vi.unstubAllGlobals()
|
|
})
|
|
|
|
describe('generated OpenCode 2 setup ownership', () => {
|
|
it.each(['throw', 'reject'] as const)(
|
|
'disposes the factory after prompt registration %s',
|
|
async (failure) => {
|
|
const hooks = { event: vi.fn(async () => {}), dispose: vi.fn(async () => {}) }
|
|
const subscribe = vi.fn(async function* () {})
|
|
const hook = () => {
|
|
if (failure === 'throw') {
|
|
throw new Error('Registration failed')
|
|
}
|
|
return Promise.reject(new Error('Registration failed'))
|
|
}
|
|
const cleanup = await createSetup(async () => hooks)({
|
|
session: { hook },
|
|
event: { subscribe }
|
|
})
|
|
expect(hooks.dispose).toHaveBeenCalledOnce()
|
|
expect(subscribe).not.toHaveBeenCalled()
|
|
await cleanup()
|
|
expect(hooks.dispose).toHaveBeenCalledOnce()
|
|
}
|
|
)
|
|
|
|
it.each(['throw', 'reject'] as const)(
|
|
'disposes the factory after prompt cleanup %s',
|
|
async (failure) => {
|
|
const hooks = { event: vi.fn(async () => {}), dispose: vi.fn(async () => {}) }
|
|
const promptDispose = vi.fn(() => {
|
|
if (failure === 'throw') {
|
|
throw new Error('Prompt cleanup failed')
|
|
}
|
|
return Promise.reject(new Error('Prompt cleanup failed'))
|
|
})
|
|
let signal: AbortSignal | undefined
|
|
const cleanup = await createSetup(async () => hooks)({
|
|
session: { hook: async () => ({ dispose: promptDispose }) },
|
|
event: {
|
|
subscribe: (options: { signal: AbortSignal }) => {
|
|
signal = options.signal
|
|
return []
|
|
}
|
|
}
|
|
})
|
|
await cleanup()
|
|
expect(signal?.aborted).toBe(true)
|
|
expect(promptDispose).toHaveBeenCalledOnce()
|
|
expect(hooks.dispose).toHaveBeenCalledOnce()
|
|
}
|
|
)
|
|
|
|
it('waits for in-flight delivery before factory cleanup after a prompt cleanup failure', async () => {
|
|
const delivery = deferred()
|
|
const started = deferred()
|
|
const order: string[] = []
|
|
const hooks = {
|
|
event: vi.fn(async () => {
|
|
started.finish()
|
|
await delivery.promise
|
|
order.push('event')
|
|
}),
|
|
dispose: vi.fn(async () => {
|
|
order.push('factory')
|
|
})
|
|
}
|
|
const cleanup = await createSetup(async () => hooks)({
|
|
session: {
|
|
hook: async () => ({
|
|
dispose: async () => {
|
|
order.push('prompt')
|
|
throw new Error('Failed')
|
|
}
|
|
})
|
|
},
|
|
event: {
|
|
subscribe: async function* ({ signal }: { signal: AbortSignal }) {
|
|
try {
|
|
yield { type: 'session.execution.started', data: { sessionID: 'synthetic' } }
|
|
if (!signal.aborted) {
|
|
yield { type: 'session.execution.succeeded', data: { sessionID: 'synthetic' } }
|
|
}
|
|
} finally {
|
|
order.push('subscription')
|
|
}
|
|
}
|
|
}
|
|
})
|
|
await started.promise
|
|
const disposing = cleanup()
|
|
await Promise.resolve()
|
|
expect(hooks.dispose).not.toHaveBeenCalled()
|
|
delivery.finish()
|
|
await disposing
|
|
expect(order).toEqual(['prompt', 'event', 'subscription', 'factory'])
|
|
expect(hooks.event).toHaveBeenCalledOnce()
|
|
expect(hooks.dispose).toHaveBeenCalledOnce()
|
|
})
|
|
|
|
it.each(['setup', 'unload'] as const)(
|
|
'keeps %s fail-open when factory cleanup also rejects',
|
|
async (stage) => {
|
|
const hooks = {
|
|
event: vi.fn(async () => {}),
|
|
dispose: vi.fn(async () => {
|
|
throw new Error('Factory cleanup failed')
|
|
})
|
|
}
|
|
const cleanup = await createSetup(async () => hooks)({
|
|
session: {
|
|
hook: async () => {
|
|
if (stage === 'setup') {
|
|
throw new Error('Registration failed')
|
|
}
|
|
return {
|
|
dispose: async () => {
|
|
throw new Error('Prompt cleanup failed')
|
|
}
|
|
}
|
|
}
|
|
},
|
|
event: { subscribe: async function* () {} }
|
|
})
|
|
await cleanup()
|
|
expect(hooks.dispose).toHaveBeenCalledOnce()
|
|
}
|
|
)
|
|
})
|