import assert from 'node:assert/strict'; import {mkdtemp, readdir, readFile, rm, writeFile, stat} from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; import {fileURLToPath} from 'node:url'; import test from 'node:test'; import {build} from 'esbuild'; const compiled = await build({ entryPoints: [fileURLToPath(new URL('../utils/logger.ts', import.meta.url))], bundle: true, platform: 'node', format: 'esm', write: false, }); const {Logger} = await import(`data:text/javascript;base64,${Buffer.from(compiled.outputFiles[0].text).toString('base64')}`); test('closing logger drains pending records, closes file and ignores late logs', async () => { const directory = await mkdtemp(path.join(os.tmpdir(), 'node-bot-logger-')); try { const logger = new Logger(directory); for (let i = 0; i < 20; i++) logger.log([logger.info(String(i))]); const closed = logger.close(); assert.equal(logger.close(), closed); logger.log([logger.info('late')]); await closed; const [name] = await readdir(directory); const lines = (await readFile(path.join(directory, name), 'utf8')).trim().split('\n'); assert.deepEqual(lines.map(line => JSON.parse(line).log[0].message), Array.from({length: 20}, (_, i) => String(i))); } finally { await rm(directory, {recursive: true, force: true}); } }); test('disk history sorts dates and numeric rotations and skips malformed records', async () => { const directory = await mkdtemp(path.join(os.tmpdir(), 'node-bot-history-')); const record = id => ({id, timestamp: 1, log: [{message: id, font_color: '#ffffff', font_style: ['normal']}]}); try { await Promise.all([ writeFile(path.join(directory, '2026-10-16_log_99.log'), JSON.stringify(record('older')) + '\n'), writeFile(path.join(directory, '2026-10-17_log.log'), JSON.stringify(record('base')) + '\n'), writeFile(path.join(directory, '2026-10-17_log_9.log'), JSON.stringify(record('nine')) + '\n'), writeFile(path.join(directory, '2026-10-17_log_10.log'), [ JSON.stringify(record('ten')), 'broken json', 'null', '{}', '{"id":"bad","timestamp":"1","log":[]}', JSON.stringify(record('latest')), ].join('\n')), writeFile(path.join(directory, 'unrelated.log'), JSON.stringify(record('ignored'))), ]); assert.deepEqual((await Logger.read_recent_log(directory, 10)).map(item => item.id), ['older', 'base', 'nine', 'ten', 'latest']); assert.deepEqual(await Logger.read_recent_log(directory, 2), [record('ten'), record('latest')]); assert.deepEqual(await Logger.read_recent_log(directory, 0), []); const missing = path.join(directory, 'missing'); assert.deepEqual(await Logger.read_recent_log(missing, 10), []); await assert.rejects(stat(missing), {code: 'ENOENT'}); } finally { await rm(directory, {recursive: true, force: true}); } }); test('disk history handles chunk boundaries, oversized lines and a bounded read budget', async () => { const directory = await mkdtemp(path.join(os.tmpdir(), 'node-bot-history-')); const record = (id, message) => ({id, timestamp: 1, log: [{message, font_color: '#ffffff', font_style: ['normal']}]}); try { const first = record('first', '中文'.repeat(25000)); const last = record('last', 'tail'); const filename = path.join(directory, '2026-10-17_log.log'); await writeFile(filename, [JSON.stringify(first), 'x'.repeat(1024 * 1024 + 1), JSON.stringify(last), ''].join('\n')); assert.deepEqual(await Logger.read_recent_log(directory, 10), [first, last]); await writeFile(filename, JSON.stringify(first) + '\n' + 'x'.repeat(17 * 1024 * 1024) + '\n' + JSON.stringify(last)); assert.deepEqual(await Logger.read_recent_log(directory, 10), [last]); } finally { await rm(directory, {recursive: true, force: true}); } }); test('instance history includes immediate in-flight and queued records, deduplicates disk and survives close', async () => { const directory = await mkdtemp(path.join(os.tmpdir(), 'node-bot-history-')); const logger = new Logger(directory); try { for (let i = 0; i < 100; i++) logger.log([logger.info(String(i))]); const immediate = logger.read_recent_log(100); const recent = logger.read_recent_log(3); const closed = logger.close(); const closing = logger.read_recent_log(100); const records = await immediate; assert.deepEqual(records.map(record => record.log[0].message), Array.from({length: 100}, (_, i) => String(i))); assert.equal(new Set(records.map(record => record.id)).size, 100); assert.ok(records.every(record => typeof record.timestamp === 'number')); assert.deepEqual((await recent).map(record => record.log[0].message), ['97', '98', '99']); assert.deepEqual(await closing, records); await closed; assert.deepEqual(await Logger.read_recent_log(directory, 100), records); assert.deepEqual(await logger.read_recent_log(100), records); } finally { await logger.close(); await rm(directory, {recursive: true, force: true}); } });