Files
michaelschiemer/tests/Unit/Framework/Logging/Handlers/ConsoleHandlerTest.php

318 lines
9.5 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Unit\Framework\Logging\Handlers;
use App\Framework\Logging\Handlers\ConsoleHandler;
use App\Framework\Logging\Formatter\LineFormatter;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\LogRecord;
use App\Framework\Logging\ValueObjects\LogContext;
use DateTimeImmutable;
use DateTimeZone;
beforeEach(function () {
$this->timestamp = new DateTimeImmutable('2024-01-15 10:30:45', new DateTimeZone('Europe/Berlin'));
$this->context = LogContext::withData(['test' => 'data']);
});
it('handles records in both CLI and web mode', function () {
// ConsoleHandler now works in both CLI and web mode
$formatter = new LineFormatter();
$handler = new ConsoleHandler($formatter, debugOnly: false);
$record = new LogRecord(
message: 'Test message',
context: $this->context,
level: LogLevel::INFO,
timestamp: $this->timestamp
);
// Should handle the record regardless of SAPI mode
$result = $handler->isHandling($record);
expect($result)->toBe(true);
});
it('respects minimum level configuration', function () {
$formatter = new LineFormatter();
$handler = new ConsoleHandler($formatter, minLevel: LogLevel::WARNING, debugOnly: false);
$debugRecord = new LogRecord(
message: 'Debug',
context: $this->context,
level: LogLevel::DEBUG,
timestamp: $this->timestamp
);
$infoRecord = new LogRecord(
message: 'Info',
context: $this->context,
level: LogLevel::INFO,
timestamp: $this->timestamp
);
$warningRecord = new LogRecord(
message: 'Warning',
context: $this->context,
level: LogLevel::WARNING,
timestamp: $this->timestamp
);
$errorRecord = new LogRecord(
message: 'Error',
context: $this->context,
level: LogLevel::ERROR,
timestamp: $this->timestamp
);
$debugResult = $handler->isHandling($debugRecord);
expect($debugResult)->toBe(false);
$infoResult = $handler->isHandling($infoRecord);
expect($infoResult)->toBe(false);
$warningResult = $handler->isHandling($warningRecord);
expect($warningResult)->toBe(true);
$errorResult = $handler->isHandling($errorRecord);
expect($errorResult)->toBe(true);
});
it('respects debug only mode when APP_DEBUG is not set', function () {
// Save original value
$originalDebug = getenv('APP_DEBUG');
// Test with APP_DEBUG = false
putenv('APP_DEBUG=false');
$formatter = new LineFormatter();
$handler = new ConsoleHandler($formatter, debugOnly: true);
$record = new LogRecord(
message: 'Test',
context: $this->context,
level: LogLevel::INFO,
timestamp: $this->timestamp
);
$result1 = $handler->isHandling($record);
expect($result1)->toBe(false);
// Test with APP_DEBUG = true
putenv('APP_DEBUG=true');
$result2 = $handler->isHandling($record);
expect($result2)->toBe(true);
// Test with debugOnly = false (should always handle)
putenv('APP_DEBUG=false');
$handler = new ConsoleHandler($formatter, debugOnly: false);
$result3 = $handler->isHandling($record);
expect($result3)->toBe(true);
// Restore original value
if ($originalDebug !== false) {
putenv("APP_DEBUG={$originalDebug}");
} else {
putenv('APP_DEBUG');
}
});
it('can change minimum level after creation', function () {
$formatter = new LineFormatter();
$handler = new ConsoleHandler($formatter, minLevel: LogLevel::DEBUG, debugOnly: false);
$infoRecord = new LogRecord(
message: 'Info',
context: $this->context,
level: LogLevel::INFO,
timestamp: $this->timestamp
);
$result1 = $handler->isHandling($infoRecord);
expect($result1)->toBe(true);
$handler->setMinLevel(LogLevel::WARNING);
$result2 = $handler->isHandling($infoRecord);
expect($result2)->toBe(false);
});
it('uses formatter for output', function () {
$formatter = new LineFormatter();
$handler = new ConsoleHandler($formatter, debugOnly: false);
$record = new LogRecord(
message: 'Test message',
context: $this->context,
level: LogLevel::INFO,
timestamp: $this->timestamp
);
// Verify handler processes records
expect($handler->isHandling($record))->toBe(true);
});
it('handles output correctly using stdout and stderr in CLI mode', function () {
$formatter = new LineFormatter();
$handler = new ConsoleHandler($formatter, stderrLevel: LogLevel::WARNING, debugOnly: false);
// Test that lower levels would go to stdout (DEBUG, INFO, NOTICE)
$infoRecord = new LogRecord(
message: 'Info message',
context: $this->context,
level: LogLevel::INFO,
timestamp: $this->timestamp
);
// Test that higher levels would go to stderr (WARNING and above)
$errorRecord = new LogRecord(
message: 'Error message',
context: $this->context,
level: LogLevel::ERROR,
timestamp: $this->timestamp
);
// We can verify the handler processes these records
$result1 = $handler->isHandling($infoRecord);
expect($result1)->toBe(true);
$result2 = $handler->isHandling($errorRecord);
expect($result2)->toBe(true);
// Capture output
ob_start();
$handler->handle($infoRecord);
$stdoutOutput = ob_get_clean();
// For stderr, we would need to redirect stderr to test it properly
// This is complex in PHPUnit/Pest, so we just verify it handles the record
ob_start();
$stderrBefore = ob_get_clean();
$handler->handle($errorRecord);
// The handler should have processed both records
$result3 = $handler->isHandling($infoRecord);
expect($result3)->toBe(true);
$result4 = $handler->isHandling($errorRecord);
expect($result4)->toBe(true);
});
it('formats records with extra data correctly', function () {
$formatter = new LineFormatter();
$handler = new ConsoleHandler($formatter, debugOnly: false);
$record = new LogRecord(
message: 'Test with extras',
context: $this->context,
level: LogLevel::INFO,
timestamp: $this->timestamp
);
// Add various types of extra data
$record = $record->withExtra('request_id', 'req-123')
->withExtra('structured_tags', ['important', 'audit'])
->withExtra('trace_context', [
'trace_id' => 'trace-abc-def-123',
'active_span' => ['spanId' => 'span-456-789'],
])
->withExtra('user_context', [
'user_id' => 'user-999',
'is_authenticated' => true,
])
->withExtra('request_context', [
'request_method' => 'POST',
'request_uri' => '/api/users/create',
]);
// The handler should process this record
$result = $handler->isHandling($record);
expect($result)->toBe(true);
// Capture the output
ob_start();
$handler->handle($record);
$output = ob_get_clean();
// The output should contain the message
expect($output)->toContain('Test with extras');
// It should contain the request_id
expect($output)->toContain('req-123');
});
it('handles records with channel information', function () {
$formatter = new LineFormatter(format: '{channel}{level_name}: {message}');
$handler = new ConsoleHandler($formatter, debugOnly: false);
$record = new LogRecord(
message: 'Database connection established',
context: $this->context,
level: LogLevel::INFO,
timestamp: $this->timestamp,
channel: 'database'
);
$result = $handler->isHandling($record);
expect($result)->toBe(true);
// Capture output
ob_start();
$handler->handle($record);
$output = ob_get_clean();
// The output should contain the channel
expect($output)->toContain('[database]');
expect($output)->toContain('Database connection established');
});
it('applies correct colors for stdout log levels in CLI mode', function () {
$formatter = new LineFormatter();
$handler = new ConsoleHandler($formatter, debugOnly: false);
// Only test stdout levels (DEBUG, INFO, NOTICE)
// WARNING and above go to stderr and cannot be captured with ob_start()
$levels = [
LogLevel::DEBUG,
LogLevel::INFO,
LogLevel::NOTICE,
];
foreach ($levels as $level) {
$record = new LogRecord(
message: "{$level->getName()} message",
context: $this->context,
level: $level,
timestamp: $this->timestamp
);
ob_start();
$handler->handle($record);
$output = ob_get_clean();
// Each level should have its color code in the output
$expectedColor = $level->getConsoleColor()->value;
expect($output)->toContain($expectedColor);
expect($output)->toContain("{$level->getName()} message");
}
});
it('uses stderr for all logs in web mode', function () {
// This test verifies that in web mode, all logs go to stderr
// We can't easily mock PHP_SAPI, but we can verify the logic exists
$formatter = new LineFormatter();
$handler = new ConsoleHandler($formatter, debugOnly: false);
$record = new LogRecord(
message: 'Web request log',
context: $this->context,
level: LogLevel::INFO,
timestamp: $this->timestamp
);
// Handler should process records
expect($handler->isHandling($record))->toBe(true);
// Note: Actual stderr/stdout routing based on PHP_SAPI is tested at runtime
// This test ensures the handler works in both modes
});