diff --git a/src/Servers/Reverb/Publishing/RedisClient.php b/src/Servers/Reverb/Publishing/RedisClient.php index 02b1031..f5cd3fb 100644 --- a/src/Servers/Reverb/Publishing/RedisClient.php +++ b/src/Servers/Reverb/Publishing/RedisClient.php @@ -96,7 +96,7 @@ public function reconnect(): void if ($this->clientReconnectionTimer >= $this->reconnectionTimeout()) { Log::error("Failed to reconnect to Redis connection [{$this->name}] within {$this->reconnectionTimeout()} second limit"); - exit; + throw new Exception("Failed to reconnect to Redis connection [{$this->name}] within {$this->reconnectionTimeout()} second limit"); } Log::info("Attempting to reconnect Redis connection [{$this->name}]"); $this->connect(); @@ -170,7 +170,9 @@ protected function configureClientErrorHandler(): void { $this->client->on('close', function () { $this->client = null; - Log::info("Disconnected fromRedis connection [{$this->name}]"); + + Log::info("Disconnected from Redis connection [{$this->name}]"); + $this->reconnect(); }); } diff --git a/tests/Unit/Servers/Reverb/Publishing/RedisPubSubProviderTest.php b/tests/Unit/Servers/Reverb/Publishing/RedisPubSubProviderTest.php index bc2a769..ee0416f 100644 --- a/tests/Unit/Servers/Reverb/Publishing/RedisPubSubProviderTest.php +++ b/tests/Unit/Servers/Reverb/Publishing/RedisPubSubProviderTest.php @@ -4,6 +4,7 @@ use Laravel\Reverb\Servers\Reverb\Contracts\PubSubIncomingMessageHandler; use Laravel\Reverb\Servers\Reverb\Publishing\RedisClientFactory; use Laravel\Reverb\Servers\Reverb\Publishing\RedisPubSubProvider; +use React\EventLoop\Loop; use React\EventLoop\LoopInterface; use React\Promise\Promise; @@ -45,15 +46,84 @@ $provider->connect(Mockery::mock(LoopInterface::class)); }); -it('can successfully reconnect', function () {})->todo(); +it('can successfully reconnect', function () { + $clientFactory = Mockery::mock(RedisClientFactory::class); + $loop = Mockery::mock(LoopInterface::class); + + $loop->shouldReceive('addTimer') + ->once() + ->with(1, Mockery::any()); + + // Publisher client + $clientFactory->shouldReceive('make') + ->once() + ->andReturn(new Promise(fn () => throw new Exception)); + + // Subscriber client + $clientFactory->shouldReceive('make') + ->once() + ->andReturn(new Promise(fn (callable $resolve) => $resolve)); + + $provider = new RedisPubSubProvider($clientFactory, Mockery::mock(PubSubIncomingMessageHandler::class), 'reverb'); + $provider->connect($loop); +}); + +it('can timeout and fail when unable to reconnect', function () { + $clientFactory = Mockery::mock(RedisClientFactory::class); + $loop = Loop::get(); + + // Publisher client + $clientFactory->shouldReceive('make') + ->once() + ->andReturn(new Promise(fn () => throw new Exception)); + + // Subscriber client + $clientFactory->shouldReceive('make') + ->once() + ->andReturn(new Promise(fn (callable $resolve) => $resolve)); -it('can timeout and fail when unable to reconnect', function () {})->todo(); + $provider = new RedisPubSubProvider($clientFactory, Mockery::mock(PubSubIncomingMessageHandler::class), 'reverb', ['host' => 'localhost', 'port' => 6379, 'timeout' => 1]); + $provider->connect($loop); -it('queues subscription events', function () {})->todo(); + $loop->run(); +})->throws(Exception::class, 'Failed to reconnect to Redis connection [publisher] within 1 second limit'); + +it('queues subscription events', function () { + $clientFactory = Mockery::mock(RedisClientFactory::class); + + $clientFactory->shouldReceive('make') + ->twice() + ->andReturn(new Promise(fn (callable $resolve) => $resolve)); + + $provider = new RedisPubSubProvider($clientFactory, Mockery::mock(PubSubIncomingMessageHandler::class), 'reverb'); + $provider->connect(Mockery::mock(LoopInterface::class)); + $provider->subscribe(); + + $subscribingClient = (new ReflectionProperty($provider, 'subscribingClient'))->getValue($provider); + $queuedSubscriptionEvents = (new ReflectionProperty($subscribingClient, 'queuedSubscriptionEvents'))->getValue($subscribingClient); + + expect(array_keys($queuedSubscriptionEvents))->toBe(['subscribe', 'on']); +}); it('can process queued subscription events', function () {})->todo(); -it('queues publish events', function () {})->todo(); +it('queues publish events', function () { + $clientFactory = Mockery::mock(RedisClientFactory::class); + + $clientFactory->shouldReceive('make') + ->twice() + ->andReturn(new Promise(fn (callable $resolve) => $resolve)); + + $provider = new RedisPubSubProvider($clientFactory, Mockery::mock(PubSubIncomingMessageHandler::class), 'reverb'); + $provider->connect(Mockery::mock(LoopInterface::class)); + $provider->publish(['event' => 'first test']); + $provider->publish(['event' => 'second test']); + + $publishingClient = (new ReflectionProperty($provider, 'publishingClient'))->getValue($provider); + $queuedPublishEvents = (new ReflectionProperty($publishingClient, 'queuedPublishEvents'))->getValue($publishingClient); + + expect($queuedPublishEvents)->toBe([['event' => 'first test'], ['event' => 'second test']]); +}); it('can process queued publish events', function () {})->todo();