129 lines
3.2 KiB
TypeScript
129 lines
3.2 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
import { doubaoTtsProvider } from '../../packages/server/src/services/hermes/tts-providers/doubao'
|
|
|
|
const mockFetch = vi.fn()
|
|
vi.stubGlobal('fetch', mockFetch)
|
|
|
|
function textResponse(body: string, init: { status?: number; contentType?: string } = {}) {
|
|
return {
|
|
ok: (init.status ?? 200) >= 200 && (init.status ?? 200) < 300,
|
|
status: init.status ?? 200,
|
|
statusText: 'OK',
|
|
headers: {
|
|
get(name: string) {
|
|
if (name.toLowerCase() === 'content-type') return init.contentType || 'application/json'
|
|
return null
|
|
},
|
|
},
|
|
async text() {
|
|
return body
|
|
},
|
|
async arrayBuffer() {
|
|
return Buffer.from(body)
|
|
},
|
|
}
|
|
}
|
|
|
|
function getJsonBody() {
|
|
const [, init] = mockFetch.mock.calls[0] as [string, RequestInit]
|
|
expect(init?.body).toBeTypeOf('string')
|
|
return JSON.parse(init.body as string)
|
|
}
|
|
|
|
describe('doubaoTtsProvider', () => {
|
|
beforeEach(() => {
|
|
mockFetch.mockReset()
|
|
})
|
|
|
|
it('defaults to mp3 at 24kHz for browser playback compatibility', async () => {
|
|
const audio = Buffer.from('mp3-audio')
|
|
mockFetch.mockResolvedValueOnce(textResponse(JSON.stringify({
|
|
data: audio.toString('base64'),
|
|
})))
|
|
|
|
const result = await doubaoTtsProvider.synthesize(
|
|
{ text: '你好' },
|
|
{
|
|
apiKey: 'secret',
|
|
},
|
|
)
|
|
|
|
expect(result).toEqual({
|
|
audio,
|
|
contentType: 'audio/mpeg',
|
|
engine: 'doubao',
|
|
provider: 'doubao',
|
|
})
|
|
expect(getJsonBody().req_params.audio_params).toEqual({
|
|
format: 'mp3',
|
|
sample_rate: 24000,
|
|
})
|
|
})
|
|
|
|
it('overrides audio format and sample rate when requested for MCU playback', async () => {
|
|
const pcm = Buffer.from('pcm-audio')
|
|
mockFetch.mockResolvedValueOnce(textResponse(JSON.stringify({
|
|
data: pcm.toString('base64'),
|
|
})))
|
|
|
|
const result = await doubaoTtsProvider.synthesize(
|
|
{ text: '已经找到设备。' },
|
|
{
|
|
apiKey: 'secret',
|
|
format: 'pcm',
|
|
sampleRate: 16000,
|
|
},
|
|
)
|
|
|
|
expect(result).toEqual({
|
|
audio: pcm,
|
|
contentType: 'audio/x-pcm',
|
|
engine: 'doubao',
|
|
provider: 'doubao',
|
|
})
|
|
expect(getJsonBody().req_params.audio_params).toEqual({
|
|
format: 'pcm',
|
|
sample_rate: 16000,
|
|
})
|
|
})
|
|
|
|
it('defaults to PCM at 24kHz when requested for MCU playback', async () => {
|
|
const pcm = Buffer.from('pcm-audio')
|
|
mockFetch.mockResolvedValueOnce(textResponse(JSON.stringify({
|
|
data: pcm.toString('base64'),
|
|
})))
|
|
|
|
const result = await doubaoTtsProvider.synthesize(
|
|
{ text: '已经找到设备。' },
|
|
{
|
|
apiKey: 'secret',
|
|
mcuPlayback: true,
|
|
},
|
|
)
|
|
|
|
expect(result).toEqual({
|
|
audio: pcm,
|
|
contentType: 'audio/x-pcm',
|
|
engine: 'doubao',
|
|
provider: 'doubao',
|
|
})
|
|
expect(getJsonBody().req_params.audio_params).toEqual({
|
|
format: 'pcm',
|
|
sample_rate: 24000,
|
|
})
|
|
})
|
|
|
|
it('rejects unsupported formats before calling Doubao', async () => {
|
|
await expect(doubaoTtsProvider.synthesize(
|
|
{ text: 'hello' },
|
|
{
|
|
apiKey: 'secret',
|
|
format: 'flac',
|
|
},
|
|
)).rejects.toThrow('Doubao TTS format must be one of')
|
|
|
|
expect(mockFetch).not.toHaveBeenCalled()
|
|
})
|
|
})
|