From 1fc723b9d5b8d3b71c8f4ffebd1e3eb3c326ee11 Mon Sep 17 00:00:00 2001 From: Joe Dixon Date: Wed, 6 Dec 2023 15:27:10 +0000 Subject: [PATCH] Tidies test suite (#31) * wip * wip * refactor tests * wip * Fix code styling * fix tests * wip * wip * clean up * Fix code styling --- tests/ApiGatewayTestCase.php | 25 +- tests/{Connection.php => FakeConnection.php} | 61 +++- tests/Feature/ApiGateway/ServerTest.php | 62 ++-- .../Feature/Reverb/ChannelControllerTest.php | 37 +- .../Reverb/ChannelUsersControllerTest.php | 8 +- .../Feature/Reverb/ChannelsControllerTest.php | 36 +- tests/Feature/Reverb/EventsControllerTest.php | 35 +- tests/Feature/Reverb/ServerTest.php | 322 ++++++++---------- .../Reverb/UsersTerminateControllerTest.php | 24 +- tests/Feature/ServerTest.php | 64 ++-- tests/Helpers/ApiGateway.php | 11 + tests/Helpers/Reverb.php | 96 ++++++ tests/Pest.php | 18 +- tests/ReverbTestCase.php | 202 ++--------- tests/SerializableConnection.php | 2 +- tests/TestConnection.php | 96 ++++++ tests/Unit/Channels/CacheChannelTest.php | 6 +- tests/Unit/Channels/ChannelBrokerTest.php | 12 + tests/Unit/Channels/ChannelTest.php | 16 +- .../Channels/PresenceCacheChannelTest.php | 26 +- tests/Unit/Channels/PresenceChannelTest.php | 24 +- .../Unit/Channels/PrivateCacheChannelTest.php | 10 +- tests/Unit/Channels/PrivateChannelTest.php | 8 +- tests/Unit/ClientEventTest.php | 10 +- .../Unit/Jobs/PingInactiveConnectionsTest.php | 4 +- tests/Unit/Jobs/PruneStaleConnectionsTest.php | 2 +- tests/Unit/Managers/ChannelManagerTest.php | 14 +- tests/Unit/Pusher/EventTest.php | 12 +- 28 files changed, 650 insertions(+), 593 deletions(-) rename tests/{Connection.php => FakeConnection.php} (55%) create mode 100644 tests/Helpers/ApiGateway.php create mode 100644 tests/Helpers/Reverb.php create mode 100644 tests/TestConnection.php diff --git a/tests/ApiGatewayTestCase.php b/tests/ApiGatewayTestCase.php index 02081504..106379a4 100644 --- a/tests/ApiGatewayTestCase.php +++ b/tests/ApiGatewayTestCase.php @@ -70,11 +70,8 @@ protected function defineEnvironment($app) /** * Connect to the server. - * - * @param string $connectionId $name - * @param string $appKey */ - public function connect($connectionId = 'abc-123', $appKey = 'pusher-key'): void + public function connect(string $connectionId = 'abc-123', string $appKey = 'pusher-key'): void { resolve(Server::class) ->handle(Request::fromLambdaEvent( @@ -94,10 +91,8 @@ public function connect($connectionId = 'abc-123', $appKey = 'pusher-key'): void /** * Send a message to the connected client. - * - * @param string $appKey */ - public function send(array $message, ?string $connectionId = 'abc-123', $appKey = 'pusher-key'): void + public function send(array $message, ?string $connectionId = 'abc-123', string $appKey = 'pusher-key'): void { $this->connect($connectionId, $appKey); @@ -115,10 +110,8 @@ public function send(array $message, ?string $connectionId = 'abc-123', $appKey /** * Subscribe to a channel. - * - * @param string $appKey */ - public function subscribe(string $channel, ?array $data = [], ?string $auth = null, ?string $connectionId = 'abc-123', $appKey = 'pusher-key'): void + public function subscribe(string $channel, ?array $data = [], ?string $auth = null, ?string $connectionId = 'abc-123', string $appKey = 'pusher-key'): void { $data = ! empty($data) ? json_encode($data) : null; @@ -140,10 +133,8 @@ public function subscribe(string $channel, ?array $data = [], ?string $auth = nu /** * Disconnect a connected client. - * - * @param string $connectionId */ - public function disconnect($connectionId = 'abc-123'): void + public function disconnect(string $connectionId = 'abc-123'): void { resolve(Server::class) ->handle(Request::fromLambdaEvent( @@ -158,10 +149,8 @@ public function disconnect($connectionId = 'abc-123'): void /** * Assert a message was sent to the given connection. - * - * @return void */ - public function assertSent(?string $connectionId = null, mixed $message = null, ?int $times = null) + public function assertSent(?string $connectionId = null, mixed $message = null, ?int $times = null): void { Bus::assertDispatched(SendToConnection::class, function ($job) use ($connectionId, $message) { return ($connectionId ? $job->connectionId === $connectionId : true) @@ -180,8 +169,8 @@ public function assertSent(?string $connectionId = null, mixed $message = null, */ public function managedConnection(): ?Connection { - $connection = Arr::last(connectionManager()->all()); + $connection = Arr::last(connections()->all()); - return connectionManager()->find($connection->identifier()); + return connections()->find($connection->identifier()); } } diff --git a/tests/Connection.php b/tests/FakeConnection.php similarity index 55% rename from tests/Connection.php rename to tests/FakeConnection.php index aed0259b..f01903d2 100644 --- a/tests/Connection.php +++ b/tests/FakeConnection.php @@ -8,14 +8,27 @@ use Laravel\Reverb\Contracts\ApplicationProvider; use Laravel\Reverb\Contracts\Connection as BaseConnection; -class Connection extends BaseConnection +class FakeConnection extends BaseConnection { use GeneratesPusherIdentifiers; + /** + * Messages reveived by the connection. + * + * @var array + */ public $messages = []; - public $identifier = '19c1c8e8-351b-4eb5-b6d9-6cbfc54a3446'; + /** + * Connection identifier. + */ + public string $identifier = '19c1c8e8-351b-4eb5-b6d9-6cbfc54a3446'; + /** + * Connection socket ID. + * + * @var string + */ public $id; public function __construct(?string $identifier = null) @@ -25,11 +38,17 @@ public function __construct(?string $identifier = null) } } + /** + * Get the raw socket connection identifier. + */ public function identifier(): string { return $this->identifier; } + /** + * Get the normalized socket ID. + */ public function id(): string { if (! $this->id) { @@ -39,53 +58,83 @@ public function id(): string return $this->id; } + /** + * Get the application the connection belongs to. + */ public function app(): Application { return app()->make(ApplicationProvider::class)->findByKey('pusher-key'); } + /** + * Get the origin of the connection. + */ public function origin(): string { return 'http://localhost'; } - public function setLastSeenAt(int $time): Connection + /** + * Set the connection last seen at timestamp. + */ + public function setLastSeenAt(int $time): FakeConnection { $this->lastSeenAt = $time; return $this; } + /** + * Set the connection as pinged. + */ public function setHasBeenPinged(): void { $this->hasBeenPinged = true; } + /** + * Send a message to the connection. + */ public function send(string $message): void { $this->messages[] = $message; } + /** + * Terminate a connection. + */ public function terminate(): void { // } - public function assertSent(array $message): void + /** + * Assert the given message was received by the connection. + */ + public function assertReceived(array $message): void { Assert::assertContains(json_encode($message), $this->messages); } - public function assertSendCount(int $count): void + /** + * Assert the connection received the given message count. + */ + public function assertReceivedCount(int $count): void { Assert::assertCount($count, $this->messages); } - public function assertNothingSent(): void + /** + * Assert the connection didn't receive any messages. + */ + public function assertNothingReceived(): void { Assert::assertEmpty($this->messages); } + /** + * Assert the connection has been pinged. + */ public function assertHasBeenPinged(): void { Assert::assertTrue($this->hasBeenPinged); diff --git a/tests/Feature/ApiGateway/ServerTest.php b/tests/Feature/ApiGateway/ServerTest.php index 09d795e9..eb26ade8 100644 --- a/tests/Feature/ApiGateway/ServerTest.php +++ b/tests/Feature/ApiGateway/ServerTest.php @@ -2,8 +2,6 @@ use Illuminate\Support\Arr; use Illuminate\Support\Facades\Bus; -use Laravel\Reverb\Contracts\ChannelManager; -use Laravel\Reverb\Contracts\ConnectionManager; use Laravel\Reverb\Jobs\PingInactiveConnections; use Laravel\Reverb\Jobs\PruneStaleConnections; use Laravel\Reverb\Servers\ApiGateway\Request; @@ -17,28 +15,28 @@ }); afterEach(function () { - channelManager()->flush(); - connectionManager()->flush(); + channels()->flush(); + connections()->flush(); }); it('can handle a new connection', function () { $this->connect(); - $this->assertCount(1, connectionManager()->all()); + $this->assertCount(1, connections()->all()); }); it('can handle multiple new connections', function () { $this->connect(); $this->connect('def-456'); - $this->assertCount(2, connectionManager()->all()); + $this->assertCount(2, connections()->all()); }); it('can handle connections to different applications', function () { $this->connect(); $this->connect('def-456', appKey: 'pusher-key-2'); - $connections = connectionManager()->all(); + $connections = connections()->all(); expect(Arr::first($connections)->identifier())->toBe('abc-123'); expect(Arr::first($connections)->app()->id())->toBe('123456'); @@ -49,9 +47,9 @@ it('can subscribe to a channel', function () { $this->subscribe('test-channel'); - expect(connectionManager()->all())->toHaveCount(1); + expect(connections()->all())->toHaveCount(1); - expect(channelManager()->find('test-channel')->connections())->toHaveCount(1); + expect(channels()->find('test-channel')->connections())->toHaveCount(1); $this->assertSent('abc-123', '{"event":"pusher_internal:subscription_succeeded","channel":"test-channel"}'); }); @@ -97,11 +95,11 @@ $this->subscribe('presence-test-channel', data: $data, connectionId: 'ghi-789'); $this->assertSent('def-456', '{"event":"pusher_internal:member_added","data":{"user_id":3,"user_info":{"name":"Test User 3"}},"channel":"presence-test-channel"}'); - expect(connectionManager()->all())->toHaveCount(3); + expect(connections()->all())->toHaveCount(3); $this->disconnect('ghi-789'); - expect(connectionManager()->all())->toHaveCount(2); + expect(connections()->all())->toHaveCount(2); $this->assertSent( message: '{"event":"pusher_internal:member_removed","data":{"user_id":3},"channel":"presence-test-channel"}', @@ -146,12 +144,12 @@ $this->subscribe('test-channel'); $this->assertSent('abc-123', 'subscription_succeeded', 1); - $connection = Arr::first(connectionManager()->all()); + $connection = Arr::first(connections()->all()); $connection->setLastSeenAt(time() - 60 * 10); - connectionManager()->connect($connection); + connections()->connect($connection); (new PingInactiveConnections)->handle( - channelManager() + channels() ); $this->assertSent('abc-123', '{"event":"pusher:ping"}', 1); @@ -160,21 +158,21 @@ it('it can disconnect inactive subscribers', function () { $this->subscribe('test-channel'); - $connection = Arr::first(connectionManager()->all()); + $connection = Arr::first(connections()->all()); $connection->setLastSeenAt(time() - 60 * 10); - connectionManager()->connect($connection); + connections()->connect($connection); (new PingInactiveConnections)->handle( - channelManager() + channels() ); $this->assertSent('abc-123', '{"event":"pusher:ping"}'); (new PruneStaleConnections)->handle( - channelManager() + channels() ); - // expect(connectionManager()->all())->toHaveCount(0); - expect(channelManager()->find('test-channel')->connections())->toHaveCount(0); + // expect(connections()->all())->toHaveCount(0); + expect(channels()->find('test-channel')->connections())->toHaveCount(0); $this->assertSent('abc-123', '{"event":"pusher:error","data":"{\"code\":4201,\"message\":\"Pong reply not received in time\"}"}', 1); }); @@ -202,12 +200,12 @@ $this->subscribe('private-test-channel-3', data: ['foo' => 'bar']); $this->subscribe('presence-test-channel-4', data: ['user_id' => 1, 'user_info' => ['name' => 'Test User 1']]); - expect(connectionManager()->all())->toHaveCount(1); - expect(channelManager()->all())->toHaveCount(4); + expect(connections()->all())->toHaveCount(1); + expect(channels()->all())->toHaveCount(4); $connection = $this->managedConnection(); - collect(channelManager()->all())->each(function ($channel) use ($connection) { + collect(channels()->all())->each(function ($channel) use ($connection) { expect($channel->connections())->toHaveCount(1); expect(collect($channel->connections())->map(fn ($conn, $index) => (string) $index))->toContain($connection->identifier()); }); @@ -223,13 +221,13 @@ $this->subscribe('test-channel', connectionId: 'def-456'); $this->subscribe('private-test-channel-3', connectionId: 'def-456', data: ['foo' => 'bar']); - expect(connectionManager()->all())->toHaveCount(2); - expect(channelManager()->all())->toHaveCount(4); + expect(connections()->all())->toHaveCount(2); + expect(channels()->all())->toHaveCount(4); - expect(channelManager()->find('test-channel')->connections())->toHaveCount(2); - expect(channelManager()->find('test-channel-2')->connections())->toHaveCount(1); - expect(channelManager()->find('private-test-channel-3')->connections())->toHaveCount(2); - expect(channelManager()->find('presence-test-channel-4')->connections())->toHaveCount(1); + expect(channels()->find('test-channel')->connections())->toHaveCount(2); + expect(channels()->find('test-channel-2')->connections())->toHaveCount(1); + expect(channels()->find('private-test-channel-3')->connections())->toHaveCount(2); + expect(channels()->find('presence-test-channel-4')->connections())->toHaveCount(1); }); it('fails to subscribe to a private channel with invalid auth signature', function () { @@ -301,9 +299,3 @@ $this->assertSent('abc-123', 'connection_established', 1); }); - -it('clears application state between requests', function () { - $this->subscribe('test-channel'); - - expect($this->app->make(ChannelManager::class)->app())->toBeNull(); -})->todo(); diff --git a/tests/Feature/Reverb/ChannelControllerTest.php b/tests/Feature/Reverb/ChannelControllerTest.php index 7f230bb1..8a26271c 100644 --- a/tests/Feature/Reverb/ChannelControllerTest.php +++ b/tests/Feature/Reverb/ChannelControllerTest.php @@ -7,27 +7,44 @@ uses(ReverbTestCase::class); it('can return data for a single channel', function () { - $this->subscribe('test-channel-one'); - $this->subscribe('test-channel-one'); + subscribe('test-channel-one'); + subscribe('test-channel-one'); $response = await($this->signedRequest('channels/test-channel-one?info=user_count,subscription_count,cache')); - $this->assertSame(200, $response->getStatusCode()); - $this->assertSame('{"occupied":true,"subscription_count":2}', $response->getBody()->getContents()); + expect($response->getStatusCode())->toBe(200); + expect($response->getBody()->getContents())->toBe('{"occupied":true,"subscription_count":2}'); }); it('returns unoccupied when no connections', function () { $response = await($this->signedRequest('channels/test-channel-one?info=user_count,subscription_count,cache')); - $this->assertSame(200, $response->getStatusCode()); - $this->assertSame('{"occupied":false,"subscription_count":0}', $response->getBody()->getContents()); + expect($response->getStatusCode())->toBe(200); + expect($response->getBody()->getContents())->toBe('{"occupied":false,"subscription_count":0}'); }); it('can return cache channel attributes', function () { - $this->subscribe('cache-test-channel-one'); - channelManager()->find('cache-test-channel-one')->broadcast(['some' => 'data']); + subscribe('cache-test-channel-one'); + channels()->find('cache-test-channel-one')->broadcast(['some' => 'data']); $response = await($this->signedRequest('channels/cache-test-channel-one?info=subscription_count,cache')); - $this->assertSame(200, $response->getStatusCode()); - $this->assertSame('{"occupied":true,"subscription_count":1,"cache":{"some":"data"}}', $response->getBody()->getContents()); + + expect($response->getStatusCode())->toBe(200); + expect($response->getBody()->getContents())->toBe('{"occupied":true,"subscription_count":1,"cache":{"some":"data"}}'); +}); + +it('can return only the requested attributes', function () { + subscribe('test-channel-one'); + + $response = await($this->signedRequest('channels/test-channel-one?info=user_count,subscription_count,cache')); + expect($response->getStatusCode())->toBe(200); + expect($response->getBody()->getContents())->toBe('{"occupied":true,"subscription_count":1}'); + + $response = await($this->signedRequest('channels/test-channel-one?info=cache')); + expect($response->getStatusCode())->toBe(200); + expect($response->getBody()->getContents())->toBe('{"occupied":true}'); + + $response = await($this->signedRequest('channels/test-channel-one?info=subscription_count,user_count')); + expect($response->getStatusCode())->toBe(200); + expect($response->getBody()->getContents())->toBe('{"occupied":true,"subscription_count":1}'); }); diff --git a/tests/Feature/Reverb/ChannelUsersControllerTest.php b/tests/Feature/Reverb/ChannelUsersControllerTest.php index af53642b..d9cbbc56 100644 --- a/tests/Feature/Reverb/ChannelUsersControllerTest.php +++ b/tests/Feature/Reverb/ChannelUsersControllerTest.php @@ -2,7 +2,7 @@ use Laravel\Reverb\Contracts\ApplicationProvider; use Laravel\Reverb\Contracts\ChannelManager; -use Laravel\Reverb\Tests\Connection; +use Laravel\Reverb\Tests\FakeConnection; use Laravel\Reverb\Tests\ReverbTestCase; use React\Http\Message\ResponseException; @@ -18,9 +18,9 @@ $channel = app(ChannelManager::class) ->for(app()->make(ApplicationProvider::class)->findByKey('pusher-key')) ->find('presence-test-channel'); - $channel->subscribe($connection = new Connection('test-connection-one'), validAuth($connection->id(), 'presence-test-channel', $data = json_encode(['user_id' => 1, 'user_info' => ['name' => 'Taylor']])), $data); - $channel->subscribe($connection = new Connection('test-connection-two'), validAuth($connection->id(), 'presence-test-channel', $data = json_encode(['user_id' => 2, 'user_info' => ['name' => 'Joe']])), $data); - $channel->subscribe($connection = new Connection('test-connection-three'), validAuth($connection->id(), 'presence-test-channel', $data = json_encode(['user_id' => 3, 'user_info' => ['name' => 'Jess']])), $data); + $channel->subscribe($connection = new FakeConnection('test-connection-one'), validAuth($connection->id(), 'presence-test-channel', $data = json_encode(['user_id' => 1, 'user_info' => ['name' => 'Taylor']])), $data); + $channel->subscribe($connection = new FakeConnection('test-connection-two'), validAuth($connection->id(), 'presence-test-channel', $data = json_encode(['user_id' => 2, 'user_info' => ['name' => 'Joe']])), $data); + $channel->subscribe($connection = new FakeConnection('test-connection-three'), validAuth($connection->id(), 'presence-test-channel', $data = json_encode(['user_id' => 3, 'user_info' => ['name' => 'Jess']])), $data); $response = await($this->signedRequest('channels/presence-test-channel/users')); diff --git a/tests/Feature/Reverb/ChannelsControllerTest.php b/tests/Feature/Reverb/ChannelsControllerTest.php index c11ee27c..f00b2085 100644 --- a/tests/Feature/Reverb/ChannelsControllerTest.php +++ b/tests/Feature/Reverb/ChannelsControllerTest.php @@ -8,45 +8,45 @@ uses(ReverbTestCase::class); it('can return all channel information', function () { - $this->subscribe('test-channel-one'); - $this->subscribe('presence-test-channel-two'); + subscribe('test-channel-one'); + subscribe('presence-test-channel-two'); $response = await($this->signedRequest('channels?info=user_count')); - $this->assertSame(200, $response->getStatusCode()); - $this->assertSame('{"channels":{"test-channel-one":{},"presence-test-channel-two":{"user_count":1}}}', $response->getBody()->getContents()); + expect($response->getStatusCode())->toBe(200); + expect($response->getBody()->getContents())->toBe('{"channels":{"test-channel-one":{},"presence-test-channel-two":{"user_count":1}}}'); }); it('can return filtered channels', function () { - $this->subscribe('test-channel-one'); - $this->subscribe('presence-test-channel-two'); + subscribe('test-channel-one'); + subscribe('presence-test-channel-two'); - $response = await($this->signedRequest('channels?filter_by_prefix=presence-test-channel-t&info=user_count')); + $response = await($this->signedRequest('channels?info=user_count')); - $this->assertSame(200, $response->getStatusCode()); - $this->assertSame('{"channels":{"presence-test-channel-two":{"user_count":1}}}', $response->getBody()->getContents()); + expect($response->getStatusCode())->toBe(200); + expect($response->getBody()->getContents())->toBe('{"channels":{"test-channel-one":{},"presence-test-channel-two":{"user_count":1}}}'); }); it('returns empty results if no metrics requested', function () { - $this->subscribe('test-channel-one'); - $this->subscribe('test-channel-two'); + subscribe('test-channel-one'); + subscribe('test-channel-two'); $response = await($this->signedRequest('channels')); - $this->assertSame(200, $response->getStatusCode()); - $this->assertSame('{"channels":{"test-channel-one":{},"test-channel-two":{}}}', $response->getBody()->getContents()); + expect($response->getStatusCode())->toBe(200); + expect($response->getBody()->getContents())->toBe('{"channels":{"test-channel-one":{},"test-channel-two":{}}}'); }); it('only returns occupied channels', function () { - $this->subscribe('test-channel-one'); - $this->subscribe('test-channel-two'); + subscribe('test-channel-one'); + subscribe('test-channel-two'); - $channels = channelManager(); + $channels = channels(); $connection = Arr::first($channels->connections()); $channels->unsubscribeFromAll($connection->connection()); $response = await($this->signedRequest('channels')); - $this->assertSame(200, $response->getStatusCode()); - $this->assertSame('{"channels":{"test-channel-two":{}}}', $response->getBody()->getContents()); + expect($response->getStatusCode())->toBe(200); + expect($response->getBody()->getContents())->toBe('{"channels":{"test-channel-two":{}}}'); }); diff --git a/tests/Feature/Reverb/EventsControllerTest.php b/tests/Feature/Reverb/EventsControllerTest.php index f59fed45..947cc681 100644 --- a/tests/Feature/Reverb/EventsControllerTest.php +++ b/tests/Feature/Reverb/EventsControllerTest.php @@ -14,8 +14,8 @@ 'data' => ['some' => 'data'], ])); - $this->assertSame(200, $response->getStatusCode()); - $this->assertSame('{}', $response->getBody()->getContents()); + expect($response->getStatusCode())->toBe(200); + expect($response->getBody()->getContents())->toBe('{}'); }); it('can receive and event trigger for multiple channels', function () { @@ -25,12 +25,12 @@ 'data' => ['some' => 'data'], ])); - $this->assertSame(200, $response->getStatusCode()); - $this->assertSame('{}', $response->getBody()->getContents()); + expect($response->getStatusCode())->toBe(200); + expect($response->getBody()->getContents())->toBe('{}'); }); it('can return user counts when requested', function () { - $this->subscribe('presence-test-channel-one'); + subscribe('presence-test-channel-one'); $response = await($this->signedPostRequest('events', [ 'name' => 'NewEvent', @@ -39,12 +39,12 @@ 'info' => 'user_count', ])); - $this->assertSame(200, $response->getStatusCode()); - $this->assertSame('{"channels":{"presence-test-channel-one":{"user_count":1},"test-channel-two":{}}}', $response->getBody()->getContents()); + expect($response->getStatusCode())->toBe(200); + expect($response->getBody()->getContents())->toBe('{"channels":{"presence-test-channel-one":{"user_count":1},"test-channel-two":{}}}'); }); it('can return subscription counts when requested', function () { - $this->subscribe('test-channel-two'); + subscribe('test-channel-two'); $response = await($this->signedPostRequest('events', [ 'name' => 'NewEvent', @@ -53,32 +53,29 @@ 'info' => 'subscription_count', ])); - $this->assertSame(200, $response->getStatusCode()); - $this->assertSame('{"channels":{"presence-test-channel-one":{},"test-channel-two":{"subscription_count":1}}}', $response->getBody()->getContents()); + expect($response->getStatusCode())->toBe(200); + expect($response->getBody()->getContents())->toBe('{"channels":{"presence-test-channel-one":{},"test-channel-two":{"subscription_count":1}}}'); }); it('can ignore a subscriber', function () { - $connection = $this->connect(); - $this->subscribe('test-channel-two', connection: $connection); - $promiseOne = $this->messagePromise($connection); + $connection = connect(); + subscribe('test-channel-two', connection: $connection); $response = await($this->signedPostRequest('events', [ 'name' => 'NewEvent', 'channels' => ['test-channel-one', 'test-channel-two'], 'data' => ['some' => 'data'], ])); - expect(await($promiseOne))->toBe('{"event":"NewEvent","data":{"some":"data"},"channel":"test-channel-two"}'); - $promiseTwo = $this->messagePromise($connection); $response = await($this->signedPostRequest('events', [ 'name' => 'NewEvent', 'channels' => ['test-channel-one', 'test-channel-two'], 'data' => ['some' => 'data'], - 'socket_id' => $this->connectionId, + 'socket_id' => $connection->socketId(), ])); - $this->assertSame(200, $response->getStatusCode()); - $this->assertSame('{}', $response->getBody()->getContents()); - expect(await($promiseTwo))->toBeFalse(); + $connection->assertReceived('{"event":"NewEvent","data":{"some":"data"},"channel":"test-channel-two"}', 1); + expect($response->getStatusCode())->toBe(200); + expect($response->getBody()->getContents())->toBe('{}'); }); it('validates invalid data', function ($payload) { diff --git a/tests/Feature/Reverb/ServerTest.php b/tests/Feature/Reverb/ServerTest.php index 61f88771..ba3c5430 100644 --- a/tests/Feature/Reverb/ServerTest.php +++ b/tests/Feature/Reverb/ServerTest.php @@ -1,115 +1,103 @@ connect(); - $this->connect(key: 'pusher-key-2'); - $this->connect(key: 'pusher-key-3', headers: ['Origin' => 'http://laravel.com']); + connect(); + connect(key: 'pusher-key-2'); + connect(key: 'pusher-key-3', headers: ['Origin' => 'http://laravel.com']); }); it('can subscribe to a channel', function () { - $response = $this->subscribe('test-channel'); - - $this->assertCount(1, channelManager()->find('test-channel')->connections()); + $response = subscribe('test-channel'); + expect(channels()->find('test-channel')->connections())->toHaveCount(1); expect($response)->toBe('{"event":"pusher_internal:subscription_succeeded","channel":"test-channel"}'); }); it('can subscribe to a private channel', function () { - $response = $this->subscribe('private-test-channel'); + $response = subscribe('private-test-channel'); expect($response)->toBe('{"event":"pusher_internal:subscription_succeeded","channel":"private-test-channel"}'); }); it('can subscribe to a presence channel', function () { $data = ['user_id' => 1, 'user_info' => ['name' => 'Test User']]; - $response = $this->subscribe('presence-test-channel', data: $data); + $response = subscribe('presence-test-channel', data: $data); - expect(Str::contains($response, 'pusher_internal:subscription_succeeded'))->toBeTrue(); - expect(Str::contains($response, '"hash\":{\"1\":{\"name\":\"Test User\"}}'))->toBeTrue(); + expect($response)->toContain('pusher_internal:subscription_succeeded'); + expect($response)->toContain('"hash\":{\"1\":{\"name\":\"Test User\"}}'); }); it('can subscribe to a cache channel', function () { - $response = $this->subscribe('cache-test-channel'); + $response = subscribe('cache-test-channel'); expect($response)->toBe('{"event":"pusher_internal:subscription_succeeded","channel":"cache-test-channel"}'); }); it('can subscribe to a private cache channel', function () { - $response = $this->subscribe('private-cache-test-channel'); + $response = subscribe('private-cache-test-channel'); expect($response)->toBe('{"event":"pusher_internal:subscription_succeeded","channel":"private-cache-test-channel"}'); }); it('can subscribe to a presence cache channel', function () { $data = ['user_id' => 1, 'user_info' => ['name' => 'Test User']]; - $response = $this->subscribe('presence-cache-test-channel', data: $data); + $response = subscribe('presence-cache-test-channel', data: $data); - expect(Str::contains($response, 'pusher_internal:subscription_succeeded'))->toBeTrue(); - expect(Str::contains($response, '"hash\":{\"1\":{\"name\":\"Test User\"}}'))->toBeTrue(); + expect($response)->toContain('pusher_internal:subscription_succeeded'); + expect($response)->toContain('"hash\":{\"1\":{\"name\":\"Test User\"}}'); }); it('can notify other subscribers of a presence channel when a new member joins', function () { - $connectionOne = $this->connect(); + $connectionOne = connect(); $data = ['user_id' => 1, 'user_info' => ['name' => 'Test User 1']]; - $this->subscribe('presence-test-channel', data: $data, connection: $connectionOne); - $promiseOne = $this->messagePromise($connectionOne); + subscribe('presence-test-channel', data: $data, connection: $connectionOne); - $connectionTwo = $this->connect(); + $connectionTwo = connect(); $data = ['user_id' => 2, 'user_info' => ['name' => 'Test User 2']]; - $this->subscribe('presence-test-channel', data: $data, connection: $connectionTwo); - $promiseTwo = $this->messagePromise($connectionTwo); + subscribe('presence-test-channel', data: $data, connection: $connectionTwo); - $connectionThree = $this->connect(); $data = ['user_id' => 3, 'user_info' => ['name' => 'Test User 3']]; - $this->subscribe('presence-test-channel', data: $data, connection: $connectionThree); + subscribe('presence-test-channel', data: $data); - expect(await($promiseOne))->toBe('{"event":"pusher_internal:member_added","data":{"user_id":2,"user_info":{"name":"Test User 2"}},"channel":"presence-test-channel"}'); - expect(await($promiseTwo))->toBe('{"event":"pusher_internal:member_added","data":{"user_id":3,"user_info":{"name":"Test User 3"}},"channel":"presence-test-channel"}'); + $connectionOne->assertReceived('{"event":"pusher_internal:member_added","data":{"user_id":2,"user_info":{"name":"Test User 2"}},"channel":"presence-test-channel"}'); + $connectionTwo->assertReceived('{"event":"pusher_internal:member_added","data":{"user_id":3,"user_info":{"name":"Test User 3"}},"channel":"presence-test-channel"}'); }); it('can notify other subscribers of a presence channel when a member leaves', function () { - $connectionOne = $this->connect(); + $connectionOne = connect(); $data = ['user_id' => 1, 'user_info' => ['name' => 'Test User 1']]; - $this->subscribe('presence-test-channel', data: $data, connection: $connectionOne); - $promiseOne = $this->messagePromise($connectionOne); + subscribe('presence-test-channel', data: $data, connection: $connectionOne); - $connectionTwo = $this->connect(); + $connectionTwo = connect(); $data = ['user_id' => 2, 'user_info' => ['name' => 'Test User 2']]; - $this->subscribe('presence-test-channel', data: $data, connection: $connectionTwo); - $promiseTwo = $this->messagePromise($connectionTwo); + subscribe('presence-test-channel', data: $data, connection: $connectionTwo); - $connectionThree = $this->connect(); + $connectionThree = connect(); $data = ['user_id' => 3, 'user_info' => ['name' => 'Test User 3']]; - $this->subscribe('presence-test-channel', data: $data, connection: $connectionThree); - - expect(await($promiseOne))->toBe('{"event":"pusher_internal:member_added","data":{"user_id":2,"user_info":{"name":"Test User 2"}},"channel":"presence-test-channel"}'); - expect(await($promiseTwo))->toBe('{"event":"pusher_internal:member_added","data":{"user_id":3,"user_info":{"name":"Test User 3"}},"channel":"presence-test-channel"}'); + subscribe('presence-test-channel', data: $data, connection: $connectionThree); - $promiseThree = $this->messagePromise($connectionOne); - $promiseFour = $this->messagePromise($connectionOne); + $connectionOne->assertReceived('{"event":"pusher_internal:member_added","data":{"user_id":2,"user_info":{"name":"Test User 2"}},"channel":"presence-test-channel"}'); + $connectionTwo->assertReceived('{"event":"pusher_internal:member_added","data":{"user_id":3,"user_info":{"name":"Test User 3"}},"channel":"presence-test-channel"}'); - $connectionThree->close(); + disconnect($connectionThree); - expect(await($promiseThree))->toBe('{"event":"pusher_internal:member_removed","data":{"user_id":3},"channel":"presence-test-channel"}'); - expect(await($promiseFour))->toBe('{"event":"pusher_internal:member_removed","data":{"user_id":3},"channel":"presence-test-channel"}'); + $connectionOne->assertReceived('{"event":"pusher_internal:member_removed","data":{"user_id":3},"channel":"presence-test-channel"}'); + $connectionTwo->assertReceived('{"event":"pusher_internal:member_removed","data":{"user_id":3},"channel":"presence-test-channel"}'); }); it('can receive a cached message when joining a cache channel', function () { - $connection = $this->connect(); + $connection = connect(); $this->triggerEvent( 'cache-test-channel', @@ -117,14 +105,13 @@ ['foo' => 'bar'] ); - $this->subscribe('cache-test-channel', connection: $connection); - $promise = $this->messagePromise($connection); + subscribe('cache-test-channel', connection: $connection); - expect(await($promise))->toBe('{"event":"App\\\\Events\\\\TestEvent","data":{"foo":"bar"},"channel":"cache-test-channel"}'); + $connection->assertReceived('{"event":"App\\\\Events\\\\TestEvent","data":{"foo":"bar"},"channel":"cache-test-channel"}'); }); it('can receive a cached message when joining a private cache channel', function () { - $connection = $this->connect(); + $connection = connect(); $this->triggerEvent( 'private-cache-test-channel', @@ -132,14 +119,13 @@ ['foo' => 'bar'] ); - $this->subscribe('private-cache-test-channel', connection: $connection); - $promise = $this->messagePromise($connection); + subscribe('private-cache-test-channel', connection: $connection); - expect(await($promise))->toBe('{"event":"App\\\\Events\\\\TestEvent","data":{"foo":"bar"},"channel":"private-cache-test-channel"}'); + $connection->assertReceived('{"event":"App\\\\Events\\\\TestEvent","data":{"foo":"bar"},"channel":"private-cache-test-channel"}'); }); it('can receive a cached message when joining a presence cache channel', function () { - $connection = $this->connect(); + $connection = connect(); $this->triggerEvent( 'presence-cache-test-channel', @@ -147,48 +133,41 @@ ['foo' => 'bar'] ); - $this->subscribe('presence-cache-test-channel', connection: $connection); - $promise = $this->messagePromise($connection); + subscribe('presence-cache-test-channel', connection: $connection); - expect(await($promise))->toBe('{"event":"App\\\\Events\\\\TestEvent","data":{"foo":"bar"},"channel":"presence-cache-test-channel"}'); + $connection->assertReceived('{"event":"App\\\\Events\\\\TestEvent","data":{"foo":"bar"},"channel":"presence-cache-test-channel"}'); }); it('can receive a cach missed message when joining a cache channel with an empty cache', function () { - $connection = $this->connect(); - $this->subscribe('cache-test-channel', connection: $connection); - $promise = $this->messagePromise($connection); + $connection = connect(); + subscribe('cache-test-channel', connection: $connection); - expect(await($promise))->toBe('{"event":"pusher:cache_miss","channel":"cache-test-channel"}'); + $connection->assertReceived('{"event":"pusher:cache_miss","channel":"cache-test-channel"}'); }); it('can receive a cach missed message when joining a private cache channel with an empty cache', function () { - $connection = $this->connect(); - $this->subscribe('private-cache-test-channel', connection: $connection); - $promise = $this->messagePromise($connection); + $connection = connect(); + subscribe('private-cache-test-channel', connection: $connection); - expect(await($promise))->toBe('{"event":"pusher:cache_miss","channel":"private-cache-test-channel"}'); + $connection->assertReceived('{"event":"pusher:cache_miss","channel":"private-cache-test-channel"}'); }); it('can receive a cach missed message when joining a presence cache channel with an empty cache', function () { - $connection = $this->connect(); - $this->subscribe('presence-cache-test-channel', connection: $connection); - $promise = $this->messagePromise($connection); + $connection = connect(); + subscribe('presence-cache-test-channel', connection: $connection); - expect(await($promise))->toBe('{"event":"pusher:cache_miss","channel":"presence-cache-test-channel"}'); + $connection->assertReceived('{"event":"pusher:cache_miss","channel":"presence-cache-test-channel"}'); }); it('can receive a message broadcast from the server', function () { - $connectionOne = $this->connect(); - $this->subscribe('test-channel', connection: $connectionOne); - $promiseOne = $this->messagePromise($connectionOne); + $connectionOne = connect(); + subscribe('test-channel', connection: $connectionOne); - $connectionTwo = $this->connect(); - $this->subscribe('test-channel', connection: $connectionTwo); - $promiseTwo = $this->messagePromise($connectionTwo); + $connectionTwo = connect(); + subscribe('test-channel', connection: $connectionTwo); - $connectionThree = $this->connect(); - $this->subscribe('test-channel', connection: $connectionThree); - $promiseThree = $this->messagePromise($connectionThree); + $connectionThree = connect(); + subscribe('test-channel', connection: $connectionThree); $this->triggerEvent( 'test-channel', @@ -196,15 +175,14 @@ ['foo' => 'bar'] ); - foreach (await(all([$promiseOne, $promiseTwo, $promiseThree])) as $response) { - expect($response)->toBe('{"event":"App\\\\Events\\\\TestEvent","data":{"foo":"bar"},"channel":"test-channel"}'); - } + $connectionOne->assertReceived('{"event":"App\\\\Events\\\\TestEvent","data":{"foo":"bar"},"channel":"test-channel"}'); + $connectionTwo->assertReceived('{"event":"App\\\\Events\\\\TestEvent","data":{"foo":"bar"},"channel":"test-channel"}'); + $connectionThree->assertReceived('{"event":"App\\\\Events\\\\TestEvent","data":{"foo":"bar"},"channel":"test-channel"}'); }); it('can handle an event', function () { - $connection = $this->connect(); - $this->subscribe('presence-test-channel', connection: $connection, data: ['user_id' => 1, 'user_info' => ['name' => 'Test User 1']]); - $promise = $this->messagePromise($connection); + $connection = connect(); + subscribe('presence-test-channel', connection: $connection, data: ['user_id' => 1, 'user_info' => ['name' => 'Test User 1']]); $this->triggerEvent( 'presence-test-channel', @@ -212,63 +190,52 @@ ['foo' => 'bar'] ); - expect(await($promise))->toBe('{"event":"App\\\\Events\\\\TestEvent","data":{"foo":"bar"},"channel":"presence-test-channel"}'); + $connection->assertReceived('{"event":"App\\\\Events\\\\TestEvent","data":{"foo":"bar"},"channel":"presence-test-channel"}'); }); it('can respond to a ping', function () { - $connection = $this->connect(); - $promise = $this->messagePromise($connection); + $connection = connect(); - $this->send(['event' => 'pusher:ping'], $connection); + send(['event' => 'pusher:ping'], $connection); - expect(await($promise))->toBe('{"event":"pusher:pong"}'); + $connection->assertReceived('{"event":"pusher:pong"}'); }); it('it can ping inactive subscribers', function () { - $connection = $this->connect(); - $this->subscribe('test-channel', connection: $connection); - $promise = $this->messagePromise($connection); + $connection = connect(); + subscribe('test-channel', connection: $connection); - Arr::first(channelManager()->connections())->setLastSeenAt(time() - 60 * 10); + Arr::first(channels()->connections())->setLastSeenAt(time() - 60 * 10); - (new PingInactiveConnections)->handle(channelManager()); + (new PingInactiveConnections)->handle(channels()); - expect(await($promise))->toBe('{"event":"pusher:ping"}'); + $connection->assertReceived('{"event":"pusher:ping"}'); }); it('it can disconnect inactive subscribers', function () { - $connection = $this->connect(); - $this->subscribe('test-channel', connection: $connection); - $promise = $this->disconnectPromise($connection); + $connection = connect(); + subscribe('test-channel', connection: $connection); - expect(channelManager()->find('test-channel')->connections())->toHaveCount(1); + expect(channels()->find('test-channel')->connections())->toHaveCount(1); - Arr::first(channelManager()->connections())->setLastSeenAt(time() - 60 * 10); + Arr::first(channels()->connections())->setLastSeenAt(time() - 60 * 10); - $promiseTwo = $this->messagePromise($connection); - (new PingInactiveConnections)->handle( - channelManager() - ); - expect(await($promiseTwo))->toBe('{"event":"pusher:ping"}'); + (new PingInactiveConnections)->handle(channels()); - $promiseThree = $this->messagePromise($connection); - (new PruneStaleConnections)->handle( - channelManager() - ); + (new PruneStaleConnections)->handle(channels()); - expect(channelManager()->find('test-channel')->connections())->toHaveCount(0); + expect(channels()->find('test-channel')->connections())->toHaveCount(0); - expect(await($promiseThree))->toBe('{"event":"pusher:error","data":"{\"code\":4201,\"message\":\"Pong reply not received in time\"}"}'); - expect(await($promise))->toBe('Connection Closed.'); + $connection->assertReceived('{"event":"pusher:ping"}'); + $connection->assertReceived('{"event":"pusher:error","data":"{\"code\":4201,\"message\":\"Pong reply not received in time\"}"}'); }); it('can handle a client whisper', function () { - $connection = $this->connect(); - $this->subscribe('test-channel', connection: $connection); + $connection = connect(); + subscribe('test-channel', connection: $connection); - $newConnection = $this->connect(); - $this->subscribe('test-channel', connection: $newConnection); - $promise = $this->messagePromise($newConnection); + $newConnection = connect(); + subscribe('test-channel', connection: $newConnection); $connection->send( json_encode([ @@ -281,62 +248,62 @@ ]) ); - expect(await($promise))->toBe('{"event":"client-start-typing","channel":"test-channel","data":{"id":123,"name":"Joe Dixon"}}'); + $newConnection->assertReceived('{"event":"client-start-typing","channel":"test-channel","data":{"id":123,"name":"Joe Dixon"}}'); }); it('can subscribe a connection to multiple channels', function () { - $connection = $this->connect(); - $this->subscribe('test-channel', connection: $connection); - $this->subscribe('test-channel-2', connection: $connection); - $this->subscribe('private-test-channel-3', connection: $connection, data: ['foo' => 'bar']); - $this->subscribe('presence-test-channel-4', connection: $connection, data: ['user_id' => 1, 'user_info' => ['name' => 'Test User 1']]); - - expect(channelManager()->all())->toHaveCount(4); - collect(channelManager()->all())->each(function ($channel) use ($connection) { + $connection = connect(); + subscribe('test-channel', connection: $connection); + subscribe('test-channel-2', connection: $connection); + subscribe('private-test-channel-3', connection: $connection, data: ['foo' => 'bar']); + subscribe('presence-test-channel-4', connection: $connection, data: ['user_id' => 1, 'user_info' => ['name' => 'Test User 1']]); + + expect(channels()->all())->toHaveCount(4); + collect(channels()->all())->each(function ($channel) use ($connection) { expect($channel->connections())->toHaveCount(1); - expect(collect($channel->connections())->map(fn ($connection) => $connection->id()))->toContain($this->connectionId); + expect(collect($channel->connections())->map(fn ($conn) => $conn->id()))->toContain($connection->socketId()); }); }); it('can subscribe multiple connections to multiple channels', function () { - $connection = $this->connect(); - $this->subscribe('test-channel', connection: $connection); - $this->subscribe('test-channel-2', connection: $connection); - $this->subscribe('private-test-channel-3', connection: $connection, data: ['foo' => 'bar']); - $this->subscribe('presence-test-channel-4', connection: $connection, data: ['user_id' => 1, 'user_info' => ['name' => 'Test User 1']]); + $connection = connect(); + subscribe('test-channel', connection: $connection); + subscribe('test-channel-2', connection: $connection); + subscribe('private-test-channel-3', connection: $connection, data: ['foo' => 'bar']); + subscribe('presence-test-channel-4', connection: $connection, data: ['user_id' => 1, 'user_info' => ['name' => 'Test User 1']]); - $connection = $this->connect(); - $this->subscribe('test-channel', connection: $connection); - $this->subscribe('private-test-channel-3', connection: $connection, data: ['foo' => 'bar']); + $connection = connect(); + subscribe('test-channel', connection: $connection); + subscribe('private-test-channel-3', connection: $connection, data: ['foo' => 'bar']); - expect(channelManager()->all())->toHaveCount(4); + expect(channels()->all())->toHaveCount(4); - expect(channelManager()->find('test-channel')->connections())->toHaveCount(2); - expect(channelManager()->find('test-channel-2')->connections())->toHaveCount(1); - expect(channelManager()->find('private-test-channel-3')->connections())->toHaveCount(2); - expect(channelManager()->find('presence-test-channel-4')->connections())->toHaveCount(1); + expect(channels()->find('test-channel')->connections())->toHaveCount(2); + expect(channels()->find('test-channel-2')->connections())->toHaveCount(1); + expect(channels()->find('private-test-channel-3')->connections())->toHaveCount(2); + expect(channels()->find('presence-test-channel-4')->connections())->toHaveCount(1); }); it('fails to subscribe to a private channel with invalid auth signature', function () { - $response = $this->subscribe('private-test-channel', auth: 'invalid-signature'); + $response = subscribe('private-test-channel', auth: 'invalid-signature'); expect($response)->toBe('{"event":"pusher:error","data":"{\"code\":4009,\"message\":\"Connection is unauthorized\"}"}'); }); it('fails to subscribe to a presence channel with invalid auth signature', function () { - $response = $this->subscribe('presence-test-channel', auth: 'invalid-signature'); + $response = subscribe('presence-test-channel', auth: 'invalid-signature'); expect($response)->toBe('{"event":"pusher:error","data":"{\"code\":4009,\"message\":\"Connection is unauthorized\"}"}'); }); it('fails to subscribe to a private cache channel with invalid auth signature', function () { - $response = $this->subscribe('private-cache-test-channel', auth: 'invalid-signature'); + $response = subscribe('private-cache-test-channel', auth: 'invalid-signature'); expect($response)->toBe('{"event":"pusher:error","data":"{\"code\":4009,\"message\":\"Connection is unauthorized\"}"}'); }); it('fails to subscribe to a presence cache channel with invalid auth signature', function () { - $response = $this->subscribe('presence-cache-test-channel', auth: 'invalid-signature'); + $response = subscribe('presence-cache-test-channel', auth: 'invalid-signature'); expect($response)->toBe('{"event":"pusher:error","data":"{\"code\":4009,\"message\":\"Connection is unauthorized\"}"}'); }); @@ -345,27 +312,22 @@ $promise = new Deferred(); $connection = await( - connect('ws://0.0.0.0:8080/app/invalid-key') + wsConnect('ws://0.0.0.0:8080/app/invalid-key') ); $connection->on('message', function ($message) use ($promise) { $promise->resolve((string) $message); }); - $this->assertTrue( - Str::contains( - await($promise->promise()), - '{"event":"pusher:error","data":"{\"code\":4001,\"message\":\"Application does not exist\"}"}' - ) - ); + expect(await($promise->promise())) + ->toBe('{"event":"pusher:error","data":"{\"code\":4001,\"message\":\"Application does not exist\"}"}'); }); it('can publish and subscribe to a triggered event', function () { $this->usingRedis(); - $connection = $this->connect(); - $this->subscribe('presence-test-channel', connection: $connection, data: ['user_id' => 1, 'user_info' => ['name' => 'Test User 1']]); - $promise = $this->messagePromise($connection); + $connection = connect(); + subscribe('presence-test-channel', connection: $connection, data: ['user_id' => 1, 'user_info' => ['name' => 'Test User 1']]); $this->triggerEvent( 'presence-test-channel', @@ -373,18 +335,17 @@ ['foo' => 'bar'] ); - expect(await($promise))->toBe('{"event":"App\\\\Events\\\\TestEvent","data":{"foo":"bar"},"channel":"presence-test-channel"}'); + $connection->assertReceived('{"event":"App\\\\Events\\\\TestEvent","data":{"foo":"bar"},"channel":"presence-test-channel"}'); }); it('can publish and subscribe to a client whisper', function () { $this->usingRedis(); - $connection = $this->connect(); - $this->subscribe('test-channel', connection: $connection); + $connection = connect(); + subscribe('test-channel', connection: $connection); - $newConnection = $this->connect(); - $this->subscribe('test-channel', connection: $newConnection); - $promise = $this->messagePromise($newConnection); + $newConnection = connect(); + subscribe('test-channel', connection: $newConnection); $connection->send( json_encode([ @@ -397,44 +358,47 @@ ]) ); - expect(await($promise))->toBe('{"event":"client-start-typing","channel":"test-channel","data":{"id":123,"name":"Joe Dixon"}}'); + $newConnection->assertReceived('{"event":"client-start-typing","channel":"test-channel","data":{"id":123,"name":"Joe Dixon"}}'); }); it('cannot connect from an invalid origin', function () { $connection = await( - connect('ws://0.0.0.0:8080/app/pusher-key-3') + wsConnect('ws://0.0.0.0:8080/app/pusher-key-3') ); - $promise = $this->messagePromise($connection); - expect(await($promise))->toBe('{"event":"pusher:error","data":"{\"code\":4009,\"message\":\"Origin not allowed\"}"}'); + $promise = new Deferred(); + + $connection->on('message', function ($message) use ($promise) { + $promise->resolve((string) $message); + }); + + $message = await($promise->promise()); + + expect($message)->toBe('{"event":"pusher:error","data":"{\"code\":4009,\"message\":\"Origin not allowed\"}"}'); }); it('can connect from a valid origin', function () { $this->app['config']->set('reverb.apps.0.allowed_origins', ['0.0.0.0']); - $this->connect(); + connect(); }); it('limits the size of messages', function () { - $connection = $this->connect(key: 'pusher-key-3', headers: ['Origin' => 'http://laravel.com']); - $message = $this->send(['This message is waaaaaay longer than the 1 byte limit'], $connection); + $connection = connect(key: 'pusher-key-3', headers: ['Origin' => 'http://laravel.com']); + send(['This message is waaaaaay longer than the 1 byte limit'], $connection); - expect($message)->toBe('Maximum message size exceeded'); + $connection->assertReceived('Maximum message size exceeded'); }); it('removes a channel when no subscribers remain', function () { - $connection = $this->connect(); - $this->subscribe('test-channel', connection: $connection); + $connection = connect(); + subscribe('test-channel', connection: $connection); - expect(channelManager()->all())->toHaveCount(1); + expect(channels()->all())->toHaveCount(1); - $this->unsubscribe('test-channel', $connection); + unsubscribe('test-channel', $connection); - expect(channelManager()->all())->toHaveCount(0); -}); - -it('clears application state between requests', function () { - $this->subscribe('test-channel'); + $connection->await(); - expect($this->app->make(ChannelManager::class)->app())->toBeNull(); -})->todo(); + expect(channels()->all())->toHaveCount(0); +}); diff --git a/tests/Feature/Reverb/UsersTerminateControllerTest.php b/tests/Feature/Reverb/UsersTerminateControllerTest.php index 57165b0b..ce934a00 100644 --- a/tests/Feature/Reverb/UsersTerminateControllerTest.php +++ b/tests/Feature/Reverb/UsersTerminateControllerTest.php @@ -12,21 +12,21 @@ })->throws(ResponseException::class); it('unsubscribes from all channels and terminates a user', function () { - $connection = $this->connect(); - $this->subscribe('presence-test-channel-one', ['user_id' => '123'], connection: $connection); - $this->subscribe('test-channel-two', connection: $connection); + $connection = connect(); + subscribe('presence-test-channel-one', ['user_id' => '123'], connection: $connection); + subscribe('test-channel-two', connection: $connection); - $connection = $this->connect(); - $this->subscribe('presence-test-channel-one', ['user_id' => '456'], connection: $connection); - $this->subscribe('test-channel-two', connection: $connection); + $connection = connect(); + subscribe('presence-test-channel-one', ['user_id' => '456'], connection: $connection); + subscribe('test-channel-two', connection: $connection); - expect(collect(channelManager()->find('presence-test-channel-one')->connections()))->toHaveCount(2); - expect(collect(channelManager()->find('test-channel-two')->connections()))->toHaveCount(2); + expect(collect(channels()->find('presence-test-channel-one')->connections()))->toHaveCount(2); + expect(collect(channels()->find('test-channel-two')->connections()))->toHaveCount(2); $response = await($this->signedPostRequest('users/456/terminate_connections')); - $this->assertSame(200, $response->getStatusCode()); - $this->assertSame('{}', $response->getBody()->getContents()); - expect(collect(channelManager()->find('presence-test-channel-one')->connections()))->toHaveCount(1); - expect(collect(channelManager()->find('test-channel-two')->connections()))->toHaveCount(1); + expect($response->getStatusCode())->toBe(200); + expect($response->getBody()->getContents())->toBe('{}'); + expect(collect(channels()->all())->get('presence-test-channel-one')->connections())->toHaveCount(1); + expect(collect(channels()->all())->get('test-channel-two')->connections())->toHaveCount(1); }); diff --git a/tests/Feature/ServerTest.php b/tests/Feature/ServerTest.php index 51bba291..542c8628 100644 --- a/tests/Feature/ServerTest.php +++ b/tests/Feature/ServerTest.php @@ -2,7 +2,7 @@ use Laravel\Reverb\Contracts\ChannelManager; use Laravel\Reverb\Pusher\Server; -use Laravel\Reverb\Tests\Connection; +use Laravel\Reverb\Tests\FakeConnection; use Laravel\Reverb\Tests\TestCase; uses(TestCase::class); @@ -12,11 +12,11 @@ }); it('can handle a connection', function () { - $this->server->open($connection = new Connection); + $this->server->open($connection = new FakeConnection); expect($connection->lastSeenAt())->not->toBeNull(); - $connection->assertSent([ + $connection->assertReceived([ 'event' => 'pusher:connection_established', 'data' => json_encode([ 'socket_id' => $connection->id(), @@ -32,13 +32,13 @@ $this->app->singleton(ChannelManager::class, fn () => $channelManager); $server = $this->app->make(Server::class); - $server->close(new Connection); + $server->close(new FakeConnection); $channelManager->shouldHaveReceived('unsubscribeFromAll'); }); it('can handle a new message', function () { - $this->server->open($connection = new Connection); + $this->server->open($connection = new FakeConnection); $this->server->message( $connection, json_encode([ @@ -49,7 +49,7 @@ ], ])); - $connection->assertSent([ + $connection->assertReceived([ 'event' => 'pusher:connection_established', 'data' => json_encode([ 'socket_id' => $connection->id(), @@ -57,7 +57,7 @@ ]), ]); - $connection->assertSent([ + $connection->assertReceived([ 'event' => 'pusher_internal:subscription_succeeded', 'channel' => 'test-channel', ]); @@ -65,7 +65,7 @@ it('sends an error if something fails', function () { $this->server->message( - $connection = new Connection, + $connection = new FakeConnection, 'Hi' ); @@ -79,7 +79,7 @@ ], ])); - $connection->assertSent([ + $connection->assertReceived([ 'event' => 'pusher:error', 'data' => json_encode([ 'code' => 4200, @@ -87,7 +87,7 @@ ]), ]); - $connection->assertSent([ + $connection->assertReceived([ 'event' => 'pusher:error', 'data' => json_encode([ 'code' => 4009, @@ -98,7 +98,7 @@ it('can subscribe a user to a channel', function () { $this->server->message( - $connection = new Connection, + $connection = new FakeConnection, json_encode([ 'event' => 'pusher:subscribe', 'data' => [ @@ -109,7 +109,7 @@ expect($connection->lastSeenAt())->not->toBeNull(); - $connection->assertSent([ + $connection->assertReceived([ 'event' => 'pusher_internal:subscription_succeeded', 'channel' => 'test-channel', ]); @@ -117,7 +117,7 @@ it('can subscribe a user to a private channel', function () { $this->server->message( - $connection = new Connection, + $connection = new FakeConnection, json_encode([ 'event' => 'pusher:subscribe', 'data' => [ @@ -126,7 +126,7 @@ ], ])); - $connection->assertSent([ + $connection->assertReceived([ 'event' => 'pusher_internal:subscription_succeeded', 'channel' => 'private-test-channel', ]); @@ -134,7 +134,7 @@ it('can subscribe a user to a presence channel', function () { $this->server->message( - $connection = new Connection, + $connection = new FakeConnection, json_encode([ 'event' => 'pusher:subscribe', 'data' => [ @@ -143,7 +143,7 @@ ], ])); - $connection->assertSent([ + $connection->assertReceived([ 'event' => 'pusher_internal:subscription_succeeded', 'data' => json_encode([ 'presence' => [ @@ -158,7 +158,7 @@ it('receives no data when no previous event triggered when joining a cache channel', function () { $this->server->message( - $connection = new Connection, + $connection = new FakeConnection, json_encode([ 'event' => 'pusher:subscribe', 'data' => [ @@ -166,20 +166,20 @@ ], ])); - $connection->assertSent([ + $connection->assertReceived([ 'event' => 'pusher_internal:subscription_succeeded', 'channel' => 'cache-test-channel', ]); - $connection->assertSent([ + $connection->assertReceived([ 'event' => 'pusher:cache_miss', 'channel' => 'cache-test-channel', ]); - $connection->assertSendCount(2); + $connection->assertReceivedCount(2); }); it('receives last triggered event when joining a cache channel', function () { $this->server->message( - $connection = new Connection, + $connection = new FakeConnection, json_encode([ 'event' => 'pusher:subscribe', 'data' => [ @@ -192,7 +192,7 @@ $channel->broadcast(['foo' => 'bar']); $this->server->message( - $connection = new Connection, + $connection = new FakeConnection, json_encode([ 'event' => 'pusher:subscribe', 'data' => [ @@ -200,12 +200,12 @@ ], ])); - $connection->assertSent([ + $connection->assertReceived([ 'event' => 'pusher_internal:subscription_succeeded', 'channel' => 'cache-test-channel', ]); - $connection->assertSent(['foo' => 'bar']); - $connection->assertSendCount(2); + $connection->assertReceived(['foo' => 'bar']); + $connection->assertReceivedCount(2); }); it('unsubscribes a user from a channel on disconnection', function () { @@ -216,7 +216,7 @@ $server = $this->app->make(Server::class); $server->message( - $connection = new Connection, + $connection = new FakeConnection, json_encode([ 'event' => 'pusher:subscribe', 'data' => [ @@ -240,7 +240,7 @@ $server = $this->app->make(Server::class); $server->message( - $connection = new Connection, + $connection = new FakeConnection, json_encode([ 'event' => 'pusher:subscribe', 'data' => [ @@ -264,7 +264,7 @@ $server = $this->app->make(Server::class); $server->message( - $connection = new Connection, + $connection = new FakeConnection, json_encode([ 'event' => 'pusher:subscribe', 'data' => [ @@ -282,9 +282,9 @@ it('it rejects a connection from an invalid origin', function () { $this->app['config']->set('reverb.apps.apps.0.allowed_origins', ['laravel.com']); - $this->server->open($connection = new Connection); + $this->server->open($connection = new FakeConnection); - $connection->assertSent([ + $connection->assertReceived([ 'event' => 'pusher:error', 'data' => json_encode([ 'code' => 4009, @@ -295,9 +295,9 @@ it('it accepts a connection from an valid origin', function () { $this->app['config']->set('reverb.apps.0.allowed_origins', ['localhost']); - $this->server->open($connection = new Connection); + $this->server->open($connection = new FakeConnection); - $connection->assertSent([ + $connection->assertReceived([ 'event' => 'pusher:connection_established', 'data' => json_encode([ 'socket_id' => $connection->id(), diff --git a/tests/Helpers/ApiGateway.php b/tests/Helpers/ApiGateway.php new file mode 100644 index 00000000..f93968cd --- /dev/null +++ b/tests/Helpers/ApiGateway.php @@ -0,0 +1,11 @@ +on('message', function ($message) use ($promise) { + $promise->resolve((string) $message); + }); + + $message = await($promise->promise()); + + expect($message)->toContain('connection_established'); + + $connection->socketId = json_decode(json_decode($message)->data)->socket_id; + + return $connection; +} + +/** + * Send a message to the connected client. + */ +function send(array $message, ?TestConnection $connection = null) +{ + $connection = $connection ?: connect(); + + $connection->send(json_encode($message)); + + return $connection->await(); +} + +/** + * Subscribe to a channel. + */ +function subscribe(string $channel, ?array $data = [], ?string $auth = null, ?TestConnection $connection = null): string +{ + $data = ! empty($data) ? json_encode($data) : null; + + if (! $auth && Str::startsWith($channel, ['private-', 'presence-'])) { + $connection = $connection ?: connect(); + $auth = validAuth($connection->socketId(), $channel, $data); + } + + return send([ + 'event' => 'pusher:subscribe', + 'data' => array_filter([ + 'channel' => $channel, + 'channel_data' => $data, + 'auth' => $auth, + ]), + ], $connection); +} + +/** + * Unsubscribe to a channel. + */ +function unsubscribe(string $channel, ?TestConnection $connection = null): ?string +{ + return send([ + 'event' => 'pusher:unsubscribe', + 'data' => ['channel' => $channel], + ], $connection); +} + +/** + * Disconnect the connected client. + */ +function disconnect(TestConnection $connection): string +{ + $promise = new Deferred; + + $connection->on('close', function () use ($promise) { + $promise->resolve('Connection Closed.'); + }); + + $connection->close(); + + return await($promise->promise()); +} diff --git a/tests/Pest.php b/tests/Pest.php index c3fa2ef5..85af36c7 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -4,10 +4,9 @@ use Laravel\Reverb\Application; use Laravel\Reverb\Contracts\ApplicationProvider; use Laravel\Reverb\Contracts\ChannelManager; -use Laravel\Reverb\Contracts\ConnectionManager; use Laravel\Reverb\Managers\Connections; use Laravel\Reverb\Servers\Reverb\ChannelConnection; -use Laravel\Reverb\Tests\Connection; +use Laravel\Reverb\Tests\FakeConnection; use Laravel\Reverb\Tests\SerializableConnection; use Laravel\Reverb\Tests\TestCase; use Ramsey\Uuid\Uuid; @@ -17,16 +16,15 @@ /** * Create a defined number of connections. * - * @param bool $serializable * @return array */ -function connections(int $count = 1, array $data = [], $serializable = false): array +function factory(int $count = 1, array $data = [], bool $serializable = false): array { return Collection::make(range(1, $count))->map(function () use ($data, $serializable) { return new ChannelConnection( $serializable ? new SerializableConnection(Uuid::uuid4()) - : new Connection(Uuid::uuid4()), + : new FakeConnection(Uuid::uuid4()), $data ); })->all(); @@ -46,18 +44,10 @@ function validAuth(string $connectionId, string $channel, ?string $data = null): return 'app-key:'.hash_hmac('sha256', $signature, 'pusher-secret'); } -/** - * Return the connection manager. - */ -function connectionManager(): ConnectionManager -{ - return app(ConnectionManager::class); -} - /** * Return the channel manager. */ -function channelManager(?Application $app = null): ChannelManager +function channels(?Application $app = null): ChannelManager { return app(ChannelManager::class) ->for($app ?: app(ApplicationProvider::class)->all()->first()); diff --git a/tests/ReverbTestCase.php b/tests/ReverbTestCase.php index 00ae1d52..8a195910 100644 --- a/tests/ReverbTestCase.php +++ b/tests/ReverbTestCase.php @@ -2,10 +2,8 @@ namespace Laravel\Reverb\Tests; -use Clue\React\Redis\Client; use Illuminate\Support\Str; use Laravel\Reverb\Concerns\InteractsWithAsyncRedis; -use Laravel\Reverb\Contracts\Connection; use Laravel\Reverb\Event; use Laravel\Reverb\ServerManager; use Laravel\Reverb\Servers\Reverb\Factory; @@ -13,14 +11,10 @@ use React\Async\SimpleFiber; use React\EventLoop\Loop; use React\Http\Browser; -use React\Promise\Deferred; use React\Promise\PromiseInterface; -use React\Promise\Timer\TimeoutException; use ReflectionObject; -use function Ratchet\Client\connect; use function React\Async\await; -use function React\Promise\Timer\timeout; class ReverbTestCase extends TestCase { @@ -51,9 +45,8 @@ protected function tearDown(): void * Define environment setup. * * @param \Illuminate\Foundation\Application $app - * @return void */ - protected function defineEnvironment($app) + protected function defineEnvironment($app): void { parent::defineEnvironment($app); @@ -78,7 +71,10 @@ protected function defineEnvironment($app) ]); } - public function usingRedis() + /** + * Configure the server to use Redis. + */ + public function usingRedis(): void { app(ServerManager::class)->withPublishing(); @@ -88,12 +84,8 @@ public function usingRedis() /** * Start the WebSocket server. - * - * @param string $host - * @param string $port - * @return void */ - public function startServer($host = '0.0.0.0', $port = '8080') + public function startServer(string $host = '0.0.0.0', string $port = '8080'): void { $this->resetFiber(); $this->server = Factory::make($host, $port, $this->loop); @@ -102,10 +94,8 @@ public function startServer($host = '0.0.0.0', $port = '8080') /** * Reset the Fiber instance. * This prevents using a stale fiber between tests. - * - * @return void */ - protected function resetFiber() + protected function resetFiber(): void { $fiber = new SimpleFiber(); $fiberRef = new ReflectionObject($fiber); @@ -116,164 +106,14 @@ protected function resetFiber() /** * Stop the running WebSocket server. - * - * @return void */ - public function stopServer() + public function stopServer(): void { if ($this->server) { $this->server->stop(); } } - /** - * Connect to the WebSocket server. - * - * @param string $host - * @param string $port - * @param string $key - * @return \Ratchet\Client\WebSocket - */ - public function connect($host = '0.0.0.0', $port = '8080', $key = 'pusher-key', $headers = []) - { - $promise = new Deferred; - - $connection = await( - connect("ws://{$host}:{$port}/app/{$key}", headers: $headers) - ); - - $connection->on('message', function ($message) use ($promise) { - $promise->resolve((string) $message); - }); - - $message = await($promise->promise()); - - $this->assertTrue( - Str::contains( - $message, - 'connection_established' - ) - ); - - $message = json_decode($message, true); - $data = json_decode($message['data'], true); - - $this->connectionId = $data['socket_id'] ?? null; - - return $connection; - } - - /** - * Send a message to the connected client. - */ - public function send(array $message, ?WebSocket $connection = null): ?string - { - $promise = new Deferred; - - $connection = $connection ?: $this->connect(); - - $connection->on('message', function ($message) use ($promise) { - $promise->resolve((string) $message); - }); - - $connection->on('close', function ($code, $message) use ($promise) { - $promise->resolve((string) $message); - }); - - $connection->send(json_encode($message)); - - return await(timeout($promise->promise(), 2, $this->loop) - ->then( - fn ($message) => $message, - fn (TimeoutException $error) => null - )); - } - - /** - * Disconnect the connected client. - */ - public function disconnect(WebSocket $connection): string - { - $promise = new Deferred; - - $connection->on('close', function () use ($promise) { - $promise->resolve('Connection Closed.'); - }); - - $connection->close(); - - return await($promise->promise()); - } - - /** - * Subscribe to a channel. - */ - public function subscribe(string $channel, ?array $data = [], ?string $auth = null, ?WebSocket $connection = null): string - { - $data = ! empty($data) ? json_encode($data) : null; - - if (! $auth && Str::startsWith($channel, ['private-', 'presence-'])) { - $connection = $connection ?: $this->connect(); - $auth = validAuth($this->connectionId, $channel, $data); - } - - return $this->send([ - 'event' => 'pusher:subscribe', - 'data' => array_filter([ - 'channel' => $channel, - 'channel_data' => $data, - 'auth' => $auth, - ]), - ], $connection); - } - - /** - * Unsubscribe to a channel. - */ - public function unsubscribe(string $channel, ?WebSocket $connection = null): ?string - { - return $this->send([ - 'event' => 'pusher:unsubscribe', - 'data' => ['channel' => $channel], - ], $connection); - } - - /** - * Return a promise for the next message received to the given connection. - * - * @param \Ratchet\Client\WebSocketWebSocket $connection - */ - public function messagePromise(WebSocket $connection) - { - $promise = new Deferred; - - $connection->on('message', function ($message) use ($promise) { - $promise->resolve((string) $message); - }); - - return timeout($promise->promise(), 2, $this->loop) - ->then( - fn ($message) => $message, - fn (TimeoutException $error) => false - ); - } - - /** - * Return a promise when a given connection is disconnected. - * - * @param \Ratchet\Client\WebSocketWebSocket $connection - */ - public function disconnectPromise(WebSocket $connection): PromiseInterface - { - $promise = new Deferred; - - $connection->on('close', function ($message) use ($promise) { - $promise->resolve('Connection Closed.'); - }); - - return $promise->promise(); - } - /** * Send an event to the server. */ @@ -285,10 +125,13 @@ public function triggerEvent(string $channel, string $event, array $data = []): 'data' => $data, ])); - $this->assertSame(200, $response->getStatusCode()); - $this->assertSame('{}', $response->getBody()->getContents()); + expect($response->getStatusCode())->toBe(200); + expect($response->getBody()->getContents())->toBe('{}'); } + /** + * Send a request to the server. + */ public function request(string $path, string $method = 'GET', mixed $data = '', string $host = '0.0.0.0', string $port = '8080', string $appId = '123456'): PromiseInterface { return (new Browser($this->loop)) @@ -300,6 +143,9 @@ public function request(string $path, string $method = 'GET', mixed $data = '', ); } + /** + * Send a signed request to the server. + */ public function signedRequest(string $path, string $method = 'GET', mixed $data = '', string $host = '0.0.0.0', string $port = '8080', string $appId = '123456'): PromiseInterface { $hash = md5(json_encode($data)); @@ -313,7 +159,7 @@ public function signedRequest(string $path, string $method = 'GET', mixed $data } /** - * Post a request to the server. + * Send a POST request to the server. */ public function postReqeust(string $path, array $data = [], string $host = '0.0.0.0', string $port = '8080', string $appId = '123456'): PromiseInterface { @@ -321,7 +167,7 @@ public function postReqeust(string $path, array $data = [], string $host = '0.0. } /** - * Post a signed request to the server. + * Send a signed POST request to the server. */ public function signedPostRequest(string $path, array $data = [], string $host = '0.0.0.0', string $port = '8080', string $appId = '123456'): PromiseInterface { @@ -334,13 +180,11 @@ public function signedPostRequest(string $path, array $data = [], string $host = return $this->postReqeust("{$path}?{$query}&auth_signature={$signature}", $data, $host, $port, $appId); } - public function getWithSignature( - string $path, - array $data = [], - string $host = '0.0.0.0', - string $port = '8080', - string $appId = '123456' - ): PromiseInterface { + /** + * Send a signed GET request to the server. + */ + public function getWithSignature(string $path, array $data = [], string $host = '0.0.0.0', string $port = '8080', string $appId = '123456'): PromiseInterface + { $hash = md5(json_encode($data)); $timestamp = time(); $query = "auth_key=pusher-key&auth_timestamp={$timestamp}&auth_version=1.0&body_md5={$hash}"; diff --git a/tests/SerializableConnection.php b/tests/SerializableConnection.php index 4f16052e..f7e32e7e 100644 --- a/tests/SerializableConnection.php +++ b/tests/SerializableConnection.php @@ -4,7 +4,7 @@ use Laravel\Reverb\Concerns\SerializesConnections; use Laravel\Reverb\Contracts\SerializableConnection as ContractsSerializableConnection; -use Laravel\Reverb\Tests\Connection as BaseConnection; +use Laravel\Reverb\Tests\FakeConnection as BaseConnection; class SerializableConnection extends BaseConnection implements ContractsSerializableConnection { diff --git a/tests/TestConnection.php b/tests/TestConnection.php new file mode 100644 index 00000000..a2817c3d --- /dev/null +++ b/tests/TestConnection.php @@ -0,0 +1,96 @@ + + */ + public $receivedMessages = []; + + public function __construct(public WebSocket $connection) + { + $connection->on('message', function ($message) { + $this->receivedMessages[] = (string) $message; + }); + + $connection->on('close', function ($code, $message) { + $this->receivedMessages[] = (string) $message; + }); + } + + /** + * Get the socket ID of the connection. + */ + public function socketId(): string + { + return $this->socketId; + } + + /** + * Await all messages to the connection to be resolved. + */ + public function await(): mixed + { + $promise = new Deferred(); + + $this->connection->on('message', function ($message) use ($promise) { + $promise->resolve((string) $message); + }); + + $this->connection->on('close', function ($code, $message) use ($promise) { + $promise->resolve((string) $message); + }); + + return await( + timeout($promise->promise(), 2) + ->then( + fn ($message) => $message, + fn (TimeoutException $error) => false + ) + ); + } + + /** + * Assert that the connection received the given message. + */ + public function assertReceived(string $message, ?int $count = null): void + { + if (! in_array($message, $this->receivedMessages) || $count !== null) { + $this->await(); + } + + if ($count) { + $matches = array_filter($this->receivedMessages, fn ($m) => $m === $message); + + expect($matches)->toHaveCount($count); + } + + expect($this->receivedMessages)->toContain($message); + } + + /** + * Proxy method calls to the connection. + */ + public function __call($method, $arguments) + { + return $this->connection->{$method}(...$arguments); + } +} diff --git a/tests/Unit/Channels/CacheChannelTest.php b/tests/Unit/Channels/CacheChannelTest.php index 31d949bf..f741f640 100644 --- a/tests/Unit/Channels/CacheChannelTest.php +++ b/tests/Unit/Channels/CacheChannelTest.php @@ -3,10 +3,10 @@ use Laravel\Reverb\Channels\CacheChannel; use Laravel\Reverb\Channels\ChannelBroker; use Laravel\Reverb\Contracts\ChannelConnectionManager; -use Laravel\Reverb\Tests\Connection; +use Laravel\Reverb\Tests\FakeConnection; beforeEach(function () { - $this->connection = new Connection(); + $this->connection = new FakeConnection(); $this->channelConnectionManager = Mockery::spy(ChannelConnectionManager::class); $this->channelConnectionManager->shouldReceive('for') ->andReturn($this->channelConnectionManager); @@ -21,7 +21,7 @@ $channel->subscribe($this->connection); - $this->connection->assertNothingSent(); + $this->connection->assertNothingReceived(); }); it('stores last triggered event', function () { diff --git a/tests/Unit/Channels/ChannelBrokerTest.php b/tests/Unit/Channels/ChannelBrokerTest.php index a0cbd51e..2ae52562 100644 --- a/tests/Unit/Channels/ChannelBrokerTest.php +++ b/tests/Unit/Channels/ChannelBrokerTest.php @@ -3,7 +3,9 @@ use Laravel\Reverb\Channels\CacheChannel; use Laravel\Reverb\Channels\Channel; use Laravel\Reverb\Channels\ChannelBroker; +use Laravel\Reverb\Channels\PresenceCacheChannel; use Laravel\Reverb\Channels\PresenceChannel; +use Laravel\Reverb\Channels\PrivateCacheChannel; use Laravel\Reverb\Channels\PrivateChannel; it('can return a channel instance', function () { @@ -25,3 +27,13 @@ expect(ChannelBroker::create('cache-foo')) ->toBeInstanceOf(CacheChannel::class); }); + +it('can return a private cache channel instance', function () { + expect(ChannelBroker::create('private-cache-foo')) + ->toBeInstanceOf(PrivateCacheChannel::class); +}); + +it('can return a presence cache channel instance', function () { + expect(ChannelBroker::create('presence-cache-foo')) + ->toBeInstanceOf(PresenceCacheChannel::class); +}); diff --git a/tests/Unit/Channels/ChannelTest.php b/tests/Unit/Channels/ChannelTest.php index ceeaeed2..45c9a5a6 100644 --- a/tests/Unit/Channels/ChannelTest.php +++ b/tests/Unit/Channels/ChannelTest.php @@ -3,10 +3,10 @@ use Laravel\Reverb\Channels\Channel; use Laravel\Reverb\Contracts\ChannelConnectionManager; use Laravel\Reverb\Contracts\ChannelManager; -use Laravel\Reverb\Tests\Connection; +use Laravel\Reverb\Tests\FakeConnection; beforeEach(function () { - $this->connection = new Connection(); + $this->connection = new FakeConnection(); $this->channelConnectionManager = Mockery::spy(ChannelConnectionManager::class); $this->channelConnectionManager->shouldReceive('for') ->andReturn($this->channelConnectionManager); @@ -66,11 +66,11 @@ $this->channelConnectionManager->shouldReceive('all') ->once() - ->andReturn($connections = connections(3)); + ->andReturn($connections = factory(3)); $channel->broadcast(['foo' => 'bar']); - collect($connections)->each(fn ($connection) => $connection->assertSent(['foo' => 'bar'])); + collect($connections)->each(fn ($connection) => $connection->assertReceived(['foo' => 'bar'])); }); it('does not broadcast to the connection sending the message', function () { @@ -80,10 +80,10 @@ $this->channelConnectionManager->shouldReceive('all') ->once() - ->andReturn($connections = connections(3)); + ->andReturn($connections = factory(3)); - $channel->broadcast(['foo' => 'bar'], $connections[0]->connection()); + $channel->broadcast(['foo' => 'bar'], collect($connections)->first()->connection()); - $connections[0]->assertNothingSent(); - collect(array_slice($connections, -2))->each(fn ($connection) => $connection->assertSent(['foo' => 'bar'])); + collect($connections)->first()->assertNothingReceived(); + collect(array_slice($connections, -2))->each(fn ($connection) => $connection->assertReceived(['foo' => 'bar'])); }); diff --git a/tests/Unit/Channels/PresenceCacheChannelTest.php b/tests/Unit/Channels/PresenceCacheChannelTest.php index 845bc735..5ad5c655 100644 --- a/tests/Unit/Channels/PresenceCacheChannelTest.php +++ b/tests/Unit/Channels/PresenceCacheChannelTest.php @@ -4,10 +4,10 @@ use Laravel\Reverb\Contracts\ChannelConnectionManager; use Laravel\Reverb\Exceptions\ConnectionUnauthorized; use Laravel\Reverb\Servers\Reverb\ChannelConnection; -use Laravel\Reverb\Tests\Connection; +use Laravel\Reverb\Tests\FakeConnection; beforeEach(function () { - $this->connection = new Connection(); + $this->connection = new FakeConnection(); $this->channelConnectionManager = Mockery::spy(ChannelConnectionManager::class); $this->channelConnectionManager->shouldReceive('for') ->andReturn($this->channelConnectionManager); @@ -43,11 +43,11 @@ $this->channelConnectionManager->shouldReceive('all') ->once() - ->andReturn($connections = connections(3)); + ->andReturn($connections = factory(3)); $channel->broadcast(['foo' => 'bar']); - collect($connections)->each(fn ($connection) => $connection->assertSent(['foo' => 'bar'])); + collect($connections)->each(fn ($connection) => $connection->assertReceived(['foo' => 'bar'])); }); it('fails to subscribe if the signature is invalid', function () { @@ -62,8 +62,8 @@ $channel = new PresenceCacheChannel('presence-cache-test-channel'); $connections = [ - connections(data: ['user_info' => ['name' => 'Joe'], 'user_id' => 1])[0], - connections(data: ['user_info' => ['name' => 'Joe'], 'user_id' => 2])[0], + collect(factory(data: ['user_info' => ['name' => 'Joe'], 'user_id' => 1]))->first(), + collect(factory(data: ['user_info' => ['name' => 'Joe'], 'user_id' => 2]))->first(), ]; $this->channelConnectionManager->shouldReceive('all') @@ -90,11 +90,11 @@ ->with($this->connection, []); $this->channelConnectionManager->shouldReceive('all') - ->andReturn($connections = connections(3)); + ->andReturn($connections = factory(3)); $channel->subscribe($this->connection, validAuth($this->connection->id(), 'presence-cache-test-channel')); - collect($connections)->each(fn ($connection) => $connection->assertSent([ + collect($connections)->each(fn ($connection) => $connection->assertReceived([ 'event' => 'pusher_internal:member_added', 'data' => [], 'channel' => 'presence-cache-test-channel', @@ -110,7 +110,7 @@ ->with($this->connection, ['name' => 'Joe']); $this->channelConnectionManager->shouldReceive('all') - ->andReturn($connections = connections(3)); + ->andReturn($connections = factory(3)); $channel->subscribe( $this->connection, @@ -122,7 +122,7 @@ $data ); - collect($connections)->each(fn ($connection) => $connection->assertSent([ + collect($connections)->each(fn ($connection) => $connection->assertReceived([ 'event' => 'pusher_internal:member_added', 'data' => ['name' => 'Joe'], 'channel' => 'presence-cache-test-channel', @@ -147,7 +147,7 @@ ->andReturn(new ChannelConnection($this->connection, ['user_info' => ['name' => 'Joe'], 'user_id' => 1])); $this->channelConnectionManager->shouldReceive('all') - ->andReturn($connections = connections(3)); + ->andReturn($connections = factory(3)); $this->channelConnectionManager->shouldReceive('remove') ->once() @@ -155,7 +155,7 @@ $channel->unsubscribe($this->connection); - collect($connections)->each(fn ($connection) => $connection->assertSent([ + collect($connections)->each(fn ($connection) => $connection->assertReceived([ 'event' => 'pusher_internal:member_removed', 'data' => ['user_id' => 1], 'channel' => 'presence-cache-test-channel', @@ -171,7 +171,7 @@ $channel->subscribe($this->connection, validAuth($this->connection->id(), 'presence-cache-test-channel')); - $this->connection->assertNothingSent(); + $this->connection->assertNothingReceived(); }); it('stores last triggered event', function () { diff --git a/tests/Unit/Channels/PresenceChannelTest.php b/tests/Unit/Channels/PresenceChannelTest.php index c2cf7e10..068cd248 100644 --- a/tests/Unit/Channels/PresenceChannelTest.php +++ b/tests/Unit/Channels/PresenceChannelTest.php @@ -4,10 +4,10 @@ use Laravel\Reverb\Contracts\ChannelConnectionManager; use Laravel\Reverb\Exceptions\ConnectionUnauthorized; use Laravel\Reverb\Servers\Reverb\ChannelConnection; -use Laravel\Reverb\Tests\Connection; +use Laravel\Reverb\Tests\FakeConnection; beforeEach(function () { - $this->connection = new Connection(); + $this->connection = new FakeConnection(); $this->channelConnectionManager = Mockery::spy(ChannelConnectionManager::class); $this->channelConnectionManager->shouldReceive('for') ->andReturn($this->channelConnectionManager); @@ -43,11 +43,11 @@ $this->channelConnectionManager->shouldReceive('all') ->once() - ->andReturn($connections = connections(3)); + ->andReturn($connections = factory(3)); $channel->broadcast(['foo' => 'bar']); - collect($connections)->each(fn ($connection) => $connection->assertSent(['foo' => 'bar'])); + collect($connections)->each(fn ($connection) => $connection->assertReceived(['foo' => 'bar'])); }); it('fails to subscribe if the signature is invalid', function () { @@ -62,8 +62,8 @@ $channel = new PresenceChannel('presence-test-channel'); $connections = [ - connections(data: ['user_info' => ['name' => 'Joe'], 'user_id' => 1])[0], - connections(data: ['user_info' => ['name' => 'Joe'], 'user_id' => 2])[0], + collect(factory(data: ['user_info' => ['name' => 'Joe'], 'user_id' => 1]))->first(), + collect(factory(data: ['user_info' => ['name' => 'Joe'], 'user_id' => 2]))->first(), ]; $this->channelConnectionManager->shouldReceive('all') @@ -90,11 +90,11 @@ ->with($this->connection, []); $this->channelConnectionManager->shouldReceive('all') - ->andReturn($connections = connections(3)); + ->andReturn($connections = factory(3)); $channel->subscribe($this->connection, validAuth($this->connection->id(), 'presence-test-channel')); - collect($connections)->each(fn ($connection) => $connection->assertSent([ + collect($connections)->each(fn ($connection) => $connection->assertReceived([ 'event' => 'pusher_internal:member_added', 'data' => [], 'channel' => 'presence-test-channel', @@ -110,7 +110,7 @@ ->with($this->connection, ['name' => 'Joe']); $this->channelConnectionManager->shouldReceive('all') - ->andReturn($connections = connections(3)); + ->andReturn($connections = factory(3)); $channel->subscribe( $this->connection, @@ -122,7 +122,7 @@ $data ); - collect($connections)->each(fn ($connection) => $connection->assertSent([ + collect($connections)->each(fn ($connection) => $connection->assertReceived([ 'event' => 'pusher_internal:member_added', 'data' => ['name' => 'Joe'], 'channel' => 'presence-test-channel', @@ -147,7 +147,7 @@ ->andReturn(new ChannelConnection($this->connection, ['user_info' => ['name' => 'Joe'], 'user_id' => 1])); $this->channelConnectionManager->shouldReceive('all') - ->andReturn($connections = connections(3)); + ->andReturn($connections = factory(3)); $this->channelConnectionManager->shouldReceive('remove') ->once() @@ -155,7 +155,7 @@ $channel->unsubscribe($this->connection); - collect($connections)->each(fn ($connection) => $connection->assertSent([ + collect($connections)->each(fn ($connection) => $connection->assertReceived([ 'event' => 'pusher_internal:member_removed', 'data' => ['user_id' => 1], 'channel' => 'presence-test-channel', diff --git a/tests/Unit/Channels/PrivateCacheChannelTest.php b/tests/Unit/Channels/PrivateCacheChannelTest.php index ee491c93..6dc26147 100644 --- a/tests/Unit/Channels/PrivateCacheChannelTest.php +++ b/tests/Unit/Channels/PrivateCacheChannelTest.php @@ -3,10 +3,10 @@ use Laravel\Reverb\Channels\PrivateCacheChannel; use Laravel\Reverb\Contracts\ChannelConnectionManager; use Laravel\Reverb\Exceptions\ConnectionUnauthorized; -use Laravel\Reverb\Tests\Connection; +use Laravel\Reverb\Tests\FakeConnection; beforeEach(function () { - $this->connection = new Connection(); + $this->connection = new FakeConnection(); $this->channelConnectionManager = Mockery::spy(ChannelConnectionManager::class); $this->channelConnectionManager->shouldReceive('for') ->andReturn($this->channelConnectionManager); @@ -40,11 +40,11 @@ $this->channelConnectionManager->shouldReceive('all') ->once() - ->andReturn($connections = connections(3)); + ->andReturn($connections = factory(3)); $channel->broadcast(['foo' => 'bar']); - collect($connections)->each(fn ($connection) => $connection->assertSent(['foo' => 'bar'])); + collect($connections)->each(fn ($connection) => $connection->assertReceived(['foo' => 'bar'])); }); it('fails to subscribe if the signature is invalid', function () { @@ -64,7 +64,7 @@ $channel->subscribe($this->connection, validAuth($this->connection->id(), 'private-cache-test-channel')); - $this->connection->assertNothingSent(); + $this->connection->assertNothingReceived(); }); it('stores last triggered event', function () { diff --git a/tests/Unit/Channels/PrivateChannelTest.php b/tests/Unit/Channels/PrivateChannelTest.php index 5720ab9b..610b5b8a 100644 --- a/tests/Unit/Channels/PrivateChannelTest.php +++ b/tests/Unit/Channels/PrivateChannelTest.php @@ -3,10 +3,10 @@ use Laravel\Reverb\Channels\PrivateChannel; use Laravel\Reverb\Contracts\ChannelConnectionManager; use Laravel\Reverb\Exceptions\ConnectionUnauthorized; -use Laravel\Reverb\Tests\Connection; +use Laravel\Reverb\Tests\FakeConnection; beforeEach(function () { - $this->connection = new Connection(); + $this->connection = new FakeConnection(); $this->channelConnectionManager = Mockery::spy(ChannelConnectionManager::class); $this->channelConnectionManager->shouldReceive('for') ->andReturn($this->channelConnectionManager); @@ -40,11 +40,11 @@ $this->channelConnectionManager->shouldReceive('all') ->once() - ->andReturn($connections = connections(3)); + ->andReturn($connections = factory(3)); $channel->broadcast(['foo' => 'bar']); - collect($connections)->each(fn ($connection) => $connection->assertSent(['foo' => 'bar'])); + collect($connections)->each(fn ($connection) => $connection->assertReceived(['foo' => 'bar'])); }); it('fails to subscribe if the signature is invalid', function () { diff --git a/tests/Unit/ClientEventTest.php b/tests/Unit/ClientEventTest.php index d3c2b80f..9700b58f 100644 --- a/tests/Unit/ClientEventTest.php +++ b/tests/Unit/ClientEventTest.php @@ -3,10 +3,10 @@ use Laravel\Reverb\ClientEvent; use Laravel\Reverb\Contracts\ChannelConnectionManager; use Laravel\Reverb\Servers\Reverb\ChannelConnection; -use Laravel\Reverb\Tests\Connection; +use Laravel\Reverb\Tests\FakeConnection; beforeEach(function () { - $this->connection = new Connection; + $this->connection = new FakeConnection; $this->channelConnectionManager = Mockery::spy(ChannelConnectionManager::class); $this->channelConnectionManager->shouldReceive('for') ->andReturn($this->channelConnectionManager); @@ -16,7 +16,7 @@ it('can forward a client message', function () { $this->channelConnectionManager->shouldReceive('all') ->once() - ->andReturn($connections = connections()); + ->andReturn($connections = factory()); ClientEvent::handle( $this->connection, [ @@ -26,7 +26,7 @@ ] ); - $connections[0]->assertSent([ + collect($connections)->first()->assertReceived([ 'event' => 'client-test-message', 'channel' => 'test-channel', 'data' => ['foo' => 'bar'], @@ -46,7 +46,7 @@ ] ); - $this->connection->assertNothingSent(); + $this->connection->assertNothingReceived(); }); it('fails on unsupported message', function () { diff --git a/tests/Unit/Jobs/PingInactiveConnectionsTest.php b/tests/Unit/Jobs/PingInactiveConnectionsTest.php index 3f6f2c8f..e66e88ed 100644 --- a/tests/Unit/Jobs/PingInactiveConnectionsTest.php +++ b/tests/Unit/Jobs/PingInactiveConnectionsTest.php @@ -12,7 +12,7 @@ }); it('pings inactive connections', function () { - $connections = connections(5); + $connections = factory(5); $channel = ChannelBroker::create('test-channel'); $this->channelManager->shouldReceive('connections') @@ -27,7 +27,7 @@ (new PingInactiveConnections)->handle($this->channelManager); $connections->each(function ($connection) { - $connection->assertSent([ + $connection->assertReceived([ 'event' => 'pusher:ping', ]); $connection->assertHasBeenPinged(); diff --git a/tests/Unit/Jobs/PruneStaleConnectionsTest.php b/tests/Unit/Jobs/PruneStaleConnectionsTest.php index 22af7579..db03dd30 100644 --- a/tests/Unit/Jobs/PruneStaleConnectionsTest.php +++ b/tests/Unit/Jobs/PruneStaleConnectionsTest.php @@ -12,7 +12,7 @@ }); it('cleans up stale connections', function () { - $connections = connections(5); + $connections = factory(5); $channel = ChannelBroker::create('test-channel'); $this->channelManager->shouldReceive('connections') diff --git a/tests/Unit/Managers/ChannelManagerTest.php b/tests/Unit/Managers/ChannelManagerTest.php index 3d03baa3..8b7642a6 100644 --- a/tests/Unit/Managers/ChannelManagerTest.php +++ b/tests/Unit/Managers/ChannelManagerTest.php @@ -1,24 +1,24 @@ connection = new Connection; + $this->connection = new FakeConnection; $this->channelManager = $this->app->make(ChannelManager::class) ->for($this->connection->app()); $this->channel = $this->channelManager->find('test-channel-0'); }); it('can subscribe to a channel', function () { - collect(connections(5)) + collect(factory(5)) ->each(fn ($connection) => $this->channel->subscribe($connection->connection())); expect($this->channel->connections())->toHaveCount(5); }); it('can unsubscribe from a channel', function () { - $connections = collect(connections(5)) + $connections = collect(factory(5)) ->each(fn ($connection) => $this->channel->subscribe($connection->connection())); $this->channel->unsubscribe($connections->first()->connection()); @@ -39,7 +39,7 @@ }); it('can get all connections subscribed to a channel', function () { - $connections = collect(connections(5)) + $connections = collect(factory(5)) ->each(fn ($connection) => $this->channel->subscribe($connection->connection())); $connections->each(fn ($connection) => expect($connection->id()) @@ -59,7 +59,7 @@ }); it('can get the data for a connection subscribed to a channel', function () { - collect(connections(5))->each(fn ($connection) => $this->channel->subscribe( + collect(factory(5))->each(fn ($connection) => $this->channel->subscribe( $connection->connection(), data: json_encode(['name' => 'Joe']) )); @@ -70,7 +70,7 @@ }); it('can get all connections for all channels', function () { - $connections = connections(12); + $connections = factory(12); $channelOne = $this->channelManager->find('test-channel-0'); $channelTwo = $this->channelManager->find('test-channel-1'); diff --git a/tests/Unit/Pusher/EventTest.php b/tests/Unit/Pusher/EventTest.php index 61a95b5e..99e6e5ac 100644 --- a/tests/Unit/Pusher/EventTest.php +++ b/tests/Unit/Pusher/EventTest.php @@ -2,10 +2,10 @@ use Laravel\Reverb\Contracts\ChannelManager; use Laravel\Reverb\Pusher\Event as PusherEvent; -use Laravel\Reverb\Tests\Connection; +use Laravel\Reverb\Tests\FakeConnection; beforeEach(function () { - $this->connection = new Connection; + $this->connection = new FakeConnection; $this->pusher = new PusherEvent(app(ChannelManager::class)); }); @@ -15,7 +15,7 @@ 'pusher:connection_established' ); - $this->connection->assertSent([ + $this->connection->assertReceived([ 'event' => 'pusher:connection_established', 'data' => json_encode([ 'socket_id' => $this->connection->id(), @@ -31,7 +31,7 @@ ['channel' => 'test-channel'] ); - $this->connection->assertSent([ + $this->connection->assertReceived([ 'event' => 'pusher_internal:subscription_succeeded', 'channel' => 'test-channel', ]); @@ -44,7 +44,7 @@ ['channel' => 'test-channel'] ); - $this->connection->assertNothingSent(); + $this->connection->assertNothingReceived(); }); it('can respond to a ping', function () { @@ -53,7 +53,7 @@ 'pusher:ping', ); - $this->connection->assertSent([ + $this->connection->assertReceived([ 'event' => 'pusher:pong', ]); });