high priority medium complexity testing pending testing specialist Tier 4

Acceptance Criteria

Unit tests cover query sanitisation: leading/trailing whitespace stripped, special characters escaped or removed before being passed to repositories
Unit tests cover deduplication: given overlapping results from Supabase and Drift, verify only unique IDs in final list and Supabase takes precedence
Unit tests cover result merging: given disjoint sets, verify all results appear in the merged list up to maxResults
Integration test: given online state, when query fires, then only SupabaseSearchRepository.search() is called (Drift not called)
Integration test: given offline state, when query fires, then only OfflineSearchRepository.search() is called (Supabase not called)
Integration test: stream emits SearchLoading before results arrive, then SearchSuccess with correct contacts
Integration test: when SupabaseSearchRepository throws, stream emits SearchError with a non-empty message
Integration test: when connectivity changes from offline to online mid-session, next query uses Supabase
All repository interactions use mocks — no real Supabase or SQLite calls in any test
Test file is structured with clear group() hierarchy: 'unit — sanitisation', 'unit — deduplication', 'integration — routing', 'integration — stream states'

Technical Requirements

frameworks
Flutter
Dart
flutter_test
BLoC
Riverpod
apis
Supabase REST (mocked)
data models
Contact
SearchResult
SearchLoading
SearchSuccess
SearchError
performance requirements
All tests must complete using fake_async — no real network calls or real timers
Full test suite for this file must run in under 5 seconds
security requirements
Mock data used in tests must not contain real personal data — use generated/fictional names and IDs only

Execution Context

Execution Tier
Tier 4

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 response = []; ... }`. For stream ordering tests, use `expectLater(service.results, emitsInOrder([isA(), isA()]))` from the async_helper package or StreamQueue.

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.

Component
Contact Search Service
service low
Epic Risks (2)
medium impact medium prob technical

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.

medium impact low prob technical

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.