timestamp = new DateTimeImmutable('2024-01-15 10:30:45', new DateTimeZone('Europe/Berlin')); }); describe('constructor', function () { it('accepts custom ident and facility', function () { $handler = new SyslogHandler( ident: 'test-app', facility: LOG_LOCAL0, minLevel: LogLevel::WARNING ); expect($handler)->toBeInstanceOf(SyslogHandler::class); }); it('uses default values when not specified', function () { $handler = new SyslogHandler(); expect($handler)->toBeInstanceOf(SyslogHandler::class); }); }); describe('isHandling()', function () { it('returns true when record level is above minLevel', function () { $handler = new SyslogHandler(minLevel: LogLevel::WARNING); $record = new LogRecord( message: 'test message', context: LogContext::empty(), level: LogLevel::ERROR, timestamp: $this->timestamp ); expect($handler->isHandling($record))->toBeTrue(); }); it('returns true when record level equals minLevel', function () { $handler = new SyslogHandler(minLevel: LogLevel::WARNING); $record = new LogRecord( message: 'test message', context: LogContext::empty(), level: LogLevel::WARNING, timestamp: $this->timestamp ); expect($handler->isHandling($record))->toBeTrue(); }); it('returns false when record level is below minLevel', function () { $handler = new SyslogHandler(minLevel: LogLevel::ERROR); $record = new LogRecord( message: 'test message', context: LogContext::empty(), level: LogLevel::WARNING, timestamp: $this->timestamp ); expect($handler->isHandling($record))->toBeFalse(); }); }); describe('handle()', function () { it('handles log records without errors', function () { $handler = new SyslogHandler(ident: 'pest-test'); $record = new LogRecord( message: 'Test log message', context: LogContext::empty(), level: LogLevel::INFO, timestamp: $this->timestamp ); // Should not throw exception $handler->handle($record); expect(true)->toBeTrue(); }); it('handles records with request_id extra', function () { $handler = new SyslogHandler(ident: 'pest-test'); $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('handles records with channel', function () { $handler = new SyslogHandler(ident: 'pest-test'); $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('handles records with context data', function () { $handler = new SyslogHandler(ident: 'pest-test'); $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 SyslogHandler(ident: 'pest-test', minLevel: LogLevel::DEBUG); $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 records with empty context', function () { $handler = new SyslogHandler(ident: 'pest-test'); $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 data with special characters', function () { $handler = new SyslogHandler(ident: 'pest-test'); $record = new LogRecord( message: 'Test message', context: LogContext::withData([ 'email' => 'test@example.com', 'path' => '/api/users/123', 'special' => "line1\nline2\ttabbed", ]), level: LogLevel::INFO, timestamp: $this->timestamp ); // Should not throw exception $handler->handle($record); expect(true)->toBeTrue(); }); }); describe('syslog priority mapping', function () { it('correctly maps emergency level', function () { $handler = new SyslogHandler(ident: 'pest-test'); $record = new LogRecord( message: 'Emergency', context: LogContext::empty(), level: LogLevel::EMERGENCY, timestamp: $this->timestamp ); // Should use LOG_EMERG priority internally $handler->handle($record); expect(true)->toBeTrue(); }); it('correctly maps alert level', function () { $handler = new SyslogHandler(ident: 'pest-test'); $record = new LogRecord( message: 'Alert', context: LogContext::empty(), level: LogLevel::ALERT, timestamp: $this->timestamp ); // Should use LOG_ALERT priority internally $handler->handle($record); expect(true)->toBeTrue(); }); it('correctly maps critical level', function () { $handler = new SyslogHandler(ident: 'pest-test'); $record = new LogRecord( message: 'Critical', context: LogContext::empty(), level: LogLevel::CRITICAL, timestamp: $this->timestamp ); // Should use LOG_CRIT priority internally $handler->handle($record); expect(true)->toBeTrue(); }); }); describe('syslog connection management', function () { it('opens syslog connection on first handle', function () { $handler = new SyslogHandler(ident: 'pest-test-connection'); $record = new LogRecord( message: 'First message', context: LogContext::empty(), level: LogLevel::INFO, timestamp: $this->timestamp ); // Should open connection internally $handler->handle($record); expect(true)->toBeTrue(); }); it('reuses open syslog connection for multiple records', function () { $handler = new SyslogHandler(ident: 'pest-test-reuse'); $record1 = new LogRecord( message: 'First message', context: LogContext::empty(), level: LogLevel::INFO, timestamp: $this->timestamp ); $record2 = new LogRecord( message: 'Second message', context: LogContext::empty(), level: LogLevel::WARNING, timestamp: $this->timestamp ); // Should reuse connection $handler->handle($record1); $handler->handle($record2); expect(true)->toBeTrue(); }); }); describe('different facilities', function () { it('accepts LOG_USER facility', function () { $handler = new SyslogHandler( ident: 'pest-test', facility: LOG_USER ); $record = new LogRecord( message: 'Test', context: LogContext::empty(), level: LogLevel::INFO, timestamp: $this->timestamp ); $handler->handle($record); expect(true)->toBeTrue(); }); it('accepts LOG_LOCAL0 facility', function () { $handler = new SyslogHandler( ident: 'pest-test', facility: LOG_LOCAL0 ); $record = new LogRecord( message: 'Test', context: LogContext::empty(), level: LogLevel::INFO, timestamp: $this->timestamp ); $handler->handle($record); expect(true)->toBeTrue(); }); it('accepts LOG_DAEMON facility', function () { $handler = new SyslogHandler( ident: 'pest-test', facility: LOG_DAEMON ); $record = new LogRecord( message: 'Test', context: LogContext::empty(), level: LogLevel::INFO, timestamp: $this->timestamp ); $handler->handle($record); expect(true)->toBeTrue(); }); }); });