docs: consolidate documentation into organized structure

- Move 12 markdown files from root to docs/ subdirectories
- Organize documentation by category:
  • docs/troubleshooting/ (1 file)  - Technical troubleshooting guides
  • docs/deployment/      (4 files) - Deployment and security documentation
  • docs/guides/          (3 files) - Feature-specific guides
  • docs/planning/        (4 files) - Planning and improvement proposals

Root directory cleanup:
- Reduced from 16 to 4 markdown files in root
- Only essential project files remain:
  • CLAUDE.md (AI instructions)
  • README.md (Main project readme)
  • CLEANUP_PLAN.md (Current cleanup plan)
  • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements)

This improves:
 Documentation discoverability
 Logical organization by purpose
 Clean root directory
 Better maintainability
This commit is contained in:
2025-10-05 11:05:04 +02:00
parent 887847dde6
commit 5050c7d73a
36686 changed files with 196456 additions and 12398919 deletions

View File

@@ -0,0 +1,174 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Framework\Logging\Processors;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\LogRecord;
use App\Framework\Logging\Processors\ExceptionEnrichmentProcessor;
use App\Framework\Logging\ValueObjects\LogContext;
use DateTimeImmutable;
use Exception;
use InvalidArgumentException;
use RuntimeException;
beforeEach(function () {
$this->processor = new ExceptionEnrichmentProcessor();
$this->timestamp = new DateTimeImmutable();
});
it('enriches log record with basic exception information', function () {
$exception = new RuntimeException('Test error message', 123);
$context = LogContext::withData(['exception' => $exception]);
$record = new LogRecord(
message: 'An error occurred',
context: $context,
level: LogLevel::ERROR,
timestamp: $this->timestamp
);
$enrichedRecord = $this->processor->processRecord($record);
$extras = $enrichedRecord->getExtras();
expect($extras['exception_class'])->toBe(RuntimeException::class);
expect($extras['exception_message'])->toBe('Test error message');
expect($extras['exception_code'])->toBe(123);
expect($extras['exception_file'])->toBeString();
expect($extras['exception_line'])->toBeInt();
});
it('includes formatted stack trace', function () {
$exception = new Exception('Test exception');
$context = LogContext::withData(['exception' => $exception]);
$record = new LogRecord(
message: 'Error with stack trace',
context: $context,
level: LogLevel::ERROR,
timestamp: $this->timestamp
);
$enrichedRecord = $this->processor->processRecord($record);
$extras = $enrichedRecord->getExtras();
expect($extras['stack_trace'])->toBeArray();
expect($extras['stack_trace_short'])->toBeString();
expect($extras['stack_trace'])->not->toBeEmpty();
});
it('handles previous exceptions in chain', function () {
$innerException = new InvalidArgumentException('Inner error');
$outerException = new RuntimeException('Outer error', 0, $innerException);
$context = LogContext::withData(['exception' => $outerException]);
$record = new LogRecord(
message: 'Chained exception',
context: $context,
level: LogLevel::ERROR,
timestamp: $this->timestamp
);
$enrichedRecord = $this->processor->processRecord($record);
$extras = $enrichedRecord->getExtras();
expect($extras['previous_exceptions'])->toBeArray();
expect($extras['previous_exceptions'])->toHaveCount(1);
expect($extras['previous_exceptions'][0]['class'])->toBe(InvalidArgumentException::class);
expect($extras['previous_exceptions'][0]['message'])->toBe('Inner error');
});
it('generates exception hash', function () {
$exception = new Exception('Test message');
$context = LogContext::withData(['exception' => $exception]);
$record = new LogRecord('Error', $context, LogLevel::ERROR, $this->timestamp);
$enriched = $this->processor->processRecord($record);
$hash = $enriched->getExtra('exception_hash');
expect($hash)->toBeString();
expect($hash)->toHaveLength(8); // MD5 first 8 chars
});
it('categorizes exception severity correctly', function () {
$cases = [
[new InvalidArgumentException('Invalid arg'), 'medium'],
[new RuntimeException('Runtime error'), 'medium'],
[new Exception('Generic exception'), 'unknown'],
];
foreach ($cases as [$exception, $expectedSeverity]) {
$context = LogContext::withData(['exception' => $exception]);
$record = new LogRecord('Test', $context, LogLevel::ERROR, $this->timestamp);
$enriched = $this->processor->processRecord($record);
$severity = $enriched->getExtra('exception_severity');
expect($severity)->toBe($expectedSeverity);
}
});
it('returns record unchanged when no exception present', function () {
$context = LogContext::withData(['some_data' => 'value']);
$record = new LogRecord(
message: 'No exception here',
context: $context,
level: LogLevel::INFO,
timestamp: $this->timestamp
);
$processedRecord = $this->processor->processRecord($record);
expect($processedRecord->getExtras())->toBeEmpty();
expect($processedRecord->getMessage())->toBe('No exception here');
});
it('finds exception nested in context', function () {
$exception = new Exception('Nested exception');
$context = LogContext::withData([
'user_data' => ['id' => 123],
'error_info' => $exception, // Exception nested in context
]);
$record = new LogRecord('Nested error', $context, LogLevel::ERROR, $this->timestamp);
$enriched = $this->processor->processRecord($record);
expect($enriched->getExtra('exception_class'))->toBe(Exception::class);
expect($enriched->getExtra('exception_message'))->toBe('Nested exception');
});
it('respects max stack trace depth', function () {
$processor = new ExceptionEnrichmentProcessor(maxStackTraceDepth: 3);
$exception = new Exception('Deep stack trace');
$context = LogContext::withData(['exception' => $exception]);
$record = new LogRecord('Deep trace', $context, LogLevel::ERROR, $this->timestamp);
$enriched = $processor->processRecord($record);
$stackTrace = $enriched->getExtra('stack_trace');
expect(count($stackTrace))->toBeLessThanOrEqual(3);
});
it('has correct processor metadata', function () {
expect($this->processor->getName())->toBe('exception_enrichment');
expect($this->processor->getPriority())->toBe(100);
});
it('formats short stack trace readably', function () {
$exception = new Exception('Test exception');
$context = LogContext::withData(['exception' => $exception]);
$record = new LogRecord('Test', $context, LogLevel::ERROR, $this->timestamp);
$enriched = $this->processor->processRecord($record);
$shortTrace = $enriched->getExtra('stack_trace_short');
expect($shortTrace)->toBeString();
expect($shortTrace)->toContain('→'); // Contains arrow separator
expect($shortTrace)->toContain('.php:'); // Contains file and line
});