Files
michaelschiemer/tests/Unit/Framework/Logging/DefaultChannelLoggerTest.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- 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.
2025-10-25 19:18:37 +02:00

260 lines
10 KiB
PHP

<?php
declare(strict_types=1);
use App\Framework\Logging\DefaultChannelLogger;
use App\Framework\Logging\HasChannel;
use App\Framework\Logging\LogChannel;
use App\Framework\Logging\Logger;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\SupportsChannels;
use App\Framework\Logging\ValueObjects\LogContext;
describe('DefaultChannelLogger', function () {
beforeEach(function () {
// Create a test logger that records calls
$this->logCalls = [];
$this->testLogger = new class($this->logCalls) implements Logger, SupportsChannels {
public function __construct(private array &$logCalls)
{
}
public function debug(string $message, ?LogContext $context = null): void
{
$this->logToChannel(LogChannel::FRAMEWORK, LogLevel::DEBUG, $message, $context);
}
public function info(string $message, ?LogContext $context = null): void
{
$this->logToChannel(LogChannel::FRAMEWORK, LogLevel::INFO, $message, $context);
}
public function notice(string $message, ?LogContext $context = null): void
{
$this->logToChannel(LogChannel::FRAMEWORK, LogLevel::NOTICE, $message, $context);
}
public function warning(string $message, ?LogContext $context = null): void
{
$this->logToChannel(LogChannel::FRAMEWORK, LogLevel::WARNING, $message, $context);
}
public function error(string $message, ?LogContext $context = null): void
{
$this->logToChannel(LogChannel::ERROR, LogLevel::ERROR, $message, $context);
}
public function critical(string $message, ?LogContext $context = null): void
{
$this->logToChannel(LogChannel::ERROR, LogLevel::CRITICAL, $message, $context);
}
public function alert(string $message, ?LogContext $context = null): void
{
$this->logToChannel(LogChannel::ERROR, LogLevel::ALERT, $message, $context);
}
public function emergency(string $message, ?LogContext $context = null): void
{
$this->logToChannel(LogChannel::ERROR, LogLevel::EMERGENCY, $message, $context);
}
public function log(LogLevel $level, string $message, ?LogContext $context = null): void
{
$this->logToChannel(LogChannel::FRAMEWORK, $level, $message, $context);
}
public function logToChannel(
LogChannel $channel,
LogLevel $level,
string $message,
?LogContext $context = null
): void {
$this->logCalls[] = [
'channel' => $channel,
'level' => $level,
'message' => $message,
'context' => $context
];
}
public function channel(LogChannel|string $channel): Logger&HasChannel
{
$logChannel = $channel instanceof LogChannel ? $channel : LogChannel::from($channel);
return new DefaultChannelLogger($this, $logChannel);
}
};
$this->channelLogger = new DefaultChannelLogger($this->testLogger, LogChannel::SECURITY);
});
describe('interface implementation', function () {
it('implements Logger and HasChannel interfaces', function () {
expect($this->channelLogger)->toBeInstanceOf(Logger::class);
expect($this->channelLogger)->toBeInstanceOf(HasChannel::class);
});
it('has correct channel property', function () {
expect($this->channelLogger->channel)->toBe(LogChannel::SECURITY);
});
});
describe('log level delegation', function () {
it('delegates debug to parent logger', function () {
$this->channelLogger->debug('Test message', LogContext::withData(['key' => 'value']));
expect(count($this->logCalls))->toBe(1);
expect($this->logCalls[0]['channel'])->toBe(LogChannel::SECURITY);
expect($this->logCalls[0]['level'])->toBe(LogLevel::DEBUG);
expect($this->logCalls[0]['message'])->toBe('Test message');
});
it('delegates info to parent logger', function () {
$this->channelLogger->info('Test message', LogContext::withData(['key' => 'value']));
expect(count($this->logCalls))->toBe(1);
expect($this->logCalls[0]['level'])->toBe(LogLevel::INFO);
});
it('delegates warning to parent logger', function () {
$this->channelLogger->warning('Test message', LogContext::withData(['key' => 'value']));
expect(count($this->logCalls))->toBe(1);
expect($this->logCalls[0]['level'])->toBe(LogLevel::WARNING);
});
it('delegates error to parent logger', function () {
$this->channelLogger->error('Test message', LogContext::withData(['key' => 'value']));
expect(count($this->logCalls))->toBe(1);
expect($this->logCalls[0]['level'])->toBe(LogLevel::ERROR);
});
it('delegates critical to parent logger', function () {
$this->channelLogger->critical('Test message', LogContext::withData(['key' => 'value']));
expect(count($this->logCalls))->toBe(1);
expect($this->logCalls[0]['level'])->toBe(LogLevel::CRITICAL);
});
it('delegates alert to parent logger', function () {
$this->channelLogger->alert('Test message', LogContext::withData(['key' => 'value']));
expect(count($this->logCalls))->toBe(1);
expect($this->logCalls[0]['level'])->toBe(LogLevel::ALERT);
});
it('delegates emergency to parent logger', function () {
$this->channelLogger->emergency('Test message', LogContext::withData(['key' => 'value']));
expect(count($this->logCalls))->toBe(1);
expect($this->logCalls[0]['level'])->toBe(LogLevel::EMERGENCY);
});
it('delegates generic log to parent logger', function () {
$this->channelLogger->log(LogLevel::WARNING, 'Test message', LogContext::withData(['key' => 'value']));
expect(count($this->logCalls))->toBe(1);
expect($this->logCalls[0]['level'])->toBe(LogLevel::WARNING);
expect($this->logCalls[0]['message'])->toBe('Test message');
});
});
describe('context handling', function () {
it('supports log context objects', function () {
$logContext = LogContext::withData(['user_id' => 123]);
$this->channelLogger->info('Test message', $logContext);
expect(count($this->logCalls))->toBe(1);
expect($this->logCalls[0]['context'])->toBeInstanceOf(LogContext::class);
});
it('supports empty context', function () {
$this->channelLogger->info('Test message');
expect(count($this->logCalls))->toBe(1);
expect($this->logCalls[0]['message'])->toBe('Test message');
});
});
describe('channel independence', function () {
it('different channels work independently', function () {
$logCalls = [];
$testLogger = new class($logCalls) implements Logger, SupportsChannels {
public function __construct(private array &$logCalls)
{
}
public function debug(string $message, ?LogContext $context = null): void
{
$this->logToChannel(LogChannel::FRAMEWORK, LogLevel::DEBUG, $message, $context);
}
public function info(string $message, ?LogContext $context = null): void
{
$this->logToChannel(LogChannel::FRAMEWORK, LogLevel::INFO, $message, $context);
}
public function notice(string $message, ?LogContext $context = null): void
{
$this->logToChannel(LogChannel::FRAMEWORK, LogLevel::NOTICE, $message, $context);
}
public function warning(string $message, ?LogContext $context = null): void
{
$this->logToChannel(LogChannel::FRAMEWORK, LogLevel::WARNING, $message, $context);
}
public function error(string $message, ?LogContext $context = null): void
{
$this->logToChannel(LogChannel::ERROR, LogLevel::ERROR, $message, $context);
}
public function critical(string $message, ?LogContext $context = null): void
{
$this->logToChannel(LogChannel::ERROR, LogLevel::CRITICAL, $message, $context);
}
public function alert(string $message, ?LogContext $context = null): void
{
$this->logToChannel(LogChannel::ERROR, LogLevel::ALERT, $message, $context);
}
public function emergency(string $message, ?LogContext $context = null): void
{
$this->logToChannel(LogChannel::ERROR, LogLevel::EMERGENCY, $message, $context);
}
public function log(LogLevel $level, string $message, ?LogContext $context = null): void
{
$this->logToChannel(LogChannel::FRAMEWORK, $level, $message, $context);
}
public function logToChannel(
LogChannel $channel,
LogLevel $level,
string $message,
?LogContext $context = null
): void {
$this->logCalls[] = ['channel' => $channel];
}
public function channel(LogChannel|string $channel): Logger&HasChannel
{
$logChannel = $channel instanceof LogChannel ? $channel : LogChannel::from($channel);
return new DefaultChannelLogger($this, $logChannel);
}
};
$cacheLogger = new DefaultChannelLogger($testLogger, LogChannel::CACHE);
$dbLogger = new DefaultChannelLogger($testLogger, LogChannel::DATABASE);
$securityLogger = new DefaultChannelLogger($testLogger, LogChannel::SECURITY);
expect($cacheLogger->channel)->toBe(LogChannel::CACHE);
expect($dbLogger->channel)->toBe(LogChannel::DATABASE);
expect($securityLogger->channel)->toBe(LogChannel::SECURITY);
});
});
});