Write tests for Bufdir Alignment Validator and document usage
epic-activity-statistics-dashboard-state-and-realtime-task-010 — Write unit tests asserting that BufdirAlignmentValidator returns a passing AlignmentReport when both query paths return identical totals, and a failing report with correct diff fields when they diverge. Add a developer documentation comment block to the validator class explaining how to invoke it from tests and the developer menu, what discrepancies it catches, and why it protects grant funding accuracy. Include at least one test fixture with known divergent data.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
The doc comment should use /// triple-slash Dart doc format and include a `@example` section with a minimal usage snippet. Explain in the doc comment that Bufdir grant funding is calculated from activity aggregates, so a discrepancy between the dashboard view and the export path means the organisation could be over- or under-reporting to the government, which has financial and legal consequences. The fixture should use realistic activity type names from the Norwegian context (e.g., 'Hjemmebesøk' / 'Home Visit', 'Telefon' / 'Phone Call') translated to English for the code. Keep stubs as simple anonymous functions returning pre-defined maps — avoid complex mock setup for a low-complexity test file.
The documentation is as valuable as the tests here — Bufdir alignment is critical to the organisations' grant funding integrity.
Testing Requirements
Use flutter_test. Use manual stubs (no Mockito required given low complexity) for StatsRepository and ExportAggregationRepository — return Future.value(Map
Each test uses a fresh validator instance. Test names must be descriptive sentences: 'returns passing report when all totals match', 'returns failing report with correct delta when totals diverge for one activity type', etc.
Supabase realtime channel subscriptions that are not properly disposed on screen close can accumulate in memory across navigation events, causing duplicate invalidation calls, ghost fetches, and eventual memory leaks on long sessions.
Mitigation & Contingency
Mitigation: Implement StatsCacheInvalidator as a Riverpod provider with an explicit ref.onDispose callback that cancels the realtime channel subscription. Write a widget test that navigates away and back multiple times and asserts that only one subscription is active at any given time.
Contingency: If subscription leaks are found in production, add a global subscription registry that enforces at-most-one subscription per channel key, and schedule a dispose sweep on app background events.
Debouncing rapid inserts may swallow the invalidation signal if the debounce window outlasts the Supabase realtime event delivery window, resulting in the dashboard showing stale totals after a bulk registration completes.
Mitigation & Contingency
Mitigation: Set the debounce window to 800ms (shorter than the typical Supabase realtime delivery latency of 1-2s for batched events) and ensure the leading-edge invalidation fires immediately while trailing duplicates are suppressed. Integration-test with a 20-record bulk insert.
Contingency: If debounce timing proves unreliable, replace debounce with a trailing-edge timer reset on each event and add a guaranteed invalidation 5 seconds after the last event regardless of subsequent events.