Write unit tests for activity query deduplication
epic-bufdir-reporting-export-processing-services-task-012 — Write comprehensive unit tests for the BufdirActivityQueryService covering multi-chapter deduplication scenarios including: peer mentor registered in two chapters with same activity, same activity submitted by both coordinator and peer mentor, boundary cases where activities have identical timestamps, and correct handling of activities that legitimately span multiple chapters.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Use `mocktail` for mocking the Supabase client since it does not require code generation unlike `mockito`. Create a `_buildActivity({required String id, required String peerId, required String typeId, required DateTime date, String? coordinatorSubmittedBy})` helper factory at the top of the test file to reduce boilerplate. For the multi-chapter scenario, construct `contact_chapter` mock records linking one `contact_id` (peer mentor) to two `organisation_unit_id` values, then assert the query service de-duplicates correctly.
For the coordinator-vs-peer-mentor scenario, the mock data should include two activity records with the same `peer_mentor_id`, `activity_type_id`, and `date` but different `submitter_role` values — assert the service returns the peer mentor's record as canonical. Document each test group with a `// Business rule:` comment explaining which requirement from the NHF workshop (duplicate warning detection, multi-chapter membership) it validates. Use `setUp` and `tearDown` properly to reset mock state between tests. Avoid `expect(...
, equals(... ))` on entire lists — use `expect(result.length, equals(1))` and then assert specific fields to produce clearer failure messages.
Testing Requirements
This task IS the testing task — the output is the test file itself. The test file must be located at `test/orchestration/bufdir_activity_query_service_test.dart` (or equivalent path matching the source structure). Required test groups: (1) `group('deduplication — same peer mentor, two chapters')` with at least 3 tests; (2) `group('deduplication — coordinator vs peer mentor submission')` with at least 2 tests; (3) `group('boundary — timestamp collisions')` with at least 3 tests; (4) `group('multi-chapter events')` with at least 2 tests; (5) `group('empty and edge inputs')` covering null period, zero activities, and single activity (no deduplication needed). Run `flutter test --coverage` and assert the deduplication method reaches 90%+ branch coverage.
The test file itself must pass `dart analyze` with zero warnings.
NHF contacts can belong to up to five local chapters simultaneously. If the deduplication logic in the activity query service incorrectly attributes cross-chapter activities, organisations will either under-report or over-report to Bufdir, which could trigger grant clawback or compliance investigations.
Mitigation & Contingency
Mitigation: Implement deduplication using the existing multi-chapter membership service as the source of truth for chapter affiliation. Write test fixtures covering all known multi-chapter edge cases and validate outputs against manually prepared reference exports from NHF.
Contingency: If deduplication cannot be made deterministic for complex hierarchies before release, gate the export behind an org-level feature flag and require NHF to validate a preview export against their manual Excel before enabling in production.
Server-side Dart libraries for Excel generation are less mature than equivalents in Node.js or Python. The chosen library may lack support for Bufdir-required formatting features (merged cells, data validation, specific date formats), requiring significant workaround effort or a library switch mid-implementation.
Mitigation & Contingency
Mitigation: Evaluate the top two Dart xlsx libraries (excel, spreadsheet_decoder) against a Bufdir template sample file before committing. Identify all required formatting features and verify library support in a spike.
Contingency: If no Dart library meets requirements, implement the Excel generation as a Supabase Edge Function in TypeScript using the well-supported ExcelJS library, exposing it to the Dart backend via an internal RPC call.
The attachment bundler must retrieve documents from Supabase Storage that were uploaded by the document attachments feature. If storage paths, RLS policies, or signed URL expiry have not been standardised across features, the bundler may fail to retrieve attachments at export time.
Mitigation & Contingency
Mitigation: Audit the document attachments feature's storage schema and RLS policies before implementing the bundler. Agree on a stable internal service-account access pattern for cross-feature storage reads.
Contingency: If cross-feature storage access cannot be made reliable, implement the bundler to include only attachments that can be retrieved successfully and produce a manifest listing any attachments that could not be bundled, rather than failing the entire export.