fix(05-review): NEW-WR-01 emit delete changes when server returns zero events (whole-cache clear)
Pre-capture all currently-cached rows into pendingDeleteRows before the whole-cache db.delete() when seenUids.length === 0. The existing >0 branch behavior is unchanged. Adds a regression test verifying onChanges receives one delete change per cached row on a full-calendar clear.
This commit is contained in:
@@ -365,4 +365,50 @@ describe('syncCalendar', () => {
|
||||
expect(mockDelete).toHaveBeenCalledTimes(1)
|
||||
expect(mockDeleteWhere).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
// NEW-WR-01: When the server returns zero events (whole-cache clear), the
|
||||
// onChanges callback must still receive a 'delete' change for each cached row
|
||||
// that is being pruned. Before this fix, the pendingDeleteRows pre-capture only
|
||||
// ran inside the seenUids.length > 0 branch, so bulk/clear deletions silently
|
||||
// dropped all delete change events.
|
||||
it('NEW-WR-01: emits delete change events for each cached row when server returns zero events', async () => {
|
||||
const { syncCalendar } = await import('../../src/broker/sync.js')
|
||||
|
||||
// First mockWhere call: calendar row lookup (ends in .limit(1)) → { limit: mockLimit }
|
||||
// Second mockWhere call: pending-delete pre-capture (direct await, no .limit()) →
|
||||
// must return an iterable array of cached rows. Use mockReturnValueOnce for the
|
||||
// second call so it returns a resolved Promise<array> instead of { limit }.
|
||||
//
|
||||
// Call order when seenUids.length === 0 and onChanges is provided:
|
||||
// 1. db.select().from(calendars).where(...).limit(1) — calendar row lookup
|
||||
// 2. db.select({uid,title}).from(calendarEvents).where(...) — pending-delete capture (awaited directly)
|
||||
mockWhere
|
||||
.mockReturnValueOnce({ limit: mockLimit }) // call 1: calendar row lookup
|
||||
.mockResolvedValueOnce([ // call 2: pending-delete capture
|
||||
{ uid: 'uid-to-delete-1', title: 'Event A' },
|
||||
{ uid: 'uid-to-delete-2', title: 'Event B' },
|
||||
])
|
||||
|
||||
const mockClient = {
|
||||
fetchCalendarObjects: vi.fn().mockResolvedValue([]),
|
||||
}
|
||||
const mockDavCal = {
|
||||
url: 'https://caldav.fastmail.com/dav/calendars/user/test@fm.com/Default/',
|
||||
displayName: 'Test Calendar',
|
||||
ctag: 'ctag-empty',
|
||||
syncToken: null,
|
||||
}
|
||||
|
||||
const collectedChanges: import('../../src/lib/eventChangeDispatcher.js').EventChange[] = []
|
||||
const onChanges = (changes: import('../../src/lib/eventChangeDispatcher.js').EventChange[]) => {
|
||||
collectedChanges.push(...changes)
|
||||
}
|
||||
|
||||
await syncCalendar(mockClient as never, mockDavCal as never, 1, onChanges)
|
||||
|
||||
// The callback must have received one delete change per cached row.
|
||||
expect(collectedChanges).toHaveLength(2)
|
||||
expect(collectedChanges[0]).toMatchObject({ uid: 'uid-to-delete-1', operation: 'delete' })
|
||||
expect(collectedChanges[1]).toMatchObject({ uid: 'uid-to-delete-2', operation: 'delete' })
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user