fix(gui): harden multimodal image attachments
This commit is contained in:
@@ -23,9 +23,11 @@ describe('connection lifecycle', () => {
|
||||
it('announces connected after describe + both streams open, then pumps frames to sinks', async () => {
|
||||
const api = new FakeApiClient()
|
||||
const muxSeen: string[] = []
|
||||
const descriptions: string[] = []
|
||||
let connected = 0
|
||||
const controller = new ConnectionController(api, {
|
||||
onMuxEnvelope: envelope => muxSeen.push(envelope.payload.type),
|
||||
onDescription: description => descriptions.push(description.version),
|
||||
onConnected: () => { connected++ },
|
||||
}, FAST)
|
||||
controller.start()
|
||||
@@ -34,6 +36,7 @@ describe('connection lifecycle', () => {
|
||||
api.pushMux(subscribedFrame())
|
||||
await vi.waitFor(() => { expect(muxSeen).toEqual(['session/subscribed']) })
|
||||
expect(api.callsOf('host.describe')).toHaveLength(1)
|
||||
expect(descriptions).toEqual(['0-fake'])
|
||||
} finally {
|
||||
controller.stop()
|
||||
}
|
||||
@@ -83,6 +86,35 @@ describe('connection lifecycle', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('treats a host.describe business error as generation failure', async () => {
|
||||
const api = new FakeApiClient()
|
||||
let describeCalls = 0
|
||||
api.onDescribe = () => {
|
||||
describeCalls += 1
|
||||
if (describeCalls === 1) {
|
||||
return Promise.resolve({
|
||||
rpcId: 'bad-describe' as never,
|
||||
result: {
|
||||
ok: false as const,
|
||||
error: { code: 'internal' as const, message: 'not ready', details: {} },
|
||||
},
|
||||
})
|
||||
}
|
||||
return Promise.resolve(ok({ version: '0', cwd: '/f', attachedSessions: 0 }))
|
||||
}
|
||||
let connected = 0
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined)
|
||||
const controller = new ConnectionController(api, { onConnected: () => { connected++ } }, FAST)
|
||||
controller.start()
|
||||
try {
|
||||
await vi.waitFor(() => { expect(describeCalls).toBe(2) })
|
||||
await vi.waitFor(() => { expect(connected).toBe(1) })
|
||||
} finally {
|
||||
controller.stop()
|
||||
warnSpy.mockRestore()
|
||||
}
|
||||
})
|
||||
|
||||
it('converges stream/error frames into reconnect instead of dispatching them', async () => {
|
||||
const api = new FakeApiClient()
|
||||
const muxSeen: string[] = []
|
||||
|
||||
@@ -207,6 +207,31 @@ describe('createFixtureApi', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('accounts for every base64 padding form and reports a missing fixture attachment', async () => {
|
||||
const api = createFixtureApi()
|
||||
const created = await api.sessions.create(req({}))
|
||||
if (!created.result.ok) throw new Error('create failed')
|
||||
const sessionId = created.result.value.sessionId
|
||||
const prompted = await api.sessions.prompt(req({
|
||||
sessionId,
|
||||
mode: 'queue' as const,
|
||||
content: ['YQ==', 'YWI=', 'YWJj'].map(data => ({
|
||||
type: 'image' as const,
|
||||
mediaType: 'image/png' as const,
|
||||
data,
|
||||
})),
|
||||
}))
|
||||
expect(prompted.result.ok).toBe(true)
|
||||
const missing = await api.sessions.attachment(req({
|
||||
sessionId,
|
||||
attachmentId: 'fixture:missing' as never,
|
||||
}))
|
||||
expect(missing.result).toMatchObject({
|
||||
ok: false, error: { details: { reason: 'ATTACHMENT_NOT_FOUND' } },
|
||||
})
|
||||
await api.sessions.cancel(req({ sessionId }))
|
||||
})
|
||||
|
||||
it('gamma interval flip emits host/session-status and a running log-less session subscribes at lastSeq -1', async () => {
|
||||
vi.useFakeTimers()
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user