From 046a9e32ef4cfeb5a1d05f73aab6153e300956cf Mon Sep 17 00:00:00 2001 From: Martin Kluska Date: Tue, 27 Feb 2024 15:06:21 +0100 Subject: [PATCH] feat(Queue): Add ability to dispatch job in testable way --- src/Console/Jobs/AbstractUniqueJob.php | 16 +- src/Core/LaraStrictServiceProvider.php | 2 + src/Exceptions/Exceptions/LogicException.php | 11 + src/Queue/Actions/DispatchChainJobsAction.php | 38 +++ src/Queue/Actions/DispatchJobAction.php | 19 ++ src/Queue/Actions/RunJobAction.php | 36 +++ src/Queue/Actions/RunOrQueueJobAction.php | 49 +++ src/Queue/Concerns/UsesCommand.php | 22 ++ .../DispatchChainJobsActionContract.php | 23 ++ .../Contracts/DispatchJobActionContract.php | 16 + src/Queue/Contracts/RunJobActionContract.php | 19 ++ .../Contracts/RunOrQueueJobActionContract.php | 28 ++ .../MethodInJobIsNotDefinedException.php | 23 ++ src/Queue/Interfaces/UsesCommandInterface.php | 19 ++ src/Queue/Jobs/Job.php | 24 ++ src/Queue/QueueServiceProvider.php | 25 ++ .../DispatchJobActionContractAssert.php | 36 +++ .../DispatchJobActionContractExpectation.php | 21 ++ .../Contracts/RunJobActionContractAssert.php | 38 +++ .../RunJobActionContractExpectation.php | 23 ++ .../RunOrQueueJobActionContractAssert.php | 45 +++ ...RunOrQueueJobActionContractExpectation.php | 25 ++ tests/Feature/Queue/Actions/CommandJob.php | 22 ++ .../Actions/DispatchChainJobsActionTest.php | 101 ++++++ .../Queue/Actions/DispatchJobActionTest.php | 23 ++ .../Queue/Actions/RunJobActionTest.php | 96 ++++++ tests/Feature/Queue/Actions/TestCommand.php | 12 + .../Queue/Actions/WithoutCommandJob.php | 28 ++ tests/Feature/TestCase.php | 20 ++ .../Queue/Actions/RunOrQueueJobActionTest.php | 304 ++++++++++++++++++ 30 files changed, 1150 insertions(+), 14 deletions(-) create mode 100644 src/Exceptions/Exceptions/LogicException.php create mode 100644 src/Queue/Actions/DispatchChainJobsAction.php create mode 100644 src/Queue/Actions/DispatchJobAction.php create mode 100644 src/Queue/Actions/RunJobAction.php create mode 100644 src/Queue/Actions/RunOrQueueJobAction.php create mode 100644 src/Queue/Concerns/UsesCommand.php create mode 100644 src/Queue/Contracts/DispatchChainJobsActionContract.php create mode 100644 src/Queue/Contracts/DispatchJobActionContract.php create mode 100644 src/Queue/Contracts/RunJobActionContract.php create mode 100644 src/Queue/Contracts/RunOrQueueJobActionContract.php create mode 100644 src/Queue/Exceptions/MethodInJobIsNotDefinedException.php create mode 100644 src/Queue/Interfaces/UsesCommandInterface.php create mode 100644 src/Queue/Jobs/Job.php create mode 100644 src/Queue/QueueServiceProvider.php create mode 100644 src/Testing/Queue/Contracts/DispatchJobActionContractAssert.php create mode 100644 src/Testing/Queue/Contracts/DispatchJobActionContractExpectation.php create mode 100644 src/Testing/Queue/Contracts/RunJobActionContractAssert.php create mode 100644 src/Testing/Queue/Contracts/RunJobActionContractExpectation.php create mode 100644 src/Testing/Queue/Contracts/RunOrQueueJobActionContractAssert.php create mode 100644 src/Testing/Queue/Contracts/RunOrQueueJobActionContractExpectation.php create mode 100644 tests/Feature/Queue/Actions/CommandJob.php create mode 100644 tests/Feature/Queue/Actions/DispatchChainJobsActionTest.php create mode 100644 tests/Feature/Queue/Actions/DispatchJobActionTest.php create mode 100644 tests/Feature/Queue/Actions/RunJobActionTest.php create mode 100644 tests/Feature/Queue/Actions/TestCommand.php create mode 100644 tests/Feature/Queue/Actions/WithoutCommandJob.php create mode 100644 tests/Unit/Queue/Actions/RunOrQueueJobActionTest.php diff --git a/src/Console/Jobs/AbstractUniqueJob.php b/src/Console/Jobs/AbstractUniqueJob.php index 17684d83..b16c8a4a 100644 --- a/src/Console/Jobs/AbstractUniqueJob.php +++ b/src/Console/Jobs/AbstractUniqueJob.php @@ -4,29 +4,17 @@ namespace LaraStrict\Console\Jobs; -use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldBeUnique; -use Illuminate\Contracts\Queue\ShouldQueue; -use Illuminate\Queue\InteractsWithQueue; +use LaraStrict\Queue\Jobs\Job; -abstract class AbstractUniqueJob implements ShouldQueue, ShouldBeUnique +abstract class AbstractUniqueJob extends Job implements ShouldBeUnique { - use Queueable; - use InteractsWithQueue; - public int $tries = 30; public int $uniqueFor = 10; public int $maxExceptions = 1; - public function __construct() - { - if ($this->queue === null) { - $this->queue = 'default'; - } - } - abstract public function uniqueId(): string; /** diff --git a/src/Core/LaraStrictServiceProvider.php b/src/Core/LaraStrictServiceProvider.php index 1449a217..e9d5f8b7 100644 --- a/src/Core/LaraStrictServiceProvider.php +++ b/src/Core/LaraStrictServiceProvider.php @@ -21,6 +21,7 @@ use LaraStrict\Providers\Actions\RunAppServiceProviderPipesAction; use LaraStrict\Providers\Pipes\PreventLazyLoadingPipe; use LaraStrict\Providers\Pipes\SetFactoryResolvingProviderPipe; +use LaraStrict\Queue\QueueServiceProvider; use LaraStrict\Testing\TestServiceProvider; class LaraStrictServiceProvider extends AbstractBaseServiceProvider @@ -42,6 +43,7 @@ public function register(): void $this->app->register(TestServiceProvider::class); $this->app->register(DockerServiceProvider::class); $this->app->register(LogServiceProvider::class); + $this->app->register(QueueServiceProvider::class); $this->app->singleton(ImplementsService::class, ImplementsService::class); diff --git a/src/Exceptions/Exceptions/LogicException.php b/src/Exceptions/Exceptions/LogicException.php new file mode 100644 index 00000000..d794cae7 --- /dev/null +++ b/src/Exceptions/Exceptions/LogicException.php @@ -0,0 +1,11 @@ +dispatchJobAction->execute(reset($jobs)); + } + + $mainJob = array_shift($jobs); + + // After main job is done, chain our jobs + $mainJob->chain($jobs); + + // Set the chain queue + $mainJob->chainQueue = $mainJob->queue; + + // Dispatch chained job + return $this->dispatchJobAction->execute($mainJob); + } +} diff --git a/src/Queue/Actions/DispatchJobAction.php b/src/Queue/Actions/DispatchJobAction.php new file mode 100644 index 00000000..645196b1 --- /dev/null +++ b/src/Queue/Actions/DispatchJobAction.php @@ -0,0 +1,19 @@ +setCommand($command); + } + + $method ??= 'handle'; + $call = [$job, $method]; + + if (is_callable($call) === false) { + throw new MethodInJobIsNotDefinedException($method, $job::class); + } + + return $this->container->call($call); + } +} diff --git a/src/Queue/Actions/RunOrQueueJobAction.php b/src/Queue/Actions/RunOrQueueJobAction.php new file mode 100644 index 00000000..0aae5c2c --- /dev/null +++ b/src/Queue/Actions/RunOrQueueJobAction.php @@ -0,0 +1,49 @@ +option('queue') === true)) { + $this->dispatchJobAction->execute($job); + + return null; + } + + if ($setupBeforeRun !== null) { + $setupBeforeRun($job); + } + + return $this->runJobAction->execute(job: $job, command: $command); + } +} diff --git a/src/Queue/Concerns/UsesCommand.php b/src/Queue/Concerns/UsesCommand.php new file mode 100644 index 00000000..a3308ddc --- /dev/null +++ b/src/Queue/Concerns/UsesCommand.php @@ -0,0 +1,22 @@ +command = $command; + } + + public function getCommand(): ?Command + { + return $this->command; + } +} diff --git a/src/Queue/Contracts/DispatchChainJobsActionContract.php b/src/Queue/Contracts/DispatchChainJobsActionContract.php new file mode 100644 index 00000000..9466b87b --- /dev/null +++ b/src/Queue/Contracts/DispatchChainJobsActionContract.php @@ -0,0 +1,23 @@ +call() method. If the job is an instance of UsesCommandInterface, it will set the + * command. that you can use for advanced output. + * + * @return mixed Returns the result of the job + */ + public function execute(Job $job, ?Command $command = null): mixed; +} diff --git a/src/Queue/Contracts/RunOrQueueJobActionContract.php b/src/Queue/Contracts/RunOrQueueJobActionContract.php new file mode 100644 index 00000000..f88f0575 --- /dev/null +++ b/src/Queue/Contracts/RunOrQueueJobActionContract.php @@ -0,0 +1,28 @@ + does not contain desired method <%s>', + $jobClass, + $method + ), $code, $previous); + } +} diff --git a/src/Queue/Interfaces/UsesCommandInterface.php b/src/Queue/Interfaces/UsesCommandInterface.php new file mode 100644 index 00000000..9613540b --- /dev/null +++ b/src/Queue/Interfaces/UsesCommandInterface.php @@ -0,0 +1,19 @@ +queue === null) { + $this->queue = 'default'; + } + } +} diff --git a/src/Queue/QueueServiceProvider.php b/src/Queue/QueueServiceProvider.php new file mode 100644 index 00000000..9f97610a --- /dev/null +++ b/src/Queue/QueueServiceProvider.php @@ -0,0 +1,25 @@ + DispatchJobAction::class, + RunJobActionContract::class => RunJobAction::class, + RunOrQueueJobActionContract::class => RunOrQueueJobAction::class, + DispatchChainJobsActionContract::class => DispatchChainJobsAction::class, + ]; +} diff --git a/src/Testing/Queue/Contracts/DispatchJobActionContractAssert.php b/src/Testing/Queue/Contracts/DispatchJobActionContractAssert.php new file mode 100644 index 00000000..843c0d38 --- /dev/null +++ b/src/Testing/Queue/Contracts/DispatchJobActionContractAssert.php @@ -0,0 +1,36 @@ + $execute + */ + public function __construct(array $execute = []) + { + parent::__construct(); + $this->setExpectations(DispatchJobActionContractExpectation::class, $execute); + } + + public function execute(Job $job): bool + { + $_expectation = $this->getExpectation(DispatchJobActionContractExpectation::class); + $_message = $this->getDebugMessage(); + + Assert::assertEquals($_expectation->job, $job, $_message); + + if (is_callable($_expectation->_hook)) { + call_user_func($_expectation->_hook, $job, $_expectation); + } + + return $_expectation->return; + } +} diff --git a/src/Testing/Queue/Contracts/DispatchJobActionContractExpectation.php b/src/Testing/Queue/Contracts/DispatchJobActionContractExpectation.php new file mode 100644 index 00000000..b589f085 --- /dev/null +++ b/src/Testing/Queue/Contracts/DispatchJobActionContractExpectation.php @@ -0,0 +1,21 @@ + $execute + */ + public function __construct(array $execute = []) + { + parent::__construct(); + $this->setExpectations(RunJobActionContractExpectation::class, $execute); + } + + public function execute(Job $job, Command $command = null): mixed + { + $_expectation = $this->getExpectation(RunJobActionContractExpectation::class); + $_message = $this->getDebugMessage(); + + Assert::assertEquals($_expectation->job, $job, $_message); + Assert::assertEquals($_expectation->command, $command, $_message); + + if (is_callable($_expectation->_hook)) { + call_user_func($_expectation->_hook, $job, $command, $_expectation); + } + + return $_expectation->return; + } +} diff --git a/src/Testing/Queue/Contracts/RunJobActionContractExpectation.php b/src/Testing/Queue/Contracts/RunJobActionContractExpectation.php new file mode 100644 index 00000000..9eba4190 --- /dev/null +++ b/src/Testing/Queue/Contracts/RunJobActionContractExpectation.php @@ -0,0 +1,23 @@ + $execute + */ + public function __construct(array $execute = []) + { + parent::__construct(); + $this->setExpectations(RunOrQueueJobActionContractExpectation::class, $execute); + } + + public function execute( + Job $job, + Command $command = null, + ?Closure $setupBeforeRun = null, + bool $shouldQueue = null, + ): mixed { + $_expectation = $this->getExpectation(RunOrQueueJobActionContractExpectation::class); + $_message = $this->getDebugMessage(); + + Assert::assertEquals($_expectation->job, $job, $_message); + Assert::assertEquals($_expectation->command, $command, $_message); + Assert::assertEquals($_expectation->setupBeforeRun, $setupBeforeRun, $_message); + Assert::assertEquals($_expectation->shouldQueue, $shouldQueue, $_message); + + if (is_callable($_expectation->_hook)) { + call_user_func($_expectation->_hook, $job, $command, $setupBeforeRun, $shouldQueue, $_expectation); + } + + return $_expectation->return; + } +} diff --git a/src/Testing/Queue/Contracts/RunOrQueueJobActionContractExpectation.php b/src/Testing/Queue/Contracts/RunOrQueueJobActionContractExpectation.php new file mode 100644 index 00000000..be809c5a --- /dev/null +++ b/src/Testing/Queue/Contracts/RunOrQueueJobActionContractExpectation.php @@ -0,0 +1,25 @@ +getCommand(); + } +} diff --git a/tests/Feature/Queue/Actions/DispatchChainJobsActionTest.php b/tests/Feature/Queue/Actions/DispatchChainJobsActionTest.php new file mode 100644 index 00000000..cc92f1b1 --- /dev/null +++ b/tests/Feature/Queue/Actions/DispatchChainJobsActionTest.php @@ -0,0 +1,101 @@ +makeAction(expectation: null); + $this->assertFalse($action->execute([])); + } + + /** + * @return array + */ + public function dataOneJob(): array + { + return [ + 'returns true on dispatch' => [ + static fn (self $self) => $self->assertOneJob(expected: true), + ], + 'returns false on dispatch' => [ + static fn (self $self) => $self->assertOneJob(expected: false), + ], + ]; + } + + /** + * @param Closure(static):void $assert + * + * @dataProvider dataOneJob + */ + public function testOneJob(Closure $assert): void + { + $assert($this); + } + + public function assertOneJob(bool $expected): void + { + $job = new WithoutCommandJob(name: 'Hello'); + + $action = $this->makeAction(new DispatchJobActionContractExpectation(return: $expected, job: $job)); + + Assert::assertEquals(expected: $expected, actual: $action->execute([$job])); + } + + /** + * @return array + */ + public function dataChainJob(): array + { + return [ + 'returns true on dispatch' => [ + static fn (self $self) => $self->assertChainJob(expected: true), + ], + 'returns false on dispatch' => [ + static fn (self $self) => $self->assertChainJob(expected: false), + ], + ]; + } + + /** + * @param Closure(static):void $assert + * + * @dataProvider dataChainJob + */ + public function testChainJob(Closure $assert): void + { + $assert($this); + } + + public function assertChainJob(bool $expected): void + { + $job = new WithoutCommandJob(name: 'Hello'); + $job2 = new WithoutCommandJob(name: 'Hello2'); + + $action = $this->makeAction(new DispatchJobActionContractExpectation(return: $expected, job: $job)); + + Assert::assertEquals(expected: $expected, actual: $action->execute([$job, $job2])); + + Assert::assertEquals([serialize($job2)], $job->chained, 'Job should be changed'); + Assert::assertEquals('default', $job->chainQueue, 'Job should be changed'); + + Assert::assertEmpty($job2->chained, 'Job2 should not be changed'); + Assert::assertNull($job2->chainQueue, 'Job2 should not be changed'); + } + + protected function makeAction(?DispatchJobActionContractExpectation $expectation): DispatchChainJobsAction + { + return new DispatchChainJobsAction(new DispatchJobActionContractAssert([$expectation])); + } +} diff --git a/tests/Feature/Queue/Actions/DispatchJobActionTest.php b/tests/Feature/Queue/Actions/DispatchJobActionTest.php new file mode 100644 index 00000000..2acc1131 --- /dev/null +++ b/tests/Feature/Queue/Actions/DispatchJobActionTest.php @@ -0,0 +1,23 @@ +execute(new WithoutCommandJob('test')); + + Queue::assertPushed(WithoutCommandJob::class, static fn ($job) => $job->getName() === 'test'); + } +} diff --git a/tests/Feature/Queue/Actions/RunJobActionTest.php b/tests/Feature/Queue/Actions/RunJobActionTest.php new file mode 100644 index 00000000..1a1d9a74 --- /dev/null +++ b/tests/Feature/Queue/Actions/RunJobActionTest.php @@ -0,0 +1,96 @@ +runJobAction = $this->make(RunJobAction::class); + $this->command = new Command(); + } + + /** + * @return array + */ + public function data(): array + { + return [ + 'with command' => [ + static fn (self $self, Job $job, string|Command $expectedResult) => Assert::assertEquals( + expected: $expectedResult, + actual: $self->runJobAction->execute(job: $job, command: $self->command), + ), + ], + 'with command, handle method' => [ + static fn (self $self, Job $job, string|Command $expectedResult) => Assert::assertEquals( + expected: $expectedResult, + actual: $self->runJobAction->execute(job: $job, command: $self->command, method: 'handle'), + ), + ], + 'without command' => [ + static fn (self $self, Job $job, string|Command $expectedResult) => Assert::assertEquals( + // When command is not passed, null is expected + expected: $expectedResult === $self->command ? null : $expectedResult, + actual: $self->runJobAction->execute(job: $job), + ), + ], + 'without command, handle method' => [ + static fn (self $self, Job $job, string|Command $expectedResult) => Assert::assertEquals( + expected: $expectedResult === $self->command ? null : $expectedResult, + actual: $self->runJobAction->execute(job: $job, method: 'handle'), + ), + ], + ]; + } + + /** + * @param AssertClosure $assert + * + * @dataProvider data + */ + public function testWithoutCommandJob(Closure $assert): void + { + $assert($this, new WithoutCommandJob('hello world!'), 'hello world!'); + } + + public function testWithoutCommandJobNonExistingMethod(): void + { + $this->expectException(MethodInJobIsNotDefinedException::class); + $this->expectExceptionMessage(sprintf( + 'Given job <%s> does not contain desired method <%s>', + WithoutCommandJob::class, + 'handleJob' + )); + $this->runJobAction->execute(job: new WithoutCommandJob('hello world!'), method: 'handleJob'); + } + + /** + * @param AssertClosure $assert + * + * @dataProvider data + */ + public function testCommandJob(Closure $assert): void + { + $assert($this, new CommandJob(), $this->command); + } +} diff --git a/tests/Feature/Queue/Actions/TestCommand.php b/tests/Feature/Queue/Actions/TestCommand.php new file mode 100644 index 00000000..2c1a893d --- /dev/null +++ b/tests/Feature/Queue/Actions/TestCommand.php @@ -0,0 +1,12 @@ +name; + } + + // Test DI + public function handle(Container $container): string + { + return $this->name; + } +} diff --git a/tests/Feature/TestCase.php b/tests/Feature/TestCase.php index dd9ffc5f..c3921122 100644 --- a/tests/Feature/TestCase.php +++ b/tests/Feature/TestCase.php @@ -24,4 +24,24 @@ protected function getPackageProviders($app) { return [LaraStrictServiceProvider::class]; } + + /** + * Get the available container instance. + * + * @template T of object + * + * @param class-string $class + * @param array $parameters + * + * @return T + */ + protected function make(string $class, array $parameters = []): object + { + $instance = $this->app() + ->make($class, $parameters); + + assert(assertion: $instance instanceof $class, description: 'Instance is not of type ' . $class); + + return $instance; + } } diff --git a/tests/Unit/Queue/Actions/RunOrQueueJobActionTest.php b/tests/Unit/Queue/Actions/RunOrQueueJobActionTest.php new file mode 100644 index 00000000..395fe8a4 --- /dev/null +++ b/tests/Unit/Queue/Actions/RunOrQueueJobActionTest.php @@ -0,0 +1,304 @@ + + */ + public function dataNoCommand(): array + { + $job = new WithoutCommandJob('Test'); + + return [ + 'dispatches job' => [ + static fn () => Assert::assertEquals( + expected: null, + actual: self::makeAction( + expectedDispatchJob: true, + expectedRunJob: false, + expectedCommand: null, + job: $job, + )->execute(job: $job), + ), + ], + 'setupBeforeRun passed but not used, dispatches job' => [ + static fn () => Assert::assertEquals( + expected: null, + actual: self::makeAction( + expectedDispatchJob: true, + expectedRunJob: false, + expectedCommand: null, + job: $job, + )->execute(job: $job, setupBeforeRun: static function (Job $job): never { + Assert::fail('setupBeforeRun should not be called'); + }), + ), + ], + '$shouldQueue=true does nothing, dispatches job' => [ + static fn () => Assert::assertEquals( + expected: null, + actual: self::makeAction( + expectedDispatchJob: true, + expectedRunJob: false, + expectedCommand: null, + job: $job, + )->execute(job: $job, shouldQueue: true), + ), + ], + '$shouldQueue=false forces the job tu run' => [ + static fn () => Assert::assertEquals( + expected: 'Test', + actual: self::makeAction( + expectedDispatchJob: false, + expectedRunJob: true, + expectedCommand: null, + job: $job, + )->execute(job: $job, shouldQueue: false), + ), + ], + '$shouldQueue=false forces the job tu run, setupBeforeRun called' => [static function () use ($job) { + $setupBeforeRunCalled = false; + Assert::assertEquals( + expected: 'Test', + actual: self::makeAction( + expectedDispatchJob: false, + expectedRunJob: true, + expectedCommand: null, + job: $job, + )->execute( + job: $job, + setupBeforeRun: static function (Job $givenJob) use ($job, &$setupBeforeRunCalled) { + Assert::assertSame($job, $givenJob, 'Job should be same'); + $setupBeforeRunCalled = true; + }, + shouldQueue: false + ), + ); + Assert::assertTrue($setupBeforeRunCalled, 'setupBeforeRun should be trigered'); + }], + ]; + } + + /** + * @param Closure():void $assert + * + * @dataProvider dataNoCommand + */ + public function testNoCommand(Closure $assert): void + { + $assert(); + } + + /** + * @return array + */ + public function dataWithCommand(): array + { + $job = new WithoutCommandJob('Test'); + $command = $this->makeCommand([]); + + return [ + 'queue not set, runs the job' => [ + static fn () => Assert::assertEquals( + expected: 'Test', + actual: self::makeAction( + expectedDispatchJob: false, + expectedRunJob: true, + expectedCommand: $command, + job: $job, + )->execute(job: $job, command: $command), + ), + ], + '$shouldQueue=true forces to dispatches job' => [ + static fn () => Assert::assertEquals( + expected: null, + actual: self::makeAction( + expectedDispatchJob: true, + expectedRunJob: false, + expectedCommand: $command, + job: $job, + )->execute(job: $job, shouldQueue: true, command: $command), + ), + ], + '$shouldQueue=false does nothing, runs the job' => [ + static fn () => Assert::assertEquals( + expected: 'Test', + actual: self::makeAction( + expectedDispatchJob: false, + expectedRunJob: true, + expectedCommand: $command, + job: $job, + )->execute(job: $job, shouldQueue: false, command: $command), + ), + ], + '$shouldQueue=false forces the job tu run, setupBeforeRun called' => [static function () use ( + $job, + $command + ) { + $setupBeforeRunCalled = false; + Assert::assertEquals( + expected: 'Test', + actual: self::makeAction( + expectedDispatchJob: false, + expectedRunJob: true, + expectedCommand: $command, + job: $job, + )->execute( + job: $job, + command: $command, + setupBeforeRun: static function (Job $givenJob) use ($job, &$setupBeforeRunCalled) { + Assert::assertSame($job, $givenJob, 'Job should be same'); + $setupBeforeRunCalled = true; + }, + shouldQueue: false + ), + ); + Assert::assertTrue($setupBeforeRunCalled, 'setupBeforeRun should be trigered'); + }], + ]; + } + + /** + * @param Closure():void $assert + * + * @dataProvider dataWithCommand + */ + public function testWithCommand(Closure $assert): void + { + $assert(); + } + + /** + * @return array + */ + public function dataWithCommandAndQueue(): array + { + $job = new WithoutCommandJob('Test'); + $command = $this->makeCommand([ + '--queue' => true, + ]); + + return [ + 'queue set, dispatches the queue' => [ + static fn () => Assert::assertEquals( + expected: null, + actual: self::makeAction( + expectedDispatchJob: true, + expectedRunJob: false, + expectedCommand: $command, + job: $job, + )->execute(job: $job, command: $command), + ), + ], + '$shouldQueue=true forces to dispatches job' => [ + static fn () => Assert::assertEquals( + expected: null, + actual: self::makeAction( + expectedDispatchJob: true, + expectedRunJob: false, + expectedCommand: $command, + job: $job, + )->execute(job: $job, shouldQueue: true, command: $command), + ), + ], + '$shouldQueue=false does nothing, runs the job' => [ + static fn () => Assert::assertEquals( + expected: 'Test', + actual: self::makeAction( + expectedDispatchJob: false, + expectedRunJob: true, + expectedCommand: $command, + job: $job, + )->execute(job: $job, shouldQueue: false, command: $command), + ), + ], + '$shouldQueue=false forces the job tu run, setupBeforeRun called' => [static function () use ( + $job, + $command + ) { + $setupBeforeRunCalled = false; + Assert::assertEquals( + expected: 'Test', + actual: self::makeAction( + expectedDispatchJob: false, + expectedRunJob: true, + expectedCommand: $command, + job: $job, + )->execute( + job: $job, + command: $command, + setupBeforeRun: static function (Job $givenJob) use ($job, &$setupBeforeRunCalled) { + Assert::assertSame($job, $givenJob, 'Job should be same'); + $setupBeforeRunCalled = true; + }, + shouldQueue: false + ), + ); + Assert::assertTrue($setupBeforeRunCalled, 'setupBeforeRun should be trigered'); + }], + ]; + } + + /** + * @param Closure():void $assert + * + * @dataProvider dataWithCommandAndQueue + */ + public function testWithCommandAndQueue(Closure $assert): void + { + $assert(); + } + + protected static function makeAction( + bool $expectedDispatchJob, + bool $expectedRunJob, + ?Command $expectedCommand, + Job $job, + ): RunOrQueueJobAction { + return new RunOrQueueJobAction( + runJobAction: new RunJobActionContractAssert([ + $expectedRunJob === false ? null : new RunJobActionContractExpectation( + return: 'Test', + job: $job, + command: $expectedCommand, + ), + ]), + dispatchJobAction: new DispatchJobActionContractAssert([ + $expectedDispatchJob === false ? null : new DispatchJobActionContractExpectation( + return: true, + job: $job, + ), + ]) + ); + } + + /** + * @param array $params + */ + protected static function makeCommand(array $params): Command + { + $command = new TestCommand(); + $definition = $command->getDefinition(); + $command->setInput(new ArrayInput($params, $definition)); + + return $command; + } +}