Skip to content

Commit

Permalink
[10.x] Support asserting against chained batches (#49003)
Browse files Browse the repository at this point in the history
* support asserting against chained batches

* work on chained batch truth tests

* work on chained batch truth tests

* more work on chained batch truth tests

* continue working on chained batch truth tests

* Apply fixes from StyleCI

* attempting to unify chained batch testing

* simplify code

* add another test

* simplify code

---------

Co-authored-by: StyleCI Bot <[email protected]>
  • Loading branch information
taylorotwell and StyleCIBot authored Nov 17, 2023
1 parent e72327a commit e275b87
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 42 deletions.
24 changes: 18 additions & 6 deletions src/Illuminate/Bus/ChainedBatch.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,19 @@ public static function prepareNestedBatches(Collection $jobs): Collection
*/
public function handle()
{
$batch = new PendingBatch(Container::getInstance(), $this->jobs);
$this->attachRemainderOfChainToEndOfBatch(
$this->toPendingBatch()
)->dispatch();
}

/**
* Convert the chained batch instance into a pending batch.
*
* @return \Illuminate\Bus\PendingBatch
*/
public function toPendingBatch()
{
$batch = Container::getInstance()->make(Dispatcher::class)->batch($this->jobs);

$batch->name = $this->name;
$batch->options = $this->options;
Expand All @@ -85,8 +97,6 @@ public function handle()
$batch->onConnection($this->connection);
}

$this->dispatchRemainderOfChainAfterBatch($batch);

foreach ($this->chainCatchCallbacks ?? [] as $callback) {
$batch->catch(function (Batch $batch, ?Throwable $exception) use ($callback) {
if (! $batch->allowsFailures()) {
Expand All @@ -95,16 +105,16 @@ public function handle()
});
}

$batch->dispatch();
return $batch;
}

/**
* Move the remainder of the chain to a "finally" batch callback.
*
* @param \Illuminate\Bus\PendingBatch $batch
* @return
* @return \Illuminate\Bus\PendingBatch
*/
protected function dispatchRemainderOfChainAfterBatch(PendingBatch $batch)
protected function attachRemainderOfChainToEndOfBatch(PendingBatch $batch)
{
if (! empty($this->chained)) {
$next = unserialize(array_shift($this->chained));
Expand All @@ -126,5 +136,7 @@ protected function dispatchRemainderOfChainAfterBatch(PendingBatch $batch)

$this->chained = [];
}

return $batch;
}
}
74 changes: 38 additions & 36 deletions src/Illuminate/Support/Testing/Fakes/BusFake.php
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,12 @@ public function assertChained(array $expectedChain)

if ($command instanceof Closure) {
[$command, $callback] = [$this->firstClosureParameterType($command), $command];
} elseif ($command instanceof ChainedBatchTruthTest) {
$instance = $command;

$command = ChainedBatch::class;

$callback = fn ($job) => $instance($job->toPendingBatch());
} elseif (! is_string($command)) {
$instance = $command;

Expand All @@ -349,9 +355,7 @@ public function assertChained(array $expectedChain)
"The expected [{$command}] job was not dispatched."
);

$this->isChainOfObjects($expectedChain)
? $this->assertDispatchedWithChainOfObjects($command, $expectedChain, $callback)
: $this->assertDispatchedWithChainOfClasses($command, $expectedChain, $callback);
$this->assertDispatchedWithChainOfObjects($command, $expectedChain, $callback);
}

/**
Expand Down Expand Up @@ -388,7 +392,7 @@ public function assertDispatchedWithoutChain($command, $callback = null)
"The expected [{$command}] job was not dispatched."
);

$this->assertDispatchedWithChainOfClasses($command, [], $callback);
$this->assertDispatchedWithChainOfObjects($command, [], $callback);
}

/**
Expand All @@ -401,48 +405,46 @@ public function assertDispatchedWithoutChain($command, $callback = null)
*/
protected function assertDispatchedWithChainOfObjects($command, $expectedChain, $callback)
{
$chain = collect($expectedChain)->map(fn ($job) => serialize($job))->all();
$chain = $expectedChain;

PHPUnit::assertTrue(
$this->dispatched($command, $callback)->filter(
fn ($job) => $job->chained == $chain
)->isNotEmpty(),
$this->dispatched($command, $callback)->filter(function ($job) use ($chain) {
if (count($chain) !== count($job->chained)) {
return false;
}

foreach ($job->chained as $index => $serializedChainedJob) {
if ($chain[$index] instanceof ChainedBatchTruthTest) {
$chainedBatch = unserialize($serializedChainedJob);

if (! $chainedBatch instanceof ChainedBatch ||
! $chain[$index]($chainedBatch->toPendingBatch())) {
return false;
}
} elseif (is_string($chain[$index])) {
if ($chain[$index] != get_class(unserialize($serializedChainedJob))) {
return false;
}
} elseif (serialize($chain[$index]) != $serializedChainedJob) {
return false;
}
}

return true;
})->isNotEmpty(),
'The expected chain was not dispatched.'
);
}

