From ce652bbbcbb24d3031cfbb32fa182e4c0f467b66 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Sat, 22 Aug 2020 20:53:33 +0300 Subject: [PATCH 01/18] Mocked message expects array --- tests/Channels/ChannelReplicationTest.php | 16 ++++++------ tests/Channels/ChannelTest.php | 16 ++++++------ .../PresenceChannelReplicationTest.php | 16 ++++++------ tests/Channels/PresenceChannelTest.php | 24 ++++++++--------- .../PrivateChannelReplicationTest.php | 8 +++--- tests/Channels/PrivateChannelTest.php | 8 +++--- tests/ConnectionTest.php | 2 +- tests/Messages/PusherClientMessageTest.php | 8 +++--- tests/Mocks/Message.php | 26 ++++++++++++++++--- tests/TestCase.php | 8 +++--- 10 files changed, 75 insertions(+), 57 deletions(-) diff --git a/tests/Channels/ChannelReplicationTest.php b/tests/Channels/ChannelReplicationTest.php index 4480442..adf1e9a 100644 --- a/tests/Channels/ChannelReplicationTest.php +++ b/tests/Channels/ChannelReplicationTest.php @@ -22,12 +22,12 @@ class ChannelReplicationTest extends TestCase { $connection = $this->getWebSocketConnection(); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:subscribe', 'data' => [ 'channel' => 'basic-channel', ], - ])); + ]); $this->pusherServer->onOpen($connection); @@ -47,12 +47,12 @@ class ChannelReplicationTest extends TestCase $this->assertTrue($channel->hasConnections()); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:unsubscribe', 'data' => [ 'channel' => 'test-channel', ], - ])); + ]); $this->pusherServer->onMessage($connection, $message); @@ -67,7 +67,7 @@ class ChannelReplicationTest extends TestCase $connection = $this->getConnectedWebSocketConnection(['test-channel']); - $message = new Message('{"event": "client-test", "data": {}, "channel": "test-channel"}'); + $message = new Message(['event' => 'client-test', 'data' => [], 'channel' => 'test-channel']); $this->pusherServer->onMessage($connection, $message); @@ -84,7 +84,7 @@ class ChannelReplicationTest extends TestCase $connection = $this->getConnectedWebSocketConnection(['test-channel']); - $message = new Message('{"event": "client-test", "data": {}, "channel": "test-channel"}'); + $message = new Message(['event' => 'client-test', 'data' => [], 'channel' => 'test-channel']); $this->pusherServer->onMessage($connection, $message); @@ -147,9 +147,9 @@ class ChannelReplicationTest extends TestCase { $connection = $this->getConnectedWebSocketConnection(); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:ping', - ])); + ]); $this->pusherServer->onMessage($connection, $message); diff --git a/tests/Channels/ChannelTest.php b/tests/Channels/ChannelTest.php index a16a83d..333a38d 100644 --- a/tests/Channels/ChannelTest.php +++ b/tests/Channels/ChannelTest.php @@ -12,12 +12,12 @@ class ChannelTest extends TestCase { $connection = $this->getWebSocketConnection(); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:subscribe', 'data' => [ 'channel' => 'basic-channel', ], - ])); + ]); $this->pusherServer->onOpen($connection); @@ -37,12 +37,12 @@ class ChannelTest extends TestCase $this->assertTrue($channel->hasConnections()); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:unsubscribe', 'data' => [ 'channel' => 'test-channel', ], - ])); + ]); $this->pusherServer->onMessage($connection, $message); @@ -57,7 +57,7 @@ class ChannelTest extends TestCase $connection = $this->getConnectedWebSocketConnection(['test-channel']); - $message = new Message('{"event": "client-test", "data": {}, "channel": "test-channel"}'); + $message = new Message(['event' => 'client-test', 'data' => [], 'channel' => 'test-channel']); $this->pusherServer->onMessage($connection, $message); @@ -74,7 +74,7 @@ class ChannelTest extends TestCase $connection = $this->getConnectedWebSocketConnection(['test-channel']); - $message = new Message('{"event": "client-test", "data": {}, "channel": "test-channel"}'); + $message = new Message(['event' => 'client-test', 'data' => [], 'channel' => 'test-channel']); $this->pusherServer->onMessage($connection, $message); @@ -137,9 +137,9 @@ class ChannelTest extends TestCase { $connection = $this->getConnectedWebSocketConnection(); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:ping', - ])); + ]); $this->pusherServer->onMessage($connection, $message); diff --git a/tests/Channels/PresenceChannelReplicationTest.php b/tests/Channels/PresenceChannelReplicationTest.php index 822ef4e..4cbe2e0 100644 --- a/tests/Channels/PresenceChannelReplicationTest.php +++ b/tests/Channels/PresenceChannelReplicationTest.php @@ -33,14 +33,14 @@ class PresenceChannelReplicationTest extends TestCase $signature = "{$connection->socketId}:presence-channel:".json_encode($channelData); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:subscribe', 'data' => [ 'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret), 'channel' => 'presence-channel', 'channel_data' => json_encode($channelData), ], - ])); + ]); $this->pusherServer->onMessage($connection, $message); @@ -67,14 +67,14 @@ class PresenceChannelReplicationTest extends TestCase $signature = "{$connection->socketId}:presence-channel:".json_encode($channelData); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:subscribe', 'data' => [ 'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret), 'channel' => 'presence-channel', 'channel_data' => json_encode($channelData), ], - ])); + ]); $this->pusherServer->onMessage($connection, $message); @@ -89,13 +89,13 @@ class PresenceChannelReplicationTest extends TestCase $this->getPublishClient() ->resetAssertions(); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:unsubscribe', 'data' => [ 'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret), 'channel' => 'presence-channel', ], - ])); + ]); $this->pusherServer->onMessage($connection, $message); @@ -117,14 +117,14 @@ class PresenceChannelReplicationTest extends TestCase $signature = "{$connection->socketId}:presence-channel:".json_encode($channelData); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:subscribe', 'data' => [ 'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret), 'channel' => 'presence-channel', 'channel_data' => json_encode($channelData), ], - ])); + ]); $this->pusherServer->onMessage($connection, $message); diff --git a/tests/Channels/PresenceChannelTest.php b/tests/Channels/PresenceChannelTest.php index a72d94f..f6481af 100644 --- a/tests/Channels/PresenceChannelTest.php +++ b/tests/Channels/PresenceChannelTest.php @@ -15,13 +15,13 @@ class PresenceChannelTest extends TestCase $connection = $this->getWebSocketConnection(); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:subscribe', 'data' => [ 'auth' => 'invalid', 'channel' => 'presence-channel', ], - ])); + ]); $this->pusherServer->onOpen($connection); @@ -46,14 +46,14 @@ class PresenceChannelTest extends TestCase $signature = "{$connection->socketId}:presence-channel:".json_encode($channelData); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:subscribe', 'data' => [ 'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret), 'channel' => 'presence-channel', 'channel_data' => json_encode($channelData), ], - ])); + ]); $this->pusherServer->onMessage($connection, $message); @@ -77,14 +77,14 @@ class PresenceChannelTest extends TestCase $signature = "{$connection->socketId}:presence-channel:".json_encode($channelData); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:subscribe', 'data' => [ 'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret), 'channel' => 'presence-channel', 'channel_data' => json_encode($channelData), ], - ])); + ]); $this->pusherServer->onMessage($connection, $message); @@ -92,13 +92,13 @@ class PresenceChannelTest extends TestCase 'channel' => 'presence-channel', ]); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:unsubscribe', 'data' => [ 'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret), 'channel' => 'presence-channel', ], - ])); + ]); $this->pusherServer->onMessage($connection, $message); } @@ -118,14 +118,14 @@ class PresenceChannelTest extends TestCase $signature = "{$connection->socketId}:presence-channel:".json_encode($channelData); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:subscribe', 'data' => [ 'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret), 'channel' => 'presence-channel', 'channel_data' => json_encode($channelData), ], - ])); + ]); $this->pusherServer->onMessage($connection, $message); @@ -150,13 +150,13 @@ class PresenceChannelTest extends TestCase $signature = "{$connection->socketId}:presence-channel:".json_encode($channelData); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:unsubscribe', 'data' => [ 'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret), 'channel' => 'presence-channel', ], - ])); + ]); $this->pusherServer->onMessage($connection, $message); diff --git a/tests/Channels/PrivateChannelReplicationTest.php b/tests/Channels/PrivateChannelReplicationTest.php index cc4bab7..3a16412 100644 --- a/tests/Channels/PrivateChannelReplicationTest.php +++ b/tests/Channels/PrivateChannelReplicationTest.php @@ -25,13 +25,13 @@ class PrivateChannelReplicationTest extends TestCase $connection = $this->getWebSocketConnection(); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:subscribe', 'data' => [ 'auth' => 'invalid', 'channel' => 'private-channel', ], - ])); + ]); $this->pusherServer->onOpen($connection); @@ -49,13 +49,13 @@ class PrivateChannelReplicationTest extends TestCase $hashedAppSecret = hash_hmac('sha256', $signature, $connection->app->secret); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:subscribe', 'data' => [ 'auth' => "{$connection->app->key}:{$hashedAppSecret}", 'channel' => 'private-channel', ], - ])); + ]); $this->pusherServer->onMessage($connection, $message); diff --git a/tests/Channels/PrivateChannelTest.php b/tests/Channels/PrivateChannelTest.php index 6b8d9b6..91f48d0 100644 --- a/tests/Channels/PrivateChannelTest.php +++ b/tests/Channels/PrivateChannelTest.php @@ -15,13 +15,13 @@ class PrivateChannelTest extends TestCase $connection = $this->getWebSocketConnection(); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:subscribe', 'data' => [ 'auth' => 'invalid', 'channel' => 'private-channel', ], - ])); + ]); $this->pusherServer->onOpen($connection); @@ -39,13 +39,13 @@ class PrivateChannelTest extends TestCase $hashedAppSecret = hash_hmac('sha256', $signature, $connection->app->secret); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:subscribe', 'data' => [ 'auth' => "{$connection->app->key}:{$hashedAppSecret}", 'channel' => 'private-channel', ], - ])); + ]); $this->pusherServer->onMessage($connection, $message); diff --git a/tests/ConnectionTest.php b/tests/ConnectionTest.php index 0aba6ec..3e17566 100644 --- a/tests/ConnectionTest.php +++ b/tests/ConnectionTest.php @@ -58,7 +58,7 @@ class ConnectionTest extends TestCase { $connection = $this->getWebSocketConnection(); - $message = new Message('{"event": "pusher:ping"}'); + $message = new Message(['event' => 'pusher:ping']); $this->pusherServer->onOpen($connection); diff --git a/tests/Messages/PusherClientMessageTest.php b/tests/Messages/PusherClientMessageTest.php index a97aed7..fed8e98 100644 --- a/tests/Messages/PusherClientMessageTest.php +++ b/tests/Messages/PusherClientMessageTest.php @@ -12,13 +12,13 @@ class PusherClientMessageTest extends TestCase { $connection = $this->getConnectedWebSocketConnection(['test-channel']); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'client-test', 'channel' => 'test-channel', 'data' => [ 'client-event' => 'test', ], - ])); + ]); $this->pusherServer->onMessage($connection, $message); @@ -42,13 +42,13 @@ class PusherClientMessageTest extends TestCase $connection1 = $this->getConnectedWebSocketConnection(['test-channel']); $connection2 = $this->getConnectedWebSocketConnection(['test-channel']); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'client-test', 'channel' => 'test-channel', 'data' => [ 'client-event' => 'test', ], - ])); + ]); $this->pusherServer->onMessage($connection1, $message); diff --git a/tests/Mocks/Message.php b/tests/Mocks/Message.php index 3b0706c..4a9be32 100644 --- a/tests/Mocks/Message.php +++ b/tests/Mocks/Message.php @@ -2,17 +2,35 @@ namespace BeyondCode\LaravelWebSockets\Tests\Mocks; -class Message extends \Ratchet\RFC6455\Messaging\Message +use Ratchet\RFC6455\Messaging\Message as BaseMessage; + +class Message extends BaseMessage { + /** + * The payload as array. + * + * @var array + */ protected $payload; - public function __construct($payload) + /** + * Create a new message instance. + * + * @param array $payload + * @return void + */ + public function __construct(array $payload) { $this->payload = $payload; } - public function getPayload() + /** + * Get the payload as json-encoded string. + * + * @return string + */ + public function getPayload(): string { - return $this->payload; + return json_encode($this->payload); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index b0c7b7a..81bd261 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -160,12 +160,12 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase $this->pusherServer->onOpen($connection); foreach ($channelsToJoin as $channel) { - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:subscribe', 'data' => [ 'channel' => $channel, ], - ])); + ]); $this->pusherServer->onMessage($connection, $message); } @@ -194,14 +194,14 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase $signature = "{$connection->socketId}:{$channel}:".json_encode($channelData); - $message = new Message(json_encode([ + $message = new Message([ 'event' => 'pusher:subscribe', 'data' => [ 'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret), 'channel' => $channel, 'channel_data' => json_encode($channelData), ], - ])); + ]); $this->pusherServer->onMessage($connection, $message); From 92870c088a2a9525b58b65810d12cc2a114e0c83 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Sat, 22 Aug 2020 21:54:48 +0300 Subject: [PATCH 02/18] wip --- src/Statistics/DnsResolver.php | 59 --------------------- tests/Commands/StartWebSocketServerTest.php | 2 +- tests/PubSub/RedisDriverTest.php | 34 ++++++++++++ 3 files changed, 35 insertions(+), 60 deletions(-) delete mode 100644 src/Statistics/DnsResolver.php diff --git a/src/Statistics/DnsResolver.php b/src/Statistics/DnsResolver.php deleted file mode 100644 index 57cfdcb..0000000 --- a/src/Statistics/DnsResolver.php +++ /dev/null @@ -1,59 +0,0 @@ -resolveInternal($domain); - } - - /** - * Resolve all domains. - * - * @param string $domain - * @param string $type - * @return FulfilledPromise - */ - public function resolveAll($domain, $type) - { - return $this->resolveInternal($domain, $type); - } - - /** - * Resolve the internal domain. - * - * @param string $domain - * @param string $type - * @return FulfilledPromise - */ - private function resolveInternal($domain, $type = null) - { - return new FulfilledPromise($this->internalIp); - } - - /** - * {@inheritdoc} - */ - public function __toString() - { - return $this->internalIp; - } -} diff --git a/tests/Commands/StartWebSocketServerTest.php b/tests/Commands/StartWebSocketServerTest.php index 637c1c8..00d0d32 100644 --- a/tests/Commands/StartWebSocketServerTest.php +++ b/tests/Commands/StartWebSocketServerTest.php @@ -9,7 +9,7 @@ class StartWebSocketServerTest extends TestCase /** @test */ public function does_not_fail_if_building_up() { - $this->artisan('websockets:serve', ['--test' => true]); + $this->artisan('websockets:serve', ['--test' => true, '--debug' => true]); $this->assertTrue(true); } diff --git a/tests/PubSub/RedisDriverTest.php b/tests/PubSub/RedisDriverTest.php index e6585a1..e6caf01 100644 --- a/tests/PubSub/RedisDriverTest.php +++ b/tests/PubSub/RedisDriverTest.php @@ -2,7 +2,10 @@ namespace BeyondCode\LaravelWebSockets\Tests\PubSub; +use BeyondCode\LaravelWebSockets\PubSub\Drivers\RedisClient; use BeyondCode\LaravelWebSockets\Tests\TestCase; +use BeyondCode\LaravelWebSockets\Tests\Mocks\RedisFactory; +use React\EventLoop\Factory as LoopFactory; class RedisDriverTest extends TestCase { @@ -46,4 +49,35 @@ class RedisDriverTest extends TestCase '1234:test-channel', $payload, ]); } + + /** @test */ + public function redis_listener_responds_properly_on_payload_by_direct_call() + { + $connection = $this->getConnectedWebSocketConnection(['test-channel']); + + $this->pusherServer->onOpen($connection); + + $channelData = [ + 'user_id' => 1, + 'user_info' => [ + 'name' => 'Marcel', + ], + ]; + + $payload = json_encode([ + 'appId' => '1234', + 'event' => 'test', + 'data' => $channelData, + 'socket' => $connection->socketId, + ]); + + $client = (new RedisClient)->boot( + LoopFactory::create(), RedisFactory::class + ); + + $client->onMessage('1234:test-channel', $payload); + + $client->getSubscribeClient() + ->assertEventDispatched('message'); + } } From 94d13b487782c86dd2a90f1b0c704714be845386 Mon Sep 17 00:00:00 2001 From: rennokki Date: Sat, 22 Aug 2020 21:55:09 +0300 Subject: [PATCH 03/18] Apply fixes from StyleCI (#481) --- tests/PubSub/RedisDriverTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PubSub/RedisDriverTest.php b/tests/PubSub/RedisDriverTest.php index e6caf01..0228fe8 100644 --- a/tests/PubSub/RedisDriverTest.php +++ b/tests/PubSub/RedisDriverTest.php @@ -3,8 +3,8 @@ namespace BeyondCode\LaravelWebSockets\Tests\PubSub; use BeyondCode\LaravelWebSockets\PubSub\Drivers\RedisClient; -use BeyondCode\LaravelWebSockets\Tests\TestCase; use BeyondCode\LaravelWebSockets\Tests\Mocks\RedisFactory; +use BeyondCode\LaravelWebSockets\Tests\TestCase; use React\EventLoop\Factory as LoopFactory; class RedisDriverTest extends TestCase From b00e19e7af7c48302404467e67f48277e25f7043 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Sat, 22 Aug 2020 22:22:45 +0300 Subject: [PATCH 04/18] wip --- src/PubSub/Drivers/RedisClient.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PubSub/Drivers/RedisClient.php b/src/PubSub/Drivers/RedisClient.php index 255d826..14b9357 100644 --- a/src/PubSub/Drivers/RedisClient.php +++ b/src/PubSub/Drivers/RedisClient.php @@ -265,7 +265,7 @@ class RedisClient extends LocalClient * @param string $payload * @return void */ - protected function onMessage(string $redisChannel, string $payload) + public function onMessage(string $redisChannel, string $payload) { $payload = json_decode($payload); From 714cc5b22def3da5f1ef8da23b2c89cd0af42d87 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Sun, 23 Aug 2020 13:38:56 +0300 Subject: [PATCH 05/18] The get() method accepts Request or nuill --- src/Statistics/Drivers/DatabaseDriver.php | 4 ++-- src/Statistics/Drivers/StatisticsDriver.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Statistics/Drivers/DatabaseDriver.php b/src/Statistics/Drivers/DatabaseDriver.php index cb5e353..034e4d4 100644 --- a/src/Statistics/Drivers/DatabaseDriver.php +++ b/src/Statistics/Drivers/DatabaseDriver.php @@ -92,10 +92,10 @@ class DatabaseDriver implements StatisticsDriver * Get the records to show to the dashboard. * * @param mixed $appId - * @param \Illuminate\Http\Request $request + * @param \Illuminate\Http\Request|null $request * @return array */ - public static function get($appId, Request $request): array + public static function get($appId, ?Request $request): array { $class = config('websockets.statistics.database.model'); diff --git a/src/Statistics/Drivers/StatisticsDriver.php b/src/Statistics/Drivers/StatisticsDriver.php index 9b9cfb0..fd77b2c 100644 --- a/src/Statistics/Drivers/StatisticsDriver.php +++ b/src/Statistics/Drivers/StatisticsDriver.php @@ -61,10 +61,10 @@ interface StatisticsDriver * Get the records to show to the dashboard. * * @param mixed $appId - * @param \Illuminate\Http\Request $request + * @param \Illuminate\Http\Request|null $request * @return void */ - public static function get($appId, Request $request); + public static function get($appId, ?Request $request); /** * Delete statistics from the store, From 108a717c0af82f5f04e8164a13211b3c36dcc401 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Sun, 23 Aug 2020 13:39:59 +0300 Subject: [PATCH 06/18] Fixed inconsistency by not passing $appId to all methods --- .../Logger/MemoryStatisticsLogger.php | 19 +++++++++---------- .../Logger/NullStatisticsLogger.php | 13 ++++++------- src/Statistics/Logger/StatisticsLogger.php | 14 ++++++-------- src/WebSockets/WebSocketHandler.php | 6 +++--- 4 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/Statistics/Logger/MemoryStatisticsLogger.php b/src/Statistics/Logger/MemoryStatisticsLogger.php index fe0ac82..43c9db4 100644 --- a/src/Statistics/Logger/MemoryStatisticsLogger.php +++ b/src/Statistics/Logger/MemoryStatisticsLogger.php @@ -6,7 +6,6 @@ use BeyondCode\LaravelWebSockets\Apps\App; use BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver; use BeyondCode\LaravelWebSockets\Statistics\Statistic; use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager; -use Ratchet\ConnectionInterface; class MemoryStatisticsLogger implements StatisticsLogger { @@ -47,12 +46,12 @@ class MemoryStatisticsLogger implements StatisticsLogger /** * Handle the incoming websocket message. * - * @param \Ratchet\ConnectionInterface $connection + * @param mixed $appId * @return void */ - public function webSocketMessage(ConnectionInterface $connection) + public function webSocketMessage($appId) { - $this->findOrMakeStatisticForAppId($connection->app->id) + $this->findOrMakeStatisticForAppId($appId) ->webSocketMessage(); } @@ -71,24 +70,24 @@ class MemoryStatisticsLogger implements StatisticsLogger /** * Handle the new conection. * - * @param \Ratchet\ConnectionInterface $connection + * @param mixed $appId * @return void */ - public function connection(ConnectionInterface $connection) + public function connection($appId) { - $this->findOrMakeStatisticForAppId($connection->app->id) + $this->findOrMakeStatisticForAppId($appId) ->connection(); } /** * Handle disconnections. * - * @param \Ratchet\ConnectionInterface $connection + * @param mixed $appId * @return void */ - public function disconnection(ConnectionInterface $connection) + public function disconnection($appId) { - $this->findOrMakeStatisticForAppId($connection->app->id) + $this->findOrMakeStatisticForAppId($appId) ->disconnection(); } diff --git a/src/Statistics/Logger/NullStatisticsLogger.php b/src/Statistics/Logger/NullStatisticsLogger.php index 94e3547..1120c2e 100644 --- a/src/Statistics/Logger/NullStatisticsLogger.php +++ b/src/Statistics/Logger/NullStatisticsLogger.php @@ -4,7 +4,6 @@ namespace BeyondCode\LaravelWebSockets\Statistics\Logger; use BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver; use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager; -use Ratchet\ConnectionInterface; class NullStatisticsLogger implements StatisticsLogger { @@ -38,10 +37,10 @@ class NullStatisticsLogger implements StatisticsLogger /** * Handle the incoming websocket message. * - * @param \Ratchet\ConnectionInterface $connection + * @param mixed $appId * @return void */ - public function webSocketMessage(ConnectionInterface $connection) + public function webSocketMessage($appId) { // } @@ -60,10 +59,10 @@ class NullStatisticsLogger implements StatisticsLogger /** * Handle the new conection. * - * @param \Ratchet\ConnectionInterface $connection + * @param mixed $appId * @return void */ - public function connection(ConnectionInterface $connection) + public function connection($appId) { // } @@ -71,10 +70,10 @@ class NullStatisticsLogger implements StatisticsLogger /** * Handle disconnections. * - * @param \Ratchet\ConnectionInterface $connection + * @param mixed $appId * @return void */ - public function disconnection(ConnectionInterface $connection) + public function disconnection($appId) { // } diff --git a/src/Statistics/Logger/StatisticsLogger.php b/src/Statistics/Logger/StatisticsLogger.php index 84b09db..6f6fe0c 100644 --- a/src/Statistics/Logger/StatisticsLogger.php +++ b/src/Statistics/Logger/StatisticsLogger.php @@ -2,17 +2,15 @@ namespace BeyondCode\LaravelWebSockets\Statistics\Logger; -use Ratchet\ConnectionInterface; - interface StatisticsLogger { /** * Handle the incoming websocket message. * - * @param \Ratchet\ConnectionInterface $connection + * @param mixed $appId * @return void */ - public function webSocketMessage(ConnectionInterface $connection); + public function webSocketMessage($appId); /** * Handle the incoming API message. @@ -25,18 +23,18 @@ interface StatisticsLogger /** * Handle the new conection. * - * @param \Ratchet\ConnectionInterface $connection + * @param mixed $appId * @return void */ - public function connection(ConnectionInterface $connection); + public function connection($appId); /** * Handle disconnections. * - * @param \Ratchet\ConnectionInterface $connection + * @param mixed $appId * @return void */ - public function disconnection(ConnectionInterface $connection); + public function disconnection($appId); /** * Save all the stored statistics. diff --git a/src/WebSockets/WebSocketHandler.php b/src/WebSockets/WebSocketHandler.php index 7a2537e..0f00342 100644 --- a/src/WebSockets/WebSocketHandler.php +++ b/src/WebSockets/WebSocketHandler.php @@ -65,7 +65,7 @@ class WebSocketHandler implements MessageComponentInterface $message->respond(); - StatisticsLogger::webSocketMessage($connection); + StatisticsLogger::webSocketMessage($connection->app->id); } /** @@ -82,7 +82,7 @@ class WebSocketHandler implements MessageComponentInterface 'socketId' => $connection->socketId, ]); - StatisticsLogger::disconnection($connection); + StatisticsLogger::disconnection($connection->app->id); } /** @@ -200,7 +200,7 @@ class WebSocketHandler implements MessageComponentInterface 'socketId' => $connection->socketId, ]); - StatisticsLogger::connection($connection); + StatisticsLogger::connection($connection->app->id); return $this; } From 2a6d91aaf75e05b2849b71670f4e0f5d4c336a5a Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Sun, 23 Aug 2020 13:40:18 +0300 Subject: [PATCH 07/18] Added tests for statistics loggers drivers --- .../Logger/MemoryStatisticsLogger.php | 10 ++++ .../Logger/StatisticsLoggerTest.php | 49 +++++++++++++++++++ tests/TestCase.php | 13 ++++- 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/Statistics/Logger/MemoryStatisticsLogger.php b/src/Statistics/Logger/MemoryStatisticsLogger.php index 43c9db4..a0bee8e 100644 --- a/src/Statistics/Logger/MemoryStatisticsLogger.php +++ b/src/Statistics/Logger/MemoryStatisticsLogger.php @@ -125,4 +125,14 @@ class MemoryStatisticsLogger implements StatisticsLogger return $this->statistics[$appId]; } + + /** + * Get the saved statistics. + * + * @return array + */ + public function getStatistics(): array + { + return $this->statistics; + } } diff --git a/tests/Statistics/Logger/StatisticsLoggerTest.php b/tests/Statistics/Logger/StatisticsLoggerTest.php index 49abd19..c7f2365 100644 --- a/tests/Statistics/Logger/StatisticsLoggerTest.php +++ b/tests/Statistics/Logger/StatisticsLoggerTest.php @@ -3,6 +3,9 @@ namespace BeyondCode\LaravelWebSockets\Tests\Statistics\Controllers; use BeyondCode\LaravelWebSockets\Facades\StatisticsLogger; +use BeyondCode\LaravelWebSockets\Statistics\Logger\MemoryStatisticsLogger; +use BeyondCode\LaravelWebSockets\Statistics\Logger\NullStatisticsLogger; +use BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry; use BeyondCode\LaravelWebSockets\Tests\TestCase; class StatisticsLoggerTest extends TestCase @@ -43,4 +46,50 @@ class StatisticsLoggerTest extends TestCase $this->assertEquals(1, StatisticsLogger::getForAppId(1234)['peak_connection_count']); } + + /** @test */ + public function it_counts_connections_with_memory_logger() + { + $connection = $this->getConnectedWebSocketConnection(['channel-1']); + + $logger = new MemoryStatisticsLogger( + $this->channelManager, + $this->statisticsDriver + ); + + $logger->webSocketMessage($connection->app->id); + $logger->apiMessage($connection->app->id); + $logger->connection($connection->app->id); + $logger->disconnection($connection->app->id); + + $logger->save(); + + $this->assertCount(1, WebSocketsStatisticsEntry::all()); + + $entry = WebSocketsStatisticsEntry::first(); + + $this->assertEquals(1, $entry->peak_connection_count); + $this->assertEquals(1, $entry->websocket_message_count); + $this->assertEquals(1, $entry->api_message_count); + } + + /** @test */ + public function it_counts_connections_with_null_logger() + { + $connection = $this->getConnectedWebSocketConnection(['channel-1']); + + $logger = new NullStatisticsLogger( + $this->channelManager, + $this->statisticsDriver + ); + + $logger->webSocketMessage($connection->app->id); + $logger->apiMessage($connection->app->id); + $logger->connection($connection->app->id); + $logger->disconnection($connection->app->id); + + $logger->save(); + + $this->assertCount(0, WebSocketsStatisticsEntry::all()); + } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 81bd261..2062a83 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -31,6 +31,13 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase */ protected $channelManager; + /** + * The used statistics driver. + * + * @var \BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver + */ + protected $statisticsDriver; + /** * {@inheritdoc} */ @@ -38,9 +45,11 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase { parent::setUp(); - $this->pusherServer = app(config('websockets.handlers.websocket')); + $this->pusherServer = $this->app->make(config('websockets.handlers.websocket')); - $this->channelManager = app(ChannelManager::class); + $this->channelManager = $this->app->make(ChannelManager::class); + + $this->statisticsDriver = $this->app->make(StatisticsDriver::class); StatisticsLogger::swap(new FakeStatisticsLogger( $this->channelManager, From 3fcfe1bb391a96a09910bd444252526d35604b19 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Sun, 23 Aug 2020 13:44:09 +0300 Subject: [PATCH 08/18] Added test for app_id --- tests/Commands/CleanStatisticsTest.php | 30 ++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/Commands/CleanStatisticsTest.php b/tests/Commands/CleanStatisticsTest.php index 91f7790..9e26a6d 100644 --- a/tests/Commands/CleanStatisticsTest.php +++ b/tests/Commands/CleanStatisticsTest.php @@ -42,4 +42,34 @@ class CleanStatisticsTest extends TestCase $this->assertCount(0, WebSocketsStatisticsEntry::where('created_at', '<', $cutOffDate)->get()); } + + /** @test */ + public function it_can_clean_the_statistics_for_app_id_only() + { + Collection::times(60)->each(function (int $index) { + WebSocketsStatisticsEntry::create([ + 'app_id' => 'app_id', + 'peak_connection_count' => 1, + 'websocket_message_count' => 2, + 'api_message_count' => 3, + 'created_at' => Carbon::now()->subDays($index)->startOfDay(), + ]); + }); + + Collection::times(60)->each(function (int $index) { + WebSocketsStatisticsEntry::create([ + 'app_id' => 'app_id2', + 'peak_connection_count' => 1, + 'websocket_message_count' => 2, + 'api_message_count' => 3, + 'created_at' => Carbon::now()->subDays($index)->startOfDay(), + ]); + }); + + $this->assertCount(120, WebSocketsStatisticsEntry::all()); + + Artisan::call('websockets:clean', ['appId' => 'app_id']); + + $this->assertCount(91, WebSocketsStatisticsEntry::all()); + } } From 96c0eb98d69d582c73c4c589232ea8e66c4526d8 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Sun, 23 Aug 2020 13:54:22 +0300 Subject: [PATCH 09/18] Addded .codecov.yml --- .codecov.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .codecov.yml diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 0000000..33dbc6b --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,18 @@ +codecov: + notify: + require_ci_to_pass: yes + +coverage: + precision: 2 + round: down + range: "70...100" + +status: + project: yes + patch: yes + changes: no + +comment: + layout: "reach, diff, flags, files, footer" + behavior: default + require_changes: no From b46cfadaa218a99bb7fe28da77c9bdce85581eb8 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Sun, 23 Aug 2020 18:34:29 +0300 Subject: [PATCH 10/18] wip --- .github/workflows/run-tests.yml | 2 +- composer.json | 2 +- .../Broadcasters/RedisPusherBroadcaster.php | 15 ++++++----- src/WebSocketsServiceProvider.php | 18 +++++++------ tests/Dashboard/DashboardTest.php | 25 +++++++++++++++++++ tests/TestCase.php | 5 +++- 6 files changed, 48 insertions(+), 19 deletions(-) create mode 100644 tests/Dashboard/DashboardTest.php diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index aaed621..c3e4762 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -45,7 +45,7 @@ jobs: - name: Install dependencies run: | - composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update + composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench-browser-kit:${{ matrix.testbench }}" --no-interaction --no-update composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest - name: Execute tests with Local driver diff --git a/composer.json b/composer.json index 276de5f..f34b96d 100644 --- a/composer.json +++ b/composer.json @@ -42,7 +42,7 @@ }, "require-dev": { "mockery/mockery": "^1.3", - "orchestra/testbench": "3.8.*|^4.0|^5.0", + "orchestra/testbench-browser-kit": "^4.0|^5.0", "phpunit/phpunit": "^8.0|^9.0" }, "autoload": { diff --git a/src/PubSub/Broadcasters/RedisPusherBroadcaster.php b/src/PubSub/Broadcasters/RedisPusherBroadcaster.php index c59f065..1c79661 100644 --- a/src/PubSub/Broadcasters/RedisPusherBroadcaster.php +++ b/src/PubSub/Broadcasters/RedisPusherBroadcaster.php @@ -45,10 +45,10 @@ class RedisPusherBroadcaster extends Broadcaster /** * Create a new broadcaster instance. * - * @param Pusher $pusher - * @param $appId - * @param \Illuminate\Contracts\Redis\Factory $redis - * @param string|null $connection + * @param Pusher $pusher + * @param mixed $appId + * @param \Illuminate\Contracts\Redis\Factory $redis + * @param string|null $connection */ public function __construct(Pusher $pusher, $appId, Redis $redis, $connection = null) { @@ -63,7 +63,6 @@ class RedisPusherBroadcaster extends Broadcaster * * @param \Illuminate\Http\Request $request * @return mixed - * * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException */ public function auth($request) @@ -83,8 +82,8 @@ class RedisPusherBroadcaster extends Broadcaster /** * Return the valid authentication response. * - * @param \Illuminate\Http\Request $request - * @param mixed $result + * @param \Illuminate\Http\Request $request + * @param mixed $result * @return mixed * @throws \Pusher\PusherException */ @@ -144,7 +143,7 @@ class RedisPusherBroadcaster extends Broadcaster ]); foreach ($this->formatChannels($channels) as $channel) { - $connection->publish("{$this->appId}:$channel", $payload); + $connection->publish("{$this->appId}:{$channel}", $payload); } } } diff --git a/src/WebSocketsServiceProvider.php b/src/WebSocketsServiceProvider.php index 09db778..f03a504 100644 --- a/src/WebSocketsServiceProvider.php +++ b/src/WebSocketsServiceProvider.php @@ -118,13 +118,15 @@ class WebSocketsServiceProvider extends ServiceProvider */ protected function registerDashboardRoutes() { - Route::prefix(config('websockets.dashboard.path'))->group(function () { - Route::middleware(config('websockets.dashboard.middleware', [AuthorizeDashboard::class]))->group(function () { - Route::get('/', ShowDashboard::class); - Route::get('/api/{appId}/statistics', [DashboardApiController::class, 'getStatistics']); - Route::post('auth', AuthenticateDashboard::class); - Route::post('event', SendMessage::class); - }); + Route::group([ + 'prefix' => config('websockets.dashboard.path'), + 'as' => 'laravel-websockets.', + 'middleware' => config('websockets.dashboard.middleware', [AuthorizeDashboard::class]), + ], function () { + Route::get('/', ShowDashboard::class)->name('dashboard'); + Route::get('/api/{appId}/statistics', [DashboardApiController::class, 'getStatistics'])->name('statistics'); + Route::post('auth', AuthenticateDashboard::class)->name('auth'); + Route::post('event', SendMessage::class)->name('send'); }); return $this; @@ -138,7 +140,7 @@ class WebSocketsServiceProvider extends ServiceProvider protected function registerDashboardGate() { Gate::define('viewWebSocketsDashboard', function ($user = null) { - return $this->app->environment('local'); + return $this->app->environment(['local', 'testing']); }); return $this; diff --git a/tests/Dashboard/DashboardTest.php b/tests/Dashboard/DashboardTest.php new file mode 100644 index 0000000..ce098ca --- /dev/null +++ b/tests/Dashboard/DashboardTest.php @@ -0,0 +1,25 @@ + 'production']); + + $this->get(route('laravel-websockets.dashboard')) + ->assertResponseStatus(403); + } + + /** @test */ + public function can_see_dashboard() + { + $this->get(route('laravel-websockets.dashboard')) + ->assertResponseOk() + ->see('WebSockets Dashboard'); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index 2062a83..c1c7f0c 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -12,10 +12,11 @@ use BeyondCode\LaravelWebSockets\Tests\Mocks\Message; use BeyondCode\LaravelWebSockets\Tests\Statistics\Logger\FakeStatisticsLogger; use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager; use GuzzleHttp\Psr7\Request; +use Orchestra\Testbench\BrowserKit\TestCase as BaseTestCase; use Ratchet\ConnectionInterface; use React\EventLoop\Factory as LoopFactory; -abstract class TestCase extends \Orchestra\Testbench\TestCase +abstract class TestCase extends BaseTestCase { /** * A test Pusher server. @@ -76,6 +77,8 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase */ protected function getEnvironmentSetUp($app) { + $app['config']->set('app.key', 'wslxrEFGWY6GfGhvN9L3wH3KSRJQQpBD'); + $app['config']->set('websockets.apps', [ [ 'name' => 'Test App', From f62ac8fd56cce4006c3a233324f8552ddc93c9f1 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Sun, 23 Aug 2020 19:12:22 +0300 Subject: [PATCH 11/18] wip --- .gitignore | 1 + src/Contracts/PushesToPusher.php | 32 ++++++ .../Controllers/AuthenticateDashboard.php | 14 +-- .../Http/Controllers/SendMessage.php | 37 +++---- src/WebSocketsServiceProvider.php | 6 +- tests/Dashboard/AuthTest.php | 100 ++++++++++++++++++ tests/Dashboard/DashboardTest.php | 6 +- tests/Models/User.php | 16 +++ tests/TestCase.php | 27 +++++ tests/TestServiceProvider.php | 31 ++++++ tests/database/factories/UserFactory.php | 22 ++++ 11 files changed, 257 insertions(+), 35 deletions(-) create mode 100644 src/Contracts/PushesToPusher.php create mode 100644 tests/Dashboard/AuthTest.php create mode 100644 tests/Models/User.php create mode 100644 tests/TestServiceProvider.php create mode 100644 tests/database/factories/UserFactory.php diff --git a/.gitignore b/.gitignore index f423e5b..a4753bd 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ vendor coverage .phpunit.result.cache .idea/ +database.sqlite diff --git a/src/Contracts/PushesToPusher.php b/src/Contracts/PushesToPusher.php new file mode 100644 index 0000000..0a3b092 --- /dev/null +++ b/src/Contracts/PushesToPusher.php @@ -0,0 +1,32 @@ +header('x-app-id')); - $broadcaster = new PusherBroadcaster(new Pusher( - $app->key, - $app->secret, - $app->id, - [] - )); + $broadcaster = $this->getPusherBroadcaster([ + 'key' => $app->key, + 'secret' => $app->secret, + 'id' =>$app->id, + ]); /* * Since the dashboard itself is already secured by the diff --git a/src/Dashboard/Http/Controllers/SendMessage.php b/src/Dashboard/Http/Controllers/SendMessage.php index 92777e4..e3eb1cd 100644 --- a/src/Dashboard/Http/Controllers/SendMessage.php +++ b/src/Dashboard/Http/Controllers/SendMessage.php @@ -3,12 +3,15 @@ namespace BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers; use BeyondCode\LaravelWebSockets\Statistics\Rules\AppId; +use BeyondCode\LaravelWebSockets\Contracts\PushesToPusher; use Illuminate\Broadcasting\Broadcasters\PusherBroadcaster; use Illuminate\Http\Request; use Pusher\Pusher; class SendMessage { + use PushesToPusher; + /** * Send the message to the requested channel. * @@ -17,7 +20,7 @@ class SendMessage */ public function __invoke(Request $request) { - $validated = $request->validate([ + $request->validate([ 'appId' => ['required', new AppId], 'key' => 'required|string', 'secret' => 'required|string', @@ -26,30 +29,18 @@ class SendMessage 'data' => 'required|json', ]); - $this->getPusherBroadcaster($validated)->broadcast( - [$validated['channel']], - $validated['event'], - json_decode($validated['data'], true) + $broadcaster = $this->getPusherBroadcaster([ + 'key' => $request->key, + 'secret' => $request->secret, + 'id' => $request->appId, + ]); + + $broadcaster->broadcast( + [$request->channel], + $request->event, + json_decode($request->data, true) ); return 'ok'; } - - /** - * Get the pusher broadcaster for the current request. - * - * @param array $validated - * @return \Illuminate\Broadcasting\Broadcasters\PusherBroadcaster - */ - protected function getPusherBroadcaster(array $validated): PusherBroadcaster - { - $pusher = new Pusher( - $validated['key'], - $validated['secret'], - $validated['appId'], - config('broadcasting.connections.pusher.options', []) - ); - - return new PusherBroadcaster($pusher); - } } diff --git a/src/WebSocketsServiceProvider.php b/src/WebSocketsServiceProvider.php index f03a504..fd9853b 100644 --- a/src/WebSocketsServiceProvider.php +++ b/src/WebSocketsServiceProvider.php @@ -125,8 +125,8 @@ class WebSocketsServiceProvider extends ServiceProvider ], function () { Route::get('/', ShowDashboard::class)->name('dashboard'); Route::get('/api/{appId}/statistics', [DashboardApiController::class, 'getStatistics'])->name('statistics'); - Route::post('auth', AuthenticateDashboard::class)->name('auth'); - Route::post('event', SendMessage::class)->name('send'); + Route::post('/auth', AuthenticateDashboard::class)->name('auth'); + Route::post('/event', SendMessage::class)->name('send'); }); return $this; @@ -140,7 +140,7 @@ class WebSocketsServiceProvider extends ServiceProvider protected function registerDashboardGate() { Gate::define('viewWebSocketsDashboard', function ($user = null) { - return $this->app->environment(['local', 'testing']); + return $this->app->environment('local'); }); return $this; diff --git a/tests/Dashboard/AuthTest.php b/tests/Dashboard/AuthTest.php new file mode 100644 index 0000000..f1214d4 --- /dev/null +++ b/tests/Dashboard/AuthTest.php @@ -0,0 +1,100 @@ +getConnectedWebSocketConnection(['test-channel']); + + $this->pusherServer->onOpen($connection); + + $this->actingAs(factory(User::class)->create()) + ->json('POST', route('laravel-websockets.auth'), [ + 'socket_id' => $connection->socketId, + 'channel_name' => 'test-channel', + ], ['x-app-id' => '1234']) + ->seeJsonStructure([ + 'auth', + 'channel_data', + ]); + } + + /** @test */ + public function can_authenticate_dashboard_over_private_channel() + { + $connection = $this->getWebSocketConnection(); + + $this->pusherServer->onOpen($connection); + + $signature = "{$connection->socketId}:private-channel"; + + $hashedAppSecret = hash_hmac('sha256', $signature, $connection->app->secret); + + $message = new Message([ + 'event' => 'pusher:subscribe', + 'data' => [ + 'auth' => "{$connection->app->key}:{$hashedAppSecret}", + 'channel' => 'private-channel', + ], + ]); + + $this->pusherServer->onMessage($connection, $message); + + $connection->assertSentEvent('pusher_internal:subscription_succeeded', [ + 'channel' => 'private-channel', + ]); + + $this->actingAs(factory(User::class)->create()) + ->json('POST', route('laravel-websockets.auth'), [ + 'socket_id' => $connection->socketId, + 'channel_name' => 'private-test-channel', + ], ['x-app-id' => '1234']) + ->seeJsonStructure([ + 'auth', + ]); + } + + /** @test */ + public function can_authenticate_dashboard_over_presence_channel() + { + $connection = $this->getWebSocketConnection(); + + $this->pusherServer->onOpen($connection); + + $channelData = [ + 'user_id' => 1, + 'user_info' => [ + 'name' => 'Marcel', + ], + ]; + + $signature = "{$connection->socketId}:presence-channel:".json_encode($channelData); + + $message = new Message([ + 'event' => 'pusher:subscribe', + 'data' => [ + 'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret), + 'channel' => 'presence-channel', + 'channel_data' => json_encode($channelData), + ], + ]); + + $this->pusherServer->onMessage($connection, $message); + + $this->actingAs(factory(User::class)->create()) + ->json('POST', route('laravel-websockets.auth'), [ + 'socket_id' => $connection->socketId, + 'channel_name' => 'presence-channel', + ], ['x-app-id' => '1234']) + ->seeJsonStructure([ + 'auth', + ]); + } +} diff --git a/tests/Dashboard/DashboardTest.php b/tests/Dashboard/DashboardTest.php index ce098ca..7ea6bc0 100644 --- a/tests/Dashboard/DashboardTest.php +++ b/tests/Dashboard/DashboardTest.php @@ -3,14 +3,13 @@ namespace BeyondCode\LaravelWebSockets\Tests\Dashboard; use BeyondCode\LaravelWebSockets\Tests\TestCase; +use BeyondCode\LaravelWebSockets\Tests\Models\User; class DashboardTest extends TestCase { /** @test */ public function cant_see_dashboard_without_authorization() { - config(['app.env' => 'production']); - $this->get(route('laravel-websockets.dashboard')) ->assertResponseStatus(403); } @@ -18,7 +17,8 @@ class DashboardTest extends TestCase /** @test */ public function can_see_dashboard() { - $this->get(route('laravel-websockets.dashboard')) + $this->actingAs(factory(User::class)->create()) + ->get(route('laravel-websockets.dashboard')) ->assertResponseOk() ->see('WebSockets Dashboard'); } diff --git a/tests/Models/User.php b/tests/Models/User.php new file mode 100644 index 0000000..1f134fb --- /dev/null +++ b/tests/Models/User.php @@ -0,0 +1,16 @@ +resetDatabase(); + + $this->loadLaravelMigrations(['--database' => 'sqlite']); + + $this->withFactories(__DIR__.'/database/factories'); + $this->pusherServer = $this->app->make(config('websockets.handlers.websocket')); $this->channelManager = $this->app->make(ChannelManager::class); @@ -69,6 +75,7 @@ abstract class TestCase extends BaseTestCase { return [ \BeyondCode\LaravelWebSockets\WebSocketsServiceProvider::class, + TestServiceProvider::class, ]; } @@ -79,6 +86,16 @@ abstract class TestCase extends BaseTestCase { $app['config']->set('app.key', 'wslxrEFGWY6GfGhvN9L3wH3KSRJQQpBD'); + $app['config']->set('auth.providers.users.model', Models\User::class); + + $app['config']->set('database.default', 'sqlite'); + + $app['config']->set('database.connections.sqlite', [ + 'driver' => 'sqlite', + 'database' => __DIR__.'/database.sqlite', + 'prefix' => '', + ]); + $app['config']->set('websockets.apps', [ [ 'name' => 'Test App', @@ -307,4 +324,14 @@ abstract class TestCase extends BaseTestCase ->make(ReplicationInterface::class) ->getPublishClient(); } + + /** + * Reset the database. + * + * @return void + */ + protected function resetDatabase() + { + file_put_contents(__DIR__.'/database.sqlite', null); + } } diff --git a/tests/TestServiceProvider.php b/tests/TestServiceProvider.php new file mode 100644 index 0000000..958086e --- /dev/null +++ b/tests/TestServiceProvider.php @@ -0,0 +1,31 @@ +define(\BeyondCode\LaravelWebSockets\Tests\Models\User::class, function () { + return [ + 'name' => 'Name'.Str::random(5), + 'email' => Str::random(5).'@gmail.com', + 'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret + 'remember_token' => Str::random(10), + ]; +}); From 82aacc7c4d2185f1ce89b43b4e752e087566b06d Mon Sep 17 00:00:00 2001 From: rennokki Date: Sun, 23 Aug 2020 19:12:46 +0300 Subject: [PATCH 12/18] Apply fixes from StyleCI (#483) --- src/Dashboard/Http/Controllers/AuthenticateDashboard.php | 1 - src/Dashboard/Http/Controllers/SendMessage.php | 4 +--- tests/Dashboard/AuthTest.php | 4 ++-- tests/Dashboard/DashboardTest.php | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Dashboard/Http/Controllers/AuthenticateDashboard.php b/src/Dashboard/Http/Controllers/AuthenticateDashboard.php index 893cc09..0007ea3 100644 --- a/src/Dashboard/Http/Controllers/AuthenticateDashboard.php +++ b/src/Dashboard/Http/Controllers/AuthenticateDashboard.php @@ -6,7 +6,6 @@ use BeyondCode\LaravelWebSockets\Apps\App; use BeyondCode\LaravelWebSockets\Contracts\PushesToPusher; use Illuminate\Broadcasting\Broadcasters\PusherBroadcaster; use Illuminate\Http\Request; -use Pusher\Pusher; class AuthenticateDashboard { diff --git a/src/Dashboard/Http/Controllers/SendMessage.php b/src/Dashboard/Http/Controllers/SendMessage.php index e3eb1cd..54da651 100644 --- a/src/Dashboard/Http/Controllers/SendMessage.php +++ b/src/Dashboard/Http/Controllers/SendMessage.php @@ -2,11 +2,9 @@ namespace BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers; -use BeyondCode\LaravelWebSockets\Statistics\Rules\AppId; use BeyondCode\LaravelWebSockets\Contracts\PushesToPusher; -use Illuminate\Broadcasting\Broadcasters\PusherBroadcaster; +use BeyondCode\LaravelWebSockets\Statistics\Rules\AppId; use Illuminate\Http\Request; -use Pusher\Pusher; class SendMessage { diff --git a/tests/Dashboard/AuthTest.php b/tests/Dashboard/AuthTest.php index f1214d4..cf73ac5 100644 --- a/tests/Dashboard/AuthTest.php +++ b/tests/Dashboard/AuthTest.php @@ -2,9 +2,9 @@ namespace BeyondCode\LaravelWebSockets\Tests\Dashboard; -use BeyondCode\LaravelWebSockets\Tests\TestCase; -use BeyondCode\LaravelWebSockets\Tests\Models\User; use BeyondCode\LaravelWebSockets\Tests\Mocks\Message; +use BeyondCode\LaravelWebSockets\Tests\Models\User; +use BeyondCode\LaravelWebSockets\Tests\TestCase; class AuthTest extends TestCase { diff --git a/tests/Dashboard/DashboardTest.php b/tests/Dashboard/DashboardTest.php index 7ea6bc0..1d6716d 100644 --- a/tests/Dashboard/DashboardTest.php +++ b/tests/Dashboard/DashboardTest.php @@ -2,8 +2,8 @@ namespace BeyondCode\LaravelWebSockets\Tests\Dashboard; -use BeyondCode\LaravelWebSockets\Tests\TestCase; use BeyondCode\LaravelWebSockets\Tests\Models\User; +use BeyondCode\LaravelWebSockets\Tests\TestCase; class DashboardTest extends TestCase { From 499a153a0ac337323908e796f21fc3b093d7cc6d Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Sun, 23 Aug 2020 20:41:17 +0300 Subject: [PATCH 13/18] wip --- ...rdApiController.php => ShowStatistics.php} | 4 +- src/WebSocketsServiceProvider.php | 6 +- tests/Dashboard/StatisticsTest.php | 64 +++++++++++++++++++ 3 files changed, 69 insertions(+), 5 deletions(-) rename src/Dashboard/Http/Controllers/{DashboardApiController.php => ShowStatistics.php} (81%) create mode 100644 tests/Dashboard/StatisticsTest.php diff --git a/src/Dashboard/Http/Controllers/DashboardApiController.php b/src/Dashboard/Http/Controllers/ShowStatistics.php similarity index 81% rename from src/Dashboard/Http/Controllers/DashboardApiController.php rename to src/Dashboard/Http/Controllers/ShowStatistics.php index c240905..134cb62 100644 --- a/src/Dashboard/Http/Controllers/DashboardApiController.php +++ b/src/Dashboard/Http/Controllers/ShowStatistics.php @@ -5,7 +5,7 @@ namespace BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers; use BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver; use Illuminate\Http\Request; -class DashboardApiController +class ShowStatistics { /** * Get statistics for an app ID. @@ -15,7 +15,7 @@ class DashboardApiController * @param mixed $appId * @return \Illuminate\Http\Response */ - public function getStatistics(Request $request, StatisticsDriver $driver, $appId) + public function __invoke(Request $request, StatisticsDriver $driver, $appId) { return $driver::get($appId, $request); } diff --git a/src/WebSocketsServiceProvider.php b/src/WebSocketsServiceProvider.php index fd9853b..12fb58f 100644 --- a/src/WebSocketsServiceProvider.php +++ b/src/WebSocketsServiceProvider.php @@ -4,7 +4,7 @@ namespace BeyondCode\LaravelWebSockets; use BeyondCode\LaravelWebSockets\Apps\AppManager; use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\AuthenticateDashboard; -use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\DashboardApiController; +use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\ShowStatistics; use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\SendMessage; use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\ShowDashboard; use BeyondCode\LaravelWebSockets\Dashboard\Http\Middleware\Authorize as AuthorizeDashboard; @@ -124,9 +124,9 @@ class WebSocketsServiceProvider extends ServiceProvider 'middleware' => config('websockets.dashboard.middleware', [AuthorizeDashboard::class]), ], function () { Route::get('/', ShowDashboard::class)->name('dashboard'); - Route::get('/api/{appId}/statistics', [DashboardApiController::class, 'getStatistics'])->name('statistics'); + Route::get('/api/{appId}/statistics', ShowStatistics::class)->name('statistics'); Route::post('/auth', AuthenticateDashboard::class)->name('auth'); - Route::post('/event', SendMessage::class)->name('send'); + Route::post('/event', SendMessage::class)->name('event'); }); return $this; diff --git a/tests/Dashboard/StatisticsTest.php b/tests/Dashboard/StatisticsTest.php new file mode 100644 index 0000000..26c2d27 --- /dev/null +++ b/tests/Dashboard/StatisticsTest.php @@ -0,0 +1,64 @@ +getConnectedWebSocketConnection(['channel-1']); + + $logger = new MemoryStatisticsLogger( + $this->channelManager, + $this->statisticsDriver + ); + + $logger->webSocketMessage($connection->app->id); + $logger->apiMessage($connection->app->id); + $logger->connection($connection->app->id); + $logger->disconnection($connection->app->id); + + $logger->save(); + + $this->actingAs(factory(User::class)->create()) + ->json('GET', route('laravel-websockets.statistics', ['appId' => '1234'])) + ->assertResponseOk() + ->seeJsonStructure([ + 'peak_connections' => ['x', 'y'], + 'websocket_message_count' => ['x', 'y'], + 'api_message_count' => ['x', 'y'], + ]); + } + + /** @test */ + public function cant_get_statistics_for_invalid_app_id() + { + $connection = $this->getConnectedWebSocketConnection(['channel-1']); + + $logger = new MemoryStatisticsLogger( + $this->channelManager, + $this->statisticsDriver + ); + + $logger->webSocketMessage($connection->app->id); + $logger->apiMessage($connection->app->id); + $logger->connection($connection->app->id); + $logger->disconnection($connection->app->id); + + $logger->save(); + + $this->actingAs(factory(User::class)->create()) + ->json('GET', route('laravel-websockets.statistics', ['appId' => 'not_found'])) + ->seeJson([ + 'peak_connections' => ['x' => [], 'y' => []], + 'websocket_message_count' => ['x' => [], 'y' => []], + 'api_message_count' => ['x' => [], 'y' => []], + ]); + } +} From 3ce7eff44d5980e222aac2e661903d513af31d6a Mon Sep 17 00:00:00 2001 From: rennokki Date: Sun, 23 Aug 2020 20:41:38 +0300 Subject: [PATCH 14/18] Apply fixes from StyleCI (#484) --- src/WebSocketsServiceProvider.php | 2 +- tests/Dashboard/StatisticsTest.php | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/WebSocketsServiceProvider.php b/src/WebSocketsServiceProvider.php index 12fb58f..4c687ed 100644 --- a/src/WebSocketsServiceProvider.php +++ b/src/WebSocketsServiceProvider.php @@ -4,9 +4,9 @@ namespace BeyondCode\LaravelWebSockets; use BeyondCode\LaravelWebSockets\Apps\AppManager; use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\AuthenticateDashboard; -use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\ShowStatistics; use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\SendMessage; use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\ShowDashboard; +use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\ShowStatistics; use BeyondCode\LaravelWebSockets\Dashboard\Http\Middleware\Authorize as AuthorizeDashboard; use BeyondCode\LaravelWebSockets\PubSub\Broadcasters\RedisPusherBroadcaster; use BeyondCode\LaravelWebSockets\Server\Router; diff --git a/tests/Dashboard/StatisticsTest.php b/tests/Dashboard/StatisticsTest.php index 26c2d27..94af6c5 100644 --- a/tests/Dashboard/StatisticsTest.php +++ b/tests/Dashboard/StatisticsTest.php @@ -3,9 +3,8 @@ namespace BeyondCode\LaravelWebSockets\Tests\Dashboard; use BeyondCode\LaravelWebSockets\Statistics\Logger\MemoryStatisticsLogger; -use BeyondCode\LaravelWebSockets\Tests\TestCase; use BeyondCode\LaravelWebSockets\Tests\Models\User; -use BeyondCode\LaravelWebSockets\Tests\Mocks\Message; +use BeyondCode\LaravelWebSockets\Tests\TestCase; class StatisticsTest extends TestCase { From afa8af5ddc1e946710bef8bdd9f6e6122daebedf Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Mon, 24 Aug 2020 09:03:52 +0300 Subject: [PATCH 15/18] Added tests for send message --- .../Http/Controllers/SendMessage.php | 22 +++++--- tests/Dashboard/SendMessageTest.php | 53 +++++++++++++++++++ 2 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 tests/Dashboard/SendMessageTest.php diff --git a/src/Dashboard/Http/Controllers/SendMessage.php b/src/Dashboard/Http/Controllers/SendMessage.php index 54da651..c8d84d8 100644 --- a/src/Dashboard/Http/Controllers/SendMessage.php +++ b/src/Dashboard/Http/Controllers/SendMessage.php @@ -4,6 +4,7 @@ namespace BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers; use BeyondCode\LaravelWebSockets\Contracts\PushesToPusher; use BeyondCode\LaravelWebSockets\Statistics\Rules\AppId; +use Exception; use Illuminate\Http\Request; class SendMessage @@ -33,12 +34,21 @@ class SendMessage 'id' => $request->appId, ]); - $broadcaster->broadcast( - [$request->channel], - $request->event, - json_decode($request->data, true) - ); + try { + $broadcaster->broadcast( + [$request->channel], + $request->event, + json_decode($request->data, true) + ); + } catch (Exception $e) { + return response()->json([ + 'ok' => false, + 'exception' => $e->getMessage(), + ]); + } - return 'ok'; + return response()->json([ + 'ok' => true, + ]); } } diff --git a/tests/Dashboard/SendMessageTest.php b/tests/Dashboard/SendMessageTest.php new file mode 100644 index 0000000..0d466ee --- /dev/null +++ b/tests/Dashboard/SendMessageTest.php @@ -0,0 +1,53 @@ +skipOnRedisReplication(); + + // Because the Pusher server is not active, + // we expect it to turn out ok: false. + + $this->actingAs(factory(User::class)->create()) + ->json('POST', route('laravel-websockets.event'), [ + 'appId' => '1234', + 'key' => 'TestKey', + 'secret' => 'TestSecret', + 'channel' => 'test-channel', + 'event' => 'some-event', + 'data' => json_encode(['data' => 'yes']), + ]) + ->seeJson([ + 'ok' => false, + ]); + } + + /** @test */ + public function cant_send_message_for_invalid_app() + { + $this->skipOnRedisReplication(); + + // Because the Pusher server is not active, + // we expect it to turn out ok: false. + + $this->actingAs(factory(User::class)->create()) + ->json('POST', route('laravel-websockets.event'), [ + 'appId' => '9999', + 'key' => 'TestKey', + 'secret' => 'TestSecret', + 'channel' => 'test-channel', + 'event' => 'some-event', + 'data' => json_encode(['data' => 'yes']), + ]) + ->assertResponseStatus(422); + } +} From cd8e53c69d049d8f061400fac46441bfc94c023b Mon Sep 17 00:00:00 2001 From: rennokki Date: Mon, 24 Aug 2020 09:04:14 +0300 Subject: [PATCH 16/18] Apply fixes from StyleCI (#486) --- tests/Dashboard/SendMessageTest.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/Dashboard/SendMessageTest.php b/tests/Dashboard/SendMessageTest.php index 0d466ee..3bdcf89 100644 --- a/tests/Dashboard/SendMessageTest.php +++ b/tests/Dashboard/SendMessageTest.php @@ -2,10 +2,8 @@ namespace BeyondCode\LaravelWebSockets\Tests\Dashboard; -use BeyondCode\LaravelWebSockets\Statistics\Logger\MemoryStatisticsLogger; -use BeyondCode\LaravelWebSockets\Tests\TestCase; use BeyondCode\LaravelWebSockets\Tests\Models\User; -use BeyondCode\LaravelWebSockets\Tests\Mocks\Message; +use BeyondCode\LaravelWebSockets\Tests\TestCase; class SendMessageTest extends TestCase { From 4bfed3310c43ca4fab147bb06687ddb2b8ec007e Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Mon, 24 Aug 2020 09:10:50 +0300 Subject: [PATCH 17/18] removed trailing comma --- src/Contracts/PushesToPusher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Contracts/PushesToPusher.php b/src/Contracts/PushesToPusher.php index 0a3b092..4c160b3 100644 --- a/src/Contracts/PushesToPusher.php +++ b/src/Contracts/PushesToPusher.php @@ -26,7 +26,7 @@ trait PushesToPusher } return new PusherBroadcaster( - new Pusher($app['key'], $app['secret'], $app['id'], config('broadcasting.connections.pusher.options', [])), + new Pusher($app['key'], $app['secret'], $app['id'], config('broadcasting.connections.pusher.options', [])) ); } } From f3b706de524dd2a1212a48da762ebd28d8fb8f0e Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Mon, 24 Aug 2020 09:39:05 +0300 Subject: [PATCH 18/18] wip --- tests/Dashboard/SendMessageTest.php | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/Dashboard/SendMessageTest.php b/tests/Dashboard/SendMessageTest.php index 3bdcf89..65ee7fb 100644 --- a/tests/Dashboard/SendMessageTest.php +++ b/tests/Dashboard/SendMessageTest.php @@ -29,6 +29,31 @@ class SendMessageTest extends TestCase ]); } + /** @test */ + public function can_send_message_on_redis_replication() + { + $this->skipOnLocalReplication(); + + // Because the Pusher server is not active, + // we expect it to turn out ok: false. + // However, the driver is set to redis, + // so Redis would take care of this + // and stream the message to all active servers instead. + + $this->actingAs(factory(User::class)->create()) + ->json('POST', route('laravel-websockets.event'), [ + 'appId' => '1234', + 'key' => 'TestKey', + 'secret' => 'TestSecret', + 'channel' => 'test-channel', + 'event' => 'some-event', + 'data' => json_encode(['data' => 'yes']), + ]) + ->seeJson([ + 'ok' => true, + ]); + } + /** @test */ public function cant_send_message_for_invalid_app() {