import { logger, createAsyncMetricsTracker } from './logger'; describe('Logger', () => { const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(); const mockConsoleError = jest.spyOn(console, 'error').mockImplementation(); const mockConsoleWarn = jest.spyOn(console, 'warn').mockImplementation(); beforeEach(() => { mockConsoleLog.mockClear(); mockConsoleError.mockClear(); mockConsoleWarn.mockClear(); }); afterAll(() => { mockConsoleLog.mockRestore(); mockConsoleError.mockRestore(); mockConsoleWarn.mockRestore(); }); describe('log methods', () => { it('should log error messages to console.error', () => { logger.error('test-component', 'test error message'); expect(mockConsoleError).toHaveBeenCalledWith( '[test-component] test error message', ); }); it('should log warning messages to console.warn', () => { logger.warn('test-component', 'test warning'); expect(mockConsoleWarn).toHaveBeenCalledWith( '[test-component] test warning', ); }); it('should include metadata in logs', () => { const metadata = { userId: '123', action: 'create' }; logger.error('auth', 'unauthorized access', undefined, metadata); expect(mockConsoleError).toHaveBeenCalledWith( '[auth] unauthorized access', { userId: '123', action: 'create' }, ); }); it('should log error with Error object and extract message/stack', () => { const error = new Error('test error'); logger.error('api', 'request failed', error); expect(mockConsoleError).toHaveBeenCalled(); const callArgs = mockConsoleError.mock.calls[0]; expect(callArgs[0]).toContain('[api]'); expect(callArgs[1]).toHaveProperty('errorMessage', 'test error'); expect(callArgs[1]).toHaveProperty('errorStack'); }); }); describe('measureAsync', () => { it('should measure async operation duration and return result', async () => { const result = await logger.measureAsync('test', 'operation', async () => { return 'success'; }); expect(result).toBe('success'); }); it('should measure async operation duration and log failure', async () => { const testError = new Error('async failure'); await expect( logger.measureAsync('test', 'operation', async () => { throw testError; }), ).rejects.toThrow('async failure'); expect(mockConsoleError).toHaveBeenCalled(); const callArgs = mockConsoleError.mock.calls[0]; expect(callArgs[0]).toContain('[test]'); expect(callArgs[0]).toContain('operation failed'); }); }); describe('measureSync', () => { it('should measure sync operation duration and return result', () => { const result = logger.measureSync('test', 'operation', () => { return 'success'; }); expect(result).toBe('success'); }); it('should measure sync operation duration and log failure', () => { expect(() => { logger.measureSync('test', 'operation', () => { throw new Error('sync failure'); }); }).toThrow('sync failure'); expect(mockConsoleError).toHaveBeenCalled(); }); }); describe('createAsyncMetricsTracker', () => { it('should track successful async operation', () => { const tracker = createAsyncMetricsTracker('api', 'fetch'); tracker.success({ statusCode: 200 }); // Logger call succeeds without console verification }); it('should track failed async operation', () => { const tracker = createAsyncMetricsTracker('api', 'fetch'); const error = new Error('network error'); tracker.error(error, { attempt: 1 }); expect(mockConsoleError).toHaveBeenCalled(); const callArgs = mockConsoleError.mock.calls[0]; expect(callArgs[0]).toContain('[api]'); expect(callArgs[0]).toContain('fetch failed'); }); }); });