feat: CI/CD pipeline setup complete - Ansible playbooks updated, secrets configured, workflow ready
This commit is contained in:
320
tests/Unit/Framework/ErrorHandling/ErrorHandlerManagerTest.php
Normal file
320
tests/Unit/Framework/ErrorHandling/ErrorHandlerManagerTest.php
Normal file
@@ -0,0 +1,320 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Framework\ErrorHandling;
|
||||
|
||||
use App\Framework\ErrorHandling\ErrorHandlerManager;
|
||||
use App\Framework\ErrorHandling\ErrorHandlerRegistry;
|
||||
use App\Framework\ErrorHandling\Handlers\ErrorHandlerInterface;
|
||||
use App\Framework\ErrorHandling\Handlers\ErrorHandlerPriority;
|
||||
use App\Framework\ErrorHandling\Handlers\HandlerResult;
|
||||
use App\Framework\Logging\Logger;
|
||||
|
||||
describe('ErrorHandlerManager', function () {
|
||||
beforeEach(function () {
|
||||
$this->registry = new ErrorHandlerRegistry();
|
||||
$this->manager = new ErrorHandlerManager($this->registry);
|
||||
});
|
||||
|
||||
it('executes handlers in priority order', function () {
|
||||
$executionOrder = [];
|
||||
|
||||
$highPriorityHandler = new class ($executionOrder) implements ErrorHandlerInterface {
|
||||
public function __construct(private array &$executionOrder) {}
|
||||
|
||||
public function canHandle(\Throwable $exception): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handle(\Throwable $exception): HandlerResult
|
||||
{
|
||||
$this->executionOrder[] = 'high';
|
||||
return HandlerResult::create(
|
||||
handled: true,
|
||||
message: 'High priority handler'
|
||||
);
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'high_priority';
|
||||
}
|
||||
|
||||
public function getPriority(): ErrorHandlerPriority
|
||||
{
|
||||
return ErrorHandlerPriority::HIGH;
|
||||
}
|
||||
};
|
||||
|
||||
$lowPriorityHandler = new class ($executionOrder) implements ErrorHandlerInterface {
|
||||
public function __construct(private array &$executionOrder) {}
|
||||
|
||||
public function canHandle(\Throwable $exception): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handle(\Throwable $exception): HandlerResult
|
||||
{
|
||||
$this->executionOrder[] = 'low';
|
||||
return HandlerResult::create(
|
||||
handled: true,
|
||||
message: 'Low priority handler'
|
||||
);
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'low_priority';
|
||||
}
|
||||
|
||||
public function getPriority(): ErrorHandlerPriority
|
||||
{
|
||||
return ErrorHandlerPriority::LOW;
|
||||
}
|
||||
};
|
||||
|
||||
$this->manager = $this->manager->register($lowPriorityHandler, $highPriorityHandler);
|
||||
|
||||
$exception = new \Exception('Test');
|
||||
$this->manager->handle($exception);
|
||||
|
||||
expect($executionOrder)->toBe(['high', 'low']);
|
||||
});
|
||||
|
||||
it('stops propagation when handler marks as final', function () {
|
||||
$called = [];
|
||||
|
||||
$finalHandler = new class ($called) implements ErrorHandlerInterface {
|
||||
public function __construct(private array &$called) {}
|
||||
|
||||
public function canHandle(\Throwable $exception): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handle(\Throwable $exception): HandlerResult
|
||||
{
|
||||
$this->called[] = 'final';
|
||||
return HandlerResult::create(
|
||||
handled: true,
|
||||
message: 'Final handler',
|
||||
isFinal: true
|
||||
);
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'final_handler';
|
||||
}
|
||||
|
||||
public function getPriority(): ErrorHandlerPriority
|
||||
{
|
||||
return ErrorHandlerPriority::HIGH;
|
||||
}
|
||||
};
|
||||
|
||||
$afterHandler = new class ($called) implements ErrorHandlerInterface {
|
||||
public function __construct(private array &$called) {}
|
||||
|
||||
public function canHandle(\Throwable $exception): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handle(\Throwable $exception): HandlerResult
|
||||
{
|
||||
$this->called[] = 'after';
|
||||
return HandlerResult::create(
|
||||
handled: true,
|
||||
message: 'After handler'
|
||||
);
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'after_handler';
|
||||
}
|
||||
|
||||
public function getPriority(): ErrorHandlerPriority
|
||||
{
|
||||
return ErrorHandlerPriority::LOW;
|
||||
}
|
||||
};
|
||||
|
||||
$this->manager = $this->manager->register($finalHandler, $afterHandler);
|
||||
|
||||
$exception = new \Exception('Test');
|
||||
$result = $this->manager->handle($exception);
|
||||
|
||||
expect($called)->toBe(['final']);
|
||||
expect($result->handled)->toBeTrue();
|
||||
});
|
||||
|
||||
it('skips handlers that cannot handle exception', function () {
|
||||
$specificHandler = new class implements ErrorHandlerInterface {
|
||||
public function canHandle(\Throwable $exception): bool
|
||||
{
|
||||
return $exception instanceof \InvalidArgumentException;
|
||||
}
|
||||
|
||||
public function handle(\Throwable $exception): HandlerResult
|
||||
{
|
||||
return HandlerResult::create(
|
||||
handled: true,
|
||||
message: 'Specific handler'
|
||||
);
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'specific';
|
||||
}
|
||||
|
||||
public function getPriority(): ErrorHandlerPriority
|
||||
{
|
||||
return ErrorHandlerPriority::HIGH;
|
||||
}
|
||||
};
|
||||
|
||||
$this->manager = $this->manager->register($specificHandler);
|
||||
|
||||
$exception = new \RuntimeException('Test');
|
||||
$result = $this->manager->handle($exception);
|
||||
|
||||
expect($result->handled)->toBeFalse();
|
||||
expect($result->results)->toBeEmpty();
|
||||
});
|
||||
|
||||
it('continues chain even if handler throws exception', function () {
|
||||
$called = [];
|
||||
|
||||
$failingHandler = new class ($called) implements ErrorHandlerInterface {
|
||||
public function __construct(private array &$called) {}
|
||||
|
||||
public function canHandle(\Throwable $exception): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handle(\Throwable $exception): HandlerResult
|
||||
{
|
||||
$this->called[] = 'failing';
|
||||
throw new \RuntimeException('Handler failed');
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'failing';
|
||||
}
|
||||
|
||||
public function getPriority(): ErrorHandlerPriority
|
||||
{
|
||||
return ErrorHandlerPriority::HIGH;
|
||||
}
|
||||
};
|
||||
|
||||
$workingHandler = new class ($called) implements ErrorHandlerInterface {
|
||||
public function __construct(private array &$called) {}
|
||||
|
||||
public function canHandle(\Throwable $exception): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handle(\Throwable $exception): HandlerResult
|
||||
{
|
||||
$this->called[] = 'working';
|
||||
return HandlerResult::create(
|
||||
handled: true,
|
||||
message: 'Working handler'
|
||||
);
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'working';
|
||||
}
|
||||
|
||||
public function getPriority(): ErrorHandlerPriority
|
||||
{
|
||||
return ErrorHandlerPriority::LOW;
|
||||
}
|
||||
};
|
||||
|
||||
$this->manager = $this->manager->register($failingHandler, $workingHandler);
|
||||
|
||||
$exception = new \Exception('Test');
|
||||
$result = $this->manager->handle($exception);
|
||||
|
||||
expect($called)->toBe(['failing', 'working']);
|
||||
expect($result->handled)->toBeTrue();
|
||||
});
|
||||
|
||||
it('aggregates results from multiple handlers', function () {
|
||||
$handler1 = new class implements ErrorHandlerInterface {
|
||||
public function canHandle(\Throwable $exception): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handle(\Throwable $exception): HandlerResult
|
||||
{
|
||||
return HandlerResult::create(
|
||||
handled: true,
|
||||
message: 'Handler 1',
|
||||
data: ['from' => 'handler1']
|
||||
);
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'handler1';
|
||||
}
|
||||
|
||||
public function getPriority(): ErrorHandlerPriority
|
||||
{
|
||||
return ErrorHandlerPriority::HIGH;
|
||||
}
|
||||
};
|
||||
|
||||
$handler2 = new class implements ErrorHandlerInterface {
|
||||
public function canHandle(\Throwable $exception): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handle(\Throwable $exception): HandlerResult
|
||||
{
|
||||
return HandlerResult::create(
|
||||
handled: true,
|
||||
message: 'Handler 2',
|
||||
data: ['from' => 'handler2']
|
||||
);
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'handler2';
|
||||
}
|
||||
|
||||
public function getPriority(): ErrorHandlerPriority
|
||||
{
|
||||
return ErrorHandlerPriority::LOW;
|
||||
}
|
||||
};
|
||||
|
||||
$this->manager = $this->manager->register($handler1, $handler2);
|
||||
|
||||
$exception = new \Exception('Test');
|
||||
$result = $this->manager->handle($exception);
|
||||
|
||||
expect($result->results)->toHaveCount(2);
|
||||
expect($result->getMessages())->toBe(['Handler 1', 'Handler 2']);
|
||||
|
||||
$combinedData = $result->getCombinedData();
|
||||
expect($combinedData)->toHaveKey('from');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user