import { beforeEach, describe, expect, it, vi } from 'vitest' import { clampTtsText, cleanTtsText } from '../../packages/server/src/services/hermes/tts-providers/text' const { textToSpeech } = vi.hoisted(() => ({ textToSpeech: vi.fn(), })) vi.mock('../../packages/server/src/services/hermes/tts', () => ({ textToSpeech, })) import { edgeTtsProvider } from '../../packages/server/src/services/hermes/tts-providers/edge' describe('edgeTtsProvider', () => { beforeEach(() => { textToSpeech.mockReset() }) it('wraps legacy textToSpeech and maps cleaned text with voice, rate, and pitch', async () => { const audio = Buffer.from('edge-audio') textToSpeech.mockResolvedValueOnce({ audio, engine: 'legacy-edge' }) const text = 'Hello world ```ts\nsecret()\n``` '.repeat(120) const result = await edgeTtsProvider.synthesize( { text }, { baseUrl: 'https://unused.example.com', voice: 'en-US-JennyNeural', rate: '+20%', pitch: '-8Hz', }, ) expect(textToSpeech).toHaveBeenCalledTimes(1) expect(textToSpeech).toHaveBeenCalledWith({ text: clampTtsText(cleanTtsText(text)), voice: 'en-US-JennyNeural', rate: '+20%', pitch: '-8Hz', outputFormat: undefined, }) expect(result).toEqual({ audio, contentType: 'audio/mpeg', engine: 'legacy-edge', provider: 'edge', }) }) it('keeps Edge on MP3 output when the caller asks for PCM audio', async () => { const audio = Buffer.from('edge-mp3') textToSpeech.mockResolvedValueOnce({ audio, engine: 'legacy-edge' }) const result = await edgeTtsProvider.synthesize( { text: 'Hello world' }, { format: 'pcm', sampleRate: 16000, }, ) expect(textToSpeech).toHaveBeenCalledWith({ text: 'Hello world', voice: undefined, rate: undefined, pitch: undefined, outputFormat: undefined, }) expect(result).toEqual({ audio, contentType: 'audio/mpeg', engine: 'legacy-edge', provider: 'edge', }) }) it('throws before calling legacy textToSpeech when cleaned text is empty', async () => { await expect( edgeTtsProvider.synthesize( { text: 'secret
' }, { baseUrl: 'https://unused.example.com', }, ), ).rejects.toThrow('Edge TTS text is empty after cleaning') expect(textToSpeech).not.toHaveBeenCalled() }) it('rejects immediately when the signal is already aborted', async () => { const controller = new AbortController() controller.abort() await expect( edgeTtsProvider.synthesize( { text: 'Hello world', signal: controller.signal }, { baseUrl: 'https://unused.example.com', }, ), ).rejects.toMatchObject({ name: 'AbortError' }) expect(textToSpeech).not.toHaveBeenCalled() }) it('rejects in-flight when the signal aborts before legacy textToSpeech resolves', async () => { let resolveTextToSpeech: ((value: { audio: Buffer; engine: string }) => void) | undefined textToSpeech.mockImplementationOnce(() => new Promise((resolve) => { resolveTextToSpeech = resolve })) const controller = new AbortController() const pending = edgeTtsProvider.synthesize( { text: 'Hello world', signal: controller.signal }, { baseUrl: 'https://unused.example.com', }, ) expect(textToSpeech).toHaveBeenCalledTimes(1) controller.abort() await expect(pending).rejects.toMatchObject({ name: 'AbortError' }) resolveTextToSpeech?.({ audio: Buffer.from('late-audio'), engine: 'legacy-edge' }) }) })