high priority medium complexity testing pending testing specialist Tier 3

Acceptance Criteria

Test suite runs green with `flutter test` and all assertions pass without flakiness across 3 consecutive runs
Happy-path test: parallel fetch of profile, history, and assignment data emits ContactDetailLoading then ContactDetailLoaded in correct order, with all three data fields populated and non-null
Network failure test: simulated Supabase exception causes stream to emit a typed ContactDetailError state containing a user-readable message and the originating error type
Retry test: first fetch throws a network exception, second fetch (triggered via retryFetch()) succeeds, stream emits ContactDetailLoaded with correct data on the second attempt
Encrypted field key test for Organisation A emits the exact list of expected encrypted field keys defined in the org configuration, with no extras or missing keys
Encrypted field key test for Organisation B (different config) emits a distinct correct key list, confirming per-org configuration isolation
All parallel fetch operations are verified to have been initiated concurrently (e.g., using Future.wait semantics) and not sequentially — confirmed via timing assertions or mock call-order inspection
Test doubles (fakes or mocks) cover all Supabase data source dependencies; no real network calls are made during test execution
Tests are isolated: no shared mutable state between test cases; each test creates its own service instance
Code coverage for ContactDetailService reaches at least 90% line coverage as reported by `flutter test --coverage`

Technical Requirements

frameworks
Flutter
flutter_test
BLoC
apis
Supabase REST API (mocked via fake implementations)
data models
ContactProfile
ActivityHistory
ContactAssignment
ContactDetailState
OrganisationConfig
performance requirements
Each individual test case completes within 2 seconds
Full test file runs within 30 seconds
Parallel fetch verified to complete faster than sequential equivalent (timing delta > 20%)
security requirements
No real Supabase credentials or tokens used in tests; all auth is mocked
Encrypted field key lists must not be hard-coded in test assertions from production secrets — use fixture files or constants
Test output must not log sensitive field values even in verbose mode

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Use the `fake_async` package to control time when testing retry delays — avoid real `await Future.delayed` in tests as it causes slowness and flakiness. Structure fakes as in-memory implementations of the data source interfaces rather than using a mocking library like Mockito, which requires code generation and adds build overhead. For verifying parallel execution, inject a Completer-based fake that records invocation timestamps; assert the delta between first and last call start is less than the sum of individual durations. For encrypted field key tests, define org configurations in fixture maps at the top of the test file — this makes the expected key lists easy to update when configs change.

Ensure the service under test is constructed fresh in a `setUp()` callback to guarantee isolation between tests. If ContactDetailService exposes a stream (typical in BLoC-style services), use `expectLater(stream, emitsInOrder([...]))` for ordered assertions.

Testing Requirements

Integration tests only (no unit tests for individual methods in this task). Use flutter_test with fake/stub Supabase data sources rather than live connections. Structure tests in three groups: (1) happy-path parallel fetch, (2) error and retry scenarios, (3) encrypted field key configuration per org. Use StreamController or StreamQueue from async package to assert emitted states in order.

Verify call counts on mocks to confirm parallel invocation. Aim for ≥90% line coverage on ContactDetailService. All tests must be deterministic — use FakeAsync if timers or delays are involved in retry logic.

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

Parallel fetching of profile, activity history, and assignment status from contact-detail-service may produce race conditions where partial state is emitted to the UI before all fetches complete, resulting in flickering or incorrect loading indicators.

Mitigation & Contingency

Mitigation: Use Future.wait or a single composed BLoC event that only emits a loaded state once all three futures resolve. Define a strict state machine: initial → loading → loaded/error with no intermediate partial-loaded states emitted to the UI.

Contingency: If parallelism proves unreliable in testing, fall back to sequential fetching with a combined loading indicator. The 500ms target may need to be renegotiated with stakeholders if sequential fetching exceeds it on slow connections.

high impact low prob integration

The partial-field update pattern in contact-edit-service assumes the contact record has not changed between when the edit screen was loaded and when the save is submitted. Concurrent edits by another coordinator could cause the earlier editor's save to silently overwrite the later one.

Mitigation & Contingency

Mitigation: Include an updated_at timestamp in the PATCH request and configure Supabase to reject updates where the server-side timestamp differs from the client's version. Return a 409-equivalent error that the service maps to a user-readable conflict message.

Contingency: If optimistic locking is too complex for initial delivery, implement a simple 'reload and retry' flow: on save error, reload the contact detail and prompt the coordinator to re-apply their changes manually.