high priority medium complexity testing pending testing specialist Tier 7

Acceptance Criteria

Happy path: repository fetches a contact record with an encrypted field and field-encryption-utils returns the plaintext value correctly
Wrong key path: injecting an invalid org key causes field-encryption-utils to return a MaskedFieldResult with the masked placeholder, not throw an exception
Missing key path: a null org key also yields MaskedFieldResult, not a crash
RLS cross-org test: a Supabase client authenticated as Org A cannot read contact records belonging to Org B (query returns empty or 403)
RLS same-org test: authenticated client for Org A successfully reads Org A contacts
All tests are deterministic and do not rely on real Supabase network calls in CI
A QA-facing markdown document `test/integration/contact_encryption_test_scenarios.md` lists every scenario with input, expected output, and pass/fail result
Tests run via `flutter test integration_test/` with the `--no-pub` flag without setup beyond environment variables
No real personal data appears in test fixtures — all encrypted payloads are synthetic

Technical Requirements

frameworks
Flutter
flutter_test
Supabase
apis
Supabase RLS policies
Supabase Auth (test user tokens)
data models
ContactDetail
EncryptedFieldMetadata
MaskedFieldResult
OrgKey
performance requirements
Each integration test scenario must complete in under 5 seconds against a local or mock Supabase instance
Test setup and teardown must not leave orphan rows in the test database
security requirements
Test org keys must be randomly generated for each test run — never committed as hardcoded secrets
RLS bypass must be tested using the service-role key only in a dedicated test environment, never in production credentials
Synthetic encrypted payloads must use the same encryption algorithm as production (AES-256-GCM or whichever field-encryption-utils implements)

Execution Context

Execution Tier
Tier 7

Tier 7 - 84 tasks

Can start after Tier 6 completes

Implementation Notes

Structure tests in three files: `contact_detail_repository_test.dart` (happy path + data shape), `field_encryption_round_trip_test.dart` (key error + masked fallback), `rls_cross_org_test.dart` (RLS enforcement). For the mock Supabase client, implement `FakeSupabaseClient` as a class implementing the Supabase client interface, returning hardcoded JSON from a `fixtures/` folder. The encrypted test payload should be pre-encrypted using the test org key so the decryption path exercises real crypto code. For injecting a bad key, pass a randomly-generated different AES key — do not stub the crypto function.

For RLS tests with real local Supabase, the `supabase/seed.sql` must create two orgs and contacts and the test must authenticate with each org's test user. Document the local setup in `integration_test/README.md`. The QA scenario document must be written in English and use a table format: Scenario | Input | Expected Output | Status.

Testing Requirements

These are integration tests (not unit tests) and must live in `integration_test/` following Flutter's integration test conventions. Use a `SupabaseMockClient` that intercepts HTTP calls and returns pre-canned JSON responses — this avoids needing a running Supabase instance in CI while still exercising real repository and encryption code paths. Alternatively, use the Supabase local CLI (`supabase start`) for local developer runs with a documented setup script. For RLS tests against a real local instance, use two distinct test JWT tokens (one per org) generated with the local Supabase JWT secret.

Aim for 100% path coverage of field-encryption-utils' public API. Each test group must call `tearDown` to reset any shared state.

Component
Contact Detail Repository
data medium
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.