/**
* Logger Unit Tests
*/
import { logger, LogLevel } from '../../src/utils/logger.js';
// Mock console methods
const mockConsole = {
log: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
info: jest.fn(),
debug: jest.fn()
};
// Replace console with mock
Object.assign(console, mockConsole);
describe('Logger', () => {
beforeEach(() => {
// Reset mocks
jest.clearAllMocks();
// Reset logger state
logger.setLevel(LogLevel.INFO);
logger.setDebugMode(false);
});
describe('LogLevel enum', () => {
it('should have correct log levels', () => {
expect(LogLevel.DEBUG).toBe(0);
expect(LogLevel.INFO).toBe(1);
expect(LogLevel.WARN).toBe(2);
expect(LogLevel.ERROR).toBe(3);
});
});
describe('setLevel', () => {
it('should set log level correctly', () => {
logger.setLevel(LogLevel.DEBUG);
expect(logger.getLevel()).toBe(LogLevel.DEBUG);
logger.setLevel(LogLevel.ERROR);
expect(logger.getLevel()).toBe(LogLevel.ERROR);
});
});
describe('setDebugMode', () => {
it('should enable debug mode', () => {
logger.setDebugMode(true);
expect(logger.isDebugMode()).toBe(true);
});
it('should disable debug mode', () => {
logger.setDebugMode(false);
expect(logger.isDebugMode()).toBe(false);
});
});
describe('debug', () => {
it('should log debug messages when level is DEBUG', () => {
logger.setLevel(LogLevel.DEBUG);
logger.debug('Test debug message');
expect(mockConsole.log).toHaveBeenCalledWith(
expect.stringContaining('[DEBUG]'),
expect.stringContaining('Test debug message')
);
});
it('should not log debug messages when level is higher than DEBUG', () => {
logger.setLevel(LogLevel.INFO);
logger.debug('Test debug message');
expect(mockConsole.log).not.toHaveBeenCalled();
});
});
describe('info', () => {
it('should log info messages when level is INFO or lower', () => {
logger.setLevel(LogLevel.INFO);
logger.info('Test info message');
expect(mockConsole.info).toHaveBeenCalledWith(
expect.stringContaining('[INFO]'),
expect.stringContaining('Test info message')
);
});
it('should not log info messages when level is higher than INFO', () => {
logger.setLevel(LogLevel.WARN);
logger.info('Test info message');
expect(mockConsole.info).not.toHaveBeenCalled();
});
});
describe('warn', () => {
it('should log warn messages when level is WARN or lower', () => {
logger.setLevel(LogLevel.WARN);
logger.warn('Test warn message');
expect(mockConsole.warn).toHaveBeenCalledWith(
expect.stringContaining('[WARN]'),
expect.stringContaining('Test warn message')
);
});
it('should not log warn messages when level is higher than WARN', () => {
logger.setLevel(LogLevel.ERROR);
logger.warn('Test warn message');
expect(mockConsole.warn).not.toHaveBeenCalled();
});
});
describe('error', () => {
it('should always log error messages', () => {
logger.setLevel(LogLevel.ERROR);
logger.error('Test error message');
expect(mockConsole.error).toHaveBeenCalledWith(
expect.stringContaining('[ERROR]'),
expect.stringContaining('Test error message')
);
});
it('should log error messages even when level is DEBUG', () => {
logger.setLevel(LogLevel.DEBUG);
logger.error('Test error message');
expect(mockConsole.error).toHaveBeenCalledWith(
expect.stringContaining('[ERROR]'),
expect.stringContaining('Test error message')
);
});
});
describe('performance', () => {
it('should log performance messages in debug mode', () => {
logger.setDebugMode(true);
logger.setLevel(LogLevel.DEBUG);
logger.performance('Test operation', 150);
expect(mockConsole.log).toHaveBeenCalledWith(
expect.stringContaining('[PERF]'),
expect.stringContaining('Test operation'),
expect.stringContaining('150ms')
);
});
it('should not log performance messages when debug mode is disabled', () => {
logger.setDebugMode(false);
logger.setLevel(LogLevel.DEBUG);
logger.performance('Test operation', 150);
expect(mockConsole.log).not.toHaveBeenCalled();
});
});
describe('log formatting', () => {
it('should include timestamp in log messages', () => {
logger.setLevel(LogLevel.INFO);
logger.info('Test message');
const logCall = mockConsole.info.mock.calls[0][0];
expect(logCall).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/);
});
it('should include log level in messages', () => {
logger.setLevel(LogLevel.INFO);
logger.info('Test message');
const logCall = mockConsole.info.mock.calls[0][0];
expect(logCall).toContain('[INFO]');
});
it('should handle multiple arguments', () => {
logger.setLevel(LogLevel.INFO);
logger.info('Test message', { data: 'value' }, 123);
expect(mockConsole.info).toHaveBeenCalledWith(
expect.stringContaining('[INFO]'),
expect.stringContaining('Test message'),
{ data: 'value' },
123
);
});
it('should handle error objects', () => {
logger.setLevel(LogLevel.ERROR);
const error = new Error('Test error');
logger.error('Error occurred', error);
expect(mockConsole.error).toHaveBeenCalledWith(
expect.stringContaining('[ERROR]'),
expect.stringContaining('Error occurred'),
error
);
});
});
describe('conditional logging', () => {
it('should respect log level hierarchy', () => {
logger.setLevel(LogLevel.WARN);
logger.debug('Debug message');
logger.info('Info message');
logger.warn('Warn message');
logger.error('Error message');
expect(mockConsole.log).not.toHaveBeenCalled(); // debug
expect(mockConsole.info).not.toHaveBeenCalled(); // info
expect(mockConsole.warn).toHaveBeenCalledTimes(1); // warn
expect(mockConsole.error).toHaveBeenCalledTimes(1); // error
});
});
});