import { describe, expect, it, vi } from 'vitest'; import { wireAccountChatEvents, type AccountRuntimeEventReporter, } from '../src/services/account-runtime-event-wiring.js'; import type { MediaReceivedListener, NewReplyListener, SyncProgressListener, } from '../src/services/chat/index.js'; function harness() { let progressListener: SyncProgressListener | null = null; let replyListener: NewReplyListener | null = null; let mediaListener: MediaReceivedListener | null = null; const chatService = { setProgressListener: vi.fn((listener: SyncProgressListener | null) => { progressListener = listener; }), setNewReplyListener: vi.fn((listener: NewReplyListener | null) => { replyListener = listener; }), setMediaReceivedListener: vi.fn((listener: MediaReceivedListener | null) => { mediaListener = listener; }), }; const events = { conversation: vi.fn(), reply: vi.fn(), mediaReceived: vi.fn(), sync: vi.fn(), } satisfies AccountRuntimeEventReporter; wireAccountChatEvents(chatService, events, '@t2_account:reddit.com'); return { events, progress: (value: Parameters[0], completion?: Parameters[1]) => { if (!progressListener) throw new Error('Progress listener was not installed'); progressListener(value, completion); }, reply: (value: Parameters[0]) => { if (!replyListener) throw new Error('Reply listener was not installed'); replyListener(value); }, media: (value: Parameters[0]) => { if (!mediaListener) throw new Error('Media listener was not installed'); mediaListener(value); }, }; } describe('wireAccountChatEvents', () => { it('publishes replies and media received by an account-owned chat service', () => { const fixture = harness(); fixture.reply({ conversationId: '!room:reddit.com', replyId: 'event-1', username: 'alice', replySnippet: 'hello', }); fixture.media({ conversationId: '!room:reddit.com', username: 'alice', count: 2 }); expect(fixture.events.conversation).toHaveBeenCalledWith({ id: '!room:reddit.com', state: 'replied', unread: 1, }); expect(fixture.events.reply).toHaveBeenCalledWith({ conversationId: '!room:reddit.com', id: 'event-1', username: 'alice', body: 'hello', }); expect(fixture.events.mediaReceived).toHaveBeenCalledWith({ conversationId: '!room:reddit.com', username: 'alice', count: 2, }); }); it('publishes account-scoped sync progress and failures', () => { const fixture = harness(); const progress = { kind: 'delta', phase: 'starting', current: 0, total: 4, startedAt: 123, } as const; fixture.progress(progress); fixture.progress({ ...progress, phase: 'listing-rooms', current: 2, label: 'Loading rooms' }); fixture.progress(null, { status: 'failed', error: 'Matrix unavailable' }); expect(fixture.events.sync).toHaveBeenNthCalledWith(1, { phase: 'started', id: 'chat-@t2_account:reddit.com-123', kind: 'delta', }); expect(fixture.events.sync).toHaveBeenNthCalledWith(2, { phase: 'progress', id: 'chat-@t2_account:reddit.com-123', kind: 'delta', current: 2, total: 4, label: 'Loading rooms', }); expect(fixture.events.sync).toHaveBeenNthCalledWith(3, { phase: 'failed', id: 'chat-@t2_account:reddit.com-123', kind: 'delta', error: 'Matrix unavailable', }); }); });