esm-logging/tests/unit/logger.test.ts

209 lines
6.9 KiB
TypeScript

import {expect, jest, test} from '@jest/globals';
import { Logger, RootLogger, ROOT } from '../../src/logger';
import { Handler } from '../../src/handler';
import { LogRecord } from '../../src/log-record';
import { Formatter } from '../../src/formatter';
import { Manager } from '../../src/manager';
import {
DEBUG,
INFO,
WARNING,
ERROR,
CRITICAL,
NOTSET,
} from '../../src/log-level';
import { MyError } from '../../src/helper/error';
describe('Logger', () => {
describe('constructor', () => {
test('sets scope and default level', () => {
const logger = new Logger('test');
expect(logger.scope).toBe('test');
expect(logger.level).toBe(NOTSET);
});
test('accepts explicit level', () => {
const logger = new Logger('test', WARNING);
expect(logger.level).toBe(WARNING);
});
});
describe('setLevel', () => {
test('changes the level', () => {
const logger = new Logger('test');
logger.setLevel(ERROR);
expect(logger.level).toBe(ERROR);
});
});
describe('getEffectiveLevel', () => {
test('returns own level when set', () => {
const logger = new Logger('test', WARNING);
expect(logger.getEffectiveLevel()).toBe(WARNING);
});
test('returns NOTSET when no level set and no parent', () => {
const logger = new Logger('test');
expect(logger.getEffectiveLevel()).toBe(NOTSET);
});
});
describe('isEnabledFor', () => {
test('returns true when level meets effective level', () => {
const logger = new Logger('test', DEBUG);
expect(logger.isEnabledFor(WARNING)).toBe(true);
expect(logger.isEnabledFor(DEBUG)).toBe(true);
});
test('returns false when level is below effective level', () => {
const logger = new Logger('test', WARNING);
expect(logger.isEnabledFor(DEBUG)).toBe(false);
expect(logger.isEnabledFor(INFO)).toBe(false);
});
test('caches results for repeated calls', () => {
const logger = new Logger('test', WARNING);
const first = logger.isEnabledFor(DEBUG);
const second = logger.isEnabledFor(DEBUG);
expect(first).toBe(second);
});
test('respects manager disable level', () => {
const root = new RootLogger(WARNING);
const manager = new Manager(root);
const logger = manager.getLogger('test');
logger.setLevel(DEBUG);
logger.manager = manager;
manager.disable = ERROR;
logger.clear();
expect(logger.isEnabledFor(DEBUG)).toBe(false);
expect(logger.isEnabledFor(WARNING)).toBe(false);
expect(logger.isEnabledFor(CRITICAL)).toBe(true);
});
});
describe('manager property', () => {
test('starts as null', () => {
const logger = new Logger('test');
expect(logger.manager).toBeNull();
});
test('can be assigned once', () => {
const logger = new Logger('test');
const root = new RootLogger(WARNING);
const manager = new Manager(root);
logger.manager = manager;
expect(logger.manager).toBe(manager);
});
test('throws on second assignment', () => {
const logger = new Logger('test');
const root = new RootLogger(WARNING);
const manager = new Manager(root);
logger.manager = manager;
expect(() => { logger.manager = manager }).toThrow('logger can only be assigned to manager once');
});
});
describe('addHandler / removeHandler', () => {
test('adds and removes handlers', () => {
const logger = new Logger('test');
const handler = new Handler(DEBUG);
expect(logger.handlers.length).toBe(0);
logger.addHandler(handler);
expect(logger.handlers.length).toBe(1);
logger.removeHandler(handler);
});
test('does not add duplicate handlers', () => {
const logger = new Logger('test');
const handler = new Handler(DEBUG);
logger.addHandler(handler);
logger.addHandler(handler);
expect(logger.handlers.length).toBe(1);
});
});
describe('level methods', () => {
let logger: Logger;
let emitted: LogRecord[];
class TestHandler extends Handler {
emit(record: LogRecord) { emitted.push(record) }
}
beforeEach(() => {
logger = new Logger('test', DEBUG);
emitted = [];
logger.addHandler(new TestHandler(DEBUG));
});
test('debug emits at DEBUG level', () => {
logger.debug('msg');
expect(emitted.length).toBe(1);
expect(emitted[0].levelno).toBe(DEBUG);
});
test('info emits at INFO level', () => {
logger.info('msg');
expect(emitted.length).toBe(1);
expect(emitted[0].levelno).toBe(INFO);
});
test('warning emits at WARNING level', () => {
logger.warning('msg');
expect(emitted.length).toBe(1);
expect(emitted[0].levelno).toBe(WARNING);
});
test('error emits at ERROR level', () => {
logger.error('msg');
expect(emitted.length).toBe(1);
expect(emitted[0].levelno).toBe(ERROR);
});
test('critical emits at CRITICAL level', () => {
logger.critical('msg');
expect(emitted.length).toBe(1);
expect(emitted[0].levelno).toBe(CRITICAL);
});
test('level methods respect effective level', () => {
logger.setLevel(ERROR);
logger.clear();
logger.debug('no');
logger.info('no');
logger.warning('no');
expect(emitted.length).toBe(0);
logger.error('yes');
logger.critical('yes');
expect(emitted.length).toBe(2);
});
});
describe('makeRecord', () => {
test('throws KeyError when extra overwrites existing LogRecord key', () => {
const logger = new Logger('test', DEBUG);
expect(() => {
logger.debug('test', {
excInfo: null,
extra: { scope: 'override' },
stackInfo: false,
stackLevel: 1,
});
}).toThrow('attempt to overwrite scope in LogRecord');
});
});
});
describe('RootLogger', () => {
test('has scope "root"', () => {
const root = new RootLogger(WARNING);
expect(root.scope).toBe('root');
});
test('accepts a level', () => {
const root = new RootLogger(ERROR);
expect(root.level).toBe(ERROR);
});
});