fix(frontend): refine streamed presentation and resolve stream edge cases

This commit is contained in:
Justin Visser 2026-08-10 21:38:14 +02:00
parent a3af8f45cc
commit 2cc33a721c
18 changed files with 450 additions and 46 deletions

View file

@ -13,14 +13,16 @@ vi.mock('../src/lib/recommendationStream', async (importOriginal) => {
const streamMock = vi.mocked(streamRecommendations)
const unmountCallbacks: Array<() => void> = []
function mountChat() {
function mountChat(
createPlaylist: Parameters<typeof useChatStream>[0] = vi.fn(async () => ({ url: null })),
) {
let chat: ReturnType<typeof useChatStream> | undefined
const root = document.createElement('div')
document.body.append(root)
const app = createApp(
defineComponent({
setup() {
chat = useChatStream(vi.fn(async () => ({ url: null })))
chat = useChatStream(createPlaylist)
return () => h('div')
},
}),
@ -106,6 +108,22 @@ describe('useChatStream', () => {
expect(turn?.transportFailure).toBeNull()
})
it('stores a metadata-free terminal error as a normal stream error', async () => {
streamMock.mockImplementation(async (_request, _signal, onEvent) => {
onEvent({ type: 'error', code: 'first_stage_failed', message: 'Planning failed.' })
})
const chat = mountChat()
await chat.send('error')
expect(assistantTurns(chat)[0]).toMatchObject({
status: 'error',
requestId: null,
error: { code: 'first_stage_failed', message: 'Planning failed.' },
transportFailure: null,
})
})
it('stores an incomplete stream as a transport failure', async () => {
streamMock.mockRejectedValue(
new StreamTransportError('unexpected_eof', 'The stream ended early.'),
@ -239,4 +257,26 @@ describe('useChatStream', () => {
expect(receivedQuery).toHaveLength(1000)
})
it('uses the bare query as the capped playlist name', async () => {
const createPlaylist = vi.fn(async () => ({ url: null }))
streamMock.mockImplementation(async (_request, _signal, onEvent) => {
onEvent(metadata('playlist'))
onEvent(track(1))
onEvent({ type: 'done', track_count: 1, total_ms: 5 })
})
const chat = mountChat(createPlaylist)
const query = `Late-night instrumental focus ${'x'.repeat(100)}`
await chat.send(query)
const turn = assistantTurns(chat)[0]
if (!turn) throw new Error('Assistant turn was not created.')
await chat.savePlaylist(turn.id)
expect(createPlaylist).toHaveBeenCalledWith({
schema_version: 1,
name: query.slice(0, 100),
track_uris: ['spotify:track:1'],
})
})
})