- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
233 lines
9.8 KiB
PHP
233 lines
9.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Unit\Framework\Logging;
|
|
|
|
use App\Framework\Logging\DefaultLogger;
|
|
use App\Framework\Logging\HasChannel;
|
|
use App\Framework\Logging\LogChannel;
|
|
use App\Framework\Logging\LogHandler;
|
|
use App\Framework\Logging\Logger;
|
|
use App\Framework\Logging\LogLevel;
|
|
use App\Framework\Logging\LogRecord;
|
|
use App\Framework\Logging\ProcessorManager;
|
|
use App\Framework\Logging\ValueObjects\LogContext;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
final class ChannelSystemTest extends TestCase
|
|
{
|
|
private array $capturedRecords = [];
|
|
|
|
private DefaultLogger $logger;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->capturedRecords = [];
|
|
|
|
// Test-Handler der alle Records captured
|
|
$testHandler = new class ($this->capturedRecords) implements LogHandler {
|
|
public function __construct(private array &$records)
|
|
{
|
|
}
|
|
|
|
public function isHandling(LogRecord $record): bool
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public function handle(LogRecord $record): void
|
|
{
|
|
$this->records[] = $record;
|
|
}
|
|
};
|
|
|
|
$this->logger = new DefaultLogger(
|
|
minLevel: LogLevel::DEBUG,
|
|
handlers: [$testHandler],
|
|
processorManager: new ProcessorManager()
|
|
);
|
|
}
|
|
|
|
public function test_channel_system_basic_functionality(): void
|
|
{
|
|
// Teste alle Channel-Logger
|
|
$this->logger->channel(LogChannel::SECURITY)->warning('Security alert', LogContext::withData(['ip' => '192.168.1.1']));
|
|
$this->logger->channel(LogChannel::CACHE)->debug('Cache miss', LogContext::withData(['key' => 'user_123']));
|
|
$this->logger->channel(LogChannel::DATABASE)->error('Query failed', LogContext::withData(['table' => 'users']));
|
|
$this->logger->channel(LogChannel::FRAMEWORK)->info('Route registered', LogContext::withData(['path' => '/api/test']));
|
|
$this->logger->channel(LogChannel::ERROR)->critical('System failure', LogContext::withData(['component' => 'auth']));
|
|
|
|
expect($this->capturedRecords)->toHaveCount(5);
|
|
|
|
// Prüfe Channels
|
|
expect($this->capturedRecords[0]->getChannel())->toBe('security');
|
|
expect($this->capturedRecords[1]->getChannel())->toBe('cache');
|
|
expect($this->capturedRecords[2]->getChannel())->toBe('database');
|
|
expect($this->capturedRecords[3]->getChannel())->toBe('framework');
|
|
expect($this->capturedRecords[4]->getChannel())->toBe('error');
|
|
|
|
// Prüfe Messages
|
|
expect($this->capturedRecords[0]->getMessage())->toBe('Security alert');
|
|
expect($this->capturedRecords[1]->getMessage())->toBe('Cache miss');
|
|
expect($this->capturedRecords[2]->getMessage())->toBe('Query failed');
|
|
expect($this->capturedRecords[3]->getMessage())->toBe('Route registered');
|
|
expect($this->capturedRecords[4]->getMessage())->toBe('System failure');
|
|
|
|
// Prüfe Levels
|
|
expect($this->capturedRecords[0]->getLevel())->toBe(LogLevel::WARNING);
|
|
expect($this->capturedRecords[1]->getLevel())->toBe(LogLevel::DEBUG);
|
|
expect($this->capturedRecords[2]->getLevel())->toBe(LogLevel::ERROR);
|
|
expect($this->capturedRecords[3]->getLevel())->toBe(LogLevel::INFO);
|
|
expect($this->capturedRecords[4]->getLevel())->toBe(LogLevel::CRITICAL);
|
|
}
|
|
|
|
public function test_standard_logging_vs_channel_logging(): void
|
|
{
|
|
// Standard-Logging
|
|
$this->logger->info('Standard log');
|
|
|
|
// Channel-Logging
|
|
$this->logger->channel(LogChannel::SECURITY)->info('Channel log');
|
|
|
|
expect($this->capturedRecords)->toHaveCount(2);
|
|
|
|
// Standard-Log hat keinen Channel
|
|
expect($this->capturedRecords[0]->getChannel())->toBeNull();
|
|
expect($this->capturedRecords[0]->getMessage())->toBe('Standard log');
|
|
|
|
// Channel-Log hat Channel
|
|
expect($this->capturedRecords[1]->getChannel())->toBe('security');
|
|
expect($this->capturedRecords[1]->getMessage())->toBe('Channel log');
|
|
}
|
|
|
|
public function test_all_log_levels_work_with_channels(): void
|
|
{
|
|
$this->logger->channel(LogChannel::SECURITY)->debug('Debug message');
|
|
$this->logger->channel(LogChannel::SECURITY)->info('Info message');
|
|
$this->logger->channel(LogChannel::SECURITY)->notice('Notice message');
|
|
$this->logger->channel(LogChannel::SECURITY)->warning('Warning message');
|
|
$this->logger->channel(LogChannel::SECURITY)->error('Error message');
|
|
$this->logger->channel(LogChannel::SECURITY)->critical('Critical message');
|
|
$this->logger->channel(LogChannel::SECURITY)->alert('Alert message');
|
|
$this->logger->channel(LogChannel::SECURITY)->emergency('Emergency message');
|
|
|
|
expect($this->capturedRecords)->toHaveCount(8);
|
|
|
|
$expectedLevels = [
|
|
LogLevel::DEBUG,
|
|
LogLevel::INFO,
|
|
LogLevel::NOTICE,
|
|
LogLevel::WARNING,
|
|
LogLevel::ERROR,
|
|
LogLevel::CRITICAL,
|
|
LogLevel::ALERT,
|
|
LogLevel::EMERGENCY,
|
|
];
|
|
|
|
foreach ($this->capturedRecords as $index => $record) {
|
|
expect($record->getChannel())->toBe('security');
|
|
expect($record->getLevel())->toBe($expectedLevels[$index]);
|
|
}
|
|
}
|
|
|
|
public function test_context_data_passed_correctly(): void
|
|
{
|
|
$context = LogContext::withData([
|
|
'user_id' => 123,
|
|
'action' => 'login',
|
|
'metadata' => ['ip' => '127.0.0.1', 'browser' => 'Chrome'],
|
|
]);
|
|
|
|
$this->logger->channel(LogChannel::SECURITY)->warning('Authentication event', $context);
|
|
|
|
expect($this->capturedRecords)->toHaveCount(1);
|
|
expect($this->capturedRecords[0]->getContext())->toMatchArray([
|
|
'user_id' => 123,
|
|
'action' => 'login',
|
|
'metadata' => ['ip' => '127.0.0.1', 'browser' => 'Chrome'],
|
|
]);
|
|
expect($this->capturedRecords[0]->getChannel())->toBe('security');
|
|
}
|
|
|
|
public function test_channel_isolation(): void
|
|
{
|
|
// Jeder Channel sollte unabhängig funktionieren
|
|
$this->logger->channel(LogChannel::SECURITY)->error('Security error');
|
|
$this->logger->channel(LogChannel::CACHE)->error('Cache error');
|
|
$this->logger->channel(LogChannel::DATABASE)->error('Database error');
|
|
|
|
expect($this->capturedRecords)->toHaveCount(3);
|
|
|
|
// Alle sollten ERROR level haben, aber verschiedene Channels
|
|
foreach ($this->capturedRecords as $record) {
|
|
expect($record->getLevel())->toBe(LogLevel::ERROR);
|
|
}
|
|
|
|
expect($this->capturedRecords[0]->getChannel())->toBe('security');
|
|
expect($this->capturedRecords[1]->getChannel())->toBe('cache');
|
|
expect($this->capturedRecords[2]->getChannel())->toBe('database');
|
|
|
|
expect($this->capturedRecords[0]->getMessage())->toBe('Security error');
|
|
expect($this->capturedRecords[1]->getMessage())->toBe('Cache error');
|
|
expect($this->capturedRecords[2]->getMessage())->toBe('Database error');
|
|
}
|
|
|
|
public function test_channel_enum_integration(): void
|
|
{
|
|
// Teste dass die Channels den Enum-Werten entsprechen
|
|
expect($this->logger->channel(LogChannel::SECURITY)->channel)->toBe(LogChannel::SECURITY);
|
|
expect($this->logger->channel(LogChannel::CACHE)->channel)->toBe(LogChannel::CACHE);
|
|
expect($this->logger->channel(LogChannel::DATABASE)->channel)->toBe(LogChannel::DATABASE);
|
|
expect($this->logger->channel(LogChannel::FRAMEWORK)->channel)->toBe(LogChannel::FRAMEWORK);
|
|
expect($this->logger->channel(LogChannel::ERROR)->channel)->toBe(LogChannel::ERROR);
|
|
|
|
// Teste dass die Channel-Namen korrekt sind
|
|
expect(LogChannel::SECURITY->value)->toBe('security');
|
|
expect(LogChannel::CACHE->value)->toBe('cache');
|
|
expect(LogChannel::DATABASE->value)->toBe('database');
|
|
expect(LogChannel::FRAMEWORK->value)->toBe('framework');
|
|
expect(LogChannel::ERROR->value)->toBe('error');
|
|
}
|
|
|
|
public function test_realistic_usage_scenario(): void
|
|
{
|
|
// Simuliere eine realistische Anwendungssequenz
|
|
|
|
// 1. Framework startet
|
|
$this->logger->channel(LogChannel::FRAMEWORK)->info('Application starting');
|
|
|
|
// 2. User versucht Login
|
|
$this->logger->channel(LogChannel::SECURITY)->info('Login attempt', LogContext::withData(['email' => 'user@test.com']));
|
|
|
|
// 3. Cache Miss
|
|
$this->logger->channel(LogChannel::CACHE)->debug('User cache miss', LogContext::withData(['email' => 'user@test.com']));
|
|
|
|
// 4. Database Query
|
|
$this->logger->channel(LogChannel::DATABASE)->debug('User lookup query', LogContext::withData(['table' => 'users']));
|
|
|
|
// 5. Successful authentication
|
|
$this->logger->channel(LogChannel::SECURITY)->info('Login successful', LogContext::withData(['user_id' => 42]));
|
|
|
|
// 6. Cache Store
|
|
$this->logger->channel(LogChannel::CACHE)->info('User cached', LogContext::withData(['user_id' => 42, 'ttl' => 3600]));
|
|
|
|
// 7. Später: Ein Fehler
|
|
$this->logger->channel(LogChannel::DATABASE)->error('Connection timeout', LogContext::withData(['host' => 'db.example.com']));
|
|
$this->logger->channel(LogChannel::ERROR)->critical('Service degraded', LogContext::withData(['affected' => ['users', 'orders']]));
|
|
|
|
expect($this->capturedRecords)->toHaveCount(8);
|
|
|
|
// Prüfe die Sequenz
|
|
$channels = array_map(fn ($record) => $record->getChannel(), $this->capturedRecords);
|
|
$expected = ['framework', 'security', 'cache', 'database', 'security', 'cache', 'database', 'error'];
|
|
|
|
expect($channels)->toBe($expected);
|
|
|
|
// Prüfe dass jeder Record den richtigen Channel hat
|
|
foreach ($this->capturedRecords as $index => $record) {
|
|
expect($record->getChannel())->toBe($expected[$index]);
|
|
}
|
|
}
|
|
}
|