From 2b5e25de60bbc4c47ad98f0f4b76be7cb4cae017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rio=20Souto?= Date: Wed, 11 Jun 2025 14:54:54 +0100 Subject: [PATCH 1/2] Creatre tests; set github workflow --- .github/workflows/tests.yml | 43 +++++++ .phpunit.cache/test-results | 1 + composer.json | 13 +- phpunit.xml.dist | 16 +++ tests/Feature/ShowJobsCommandTest.php | 164 ++++++++++++++++++++++++++ tests/Pest.php | 5 + tests/TestCase.php | 27 +++++ 7 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/tests.yml create mode 100644 .phpunit.cache/test-results create mode 100644 phpunit.xml.dist create mode 100644 tests/Feature/ShowJobsCommandTest.php create mode 100644 tests/Pest.php create mode 100644 tests/TestCase.php diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..90881f9 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,43 @@ +name: Tests + +on: [push, pull_request] + +jobs: + tests: + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + php: [8.1, 8.2, 8.3, 8.4] + dependency-version: [prefer-lowest, prefer-stable] + + name: PHP ${{ matrix.php }} - ${{ matrix.dependency-version }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: mbstring, redis, dom, zip + tools: composer:v2 + coverage: none + + - name: Get Composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache Composer dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: deps-${{ matrix.php }}-${{ matrix.dependency-version }}-${{ hashFiles('composer.lock') }} + restore-keys: deps-${{ matrix.php }}-${{ matrix.dependency-version }}- + + - name: Install dependencies + run: composer update --${{ matrix.dependency-version }} --no-interaction --prefer-dist + + - name: Run Pest tests + run: ./vendor/bin/pest diff --git a/.phpunit.cache/test-results b/.phpunit.cache/test-results new file mode 100644 index 0000000..78e499c --- /dev/null +++ b/.phpunit.cache/test-results @@ -0,0 +1 @@ +{"version":"pest_2.36.0","defects":{"P\\Tests\\Feature\\ShowJobsCommandTest::__pest_evaluable_it_filters_by___from_and___to_date_range":7},"times":{"P\\Tests\\Feature\\ShowJobsCommandTest::__pest_evaluable_it_shows_no_jobs_when_Redis_is_empty":0.025,"P\\Tests\\Feature\\ShowJobsCommandTest::__pest_evaluable_it_counts_one_delayed_job_in_Redis":0.001,"P\\Tests\\Feature\\ShowJobsCommandTest::__pest_evaluable_it_shows_message_when_no_jobs_exist":0.022,"P\\Tests\\Feature\\ShowJobsCommandTest::__pest_evaluable_it_filters_by___queue":0.002,"P\\Tests\\Feature\\ShowJobsCommandTest::__pest_evaluable_it_filters_by___job__partial_match_":0.001,"P\\Tests\\Feature\\ShowJobsCommandTest::__pest_evaluable_it_filters_by___uuid__exact_match_":0.001,"P\\Tests\\Feature\\ShowJobsCommandTest::__pest_evaluable_it_filters_by___identifier__serialized_in_payload_":0.001,"P\\Tests\\Feature\\ShowJobsCommandTest::__pest_evaluable_it_filters_by___from_and___to_date_range":0.028,"P\\Tests\\Feature\\ShowJobsCommandTest::__pest_evaluable_it_respects___limit_and___page":0.007,"P\\Tests\\Feature\\ShowJobsCommandTest::__pest_evaluable_it_outputs_in_JSON_when___json_is_used":0.006,"P\\Tests\\Feature\\ShowJobsCommandTest::__pest_evaluable_it_shows_total_count_when_using___count":0.001,"P\\Tests\\Feature\\ShowJobsCommandTest::__pest_evaluable_it_filters_jobs_by___from_and___to_date_range":0.002}} \ No newline at end of file diff --git a/composer.json b/composer.json index 1d29d99..9a9ddfa 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,9 @@ }, "require-dev": { "laravel/pint": "^1.2.0", + "orchestra/testbench": "^8.36", + "pestphp/pest": "^2.36", + "pestphp/pest-plugin-laravel": "^2.4", "phpstan/phpstan": "^1.8.6" }, "autoload": { @@ -27,11 +30,19 @@ "Msouto\\RedisQueueInspector\\": "src/" } }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, "minimum-stability": "dev", "prefer-stable": true, "config": { "sort-packages": true, - "preferred-install": "dist" + "preferred-install": "dist", + "allow-plugins": { + "pestphp/pest-plugin": true + } }, "extra": { "laravel": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..4d01d8c --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,16 @@ + + + + + ./src + + + + + ./tests + + + diff --git a/tests/Feature/ShowJobsCommandTest.php b/tests/Feature/ShowJobsCommandTest.php new file mode 100644 index 0000000..a4430c8 --- /dev/null +++ b/tests/Feature/ShowJobsCommandTest.php @@ -0,0 +1,164 @@ +assertSuccessful() + ->expectsOutput('No matching delayed jobs found.'); +}); + +it('filters by --queue', function () { + $payload = json_encode([ + 'uuid' => 'uuid-queue-1', + 'displayName' => 'App\\Jobs\\QueuedJob', + 'data' => ['command' => serialize('dummy')], + ]); + + Redis::zadd('queues:other:delayed', time() + 300, $payload); + + artisan('queue:inspect --queue=default --count --json') + ->assertSuccessful() + ->expectsOutput(json_encode(['count' => 0], JSON_PRETTY_PRINT)); + + artisan('queue:inspect --queue=other --count --json') + ->assertSuccessful() + ->expectsOutput(json_encode(['count' => 1], JSON_PRETTY_PRINT)); +}); + +it('filters by --job (partial match)', function () { + $payload = json_encode([ + 'uuid' => 'uuid-job-1', + 'displayName' => 'App\\Jobs\\TestNotificationJob', + 'data' => ['command' => serialize('dummy')], + ]); + + Redis::zadd('queues:default:delayed', time() + 300, $payload); + + artisan('queue:inspect --job=Notification --count --json') + ->assertSuccessful() + ->expectsOutput(json_encode(['count' => 1], JSON_PRETTY_PRINT)); +}); + +it('filters by --uuid (exact match)', function () { + $uuid = 'exact-uuid-1234'; + $payload = json_encode([ + 'uuid' => $uuid, + 'displayName' => 'App\\Jobs\\Something', + 'data' => ['command' => serialize('dummy')], + ]); + + Redis::zadd('queues:default:delayed', time() + 300, $payload); + + artisan("queue:inspect --uuid={$uuid} --count --json") + ->assertSuccessful() + ->expectsOutput(json_encode(['count' => 1], JSON_PRETTY_PRINT)); +}); + +it('filters by --identifier (serialized in payload)', function () { + $identifier = 42; + $serialized = serialize((object) ['id' => $identifier]); + + $payload = json_encode([ + 'uuid' => 'uuid-identifier', + 'displayName' => 'App\\Jobs\\TargetJob', + 'data' => ['command' => $serialized], + ]); + + Redis::zadd('queues:default:delayed', time() + 300, $payload); + + artisan("queue:inspect --identifier={$identifier} --count --json") + ->assertSuccessful() + ->expectsOutput(json_encode(['count' => 1], JSON_PRETTY_PRINT)); +}); + +it('filters jobs by --from and --to date range', function () { + $today = now(); + $releaseAt = $today->copy()->addHours(2); // falls within today + + Redis::zadd('queues:default:delayed', $releaseAt->timestamp, json_encode([ + 'uuid' => 'uuid-date-range', + 'displayName' => 'App\\Jobs\\ScheduledJob', + 'data' => ['command' => serialize('dummy')], + ])); + + Artisan::call('queue:inspect --from=' . $today->format('Y-m-d') . ' --to=' . $today->format('Y-m-d') . ' --json'); + $output = Artisan::output(); + $json = json_decode($output, true); + + expect($json) + ->toHaveKey('jobs') + ->and($json['jobs'][0]['job'])->toBe('App\\Jobs\\ScheduledJob'); +}); + +it('respects --limit and --page', function () { + Redis::flushall(); + + foreach (range(1, 5) as $i) { + Redis::zadd('queues:default:delayed', time() + $i, json_encode([ + 'uuid' => "uuid-{$i}", + 'displayName' => "App\\Jobs\\PaginatedJob{$i}", + 'data' => ['command' => serialize('dummy')], + ])); + } + + // Page 1 + Artisan::call('queue:inspect --limit=2 --page=1 --json'); + $outputPage1 = Artisan::output(); + $jsonPage1 = json_decode($outputPage1, true); + + expect($jsonPage1)->toHaveKey('jobs'); + expect($jsonPage1['jobs'])->toHaveCount(2); + expect($jsonPage1['jobs'][0]['job'])->toBe('App\\Jobs\\PaginatedJob1'); + expect($jsonPage1['jobs'][1]['job'])->toBe('App\\Jobs\\PaginatedJob2'); + + // Page 2 + Artisan::call('queue:inspect --limit=2 --page=2 --json'); + $outputPage2 = Artisan::output(); + $jsonPage2 = json_decode($outputPage2, true); + + expect($jsonPage2)->toHaveKey('jobs'); + expect($jsonPage2['jobs'])->toHaveCount(2); + expect($jsonPage2['jobs'][0]['job'])->toBe('App\\Jobs\\PaginatedJob3'); + expect($jsonPage2['jobs'][1]['job'])->toBe('App\\Jobs\\PaginatedJob4'); +}); + +it('outputs in JSON when --json is used', function () { + Redis::flushall(); + + Redis::zadd('queues:default:delayed', time() + 300, json_encode([ + 'uuid' => 'uuid-json-output', + 'displayName' => 'App\\Jobs\\JsonJob', + 'data' => ['command' => serialize('dummy')], + ])); + + Artisan::call('queue:inspect --json'); + $output = Artisan::output(); + + $data = json_decode($output, true); + + expect($data)->toBeArray() + ->and($data)->toHaveKey('jobs') + ->and($data['jobs'])->toBeArray() + ->and($data['jobs'][0]['job'])->toBe('App\\Jobs\\JsonJob'); +}); + +it('shows total count when using --count', function () { + Redis::zadd('queues:default:delayed', time() + 300, json_encode([ + 'uuid' => 'uuid-count', + 'displayName' => 'App\\Jobs\\CountingJob', + 'data' => ['command' => serialize('dummy')], + ])); + + artisan('queue:inspect --count --json') + ->assertSuccessful() + ->expectsOutput(json_encode(['count' => 1], JSON_PRETTY_PRINT)); +}); diff --git a/tests/Pest.php b/tests/Pest.php new file mode 100644 index 0000000..d47845c --- /dev/null +++ b/tests/Pest.php @@ -0,0 +1,5 @@ +in(__DIR__); diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..8486d52 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,27 @@ +set('queue.default', 'redis'); + $app['config']->set('queue.connections.redis', [ + 'driver' => 'redis', + 'connection' => 'default', + ]); + } +} From 5065b494eead457f22d91478bb084e63d8f7d71d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rio=20Souto?= Date: Wed, 11 Jun 2025 15:01:45 +0100 Subject: [PATCH 2/2] add redis image to workflow --- .github/workflows/tests.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 90881f9..68386a7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,6 +5,17 @@ on: [push, pull_request] jobs: tests: runs-on: ubuntu-latest + services: + redis: + image: redis + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + strategy: fail-fast: true matrix: