feat(gateway): add sender presence tracking

This commit is contained in:
William Valentin
2026-02-15 19:28:16 -08:00
parent 421942f66d
commit c6e3d09ecc
11 changed files with 358 additions and 8 deletions
+65
View File
@@ -132,6 +132,71 @@ describe('ChannelRegistry', () => {
expect(adapter.sendFn).toHaveBeenCalledWith('user-42', { text: 'pong' });
});
it('tracks presence from inbound messages', async () => {
const adapter = createMockAdapter('test-channel');
registry.register(adapter);
registry.setMessageHandler(async () => {});
adapter.triggerMessage(makeMessage('test-channel'));
await vi.waitFor(() => {
expect(registry.getPresence()).toHaveLength(1);
});
const [presence] = registry.getPresence();
expect(presence.channel).toBe('test-channel');
expect(presence.senderId).toBe('user-42');
expect(presence.messageCount).toBe(1);
expect(presence.status).toBe('online');
});
it('marks presence offline after inactivity window', async () => {
vi.useFakeTimers();
try {
registry = new ChannelRegistry({ offlineAfterMs: 1000 });
const adapter = createMockAdapter('test-channel');
registry.register(adapter);
registry.setMessageHandler(async () => {});
adapter.triggerMessage(makeMessage('test-channel'));
await vi.runAllTimersAsync();
vi.advanceTimersByTime(1500);
const [presence] = registry.getPresence();
expect(presence.status).toBe('offline');
} finally {
vi.useRealTimers();
}
});
it('filters presence by channel and status', async () => {
vi.useFakeTimers();
try {
registry = new ChannelRegistry({ offlineAfterMs: 1000 });
const a1 = createMockAdapter('telegram');
const a2 = createMockAdapter('discord');
registry.register(a1);
registry.register(a2);
registry.setMessageHandler(async () => {});
a1.triggerMessage(makeMessage('telegram'));
a2.triggerMessage({ ...makeMessage('discord'), senderId: 'user-99' });
await vi.runAllTimersAsync();
vi.advanceTimersByTime(1500);
a2.triggerMessage({ ...makeMessage('discord'), senderId: 'user-99' });
const telegramOnly = registry.getPresence({ channel: 'telegram' });
expect(telegramOnly).toHaveLength(1);
expect(telegramOnly[0].channel).toBe('telegram');
const onlineOnly = registry.getPresence({ status: 'online' });
expect(onlineOnly).toHaveLength(1);
expect(onlineOnly[0].channel).toBe('discord');
} finally {
vi.useRealTimers();
}
});
it('routes reply using metadata.replyPeerId when provided', async () => {
const adapter = createMockAdapter('test-channel');
registry.register(adapter);