105 lines
3.2 KiB
JavaScript
105 lines
3.2 KiB
JavaScript
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\[/)
|
|
})
|