Unit and integration tests for data layer components
epic-multi-chapter-membership-handling-data-layer-task-010 — Write unit tests for ContactChapterRepository (mock Supabase adapter) covering: add affiliation success, add affiliation rejects on 5th+1 attempt, remove affiliation, fetch chapters. Write unit tests for CrossChapterActivityQuery verifying correct JOIN result mapping and that no N+1 queries are issued. Write unit tests for DuplicateWarningEventLogger verifying correct payload construction and that insert failures do not throw. Include at least one integration test per component against a local Supabase test instance.
Acceptance Criteria
Technical Requirements
Execution Context
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.
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.
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.
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.