chore: initial commit
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
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});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user