critical priority high complexity testing pending testing specialist Tier 3

Acceptance Criteria

Test suite achieves minimum 95% line coverage of ParticipantDeduplicationService (all three tasks: 005, 006, 007)
Proxy deduplication tests: coordinator registers for peer mentor → count = 1; two coordinators independently register same participant → count = 1
5-chapter member test: member with max 5 chapter affiliations contributing 5 separate activity records → count = 1 per Bufdir category
Cross-category test: same participant active in 'youth' and 'disability-sport' categories → count = 1 per category (total contribution = 2 across categories)
Baseline (zero-dedup) test: 20 distinct participants, no proxy or multi-chapter overlap → count = 20, audit trail shows 0 removals
Audit trail verification: every removed duplicate has non-null record_id, participant_id, reason code, and timestamp
Grant fraud regression test 1: coordinator inflates count by registering same participant under different misspelled name variations → normalization catches it, count = 1
Grant fraud regression test 2: same participant registered across two orgs → each org's count is independent (1 per org, no cross-org leakage)
Grant fraud regression test 3: 5-chapter member with activity in all 5 chapters reported as 5 separate participants → dedup reduces to 1
Multi-org isolation test: org A's deduplication result is unaffected by org B's data in the same test database
Performance regression test: 50,000 records processed in under 2 seconds (verified in CI)
All tests pass on both debug and release Flutter build modes

Technical Requirements

frameworks
flutter_test
Flutter
Dart
apis
Supabase test instance (dedicated test project, not production)
data models
ActivityRecord
Participant
ChapterAffiliation
ProxyRegistration
UniqueParticipantCountResult
DuplicateAuditEntry
performance requirements
Full test suite completes in under 60 seconds
Performance regression test explicitly asserts 50,000-record run under 2 seconds using Stopwatch
security requirements
Test fixtures use synthetic participant IDs only — no real NHF member data
Multi-org isolation tests explicitly verify RLS prevents cross-org data reads

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

The grant fraud regression tests are the most critical deliverable here — they directly protect grant compliance. Treat them as specification tests: each test name should read as a plain-English statement of a guarantee the system provides (e.g., 'member registered under 5 chapter affiliations counts as 1 not 5'). Use a test fixture builder pattern: create helper functions like `buildProxyScenario()`, `buildMultiChapterScenario(int chapters)`, `buildMultiOrgScenario()` that return pre-configured lists of ActivityRecord objects — this makes tests readable and reusable. For the multi-org isolation test, insert data for org A and org B in the same Supabase test instance and verify that querying org A's count does not include org B's participants — this validates that Supabase RLS policies are correctly configured, not just that the Dart code is correct.

Add a CI check that fails the build if coverage drops below 95% on this service file.

Testing Requirements

All tests use flutter_test. Unit tests mock Supabase client to return fixture data (use mockito or manual fakes — confirm team standard). Integration tests hit the Supabase test project with setUp/tearDown that inserts and removes fixture rows. Organize tests into groups: 'proxy deduplication', 'multi-chapter deduplication', 'getUniqueParticipantCount', 'audit trail', 'grant fraud regressions', 'multi-org isolation', 'performance'.

Parameterize the grant fraud tests using a list of input variants (misspellings, case variations, whitespace) to keep the test file concise. Aim for 95%+ line coverage measured by `flutter test --coverage` with lcov output for CI.

Component
Participant Deduplication Service
service high
Epic Risks (4)
high impact high prob integration

NHF members can belong to up to 5 local chapters. When a participant has activities registered under different chapter IDs within the same reporting period, deduplication requires a reliable cross-chapter identity key. If national IDs are absent for some members (a known data quality issue in NHF's systems), the deduplication service may fail to identify duplicates, resulting in inflated counts submitted to Bufdir.

Mitigation & Contingency

Mitigation: Implement a multi-attribute identity matching strategy: primary match on national_id, fallback to (full_name + birth_year + municipality) composite key. Expose a low-confidence match list in DeduplicationAnomalyReport that coordinators can review and manually resolve before submission.

Contingency: If identity data quality is too poor for reliable automated deduplication for specific organisations, add an organisation-level config flag that disables cross-chapter deduplication for that org and requires coordinators to manually review the anomaly report before submitting.

high impact medium prob integration

The geographic distribution algorithm must resolve NHF's 1,400 local chapter hierarchy to regional aggregates. If the organizational unit hierarchy in the database is incomplete (missing parent-child relationships for some chapters), the geographic service will silently drop activities from unmapped chapters, producing an understated geographic breakdown.

Mitigation & Contingency

Mitigation: Add a hierarchy completeness validation step in GeographicDistributionService that counts activities without a resolvable region assignment and surfaces them as an 'unmapped_activities' field in the distribution result. Block export if unmapped_activities > 0.

Contingency: Provide a 'national' fallback bucket for activities from chapters with no region assignment, clearly labelled in the preview screen so coordinators are alerted to fix the org hierarchy data before re-running aggregation.

high impact low prob technical

BufdirAggregationService orchestrates four dependent services. If one service (e.g., GeographicDistributionService) throws mid-pipeline, the partially assembled metrics payload may be silently cached or returned as if complete, resulting in a Bufdir submission missing the geographic breakdown section.

Mitigation & Contingency

Mitigation: Implement the orchestrator as a transactional pipeline using Dart's Result type pattern: each stage returns Either<AggregationError, PartialResult>, and the orchestrator only proceeds if all stages succeed. The final payload is only assembled and persisted when all stages return success.

Contingency: If a partial failure state reaches the UI, the AggregationProgressIndicator must display a specific stage failure message with a retry option that re-runs only the failed stage rather than the full pipeline.

medium impact medium prob scope

Internal activity types that have no corresponding Bufdir category in the mapping configuration will cause the aggregation to silently exclude those activities from the final counts. Coordinators may not notice the omission until Bufdir queries why submission totals are lower than expected.

Mitigation & Contingency

Mitigation: BufdirAggregationService must produce an unmapped_activity_types list as part of its output. If any internal activity types are unmapped, display a blocking warning in the AggregationSummaryWidget listing the unmapped types before allowing the coordinator to proceed to export.

Contingency: Allow coordinators to temporarily assign unmapped activity types to a Bufdir 'other' catch-all category as an emergency workaround, with an audit flag indicating manual override was applied for that submission.