Files

27 lines
2.0 KiB
JavaScript

/**
* lib/test.mjs — 작은 테스트 러너(의존성 없음). 형제 제품(AriMail)과 같은 방식: 파일을 node 로 직접 실행하면 순서대로 돈다.
* import { test, eq, ok, rejects, tmpDir, done } from '../lib/test.mjs';
* await test('이름', async () => { ... }); 실패하면 즉시 요약을 내고 종료 코드 1.
* done(); 마지막에 요약 출력.
*/
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
let passed = 0; let failed = 0; const dirs = [];
export async function test(name, fn) {
const t0 = Date.now();
try { await fn(); passed++; console.log(` ✔ ${name} (${Date.now() - t0}ms)`); }
catch (e) { failed++; console.error(` ✘ ${name}\n ${e && e.stack ? e.stack.split('\n').slice(0, 6).join('\n ') : e}`); process.exitCode = 1; }
}
export function eq(a, b, msg = '') { const sa = JSON.stringify(a); const sb = JSON.stringify(b); if (sa !== sb) throw new Error(`${msg ? msg + ': ' : ''}기대 ${sb} 실제 ${sa}`); }
export function ok(v, msg = '') { if (!v) throw new Error(`${msg || '참이어야 합니다'} (실제 ${JSON.stringify(v)})`); }
export async function rejects(fn, codeOrRe, msg = '') {
try { await fn(); } catch (e) { if (codeOrRe == null) return e; const s = `${e.code || ''} ${e.message || ''}`; if (codeOrRe instanceof RegExp ? codeOrRe.test(s) : (e.code === codeOrRe || String(e.message).includes(codeOrRe))) return e; throw new Error(`${msg || '다른 오류'}: 기대 ${codeOrRe} 실제 ${s}`); }
throw new Error(`${msg || '예외가 나야 합니다'}`);
}
/** 테스트용 임시 디렉터리(종료 시 삭제) */
export function tmpDir(prefix = 'aw-test-') { const d = mkdtempSync(join(tmpdir(), prefix)); dirs.push(d); return d; }
export function done() { for (const d of dirs) { try { rmSync(d, { recursive: true, force: true }); } catch (_) {} } console.log(`${failed ? '✘' : '✔'} 통과 ${passed} · 실패 ${failed}`); if (failed) process.exit(1); }
export const sleep = (ms) => new Promise((r) => setTimeout(r, ms));