From 51f84e3c40c82957ad5aa6417c580549283b031e Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Thu, 13 Aug 2020 16:18:14 +0300 Subject: [PATCH] set up tests --- .github/workflows/run-tests.yml | 5 ++++- config/websockets.php | 12 +++++------- src/Console/StartWebSocketServer.php | 4 +++- .../Controllers/FetchChannelsController.php | 8 ++++---- src/PubSub/Drivers/LocalClient.php | 1 - src/PubSub/Drivers/RedisClient.php | 12 ++++++++---- src/WebSockets/Channels/Channel.php | 10 +++++----- src/WebSockets/Channels/PresenceChannel.php | 10 +++++----- src/WebSocketsServiceProvider.php | 17 +++++++++-------- tests/TestCase.php | 12 ++++++++++++ 10 files changed, 55 insertions(+), 36 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index f695a5f..a303a81 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -43,8 +43,11 @@ jobs: composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest - name: Execute tests - run: vendor/bin/phpunit --coverage-text --coverage-clover=coverage.xml + run: | + REPLICATION_DRIVER=local phpunit --coverage-text --coverage-clover=coverage_local.xml + REPLICATION_DRIVER=redis phpunit --coverage-text --coverage-clover=coverage_redis.xml - uses: codecov/codecov-action@v1 with: fail_ci_if_error: false + file: '*.xml' diff --git a/config/websockets.php b/config/websockets.php index be4a166..1c9f61f 100644 --- a/config/websockets.php +++ b/config/websockets.php @@ -143,23 +143,21 @@ return [ /* |-------------------------------------------------------------------------- - | Broadcasting Replication + | Broadcasting Replication PubSub |-------------------------------------------------------------------------- | | You can enable replication to publish and subscribe to | messages across the driver. - | - | By default, it is disabled, but you can configure it to use drivers + + | By default, it is set to 'local', but you can configure it to use drivers | like Redis to ensure connection between multiple instances of - | WebSocket servers. + | WebSocket servers. Just set the driver to 'redis' to enable the PubSub using Redis. | */ 'replication' => [ - 'enabled' => false, - - 'driver' => 'redis', + 'driver' => 'local', 'redis' => [ diff --git a/src/Console/StartWebSocketServer.php b/src/Console/StartWebSocketServer.php index 28af82f..eb02101 100644 --- a/src/Console/StartWebSocketServer.php +++ b/src/Console/StartWebSocketServer.php @@ -166,7 +166,9 @@ class StartWebSocketServer extends Command protected function configurePubSubReplication() { - $this->laravel->get(ReplicationInterface::class)->boot($this->loop); + $this->laravel + ->get(ReplicationInterface::class) + ->boot($this->loop); return $this; } diff --git a/src/HttpApi/Controllers/FetchChannelsController.php b/src/HttpApi/Controllers/FetchChannelsController.php index 7d0a6aa..13a274f 100644 --- a/src/HttpApi/Controllers/FetchChannelsController.php +++ b/src/HttpApi/Controllers/FetchChannelsController.php @@ -13,13 +13,13 @@ use Symfony\Component\HttpKernel\Exception\HttpException; class FetchChannelsController extends Controller { /** @var ReplicationInterface */ - protected $replication; + protected $pubsub; - public function __construct(ChannelManager $channelManager, ReplicationInterface $replication) + public function __construct(ChannelManager $channelManager, ReplicationInterface $pubsub) { parent::__construct($channelManager); - $this->replication = $replication; + $this->pubsub = $pubsub; } public function __invoke(Request $request) @@ -51,7 +51,7 @@ class FetchChannelsController extends Controller // We ask the replication backend to get us the member count per channel. // We get $counts back as a key-value array of channel names and their member count. - return $this->replication + return $this->pubsub ->channelMemberCounts($request->appId, $channelNames) ->then(function (array $counts) use ($channels, $attributes) { return [ diff --git a/src/PubSub/Drivers/LocalClient.php b/src/PubSub/Drivers/LocalClient.php index 42f013b..22b2fe9 100644 --- a/src/PubSub/Drivers/LocalClient.php +++ b/src/PubSub/Drivers/LocalClient.php @@ -38,7 +38,6 @@ class LocalClient implements ReplicationInterface */ public function publish(string $appId, string $channel, stdClass $payload): bool { - // Nothing to do, nobody to publish to return true; } diff --git a/src/PubSub/Drivers/RedisClient.php b/src/PubSub/Drivers/RedisClient.php index 7a52c4f..e3faa75 100644 --- a/src/PubSub/Drivers/RedisClient.php +++ b/src/PubSub/Drivers/RedisClient.php @@ -257,20 +257,24 @@ class RedisClient implements ReplicationInterface */ protected function getConnectionUri() { - $name = config('websockets.replication.connection') ?? 'default'; - $config = config("database.redis.$name"); + $name = config('websockets.replication.redis.connection') ?? 'default'; + $config = config('database.redis')[$name]; + $host = $config['host']; - $port = $config['port'] ? (':'.$config['port']) : ':6379'; + $port = $config['port'] ?: 6379; $query = []; + if ($config['password']) { $query['password'] = $config['password']; } + if ($config['database']) { $query['database'] = $config['database']; } + $query = http_build_query($query); - return "redis://$host$port".($query ? '?'.$query : ''); + return "redis://{$host}:{$port}".($query ? "?{$query}" : ''); } } diff --git a/src/WebSockets/Channels/Channel.php b/src/WebSockets/Channels/Channel.php index 75a9791..bf84564 100644 --- a/src/WebSockets/Channels/Channel.php +++ b/src/WebSockets/Channels/Channel.php @@ -15,7 +15,7 @@ class Channel protected $channelName; /** @var ReplicationInterface */ - protected $replication; + protected $pubsub; /** @var \Ratchet\ConnectionInterface[] */ protected $subscribedConnections = []; @@ -23,7 +23,7 @@ class Channel public function __construct(string $channelName) { $this->channelName = $channelName; - $this->replication = app(ReplicationInterface::class); + $this->pubsub = app(ReplicationInterface::class); } public function getChannelName(): string @@ -68,7 +68,7 @@ class Channel $this->saveConnection($connection); // Subscribe to broadcasted messages from the pub/sub backend - $this->replication->subscribe($connection->app->id, $this->channelName); + $this->pubsub->subscribe($connection->app->id, $this->channelName); $connection->send(json_encode([ 'event' => 'pusher_internal:subscription_succeeded', @@ -81,7 +81,7 @@ class Channel unset($this->subscribedConnections[$connection->socketId]); // Unsubscribe from the pub/sub backend - $this->replication->unsubscribe($connection->app->id, $this->channelName); + $this->pubsub->unsubscribe($connection->app->id, $this->channelName); if (! $this->hasConnections()) { DashboardLogger::vacated($connection, $this->channelName); @@ -120,7 +120,7 @@ class Channel // in this case. If this came from TriggerEventController, then we still want // to publish to get the message out to other server instances. if ($publish) { - $this->replication->publish($appId, $this->channelName, $payload); + $this->pubsub->publish($appId, $this->channelName, $payload); } // Performance optimization, if we don't have a socket ID, diff --git a/src/WebSockets/Channels/PresenceChannel.php b/src/WebSockets/Channels/PresenceChannel.php index 94a1426..b2ce982 100644 --- a/src/WebSockets/Channels/PresenceChannel.php +++ b/src/WebSockets/Channels/PresenceChannel.php @@ -28,7 +28,7 @@ class PresenceChannel extends Channel public function getUsers(string $appId) { // Get the members list from the replication backend - return $this->replication + return $this->pubsub ->channelMembers($appId, $this->channelName); } @@ -49,7 +49,7 @@ class PresenceChannel extends Channel $this->users[$connection->socketId] = $channelData; // Add the connection as a member of the channel - $this->replication + $this->pubsub ->joinChannel( $connection->app->id, $this->channelName, @@ -59,7 +59,7 @@ class PresenceChannel extends Channel // We need to pull the channel data from the replication backend, // otherwise we won't be sending the full details of the channel - $this->replication + $this->pubsub ->channelMembers($connection->app->id, $this->channelName) ->then(function ($users) use ($connection) { // Send the success event @@ -86,7 +86,7 @@ class PresenceChannel extends Channel } // Remove the connection as a member of the channel - $this->replication + $this->pubsub ->leaveChannel( $connection->app->id, $this->channelName, @@ -110,7 +110,7 @@ class PresenceChannel extends Channel */ public function toArray(string $appId = null) { - return $this->replication + return $this->pubsub ->channelMembers($appId, $this->channelName) ->then(function ($users) { return array_merge(parent::toArray(), [ diff --git a/src/WebSocketsServiceProvider.php b/src/WebSocketsServiceProvider.php index a0bd848..4a031e7 100644 --- a/src/WebSocketsServiceProvider.php +++ b/src/WebSocketsServiceProvider.php @@ -24,6 +24,7 @@ use Illuminate\Support\Facades\Schema; use Illuminate\Support\ServiceProvider; use Psr\Log\LoggerInterface; use Pusher\Pusher; +use React\EventLoop\Factory as LoopFactory; class WebSocketsServiceProvider extends ServiceProvider { @@ -56,19 +57,19 @@ class WebSocketsServiceProvider extends ServiceProvider protected function configurePubSub() { - if (config('websockets.replication.enabled') !== true || config('websockets.replication.driver') !== 'redis') { + if (config('websockets.replication.driver') === 'local') { $this->app->singleton(ReplicationInterface::class, function () { - return new LocalClient(); + return new LocalClient; }); - - return; } - $this->app->singleton(ReplicationInterface::class, function () { - return (new RedisClient())->boot($this->loop); - }); + if (config('websockets.replication.driver') === 'redis') { + $this->app->singleton(ReplicationInterface::class, function () { + return (new RedisClient)->boot($this->loop ?? LoopFactory::create()); + }); + } - $this->app->get(BroadcastManager::class)->extend('redis-pusher', function ($app, array $config) { + $this->app->get(BroadcastManager::class)->extend('websockets', function ($app, array $config) { $pusher = new Pusher( $config['key'], $config['secret'], $config['app_id'], $config['options'] ?? [] diff --git a/tests/TestCase.php b/tests/TestCase.php index b209783..54e9f7a 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -56,6 +56,18 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase ], ]); + $app['config']->set('database.redis.default', [ + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'password' => env('REDIS_PASSWORD', null), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_DB', '0'), + ]); + + $app['config']->set( + 'websockets.replication.driver', + getenv('REPLICATION_DRIVER') ?: 'local' + ); + include_once __DIR__.'/../database/migrations/create_websockets_statistics_entries_table.php.stub'; (new \CreateWebSocketsStatisticsEntriesTable())->up();