high priority low complexity testing pending testing specialist Tier 1

Acceptance Criteria

Test file at test/mappers/bufdir_category_mapper_test.dart runs with flutter test
Successful mapping test: each activity_type_id in the fixture maps to the correct Bufdir category code as defined in activity_type_configuration
All known Bufdir category codes appear in the fixture — no code is untested
Unmapped activity type test: an activity_type_id not present in configuration produces a MappingWarning (not an exception) with the unknown ID in the warning message
Batch mapping test: mapBatch([a, b, c]) returns results in the same order as input — verified with a fixture where IDs are non-sequential
Null safety test: a configuration row where bufdir_code is null results in a null or sentinel mapped value, not a null pointer error
Missing row test: activity_type_id with no configuration row at all is handled identically to a null-code row (documented and tested)
Audit log test: unmapped activity type triggers exactly one audit log entry creation via the injected AuditLogger mock
Batch with mixed mapped/unmapped: mapped items return correct codes, unmapped items return sentinel, audit log called once per unmapped item
Fixture version comment: fixture file documents the Bufdir category schema version it represents (e.g., '// Bufdir schema v2025-Q1')
100% method coverage for BufdirCategoryMapper

Technical Requirements

frameworks
Flutter
flutter_test
mockito or mocktail
data models
ActivityTypeConfiguration
BufdirCategoryCode
MappingWarning
MappingResult
performance requirements
mapBatch with 500 items completes in under 10 ms (in-memory lookup, no async)
security requirements
Audit log entries for unmapped types must not include PII from the activity record — only the activity_type_id

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Implement BufdirCategoryMapper with an in-memory Map lookup table populated from the injected List at construction time — O(1) per lookup. Define a MappingResult sealed class with variants MappedResult(String code) and UnmappedResult(String activityTypeId, MappingWarning warning) so callers can distinguish without null checks. The audit logger should be injected (not a static global) for testability. The fixture must include at least one row per Bufdir code used in the current production schema — coordinate with the data team to get the authoritative list.

Document the schema version in a const String bufdirSchemaVersion = '2025-Q1' in the fixture file so tests self-document when they were last verified against the real schema.

Testing Requirements

Unit tests with flutter_test and mocked AuditLogger. Build fixture as a const list of ActivityTypeConfiguration objects covering all known Bufdir codes. Group tests: successful mappings (one per code), unmapped handling, batch ordering, null safety, audit logging. Use verify(mockAuditLogger.logUnmapped(any)).called(N) to assert exact audit call count.

Assert MappingWarning contains the unknown ID. Assert batch output list length equals input length. Run flutter test --coverage; confirm 100% method coverage.

Component
Bufdir Category Mapper
service medium
Epic Risks (3)
high impact medium prob technical

NHF's three-level hierarchy (national / region / chapter) with 1,400 chapters may have edge cases such as chapters belonging to multiple regions, orphaned nodes, or missing parent links in the database. Incorrect scope expansion would silently under- or over-report activities, which could invalidate a Bufdir submission.

Mitigation & Contingency

Mitigation: Obtain a full hierarchy fixture export from NHF before implementation begins. Write exhaustive unit tests covering boundary cases: single chapter, full national roll-up, chapters with no activities, and chapters assigned to multiple regions. Validate resolver output against a known-good manual count.

Contingency: If hierarchy data quality is too poor for automated resolution at launch, implement a manual scope override in the coordinator UI that allows the coordinator to explicitly select org units from a tree picker, bypassing the resolver.

medium impact high prob dependency

The activity_type_configuration table may not cover all activity types currently in use, leaving a subset unmapped at launch. Bufdir submissions with unmapped categories will be incomplete and may be rejected by Bufdir.

Mitigation & Contingency

Mitigation: Run a query against production activity data before implementation to enumerate all distinct activity type IDs. Cross-reference with Bufdir's published category schema (request from Norse Digital Products). Flag every gap as a known issue and build the warning surface into the preview panel.

Contingency: Implement a fallback 'Other' category bucket for unmapped types and surface a prominent warning in the export preview requiring coordinator acknowledgement before proceeding. Log unmapped types for post-launch cleanup.

high impact low prob security

Supabase RLS policies on generated_reports and the storage bucket must enforce strict org isolation. A misconfigured policy could allow a coordinator from one organisation to read another organisation's export files, creating a serious data breach with GDPR implications.

Mitigation & Contingency

Mitigation: Write RLS integration tests that attempt cross-org reads with explicitly different JWT tokens and assert that all attempts return empty sets or 403 errors. Include RLS policy review in the pull request checklist. Use Supabase's built-in policy tester during development.

Contingency: If a policy gap is discovered post-deployment, immediately revoke all signed URLs for affected exports, audit the access log for unauthorised reads, and issue a coordinated disclosure to affected organisations per GDPR breach notification requirements.