Implement result merging and deduplication logic
epic-contact-search-service-infrastructure-task-005 — Build the merging and deduplication layer in ContactSearchService that combines results from multiple sources (Supabase and Drift), removes duplicate contact entries by ID, applies a consistent sort order, and ensures the merged result set does not exceed a configurable maximum size.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Implement as a pure function or static method `List
The maxResults default of 50 should be a named constant in a SearchConstants class to allow future configuration. Do not use List.toSet() for deduplication as it relies on object equality which may not be defined on Contact model.
Testing Requirements
Unit tests only. Test mergeAndDeduplicate() in isolation with mock contact lists. Cover: (1) full overlap ā all IDs duplicated, (2) partial overlap ā some shared IDs, (3) no overlap ā disjoint sets, (4) empty left source, (5) empty right source, (6) both empty, (7) result truncation at maxResults boundary, (8) sort order correctness with identical names but different IDs, (9) Supabase precedence over Drift for same ID. Aim for 100% branch coverage on the merge function.
Use flutter_test package.
Cancelling in-flight Supabase HTTP requests via RxDart switchMap may not actually abort the server-side query if the Supabase Dart client does not support request cancellation tokens, leading to wasted API calls and potential race conditions where a slow earlier response arrives after a faster later one.
Mitigation & Contingency
Mitigation: Audit the supabase-flutter client's cancellation support before implementation. Use RxDart switchMap to discard stale emissions even if HTTP cancellation is unavailable, ensuring only the latest result reaches the UI.
Contingency: If race conditions surface in testing, add a query sequence counter to tag each emission and discard any response whose sequence number is lower than the most recently emitted one.
Connectivity detection used to route between online and offline repositories may have latency or give false positives on flaky connections, causing the service to attempt Supabase queries that time out instead of falling back to the cache promptly.
Mitigation & Contingency
Mitigation: Use a try/catch with a short timeout on Supabase calls. On network error, immediately fall back to the offline repository and emit a cached result with an offline indicator rather than surfacing an error state.
Contingency: If the timeout-based fallback proves insufficient, implement a connection health check stream that pre-validates connectivity before each query batch.