/**
* Assert if a job was dispatched with chained jobs based on a truth-test callback.
* Create a new assertion about a chained batch.
*
* @param string $command
* @param array $expectedChain
* @param callable|null $callback
* @return void
*/
protected function assertDispatchedWithChainOfClasses($command, $expectedChain, $callback)
{
$matching = $this->dispatched($command, $callback)->map->chained->map(function ($chain) {
return collect($chain)->map(
fn ($job) => get_class(unserialize($job))
);
})->filter(
fn ($chain) => $chain->all() === $expectedChain
);

PHPUnit::assertTrue(
$matching->isNotEmpty(), 'The expected chain was not dispatched.'
);
}

/**
* Determine if the given chain is entirely composed of objects.
*
* @param array $chain
* @return bool
* @param \Closure $callback
* @return \Illuminate\Support\Testing\Fakes\ChainedBatchTruthTest
*/
protected function isChainOfObjects($chain)
public function chainedBatch(Closure $callback)
{
return ! collect($chain)->contains(fn ($job) => ! is_object($job));
return new ChainedBatchTruthTest($callback);
}

/**
Expand Down
37 changes: 37 additions & 0 deletions src/Illuminate/Support/Testing/Fakes/ChainedBatchTruthTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Illuminate\Support\Testing\Fakes;

use Closure;

class ChainedBatchTruthTest
{
/**
* The underlying truth test.
*
* @var \Closure
*/
protected $callback;

/**
* Create a new truth test instance.
*
* @param \Closure $callback
* @return void
*/
public function __construct(Closure $callback)
{
$this->callback = $callback;
}

/**
* Invoke the truth test with the given pending batch.
*
* @param \Illuminate\Bus\PendingBatch
* @return bool
*/
public function __invoke($pendingBatch)
{
return call_user_func($this->callback, $pendingBatch);
}
}
50 changes: 50 additions & 0 deletions tests/Support/SupportTestingBusFakeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

use Illuminate\Bus\Batch;
use Illuminate\Bus\Queueable;
use Illuminate\Container\Container;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Contracts\Bus\QueueingDispatcher;
use Illuminate\Support\Testing\Fakes\BatchRepositoryFake;
use Illuminate\Support\Testing\Fakes\BusFake;
Expand Down Expand Up @@ -468,6 +470,10 @@ public function testAssertNothingDispatched()

public function testAssertChained()
{
Container::setInstance($container = new Container);

$container->instance(Dispatcher::class, $this->fake);

$this->fake->chain([
new ChainedJobStub,
])->dispatch();
Expand All @@ -485,6 +491,50 @@ public function testAssertChained()
ChainedJobStub::class,
OtherBusJobStub::class,
]);

$this->fake->chain([
new ChainedJobStub,
$this->fake->batch([
new OtherBusJobStub,
new OtherBusJobStub,
]),
new ChainedJobStub,
])->dispatch();

$this->fake->assertChained([
ChainedJobStub::class,
$this->fake->chainedBatch(function ($pendingBatch) {
return $pendingBatch->jobs->count() === 2;
}),
ChainedJobStub::class,
]);

$this->fake->assertChained([
new ChainedJobStub,
$this->fake->chainedBatch(function ($pendingBatch) {
return $pendingBatch->jobs->count() === 2;
}),
new ChainedJobStub,
]);

$this->fake->chain([
$this->fake->batch([
new OtherBusJobStub,
new OtherBusJobStub,
]),
new ChainedJobStub,
new ChainedJobStub,
])->dispatch();

$this->fake->assertChained([
$this->fake->chainedBatch(function ($pendingBatch) {
return $pendingBatch->jobs->count() === 2;
}),
ChainedJobStub::class,
ChainedJobStub::class,
]);

Container::setInstance(null);
}

public function testAssertDispatchedWithIgnoreClass()
Expand Down

0 comments on commit e275b87

Please sign in to comment.