Files
ytplayer/server/audit-log.test.js
2026-10-14 01:54:08 +08:00

36 lines
1.7 KiB
JavaScript

import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { Hono } from 'hono';
const root = mkdtempSync(join(tmpdir(), 'ytp-audit-test-'));
process.env.DB_PATH = join(root, 'audit.db');
const db = await import('./db.js');
const { audit, registerAuditRoutes } = await import('./audit-log.js');
const app = new Hono();
const auth = async (c, next) => c.req.header('x-admin') === 'yes' ? next() : c.json({ ok: false }, 401);
beforeAll(async () => {
await db.initDb();
registerAuditRoutes(app, { db, adminAuth: auth });
await audit(db, 'admin', 'notes.save.lyrics', 'video_1', { rev: 2 });
await audit(db, 'editor@example.test', 'report.resolve', '42', { batch: true });
});
afterAll(() => rmSync(root, { recursive: true, force: true }));
describe('admin audit log', () => {
test('is authenticated, filtered, paged and exports CSV/JSON', async () => {
expect((await app.request('/api/admin/audit')).status).toBe(401);
const headers = { 'x-admin': 'yes' };
const filtered = await (await app.request('/api/admin/audit?actor=editor&action=resolve', { headers })).json();
expect(filtered.total).toBe(1);
expect(filtered.events[0]).toMatchObject({ actor: 'editor@example.test', action: 'report.resolve', target: '42', detail: { batch: true } });
const csv = await app.request('/api/admin/audit?format=csv', { headers });
expect(csv.headers.get('content-type')).toContain('text/csv');
expect(await csv.text()).toContain('notes.save.lyrics');
const json = await (await app.request('/api/admin/audit?format=json', { headers })).json();
expect(json.events).toHaveLength(2);
});
});