Write unit and integration tests for all repositories
epic-organizational-hierarchy-management-foundation-task-013 — Write comprehensive flutter_test unit tests for OrganizationUnitRepository (CRUD, soft-delete, subtree RPC), UnitAssignmentRepository (uniqueness constraints, primary-assignment enforcement), and HierarchyCache (in-memory ops, local-storage round-trip, invalidation). Mock Supabase client for unit tests. Write integration tests against a local Supabase instance for constraint validation and RPC correctness.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 6 - 158 tasks
Can start after Tier 5 completes
Implementation Notes
Create a SupabaseTestHelper class in test/helpers/ that: (1) initializes Supabase with local test URL, (2) provides seedOrganizationUnit() and seedUnitAssignment() helpers, (3) provides cleanup(). For mocking SupabaseClient in unit tests, use mocktail's when().thenAnswer() to return controlled PostgrestResponse objects. Key pattern for mocking Supabase query chains: create a MockSupabaseQueryBuilder that returns itself on .from(), .select(), .eq(), .is_(), .single(), .execute() calls, then configure the final .execute() to return the desired data. For HierarchyCache local-storage tests, use SharedPreferences.setMockInitialValues().
Run integration tests only when the INTEGRATION_TESTS environment variable is set to avoid slowing down every CI run. Document the local Supabase setup steps in a test/README.md.
Testing Requirements
This task IS the testing task. Structure: (1) Unit tests in test/repositories/organization_unit_repository_test.dart, unit_assignment_repository_test.dart, hierarchy_cache_test.dart. (2) Integration tests in integration_test/repositories/. Use Mockito (or mocktail) to generate mock SupabaseClient.
For integration tests, use supabase_flutter with a local Supabase URL pointing to Docker. Seed test data in setUp() and clean up in tearDown() using the service role key. Use group() to organize tests by method. Use expect(find.text(...)) for error message assertions where applicable.
Test edge cases: empty organization (no units), single root unit, deeply nested hierarchy (5+ levels), softDelete of a parent (children should be unaffected in DB, absent from active tree view).
Recursive CTE queries for large hierarchies (1,400+ nodes) may exceed Supabase query timeouts or produce unacceptably slow responses, degrading tree load time beyond the 1-second target.
Mitigation & Contingency
Mitigation: Implement Supabase RPC functions for subtree fetches rather than client-side recursive calls. Use materialized path or closure table as a supplemental index for depth-first traversal. Benchmark with realistic NHF data volumes during development.
Contingency: Fall back to a pre-computed flat unit list stored in the hierarchy cache with client-side tree reconstruction, trading freshness for speed. Add a background refresh job to keep the cache warm.
Concurrent writes from multiple admin sessions could cause cache staleness, leading to stale tree views and incorrect ancestor path computations that corrupt aggregation results.
Mitigation & Contingency
Mitigation: Use optimistic versioning on cache entries with a short TTL (5 minutes) as a safety net. Subscribe to Supabase Realtime on the organization_units table to push invalidation events to all connected clients.
Contingency: Provide a manual 'Refresh Hierarchy' action in the admin portal that forces a full cache bust, and display a staleness warning banner when the cache age exceeds the TTL.
Persisting the flat unit list to local storage may expose organization structure data if the device is compromised or the storage is not properly encrypted, violating data protection requirements.
Mitigation & Contingency
Mitigation: Use flutter_secure_storage (AES-256 backed by Keychain/Keystore) for the local unit list cache rather than SharedPreferences. Include only unit IDs, names, and types — no member PII.
Contingency: Disable local-storage persistence entirely and rely on in-memory cache only. Accept the trade-off of no offline hierarchy access for the security guarantee.