Write unit and integration tests for ContactSearchService
epic-contact-search-service-infrastructure-task-008 — Write unit tests for query sanitisation, deduplication, and result merging logic. Write integration tests simulating online and offline routing, verifying that the correct repository is called based on connectivity state, and that the reactive stream emits correct loading/success/error states. Mock SupabaseSearchRepository and OfflineSearchRepository.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 4 - 323 tasks
Can start after Tier 3 completes
Implementation Notes
Create manual stub classes (not Mockito-generated) for repositories if the repo interfaces are simple — this avoids build_runner dependency for tests. Structure stubs to record call counts and arguments: `class FakeSupabaseSearchRepository implements SupabaseSearchRepository { int callCount = 0; List
Test the connectivity routing by injecting a `FakeConnectivityService` with a settable `isOnline` bool. Ensure tearDown() calls `service.dispose()` to close streams and prevent test pollution. Document each test case intent with a clear `// Arrange / Act / Assert` comment structure.
Testing Requirements
Unit tests (pure Dart logic, no Flutter widget tree) and integration tests (ContactSearchService wired with mocked repositories). Use Mockito or manual stub classes for SupabaseSearchRepository and OfflineSearchRepository. For stream state testing, use StreamQueue to assert ordered emissions: [SearchLoading, SearchSuccess(contacts)]. For error path, verify SearchError is emitted and stream does not close (service remains usable after error).
For connectivity routing, inject a mock ConnectivityService that returns configurable online/offline state. Test file: `test/services/contact_search_service_test.dart`. Aim for >90% line coverage on ContactSearchService.
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.