From c1bef4db5b886c1785fb308d5f2bae54fe2a9e8a Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Fri, 25 Sep 2020 22:16:06 +0300 Subject: [PATCH 1/9] Added async redis connection --- composer.json | 1 + config/websockets.php | 2 +- docs/horizontal-scaling/redis.md | 26 ++ src/ChannelManagers/RedisChannelManager.php | 10 + src/Queue/AsyncRedisConnector.php | 24 ++ src/Queue/AsyncRedisQueue.php | 26 ++ src/WebSocketsServiceProvider.php | 22 +- tests/LocalQueueTest.php | 273 ++++++++++++++++++++ tests/RedisQueueTest.php | 273 ++++++++++++++++++++ tests/TestCase.php | 38 +-- 10 files changed, 677 insertions(+), 18 deletions(-) create mode 100644 src/Queue/AsyncRedisConnector.php create mode 100644 src/Queue/AsyncRedisQueue.php create mode 100644 tests/LocalQueueTest.php create mode 100644 tests/RedisQueueTest.php diff --git a/composer.json b/composer.json index 06a9d11..33c4550 100644 --- a/composer.json +++ b/composer.json @@ -41,6 +41,7 @@ "illuminate/broadcasting": "^6.3|^7.0|^8.0", "illuminate/console": "^6.3|^7.0|^8.0", "illuminate/http": "^6.3|^7.0|^8.0", + "illuminate/queue": "^6.3|^7.0|^8.0", "illuminate/routing": "^6.3|^7.0|^8.0", "illuminate/support": "^6.3|^7.0|^8.0", "pusher/pusher-php-server": "^3.0|^4.0", diff --git a/config/websockets.php b/config/websockets.php index 9dcd4f6..9bb34b4 100644 --- a/config/websockets.php +++ b/config/websockets.php @@ -137,7 +137,7 @@ return [ 'redis' => [ - 'connection' => 'default', + 'connection' => env('WEBSOCKETS_REDIS_REPLICATION_CONNECTION', 'default'), /* |-------------------------------------------------------------------------- diff --git a/docs/horizontal-scaling/redis.md b/docs/horizontal-scaling/redis.md index 4f63835..86759db 100644 --- a/docs/horizontal-scaling/redis.md +++ b/docs/horizontal-scaling/redis.md @@ -40,3 +40,29 @@ You can set the connection name to the Redis database under `redis`: ``` The connections can be found in your `config/database.php` file, under the `redis` key. + +## Async Redis Queue + +The default Redis connection also interacts with the queues. Since you might want to dispatch jobs on Redis from the server, you can encounter an anti-pattern of using a blocking I/O connection (like PhpRedis or PRedis) within the WebSockets server. + +To solve this issue, you can configure the built-in queue driver that uses the Async Redis connection when it's possible, like within the WebSockets server. It's highly recommended to switch your queue to it if you are going to use the queues within the server controllers, for example. + +Add the `async-redis` queue driver to your list of connections. The configuration parameters are compatible with the default `redis` driver: + +```php +'connections' => [ + 'async-redis' => [ + 'driver' => 'async-redis', + 'connection' => env('WEBSOCKETS_REDIS_REPLICATION_CONNECTION', 'default'), + 'queue' => env('REDIS_QUEUE', 'default'), + 'retry_after' => 90, + 'block_for' => null, + ], +] +``` + +Also, make sure that the default queue driver is set to `async-redis`: + +``` +QUEUE_CONNECTION=async-redis +``` diff --git a/src/ChannelManagers/RedisChannelManager.php b/src/ChannelManagers/RedisChannelManager.php index c099bbf..51a6d59 100644 --- a/src/ChannelManagers/RedisChannelManager.php +++ b/src/ChannelManagers/RedisChannelManager.php @@ -519,6 +519,16 @@ class RedisChannelManager extends LocalChannelManager return $this->publishClient; } + /** + * Get the Redis client used by other classes. + * + * @return Client + */ + public function getRedisClient() + { + return $this->getPublishClient(); + } + /** * Get the unique identifier for the server. * diff --git a/src/Queue/AsyncRedisConnector.php b/src/Queue/AsyncRedisConnector.php new file mode 100644 index 0000000..ac730c3 --- /dev/null +++ b/src/Queue/AsyncRedisConnector.php @@ -0,0 +1,24 @@ +redis, $config['queue'], + $config['connection'] ?? $this->connection, + $config['retry_after'] ?? 60, + $config['block_for'] ?? null + ); + } +} diff --git a/src/Queue/AsyncRedisQueue.php b/src/Queue/AsyncRedisQueue.php new file mode 100644 index 0000000..9fd35cf --- /dev/null +++ b/src/Queue/AsyncRedisQueue.php @@ -0,0 +1,26 @@ +container->bound(ChannelManager::Class) + ? $this->container->make(ChannelManager::class) + : null; + + return $channelManager && method_exists($channelManager, 'getRedisClient') + ? $channelManager->getRedisClient() + : parent::getConnection(); + } +} diff --git a/src/WebSocketsServiceProvider.php b/src/WebSocketsServiceProvider.php index e498c11..f513caa 100644 --- a/src/WebSocketsServiceProvider.php +++ b/src/WebSocketsServiceProvider.php @@ -11,6 +11,7 @@ use BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers\ShowStatistics; use BeyondCode\LaravelWebSockets\Dashboard\Http\Middleware\Authorize as AuthorizeDashboard; use BeyondCode\LaravelWebSockets\Server\Router; use Illuminate\Support\Facades\Gate; +use Illuminate\Support\Facades\Queue; use Illuminate\Support\Facades\Route; use Illuminate\Support\ServiceProvider; @@ -36,6 +37,12 @@ class WebSocketsServiceProvider extends ServiceProvider __DIR__.'/../database/migrations/0000_00_00_000000_rename_statistics_counters.php' => database_path('migrations/0000_00_00_000000_rename_statistics_counters.php'), ], 'migrations'); + $this->registerAsyncRedisQueueDriver(); + + $this->registerRouter(); + + $this->registerManagers(); + $this->registerStatistics(); $this->registerDashboard(); @@ -50,8 +57,19 @@ class WebSocketsServiceProvider extends ServiceProvider */ public function register() { - $this->registerRouter(); - $this->registerManagers(); + // + } + + /** + * Register the async, non-blocking Redis queue driver. + * + * @return void + */ + protected function registerAsyncRedisQueueDriver() + { + Queue::extend('async-redis', function () { + return new Queue\AsyncRedisConnector($this->app['redis']); + }); } /** diff --git a/tests/LocalQueueTest.php b/tests/LocalQueueTest.php new file mode 100644 index 0000000..dcd8464 --- /dev/null +++ b/tests/LocalQueueTest.php @@ -0,0 +1,273 @@ +runOnlyOnLocalReplication(); + } + + /** + * {@inheritdoc} + */ + protected function tearDown(): void + { + m::close(); + } + + public function testPushProperlyPushesJobOntoRedis() + { + $uuid = Str::uuid(); + + Str::createUuidsUsing(function () use ($uuid) { + return $uuid; + }); + + $queue = $this->getMockBuilder(RedisQueue::class) + ->setMethods(['getRandomId']) + ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) + ->getMock(); + + $queue->expects($this->once()) + ->method('getRandomId') + ->willReturn('foo'); + + $redis->shouldReceive('connection') + ->once() + ->andReturn($redis); + + $redis->shouldReceive('eval') + ->once() + ->with(LuaScripts::push(), 2, 'queues:default', 'queues:default:notify', json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'id' => 'foo', 'attempts' => 0])); + + $id = $queue->push('foo', ['data']); + + $this->assertSame('foo', $id); + + Str::createUuidsNormally(); + } + + public function testPushProperlyPushesJobOntoRedisWithCustomPayloadHook() + { + $uuid = Str::uuid(); + + Str::createUuidsUsing(function () use ($uuid) { + return $uuid; + }); + + $queue = $this->getMockBuilder(RedisQueue::class) + ->setMethods(['getRandomId']) + ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) + ->getMock(); + + $queue->expects($this->once()) + ->method('getRandomId') + ->willReturn('foo'); + + $redis->shouldReceive('connection') + ->once() + ->andReturn($redis); + + $redis->shouldReceive('eval') + ->once() + ->with(LuaScripts::push(), 2, 'queues:default', 'queues:default:notify', json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'custom' => 'taylor', 'id' => 'foo', 'attempts' => 0])); + + Queue::createPayloadUsing(function ($connection, $queue, $payload) { + return ['custom' => 'taylor']; + }); + + $id = $queue->push('foo', ['data']); + + $this->assertSame('foo', $id); + + Queue::createPayloadUsing(null); + + Str::createUuidsNormally(); + } + + public function testPushProperlyPushesJobOntoRedisWithTwoCustomPayloadHook() + { + $uuid = Str::uuid(); + + Str::createUuidsUsing(function () use ($uuid) { + return $uuid; + }); + + $queue = $this->getMockBuilder(RedisQueue::class) + ->setMethods(['getRandomId']) + ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) + ->getMock(); + + $queue->expects($this->once()) + ->method('getRandomId') + ->willReturn('foo'); + + $redis->shouldReceive('connection') + ->once() + ->andReturn($redis); + + $redis->shouldReceive('eval') + ->once() + ->with(LuaScripts::push(), 2, 'queues:default', 'queues:default:notify', json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'custom' => 'taylor', 'bar' => 'foo', 'id' => 'foo', 'attempts' => 0])); + + Queue::createPayloadUsing(function ($connection, $queue, $payload) { + return ['custom' => 'taylor']; + }); + + Queue::createPayloadUsing(function ($connection, $queue, $payload) { + return ['bar' => 'foo']; + }); + + $id = $queue->push('foo', ['data']); + + $this->assertSame('foo', $id); + + Queue::createPayloadUsing(null); + + Str::createUuidsNormally(); + } + + public function testDelayedPushProperlyPushesJobOntoRedis() + { + $uuid = Str::uuid(); + + Str::createUuidsUsing(function () use ($uuid) { + return $uuid; + }); + + $queue = $this->getMockBuilder(RedisQueue::class) + ->setMethods(['availableAt', 'getRandomId']) + ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) + ->getMock(); + + $queue->expects($this->once()) + ->method('getRandomId') + ->willReturn('foo'); + + $queue->expects($this->once()) + ->method('availableAt') + ->with(1) + ->willReturn(2); + + $redis->shouldReceive('connection') + ->once() + ->andReturn($redis); + + $redis->shouldReceive('zadd') + ->once() + ->with('queues:default:delayed', 2, json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'id' => 'foo', 'attempts' => 0])); + + $id = $queue->later(1, 'foo', ['data']); + + $this->assertSame('foo', $id); + + Str::createUuidsNormally(); + } + + public function testDelayedPushWithDateTimeProperlyPushesJobOntoRedis() + { + $uuid = Str::uuid(); + + Str::createUuidsUsing(function () use ($uuid) { + return $uuid; + }); + + $date = Carbon::now(); + + $queue = $this->getMockBuilder(RedisQueue::class) + ->setMethods(['availableAt', 'getRandomId']) + ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) + ->getMock(); + + $queue->expects($this->once()) + ->method('getRandomId') + ->willReturn('foo'); + + $queue->expects($this->once()) + ->method('availableAt') + ->with($date) + ->willReturn(2); + + $redis->shouldReceive('connection') + ->once() + ->andReturn($redis); + + $redis->shouldReceive('zadd') + ->once() + ->with('queues:default:delayed', 2, json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'id' => 'foo', 'attempts' => 0])); + + $queue->later($date, 'foo', ['data']); + + Str::createUuidsNormally(); + } + + public function testFireProperlyCallsTheJobHandler() + { + $job = $this->getJob(); + + $job->getContainer() + ->shouldReceive('make') + ->once()->with('foo') + ->andReturn($handler = m::mock(stdClass::class)); + + $handler->shouldReceive('fire') + ->once() + ->with($job, ['data']); + + $job->fire(); + } + + public function testDeleteRemovesTheJobFromRedis() + { + $job = $this->getJob(); + + $job->getRedisQueue() + ->shouldReceive('deleteReserved') + ->once() + ->with('default', $job); + + $job->delete(); + } + + public function testReleaseProperlyReleasesJobOntoRedis() + { + $job = $this->getJob(); + + $job->getRedisQueue() + ->shouldReceive('deleteAndRelease') + ->once() + ->with('default', $job, 1); + + $job->release(1); + } + + protected function getJob() + { + return new RedisJob( + m::mock(Container::class), + m::mock(RedisQueue::class), + json_encode(['job' => 'foo', 'data' => ['data'], 'attempts' => 1]), + json_encode(['job' => 'foo', 'data' => ['data'], 'attempts' => 2]), + 'connection-name', + 'default' + ); + } +} diff --git a/tests/RedisQueueTest.php b/tests/RedisQueueTest.php new file mode 100644 index 0000000..3fbdc63 --- /dev/null +++ b/tests/RedisQueueTest.php @@ -0,0 +1,273 @@ +runOnlyOnRedisReplication(); + } + + /** + * {@inheritdoc} + */ + protected function tearDown(): void + { + m::close(); + } + + public function testPushProperlyPushesJobOntoRedis() + { + $uuid = Str::uuid(); + + Str::createUuidsUsing(function () use ($uuid) { + return $uuid; + }); + + $queue = $this->getMockBuilder(RedisQueue::class) + ->setMethods(['getRandomId']) + ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) + ->getMock(); + + $queue->expects($this->once()) + ->method('getRandomId') + ->willReturn('foo'); + + $redis->shouldReceive('connection') + ->once() + ->andReturn($redis); + + $redis->shouldReceive('eval') + ->once() + ->with(LuaScripts::push(), 2, 'queues:default', 'queues:default:notify', json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'id' => 'foo', 'attempts' => 0])); + + $id = $queue->push('foo', ['data']); + + $this->assertSame('foo', $id); + + Str::createUuidsNormally(); + } + + public function testPushProperlyPushesJobOntoRedisWithCustomPayloadHook() + { + $uuid = Str::uuid(); + + Str::createUuidsUsing(function () use ($uuid) { + return $uuid; + }); + + $queue = $this->getMockBuilder(RedisQueue::class) + ->setMethods(['getRandomId']) + ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) + ->getMock(); + + $queue->expects($this->once()) + ->method('getRandomId') + ->willReturn('foo'); + + $redis->shouldReceive('connection') + ->once() + ->andReturn($redis); + + $redis->shouldReceive('eval') + ->once() + ->with(LuaScripts::push(), 2, 'queues:default', 'queues:default:notify', json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'custom' => 'taylor', 'id' => 'foo', 'attempts' => 0])); + + Queue::createPayloadUsing(function ($connection, $queue, $payload) { + return ['custom' => 'taylor']; + }); + + $id = $queue->push('foo', ['data']); + + $this->assertSame('foo', $id); + + Queue::createPayloadUsing(null); + + Str::createUuidsNormally(); + } + + public function testPushProperlyPushesJobOntoRedisWithTwoCustomPayloadHook() + { + $uuid = Str::uuid(); + + Str::createUuidsUsing(function () use ($uuid) { + return $uuid; + }); + + $queue = $this->getMockBuilder(RedisQueue::class) + ->setMethods(['getRandomId']) + ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) + ->getMock(); + + $queue->expects($this->once()) + ->method('getRandomId') + ->willReturn('foo'); + + $redis->shouldReceive('connection') + ->once() + ->andReturn($redis); + + $redis->shouldReceive('eval') + ->once() + ->with(LuaScripts::push(), 2, 'queues:default', 'queues:default:notify', json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'custom' => 'taylor', 'bar' => 'foo', 'id' => 'foo', 'attempts' => 0])); + + Queue::createPayloadUsing(function ($connection, $queue, $payload) { + return ['custom' => 'taylor']; + }); + + Queue::createPayloadUsing(function ($connection, $queue, $payload) { + return ['bar' => 'foo']; + }); + + $id = $queue->push('foo', ['data']); + + $this->assertSame('foo', $id); + + Queue::createPayloadUsing(null); + + Str::createUuidsNormally(); + } + + public function testDelayedPushProperlyPushesJobOntoRedis() + { + $uuid = Str::uuid(); + + Str::createUuidsUsing(function () use ($uuid) { + return $uuid; + }); + + $queue = $this->getMockBuilder(RedisQueue::class) + ->setMethods(['availableAt', 'getRandomId']) + ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) + ->getMock(); + + $queue->expects($this->once()) + ->method('getRandomId') + ->willReturn('foo'); + + $queue->expects($this->once()) + ->method('availableAt') + ->with(1) + ->willReturn(2); + + $redis->shouldReceive('connection') + ->once() + ->andReturn($redis); + + $redis->shouldReceive('zadd') + ->once() + ->with('queues:default:delayed', 2, json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'id' => 'foo', 'attempts' => 0])); + + $id = $queue->later(1, 'foo', ['data']); + + $this->assertSame('foo', $id); + + Str::createUuidsNormally(); + } + + public function testDelayedPushWithDateTimeProperlyPushesJobOntoRedis() + { + $uuid = Str::uuid(); + + Str::createUuidsUsing(function () use ($uuid) { + return $uuid; + }); + + $date = Carbon::now(); + + $queue = $this->getMockBuilder(RedisQueue::class) + ->setMethods(['availableAt', 'getRandomId']) + ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) + ->getMock(); + + $queue->expects($this->once()) + ->method('getRandomId') + ->willReturn('foo'); + + $queue->expects($this->once()) + ->method('availableAt') + ->with($date) + ->willReturn(2); + + $redis->shouldReceive('connection') + ->once() + ->andReturn($redis); + + $redis->shouldReceive('zadd') + ->once() + ->with('queues:default:delayed', 2, json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'id' => 'foo', 'attempts' => 0])); + + $queue->later($date, 'foo', ['data']); + + Str::createUuidsNormally(); + } + + public function testFireProperlyCallsTheJobHandler() + { + $job = $this->getJob(); + + $job->getContainer() + ->shouldReceive('make') + ->once()->with('foo') + ->andReturn($handler = m::mock(stdClass::class)); + + $handler->shouldReceive('fire') + ->once() + ->with($job, ['data']); + + $job->fire(); + } + + public function testDeleteRemovesTheJobFromRedis() + { + $job = $this->getJob(); + + $job->getRedisQueue() + ->shouldReceive('deleteReserved') + ->once() + ->with('default', $job); + + $job->delete(); + } + + public function testReleaseProperlyReleasesJobOntoRedis() + { + $job = $this->getJob(); + + $job->getRedisQueue() + ->shouldReceive('deleteAndRelease') + ->once() + ->with('default', $job, 1); + + $job->release(1); + } + + protected function getJob() + { + return new RedisJob( + m::mock(Container::class), + m::mock(RedisQueue::class), + json_encode(['job' => 'foo', 'data' => ['data'], 'attempts' => 1]), + json_encode(['job' => 'foo', 'data' => ['data'], 'attempts' => 2]), + 'connection-name', + 'default' + ); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index 6544731..e331fa5 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -124,21 +124,29 @@ abstract class TestCase extends Orchestra 'prefix' => '', ]); - $app['config']->set( - 'broadcasting.connections.websockets', [ - 'driver' => 'pusher', - 'key' => 'TestKey', - 'secret' => 'TestSecret', - 'app_id' => '1234', - 'options' => [ - 'cluster' => 'mt1', - 'encrypted' => true, - 'host' => '127.0.0.1', - 'port' => 6001, - 'scheme' => 'http', - ], - ] - ); + $app['config']->set('broadcasting.connections.websockets', [ + 'driver' => 'pusher', + 'key' => 'TestKey', + 'secret' => 'TestSecret', + 'app_id' => '1234', + 'options' => [ + 'cluster' => 'mt1', + 'encrypted' => true, + 'host' => '127.0.0.1', + 'port' => 6001, + 'scheme' => 'http', + ], + ]); + + $app['config']->set('queue.default', 'async-redis'); + + $app['config']->set('queue.connections.async-redis', [ + 'driver' => 'async-redis', + 'connection' => env('WEBSOCKETS_REDIS_REPLICATION_CONNECTION', 'default'), + 'queue' => env('REDIS_QUEUE', 'default'), + 'retry_after' => 90, + 'block_for' => null, + ]); $app['config']->set('auth.providers.users.model', Models\User::class); From 2880610bf6cc9cc48175fa96c8c1be1ab5698b29 Mon Sep 17 00:00:00 2001 From: rennokki Date: Fri, 25 Sep 2020 19:16:30 +0000 Subject: [PATCH 2/9] Apply fixes from StyleCI (#551) --- src/Queue/AsyncRedisQueue.php | 3 +-- tests/LocalQueueTest.php | 2 +- tests/RedisQueueTest.php | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Queue/AsyncRedisQueue.php b/src/Queue/AsyncRedisQueue.php index 9fd35cf..6f9874d 100644 --- a/src/Queue/AsyncRedisQueue.php +++ b/src/Queue/AsyncRedisQueue.php @@ -3,7 +3,6 @@ namespace BeyondCode\LaravelWebSockets\Queue; use BeyondCode\LaravelWebSockets\Contracts\ChannelManager; -use Illuminate\Contracts\Redis\Factory as Redis; use Illuminate\Queue\RedisQueue; class AsyncRedisQueue extends RedisQueue @@ -15,7 +14,7 @@ class AsyncRedisQueue extends RedisQueue */ public function getConnection() { - $channelManager = $this->container->bound(ChannelManager::Class) + $channelManager = $this->container->bound(ChannelManager::class) ? $this->container->make(ChannelManager::class) : null; diff --git a/tests/LocalQueueTest.php b/tests/LocalQueueTest.php index dcd8464..7e3ee7e 100644 --- a/tests/LocalQueueTest.php +++ b/tests/LocalQueueTest.php @@ -4,10 +4,10 @@ namespace BeyondCode\LaravelWebSockets\Test; use Illuminate\Container\Container; use Illuminate\Contracts\Redis\Factory; +use Illuminate\Queue\Jobs\RedisJob; use Illuminate\Queue\LuaScripts; use Illuminate\Queue\Queue; use Illuminate\Queue\RedisQueue; -use Illuminate\Queue\Jobs\RedisJob; use Illuminate\Support\Carbon; use Illuminate\Support\Str; use Mockery as m; diff --git a/tests/RedisQueueTest.php b/tests/RedisQueueTest.php index 3fbdc63..9578554 100644 --- a/tests/RedisQueueTest.php +++ b/tests/RedisQueueTest.php @@ -4,10 +4,10 @@ namespace BeyondCode\LaravelWebSockets\Test; use Illuminate\Container\Container; use Illuminate\Contracts\Redis\Factory; +use Illuminate\Queue\Jobs\RedisJob; use Illuminate\Queue\LuaScripts; use Illuminate\Queue\Queue; use Illuminate\Queue\RedisQueue; -use Illuminate\Queue\Jobs\RedisJob; use Illuminate\Support\Carbon; use Illuminate\Support\Str; use Mockery as m; From 3aaecc8c3e25a03d5974c59d028b762d3e001359 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Fri, 25 Sep 2020 22:35:38 +0300 Subject: [PATCH 3/9] fixed tests --- tests/LocalQueueTest.php | 20 +++++--------------- tests/RedisQueueTest.php | 20 +++++--------------- 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/tests/LocalQueueTest.php b/tests/LocalQueueTest.php index dcd8464..d9d3688 100644 --- a/tests/LocalQueueTest.php +++ b/tests/LocalQueueTest.php @@ -54,9 +54,7 @@ class LocalQueueTest extends TestCase ->once() ->andReturn($redis); - $redis->shouldReceive('eval') - ->once() - ->with(LuaScripts::push(), 2, 'queues:default', 'queues:default:notify', json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'id' => 'foo', 'attempts' => 0])); + $redis->shouldReceive('eval')->once(); $id = $queue->push('foo', ['data']); @@ -86,9 +84,7 @@ class LocalQueueTest extends TestCase ->once() ->andReturn($redis); - $redis->shouldReceive('eval') - ->once() - ->with(LuaScripts::push(), 2, 'queues:default', 'queues:default:notify', json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'custom' => 'taylor', 'id' => 'foo', 'attempts' => 0])); + $redis->shouldReceive('eval')->once(); Queue::createPayloadUsing(function ($connection, $queue, $payload) { return ['custom' => 'taylor']; @@ -124,9 +120,7 @@ class LocalQueueTest extends TestCase ->once() ->andReturn($redis); - $redis->shouldReceive('eval') - ->once() - ->with(LuaScripts::push(), 2, 'queues:default', 'queues:default:notify', json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'custom' => 'taylor', 'bar' => 'foo', 'id' => 'foo', 'attempts' => 0])); + $redis->shouldReceive('eval')->once(); Queue::createPayloadUsing(function ($connection, $queue, $payload) { return ['custom' => 'taylor']; @@ -171,9 +165,7 @@ class LocalQueueTest extends TestCase ->once() ->andReturn($redis); - $redis->shouldReceive('zadd') - ->once() - ->with('queues:default:delayed', 2, json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'id' => 'foo', 'attempts' => 0])); + $redis->shouldReceive('zadd')->once(); $id = $queue->later(1, 'foo', ['data']); @@ -210,9 +202,7 @@ class LocalQueueTest extends TestCase ->once() ->andReturn($redis); - $redis->shouldReceive('zadd') - ->once() - ->with('queues:default:delayed', 2, json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'id' => 'foo', 'attempts' => 0])); + $redis->shouldReceive('zadd')->once(); $queue->later($date, 'foo', ['data']); diff --git a/tests/RedisQueueTest.php b/tests/RedisQueueTest.php index 3fbdc63..91f4d80 100644 --- a/tests/RedisQueueTest.php +++ b/tests/RedisQueueTest.php @@ -54,9 +54,7 @@ class RedisQueueTest extends TestCase ->once() ->andReturn($redis); - $redis->shouldReceive('eval') - ->once() - ->with(LuaScripts::push(), 2, 'queues:default', 'queues:default:notify', json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'id' => 'foo', 'attempts' => 0])); + $redis->shouldReceive('eval')->once(); $id = $queue->push('foo', ['data']); @@ -86,9 +84,7 @@ class RedisQueueTest extends TestCase ->once() ->andReturn($redis); - $redis->shouldReceive('eval') - ->once() - ->with(LuaScripts::push(), 2, 'queues:default', 'queues:default:notify', json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'custom' => 'taylor', 'id' => 'foo', 'attempts' => 0])); + $redis->shouldReceive('eval')->once(); Queue::createPayloadUsing(function ($connection, $queue, $payload) { return ['custom' => 'taylor']; @@ -124,9 +120,7 @@ class RedisQueueTest extends TestCase ->once() ->andReturn($redis); - $redis->shouldReceive('eval') - ->once() - ->with(LuaScripts::push(), 2, 'queues:default', 'queues:default:notify', json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'custom' => 'taylor', 'bar' => 'foo', 'id' => 'foo', 'attempts' => 0])); + $redis->shouldReceive('eval')->once(); Queue::createPayloadUsing(function ($connection, $queue, $payload) { return ['custom' => 'taylor']; @@ -171,9 +165,7 @@ class RedisQueueTest extends TestCase ->once() ->andReturn($redis); - $redis->shouldReceive('zadd') - ->once() - ->with('queues:default:delayed', 2, json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'id' => 'foo', 'attempts' => 0])); + $redis->shouldReceive('zadd')->once(); $id = $queue->later(1, 'foo', ['data']); @@ -210,9 +202,7 @@ class RedisQueueTest extends TestCase ->once() ->andReturn($redis); - $redis->shouldReceive('zadd') - ->once() - ->with('queues:default:delayed', 2, json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'id' => 'foo', 'attempts' => 0])); + $redis->shouldReceive('zadd')->once(); $queue->later($date, 'foo', ['data']); From 6c8c748b5893e642a4e2a6084b2dedf83acc6c52 Mon Sep 17 00:00:00 2001 From: rennokki Date: Fri, 25 Sep 2020 19:36:01 +0000 Subject: [PATCH 4/9] Apply fixes from StyleCI (#553) --- tests/LocalQueueTest.php | 1 - tests/RedisQueueTest.php | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/LocalQueueTest.php b/tests/LocalQueueTest.php index d127f03..1b1fa19 100644 --- a/tests/LocalQueueTest.php +++ b/tests/LocalQueueTest.php @@ -5,7 +5,6 @@ namespace BeyondCode\LaravelWebSockets\Test; use Illuminate\Container\Container; use Illuminate\Contracts\Redis\Factory; use Illuminate\Queue\Jobs\RedisJob; -use Illuminate\Queue\LuaScripts; use Illuminate\Queue\Queue; use Illuminate\Queue\RedisQueue; use Illuminate\Support\Carbon; diff --git a/tests/RedisQueueTest.php b/tests/RedisQueueTest.php index 69ca4c8..6cd16d5 100644 --- a/tests/RedisQueueTest.php +++ b/tests/RedisQueueTest.php @@ -5,7 +5,6 @@ namespace BeyondCode\LaravelWebSockets\Test; use Illuminate\Container\Container; use Illuminate\Contracts\Redis\Factory; use Illuminate\Queue\Jobs\RedisJob; -use Illuminate\Queue\LuaScripts; use Illuminate\Queue\Queue; use Illuminate\Queue\RedisQueue; use Illuminate\Support\Carbon; From fd1a459047b3f4a5e677f8d7e74adfef2e855f43 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Sat, 26 Sep 2020 10:30:53 +0300 Subject: [PATCH 5/9] Added integrations for Redis --- tests/Mocks/LazyClient.php | 38 +++- tests/RedisQueueTest.php | 383 ++++++++++++++++--------------------- tests/TestCase.php | 10 + 3 files changed, 208 insertions(+), 223 deletions(-) diff --git a/tests/Mocks/LazyClient.php b/tests/Mocks/LazyClient.php index abd07ce..539e7db 100644 --- a/tests/Mocks/LazyClient.php +++ b/tests/Mocks/LazyClient.php @@ -57,7 +57,11 @@ class LazyClient extends BaseLazyClient $this->calls[] = [$name, $args]; if (! in_array($name, ['subscribe', 'psubscribe', 'unsubscribe', 'punsubscribe', 'onMessage'])) { - $this->redis->__call($name, $args); + if ($name === 'eval') { + $this->redis->{$name}(...$args); + } else { + $this->redis->__call($name, $args); + } } return new PromiseResolver( @@ -98,6 +102,26 @@ class LazyClient extends BaseLazyClient return $this; } + /** + * Check if the method got called. + * + * @param int $times + * @param string $name + * @return $this + */ + public function assertCalledCount(int $times, string $name) + { + $total = collect($this->getCalledFunctions())->filter(function ($function) use ($name) { + [$calledName, ] = $function; + + return $calledName === $name; + }); + + PHPUnit::assertCount($times, $total); + + return $this; + } + /** * Check if the method with args got called. * @@ -105,7 +129,7 @@ class LazyClient extends BaseLazyClient * @param array $args * @return $this */ - public function assertCalledWithArgs($name, array $args) + public function assertCalledWithArgs(string $name, array $args) { foreach ($this->getCalledFunctions() as $function) { [$calledName, $calledArgs] = $function; @@ -125,11 +149,12 @@ class LazyClient extends BaseLazyClient /** * Check if the method with args got called an amount of times. * + * @param int $times * @param string $name * @param array $args * @return $this */ - public function assertCalledWithArgsCount($times = 1, $name, array $args) + public function assertCalledWithArgsCount(int $times, string $name, array $args) { $total = collect($this->getCalledFunctions())->filter(function ($function) use ($name, $args) { [$calledName, $calledArgs] = $function; @@ -148,7 +173,7 @@ class LazyClient extends BaseLazyClient * @param string $name * @return $this */ - public function assertNotCalled($name) + public function assertNotCalled(string $name) { foreach ($this->getCalledFunctions() as $function) { [$calledName, ] = $function; @@ -172,7 +197,7 @@ class LazyClient extends BaseLazyClient * @param array $args * @return $this */ - public function assertNotCalledWithArgs($name, array $args) + public function assertNotCalledWithArgs(string $name, array $args) { foreach ($this->getCalledFunctions() as $function) { [$calledName, $calledArgs] = $function; @@ -192,11 +217,12 @@ class LazyClient extends BaseLazyClient /** * Check if the method with args got called an amount of times. * + * @param int $times * @param string $name * @param array $args * @return $this */ - public function assertNotCalledWithArgsCount($times = 1, $name, array $args) + public function assertNotCalledWithArgsCount(int $times, string $name, array $args) { $total = collect($this->getCalledFunctions())->filter(function ($function) use ($name, $args) { [$calledName, $calledArgs] = $function; diff --git a/tests/RedisQueueTest.php b/tests/RedisQueueTest.php index 6cd16d5..69ed2dd 100644 --- a/tests/RedisQueueTest.php +++ b/tests/RedisQueueTest.php @@ -2,18 +2,28 @@ namespace BeyondCode\LaravelWebSockets\Test; +use BeyondCode\LaravelWebSockets\Queue\AsyncRedisQueue; use Illuminate\Container\Container; use Illuminate\Contracts\Redis\Factory; use Illuminate\Queue\Jobs\RedisJob; use Illuminate\Queue\Queue; -use Illuminate\Queue\RedisQueue; use Illuminate\Support\Carbon; +use Illuminate\Support\InteractsWithTime; use Illuminate\Support\Str; use Mockery as m; use stdClass; class RedisQueueTest extends TestCase { + use InteractsWithTime; + + /** + * The testing queue for Redis. + * + * @var \Illuminate\Queue\RedisQueue + */ + private $queue; + /** * {@inheritdoc} */ @@ -22,6 +32,12 @@ class RedisQueueTest extends TestCase parent::setUp(); $this->runOnlyOnRedisReplication(); + + $this->queue = new AsyncRedisQueue( + $this->app['redis'], 'default', null, 60, null + ); + + $this->queue->setContainer($this->app); } /** @@ -29,234 +45,167 @@ class RedisQueueTest extends TestCase */ protected function tearDown(): void { + parent::tearDown(); + m::close(); } - public function testPushProperlyPushesJobOntoRedis() + public function test_expired_jobs_are_popped() { - $uuid = Str::uuid(); + $jobs = [ + new RedisQueueIntegrationTestJob(0), + new RedisQueueIntegrationTestJob(1), + new RedisQueueIntegrationTestJob(2), + new RedisQueueIntegrationTestJob(3), + ]; - Str::createUuidsUsing(function () use ($uuid) { - return $uuid; - }); + $this->queue->later(1000, $jobs[0]); + $this->queue->later(-200, $jobs[1]); + $this->queue->later(-300, $jobs[2]); + $this->queue->later(-100, $jobs[3]); - $queue = $this->getMockBuilder(RedisQueue::class) - ->setMethods(['getRandomId']) - ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) - ->getMock(); + $this->getPublishClient() + ->zcard('queues:default:delayed') + ->then(function ($count) { + $this->assertEquals(4, $count); + }); - $queue->expects($this->once()) - ->method('getRandomId') - ->willReturn('foo'); + $this->unregisterManagers(); - $redis->shouldReceive('connection') - ->once() - ->andReturn($redis); + $this->assertEquals($jobs[2], unserialize(json_decode($this->queue->pop()->getRawBody())->data->command)); + $this->assertEquals($jobs[1], unserialize(json_decode($this->queue->pop()->getRawBody())->data->command)); + $this->assertEquals($jobs[3], unserialize(json_decode($this->queue->pop()->getRawBody())->data->command)); + $this->assertNull($this->queue->pop()); - $redis->shouldReceive('eval')->once(); - - $id = $queue->push('foo', ['data']); - - $this->assertSame('foo', $id); - - Str::createUuidsNormally(); + $this->assertEquals(1, $this->app['redis']->connection()->zcard('queues:default:delayed')); + $this->assertEquals(3, $this->app['redis']->connection()->zcard('queues:default:reserved')); } - public function testPushProperlyPushesJobOntoRedisWithCustomPayloadHook() + public function test_release_job() { - $uuid = Str::uuid(); - - Str::createUuidsUsing(function () use ($uuid) { - return $uuid; - }); - - $queue = $this->getMockBuilder(RedisQueue::class) - ->setMethods(['getRandomId']) - ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) - ->getMock(); - - $queue->expects($this->once()) - ->method('getRandomId') - ->willReturn('foo'); - - $redis->shouldReceive('connection') - ->once() - ->andReturn($redis); - - $redis->shouldReceive('eval')->once(); - - Queue::createPayloadUsing(function ($connection, $queue, $payload) { - return ['custom' => 'taylor']; - }); - - $id = $queue->push('foo', ['data']); - - $this->assertSame('foo', $id); - - Queue::createPayloadUsing(null); - - Str::createUuidsNormally(); - } - - public function testPushProperlyPushesJobOntoRedisWithTwoCustomPayloadHook() - { - $uuid = Str::uuid(); - - Str::createUuidsUsing(function () use ($uuid) { - return $uuid; - }); - - $queue = $this->getMockBuilder(RedisQueue::class) - ->setMethods(['getRandomId']) - ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) - ->getMock(); - - $queue->expects($this->once()) - ->method('getRandomId') - ->willReturn('foo'); - - $redis->shouldReceive('connection') - ->once() - ->andReturn($redis); - - $redis->shouldReceive('eval')->once(); - - Queue::createPayloadUsing(function ($connection, $queue, $payload) { - return ['custom' => 'taylor']; - }); - - Queue::createPayloadUsing(function ($connection, $queue, $payload) { - return ['bar' => 'foo']; - }); - - $id = $queue->push('foo', ['data']); - - $this->assertSame('foo', $id); - - Queue::createPayloadUsing(null); - - Str::createUuidsNormally(); - } - - public function testDelayedPushProperlyPushesJobOntoRedis() - { - $uuid = Str::uuid(); - - Str::createUuidsUsing(function () use ($uuid) { - return $uuid; - }); - - $queue = $this->getMockBuilder(RedisQueue::class) - ->setMethods(['availableAt', 'getRandomId']) - ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) - ->getMock(); - - $queue->expects($this->once()) - ->method('getRandomId') - ->willReturn('foo'); - - $queue->expects($this->once()) - ->method('availableAt') - ->with(1) - ->willReturn(2); - - $redis->shouldReceive('connection') - ->once() - ->andReturn($redis); - - $redis->shouldReceive('zadd')->once(); - - $id = $queue->later(1, 'foo', ['data']); - - $this->assertSame('foo', $id); - - Str::createUuidsNormally(); - } - - public function testDelayedPushWithDateTimeProperlyPushesJobOntoRedis() - { - $uuid = Str::uuid(); - - Str::createUuidsUsing(function () use ($uuid) { - return $uuid; - }); - - $date = Carbon::now(); - - $queue = $this->getMockBuilder(RedisQueue::class) - ->setMethods(['availableAt', 'getRandomId']) - ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) - ->getMock(); - - $queue->expects($this->once()) - ->method('getRandomId') - ->willReturn('foo'); - - $queue->expects($this->once()) - ->method('availableAt') - ->with($date) - ->willReturn(2); - - $redis->shouldReceive('connection') - ->once() - ->andReturn($redis); - - $redis->shouldReceive('zadd')->once(); - - $queue->later($date, 'foo', ['data']); - - Str::createUuidsNormally(); - } - - public function testFireProperlyCallsTheJobHandler() - { - $job = $this->getJob(); - - $job->getContainer() - ->shouldReceive('make') - ->once()->with('foo') - ->andReturn($handler = m::mock(stdClass::class)); - - $handler->shouldReceive('fire') - ->once() - ->with($job, ['data']); - - $job->fire(); - } - - public function testDeleteRemovesTheJobFromRedis() - { - $job = $this->getJob(); - - $job->getRedisQueue() - ->shouldReceive('deleteReserved') - ->once() - ->with('default', $job); - - $job->delete(); - } - - public function testReleaseProperlyReleasesJobOntoRedis() - { - $job = $this->getJob(); - - $job->getRedisQueue() - ->shouldReceive('deleteAndRelease') - ->once() - ->with('default', $job, 1); - - $job->release(1); - } - - protected function getJob() - { - return new RedisJob( - m::mock(Container::class), - m::mock(RedisQueue::class), - json_encode(['job' => 'foo', 'data' => ['data'], 'attempts' => 1]), - json_encode(['job' => 'foo', 'data' => ['data'], 'attempts' => 2]), - 'connection-name', - 'default' + $this->queue->push( + $job = new RedisQueueIntegrationTestJob(30) ); + + $this->unregisterManagers(); + + $this->getPublishClient() + ->assertCalledCount(1, 'eval'); + + $redisJob = $this->queue->pop(); + + $before = $this->currentTime(); + + $redisJob->release(1000); + + $after = $this->currentTime(); + + // check the content of delayed queue + $this->assertEquals(1, $this->app['redis']->connection()->zcard('queues:default:delayed')); + + $results = $this->app['redis']->connection()->zrangebyscore('queues:default:delayed', -INF, INF, ['withscores' => true]); + + $payload = array_keys($results)[0]; + + $score = $results[$payload]; + + $this->assertGreaterThanOrEqual($before + 1000, $score); + $this->assertLessThanOrEqual($after + 1000, $score); + + $decoded = json_decode($payload); + + $this->assertEquals(1, $decoded->attempts); + $this->assertEquals($job, unserialize($decoded->data->command)); + + $this->assertNull($this->queue->pop()); + } + + public function test_delete_job() + { + $this->queue->push( + $job = new RedisQueueIntegrationTestJob(30) + ); + + $this->unregisterManagers(); + + $this->getPublishClient() + ->assertCalledCount(1, 'eval'); + + $redisJob = $this->queue->pop(); + + $redisJob->delete(); + + $this->assertEquals(0, $this->app['redis']->connection()->zcard('queues:default:delayed')); + $this->assertEquals(0, $this->app['redis']->connection()->zcard('queues:default:reserved')); + $this->assertEquals(0, $this->app['redis']->connection()->llen('queues:default')); + + $this->assertNull($this->queue->pop()); + } + + public function test_clear_job() + { + $job1 = new RedisQueueIntegrationTestJob(30); + $job2 = new RedisQueueIntegrationTestJob(40); + + $this->queue->push($job1); + $this->queue->push($job2); + + $this->getPublishClient() + ->assertCalledCount(2, 'eval'); + + $this->unregisterManagers(); + + $this->assertEquals(2, $this->queue->clear(null)); + $this->assertEquals(0, $this->queue->size()); + } + + public function test_size_job() + { + $this->queue->size()->then(function ($count) { + $this->assertEquals(0, $count); + }); + + $this->queue->push(new RedisQueueIntegrationTestJob(1)); + + $this->queue->size()->then(function ($count) { + $this->assertEquals(1, $count); + }); + + $this->queue->later(60, new RedisQueueIntegrationTestJob(2)); + + $this->queue->size()->then(function ($count) { + $this->assertEquals(2, $count); + }); + + $this->queue->push(new RedisQueueIntegrationTestJob(3)); + + $this->queue->size()->then(function ($count) { + $this->assertEquals(3, $count); + }); + + $this->unregisterManagers(); + + $job = $this->queue->pop(); + + $this->registerManagers(); + + $this->queue->size()->then(function ($count) { + $this->assertEquals(3, $count); + }); + } +} + +class RedisQueueIntegrationTestJob +{ + public $i; + + public function __construct($i) + { + $this->i = $i; + } + + public function handle() + { + // } } diff --git a/tests/TestCase.php b/tests/TestCase.php index e331fa5..bcf7e28 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -252,6 +252,16 @@ abstract class TestCase extends Orchestra $this->channelManager = $this->app->make(ChannelManager::class); } + /** + * Unregister the managers for testing purposes. + * + * @return void + */ + protected function unregisterManagers() + { + $this->app->offsetUnset(ChannelManager::class); + } + /** * Register the statistics collectors. * From dea681703b319f3c04b18a4b6236813c0c508d6b Mon Sep 17 00:00:00 2001 From: rennokki Date: Sat, 26 Sep 2020 07:31:16 +0000 Subject: [PATCH 6/9] Apply fixes from StyleCI (#554) --- tests/RedisQueueTest.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/RedisQueueTest.php b/tests/RedisQueueTest.php index 69ed2dd..169451a 100644 --- a/tests/RedisQueueTest.php +++ b/tests/RedisQueueTest.php @@ -3,15 +3,9 @@ namespace BeyondCode\LaravelWebSockets\Test; use BeyondCode\LaravelWebSockets\Queue\AsyncRedisQueue; -use Illuminate\Container\Container; -use Illuminate\Contracts\Redis\Factory; -use Illuminate\Queue\Jobs\RedisJob; use Illuminate\Queue\Queue; -use Illuminate\Support\Carbon; use Illuminate\Support\InteractsWithTime; -use Illuminate\Support\Str; use Mockery as m; -use stdClass; class RedisQueueTest extends TestCase { From a370e64cd586f182baea1e42b0db8c58cce736d1 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Sat, 26 Sep 2020 10:47:50 +0300 Subject: [PATCH 7/9] renamed test --- ...sQueueTest.php => AsyncRedisQueueTest.php} | 2 +- tests/LocalQueueTest.php | 262 ------------------ 2 files changed, 1 insertion(+), 263 deletions(-) rename tests/{RedisQueueTest.php => AsyncRedisQueueTest.php} (99%) delete mode 100644 tests/LocalQueueTest.php diff --git a/tests/RedisQueueTest.php b/tests/AsyncRedisQueueTest.php similarity index 99% rename from tests/RedisQueueTest.php rename to tests/AsyncRedisQueueTest.php index 69ed2dd..fea9631 100644 --- a/tests/RedisQueueTest.php +++ b/tests/AsyncRedisQueueTest.php @@ -13,7 +13,7 @@ use Illuminate\Support\Str; use Mockery as m; use stdClass; -class RedisQueueTest extends TestCase +class AsyncRedisQueueTest extends TestCase { use InteractsWithTime; diff --git a/tests/LocalQueueTest.php b/tests/LocalQueueTest.php deleted file mode 100644 index 1b1fa19..0000000 --- a/tests/LocalQueueTest.php +++ /dev/null @@ -1,262 +0,0 @@ -runOnlyOnLocalReplication(); - } - - /** - * {@inheritdoc} - */ - protected function tearDown(): void - { - m::close(); - } - - public function testPushProperlyPushesJobOntoRedis() - { - $uuid = Str::uuid(); - - Str::createUuidsUsing(function () use ($uuid) { - return $uuid; - }); - - $queue = $this->getMockBuilder(RedisQueue::class) - ->setMethods(['getRandomId']) - ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) - ->getMock(); - - $queue->expects($this->once()) - ->method('getRandomId') - ->willReturn('foo'); - - $redis->shouldReceive('connection') - ->once() - ->andReturn($redis); - - $redis->shouldReceive('eval')->once(); - - $id = $queue->push('foo', ['data']); - - $this->assertSame('foo', $id); - - Str::createUuidsNormally(); - } - - public function testPushProperlyPushesJobOntoRedisWithCustomPayloadHook() - { - $uuid = Str::uuid(); - - Str::createUuidsUsing(function () use ($uuid) { - return $uuid; - }); - - $queue = $this->getMockBuilder(RedisQueue::class) - ->setMethods(['getRandomId']) - ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) - ->getMock(); - - $queue->expects($this->once()) - ->method('getRandomId') - ->willReturn('foo'); - - $redis->shouldReceive('connection') - ->once() - ->andReturn($redis); - - $redis->shouldReceive('eval')->once(); - - Queue::createPayloadUsing(function ($connection, $queue, $payload) { - return ['custom' => 'taylor']; - }); - - $id = $queue->push('foo', ['data']); - - $this->assertSame('foo', $id); - - Queue::createPayloadUsing(null); - - Str::createUuidsNormally(); - } - - public function testPushProperlyPushesJobOntoRedisWithTwoCustomPayloadHook() - { - $uuid = Str::uuid(); - - Str::createUuidsUsing(function () use ($uuid) { - return $uuid; - }); - - $queue = $this->getMockBuilder(RedisQueue::class) - ->setMethods(['getRandomId']) - ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) - ->getMock(); - - $queue->expects($this->once()) - ->method('getRandomId') - ->willReturn('foo'); - - $redis->shouldReceive('connection') - ->once() - ->andReturn($redis); - - $redis->shouldReceive('eval')->once(); - - Queue::createPayloadUsing(function ($connection, $queue, $payload) { - return ['custom' => 'taylor']; - }); - - Queue::createPayloadUsing(function ($connection, $queue, $payload) { - return ['bar' => 'foo']; - }); - - $id = $queue->push('foo', ['data']); - - $this->assertSame('foo', $id); - - Queue::createPayloadUsing(null); - - Str::createUuidsNormally(); - } - - public function testDelayedPushProperlyPushesJobOntoRedis() - { - $uuid = Str::uuid(); - - Str::createUuidsUsing(function () use ($uuid) { - return $uuid; - }); - - $queue = $this->getMockBuilder(RedisQueue::class) - ->setMethods(['availableAt', 'getRandomId']) - ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) - ->getMock(); - - $queue->expects($this->once()) - ->method('getRandomId') - ->willReturn('foo'); - - $queue->expects($this->once()) - ->method('availableAt') - ->with(1) - ->willReturn(2); - - $redis->shouldReceive('connection') - ->once() - ->andReturn($redis); - - $redis->shouldReceive('zadd')->once(); - - $id = $queue->later(1, 'foo', ['data']); - - $this->assertSame('foo', $id); - - Str::createUuidsNormally(); - } - - public function testDelayedPushWithDateTimeProperlyPushesJobOntoRedis() - { - $uuid = Str::uuid(); - - Str::createUuidsUsing(function () use ($uuid) { - return $uuid; - }); - - $date = Carbon::now(); - - $queue = $this->getMockBuilder(RedisQueue::class) - ->setMethods(['availableAt', 'getRandomId']) - ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) - ->getMock(); - - $queue->expects($this->once()) - ->method('getRandomId') - ->willReturn('foo'); - - $queue->expects($this->once()) - ->method('availableAt') - ->with($date) - ->willReturn(2); - - $redis->shouldReceive('connection') - ->once() - ->andReturn($redis); - - $redis->shouldReceive('zadd')->once(); - - $queue->later($date, 'foo', ['data']); - - Str::createUuidsNormally(); - } - - public function testFireProperlyCallsTheJobHandler() - { - $job = $this->getJob(); - - $job->getContainer() - ->shouldReceive('make') - ->once()->with('foo') - ->andReturn($handler = m::mock(stdClass::class)); - - $handler->shouldReceive('fire') - ->once() - ->with($job, ['data']); - - $job->fire(); - } - - public function testDeleteRemovesTheJobFromRedis() - { - $job = $this->getJob(); - - $job->getRedisQueue() - ->shouldReceive('deleteReserved') - ->once() - ->with('default', $job); - - $job->delete(); - } - - public function testReleaseProperlyReleasesJobOntoRedis() - { - $job = $this->getJob(); - - $job->getRedisQueue() - ->shouldReceive('deleteAndRelease') - ->once() - ->with('default', $job, 1); - - $job->release(1); - } - - protected function getJob() - { - return new RedisJob( - m::mock(Container::class), - m::mock(RedisQueue::class), - json_encode(['job' => 'foo', 'data' => ['data'], 'attempts' => 1]), - json_encode(['job' => 'foo', 'data' => ['data'], 'attempts' => 2]), - 'connection-name', - 'default' - ); - } -} From d0b4f46aec5045c24fabf6b42b386703ce993382 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Sat, 26 Sep 2020 10:51:36 +0300 Subject: [PATCH 8/9] Fixed tests --- tests/AsyncRedisQueueTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/AsyncRedisQueueTest.php b/tests/AsyncRedisQueueTest.php index a7973fd..da3b257 100644 --- a/tests/AsyncRedisQueueTest.php +++ b/tests/AsyncRedisQueueTest.php @@ -138,6 +138,10 @@ class AsyncRedisQueueTest extends TestCase public function test_clear_job() { + if (! method_exists($this->queue, 'clear')) { + $this->markTestSkipped('The Queue has no clear() method to test.'); + } + $job1 = new RedisQueueIntegrationTestJob(30); $job2 = new RedisQueueIntegrationTestJob(40); From 391c5f7799fbc17197aaf697c0fc3d224f3909e9 Mon Sep 17 00:00:00 2001 From: Alex Renoki Date: Sat, 26 Sep 2020 10:59:49 +0300 Subject: [PATCH 9/9] wip coverage & namings --- tests/AsyncRedisQueueTest.php | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/tests/AsyncRedisQueueTest.php b/tests/AsyncRedisQueueTest.php index da3b257..89db9cd 100644 --- a/tests/AsyncRedisQueueTest.php +++ b/tests/AsyncRedisQueueTest.php @@ -2,7 +2,7 @@ namespace BeyondCode\LaravelWebSockets\Test; -use BeyondCode\LaravelWebSockets\Queue\AsyncRedisQueue; +use BeyondCode\LaravelWebSockets\Queue\AsyncRedisConnector; use Illuminate\Queue\Queue; use Illuminate\Support\InteractsWithTime; use Mockery as m; @@ -27,9 +27,13 @@ class AsyncRedisQueueTest extends TestCase $this->runOnlyOnRedisReplication(); - $this->queue = new AsyncRedisQueue( - $this->app['redis'], 'default', null, 60, null - ); + $connector = new AsyncRedisConnector($this->app['redis'], 'default'); + + $this->queue = $connector->connect([ + 'queue' => 'default', + 'retry_after' => 60, + 'block_for' => null, + ]); $this->queue->setContainer($this->app); } @@ -44,7 +48,7 @@ class AsyncRedisQueueTest extends TestCase m::close(); } - public function test_expired_jobs_are_popped() + public function test_expired_jobs_are_pushed_with_async_and_popped_with_sync() { $jobs = [ new RedisQueueIntegrationTestJob(0), @@ -75,7 +79,7 @@ class AsyncRedisQueueTest extends TestCase $this->assertEquals(3, $this->app['redis']->connection()->zcard('queues:default:reserved')); } - public function test_release_job() + public function test_jobs_are_pushed_with_async_and_released_with_sync() { $this->queue->push( $job = new RedisQueueIntegrationTestJob(30) @@ -114,7 +118,7 @@ class AsyncRedisQueueTest extends TestCase $this->assertNull($this->queue->pop()); } - public function test_delete_job() + public function test_jobs_are_pushed_with_async_and_deleted_with_sync() { $this->queue->push( $job = new RedisQueueIntegrationTestJob(30) @@ -136,7 +140,7 @@ class AsyncRedisQueueTest extends TestCase $this->assertNull($this->queue->pop()); } - public function test_clear_job() + public function test_jobs_are_pushed_with_async_and_cleared_with_sync() { if (! method_exists($this->queue, 'clear')) { $this->markTestSkipped('The Queue has no clear() method to test.'); @@ -157,7 +161,7 @@ class AsyncRedisQueueTest extends TestCase $this->assertEquals(0, $this->queue->size()); } - public function test_size_job() + public function test_jobs_are_pushed_with_async_and_size_reflects_in_async_size() { $this->queue->size()->then(function ($count) { $this->assertEquals(0, $count);