Http::response('fake-mp3-bytes', 200, ['Content-Type' => 'audio/mpeg']), ]); $provider = AiProvider::factory()->create([ 'provider' => 'elevenlabs', 'api_key' => 'el-test', 'config' => ['voice_id' => 'voice123'], ]); $result = (new ElevenLabsDriver)->generate($provider, 'Hello world'); expect($result->content)->toBe('fake-mp3-bytes'); expect($result->contentType)->toBe('audio/mpeg'); Http::assertSent(function ($request) { return $request->url() === 'https://api.elevenlabs.io/v1/text-to-speech/voice123' && $request->hasHeader('xi-api-key', 'el-test') && $request['text'] === 'Hello world'; }); }); test('a voice_id passed in options overrides the provider config', function () { Http::fake(['api.elevenlabs.io/*' => Http::response('bytes')]); $provider = AiProvider::factory()->create(['provider' => 'elevenlabs', 'config' => ['voice_id' => 'default-voice']]); (new ElevenLabsDriver)->generate($provider, 'hi', ['voice_id' => 'custom-voice']); Http::assertSent(fn ($request) => str_contains($request->url(), '/text-to-speech/custom-voice')); }); test('it throws when no voice_id is configured anywhere', function () { $provider = AiProvider::factory()->create(['provider' => 'elevenlabs', 'config' => null]); (new ElevenLabsDriver)->generate($provider, 'hi'); })->throws(AiGenerationException::class, 'no voice_id configured'); test('it throws when ElevenLabs returns an error', function () { Http::fake([ 'api.elevenlabs.io/*' => Http::response(['detail' => ['message' => 'quota exceeded']], 429), ]); $provider = AiProvider::factory()->create(['provider' => 'elevenlabs', 'config' => ['voice_id' => 'v1']]); (new ElevenLabsDriver)->generate($provider, 'hi'); })->throws(AiGenerationException::class, 'quota exceeded');