import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { Logger } from '../src/index.js'; describe('Logger', () => { const packageName = 'test-pkg'; let consoleLogSpy: any; let consoleWarnSpy: any; let consoleErrorSpy: any; beforeEach(() => { consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => { }); consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { }); consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => { }); vi.stubEnv('LOG_LEVEL', 'info'); vi.stubEnv('THROW_ERROR', 'false'); vi.stubEnv('DEBUG', ''); }); afterEach(() => { vi.restoreAllMocks(); vi.unstubAllEnvs(); }); describe('Thresholds', () => { it('should suppress verbose and debug logs by default (info level)', () => { const logger = new Logger({ packageName }); logger.verbose('v'); logger.debug('d'); expect(consoleLogSpy).not.toHaveBeenCalled(); }); it('should emit info, warn, and error logs by default', () => { const logger = new Logger({ packageName }); logger.info('i'); logger.warn('w'); logger.error('e'); expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('[INFO] i')); expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining('[WARN] w')); expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('[ERROR] e')); }); it('should honor custom LOG_LEVEL', () => { vi.stubEnv('LOG_LEVEL', 'warn'); const logger = new Logger({ packageName }); logger.info('i'); logger.warn('w'); expect(consoleLogSpy).not.toHaveBeenCalled(); expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining('[WARN] w')); }); }); describe('DEBUG Namespace', () => { it('should enable debug logs when namespace matches DEBUG', () => { vi.stubEnv('DEBUG', 'foo:*'); const logger = new Logger({ packageName, debugNamespace: 'foo:bar' }); logger.debug('hello'); expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('[DEBUG] hello')); }); it('should NOT enable debug logs when namespace does not match DEBUG', () => { vi.stubEnv('DEBUG', 'foo:*'); const logger = new Logger({ packageName, debugNamespace: 'baz' }); logger.debug('hello'); expect(consoleLogSpy).not.toHaveBeenCalled(); }); it('should support multiple DEBUG patterns', () => { vi.stubEnv('DEBUG', 'foo,bar:*'); const logger1 = new Logger({ packageName, debugNamespace: 'foo' }); const logger2 = new Logger({ packageName, debugNamespace: 'bar:baz' }); logger1.debug('d1'); logger2.debug('d2'); expect(consoleLogSpy).toHaveBeenCalledTimes(2); }); }); describe('Error Throwing', () => { it('should throw Error by default when level is error', () => { vi.stubEnv('THROW_ERROR', 'true'); const logger = new Logger({ packageName }); expect(() => logger.error('failed')).toThrow('failed'); }); it('should NOT throw when THROW_ERROR is false', () => { vi.stubEnv('THROW_ERROR', 'false'); const logger = new Logger({ packageName }); expect(() => logger.error('failed')).not.toThrow(); }); it('should throw the provided Error object if available', () => { vi.stubEnv('THROW_ERROR', 'true'); const logger = new Logger({ packageName }); const customError = new Error('custom'); expect(() => logger.error('msg', customError)).toThrow(customError); }); }); describe('Prefixing', () => { it('should use custom environment prefix', () => { vi.stubEnv('MYAPP_LOG_LEVEL', 'error'); const logger = new Logger({ packageName, envPrefix: 'MYAPP' }); logger.warn('w'); expect(consoleWarnSpy).not.toHaveBeenCalled(); vi.stubEnv('MYAPP_THROW_ERROR', 'true'); const logger2 = new Logger({ packageName, envPrefix: 'MYAPP' }); expect(() => logger2.error('e')).toThrow(); }); }); });