feat(telemetry): exportTo writes events.jsonl + stats.md (#7 v0.2.3)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { mkdir, appendFile, readFile, readdir, unlink } from 'node:fs/promises';
|
||||
import { mkdir, appendFile, readFile, readdir, unlink, writeFile } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import { validateEvent, TelemetryEvent } from './telemetryEvents.js';
|
||||
import { aggregateStats } from './telemetryStats.js';
|
||||
|
||||
const KST_OFFSET_MS = 9 * 60 * 60 * 1000;
|
||||
|
||||
@@ -105,4 +106,14 @@ export class TelemetryService {
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
async exportTo(outDir: string): Promise<{ eventCount: number }> {
|
||||
const events = await this.readAllRecent();
|
||||
await mkdir(outDir, { recursive: true });
|
||||
const eventsContent = events.map((e) => JSON.stringify(e)).join('\n') + (events.length > 0 ? '\n' : '');
|
||||
await writeFile(join(outDir, 'events.jsonl'), eventsContent, 'utf8');
|
||||
const stats = aggregateStats(events, this.now());
|
||||
await writeFile(join(outDir, 'stats.md'), stats.md, 'utf8');
|
||||
return { eventCount: stats.eventCount };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,3 +172,38 @@ describe('TelemetryService.readAllRecent', () => {
|
||||
expect(await svc.readAllRecent()).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TelemetryService.exportTo', () => {
|
||||
let dir: string;
|
||||
let outDir: string;
|
||||
beforeEach(() => {
|
||||
dir = mkdtempSync(join(tmpdir(), 'inkling-telem-'));
|
||||
outDir = mkdtempSync(join(tmpdir(), 'inkling-export-'));
|
||||
});
|
||||
afterEach(() => {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
rmSync(outDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('writes events.jsonl (concat) + stats.md to folder', async () => {
|
||||
writeFileSync(join(dir, 'events-2026-05-01.jsonl'),
|
||||
JSON.stringify({ ts: '2026-05-01T00:00:00.000Z', kind: 'capture', payload: { noteId: 'a', rawTextLength: 1, hasMedia: false } }) + '\n');
|
||||
const svc = new TelemetryService(dir, () => new Date('2026-05-01T12:00:00Z'), 14);
|
||||
const r = await svc.exportTo(outDir);
|
||||
expect(r.eventCount).toBe(1);
|
||||
expect(existsSync(join(outDir, 'events.jsonl'))).toBe(true);
|
||||
expect(existsSync(join(outDir, 'stats.md'))).toBe(true);
|
||||
const events = readFileSync(join(outDir, 'events.jsonl'), 'utf8').trim().split('\n');
|
||||
expect(events).toHaveLength(1);
|
||||
const stats = readFileSync(join(outDir, 'stats.md'), 'utf8');
|
||||
expect(stats).toContain('총 이벤트: 1');
|
||||
});
|
||||
|
||||
it('handles empty input — writes 0-event stats', async () => {
|
||||
const svc = new TelemetryService(dir, () => new Date('2026-05-01T12:00:00Z'), 14);
|
||||
const r = await svc.exportTo(outDir);
|
||||
expect(r.eventCount).toBe(0);
|
||||
expect(readFileSync(join(outDir, 'events.jsonl'), 'utf8')).toBe('');
|
||||
expect(readFileSync(join(outDir, 'stats.md'), 'utf8')).toContain('총 이벤트: 0');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user