feat: CI/CD pipeline setup complete - Ansible playbooks updated, secrets configured, workflow ready

This commit is contained in:
2025-10-31 01:39:24 +01:00
parent 55c04e4fd0
commit e26eb2aa12
601 changed files with 44184 additions and 32477 deletions

View File

@@ -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();
});
});
});