- Use ICAL.Event.isRecurring() (parity with expand.ts) to detect RRULE/RDATE
- Add hasRrule to .values() INSERT and .onDuplicateKeyUpdate() SET so the flag
is set on first sync and self-heals on every subsequent re-sync
- Without this fix every event had has_rrule=0 (column default), causing the
events route recurring-master pre-filter to return zero recurring occurrences
- Add sync.test.ts cases: hasRrule=true for timed+all-day recurring VEVENTs,
hasRrule=false for non-recurring, and hasRrule in onDuplicateKeyUpdate.set
- Use separate mock functions for insert/select chain to enable per-test assertions
- Reset mock implementations in beforeEach after vi.clearAllMocks
- Mock returns {id:42} from select (calendar ID for event upsert)
- 6 tests: all-day DATE vs timed TIMESTAMP, allDay flag, upsert on dup UID, rawVevent blob, ctag update
- Mocks db singleton at module level with vi.mock hoisting pattern
- Uses SAMPLE_VEVENT_TIMED/ALLDAY fixtures from tests/helpers/db.ts
- RED gate: all fail (src/broker/sync.ts does not exist yet)