import { describe, expect, it } from 'vitest'; import winston from 'winston'; import { RealtimeEventHub, type RealtimeEvent } from '../src/web/events/index.js'; import { LogBroadcaster } from '../src/web/log-broadcaster.js'; import { attachLogSink } from '../src/utils/logger.js'; describe('LogBroadcaster realtime bridge', () => { it('publishes each log through the typed event hub', () => { const occurredAt = new Date('2026-08-08T15:00:00.000Z'); const hub = new RealtimeEventHub({ capacity: 10, clock: { now: () => occurredAt }, }); const received: RealtimeEvent[] = []; let recent: unknown; hub.subscribe((event) => received.push(event)); const broadcaster = new LogBroadcaster(hub); broadcaster.push('warn', 'Connection is stale', { attempts: 2 }); broadcaster.recent( { query: {} } as never, { json: (value: unknown) => { recent = value; } } as never, ); expect(received).toEqual([ expect.objectContaining({ type: 'log.created', payload: { level: 'warn', message: 'Connection is stale', data: { attempts: 2 }, }, }), ]); expect(recent).toEqual([ expect.objectContaining({ timestamp: occurredAt.getTime() }), ]); }); it('sanitizes circular network errors before buffering or publishing them', () => { const hub = new RealtimeEventHub({ capacity: 10 }); const received: RealtimeEvent[] = []; hub.subscribe((event) => received.push(event)); const broadcaster = new LogBroadcaster(hub); const socket: Record = { constructorName: 'TLSSocket' }; const parser: Record = { socket }; socket.parser = parser; const networkError = Object.assign(new Error('upstream request failed'), { code: 'ECONNRESET', request: socket, }); let serializedRecent = ''; broadcaster.push('error', 'Matrix request failed', { error: networkError, socket, authorization: 'Bearer must-not-leak', }); broadcaster.recent( { query: {} } as never, { json: (value: unknown) => { serializedRecent = JSON.stringify(value); } } as never, ); expect(() => JSON.stringify(received)).not.toThrow(); expect(() => JSON.parse(serializedRecent)).not.toThrow(); expect(serializedRecent).toContain('ECONNRESET'); expect(serializedRecent).toContain('[Circular]'); expect(serializedRecent).toContain('[Redacted]'); expect(serializedRecent).not.toContain('must-not-leak'); expect(serializedRecent).not.toContain('"request"'); }); it('preserves account metadata from a child logger', async () => { const broadcaster = new LogBroadcaster(); const logger = winston.createLogger({ level: 'debug' }); attachLogSink(logger, broadcaster); logger.child({ accountId: '@t2_owner:reddit.com', redditUsername: 'operator', }).warn('Matrix request failed: M_LIMIT_EXCEEDED'); await new Promise((resolve) => setImmediate(resolve)); expect(broadcaster.getRecent()).toEqual([ expect.objectContaining({ level: 'warn', message: 'Matrix request failed: M_LIMIT_EXCEEDED', data: { accountId: '@t2_owner:reddit.com', redditUsername: 'operator', }, }), ]); }); });