1
0
Fork 0
mirror of https://github.com/archtechx/jobpipeline.git synced 2025-12-12 21:14:03 +00:00
jobpipeline/tests/JobPipelineTest.php
2025-07-22 12:33:20 +02:00

333 lines
7.9 KiB
PHP

<?php
namespace Stancl\JobPipeline\Tests;
use Exception;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Queue;
use Orchestra\Testbench\TestCase;
use PHPUnit\Framework\Attributes\Test;
use Spatie\Valuestore\Valuestore;
use Stancl\JobPipeline\JobPipeline;
class JobPipelineTest extends TestCase
{
public $mockConsoleOutput = false;
/** @var Valuestore */
protected $valuestore;
public function setUp(): void
{
parent::setUp();
config(['queue.default' => 'redis']);
$this->valuestore = Valuestore::make(__DIR__ . '/tmp/jobpipelinetest.json')->flush();
}
#[Test]
public function job_pipeline_can_listen_to_any_event()
{
Event::listen(TestEvent::class, JobPipeline::make([
FooJob::class,
])->send(function () {
return $this->valuestore;
})->toListener());
$this->assertFalse($this->valuestore->has('foo'));
event(new TestEvent(new TestModel()));
$this->assertSame('bar', $this->valuestore->get('foo'));
}
#[Test]
public function job_pipeline_can_be_queued()
{
Queue::fake();
Event::listen(TestEvent::class, JobPipeline::make([
FooJob::class,
])->send(function () {
return $this->valuestore;
})->shouldBeQueued(true)->toListener());
Queue::assertNothingPushed();
event(new TestEvent(new TestModel()));
$this->assertFalse($this->valuestore->has('foo'));
Queue::pushed(JobPipeline::class, function (JobPipeline $pipeline) {
$this->assertSame([FooJob::class], $pipeline->jobs);
});
}
#[Test]
public function job_pipelines_run_when_queued()
{
Event::listen(TestEvent::class, JobPipeline::make([
FooJob::class,
])->send(function () {
return $this->valuestore;
})->shouldBeQueued(true)->toListener());
$this->assertFalse($this->valuestore->has('foo'));
event(new TestEvent(new TestModel()));
$this->artisan('queue:work --once');
sleep(1);
$this->assertSame('bar', $this->valuestore->get('foo'));
}
#[Test]
public function job_pipelines_can_use_a_specific_queue()
{
Event::listen(TestEvent::class, JobPipeline::make([
FooJob::class,
])->send(function () {
return $this->valuestore;
})->shouldBeQueued(queue: 'another')->toListener());
$this->assertFalse($this->valuestore->has('foo'));
event(new TestEvent(new TestModel()));
$this->artisan('queue:work --once --queue=default');
sleep(1);
// Job hasn't run since it was pushed to the 'another' queue
$this->assertNull($this->valuestore->get('foo'));
$this->artisan('queue:work --once --queue=another');
sleep(1);
$this->assertSame('bar', $this->valuestore->get('foo'));
}
#[Test]
public function job_pipeline_executes_jobs_and_passes_the_object_sequentially()
{
Event::listen(TestEvent::class, JobPipeline::make([
FirstJob::class,
SecondJob::class,
])->send(function (TestEvent $event) {
return [$event->testModel, $this->valuestore];
})->toListener());
$this->assertFalse($this->valuestore->has('foo'));
event(new TestEvent(new TestModel()));
$this->assertSame('first job changed property', $this->valuestore->get('foo'));
}
#[Test]
public function send_can_return_multiple_arguments()
{
Event::listen(TestEvent::class, JobPipeline::make([
JobWithMultipleArguments::class
])->send(function () {
return ['a', 'b'];
})->toListener());
$this->assertFalse(app()->bound('test_args'));
event(new TestEvent(new TestModel()));
$this->assertSame(['a', 'b'], app('test_args'));
}
#[Test]
public function the_pipeline_can_be_canceled_by_returning_false_from_any_job()
{
Event::listen(TestEvent::class, JobPipeline::make([
FalseJob::class,
FooJob::class,
])->send(function () {
return $this->valuestore;
})->shouldBeQueued(true)->toListener());
event(new TestEvent(new TestModel()));
$this->artisan('queue:work --once');
sleep(1);
$this->assertTrue($this->valuestore->get('false_job_executed'));
// Foo job is not excuted
$this->assertFalse($this->valuestore->has('foo'));
}
#[Test]
public function the_pipeline_can_execute_failed_method_on_job()
{
Event::listen(TestEvent::class, JobPipeline::make([
ExceptionJob::class,
])->send(function () {
return $this->valuestore;
})->toListener());
event(new TestEvent(new TestModel()));
sleep(1);
$this->assertEquals($this->valuestore->get('exeception'), 'pipeline exception');
}
#[Test]
public function closures_can_be_used_as_jobs()
{
$passes = false;
Event::listen(TestEvent::class, JobPipeline::make([
function (TestModel $model) use (&$passes) {
$passes = $model instanceof TestModel;
}
])->send(function (TestEvent $event) {
return $event->testModel;
})->toListener());
event(new TestEvent(new TestModel()));
$this->assertTrue($passes);
}
#[Test]
public function failures_in_closures_will_throw_correctly()
{
$this->expectExceptionMessage('foobar');
Event::listen(TestEvent::class, JobPipeline::make([
function () {
throw new Exception('foobar');
}
])->send(function (TestEvent $event) {
return $this->valuestore;
})->shouldBeQueued(false)->toListener());
event(new TestEvent(new TestModel()));
}
}
class FooJob
{
protected $valuestore;
public function __construct(Valuestore $valuestore)
{
$this->valuestore = $valuestore;
}
public function handle()
{
$this->valuestore->put('foo', 'bar');
}
};
class TestModel extends Model
{
}
class TestEvent
{
/** @var TestModel $testModel */
public $testModel;
public function __construct(TestModel $testModel)
{
$this->testModel = $testModel;
}
}
class FirstJob
{
public $testModel;
public function __construct(TestModel $testModel)
{
$this->testModel = $testModel;
}
public function handle()
{
$this->testModel->foo = 'first job changed property';
}
}
class SecondJob
{
public $testModel;
protected $valuestore;
public function __construct(TestModel $testModel, Valuestore $valuestore)
{
$this->testModel = $testModel;
$this->valuestore = $valuestore;
}
public function handle()
{
$this->valuestore->put('foo', $this->testModel->foo);
}
}
class JobWithMultipleArguments
{
protected $first;
protected $second;
public function __construct($first, $second)
{
$this->first = $first;
$this->second = $second;
}
public function handle()
{
// we dont queue this job so no need to use valuestore here
app()->instance('test_args', [$this->first, $this->second]);
}
}
class FalseJob
{
protected $valuestore;
public function __construct(Valuestore $valuestore)
{
$this->valuestore = $valuestore;
}
public function handle()
{
$this->valuestore->put('false_job_executed', true);
return false;
}
}
class ExceptionJob
{
protected $valuestore;
public function __construct(Valuestore $valuestore)
{
$this->valuestore = $valuestore;
}
public function handle()
{
throw new \Exception('pipeline exception', 1);
}
public function failed(\Throwable $e)
{
$this->valuestore->put('exeception', $e->getMessage());
}
}