Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Tests

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:
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
1 change: 1 addition & 0 deletions .phpunit.cache/test-results
Original file line number Diff line number Diff line change
@@ -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}}
13 changes: 12 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,29 @@
},
"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": {
"psr-4": {
"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": {
Expand Down
16 changes: 16 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.0/phpunit.xsd"
colors="true"
cacheDirectory=".phpunit.cache">
<source>
<include>
<directory suffix=".php">./src</directory>
</include>
</source>
<testsuites>
<testsuite name="default">
<directory suffix=".php">./tests</directory>
</testsuite>
</testsuites>
</phpunit>
164 changes: 164 additions & 0 deletions tests/Feature/ShowJobsCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
<?php

namespace Tests\Feature;

use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Redis;
use function Pest\Laravel\artisan;

beforeEach(function () {
Redis::flushall();
});

it('shows message when no jobs exist', function () {
artisan('queue:inspect')
->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));
});
5 changes: 5 additions & 0 deletions tests/Pest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

use Tests\TestCase;

uses(TestCase::class)->in(__DIR__);
27 changes: 27 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Tests;

use Msouto\RedisQueueInspector\Providers\RedisQueueInspectorServiceProvider;
use Orchestra\Testbench\TestCase as Orchestra;

abstract class TestCase extends Orchestra
{
protected function getPackageProviders($app): array
{
return [
RedisQueueInspectorServiceProvider::class,
];
}

protected function defineEnvironment($app): void
{
$app['config']->set('queue.default', 'redis');
$app['config']->set('queue.connections.redis', [
'driver' => 'redis',
'connection' => 'default',
]);
}
}