high priority medium complexity testing pending testing specialist Tier 3

Acceptance Criteria

Test suite achieves ≥ 90% branch coverage on OrgHierarchyService as reported by dart test --coverage; coverage report is committed alongside the test file
Tree construction tests: verify that a flat list of OrgNode rows with parent_id references is correctly assembled into a nested OrgHierarchyTree with parent→children relationships intact; orphan nodes (parent_id pointing to non-existent node) are handled gracefully (logged and excluded)
Subtree ID resolution tests: getSubtreeIds(nodeId) returns correct IDs for (a) a leaf node (only itself), (b) a mid-level node (itself + all descendants), (c) the root node (all nodes); tested on both nested-model and flat-list model variants
Cache hit test: calling getTree() twice returns the same object reference on the second call (no service call made); assert repository mock is called exactly once
Cache miss test: calling getTree() after cache TTL expiry (or after manual invalidation) re-fetches from the repository; assert mock called twice total
Search ranking test: searchNodes('oslo') returns results ordered by match quality — exact name match ranks above partial match; assert result order for a controlled dataset
Cache invalidation on mutation test: calling any mutating method (e.g., renameNode, addNode) clears the cached tree; subsequent getTree() call re-fetches from repository
Edge case: getTree() on an empty dataset returns an empty tree without throwing
Edge case: searchNodes with an empty string returns the full unfiltered tree (or empty list, per documented API contract — test documents the expected behaviour)

Technical Requirements

frameworks
flutter_test
mocktail (or mockito) for repository mocking
apis
OrgHierarchyService public API
OrgHierarchyRepository (mocked)
data models
OrgNode
OrgHierarchyTree
OrgSearchResult
performance requirements
All unit tests must complete within 5 s total; no test may perform real I/O or network calls
security requirements
Tests must verify that getSubtreeIds never returns node IDs outside the root node's descendant set, confirming no scope-escape bug

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Focus exclusively on OrgHierarchyService; do not test the repository implementation or the Flutter widget in this task. Use fake/stub OrgNode data defined as constants at the top of the test file so all test groups share the same readable dataset. For cache timing tests, inject a Clock or DateTime provider into OrgHierarchyService (if not already done) so tests can advance time without real sleeps — if the service uses DateTime.now() directly, coordinate with the implementation task to add clock injection before writing tests. For search ranking, define a scoring algorithm expectation in the test first (e.g., exact match = 2 points, prefix match = 1 point) so the test documents the contract rather than testing an opaque implementation detail.

Use verify(mockRepo.fetchAll()).called(1) assertions from mocktail to confirm cache behaviour without depending on internal state.

Testing Requirements

Pure Dart unit tests using flutter_test (no widget rendering required). Use mocktail to create a MockOrgHierarchyRepository that returns controlled datasets. Each test group should use setUp to create a fresh service instance and a fresh mock to prevent state leakage between tests. Organise tests in groups: 'tree construction', 'subtree resolution', 'cache behaviour', 'search ranking', 'cache invalidation', 'edge cases'.

Run with dart test --coverage and fail the CI pipeline if coverage on the OrgHierarchyService class drops below 90%.

Component
Organisation Hierarchy Service
service high
Epic Risks (4)
medium impact high prob technical

OrgHierarchyNavigator rendering NHF's full 1,400-chapter tree in a single widget may cause Flutter frame-rate drops below 60 fps on mid-range devices, making the navigator unusable for NHF national admins.

Mitigation & Contingency

Mitigation: Implement lazy expansion: only load immediate children on node expand rather than the full tree upfront. Use virtual scrolling for long sibling lists. Test with a synthetic 1,400-node dataset on a low-end Android device during development.

Contingency: If lazy expansion is insufficient, replace the tree widget with a paginated drill-down navigator (select level → select child) that avoids rendering more than 50 nodes at a time.

medium impact medium prob dependency

Bufdir may update their required export column structure or file format during or after development. If the AdminExportService hardcodes the current Bufdir schema, any format change requires a code release rather than a config update.

Mitigation & Contingency

Mitigation: Drive the Bufdir column mapping from a configuration repository rather than hardcoded constants. Abstract column definitions into a named schema config so that format changes require only a config update and re-deployment without service logic changes.

Contingency: If Bufdir format changes post-launch, release a config update within one sprint. If the change is structural (new required sections), scope a targeted service update and communicate timeline to partner organisations.

high impact medium prob integration

Role transition side-effects in UserManagementService (e.g., certification expiry removing mentor from chapter listing, pause triggering coordinator notification) may interact with external services like HLF's website sync. Incomplete side-effect handling could leave the system in an inconsistent state.

Mitigation & Contingency

Mitigation: Model side-effects as explicit domain events published after the primary state change is persisted. Implement event handlers as idempotent operations so re-processing is safe. Write integration tests that assert all side-effects fire correctly for each role transition type.

Contingency: If a side-effect fails after the primary change is persisted, log the failure with full context and trigger a manual reconciliation alert to the on-call team. Provide an admin-accessible re-trigger action for failed side-effects.

medium impact medium prob scope

If AdminStatisticsService cache TTL is set too long, org_admin may see significantly stale KPI values (e.g., a mentor newly paused an hour ago still appears as active), undermining trust in the dashboard.

Mitigation & Contingency

Mitigation: Default cache TTL to 5 minutes with a manual refresh action on the dashboard. Implement cache invalidation triggered by UserManagementService write operations that affect counted entities.

Contingency: If staleness causes org admin complaints post-launch, reduce TTL to 60 seconds and introduce a real-time Supabase subscription for high-impact counters (paused mentors, expiring certifications).