test(formatter): add tests for formatting, formatTime, formatError
Cover %-style substitution with %(name)s, %(levelno)d, %(levelname)s, multiple placeholders, unknown field errors, asctime population, ISO8601 and custom datefmt time formatting, Error.stack formatting, usesTime detection, and LogRecord.getMessage with %s arg substitution.
This commit is contained in:
parent
7b9e88ef59
commit
3422cfb799
1 changed files with 143 additions and 0 deletions
143
tests/unit/formatter.test.ts
Normal file
143
tests/unit/formatter.test.ts
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
import {expect, jest, test} from '@jest/globals';
|
||||||
|
import { Formatter, DEFAULT_FORMATTER } from '../../src/formatter';
|
||||||
|
import { LogRecord } from '../../src/log-record';
|
||||||
|
import { DEBUG, WARNING, INFO } from '../../src/log-level';
|
||||||
|
import { MyError } from '../../src/helper/error';
|
||||||
|
|
||||||
|
function makeRecord(level: number, msg: string): LogRecord {
|
||||||
|
return new LogRecord('test.module', { level, msg });
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Formatter', () => {
|
||||||
|
describe('constructor', () => {
|
||||||
|
test('uses default format when no options given', () => {
|
||||||
|
const fmt = new Formatter();
|
||||||
|
const record = makeRecord(WARNING, 'hello');
|
||||||
|
const result = fmt.format(record);
|
||||||
|
expect(result).toBe('hello');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('accepts custom format string', () => {
|
||||||
|
const fmt = new Formatter({ fmt: '%(levelname)s - %(message)s' });
|
||||||
|
const record = makeRecord(DEBUG, 'debug msg');
|
||||||
|
expect(fmt.format(record)).toBe('DEBUG - debug msg');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws on invalid style', () => {
|
||||||
|
expect(() => new Formatter({ style: '{' })).toThrow('style must be one of');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('format', () => {
|
||||||
|
test('substitutes %(name)s with logger scope', () => {
|
||||||
|
const fmt = new Formatter({ fmt: '[%(name)s] %(message)s' });
|
||||||
|
const record = makeRecord(INFO, 'test');
|
||||||
|
expect(fmt.format(record)).toBe('[test.module] test');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('substitutes %(levelno)d with numeric level', () => {
|
||||||
|
const fmt = new Formatter({ fmt: '%(levelno)d: %(message)s' });
|
||||||
|
const record = makeRecord(WARNING, 'warn');
|
||||||
|
expect(fmt.format(record)).toBe('30: warn');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('substitutes %(levelname)s with level name', () => {
|
||||||
|
const fmt = new Formatter({ fmt: '%(levelname)s %(message)s' });
|
||||||
|
const record = makeRecord(DEBUG, 'msg');
|
||||||
|
expect(fmt.format(record)).toBe('DEBUG msg');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('handles multiple placeholders', () => {
|
||||||
|
const fmt = new Formatter({
|
||||||
|
fmt: '%(levelname)s:%(name)s:%(levelno)d:%(message)s'
|
||||||
|
});
|
||||||
|
const record = makeRecord(WARNING, 'multi');
|
||||||
|
expect(fmt.format(record)).toBe('WARNING:test.module:30:multi');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws on unknown field', () => {
|
||||||
|
const fmt = new Formatter({ fmt: '%(nonexistent)s' });
|
||||||
|
const record = makeRecord(DEBUG, 'test');
|
||||||
|
expect(() => fmt.format(record)).toThrow('formatting field not found');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('populates asctime when format uses it', () => {
|
||||||
|
const fmt = new Formatter({
|
||||||
|
fmt: '%(asctime)s %(message)s'
|
||||||
|
});
|
||||||
|
const record = makeRecord(INFO, 'timed');
|
||||||
|
const result = fmt.format(record);
|
||||||
|
expect(result).toContain('timed');
|
||||||
|
expect(record.asctime.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not populate asctime when format does not use it', () => {
|
||||||
|
const fmt = new Formatter({ fmt: '%(message)s' });
|
||||||
|
const record = makeRecord(INFO, 'no time');
|
||||||
|
fmt.format(record);
|
||||||
|
expect(record.asctime).toBe('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('formatTime', () => {
|
||||||
|
test('returns ISO8601-like string by default', () => {
|
||||||
|
const fmt = new Formatter();
|
||||||
|
const record = makeRecord(INFO, 'test');
|
||||||
|
const result = fmt.formatTime(record);
|
||||||
|
expect(result).toMatch(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('uses custom datefmt with strftime tokens', () => {
|
||||||
|
const fmt = new Formatter();
|
||||||
|
const record = makeRecord(INFO, 'test');
|
||||||
|
const result = fmt.formatTime(record, '%Y/%m/%d');
|
||||||
|
expect(result).toMatch(/^\d{4}\/\d{2}\/\d{2}$/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('formatError', () => {
|
||||||
|
test('returns stack trace when available', () => {
|
||||||
|
const fmt = new Formatter();
|
||||||
|
const err = new MyError('test error');
|
||||||
|
const result = fmt.formatError(err);
|
||||||
|
expect(result).toContain('test error');
|
||||||
|
expect(result).toContain('\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns string representation when no stack', () => {
|
||||||
|
const fmt = new Formatter();
|
||||||
|
const err = new MyError('no stack');
|
||||||
|
err.stack = undefined;
|
||||||
|
const result = fmt.formatError(err);
|
||||||
|
expect(result).toContain('no stack');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('usesTime', () => {
|
||||||
|
test('returns true when format contains asctime', () => {
|
||||||
|
const fmt = new Formatter({ fmt: '%(asctime)s %(message)s' });
|
||||||
|
expect(fmt.usesTime()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns false when format does not contain asctime', () => {
|
||||||
|
const fmt = new Formatter({ fmt: '%(message)s' });
|
||||||
|
expect(fmt.usesTime()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('LogRecord.getMessage', () => {
|
||||||
|
test('returns the message string', () => {
|
||||||
|
const record = makeRecord(INFO, 'plain message');
|
||||||
|
expect(record.getMessage()).toBe('plain message');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('substitutes %s args into message', () => {
|
||||||
|
const record = new LogRecord('test', {
|
||||||
|
level: INFO,
|
||||||
|
msg: 'hello %s, you have %s items',
|
||||||
|
args: ['world', '5'],
|
||||||
|
});
|
||||||
|
expect(record.getMessage()).toBe('hello world, you have 5 items');
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue