test(sync): interleaving tests for the ungated sync↔backfill and sync↔prune pairs #191

Merged
JMR-dev merged 2 commits from test-53-sync-concurrency into main 2026-07-03 02:14:46 +00:00
2 Commits
Author SHA1 Message Date
Jason Ross cf274781bd Merge main into test-53-sync-concurrency 2026-07-02 21:03:10 -05:00
JMR-devandClaude Opus 4.8 b4a450a8b8 test(sync): interleaving tests for the ungated sync↔backfill and sync↔prune pairs
MailMaintenanceGate serializes only the backfill↔prune pair. The other two
pairs — sync↔backfill and sync↔prune — are deliberately ungated (foreground
sync uses its own syncMutex to stay UI-responsive) and rely on a disjoint-by-UID
argument for safety, with no test covering it (issue #53).

Add MailSyncConcurrencyTest: a real MailSyncer and a real MailBackfiller/
MailPruner wired to one shared in-memory message store, with CompletableDeferred
gates (mirroring MailMaintenanceGateTest) that park one actor mid-critical-
section while the other's whole critical section runs. Each interleaving is
driven to the boundary (window edge / count floor) where an overlap would
surface as a lost, duplicated, or wrongly-deleted row:

- sync's windowed reconcile runs while a backfill is parked mid-paging just
  below the window (tightest edge: lowestSyncedUid == minWindowUid);
- a backfill's below-window page lands after a concurrent full sync of the
  window (stale-boundary ordering);
- a count-retention prune runs while a foreground sync is parked in its fetch;
- a full foreground sync runs while a prune is parked inside its critical
  section, before it touches the message table.

All four pass: the disjointness invariant holds unlocked. No production code
changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 21:00:23 -05:00