high priority medium complexity testing pending testing specialist Tier 6

Acceptance Criteria

Test suite runs with flutter test and all tests pass with zero flaky outcomes across 10 consecutive runs
assignment-repository tests cover: fetch assignments for a given contact ID, RLS rejection (mock returns 403/empty), and assignment not found returning null or empty list
contact-detail-repository tests cover: successful fetch, RLS rejection (mock Supabase returns permission denied), contact not found, and malformed JSON from Supabase returning a typed error
contact-form-validator tests cover: valid email accepted, invalid email formats rejected (missing @, missing TLD, whitespace), valid Norwegian phone (+47 and 8-digit), invalid phone rejected, NHF 5-chapter maximum enforced (4 chapters passes, 5 chapters passes, 6 chapters fails with correct error code), empty required fields rejected
field-encryption-utils tests cover: successful AES-GCM round-trip, HMAC integrity violation returns DecryptionError, wrong key returns DecryptionError, decryptOrMask() returns MaskedFieldResult for all error codes, kMaskedFieldPlaceholder is never length-matched to input
Code coverage report generated via flutter test --coverage shows ≥90% line coverage on all four components
No test uses real Supabase network calls — all repository tests use a mock or fake Supabase client
Test file naming follows the pattern {component}_test.dart in the test/ mirror directory structure
All tests include descriptive test names following 'given X when Y then Z' convention

Technical Requirements

frameworks
flutter_test
mocktail or mockito for mock generation
Flutter test coverage via lcov
apis
Supabase mock client interface
field-encryption-utils API (task-008, task-009)
contact-form-validator API (task-006)
assignment-repository API (task-002)
contact-detail-repository API (task-004)
data models
Assignment
ContactDetail
ContactFormValidationResult
EncryptedFieldBlob
FieldDisplayResult (DisplayFieldResult | MaskedFieldResult)
performance requirements
Full test suite completes in under 60 seconds
No test has a timeout longer than 5 seconds (all I/O is mocked)
security requirements
No real Supabase credentials or org encryption keys appear in test fixtures
Test vectors for AES-GCM use synthetic keys that are clearly marked as test-only
RLS rejection scenarios must be explicitly tested to confirm no data leakage on permission error

Execution Context

Execution Tier
Tier 6

Tier 6 - 158 tasks

Can start after Tier 5 completes

Implementation Notes

Use mocktail over mockito for null-safety compatibility — mocktail requires no code generation step, keeping the test setup fast. Create a shared test/helpers/supabase_mock.dart that exports a pre-configured MockSupabaseClient to avoid duplication across the four test files. For NHF 5-chapter edge cases, create a parameterised test using a list of (chaptersCount, shouldPass) tuples and forEach() — this is more readable than six individual test cases. For crypto tests, hardcode a 32-byte test key and a known IV + ciphertext + HMAC tuple generated offline so the tests are deterministic and do not rely on random number generation.

Document the test vector source in a comment. Cover the boundary where chapter count == 5 explicitly (this is the exact NHF limit and is the most likely regression point). Integration tests hitting a real Supabase test project can be placed in test/integration/ and excluded from the standard CI run via a tag.

Testing Requirements

This task IS the testing task. The deliverable is the test suite itself. Organize tests into four test files: assignment_repository_test.dart, contact_detail_repository_test.dart, contact_form_validator_test.dart, field_encryption_utils_test.dart. Each file should have a setUp() that initialises mocks and a tearDown() that disposes them.

Use group() blocks to separate logical scenarios within each file. For the encryption tests, use known AES-GCM test vectors to validate correctness independently of the Dart crypto library's own tests. For RLS tests, mock the Supabase client to return a PostgrestException with code 42501 (insufficient_privilege) and verify the repository returns the correct typed error. Run flutter test --coverage and generate an lcov report; add a CI check that fails if coverage drops below 90% on these four files.

Component
Field Encryption Utilities
infrastructure high
Epic Risks (3)
high impact medium prob security

Blindeforbundet's encryption key retrieval mechanism may not be finalised at implementation time, or session key availability via Supabase RLS may be inconsistent, causing decryption failures that expose masked placeholders to users and degrade the experience.

Mitigation & Contingency

Mitigation: Agree with Blindeforbundet on key storage and retrieval contract before implementation starts. Prototype key retrieval in a spike against the staging Supabase instance and validate the full decrypt/verify cycle with real test data before committing to the implementation.

Contingency: Implement a fallback that shows a 'field temporarily unavailable' state with a retry affordance. Log decryption failures server-side for audit. Escalate to Blindeforbundet stakeholders to unblock key management before the service tier epic begins.

medium impact medium prob technical

NHF contacts may belong to up to 5 chapters, each governed by separate RLS policies. A coordinator's chapter scope may not cover all affiliations, causing partial profile reads or silent data omissions that are difficult to detect in tests.

Mitigation & Contingency

Mitigation: Map all RLS policy combinations for multi-chapter contacts early. Write integration tests that create contacts with 5 affiliations and query them from coordinators with varying chapter scopes. Use Supabase's RLS test utilities to verify row visibility per role.

Contingency: Add an explicit 'affiliation partially visible' state in the repository response model so the UI can communicate scope limitations to the coordinator rather than silently showing incomplete data.

low impact medium prob scope

Organisation-specific validation rules (e.g., NHF chapter limit, Blindeforbundet encrypted field edit flow) may expand in scope during implementation as edge cases are discovered, causing the validator to grow beyond the planned complexity.

Mitigation & Contingency

Mitigation: Define the complete validation rule set with product and org stakeholders before coding begins. Document each rule with its source organisation and acceptance test. Use a rule registry pattern so new rules can be added without modifying core validator logic.

Contingency: Timebox validator enhancements to 2 hours per additional rule. Defer non-blocking rules to a follow-on maintenance task rather than blocking the epic delivery.