fix(03): WR-04 rank failed/dead outbox row above done in sync-status

This commit is contained in:
Lucas Berger
2026-06-09 11:04:00 -04:00
parent 5b720ffdb8
commit fd13852eb9
2 changed files with 53 additions and 1 deletions
+40
View File
@@ -547,6 +547,46 @@ describe('GET /api/events/sync-status', () => {
const body = await res.json() as { uid: string; status: string }
expect(body.status).toBe('done')
})
// WR-04 (iteration 2): with multiple rows for one uid, the query must rank a
// failed/dead row above a later done row so a silently-failed queued write is not
// masked by a subsequent success. The handler returns the top-ranked row's status and
// surfaces its error. The orderBy must include the status-priority CASE expression so
// the ranking is enforced in SQL (the mock cannot run real ORDER BY).
it('WR-04: prioritizes a dead row over a done row and surfaces its error', async () => {
// Seed the row the priority-ordered query WOULD return first: the dead one.
mockDbRows = [{ uid: 'uid-001@familysync', status: 'dead', lastError: 'boom', userId: 1 }]
const orderBySpy = vi.fn().mockReturnValue({
limit: vi.fn().mockImplementation(() => Promise.resolve(mockDbRows)),
})
const mockSimpleWhere = vi.fn().mockReturnValue({ orderBy: orderBySpy })
mockFromFn.mockReturnValue({ where: mockSimpleWhere })
mockSelectFn.mockReturnValue({ from: mockFromFn })
const { app } = await import('../../src/index.js')
const res = await app.request('/api/events/sync-status?uid=uid-001%40familysync')
expect(res.status).toBe(200)
const body = (await res.json()) as { uid: string; status: string; error?: string }
expect(body.status).toBe('dead')
expect(body.error).toBe('boom')
// The status-priority CASE expression must be part of the ORDER BY (WR-04).
// Drizzle sql`` builds a SQL object whose static text lives in queryChunks; scan
// those for the literal 'case' rather than JSON.stringify (the Drizzle structures
// are circular and throw on serialization).
expect(orderBySpy).toHaveBeenCalled()
const orderByArgs = orderBySpy.mock.calls[0] as Array<{ queryChunks?: unknown[] }>
const caseText = orderByArgs
.flatMap((arg) => (Array.isArray(arg?.queryChunks) ? arg.queryChunks : []))
.map((chunk) => {
// String chunks are { value: string[] } (StringChunk); join their values.
const value = (chunk as { value?: unknown })?.value
return Array.isArray(value) ? value.join('') : ''
})
.join(' ')
.toLowerCase()
expect(caseText).toContain('case')
})
})
// ---------------------------------------------------------------------------