import { LoggingService, LogLevel } from '../../../src/Application/Services/LoggingService'; import { logAuth, logError, logDatabase, logStartup } from '../../../src/Application/Services/Logger'; import fs from 'fs'; import path from 'path'; describe('LoggingService', () => { let loggingService: LoggingService; const testLogsDir = path.join(process.cwd(), 'test-logs'); beforeEach(() => { // Clean up any existing test logs if (fs.existsSync(testLogsDir)) { fs.rmSync(testLogsDir, { recursive: true, force: true }); } // Mock environment variables for testing process.env.MAX_LOGS_PER_FILE = '10'; process.env.MINIO_ENDPOINT = ''; loggingService = LoggingService.getInstance(); }); afterEach(() => { // Clean up test logs if (fs.existsSync(testLogsDir)) { fs.rmSync(testLogsDir, { recursive: true, force: true }); } // Clean up environment variables delete process.env.MAX_LOGS_PER_FILE; delete process.env.MINIO_ENDPOINT; }); describe('Log Level Functions', () => { it('should log authentication events', () => { const consoleSpy = jest.spyOn(console, 'info').mockImplementation(); logAuth('Test auth message', 'user123', { action: 'login' }); expect(consoleSpy).toHaveBeenCalled(); const logCall = consoleSpy.mock.calls[0][0]; expect(logCall).toContain('[AUTH]'); expect(logCall).toContain('Test auth message'); consoleSpy.mockRestore(); }); it('should log error events with stack trace', () => { const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); const testError = new Error('Test error message'); logError('Test error occurred', testError); expect(consoleSpy).toHaveBeenCalled(); const logCall = consoleSpy.mock.calls[0][0]; expect(logCall).toContain('[ERROR]'); expect(logCall).toContain('Test error occurred'); consoleSpy.mockRestore(); }); it('should log database operations with timing', () => { const consoleSpy = jest.spyOn(console, 'info').mockImplementation(); logDatabase('Query executed', 'SELECT * FROM users', 45); expect(consoleSpy).toHaveBeenCalled(); const logCall = consoleSpy.mock.calls[0][0]; expect(logCall).toContain('[DATABASE]'); expect(logCall).toContain('Query executed'); consoleSpy.mockRestore(); }); it('should log startup events', () => { const consoleSpy = jest.spyOn(console, 'log').mockImplementation(); logStartup('Application started', { version: '1.0.0' }); expect(consoleSpy).toHaveBeenCalled(); const logCall = consoleSpy.mock.calls[0][0]; expect(logCall).toContain('[STARTUP]'); expect(logCall).toContain('Application started'); consoleSpy.mockRestore(); }); }); describe('Log Formatting', () => { it('should include timestamp in log entries', () => { const consoleSpy = jest.spyOn(console, 'log').mockImplementation(); logStartup('Test message'); expect(consoleSpy).toHaveBeenCalled(); const logCall = consoleSpy.mock.calls[0][0]; // Check if timestamp is in ISO format const timestampRegex = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/; expect(logCall).toMatch(timestampRegex); consoleSpy.mockRestore(); }); it('should include metadata in log entries', () => { const consoleSpy = jest.spyOn(console, 'info').mockImplementation(); const metadata = { userId: '123', action: 'test' }; logAuth('Test with metadata', 'user123', metadata); expect(consoleSpy).toHaveBeenCalled(); const logCall = consoleSpy.mock.calls[0][0]; expect(logCall).toContain('Meta:'); expect(logCall).toContain('"userId":"123"'); expect(logCall).toContain('"action":"test"'); consoleSpy.mockRestore(); }); }); describe('Request Logging Middleware', () => { it('should create request logging middleware', () => { const middleware = loggingService.requestLoggingMiddleware(); expect(typeof middleware).toBe('function'); expect(middleware.length).toBe(3); // req, res, next }); it('should create error logging middleware', () => { const middleware = loggingService.errorLoggingMiddleware(); expect(typeof middleware).toBe('function'); expect(middleware.length).toBe(4); // error, req, res, next }); }); describe('Log Levels', () => { it('should have all required log levels defined', () => { expect(LogLevel.REQUEST).toBe('REQUEST'); expect(LogLevel.ERROR).toBe('ERROR'); expect(LogLevel.WARNING).toBe('WARNING'); expect(LogLevel.AUTH).toBe('AUTH'); expect(LogLevel.DATABASE).toBe('DATABASE'); expect(LogLevel.STARTUP).toBe('STARTUP'); expect(LogLevel.CONNECTION).toBe('CONNECTION'); expect(LogLevel.OTHER).toBe('OTHER'); }); }); describe('Singleton Pattern', () => { it('should return the same instance', () => { const instance1 = LoggingService.getInstance(); const instance2 = LoggingService.getInstance(); expect(instance1).toBe(instance2); }); }); describe('File Operations', () => { it('should handle missing Minio configuration gracefully', () => { // Test that the service starts without Minio config expect(() => LoggingService.getInstance()).not.toThrow(); }); it('should generate monthly directory structure', () => { const now = new Date(); const year = now.getFullYear(); const month = String(now.getMonth() + 1).padStart(2, '0'); const expectedPath = path.join('logs', `${year}-${month}`); // This tests the internal logic through the public interface logStartup('Test for directory creation'); // Since we can't directly test the private method, we verify the service doesn't crash expect(loggingService).toBeDefined(); }); }); describe('Error Handling', () => { it('should handle logging errors gracefully', () => { // Mock fs.appendFileSync to throw an error const originalAppendFileSync = fs.appendFileSync; const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); fs.appendFileSync = jest.fn(() => { throw new Error('Disk full'); }); expect(() => { logStartup('This should not crash'); }).not.toThrow(); // Restore original function fs.appendFileSync = originalAppendFileSync; consoleSpy.mockRestore(); }); it('should continue logging to console even if file logging fails', () => { const consoleSpy = jest.spyOn(console, 'log').mockImplementation(); // Mock file system to fail const originalAppendFileSync = fs.appendFileSync; fs.appendFileSync = jest.fn(() => { throw new Error('File system error'); }); logStartup('Test message'); // Should still log to console expect(consoleSpy).toHaveBeenCalled(); // Restore fs.appendFileSync = originalAppendFileSync; consoleSpy.mockRestore(); }); }); });