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;
+ }
+}