Expose reactive search result stream from ContactSearchService
epic-contact-search-service-infrastructure-task-006 — Wire the debounced query stream from SearchDebounceUtility into ContactSearchService and expose a reactive Stream<SearchResult> (or equivalent) that emits loading, success, and error states. Ensure in-flight Supabase or Drift queries are cancelled when a new debounced query arrives.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
Model the stream using a `StreamController
Wrap the async repository call in try/catch — catch PostgrestException, SocketException, and generic Exception separately if different error messages are needed. Expose via a getter `Stream
Keep the service framework-agnostic (pure Dart) so it can be used with either BLoC or Riverpod.
Testing Requirements
Unit tests using flutter_test and fake_async for timing control. Test: (1) loading state emitted immediately on debounce fire, (2) success state with correct contacts on completion, (3) error state on repository exception — verify message is user-friendly, (4) stale result cancellation — emit two rapid queries, verify only the second result appears, (5) empty query emits no loading/search events (relies on SearchDebounceUtility filtering), (6) dispose closes stream cleanly. Use StreamQueue from async package to assert stream emission order. Mock SupabaseSearchRepository and OfflineSearchRepository with Mockito or manual fakes.
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.