Integration test: online-to-offline search fallback flow
epic-contact-search-data-layer-task-012 — Write an integration test that exercises the full data layer flow: populate cache via ContactCacheSyncRepository, simulate offline state (mock Supabase throwing NetworkException), invoke search via OfflineSearchRepository, and assert results are returned from the local cache. Verify that the ContactSearchResult model is identical regardless of which repository source is used.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 6 - 158 tasks
Can start after Tier 5 completes
Implementation Notes
The key challenge is reconfiguring the Supabase mock mid-test to transition from online to offline. Use a configurable fake: a FakeSupabaseClient class with a shouldThrowNetwork flag that can be toggled between test phases. For connectivity detection, inject a StreamController
The test should explicitly assert the routing decision: after the offline event is pushed, verify that OfflineSearchRepository.search was called (via a spy) and SupabaseSearchRepository.search was NOT called. This validates that the routing layer responded correctly to the connectivity change.
Testing Requirements
Integration test targeting the data layer only — no widget tree, no Flutter engine required unless testing through a widget. Use standard flutter test runner (not integration_test package) if the test does not require a device. Seed 10 contact records with distinct first_name, last_name, organization_id. Assert results using deep equality on ContactSearchResult.
Include a negative case: assert that when Supabase is available (online), the same query routes to the Supabase repository (not the cache). Include a boundary case: empty cache + offline = empty list, not error. Run as part of CI with flutter test --coverage.
Supabase RLS policies may not correctly scope ilike search results to the authenticated user's organisation and chapter, causing data leakage across organisations or empty result sets for valid queries.
Mitigation & Contingency
Mitigation: Reuse and extend existing RLS query builder patterns from the contact-list-management feature. Write integration tests against a seeded multi-organisation test database to verify cross-org isolation before merging.
Contingency: If RLS scoping is insufficient, add an explicit organisation_id filter in the Dart query builder layer as a defence-in-depth measure while the Supabase policy is corrected.
Adding new Drift tables for the contact cache may conflict with existing migrations or schema versions in the contact-list-management feature if both features cache the same contacts table, causing migration failures on user devices.
Mitigation & Contingency
Mitigation: Audit existing Drift schema versions from contact-list-management before writing new migrations. Reuse existing cache tables if the schema already covers required fields; only add missing fields via ALTER or new version.
Contingency: If schema conflict occurs, consolidate into a single shared cache table owned by contact-list-management and expose a DAO interface to the search feature, avoiding duplicated schema ownership.
The offline cache may surface significantly stale contact data if sync has not run recently, leading coordinators to act on outdated information (wrong phone numbers, changed assignments).
Mitigation & Contingency
Mitigation: Store and surface the last-sync timestamp prominently in the UI layer. Trigger a background cache refresh on app foreground when connectivity is detected.
Contingency: If staleness becomes a reported UX issue, implement a maximum-age threshold that shows a warning banner when the cache is older than a configurable limit (e.g. 24 hours).