high priority medium complexity testing pending testing specialist Tier 5

Acceptance Criteria

ContactChapterRepository unit tests cover: successful add affiliation (returns created ContactChapter), add affiliation rejected when contact already has 5 chapter memberships (throws domain exception, not raw Supabase exception), remove affiliation (returns void on success), fetch all chapters for a contact ID (returns correctly mapped list), fetch all contacts for a chapter ID (returns correctly mapped list)
All ContactChapterRepository unit tests use a mock/fake adapter — no real Supabase connection is required for any unit test
CrossChapterActivityQuery unit tests verify that the query method issues exactly one database call (not N+1 per contact), and that the returned list of activities is correctly mapped from the raw JOIN response shape
DuplicateWarningEventLogger unit tests verify that the log payload includes all required fields (contact_id, chapter_id, detected_at, triggered_by_user_id), and that if the underlying insert throws, the logger catches the error and returns without rethrowing
At least one integration test exists for each of the three components, executed against a local Supabase test instance with real data seeded via test fixtures
Integration tests clean up all seeded rows after each test (using `tearDown` or equivalent)
Test file names follow the project naming convention (e.g., `contact_chapter_repository_test.dart`)
All tests pass with `flutter test` with no skipped assertions
Overall line coverage for the three data layer components is ≥ 85% as reported by `flutter test --coverage`

Technical Requirements

frameworks
Flutter
flutter_test
Riverpod
apis
Supabase PostgREST (integration tests only)
Supabase Auth (integration test seeding)
data models
contact_chapter
contact
activity
assignment
performance requirements
Unit tests must complete the full suite in under 10 seconds
Integration tests against local Supabase must complete in under 60 seconds total
security requirements
Integration test Supabase credentials must be read from environment variables or a `.env.test` file excluded from version control — never hardcoded
Test data must not include real personally identifiable information — use generated fake UUIDs and placeholder names

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

For the N+1 test on CrossChapterActivityQuery, inject a spy/mock adapter that records the number of times its query method is called, then assert `callCount == 1` after invoking the query with a dataset containing multiple contacts. For the DuplicateWarningEventLogger failure test, configure the mock to throw a `PostgrestException` and assert that the logger's method returns normally (no exception bubbles). For integration tests, use Supabase's `service_role` key only in the test environment to seed and tear down data; client-role key for the actual test assertions to verify RLS behaves correctly. Avoid `expect(result, isNotNull)` alone — always assert on the structure and values of returned objects to prevent false-positive green tests.

Testing Requirements

This task IS the testing task. The test suite itself is the deliverable. Use `flutter_test` as the test framework. Use `mockito` or `mocktail` (whichever is already in use in the project) to generate mock adapters.

Structure test files in `test/data_layer/` mirroring the `lib/` directory structure. Each `group()` block should correspond to one class under test. Use `setUp` to instantiate the class under test with mock dependencies injected. For integration tests, use a dedicated `test/integration/` folder and guard them with a `--dart-define=RUN_INTEGRATION_TESTS=true` flag so CI can separate fast unit runs from slower integration runs.

Component
Cross-Chapter Activity Query
data high
Epic Risks (3)
high impact medium prob technical

The Cross-Chapter Activity Query must avoid N+1 fetches across chapters. If naively implemented as a per-chapter loop, it will cause severe performance degradation for contacts affiliated with 5 chapters on poor mobile connections.

Mitigation & Contingency

Mitigation: Design the query as a single PostgREST join of contact_chapters and activities on contact_id from the start. Add a query performance test with 5 affiliations and 100+ activities to the integration test suite and enforce a maximum execution time threshold.

Contingency: If a performance regression is detected post-merge, introduce a Supabase RPC function (stored procedure) to move the join server-side, bypassing any client-side N+1 pattern.

high impact low prob security

If the Duplicate Warning Event Logger write fails silently (network error, RLS denial), audit entries will be missing from the Bufdir compliance record without the user being aware.

Mitigation & Contingency

Mitigation: Implement the logger with a local fallback queue: if the Supabase write fails, persist the event locally and retry on next launch. Log all failures to a verbose output channel.

Contingency: Add a reconciliation job that compares locally queued events to Supabase entries and re-submits any gaps. Provide a data export of the local queue for manual audit if reconciliation fails.

medium impact low prob technical

Two coordinators simultaneously adding the 5th chapter affiliation for the same contact could bypass the maximum enforcement check if both reads occur before either write completes.

Mitigation & Contingency

Mitigation: Enforce the 5-affiliation maximum as a database-level constraint (CHECK + trigger or RPC with a FOR UPDATE lock) rather than relying solely on application-layer validation.

Contingency: If a constraint violation is detected in production, run a corrective query to end the most recently created excess affiliation and notify the relevant coordinator.