import { describe, expect, it, vi } from 'vitest' import type { IssueChange } from '../changes/apply-changes-v0.js' import { affectedIssues, coalesceKey, enqueueWrite, pendingWrites, replayQueue, type QueuedWrite } from './write-queue-v0.js' const reest = (issue: number, estimate: string): IssueChange => ({ kind: 'reestimate', issue, estimate }) as IssueChange const assign = (issue: number, assignee: string | null): IssueChange => ({ kind: 'assign', issue, assignee }) describe('write-queue-v0 (#33)', () => { it('coalesces repeat writes to the same (issue, axis) — replay applies the latest once', () => { let q: QueuedWrite[] = [] q = enqueueWrite(q, reest(7, 'est/2d'), '2026-02-10T09:00:00Z') q = enqueueWrite(q, reest(7, 'est/5d'), '2026-02-10T09:05:00Z') // supersedes est/2d q = enqueueWrite(q, assign(7, 'christian'), '2026-02-10T09:06:00Z') // different axis — kept expect(q).toHaveLength(2) const pending = pendingWrites(q) expect(pending).toEqual([ { kind: 'reestimate', issue: 7, estimate: 'est/5d' }, { kind: 'assign', issue: 7, assignee: 'christian' }, ]) expect(coalesceKey(reest(7, 'est/2d'))).toBe('7:reestimate') }) it('keeps writes to different issues and axes distinct', () => { let q: QueuedWrite[] = [] q = enqueueWrite(q, reest(7, 'est/2d'), 't1') q = enqueueWrite(q, reest(8, 'est/3d'), 't2') q = enqueueWrite(q, assign(8, null), 't3') expect(q).toHaveLength(3) expect(affectedIssues(q)).toEqual([7, 8]) }) it('a burst of edits then reconnect lands the final state without duplicating', async () => { let q: QueuedWrite[] = [] // offline: three edits to #7's estimate, one assign q = enqueueWrite(q, reest(7, 'est/1d'), 't1') q = enqueueWrite(q, reest(7, 'est/2d'), 't2') q = enqueueWrite(q, reest(7, 'est/8d'), 't3') q = enqueueWrite(q, assign(7, 'stephen'), 't4') const apply = vi.fn(async () => ({ ok: true })) const { drained, remaining } = await replayQueue(q, apply) // only the final estimate + the assign are applied — not three estimate writes expect(apply).toHaveBeenCalledTimes(2) expect(apply).toHaveBeenNthCalledWith(1, { kind: 'reestimate', issue: 7, estimate: 'est/8d' }) expect(apply).toHaveBeenNthCalledWith(2, { kind: 'assign', issue: 7, assignee: 'stephen' }) expect(remaining).toHaveLength(0) expect(drained).toHaveLength(2) }) it('keeps writes that still fail on reconnect queued (never throws)', async () => { let q: QueuedWrite[] = [] q = enqueueWrite(q, reest(7, 'est/2d'), 't1') q = enqueueWrite(q, reest(8, 'est/3d'), 't2') // #7 applies, #8 rejects (still unreachable) — and one apply throws const apply = vi.fn(async (c: IssueChange) => { if (c.issue === 8) throw new Error('offline') return { ok: true } }) const { drained, remaining } = await replayQueue(q, apply) expect(drained.map((w) => w.change.issue)).toEqual([7]) expect(remaining.map((w) => w.change.issue)).toEqual([8]) }) })