2026-07-08 06:35:02 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace Blax\WebRtc\Tests;
|
|
|
|
|
|
|
|
|
|
use Blax\WebRtc\Signaling\RelaySignalingHandler;
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
use Blax\WebRtc\Tests\Fixtures\AllowAuthorizer;
|
|
|
|
|
use Blax\WebRtc\Tests\Fixtures\CollectingEvents;
|
2026-07-08 06:35:02 +00:00
|
|
|
use Blax\WebRtc\Tests\Fixtures\FakePeer;
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
use Blax\WebRtc\Tests\Fixtures\MemoryRecordingStore;
|
2026-07-08 06:35:02 +00:00
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
|
|
|
|
|
|
final class RelaySignalingHandlerTest extends TestCase
|
|
|
|
|
{
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
public function test_welcomes_a_peer_with_its_id_and_the_record_flag(): void
|
2026-07-08 06:35:02 +00:00
|
|
|
{
|
|
|
|
|
$a = new FakePeer('a');
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
(new RelaySignalingHandler)->onOpen($a);
|
2026-07-08 06:35:02 +00:00
|
|
|
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
$this->assertSame(['type' => 'welcome', 'peer' => 'a', 'record' => false], $a->lastJson());
|
2026-07-08 06:35:02 +00:00
|
|
|
}
|
|
|
|
|
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
public function test_public_join_returns_others_notifies_them_and_has_no_member_list(): void
|
2026-07-08 06:35:02 +00:00
|
|
|
{
|
|
|
|
|
$handler = new RelaySignalingHandler;
|
|
|
|
|
$a = new FakePeer('a');
|
|
|
|
|
$b = new FakePeer('b');
|
|
|
|
|
$handler->onOpen($a);
|
|
|
|
|
$handler->onOpen($b);
|
|
|
|
|
|
|
|
|
|
$handler->onMessage($a, $this->json(['type' => 'join', 'room' => 'lobby']));
|
|
|
|
|
$handler->onMessage($b, $this->json(['type' => 'join', 'room' => 'lobby']));
|
|
|
|
|
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
$joined = $b->lastJson();
|
|
|
|
|
$this->assertSame('joined', $joined['type']);
|
|
|
|
|
$this->assertSame('public', $joined['roomType']);
|
|
|
|
|
$this->assertSame(['a'], $joined['peers']);
|
|
|
|
|
$this->assertArrayNotHasKey('members', $joined);
|
2026-07-08 06:35:02 +00:00
|
|
|
$this->assertContains(['type' => 'peer-joined', 'room' => 'lobby', 'peer' => 'b'], $a->jsonMessages());
|
|
|
|
|
}
|
|
|
|
|
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
public function test_presence_room_shares_the_member_list_with_info(): void
|
2026-07-08 06:35:02 +00:00
|
|
|
{
|
|
|
|
|
$handler = new RelaySignalingHandler;
|
|
|
|
|
$a = new FakePeer('a');
|
|
|
|
|
$b = new FakePeer('b');
|
|
|
|
|
$handler->onOpen($a);
|
|
|
|
|
$handler->onOpen($b);
|
|
|
|
|
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
$handler->onMessage($a, $this->json(['type' => 'join', 'room' => 'open-presence-lobby', 'info' => ['name' => 'Ann']]));
|
|
|
|
|
$handler->onMessage($b, $this->json(['type' => 'join', 'room' => 'open-presence-lobby', 'info' => ['name' => 'Bob']]));
|
2026-07-08 06:35:02 +00:00
|
|
|
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
$joined = $b->lastJson();
|
|
|
|
|
$this->assertSame('open-presence', $joined['roomType']);
|
|
|
|
|
$this->assertSame(['a' => ['name' => 'Ann'], 'b' => ['name' => 'Bob']], $joined['members']);
|
|
|
|
|
$this->assertContains(
|
|
|
|
|
['type' => 'peer-joined', 'room' => 'open-presence-lobby', 'peer' => 'b', 'info' => ['name' => 'Bob']],
|
|
|
|
|
$a->jsonMessages(),
|
|
|
|
|
);
|
2026-07-08 06:35:02 +00:00
|
|
|
}
|
|
|
|
|
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
public function test_private_room_is_denied_by_default(): void
|
2026-07-08 06:35:02 +00:00
|
|
|
{
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
$handler = new RelaySignalingHandler; // DefaultRoomAuthorizer denies
|
2026-07-08 06:35:02 +00:00
|
|
|
$a = new FakePeer('a');
|
|
|
|
|
$handler->onOpen($a);
|
|
|
|
|
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
$handler->onMessage($a, $this->json(['type' => 'join', 'room' => 'private-x']));
|
2026-07-08 06:35:02 +00:00
|
|
|
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
$this->assertSame(['type' => 'error', 'error' => 'unauthorized'], $a->lastJson());
|
|
|
|
|
$this->assertNull($handler->rooms()->roomOf('a'));
|
2026-07-08 06:35:02 +00:00
|
|
|
}
|
|
|
|
|
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
public function test_private_room_is_allowed_by_a_bound_authorizer(): void
|
|
|
|
|
{
|
|
|
|
|
$handler = new RelaySignalingHandler(authorizer: new AllowAuthorizer(['role' => 'host']));
|
|
|
|
|
$a = new FakePeer('a');
|
|
|
|
|
$handler->onOpen($a);
|
|
|
|
|
|
|
|
|
|
$handler->onMessage($a, $this->json(['type' => 'join', 'room' => 'private-x', 'auth' => ['token' => 't']]));
|
|
|
|
|
|
|
|
|
|
$joined = $a->lastJson();
|
|
|
|
|
$this->assertSame('joined', $joined['type']);
|
|
|
|
|
$this->assertSame('private', $joined['roomType']);
|
|
|
|
|
$this->assertSame('private-x', $handler->rooms()->roomOf('a'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_relay_forwards_to_the_target_with_the_sender(): void
|
2026-07-08 06:35:02 +00:00
|
|
|
{
|
|
|
|
|
$handler = new RelaySignalingHandler;
|
|
|
|
|
$a = new FakePeer('a');
|
|
|
|
|
$b = new FakePeer('b');
|
|
|
|
|
$handler->onOpen($a);
|
|
|
|
|
$handler->onOpen($b);
|
|
|
|
|
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
$handler->onMessage($a, $this->json(['type' => 'relay', 'to' => 'b', 'data' => ['sdp' => 'OFFER']]));
|
2026-07-08 06:35:02 +00:00
|
|
|
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
$this->assertSame(['type' => 'relay', 'from' => 'a', 'data' => ['sdp' => 'OFFER']], $b->lastJson());
|
2026-07-08 06:35:02 +00:00
|
|
|
}
|
|
|
|
|
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
public function test_leave_notifies_the_remaining_peers(): void
|
2026-07-08 06:35:02 +00:00
|
|
|
{
|
|
|
|
|
$handler = new RelaySignalingHandler;
|
|
|
|
|
$a = new FakePeer('a');
|
|
|
|
|
$b = new FakePeer('b');
|
|
|
|
|
$handler->onOpen($a);
|
|
|
|
|
$handler->onOpen($b);
|
|
|
|
|
$handler->onMessage($a, $this->json(['type' => 'join', 'room' => 'lobby']));
|
|
|
|
|
$handler->onMessage($b, $this->json(['type' => 'join', 'room' => 'lobby']));
|
|
|
|
|
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
$handler->onMessage($b, $this->json(['type' => 'leave']));
|
2026-07-08 06:35:02 +00:00
|
|
|
|
|
|
|
|
$this->assertContains(['type' => 'peer-left', 'peer' => 'b'], $a->jsonMessages());
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
$this->assertSame(['type' => 'left'], $b->lastJson());
|
2026-07-08 06:35:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_broadcast_reaches_the_rest_of_the_room_only(): void
|
|
|
|
|
{
|
|
|
|
|
$handler = new RelaySignalingHandler;
|
|
|
|
|
$peers = [new FakePeer('a'), new FakePeer('b'), new FakePeer('c')];
|
|
|
|
|
foreach ($peers as $p) {
|
|
|
|
|
$handler->onOpen($p);
|
|
|
|
|
$handler->onMessage($p, $this->json(['type' => 'join', 'room' => 'r']));
|
|
|
|
|
}
|
|
|
|
|
[$a, $b, $c] = $peers;
|
|
|
|
|
|
|
|
|
|
$handler->onMessage($a, $this->json(['type' => 'broadcast', 'data' => ['x' => 1]]));
|
|
|
|
|
|
|
|
|
|
$expected = ['type' => 'broadcast', 'from' => 'a', 'data' => ['x' => 1]];
|
|
|
|
|
$this->assertContains($expected, $b->jsonMessages());
|
|
|
|
|
$this->assertContains($expected, $c->jsonMessages());
|
|
|
|
|
$this->assertNotContains($expected, $a->jsonMessages());
|
|
|
|
|
}
|
|
|
|
|
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
public function test_streamed_binary_audio_is_recorded_and_finalized_on_disconnect(): void
|
|
|
|
|
{
|
|
|
|
|
$store = new MemoryRecordingStore;
|
|
|
|
|
$events = new CollectingEvents;
|
|
|
|
|
$handler = new RelaySignalingHandler(recorder: $store, events: $events);
|
|
|
|
|
|
|
|
|
|
$a = new FakePeer('a');
|
|
|
|
|
$handler->onOpen($a);
|
|
|
|
|
$this->assertTrue($a->lastJson()['record'], 'welcome should tell the client to record');
|
|
|
|
|
|
|
|
|
|
$handler->onMessage($a, $this->json(['type' => 'join', 'room' => 'lobby']));
|
|
|
|
|
$handler->onBinary($a, 'AUDIO-1');
|
|
|
|
|
$handler->onBinary($a, 'AUDIO-2');
|
|
|
|
|
$this->assertSame('AUDIO-1AUDIO-2', $store->data['lobby|a']);
|
|
|
|
|
|
|
|
|
|
$handler->onClose($a);
|
|
|
|
|
$this->assertContains('lobby|a', $store->finalized);
|
|
|
|
|
|
|
|
|
|
$finalized = $events->ofType('recording-finalized');
|
|
|
|
|
$this->assertCount(1, $finalized);
|
|
|
|
|
$this->assertSame('mem://lobby|a', $finalized[0]['recording']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_binary_without_a_recorder_is_ignored(): void
|
|
|
|
|
{
|
|
|
|
|
$handler = new RelaySignalingHandler; // no recorder
|
|
|
|
|
$a = new FakePeer('a');
|
|
|
|
|
$handler->onOpen($a);
|
|
|
|
|
$handler->onMessage($a, $this->json(['type' => 'join', 'room' => 'lobby']));
|
|
|
|
|
|
|
|
|
|
$handler->onBinary($a, 'AUDIO'); // must be a harmless no-op
|
|
|
|
|
|
|
|
|
|
$this->assertTrue(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_emits_ordered_call_lifecycle_events(): void
|
|
|
|
|
{
|
|
|
|
|
$events = new CollectingEvents;
|
|
|
|
|
$handler = new RelaySignalingHandler(events: $events);
|
|
|
|
|
$a = new FakePeer('a');
|
|
|
|
|
|
|
|
|
|
$handler->onOpen($a);
|
|
|
|
|
$handler->onMessage($a, $this->json(['type' => 'join', 'room' => 'lobby']));
|
|
|
|
|
$handler->onClose($a);
|
|
|
|
|
|
|
|
|
|
$types = $events->types();
|
|
|
|
|
$this->assertContains('peer-connected', $types);
|
|
|
|
|
$this->assertContains('room-joined', $types);
|
|
|
|
|
$this->assertContains('room-left', $types);
|
|
|
|
|
$this->assertContains('peer-disconnected', $types);
|
|
|
|
|
|
|
|
|
|
$seqs = array_column($events->events, 'seq');
|
|
|
|
|
$this->assertSame(range(1, count($seqs)), $seqs, 'seq should be monotonic from 1');
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-08 06:35:02 +00:00
|
|
|
public function test_invalid_json_and_unknown_type_are_errors(): void
|
|
|
|
|
{
|
|
|
|
|
$handler = new RelaySignalingHandler;
|
|
|
|
|
$a = new FakePeer('a');
|
|
|
|
|
$handler->onOpen($a);
|
|
|
|
|
|
|
|
|
|
$handler->onMessage($a, 'not json');
|
|
|
|
|
$this->assertSame('error', $a->lastJson()['type']);
|
|
|
|
|
|
|
|
|
|
$handler->onMessage($a, $this->json(['type' => 'wat']));
|
|
|
|
|
$this->assertSame('error', $a->lastJson()['type']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @param array<string,mixed> $data */
|
|
|
|
|
private function json(array $data): string
|
|
|
|
|
{
|
|
|
|
|
return (string) json_encode($data);
|
|
|
|
|
}
|
|
|
|
|
}
|