127 lines
4.9 KiB
PHP
127 lines
4.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Blax\WebRtc\Tests;
|
|
|
|
use Blax\WebRtc\Signaling\SignalingHandler;
|
|
use Blax\WebRtc\Tests\Fixtures\FakeMediaEngine;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
final class SignalingHandlerTest extends TestCase
|
|
{
|
|
public function test_offer_returns_the_engine_answer(): void
|
|
{
|
|
$engine = new FakeMediaEngine;
|
|
$result = (new SignalingHandler($engine))->handle([
|
|
'type' => 'offer',
|
|
'peer' => 'p1',
|
|
'sdp' => 'OFFER-SDP',
|
|
]);
|
|
|
|
$this->assertSame(['type' => 'answer', 'peer' => 'p1', 'sdp' => 'answer-for-p1'], $result);
|
|
$this->assertSame(['offer', 'p1', 'OFFER-SDP'], $engine->calls[0]);
|
|
}
|
|
|
|
public function test_ice_is_forwarded_and_needs_no_reply(): void
|
|
{
|
|
$engine = new FakeMediaEngine;
|
|
$result = (new SignalingHandler($engine))->handle([
|
|
'type' => 'ice',
|
|
'peer' => 'p1',
|
|
'candidate' => ['candidate' => 'a=x'],
|
|
]);
|
|
|
|
$this->assertNull($result);
|
|
$this->assertSame(['ice', 'p1', ['candidate' => 'a=x']], $engine->calls[0]);
|
|
}
|
|
|
|
public function test_record_connect_bridge_close_are_acked_and_routed(): void
|
|
{
|
|
$engine = new FakeMediaEngine;
|
|
$handler = new SignalingHandler($engine);
|
|
|
|
$this->assertSame(['type' => 'ack', 'peer' => 'p1'], $handler->handle(['type' => 'record', 'peer' => 'p1', 'path' => '/tmp/a.opus']));
|
|
$this->assertSame(['type' => 'ack', 'peer' => 'p1'], $handler->handle(['type' => 'connect', 'peer' => 'p1', 'other' => 'p2']));
|
|
$this->assertSame(['type' => 'ack', 'peer' => 'p1'], $handler->handle(['type' => 'bridge', 'peer' => 'p1', 'options' => ['model' => 'gpt-realtime']]));
|
|
$this->assertSame(['type' => 'ack', 'peer' => 'p1'], $handler->handle(['type' => 'close', 'peer' => 'p1']));
|
|
|
|
$this->assertSame(['record', 'p1', '/tmp/a.opus'], $engine->calls[0]);
|
|
$this->assertSame(['connect', 'p1', 'p2'], $engine->calls[1]);
|
|
$this->assertSame(['bridge', 'p1', ['model' => 'gpt-realtime']], $engine->calls[2]);
|
|
$this->assertSame(['close', 'p1'], $engine->calls[3]);
|
|
}
|
|
|
|
public function test_a_missing_peer_is_an_error(): void
|
|
{
|
|
$result = (new SignalingHandler(new FakeMediaEngine))->handle(['type' => 'offer']);
|
|
|
|
$this->assertSame('error', $result['type']);
|
|
}
|
|
|
|
public function test_an_unknown_type_is_an_error(): void
|
|
{
|
|
$result = (new SignalingHandler(new FakeMediaEngine))->handle(['type' => 'wat', 'peer' => 'p1']);
|
|
|
|
$this->assertSame('error', $result['type']);
|
|
$this->assertStringContainsString('wat', $result['error']);
|
|
}
|
|
|
|
public function test_an_engine_exception_is_caught_as_an_error_reply(): void
|
|
{
|
|
$engine = new FakeMediaEngine;
|
|
$engine->throwOnOffer = new \RuntimeException('boom');
|
|
|
|
$result = (new SignalingHandler($engine))->handle(['type' => 'offer', 'peer' => 'p1', 'sdp' => 'x']);
|
|
|
|
$this->assertSame(['type' => 'error', 'peer' => 'p1', 'error' => 'boom'], $result);
|
|
}
|
|
|
|
public function test_join_meshes_a_new_peer_with_those_already_present(): void
|
|
{
|
|
$engine = new FakeMediaEngine;
|
|
$handler = new SignalingHandler($engine);
|
|
|
|
$this->assertSame(
|
|
['type' => 'joined', 'peer' => 'p1', 'room' => 'lobby', 'peers' => []],
|
|
$handler->handle(['type' => 'join', 'peer' => 'p1', 'room' => 'lobby']),
|
|
);
|
|
$this->assertSame(
|
|
['type' => 'joined', 'peer' => 'p2', 'room' => 'lobby', 'peers' => ['p1']],
|
|
$handler->handle(['type' => 'join', 'peer' => 'p2', 'room' => 'lobby']),
|
|
);
|
|
|
|
$this->assertContains(['connect', 'p2', 'p1'], $engine->calls);
|
|
}
|
|
|
|
public function test_join_without_a_room_is_an_error(): void
|
|
{
|
|
$result = (new SignalingHandler(new FakeMediaEngine))->handle(['type' => 'join', 'peer' => 'p1']);
|
|
|
|
$this->assertSame('error', $result['type']);
|
|
}
|
|
|
|
public function test_leave_reports_the_remaining_peers(): void
|
|
{
|
|
$handler = new SignalingHandler(new FakeMediaEngine);
|
|
$handler->handle(['type' => 'join', 'peer' => 'p1', 'room' => 'lobby']);
|
|
$handler->handle(['type' => 'join', 'peer' => 'p2', 'room' => 'lobby']);
|
|
|
|
$this->assertSame(
|
|
['type' => 'left', 'peer' => 'p2', 'peers' => ['p1']],
|
|
$handler->handle(['type' => 'leave', 'peer' => 'p2']),
|
|
);
|
|
}
|
|
|
|
public function test_close_also_removes_the_peer_from_its_room(): void
|
|
{
|
|
$engine = new FakeMediaEngine;
|
|
$handler = new SignalingHandler($engine);
|
|
$handler->handle(['type' => 'join', 'peer' => 'p1', 'room' => 'lobby']);
|
|
|
|
$this->assertSame(['type' => 'ack', 'peer' => 'p1'], $handler->handle(['type' => 'close', 'peer' => 'p1']));
|
|
$this->assertNull($handler->rooms()->roomOf('p1'));
|
|
$this->assertContains(['close', 'p1'], $engine->calls);
|
|
}
|
|
}
|