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 06a9d11e4e..33c4550f44 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 9dcd4f6826..9bb34b4d34 100644 --- a/config/websockets.php +++ b/config/websockets.php @@ -137,7 +137,7 @@ '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 4f6383583b..86759db300 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 c099bbfeb2..51a6d59e88 100644 --- a/src/ChannelManagers/RedisChannelManager.php +++ b/src/ChannelManagers/RedisChannelManager.php @@ -519,6 +519,16 @@ public function getPublishClient() 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 0000000000..ac730c306c --- /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 0000000000..9fd35cfd14 --- /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 e498c11934..f513caa473 100644 --- a/src/WebSocketsServiceProvider.php +++ b/src/WebSocketsServiceProvider.php @@ -11,6 +11,7 @@ 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 @@ public function boot() __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 @@ public function boot() */ 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 0000000000..dcd8464c82 --- /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 0000000000..3fbdc63361 --- /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 65447315d0..e331fa5b90 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -124,21 +124,29 @@ public function getEnvironmentSetUp($app) '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 9fd35cfd14..6f9874da29 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 dcd8464c82..7e3ee7e1d8 100644 --- a/tests/LocalQueueTest.php +++ b/tests/LocalQueueTest.php @@ -4,10 +4,10 @@ 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 3fbdc63361..9578554e2e 100644 --- a/tests/RedisQueueTest.php +++ b/tests/RedisQueueTest.php @@ -4,10 +4,10 @@ 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 dcd8464c82..d9d36888b9 100644 --- a/tests/LocalQueueTest.php +++ b/tests/LocalQueueTest.php @@ -54,9 +54,7 @@ public function testPushProperlyPushesJobOntoRedis() ->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 @@ public function testPushProperlyPushesJobOntoRedisWithCustomPayloadHook() ->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 @@ public function testPushProperlyPushesJobOntoRedisWithTwoCustomPayloadHook() ->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 @@ public function testDelayedPushProperlyPushesJobOntoRedis() ->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 @@ public function testDelayedPushWithDateTimeProperlyPushesJobOntoRedis() ->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 3fbdc63361..91f4d8003e 100644 --- a/tests/RedisQueueTest.php +++ b/tests/RedisQueueTest.php @@ -54,9 +54,7 @@ public function testPushProperlyPushesJobOntoRedis() ->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 @@ public function testPushProperlyPushesJobOntoRedisWithCustomPayloadHook() ->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 @@ public function testPushProperlyPushesJobOntoRedisWithTwoCustomPayloadHook() ->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 @@ public function testDelayedPushProperlyPushesJobOntoRedis() ->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 @@ public function testDelayedPushWithDateTimeProperlyPushesJobOntoRedis() ->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 d127f034f3..1b1fa19efd 100644 --- a/tests/LocalQueueTest.php +++ b/tests/LocalQueueTest.php @@ -5,7 +5,6 @@ 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 69ca4c8779..6cd16d536e 100644 --- a/tests/RedisQueueTest.php +++ b/tests/RedisQueueTest.php @@ -5,7 +5,6 @@ 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 | 297 +++++++++++++++---------------------- tests/TestCase.php | 10 ++ 3 files changed, 165 insertions(+), 180 deletions(-) diff --git a/tests/Mocks/LazyClient.php b/tests/Mocks/LazyClient.php index abd07ced3e..539e7db413 100644 --- a/tests/Mocks/LazyClient.php +++ b/tests/Mocks/LazyClient.php @@ -57,7 +57,11 @@ public function __call($name, $args) $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 @@ public function assertCalled($name) 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 @@ public function assertCalled($name) * @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 @@ public function assertCalledWithArgs($name, array $args) /** * 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 @@ public function assertCalledWithArgsCount($times = 1, $name, array $args) * @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 @@ public function assertNotCalled($name) * @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 @@ public function assertNotCalledWithArgs($name, array $args) /** * 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 6cd16d536e..69ed2dd9ba 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 @@ public function setUp(): void parent::setUp(); $this->runOnlyOnRedisReplication(); + + $this->queue = new AsyncRedisQueue( + $this->app['redis'], 'default', null, 60, null + ); + + $this->queue->setContainer($this->app); } /** @@ -29,234 +45,167 @@ public function setUp(): void */ protected function tearDown(): void { + parent::tearDown(); + m::close(); } - public function testPushProperlyPushesJobOntoRedis() + public function test_expired_jobs_are_popped() { - $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); + $jobs = [ + new RedisQueueIntegrationTestJob(0), + new RedisQueueIntegrationTestJob(1), + new RedisQueueIntegrationTestJob(2), + new RedisQueueIntegrationTestJob(3), + ]; + + $this->queue->later(1000, $jobs[0]); + $this->queue->later(-200, $jobs[1]); + $this->queue->later(-300, $jobs[2]); + $this->queue->later(-100, $jobs[3]); + + $this->getPublishClient() + ->zcard('queues:default:delayed') + ->then(function ($count) { + $this->assertEquals(4, $count); + }); + + $this->unregisterManagers(); + + $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()); + + $this->assertEquals(1, $this->app['redis']->connection()->zcard('queues:default:delayed')); + $this->assertEquals(3, $this->app['redis']->connection()->zcard('queues:default:reserved')); + } - $redis->shouldReceive('eval')->once(); + public function test_release_job() + { + $this->queue->push( + $job = new RedisQueueIntegrationTestJob(30) + ); - $id = $queue->push('foo', ['data']); + $this->unregisterManagers(); - $this->assertSame('foo', $id); + $this->getPublishClient() + ->assertCalledCount(1, 'eval'); - Str::createUuidsNormally(); - } + $redisJob = $this->queue->pop(); - public function testPushProperlyPushesJobOntoRedisWithCustomPayloadHook() - { - $uuid = Str::uuid(); + $before = $this->currentTime(); - Str::createUuidsUsing(function () use ($uuid) { - return $uuid; - }); + $redisJob->release(1000); - $queue = $this->getMockBuilder(RedisQueue::class) - ->setMethods(['getRandomId']) - ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) - ->getMock(); + $after = $this->currentTime(); - $queue->expects($this->once()) - ->method('getRandomId') - ->willReturn('foo'); + // check the content of delayed queue + $this->assertEquals(1, $this->app['redis']->connection()->zcard('queues:default:delayed')); - $redis->shouldReceive('connection') - ->once() - ->andReturn($redis); + $results = $this->app['redis']->connection()->zrangebyscore('queues:default:delayed', -INF, INF, ['withscores' => true]); - $redis->shouldReceive('eval')->once(); + $payload = array_keys($results)[0]; - Queue::createPayloadUsing(function ($connection, $queue, $payload) { - return ['custom' => 'taylor']; - }); + $score = $results[$payload]; - $id = $queue->push('foo', ['data']); + $this->assertGreaterThanOrEqual($before + 1000, $score); + $this->assertLessThanOrEqual($after + 1000, $score); - $this->assertSame('foo', $id); + $decoded = json_decode($payload); - Queue::createPayloadUsing(null); + $this->assertEquals(1, $decoded->attempts); + $this->assertEquals($job, unserialize($decoded->data->command)); - Str::createUuidsNormally(); + $this->assertNull($this->queue->pop()); } - public function testPushProperlyPushesJobOntoRedisWithTwoCustomPayloadHook() + public function test_delete_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'); + $this->queue->push( + $job = new RedisQueueIntegrationTestJob(30) + ); - $redis->shouldReceive('connection') - ->once() - ->andReturn($redis); + $this->unregisterManagers(); - $redis->shouldReceive('eval')->once(); + $this->getPublishClient() + ->assertCalledCount(1, 'eval'); - Queue::createPayloadUsing(function ($connection, $queue, $payload) { - return ['custom' => 'taylor']; - }); + $redisJob = $this->queue->pop(); - Queue::createPayloadUsing(function ($connection, $queue, $payload) { - return ['bar' => 'foo']; - }); + $redisJob->delete(); - $id = $queue->push('foo', ['data']); + $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->assertSame('foo', $id); - - Queue::createPayloadUsing(null); - - Str::createUuidsNormally(); + $this->assertNull($this->queue->pop()); } - public function testDelayedPushProperlyPushesJobOntoRedis() + public function test_clear_job() { - $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); + $job1 = new RedisQueueIntegrationTestJob(30); + $job2 = new RedisQueueIntegrationTestJob(40); - $redis->shouldReceive('connection') - ->once() - ->andReturn($redis); + $this->queue->push($job1); + $this->queue->push($job2); - $redis->shouldReceive('zadd')->once(); + $this->getPublishClient() + ->assertCalledCount(2, 'eval'); - $id = $queue->later(1, 'foo', ['data']); + $this->unregisterManagers(); - $this->assertSame('foo', $id); - - Str::createUuidsNormally(); + $this->assertEquals(2, $this->queue->clear(null)); + $this->assertEquals(0, $this->queue->size()); } - public function testDelayedPushWithDateTimeProperlyPushesJobOntoRedis() + public function test_size_job() { - $uuid = Str::uuid(); - - Str::createUuidsUsing(function () use ($uuid) { - return $uuid; + $this->queue->size()->then(function ($count) { + $this->assertEquals(0, $count); }); - $date = Carbon::now(); - - $queue = $this->getMockBuilder(RedisQueue::class) - ->setMethods(['availableAt', 'getRandomId']) - ->setConstructorArgs([$redis = m::mock(Factory::class), 'default']) - ->getMock(); + $this->queue->push(new RedisQueueIntegrationTestJob(1)); - $queue->expects($this->once()) - ->method('getRandomId') - ->willReturn('foo'); - - $queue->expects($this->once()) - ->method('availableAt') - ->with($date) - ->willReturn(2); + $this->queue->size()->then(function ($count) { + $this->assertEquals(1, $count); + }); - $redis->shouldReceive('connection') - ->once() - ->andReturn($redis); + $this->queue->later(60, new RedisQueueIntegrationTestJob(2)); - $redis->shouldReceive('zadd')->once(); + $this->queue->size()->then(function ($count) { + $this->assertEquals(2, $count); + }); - $queue->later($date, 'foo', ['data']); + $this->queue->push(new RedisQueueIntegrationTestJob(3)); - Str::createUuidsNormally(); - } + $this->queue->size()->then(function ($count) { + $this->assertEquals(3, $count); + }); - public function testFireProperlyCallsTheJobHandler() - { - $job = $this->getJob(); + $this->unregisterManagers(); - $job->getContainer() - ->shouldReceive('make') - ->once()->with('foo') - ->andReturn($handler = m::mock(stdClass::class)); + $job = $this->queue->pop(); - $handler->shouldReceive('fire') - ->once() - ->with($job, ['data']); + $this->registerManagers(); - $job->fire(); + $this->queue->size()->then(function ($count) { + $this->assertEquals(3, $count); + }); } +} - public function testDeleteRemovesTheJobFromRedis() - { - $job = $this->getJob(); - - $job->getRedisQueue() - ->shouldReceive('deleteReserved') - ->once() - ->with('default', $job); - - $job->delete(); - } +class RedisQueueIntegrationTestJob +{ + public $i; - public function testReleaseProperlyReleasesJobOntoRedis() + public function __construct($i) { - $job = $this->getJob(); - - $job->getRedisQueue() - ->shouldReceive('deleteAndRelease') - ->once() - ->with('default', $job, 1); - - $job->release(1); + $this->i = $i; } - protected function getJob() + public function handle() { - 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 e331fa5b90..bcf7e287d6 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -252,6 +252,16 @@ protected function registerManagers() $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 69ed2dd9ba..169451a3e2 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 69ed2dd9ba..fea96319ca 100644 --- a/tests/RedisQueueTest.php +++ b/tests/AsyncRedisQueueTest.php @@ -13,7 +13,7 @@ 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 1b1fa19efd..0000000000 --- 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 a7973fd339..da3b2575af 100644 --- a/tests/AsyncRedisQueueTest.php +++ b/tests/AsyncRedisQueueTest.php @@ -138,6 +138,10 @@ public function test_delete_job() 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 da3b2575af..89db9cd4f5 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 @@ public function setUp(): void $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 @@ protected function tearDown(): void 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 @@ public function test_expired_jobs_are_popped() $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 @@ public function test_release_job() $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 @@ public function test_delete_job() $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 @@ public function test_clear_job() $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);