carouselforge / src /lib /monitoring /logger.test.ts
CarouselForge Developer
feat: Phase 15 complete — mobile responsiveness, monitoring, Telegram polish, UAT
d0ded63
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');
});
});
});