/** * RED scaffold — pushCoalescer (Plan 05-03 turns this GREEN). * * Asserts that coalesceListPush: * - Fires the dispatch function exactly ONCE when N calls arrive within the coalesce window * - Passes the correct count (N) to the dispatch function * - Passes the actor's own userId as excludeUserId to suppress self-notifications (D-03) * * These tests fail now because pushCoalescer.ts does not yet exist. * Run: pnpm --filter @familysync/api exec vitest run tests/lib/pushCoalescer.test.ts */ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; describe('pushCoalescer — list-change burst coalescing (D-01)', () => { beforeEach(() => { vi.useFakeTimers(); vi.resetModules(); }); afterEach(() => { vi.useRealTimers(); }); it('collapses N rapid coalesceListPush calls into a single dispatch with count=N', async () => { const dispatch = vi.fn().mockResolvedValue(undefined); const { coalesceListPush } = await import('../../src/lib/pushCoalescer.js'); const listId = 1; const actorId = 10; const N = 5; for (let i = 0; i < N; i++) { coalesceListPush(listId, actorId, dispatch); } // Advance time past the coalesce window await vi.runAllTimersAsync(); expect(dispatch).toHaveBeenCalledTimes(1); const [calledListId, calledActorId, calledCount] = dispatch.mock.calls[0]; expect(calledListId).toBe(listId); expect(calledActorId).toBe(actorId); expect(calledCount).toBe(N); }); it('passes the actor userId as excludeUserId so the actor does not notify themselves (D-03)', async () => { const dispatch = vi.fn().mockResolvedValue(undefined); const { coalesceListPush } = await import('../../src/lib/pushCoalescer.js'); const listId = 2; const actorId = 99; coalesceListPush(listId, actorId, dispatch); await vi.runAllTimersAsync(); expect(dispatch).toHaveBeenCalledTimes(1); const [, calledActorId] = dispatch.mock.calls[0]; expect(calledActorId).toBe(actorId); }); it('fires separate dispatches for different lists independently', async () => { const dispatch = vi.fn().mockResolvedValue(undefined); const { coalesceListPush } = await import('../../src/lib/pushCoalescer.js'); coalesceListPush(1, 10, dispatch); coalesceListPush(1, 10, dispatch); coalesceListPush(2, 10, dispatch); // different list coalesceListPush(2, 10, dispatch); await vi.runAllTimersAsync(); // Two separate dispatches — one per distinct listId expect(dispatch).toHaveBeenCalledTimes(2); }); });