import test from 'node:test' import assert from 'node:assert/strict' import { formatLogEntry, formatLogTimestamp, normalizeLogLevel, resolveExpiredLogFilenames, resolveLogFilePath, shouldWriteLog, } from './logger.js' test('normalizeLogLevel falls back to info for unsupported values', () => { assert.equal(normalizeLogLevel('debug'), 'debug') assert.equal(normalizeLogLevel('WARN'), 'warn') assert.equal(normalizeLogLevel('verbose'), 'info') assert.equal(normalizeLogLevel(''), 'info') }) test('shouldWriteLog respects configured minimum log level', () => { assert.equal(shouldWriteLog('debug', 'debug'), true) assert.equal(shouldWriteLog('info', 'debug'), true) assert.equal(shouldWriteLog('debug', 'info'), false) assert.equal(shouldWriteLog('info', 'warn'), false) assert.equal(shouldWriteLog('warn', 'warn'), true) assert.equal(shouldWriteLog('error', 'warn'), true) }) test('resolveLogFilePath uses daily log file names by channel', () => { assert.match(resolveLogFilePath('app', '2026-04-14T10:00:00.000Z'), /app-2026-04-14\.log$/) assert.match(resolveLogFilePath('webhook', '2026-04-14T10:00:00.000Z'), /webhook-2026-04-14\.log$/) }) test('resolveExpiredLogFilenames keeps latest seven days and ignores unknown files', () => { const expired = resolveExpiredLogFilenames([ 'app-2026-04-14.log', 'app-2026-04-13.log', 'app-2026-04-08.log', 'app-2026-04-07.log', 'webhook-2026-04-06.log', 'app.log', 'webhook.log', 'random.txt', ], '2026-04-14T12:00:00.000Z', 7) assert.deepEqual(expired, [ 'app-2026-04-07.log', 'webhook-2026-04-06.log', 'app.log', 'webhook.log', ]) }) test('formatLogTimestamp renders readable local timestamp with timezone offset', () => { const rendered = formatLogTimestamp('2026-04-14T10:36:24.974Z') assert.match(rendered, /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3} [+-]\d{2}:\d{2}$/) }) test('formatLogEntry prints readable one-line output for flat details', () => { const text = formatLogEntry({ time: '2026-04-14T10:36:24.974Z', level: 'info', scope: '[http/access]', message: 'request completed', pid: 4671, detail: { requestId: 'req-123', method: 'GET', statusCode: 401, }, }, { color: false }) assert.match(text, /INFO\s+\[http\/access\] request completed/) assert.match(text, /pid=4671/) assert.match(text, /requestId=req-123/) assert.match(text, /method=GET/) assert.match(text, /statusCode=401/) assert.doesNotMatch(text, /^\{/) }) test('formatLogEntry prints nested detail blocks without ansi colors in file mode', () => { const text = formatLogEntry({ time: '2026-04-14T10:36:24.974Z', level: 'warn', scope: '[admin/auth]', message: '后台鉴权失败', pid: 4671, detail: { statusCode: 401, errorCode: 'admin_auth_required', error: { message: '未登录或登录已失效', stack: 'Error: test', }, }, }, { color: false }) assert.match(text, /WARN\s+\[admin\/auth\] 后台鉴权失败/) assert.match(text, /statusCode=401/) assert.match(text, /errorCode=admin_auth_required/) assert.match(text, /\n \{/) assert.match(text, /未登录或登录已失效/) assert.doesNotMatch(text, /\u001B\[/) })