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
61 changes: 61 additions & 0 deletions src/Laravel/Constraint/Bus/HasHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

namespace Craftzing\TestBench\Laravel\Constraint\Bus;

use Craftzing\TestBench\PHPUnit\Constraint\ProvidesAdditionalFailureDescription;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Support\Facades\Bus;
use InvalidArgumentException;
use PHPUnit\Framework\Constraint\Constraint;
use ReflectionClass;

use function class_exists;
use function gettype;
use function is_string;

final class HasHandler extends Constraint
{
use ProvidesAdditionalFailureDescription;

private readonly Dispatcher $bus;

public function __construct(
/* @var class-string */
private readonly string $handlerClassFQN,
) {
$this->bus = Bus::getFacadeRoot();
}

protected function matches(mixed $other): bool
{
is_string($other) or throw new InvalidArgumentException(
self::class . ' can only be evaluated for strings, got ' . gettype($other) . '.',
);
class_exists($other) or throw new InvalidArgumentException(
self::class . " can only be evaluated for existing classes, got $other.",
);
$message = new ReflectionClass($other)->newInstanceWithoutConstructor();
$actualHandler = $this->bus->getCommandHandler($message);

if ($actualHandler === false) {
$this->additionalFailureDescriptions[] = "$other has no handler mapped to it.";

return false;
}

if ($actualHandler::class !== $this->handlerClassFQN) {
$this->additionalFailureDescriptions[] = "$other has a different handler mapped to it.";

return false;
}

return true;
}

public function toString(): string
{
return 'has handler';
}
}
77 changes: 77 additions & 0 deletions src/Laravel/Constraint/Bus/HasHandlerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

declare(strict_types=1);

namespace Craftzing\TestBench\Laravel\Constraint\Bus;

use Craftzing\TestBench\PHPUnit\Doubles\SpyCallable;
use Illuminate\Support\Facades\Bus;
use InvalidArgumentException;
use Orchestra\Testbench\TestCase;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\TestWith;
use PHPUnit\Framework\ExpectationFailedException;
use stdClass;

/**
* @codeCoverageIgnore
*/
final class HasHandlerTest extends TestCase
{
#[Test]
#[TestWith([true], 'Boolean')]
#[TestWith([1], 'Integers')]
#[TestWith([['event']], 'Array')]
public function itCannotEvaluateUnsupportedValueTypes(mixed $value): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage(HasHandler::class . ' can only be evaluated for strings');

$this->assertThat($value, new HasHandler('SomeHandlerClassFCN'));
}

#[Test]
public function itCannotEvaluateStringThatAreNotExistingClasses(): void
{
$value = 'NotAClass';

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage(HasHandler::class . " can only be evaluated for existing classes, got $value.");

$this->assertThat($value, new HasHandler('SomeHandlerClassFCN'));
}

#[Test]
public function itFailsWhenNoHandlerIsMapped(): void
{
$messageClassFCN = stdClass::class;

$this->expectException(ExpectationFailedException::class);
$this->expectExceptionMessage('has handler');
$this->expectExceptionMessage('stdClass has no handler mapped to it');

$this->assertThat($messageClassFCN, new HasHandler('SomeHandlerClassFCN'));
}

#[Test]
public function itFailsWhenDifferentHandlerIsMapped(): void
{
$messageClassFCN = stdClass::class;
Bus::map([$messageClassFCN => SpyCallable::class]);

$this->expectException(ExpectationFailedException::class);
$this->expectExceptionMessage('has handler');
$this->expectExceptionMessage('stdClass has a different handler mapped to it');

$this->assertThat($messageClassFCN, new HasHandler('SomeHandlerClassFCN'));
}

#[Test]
public function itPassesWhenGivenHandlerIsMapped(): void
{
$messageClassFCN = stdClass::class;
Bus::map([$messageClassFCN => SpyCallable::class]);

$this->assertThat($messageClassFCN, new HasHandler(SpyCallable::class));
}
}