feat: CI/CD pipeline setup complete - Ansible playbooks updated, secrets configured, workflow ready
This commit is contained in:
@@ -1,232 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Framework\Logging\LogLevel;
|
||||
use App\Framework\Logging\LogRecord;
|
||||
use App\Framework\Logging\Processors\ExceptionProcessor;
|
||||
use App\Framework\Logging\ValueObjects\LogContext;
|
||||
|
||||
describe('ExceptionProcessor', function () {
|
||||
beforeEach(function () {
|
||||
$this->timestamp = new DateTimeImmutable('2024-01-15 10:30:45', new DateTimeZone('Europe/Berlin'));
|
||||
$this->processor = new ExceptionProcessor();
|
||||
});
|
||||
|
||||
describe('constructor', function () {
|
||||
it('can be instantiated with default config', function () {
|
||||
$processor = new ExceptionProcessor();
|
||||
|
||||
expect($processor instanceof ExceptionProcessor)->toBeTrue();
|
||||
});
|
||||
|
||||
it('can be instantiated with custom config', function () {
|
||||
$processor = new ExceptionProcessor(
|
||||
includeStackTraces: false,
|
||||
traceDepth: 5
|
||||
);
|
||||
|
||||
expect($processor instanceof ExceptionProcessor)->toBeTrue();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getPriority()', function () {
|
||||
it('returns priority 15', function () {
|
||||
expect($this->processor->getPriority())->toBe(15);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getName()', function () {
|
||||
it('returns name exception', function () {
|
||||
expect($this->processor->getName())->toBe('exception');
|
||||
});
|
||||
});
|
||||
|
||||
describe('processRecord()', function () {
|
||||
it('returns record unchanged when no exception present', function () {
|
||||
$record = new LogRecord(
|
||||
message: 'Test message',
|
||||
context: LogContext::empty(),
|
||||
level: LogLevel::INFO,
|
||||
timestamp: $this->timestamp
|
||||
);
|
||||
|
||||
$processed = $this->processor->processRecord($record);
|
||||
|
||||
expect($processed->getExtras())->toBeEmpty();
|
||||
});
|
||||
|
||||
it('formats basic exception information', function () {
|
||||
$exception = new RuntimeException('Test error', 123);
|
||||
|
||||
$record = new LogRecord(
|
||||
message: 'Error occurred',
|
||||
context: LogContext::withData(['exception' => $exception]),
|
||||
level: LogLevel::ERROR,
|
||||
timestamp: $this->timestamp
|
||||
);
|
||||
|
||||
$processed = $this->processor->processRecord($record);
|
||||
|
||||
$exceptionData = $processed->getExtra('exception');
|
||||
|
||||
expect($exceptionData)->toBeArray();
|
||||
expect($exceptionData['class'])->toBe('RuntimeException');
|
||||
expect($exceptionData['message'])->toBe('Test error');
|
||||
expect($exceptionData['code'])->toBe(123);
|
||||
expect(isset($exceptionData['file']))->toBeTrue();
|
||||
expect(isset($exceptionData['line']))->toBeTrue();
|
||||
});
|
||||
|
||||
it('includes stack trace by default', function () {
|
||||
$exception = new Exception('Test exception');
|
||||
|
||||
$record = new LogRecord(
|
||||
message: 'Error with trace',
|
||||
context: LogContext::withData(['exception' => $exception]),
|
||||
level: LogLevel::ERROR,
|
||||
timestamp: $this->timestamp
|
||||
);
|
||||
|
||||
$processed = $this->processor->processRecord($record);
|
||||
|
||||
$exceptionData = $processed->getExtra('exception');
|
||||
|
||||
expect(isset($exceptionData['trace']))->toBeTrue();
|
||||
expect($exceptionData['trace'])->toBeArray();
|
||||
});
|
||||
|
||||
it('excludes stack trace when disabled', function () {
|
||||
$processor = new ExceptionProcessor(includeStackTraces: false);
|
||||
$exception = new Exception('Test exception');
|
||||
|
||||
$record = new LogRecord(
|
||||
message: 'Error without trace',
|
||||
context: LogContext::withData(['exception' => $exception]),
|
||||
level: LogLevel::ERROR,
|
||||
timestamp: $this->timestamp
|
||||
);
|
||||
|
||||
$processed = $processor->processRecord($record);
|
||||
|
||||
$exceptionData = $processed->getExtra('exception');
|
||||
|
||||
expect(isset($exceptionData['trace']))->toBeFalse();
|
||||
});
|
||||
|
||||
it('handles nested exceptions', function () {
|
||||
$innerException = new InvalidArgumentException('Inner error');
|
||||
$outerException = new RuntimeException('Outer error', 0, $innerException);
|
||||
|
||||
$record = new LogRecord(
|
||||
message: 'Nested exception',
|
||||
context: LogContext::withData(['exception' => $outerException]),
|
||||
level: LogLevel::ERROR,
|
||||
timestamp: $this->timestamp
|
||||
);
|
||||
|
||||
$processed = $this->processor->processRecord($record);
|
||||
|
||||
$exceptionData = $processed->getExtra('exception');
|
||||
|
||||
expect($exceptionData['class'])->toBe('RuntimeException');
|
||||
expect(isset($exceptionData['previous']))->toBeTrue();
|
||||
expect($exceptionData['previous']['class'])->toBe('InvalidArgumentException');
|
||||
expect($exceptionData['previous']['message'])->toBe('Inner error');
|
||||
});
|
||||
|
||||
it('limits stack trace depth', function () {
|
||||
$processor = new ExceptionProcessor(traceDepth: 3);
|
||||
$exception = new Exception('Deep exception');
|
||||
|
||||
$record = new LogRecord(
|
||||
message: 'Deep trace',
|
||||
context: LogContext::withData(['exception' => $exception]),
|
||||
level: LogLevel::ERROR,
|
||||
timestamp: $this->timestamp
|
||||
);
|
||||
|
||||
$processed = $processor->processRecord($record);
|
||||
|
||||
$exceptionData = $processed->getExtra('exception');
|
||||
|
||||
expect(isset($exceptionData['trace']))->toBeTrue();
|
||||
expect(count($exceptionData['trace']))->toBeLessThanOrEqual(3);
|
||||
});
|
||||
|
||||
it('formats stack trace entries correctly', function () {
|
||||
$exception = new Exception('Test exception');
|
||||
|
||||
$record = new LogRecord(
|
||||
message: 'Error with trace',
|
||||
context: LogContext::withData(['exception' => $exception]),
|
||||
level: LogLevel::ERROR,
|
||||
timestamp: $this->timestamp
|
||||
);
|
||||
|
||||
$processed = $this->processor->processRecord($record);
|
||||
|
||||
$exceptionData = $processed->getExtra('exception');
|
||||
$trace = $exceptionData['trace'];
|
||||
|
||||
expect($trace)->toBeArray();
|
||||
if (count($trace) > 0) {
|
||||
$firstFrame = $trace[0];
|
||||
expect(isset($firstFrame['file']))->toBeTrue();
|
||||
expect(isset($firstFrame['line']))->toBeTrue();
|
||||
}
|
||||
});
|
||||
|
||||
it('includes function information in stack trace', function () {
|
||||
$exception = new Exception('Test exception');
|
||||
|
||||
$record = new LogRecord(
|
||||
message: 'Error',
|
||||
context: LogContext::withData(['exception' => $exception]),
|
||||
level: LogLevel::ERROR,
|
||||
timestamp: $this->timestamp
|
||||
);
|
||||
|
||||
$processed = $this->processor->processRecord($record);
|
||||
|
||||
$exceptionData = $processed->getExtra('exception');
|
||||
$trace = $exceptionData['trace'];
|
||||
|
||||
// At least one frame should have function info
|
||||
$hasFunctionInfo = false;
|
||||
foreach ($trace as $frame) {
|
||||
if (isset($frame['function'])) {
|
||||
$hasFunctionInfo = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
expect($hasFunctionInfo)->toBeTrue();
|
||||
});
|
||||
|
||||
it('handles exception without previous exception', function () {
|
||||
$exception = new Exception('Single exception');
|
||||
|
||||
$record = new LogRecord(
|
||||
message: 'Single error',
|
||||
context: LogContext::withData(['exception' => $exception]),
|
||||
level: LogLevel::ERROR,
|
||||
timestamp: $this->timestamp
|
||||
);
|
||||
|
||||
$processed = $this->processor->processRecord($record);
|
||||
|
||||
$exceptionData = $processed->getExtra('exception');
|
||||
|
||||
expect(isset($exceptionData['previous']))->toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
describe('readonly behavior', function () {
|
||||
it('is a final class', function () {
|
||||
$reflection = new ReflectionClass(ExceptionProcessor::class);
|
||||
|
||||
expect($reflection->isFinal())->toBeTrue();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user