laravel-webrtc/tests/OpenAiRealtimeBridgeTest.php

70 lines
2.9 KiB
PHP
Raw Permalink Normal View History

<?php
declare(strict_types=1);
namespace Blax\WebRtc\Tests;
use Blax\WebRtc\Realtime\OpenAiRealtimeBridge;
use Blax\WebRtc\Tests\Fixtures\ScriptedRealtimeTransport;
use PHPUnit\Framework\TestCase;
final class OpenAiRealtimeBridgeTest extends TestCase
{
public function test_exchange_drives_the_session_and_assembles_the_reply(): void
{
$inbound = [
(string) json_encode(['type' => 'session.updated']),
(string) json_encode(['type' => 'response.output_audio_transcript.delta', 'delta' => 'Roger, ']),
(string) json_encode(['type' => 'response.output_audio.delta', 'delta' => base64_encode('AB')]),
(string) json_encode(['type' => 'response.output_audio_transcript.delta', 'delta' => 'wilco.']),
(string) json_encode(['type' => 'response.output_audio.delta', 'delta' => base64_encode('CD')]),
(string) json_encode(['type' => 'response.done']),
];
$fake = new ScriptedRealtimeTransport($inbound);
$bridge = new OpenAiRealtimeBridge(fn ($url, $headers, $timeout) => $fake);
$turn = $bridge->exchange('PILOTPCM', ['api_key' => 'sk-test', 'voice' => 'echo', 'instructions' => 'be ATC']);
$this->assertNull($turn->error);
$this->assertTrue($turn->ok());
$this->assertSame('ABCD', $turn->pcm);
$this->assertSame('Roger, wilco.', $turn->transcript);
$this->assertTrue($fake->closed);
// Correct GA protocol on the wire, in order.
$types = $fake->sentTypes();
$this->assertSame('session.update', $types[0]);
$this->assertContains('input_audio_buffer.append', $types);
$this->assertContains('input_audio_buffer.commit', $types);
$this->assertContains('response.create', $types);
// Session config carries the voice + instructions and audio-only modality.
$session = json_decode($fake->sent[0], true)['session'];
$this->assertSame(['audio'], $session['output_modalities']);
$this->assertSame('echo', $session['audio']['output']['voice']);
$this->assertSame('be ATC', $session['instructions']);
}
public function test_it_validates_before_dialing(): void
{
$bridge = new OpenAiRealtimeBridge(function () {
throw new \RuntimeException('should not dial');
});
$this->assertSame('missing api_key', $bridge->exchange('pcm', [])->error);
$this->assertSame('empty input audio', $bridge->exchange('', ['api_key' => 'k'])->error);
}
public function test_it_surfaces_provider_errors(): void
{
$inbound = [(string) json_encode(['type' => 'error', 'error' => ['message' => 'boom']])];
$bridge = new OpenAiRealtimeBridge(fn ($url, $headers, $timeout) => new ScriptedRealtimeTransport($inbound));
$turn = $bridge->exchange('PCM', ['api_key' => 'k']);
$this->assertSame('boom', $turn->error);
$this->assertFalse($turn->ok());
$this->assertSame('', $turn->pcm);
}
}