Cover StreamHandler emit to custom Writable, default stream fallback, and error handling on write failure. Cover ConsoleHandler level-to- method mapping (error/warn/log). Cover StderrHandler console.error output. Cover FileHandler browser rejection. Cover Handler base class handleError not throwing.
211 lines
6.6 KiB
TypeScript
211 lines
6.6 KiB
TypeScript
import {expect, jest, test} from '@jest/globals';
|
|
import {
|
|
Handler,
|
|
StreamHandler,
|
|
ConsoleHandler,
|
|
StderrHandler,
|
|
FileHandler,
|
|
} from '../../src/handler';
|
|
import { Formatter, DEFAULT_FORMATTER } from '../../src/formatter';
|
|
import { LogRecord } from '../../src/log-record';
|
|
import { Writable } from '../../src/helper/stream';
|
|
import { DEBUG, INFO, WARNING, ERROR, CRITICAL, NOTSET } from '../../src/log-level';
|
|
|
|
function makeRecord(level: number, msg: string): LogRecord {
|
|
return new LogRecord('test', { level, msg });
|
|
}
|
|
|
|
describe('Handler', () => {
|
|
describe('constructor', () => {
|
|
test('defaults to NOTSET level', () => {
|
|
const h = new Handler();
|
|
expect(h.level).toBe(NOTSET);
|
|
});
|
|
|
|
test('accepts a level argument', () => {
|
|
const h = new Handler(WARNING);
|
|
expect(h.level).toBe(WARNING);
|
|
});
|
|
});
|
|
|
|
describe('level setter', () => {
|
|
test('sets the level without infinite recursion', () => {
|
|
const h = new Handler();
|
|
h.level = ERROR;
|
|
expect(h.level).toBe(ERROR);
|
|
});
|
|
});
|
|
|
|
describe('formatter', () => {
|
|
test('getter returns null when no formatter set', () => {
|
|
const h = new Handler();
|
|
expect(h.formatter).toBeNull();
|
|
});
|
|
|
|
test('getter returns assigned formatter', () => {
|
|
const h = new Handler();
|
|
const fmt = new Formatter();
|
|
h.formatter = fmt;
|
|
expect(h.formatter).toBe(fmt);
|
|
});
|
|
});
|
|
|
|
describe('format', () => {
|
|
test('uses DEFAULT_FORMATTER when no formatter assigned', () => {
|
|
const h = new Handler();
|
|
const record = makeRecord(DEBUG, 'hello');
|
|
const result = h.format(record);
|
|
expect(typeof result).toBe('string');
|
|
});
|
|
|
|
test('returns a string', () => {
|
|
const h = new Handler();
|
|
const record = makeRecord(WARNING, 'test message');
|
|
expect(typeof h.format(record)).toBe('string');
|
|
});
|
|
});
|
|
|
|
describe('emit', () => {
|
|
test('throws NotImplementedError on base class', () => {
|
|
const h = new Handler();
|
|
const record = makeRecord(DEBUG, 'hello');
|
|
expect(() => h.emit(record)).toThrow('emit must be implemented');
|
|
});
|
|
});
|
|
|
|
describe('close', () => {
|
|
test('sets closed to true', () => {
|
|
const h = new Handler();
|
|
expect(h.closed).toBe(false);
|
|
h.close();
|
|
expect(h.closed).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('handleError', () => {
|
|
test('does not throw', () => {
|
|
const h = new Handler();
|
|
const record = makeRecord(DEBUG, 'test');
|
|
expect(() => h.handleError(record)).not.toThrow();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('StreamHandler', () => {
|
|
test('writes formatted output to the stream', () => {
|
|
const written: string[] = [];
|
|
const stream: Writable = { write: (data: string) => { written.push(data) } };
|
|
const h = new StreamHandler(stream);
|
|
const record = makeRecord(WARNING, 'stream test');
|
|
h.emit(record);
|
|
expect(written.length).toBe(1);
|
|
expect(written[0]).toContain('stream test');
|
|
});
|
|
|
|
test('uses default stream when none provided', () => {
|
|
const h = new StreamHandler();
|
|
const record = makeRecord(DEBUG, 'default stream');
|
|
expect(() => h.emit(record)).not.toThrow();
|
|
});
|
|
|
|
test('calls handleError on emit failure', () => {
|
|
const stream: Writable = {
|
|
write: () => { throw new Error('write failed') }
|
|
};
|
|
const h = new StreamHandler(stream);
|
|
const record = makeRecord(DEBUG, 'fail');
|
|
expect(() => h.emit(record)).not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('ConsoleHandler', () => {
|
|
let origLog: typeof console.log;
|
|
let origWarn: typeof console.warn;
|
|
let origError: typeof console.error;
|
|
let logged: string[];
|
|
let warned: string[];
|
|
let errored: string[];
|
|
|
|
beforeEach(() => {
|
|
logged = [];
|
|
warned = [];
|
|
errored = [];
|
|
origLog = console.log;
|
|
origWarn = console.warn;
|
|
origError = console.error;
|
|
console.log = (...args: any[]) => { logged.push(args.join(' ')) };
|
|
console.warn = (...args: any[]) => { warned.push(args.join(' ')) };
|
|
console.error = (...args: any[]) => { errored.push(args.join(' ')) };
|
|
});
|
|
|
|
afterEach(() => {
|
|
console.log = origLog;
|
|
console.warn = origWarn;
|
|
console.error = origError;
|
|
});
|
|
|
|
test('uses console.error for ERROR level', () => {
|
|
const h = new ConsoleHandler();
|
|
h.emit(makeRecord(ERROR, 'error msg'));
|
|
expect(errored.length).toBe(1);
|
|
expect(errored[0]).toContain('error msg');
|
|
});
|
|
|
|
test('uses console.error for CRITICAL level', () => {
|
|
const h = new ConsoleHandler();
|
|
h.emit(makeRecord(CRITICAL, 'critical msg'));
|
|
expect(errored.length).toBe(1);
|
|
expect(errored[0]).toContain('critical msg');
|
|
});
|
|
|
|
test('uses console.warn for WARNING level', () => {
|
|
const h = new ConsoleHandler();
|
|
h.emit(makeRecord(WARNING, 'warn msg'));
|
|
expect(warned.length).toBe(1);
|
|
expect(warned[0]).toContain('warn msg');
|
|
});
|
|
|
|
test('uses console.log for INFO level', () => {
|
|
const h = new ConsoleHandler();
|
|
h.emit(makeRecord(INFO, 'info msg'));
|
|
expect(logged.length).toBe(1);
|
|
expect(logged[0]).toContain('info msg');
|
|
});
|
|
|
|
test('uses console.log for DEBUG level', () => {
|
|
const h = new ConsoleHandler();
|
|
h.emit(makeRecord(DEBUG, 'debug msg'));
|
|
expect(logged.length).toBe(1);
|
|
expect(logged[0]).toContain('debug msg');
|
|
});
|
|
});
|
|
|
|
describe('StderrHandler', () => {
|
|
test('accepts a level', () => {
|
|
const h = new StderrHandler(ERROR);
|
|
expect(h.level).toBe(ERROR);
|
|
});
|
|
|
|
test('emits via console.error', () => {
|
|
const errored: string[] = [];
|
|
const origError = console.error;
|
|
console.error = (...args: any[]) => { errored.push(args.join(' ')) };
|
|
try {
|
|
const h = new StderrHandler(DEBUG);
|
|
h.emit(makeRecord(WARNING, 'stderr test'));
|
|
expect(errored.length).toBe(1);
|
|
expect(errored[0]).toContain('stderr test');
|
|
}
|
|
finally {
|
|
console.error = origError;
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('FileHandler', () => {
|
|
test('throws NotImplementedError on construction', () => {
|
|
expect(() => new FileHandler({
|
|
filename: 'test.log'
|
|
})).toThrow('not available in browser');
|
|
});
|
|
});
|