diff --git a/.travis.yml b/.travis.yml index bc37b4a..331554b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,10 @@ language: php php: - - 5.6 - - 7.0 + - 7.1 matrix: fast_finish: true - allow_failures: - - php: 7.0 before_script: - composer install --no-interaction --dev --prefer-dist diff --git a/composer.json b/composer.json index 9104a49..ddd7195 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ } ], "require": { - "php": ">=5.6", + "php": ">=7.1", "illuminate/queue": "^5.3.24", "laravel-doctrine/orm": "^1.0" }, diff --git a/src/EntityManagerClosedException.php b/src/Exceptions/EntityManagerClosedException.php similarity index 65% rename from src/EntityManagerClosedException.php rename to src/Exceptions/EntityManagerClosedException.php index a7fabc2..135fa12 100644 --- a/src/EntityManagerClosedException.php +++ b/src/Exceptions/EntityManagerClosedException.php @@ -1,7 +1,7 @@ assertEntityManagerOpen(); } catch (EntityManagerClosedException $e) { if ($this->exceptions) { - $this->exceptions->report(new EntityManagerClosedException); + $this->exceptions->report($e); } return false; @@ -122,9 +125,7 @@ public function runNextJob($connectionName, $queue, WorkerOptions $options) $this->assertGoodDatabaseConnection(); try { - $job = $this->getNextJob( - $this->manager->connection($connectionName), $queue - ); + $job = $this->fetchNewJob($connectionName, $queue); // If we're able to pull a job off of the stack, we will process it and then return // from this method. If there is no job on the queue, we will "sleep" the worker @@ -137,20 +138,12 @@ public function runNextJob($connectionName, $queue, WorkerOptions $options) return true; } - } catch (Exception $e) { - if ($this->exceptions) { - $this->exceptions->report($e); - } - - if ($e instanceof QueueMustStop) { - return false; - } - } catch (Throwable $e) { + } catch (Exception | Throwable $e) { if ($this->exceptions) { $this->exceptions->report(new FatalThrowableError($e)); } - if ($e instanceof QueueMustStop) { + if ($e instanceof QueueMustStop || $e instanceof QueueFailure) { return false; } } @@ -186,4 +179,21 @@ private function assertGoodDatabaseConnection() $connection->connect(); } } + + /** + * @param string $connectionName + * @param null $queue + * @return \Illuminate\Contracts\Queue\Job|null + * @throws QueueFailureException + */ + private function fetchNewJob(string $connectionName, $queue): ?\Illuminate\Contracts\Queue\Job + { + try { + return $this->getNextJob( + $this->manager->connection($connectionName), $queue + ); + } catch (Exception | Throwable $e) { + throw new QueueFailureException($e); + } + } } diff --git a/tests/WorkerTest.php b/tests/WorkerTest.php index 4cc7267..9e8e3ef 100644 --- a/tests/WorkerTest.php +++ b/tests/WorkerTest.php @@ -14,11 +14,13 @@ use Illuminate\Queue\QueueManager; use Illuminate\Queue\Worker as IlluminateWorker; use Illuminate\Queue\WorkerOptions; -use MaxBrokman\SafeQueue\EntityManagerClosedException; +use MaxBrokman\SafeQueue\Exceptions\EntityManagerClosedException; +use MaxBrokman\SafeQueue\Exceptions\QueueFailureException; use MaxBrokman\SafeQueue\QueueMustStop; use MaxBrokman\SafeQueue\Stopper; use MaxBrokman\SafeQueue\Worker; use Mockery as m; +use Symfony\Component\Debug\Exception\FatalThrowableError; class WorkerTest extends \PHPUnit_Framework_TestCase { @@ -123,6 +125,15 @@ protected function prepareToRunJob($job) call_user_func_array([$popExpectation, 'andReturn'], $jobs); } + protected function prepareToRunJobFails() + { + $this->queueManager->shouldReceive('isDownForMaintenance')->andReturn(false); + $this->queueManager->shouldReceive('connection')->andReturn($this->queue); + $this->queueManager->shouldReceive('getName')->andReturn('test'); + + $this->queue->shouldReceive('pop')->andThrow(new QueueFailureException(new BadThingHappened())); + } + public function testExtendsLaravelWorker() { $this->assertInstanceOf(IlluminateWorker::class, $this->worker); @@ -160,7 +171,7 @@ public function testReconnected() // We must stop $this->stopper->shouldReceive('stop')->once(); // We must log this fact - $this->exceptions->shouldReceive('report')->with(m::type(BadThingHappened::class))->once(); + $this->exceptions->shouldReceive('report')->with(m::type(FatalThrowableError::class))->once(); // Make a job $job = m::mock(Job::class); @@ -176,8 +187,8 @@ public function testReconnected() public function testLoops() { // Entity manager will report open and good connection - $this->entityManager->shouldReceive('isOpen')->andReturn(true)->times(2); - $this->dbConnection->shouldReceive('ping')->andReturn(true)->times(2); + $this->entityManager->shouldReceive('isOpen')->andReturn(true)->times(3); + $this->dbConnection->shouldReceive('ping')->andReturn(true)->times(3); // We must stop $this->stopper->shouldReceive('stop')->once(); @@ -192,9 +203,9 @@ public function testLoops() $jobTwo->shouldReceive('fire')->once()->andThrow(new BadThingHappened()); $jobTwo->shouldIgnoreMissing(); - $this->exceptions->shouldReceive('report')->with(m::type(BadThingHappened::class))->once(); + $this->exceptions->shouldReceive('report')->with(m::type(FatalThrowableError::class))->once(); - $this->prepareToRunJob([$jobOne, $jobTwo]); + $this->prepareToRunJob([null, $jobOne, $jobTwo]); $this->worker->daemon('test', null, $this->options); } @@ -214,6 +225,25 @@ public function testRestartsNicely() $this->worker->daemon('test', null, $this->options); } + + public function testQueueFailure() + { + // Entity manager will report open and good connection + $this->entityManager->shouldReceive('isOpen')->andReturn(true)->times(1); + $this->dbConnection->shouldReceive('ping')->andReturn(true)->times(1); + + // We must stop + $this->stopper->shouldReceive('stop')->once(); + + // Make a job + $job = m::mock(Job::class); + + $this->exceptions->shouldReceive('report')->with(m::type(FatalThrowableError::class))->once(); + + $this->prepareToRunJobFails(); + + $this->worker->daemon('test', null, $this->options); + } } class BadThingHappened extends \Exception implements QueueMustStop