getMockBuilder(DatabaseQueue::class)->onlyMethods(['currentTime'])->setConstructorArgs([$database = m::mock(Connection::class), 'table', 'default'])->getMock(); $queue->expects($this->any())->method('currentTime')->willReturn('time'); $queue->setContainer($container = m::spy(Container::class)); $database->shouldReceive('table')->with('table')->andReturn($query = m::mock(stdClass::class)); $query->shouldReceive('insertGetId')->once()->andReturnUsing(function ($array) use ($uuid, $displayNameStartsWith, $jobStartsWith) { $payload = json_decode($array['payload'], true); $this->assertSame($uuid, $payload['uuid']); $this->assertStringContainsString($displayNameStartsWith, $payload['displayName']); $this->assertStringContainsString($jobStartsWith, $payload['job']); $this->assertSame('default', $array['queue']); $this->assertEquals(0, $array['attempts']); $this->assertNull($array['reserved_at']); $this->assertIsInt($array['available_at']); }); $queue->push($job, ['data']); $container->shouldHaveReceived('bound')->with('events')->twice(); Str::createUuidsNormally(); } public static function pushJobsDataProvider() { $uuid = Str::uuid()->toString(); return [ [$uuid, new MyTestJob, 'MyTestJob', 'CallQueuedHandler'], [$uuid, fn () => 0, 'Closure', 'CallQueuedHandler'], [$uuid, 'foo', 'foo', 'foo'], ]; } public function testDelayedPushProperlyPushesJobOntoDatabase() { $uuid = Str::uuid(); Str::createUuidsUsing(function () use ($uuid) { return $uuid; }); $time = Carbon::now(); Carbon::setTestNow($time); $queue = $this->getMockBuilder(DatabaseQueue::class) ->onlyMethods(['currentTime']) ->setConstructorArgs([$database = m::mock(Connection::class), 'table', 'default']) ->getMock(); $queue->expects($this->any())->method('currentTime')->willReturn('time'); $queue->setContainer($container = m::spy(Container::class)); $database->shouldReceive('table')->with('table')->andReturn($query = m::mock(stdClass::class)); $query->shouldReceive('insertGetId')->once()->andReturnUsing(function ($array) use ($uuid, $time) { $this->assertSame('default', $array['queue']); $this->assertSame(json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'failOnTimeout' => false, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'createdAt' => $time->getTimestamp(), 'delay' => 10]), $array['payload']); $this->assertEquals(0, $array['attempts']); $this->assertNull($array['reserved_at']); $this->assertIsInt($array['available_at']); }); $queue->later(10, 'foo', ['data']); $container->shouldHaveReceived('bound')->with('events')->twice(); Carbon::setTestNow(); Str::createUuidsNormally(); } public function testPushIncludesBatchIdInPayloadForBatchableJob() { $uuid = Str::uuid()->toString(); Str::createUuidsUsing(function () use ($uuid) { return $uuid; }); $job = (new MyBatchableJob)->withBatchId('test-batch-id'); $queue = $this->getMockBuilder(DatabaseQueue::class)->onlyMethods(['currentTime'])->setConstructorArgs([$database = m::mock(Connection::class), 'table', 'default'])->getMock(); $queue->expects($this->any())->method('currentTime')->willReturn('time'); $queue->setContainer($container = m::spy(Container::class)); $database->shouldReceive('table')->with('table')->andReturn($query = m::mock(stdClass::class)); $query->shouldReceive('insertGetId')->once()->andReturnUsing(function ($array) { $payload = json_decode($array['payload'], true); $this->assertSame('test-batch-id', $payload['data']['batchId']); }); $queue->push($job, ['data']); $container->shouldHaveReceived('bound')->with('events')->twice(); Str::createUuidsNormally(); } public function testFailureToCreatePayloadFromObject() { $this->expectException('InvalidArgumentException'); $job = new stdClass; $job->invalid = "\xc3\x28"; $queue = m::mock(Queue::class)->makePartial(); $class = new ReflectionClass(Queue::class); $createPayload = $class->getMethod('createPayload'); $createPayload->invokeArgs($queue, [ $job, 'queue-name', ]); } public function testFailureToCreatePayloadFromArray() { $this->expectException('InvalidArgumentException'); $queue = m::mock(Queue::class)->makePartial(); $class = new ReflectionClass(Queue::class); $createPayload = $class->getMethod('createPayload'); $createPayload->invokeArgs($queue, [ ["\xc3\x28"], 'queue-name', ]); } public function testBulkBatchPushesOntoDatabase() { $uuid = Str::uuid(); Str::createUuidsUsing(function () use ($uuid) { return $uuid; }); $time = Carbon::now(); Carbon::setTestNow($time); $database = m::mock(Connection::class); $queue = $this->getMockBuilder(DatabaseQueue::class)->onlyMethods(['currentTime', 'availableAt'])->setConstructorArgs([$database, 'table', 'default'])->getMock(); $queue->expects($this->any())->method('currentTime')->willReturn('created'); $queue->expects($this->any())->method('availableAt')->willReturn('available'); $database->shouldReceive('table')->with('table')->andReturn($query = m::mock(stdClass::class)); $query->shouldReceive('insert')->once()->andReturnUsing(function ($records) use ($uuid, $time) { $this->assertEquals([[ 'queue' => 'queue', 'payload' => json_encode(['uuid' => $uuid, 'displayName' => 'foo', 'job' => 'foo', 'maxTries' => null, 'maxExceptions' => null, 'failOnTimeout' => false, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'createdAt' => $time->getTimestamp(), 'delay' => null]), 'attempts' => 0, 'reserved_at' => null, 'available_at' => 'available', 'created_at' => 'created', ], [ 'queue' => 'queue', 'payload' => json_encode(['uuid' => $uuid, 'displayName' => 'bar', 'job' => 'bar', 'maxTries' => null, 'maxExceptions' => null, 'failOnTimeout' => false, 'backoff' => null, 'timeout' => null, 'data' => ['data'], 'createdAt' => $time->getTimestamp(), 'delay' => null]), 'attempts' => 0, 'reserved_at' => null, 'available_at' => 'available', 'created_at' => 'created', ]], $records); }); $queue->bulk(['foo', 'bar'], ['data'], 'queue'); Carbon::setTestNow(); Str::createUuidsNormally(); } public function testBuildDatabaseRecordWithPayloadAtTheEnd() { $queue = m::mock(DatabaseQueue::class); $record = $queue->buildDatabaseRecord('queue', 'any_payload', 0); $this->assertArrayHasKey('payload', $record); $this->assertArrayHasKey('payload', array_slice($record, -1, 1, true)); } public function testGetLockForPoppingIsCached() { $database = m::mock(Connection::class); $queue = new DatabaseQueue($database, 'table', 'default'); $pdo = m::mock(\PDO::class); $pdo->shouldReceive('getAttribute')->with(\PDO::ATTR_DRIVER_NAME)->once()->andReturn('mysql'); $pdo->shouldReceive('getAttribute')->with(\PDO::ATTR_SERVER_VERSION)->once()->andReturn('8.0.36'); $database->shouldReceive('getPdo')->andReturn($pdo); $database->shouldReceive('getConfig')->with('version')->andReturn(null); $method = new \ReflectionMethod($queue, 'getLockForPopping'); $result1 = $method->invoke($queue); $result2 = $method->invoke($queue); $this->assertSame('FOR UPDATE SKIP LOCKED', $result1); $this->assertSame($result1, $result2); } } class MyTestJob { public function handle() { // ... } } class MyBatchableJob { use Batchable; }