From b289d142870f46ce870b0b18030c5bdadd3be176 Mon Sep 17 00:00:00 2001 From: Michal Sniatala Date: Thu, 21 Dec 2023 14:26:58 +0100 Subject: [PATCH] tests: add base tests for commands (#27) * add base tests for commands * fix config load * disable phpstan-strict-rules in rector.php * Revert "disable phpstan-strict-rules in rector.php" This reverts commit 5c5d115e2aae806f320b824fcb0799b6e316907a. * add rector separately * update rector version in the workflow * add suggestions from the the code review --- .github/workflows/rector.yml | 2 +- phpunit.xml.dist | 3 +- rector.php | 7 +- src/Commands/QueueFailed.php | 10 ++- src/Commands/QueuePublish.php | 2 +- src/Commands/QueueStop.php | 2 +- tests/Commands/QueueClearTest.php | 40 +++++++++++ tests/Commands/QueueFailedTest.php | 51 ++++++++++++++ tests/Commands/QueueFlushTest.php | 89 +++++++++++++++++++++++ tests/Commands/QueueForgetTest.php | 62 ++++++++++++++++ tests/Commands/QueuePublishTest.php | 27 +++++++ tests/Commands/QueueRetryTest.php | 62 ++++++++++++++++ tests/Commands/QueueStopTest.php | 40 +++++++++++ tests/Commands/QueueWorkTest.php | 105 ++++++++++++++++++++++++++++ tests/_support/CLITestCase.php | 62 ++++++++++++++++ 15 files changed, 555 insertions(+), 9 deletions(-) create mode 100644 tests/Commands/QueueClearTest.php create mode 100644 tests/Commands/QueueFailedTest.php create mode 100644 tests/Commands/QueueFlushTest.php create mode 100644 tests/Commands/QueueForgetTest.php create mode 100644 tests/Commands/QueuePublishTest.php create mode 100644 tests/Commands/QueueRetryTest.php create mode 100644 tests/Commands/QueueStopTest.php create mode 100644 tests/Commands/QueueWorkTest.php create mode 100644 tests/_support/CLITestCase.php diff --git a/.github/workflows/rector.yml b/.github/workflows/rector.yml index f4482bf..4fc0e1d 100644 --- a/.github/workflows/rector.yml +++ b/.github/workflows/rector.yml @@ -62,5 +62,5 @@ jobs: - name: Analyze for refactoring run: | - composer global require --dev rector/rector:^0.15.1 + composer global require --dev rector/rector:^0.18.12 rector process --dry-run --no-progress-bar diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 470e815..8b5daca 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -24,7 +24,8 @@ ./src/ - ./src/Commands + ./src/Commands/Generators + ./src/Commands/Utils ./src/Config diff --git a/rector.php b/rector.php index 21a53f9..c8b9919 100644 --- a/rector.php +++ b/rector.php @@ -63,9 +63,10 @@ realpath(getcwd()) . '/vendor/codeigniter4/framework/system/Test/bootstrap.php', ]); - if (is_file(__DIR__ . '/phpstan.neon.dist')) { - $rectorConfig->phpstanConfig(__DIR__ . '/phpstan.neon.dist'); - } + $rectorConfig->phpstanConfigs([ + __DIR__ . '/phpstan.neon.dist', + __DIR__ . '/vendor/phpstan/phpstan-strict-rules/rules.neon', + ]); // Set the target version for refactoring $rectorConfig->phpVersion(PhpVersion::PHP_81); diff --git a/src/Commands/QueueFailed.php b/src/Commands/QueueFailed.php index 5a0b791..89c4749 100644 --- a/src/Commands/QueueFailed.php +++ b/src/Commands/QueueFailed.php @@ -56,7 +56,7 @@ public function run(array $params) $queue = $params['queue'] ?? CLI::getOption('queue'); /** @var QueueConfig $config */ - $config = config('queue'); + $config = config('Queue'); $results = service('queue')->listFailed($queue); @@ -64,7 +64,13 @@ public function run(array $params) $tbody = []; foreach ($results as $result) { - $tbody[] = [$result->id, $result->connection, $result->queue, $this->getClassName($result->payload['job'], $config), $result->failed_at]; + $tbody[] = [ + $result->id, + $result->connection, + $result->queue, + $this->getClassName($result->payload['job'], $config), + $result->failed_at, + ]; } CLI::table($tbody, $thead); diff --git a/src/Commands/QueuePublish.php b/src/Commands/QueuePublish.php index 4caddc5..2a7c1d1 100644 --- a/src/Commands/QueuePublish.php +++ b/src/Commands/QueuePublish.php @@ -13,7 +13,7 @@ class QueuePublish extends BaseCommand { protected $group = 'Queue'; protected $name = 'queue:publish'; - protected $description = 'Publish QueueJob config file into the current application.'; + protected $description = 'Publish Queue config file into the current application.'; public function run(array $params): void { diff --git a/src/Commands/QueueStop.php b/src/Commands/QueueStop.php index d9a7e87..27966a2 100644 --- a/src/Commands/QueueStop.php +++ b/src/Commands/QueueStop.php @@ -72,7 +72,7 @@ public function run(array $params) cache()->save($cacheName, $startTime, MINUTE * 10); - CLI::write('QueueJob will be stopped after the current job finish', 'yellow'); + CLI::write('Queue will be stopped after the current job finish', 'yellow'); return EXIT_SUCCESS; } diff --git a/tests/Commands/QueueClearTest.php b/tests/Commands/QueueClearTest.php new file mode 100644 index 0000000..300e3d9 --- /dev/null +++ b/tests/Commands/QueueClearTest.php @@ -0,0 +1,40 @@ +assertNotFalse(command('queue:clear')); + $output = $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeErrorFilter(); + + $this->assertSame('The queueName is not specified.', $output); + } + + public function testRun(): void + { + CITestStreamFilter::registration(); + CITestStreamFilter::addOutputFilter(); + + $this->assertNotFalse(command('queue:clear test')); + $output = $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeOutputFilter(); + + $this->assertSame('Queue test has been cleared.', $output); + } +} diff --git a/tests/Commands/QueueFailedTest.php b/tests/Commands/QueueFailedTest.php new file mode 100644 index 0000000..37e61b6 --- /dev/null +++ b/tests/Commands/QueueFailedTest.php @@ -0,0 +1,51 @@ + 'database', + 'queue' => 'test', + 'payload' => ['job' => 'failure', 'data' => ['key' => 'value']], + 'priority' => 'default', + 'exception' => 'Exception: Test error', + ]); + + CITestStreamFilter::registration(); + CITestStreamFilter::addOutputFilter(); + + $this->assertNotFalse(command('queue:failed')); + $output = $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeOutputFilter(); + + $expect = <<<'EOT' + +----+------------+-------+----------------------------+---------------------+ + | ID | Connection | Queue | Class | Failed At | + +----+------------+-------+----------------------------+---------------------+ + | 1 | database | test | Tests\Support\Jobs\Failure | 2023-12-19 14:15:16 | + +----+------------+-------+----------------------------+---------------------+ + EOT; + + $this->assertSame($expect, $output); + } +} diff --git a/tests/Commands/QueueFlushTest.php b/tests/Commands/QueueFlushTest.php new file mode 100644 index 0000000..255439b --- /dev/null +++ b/tests/Commands/QueueFlushTest.php @@ -0,0 +1,89 @@ + 'database', + 'queue' => 'test', + 'payload' => ['job' => 'failure', 'data' => ['key' => 'value']], + 'priority' => 'default', + 'exception' => 'Exception: Test error', + ]); + + CITestStreamFilter::registration(); + CITestStreamFilter::addOutputFilter(); + + $this->assertNotFalse(command('queue:flush')); + $output = $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeOutputFilter(); + + $this->assertSame('All failed jobs has been removed from the queue ', $output); + } + + public function testRunWithQueue(): void + { + Time::setTestNow('2023-12-19 14:15:16'); + + fake(QueueJobFailedModel::class, [ + 'connection' => 'database', + 'queue' => 'test', + 'payload' => ['job' => 'failure', 'data' => ['key' => 'value']], + 'priority' => 'default', + 'exception' => 'Exception: Test error', + ]); + + CITestStreamFilter::registration(); + CITestStreamFilter::addOutputFilter(); + + $this->assertNotFalse(command('queue:flush -queue default')); + $output = $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeOutputFilter(); + + $this->assertSame('All failed jobs has been removed from the queue default', $output); + } + + public function testRunWithQueueAndHour(): void + { + Time::setTestNow('2023-12-19 14:15:16'); + + fake(QueueJobFailedModel::class, [ + 'connection' => 'database', + 'queue' => 'test', + 'payload' => ['job' => 'failure', 'data' => ['key' => 'value']], + 'priority' => 'default', + 'exception' => 'Exception: Test error', + ]); + + CITestStreamFilter::registration(); + CITestStreamFilter::addOutputFilter(); + + $this->assertNotFalse(command('queue:flush -queue default -hours 2')); + $output = $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeOutputFilter(); + + $this->assertSame('All failed jobs older than 2 hours has been removed from the queue default', $output); + } +} diff --git a/tests/Commands/QueueForgetTest.php b/tests/Commands/QueueForgetTest.php new file mode 100644 index 0000000..710eca1 --- /dev/null +++ b/tests/Commands/QueueForgetTest.php @@ -0,0 +1,62 @@ +assertNotFalse(command('queue:forget')); + $output = $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeErrorFilter(); + + $this->assertSame('The ID of the failed job is not specified.', $output); + } + + public function testRunFailed(): void + { + CITestStreamFilter::registration(); + CITestStreamFilter::addOutputFilter(); + + $this->assertNotFalse(command('queue:forget 123')); + $output = $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeOutputFilter(); + + $this->assertSame('Could not find the failed job with ID 123', $output); + } + + public function testRun(): void + { + fake(QueueJobFailedModel::class, [ + 'connection' => 'database', + 'queue' => 'test', + 'payload' => ['job' => 'failure', 'data' => ['key' => 'value']], + 'priority' => 'default', + 'exception' => 'Exception: Test error', + ]); + + CITestStreamFilter::registration(); + CITestStreamFilter::addOutputFilter(); + + $this->assertNotFalse(command('queue:forget 1')); + $output = $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeOutputFilter(); + + $this->assertSame('Failed job with ID 1 has been removed.', $output); + } +} diff --git a/tests/Commands/QueuePublishTest.php b/tests/Commands/QueuePublishTest.php new file mode 100644 index 0000000..a39d5bd --- /dev/null +++ b/tests/Commands/QueuePublishTest.php @@ -0,0 +1,27 @@ +assertNotFalse(command('queue:publish')); + $output = $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeOutputFilter(); + + $this->assertSame(' Published! You can customize the configuration by editing the "app/Config/Queue.php" file.', $output); + } +} diff --git a/tests/Commands/QueueRetryTest.php b/tests/Commands/QueueRetryTest.php new file mode 100644 index 0000000..fdeafc6 --- /dev/null +++ b/tests/Commands/QueueRetryTest.php @@ -0,0 +1,62 @@ +assertNotFalse(command('queue:retry')); + $output = $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeErrorFilter(); + + $this->assertSame('The ID of the failed job is not specified.', $output); + } + + public function testRunFailed(): void + { + CITestStreamFilter::registration(); + CITestStreamFilter::addOutputFilter(); + + $this->assertNotFalse(command('queue:retry all -queue test')); + $output = $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeOutputFilter(); + + $this->assertSame('No failed jobs has been restored to the queue test', $output); + } + + public function testRun(): void + { + fake(QueueJobFailedModel::class, [ + 'connection' => 'database', + 'queue' => 'test', + 'payload' => ['job' => 'failure', 'data' => ['key' => 'value']], + 'priority' => 'default', + 'exception' => 'Exception: Test error', + ]); + + CITestStreamFilter::registration(); + CITestStreamFilter::addOutputFilter(); + + $this->assertNotFalse(command('queue:retry 1')); + $output = $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeOutputFilter(); + + $this->assertSame('1 failed job(s) has been restored to the queue ', $output); + } +} diff --git a/tests/Commands/QueueStopTest.php b/tests/Commands/QueueStopTest.php new file mode 100644 index 0000000..3324b0f --- /dev/null +++ b/tests/Commands/QueueStopTest.php @@ -0,0 +1,40 @@ +assertNotFalse(command('queue:stop')); + $output = $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeErrorFilter(); + + $this->assertSame('The queueName is not specified.', $output); + } + + public function testRun(): void + { + CITestStreamFilter::registration(); + CITestStreamFilter::addOutputFilter(); + + $this->assertNotFalse(command('queue:stop test')); + $output = $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeOutputFilter(); + + $this->assertSame('Queue will be stopped after the current job finish', $output); + } +} diff --git a/tests/Commands/QueueWorkTest.php b/tests/Commands/QueueWorkTest.php new file mode 100644 index 0000000..5c0993f --- /dev/null +++ b/tests/Commands/QueueWorkTest.php @@ -0,0 +1,105 @@ +assertNotFalse(command('queue:work')); + $output = $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeErrorFilter(); + + $this->assertSame('The queueName is not specified.', $output); + } + + public function testRunWithEmptyQueue(): void + { + CITestStreamFilter::registration(); + CITestStreamFilter::addOutputFilter(); + + $this->assertNotFalse(command('queue:work test --stop-when-empty')); + $output = $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeOutputFilter(); + + $expect = <<<'EOT' + Listening for the jobs with the queue: test + + + No job available. Stopping. + EOT; + + $this->assertSame($expect, $output); + } + + public function testRunWithQueueFailed(): void + { + Time::setTestNow('2023-12-19 14:15:16'); + + fake(QueueJobModel::class, [ + 'connection' => 'database', + 'queue' => 'test', + 'payload' => ['job' => 'failure', 'data' => ['key' => 'value']], + 'priority' => 'default', + 'status' => 0, + 'attempts' => 0, + 'available_at' => 1_702_977_074, + ]); + + CITestStreamFilter::registration(); + CITestStreamFilter::addOutputFilter(); + + $this->assertNotFalse(command('queue:work test sleep 1 --stop-when-empty')); + $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeOutputFilter(); + + $this->assertSame('Listening for the jobs with the queue: test', $this->getLine(0)); + $this->assertSame('Starting a new job: failure, with ID: 1', $this->getLine(3)); + $this->assertSame('The processing of this job failed', $this->getLine(4)); + $this->assertSame('No job available. Stopping.', $this->getLine(7)); + } + + public function testRunWithQueueSucceed(): void + { + Time::setTestNow('2023-12-19 14:15:16'); + + fake(QueueJobModel::class, [ + 'connection' => 'database', + 'queue' => 'test', + 'payload' => ['job' => 'success', 'data' => ['key' => 'value']], + 'priority' => 'default', + 'status' => 0, + 'attempts' => 0, + 'available_at' => 1_702_977_074, + ]); + + CITestStreamFilter::registration(); + CITestStreamFilter::addOutputFilter(); + + $this->assertNotFalse(command('queue:work test sleep 1 --stop-when-empty')); + $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeOutputFilter(); + + $this->assertSame('Listening for the jobs with the queue: test', $this->getLine(0)); + $this->assertSame('Starting a new job: success, with ID: 1', $this->getLine(3)); + $this->assertSame('The processing of this job was successful', $this->getLine(4)); + $this->assertSame('No job available. Stopping.', $this->getLine(7)); + } +} diff --git a/tests/_support/CLITestCase.php b/tests/_support/CLITestCase.php new file mode 100644 index 0000000..9cc05dd --- /dev/null +++ b/tests/_support/CLITestCase.php @@ -0,0 +1,62 @@ +lines = []; + $output = $this->removeColorCodes($output); + $this->lines = explode("\n", $output); + + return $output; + } + + protected function getLine(int $line = 0): ?string + { + return $this->lines[$line] ?? null; + } + + protected function getLines(): string + { + return implode('', $this->lines); + } + + protected function removeColorCodes(string $output): string + { + $colors = $this->getPrivateProperty(CLI::class, 'foreground_colors'); + $colors = array_values(array_map(static fn ($color) => "\033[" . $color . 'm', $colors)); + $colors = array_merge(["\033[0m"], $colors); + + $output = str_replace($colors, '', trim($output)); + + if (is_windows()) { + $output = str_replace("\r\n", "\n", $output); + } + + return $output; + } +}