Integration tests for service and provider layer
epic-contact-list-management-business-logic-task-010 — Write integration tests covering all three role filtering paths in ContactListService, both local and remote search paths in ContactSearchService with mock Supabase client, and the auto-invalidation behaviour of ContactListRiverpodProvider using ProviderContainer. Include tests for edge cases: empty contact list, threshold boundary search path switching, and role context change triggering a provider rebuild.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 7 - 84 tasks
Can start after Tier 6 completes
Implementation Notes
For mocking the Supabase query builder chain (which is fluent/chainable), create a stub that records which filter methods were called and in what order, then assert the call sequence in tests rather than mocking return values for every chain step. This makes assertions more readable and less brittle. For ProviderContainer tests, use container.read(provider) to trigger the initial build, then use container.updateOverrides() or override the dependency provider to simulate a role/org change. Verify that the old stream subscription was cancelled by checking that the mock client's subscribe() was called exactly once per context, and unsubscribe() was called before the new subscribe().
Document each test with a one-line comment explaining what scenario it covers.
Testing Requirements
All tests use flutter_test with ProviderContainer for provider-layer tests and direct service instantiation for service-layer tests. Create a MockSupabaseClient using Mockito or manual stub implementing the Supabase query builder interface. Group tests by class under descriptive group() blocks: 'ContactListService', 'ContactSearchService', 'ContactListRiverpodProvider'. Use setUp()/tearDown() to reset provider containers between tests.
Assert both the emitted AsyncValue sequence (loading, then data/error) and the actual content of the data payload. Aim for 100% branch coverage of the three role filter paths and both search paths.
For organizations with large contact lists (NHF has 1,400 local chapters and potentially thousands of contacts), local in-memory filtering may be too slow and Supabase ILIKE queries without supporting indexes may exceed acceptable response times or accumulate excessive read costs, degrading search usability for power users.
Mitigation & Contingency
Mitigation: Define and document the list-size threshold in ContactSearchService before implementation. Confirm that indexes on name and notes columns exist in the Supabase schema before enabling server-side search. Profile ContactSearchService against realistic data volumes in the staging environment using the largest expected org.
Contingency: If response times are unacceptable in staging, introduce result-count pagination in ContactListService and add a user-visible 'showing top N results — refine your search' indicator, deferring full pagination to a follow-up task.
In NHF's multi-chapter context, when a user switches organization, Riverpod providers may emit a brief window of stale contact data scoped to the previous organization before the invalidation cycle completes, transiently exposing contacts from the wrong chapter.
Mitigation & Contingency
Mitigation: Model organization context as a Riverpod provider dependency so that any context change immediately marks contact providers as stale. Render a loading skeleton instead of the stale list during the re-fetch transition. Cover this scenario in integration tests with explicit org-switch sequences.
Contingency: If race conditions are observed during QA, add an explicit organization_id equality check in ContactListService that compares each fetched record's scope to the active session org, discarding any mismatched batch before returning results to the provider.