Files
michaelschiemer/tests/Unit/Framework/Logging/Handlers/WebHandlerTest.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

305 lines
9.9 KiB
PHP

<?php
declare(strict_types=1);
use App\Framework\Logging\Handlers\WebHandler;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\LogRecord;
use App\Framework\Logging\ValueObjects\LogContext;
describe('WebHandler', function () {
beforeEach(function () {
$this->timestamp = new DateTimeImmutable('2024-01-15 10:30:45', new DateTimeZone('Europe/Berlin'));
$this->context = LogContext::withData(['test' => 'data']);
});
describe('constructor', function () {
it('accepts custom minimum level', function () {
$handler = new WebHandler(
minLevel: LogLevel::WARNING,
debugOnly: false
);
expect($handler)->toBeInstanceOf(WebHandler::class);
});
it('uses default values when not specified', function () {
$handler = new WebHandler();
expect($handler)->toBeInstanceOf(WebHandler::class);
});
it('accepts LogLevel enum as minLevel', function () {
$handler = new WebHandler(minLevel: LogLevel::ERROR);
expect($handler)->toBeInstanceOf(WebHandler::class);
});
});
describe('isHandling()', function () {
it('returns false in CLI mode (test environment)', function () {
// Note: Tests run in CLI mode, so WebHandler always returns false
expect(PHP_SAPI)->toBe('cli');
$handler = new WebHandler(debugOnly: false);
$record = new LogRecord(
message: 'Test message',
context: $this->context,
level: LogLevel::INFO,
timestamp: $this->timestamp
);
// In CLI mode, handler should not handle any records
expect($handler->isHandling($record))->toBeFalse();
});
it('respects minimum level when not in CLI', function () {
// This test documents behavior, even though it won't execute in web mode during tests
$handler = new WebHandler(
minLevel: LogLevel::ERROR,
debugOnly: false
);
$infoRecord = new LogRecord(
message: 'Info message',
context: LogContext::empty(),
level: LogLevel::INFO,
timestamp: $this->timestamp
);
$errorRecord = new LogRecord(
message: 'Error message',
context: LogContext::empty(),
level: LogLevel::ERROR,
timestamp: $this->timestamp
);
// In CLI, both return false (PHP_SAPI check comes first)
expect($handler->isHandling($infoRecord))->toBeFalse();
expect($handler->isHandling($errorRecord))->toBeFalse();
});
it('checks debug mode when debugOnly is true', function () {
// Save original APP_DEBUG value
$originalDebug = getenv('APP_DEBUG');
// Test with debug enabled
putenv('APP_DEBUG=true');
$handler = new WebHandler(debugOnly: true);
$record = new LogRecord(
message: 'Test message',
context: $this->context,
level: LogLevel::INFO,
timestamp: $this->timestamp
);
// Still false because we're in CLI
expect($handler->isHandling($record))->toBeFalse();
// Restore original value
if ($originalDebug !== false) {
putenv("APP_DEBUG={$originalDebug}");
} else {
putenv('APP_DEBUG');
}
});
});
describe('handle() - format testing', function () {
it('formats basic log message', function () {
$handler = new WebHandler(debugOnly: false);
$record = new LogRecord(
message: 'Test log message',
context: LogContext::empty(),
level: LogLevel::INFO,
timestamp: $this->timestamp
);
// In CLI mode, handler would not normally be called, but we can test the method directly
// The handle() method should not throw errors even in CLI
$handler->handle($record);
expect(true)->toBeTrue();
});
it('includes request_id when present in extras', function () {
$handler = new WebHandler(debugOnly: false);
$record = (new LogRecord(
message: 'Test message',
context: LogContext::empty(),
level: LogLevel::INFO,
timestamp: $this->timestamp
))->addExtra('request_id', 'req-123');
// Should not throw exception
$handler->handle($record);
expect(true)->toBeTrue();
});
it('includes channel when present', function () {
$handler = new WebHandler(debugOnly: false);
$record = new LogRecord(
message: 'Test message',
context: LogContext::empty(),
level: LogLevel::INFO,
timestamp: $this->timestamp,
channel: 'security'
);
// Should not throw exception
$handler->handle($record);
expect(true)->toBeTrue();
});
it('includes context data', function () {
$handler = new WebHandler(debugOnly: false);
$record = new LogRecord(
message: 'Test message',
context: LogContext::withData(['user_id' => 123, 'action' => 'login']),
level: LogLevel::INFO,
timestamp: $this->timestamp
);
// Should not throw exception
$handler->handle($record);
expect(true)->toBeTrue();
});
it('handles all log levels', function () {
$handler = new WebHandler(
minLevel: LogLevel::DEBUG,
debugOnly: false
);
$levels = [
LogLevel::DEBUG,
LogLevel::INFO,
LogLevel::NOTICE,
LogLevel::WARNING,
LogLevel::ERROR,
LogLevel::CRITICAL,
LogLevel::ALERT,
LogLevel::EMERGENCY,
];
foreach ($levels as $level) {
$record = new LogRecord(
message: "{$level->getName()} message",
context: LogContext::empty(),
level: $level,
timestamp: $this->timestamp
);
// Should not throw exception
$handler->handle($record);
}
expect(true)->toBeTrue();
});
it('handles empty context', function () {
$handler = new WebHandler(debugOnly: false);
$record = new LogRecord(
message: 'Message without context',
context: LogContext::empty(),
level: LogLevel::INFO,
timestamp: $this->timestamp
);
// Should not throw exception
$handler->handle($record);
expect(true)->toBeTrue();
});
it('handles complex context with special characters', function () {
$handler = new WebHandler(debugOnly: false);
$record = new LogRecord(
message: 'Test message',
context: LogContext::withData([
'email' => 'test@example.com',
'path' => '/api/users/123',
'special' => "line1\nline2\ttabbed",
'unicode' => '日本語',
]),
level: LogLevel::INFO,
timestamp: $this->timestamp
);
// Should not throw exception
$handler->handle($record);
expect(true)->toBeTrue();
});
it('handles record with both request_id and channel', function () {
$handler = new WebHandler(debugOnly: false);
$record = (new LogRecord(
message: 'Complete log',
context: LogContext::withData(['key' => 'value']),
level: LogLevel::WARNING,
timestamp: $this->timestamp,
channel: 'api'
))->addExtra('request_id', 'req-456');
// Should not throw exception
$handler->handle($record);
expect(true)->toBeTrue();
});
});
describe('debug mode behavior', function () {
it('creates handler with debugOnly enabled by default', function () {
$handler = new WebHandler();
expect($handler)->toBeInstanceOf(WebHandler::class);
});
it('creates handler with debugOnly disabled', function () {
$handler = new WebHandler(debugOnly: false);
expect($handler)->toBeInstanceOf(WebHandler::class);
});
});
describe('minimum level configuration', function () {
it('accepts DEBUG level', function () {
$handler = new WebHandler(minLevel: LogLevel::DEBUG);
expect($handler)->toBeInstanceOf(WebHandler::class);
});
it('accepts WARNING level', function () {
$handler = new WebHandler(minLevel: LogLevel::WARNING);
expect($handler)->toBeInstanceOf(WebHandler::class);
});
it('accepts ERROR level', function () {
$handler = new WebHandler(minLevel: LogLevel::ERROR);
expect($handler)->toBeInstanceOf(WebHandler::class);
});
it('accepts CRITICAL level', function () {
$handler = new WebHandler(minLevel: LogLevel::CRITICAL);
expect($handler)->toBeInstanceOf(WebHandler::class);
});
});
describe('readonly behavior', function () {
it('is a readonly class', function () {
$reflection = new ReflectionClass(WebHandler::class);
expect($reflection->isReadOnly())->toBeTrue();
});
});
});