mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
fix(hub,web): handle unavailable voice backends
via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run>
This commit is contained in:
@@ -488,6 +488,21 @@ describe('GET /api/voice/backend', () => {
|
||||
}
|
||||
})
|
||||
|
||||
test('returns no backend when no voice credentials are configured', async () => {
|
||||
delete process.env.VOICE_BACKEND
|
||||
delete process.env.ELEVENLABS_API_KEY
|
||||
delete process.env.GEMINI_API_KEY
|
||||
delete process.env.GOOGLE_API_KEY
|
||||
delete process.env.DASHSCOPE_API_KEY
|
||||
delete process.env.QWEN_API_KEY
|
||||
const app = createApp()
|
||||
const headers = await authHeaders()
|
||||
const res = await app.request('/api/voice/backend', { headers })
|
||||
expect(res.status).toBe(200)
|
||||
const body = await res.json() as { backend: string | null; backends: string[] }
|
||||
expect(body).toEqual({ backend: null, backends: [] })
|
||||
})
|
||||
|
||||
test('returns elevenlabs by default with backends list', async () => {
|
||||
delete process.env.VOICE_BACKEND
|
||||
delete process.env.GEMINI_API_KEY
|
||||
|
||||
@@ -127,4 +127,14 @@ describe('ApiClient error mapping', () => {
|
||||
expect(init?.body).toBeInstanceOf(FormData)
|
||||
expect(new Headers(init?.headers).has('content-type')).toBe(false)
|
||||
})
|
||||
|
||||
it('preserves an unavailable voice backend response', async () => {
|
||||
fetchMock.mockResolvedValueOnce(
|
||||
new Response(JSON.stringify({ backend: null, backends: [] }), { status: 200 })
|
||||
)
|
||||
|
||||
const api = new ApiClient('test-token')
|
||||
await expect(api.fetchVoiceBackend()).resolves.toEqual({ backend: null, backends: [] })
|
||||
expect(fetchMock.mock.calls[0]?.[0]).toBe('/api/voice/backend')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -951,7 +951,7 @@ export class ApiClient {
|
||||
return this.getToken ? this.getToken() : this.token
|
||||
}
|
||||
|
||||
async fetchVoiceBackend(): Promise<{ backend: string; backends: string[] }> {
|
||||
async fetchVoiceBackend(): Promise<{ backend: string | null; backends: string[] }> {
|
||||
return await this.request('/api/voice/backend')
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { cleanup, render, waitFor } from '@testing-library/react'
|
||||
import { act, cleanup, render, waitFor } from '@testing-library/react'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { VoiceBackendSession } from '@/realtime/VoiceBackendSession'
|
||||
import type { ApiClient } from '@/api/client'
|
||||
@@ -10,15 +10,15 @@ vi.mock('@/api/voice', () => ({
|
||||
}))
|
||||
|
||||
vi.mock('@/realtime/RealtimeVoiceSession', () => ({
|
||||
RealtimeVoiceSession: () => null,
|
||||
RealtimeVoiceSession: () => <div data-testid="elevenlabs-session" />,
|
||||
}))
|
||||
|
||||
vi.mock('@/realtime/GeminiLiveVoiceSession', () => ({
|
||||
GeminiLiveVoiceSession: () => null,
|
||||
GeminiLiveVoiceSession: () => <div data-testid="gemini-session" />,
|
||||
}))
|
||||
|
||||
vi.mock('@/realtime/QwenVoiceSession', () => ({
|
||||
QwenVoiceSession: () => null,
|
||||
QwenVoiceSession: () => <div data-testid="qwen-session" />,
|
||||
}))
|
||||
|
||||
const api = {} as ApiClient
|
||||
@@ -48,6 +48,29 @@ describe('VoiceBackendSession', () => {
|
||||
consoleError.mockRestore()
|
||||
})
|
||||
|
||||
it('does not mount a voice session when no backend is configured', async () => {
|
||||
fetchVoiceBackendMock.mockResolvedValue({ backend: null, backends: [] })
|
||||
const onReadyChange = vi.fn()
|
||||
|
||||
const view = render(
|
||||
<VoiceBackendSession
|
||||
api={api}
|
||||
micMuted={false}
|
||||
onStatusChange={vi.fn()}
|
||||
onReadyChange={onReadyChange}
|
||||
/>
|
||||
)
|
||||
await act(async () => {
|
||||
await Promise.resolve()
|
||||
})
|
||||
|
||||
expect(fetchVoiceBackendMock).toHaveBeenCalledWith(api)
|
||||
expect(view.queryByTestId('elevenlabs-session')).toBeNull()
|
||||
expect(view.queryByTestId('gemini-session')).toBeNull()
|
||||
expect(view.queryByTestId('qwen-session')).toBeNull()
|
||||
expect(onReadyChange).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('reports the detected backend as ready', async () => {
|
||||
fetchVoiceBackendMock.mockResolvedValue({ backend: 'elevenlabs', backends: ['elevenlabs'] })
|
||||
const onStatusChange = vi.fn()
|
||||
|
||||
Reference in New Issue
Block a user