- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
207 lines
5.4 KiB
PHP
207 lines
5.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Framework\CommandBus\CommandHandlerDescriptor;
|
|
use App\Framework\CommandBus\CommandHandlersCollection;
|
|
use App\Framework\CommandBus\DefaultCommandBus;
|
|
use App\Framework\CommandBus\Exceptions\NoHandlerFound;
|
|
use App\Framework\CommandBus\ShouldQueue;
|
|
use App\Framework\Context\ContextType;
|
|
use App\Framework\Context\ExecutionContext;
|
|
use App\Framework\DI\DefaultContainer;
|
|
use App\Framework\Logging\Logger;
|
|
use App\Framework\Queue\Queue;
|
|
|
|
beforeEach(function () {
|
|
$this->container = new DefaultContainer();
|
|
$this->executionContext = createTestExecutionContext();
|
|
$this->queue = new TestQueue();
|
|
$this->logger = new TestLogger();
|
|
});
|
|
|
|
test('command handlers collection returns correct handler', function () {
|
|
$descriptor = new CommandHandlerDescriptor(TestCommandHandler::class, 'handle', TestCommand::class);
|
|
$collection = new CommandHandlersCollection($descriptor);
|
|
|
|
$retrieved = $collection->get(TestCommand::class);
|
|
|
|
expect($retrieved)->toBe($descriptor)
|
|
->and($retrieved->class)->toBe(TestCommandHandler::class)
|
|
->and($retrieved->method)->toBe('handle')
|
|
->and($retrieved->command)->toBe(TestCommand::class);
|
|
});
|
|
|
|
test('command handlers collection returns null for non-existent command', function () {
|
|
$collection = new CommandHandlersCollection();
|
|
|
|
$result = $collection->get('NonExistentCommand');
|
|
|
|
expect($result)->toBeNull();
|
|
});
|
|
|
|
test('command handler descriptor stores class and method', function () {
|
|
$descriptor = new CommandHandlerDescriptor('TestClass', 'testMethod', 'TestCommand');
|
|
|
|
expect($descriptor->class)->toBe('TestClass')
|
|
->and($descriptor->method)->toBe('testMethod')
|
|
->and($descriptor->command)->toBe('TestCommand');
|
|
});
|
|
|
|
test('no handler found exception contains command class', function () {
|
|
$commandClass = 'TestCommand';
|
|
$exception = NoHandlerFound::forCommand($commandClass);
|
|
|
|
expect($exception->getMessage())
|
|
->toContain($commandClass);
|
|
});
|
|
|
|
test('dispatch executes command handler directly', function () {
|
|
$command = new TestCommand('test-data');
|
|
$handler = new TestCommandHandler();
|
|
$handlerDescriptor = new CommandHandlerDescriptor(TestCommandHandler::class, 'handle', TestCommand::class);
|
|
$commandHandlers = new CommandHandlersCollection($handlerDescriptor);
|
|
|
|
$commandBus = new DefaultCommandBus(
|
|
$commandHandlers,
|
|
$this->container,
|
|
$this->executionContext,
|
|
$this->queue,
|
|
$this->logger,
|
|
[] // No middlewares for basic tests
|
|
);
|
|
|
|
$this->container->instance(TestCommandHandler::class, $handler);
|
|
|
|
$result = $commandBus->dispatch($command);
|
|
|
|
expect($result)->toBe('Handled: test-data');
|
|
});
|
|
|
|
test('dispatch throws exception when no handler found', function () {
|
|
$command = new TestCommand('test-data');
|
|
$commandHandlers = new CommandHandlersCollection(); // Empty collection
|
|
|
|
$commandBus = new DefaultCommandBus(
|
|
$commandHandlers,
|
|
$this->container,
|
|
$this->executionContext,
|
|
$this->queue,
|
|
$this->logger,
|
|
[] // No middlewares for basic tests
|
|
);
|
|
|
|
expect(fn () => $commandBus->dispatch($command))
|
|
->toThrow(NoHandlerFound::class);
|
|
});
|
|
|
|
test('should queue attribute is recognized', function () {
|
|
$command = new QueueableTestCommand('queue-data');
|
|
$commandHandlers = new CommandHandlersCollection(); // Empty collection to test queueing
|
|
|
|
$commandBus = new DefaultCommandBus(
|
|
$commandHandlers,
|
|
$this->container,
|
|
$this->executionContext,
|
|
$this->queue,
|
|
$this->logger,
|
|
[] // No middlewares for basic tests
|
|
);
|
|
|
|
// For queued commands, the result should be null
|
|
$result = $commandBus->dispatch($command);
|
|
|
|
expect($result)->toBeNull()
|
|
->and($this->queue->wasUsed())->toBeTrue();
|
|
});
|
|
|
|
// Test fixtures
|
|
class TestCommand
|
|
{
|
|
public function __construct(
|
|
public readonly string $data
|
|
) {
|
|
}
|
|
}
|
|
|
|
#[ShouldQueue]
|
|
class QueueableTestCommand
|
|
{
|
|
public function __construct(
|
|
public readonly string $data
|
|
) {
|
|
}
|
|
}
|
|
|
|
class TestCommandHandler
|
|
{
|
|
public function handle(TestCommand $command): string
|
|
{
|
|
return 'Handled: ' . $command->data;
|
|
}
|
|
}
|
|
|
|
// ExecutionContext is final, so we create a simple instance
|
|
function createTestExecutionContext(): ExecutionContext
|
|
{
|
|
return new ExecutionContext(ContextType::WEB);
|
|
}
|
|
|
|
class TestQueue implements Queue
|
|
{
|
|
private bool $used = false;
|
|
|
|
private array $jobs = [];
|
|
|
|
public function push(object $job): void
|
|
{
|
|
$this->used = true;
|
|
$this->jobs[] = $job;
|
|
}
|
|
|
|
public function pop(): ?object
|
|
{
|
|
return array_shift($this->jobs);
|
|
}
|
|
|
|
public function wasUsed(): bool
|
|
{
|
|
return $this->used;
|
|
}
|
|
}
|
|
|
|
class TestLogger implements Logger
|
|
{
|
|
public function emergency(string $message, array $context = []): void
|
|
{
|
|
}
|
|
|
|
public function alert(string $message, array $context = []): void
|
|
{
|
|
}
|
|
|
|
public function critical(string $message, array $context = []): void
|
|
{
|
|
}
|
|
|
|
public function error(string $message, array $context = []): void
|
|
{
|
|
}
|
|
|
|
public function warning(string $message, array $context = []): void
|
|
{
|
|
}
|
|
|
|
public function notice(string $message, array $context = []): void
|
|
{
|
|
}
|
|
|
|
public function info(string $message, array $context = []): void
|
|
{
|
|
}
|
|
|
|
public function debug(string $message, array $context = []): void
|
|
{
|
|
}
|
|
}
|