high priority medium complexity testing pending testing specialist Tier 3

Acceptance Criteria

PostSessionReportRepository — createDraft: verifies insert payload contains `status: 'draft'`, `activity_id`, and all required fields; returns PostSessionReport on success
PostSessionReportRepository — getByActivityId: returns the report for a valid activity_id; returns null when no report exists for that activity_id
PostSessionReportRepository — update (partial): verifies only the changed fields are sent in the update payload (not the full object); timestamp `updated_at` is refreshed
PostSessionReportRepository — saveDraft: persists partial data without requiring all required fields to be populated; status remains 'draft'
PostSessionReportRepository — submit: transitions status from 'draft' to 'submitted'; verifies all required fields are validated before DB write (or delegates to validator)
PostSessionReportRepository — activity_id FK enforcement: when activity_id does not exist (Supabase FK violation), wraps error in InvalidReferenceException with the activity_id value in the message
PostSessionReportRepository — permission error: when RLS blocks read/write, throws PermissionException (not raw PostgrestException)
OrgFieldConfigLoader — successful fetch: returns parsed List<FieldConfig> from Supabase JSONB column for a given org_id
OrgFieldConfigLoader — cache hit: repeated call within TTL returns cached value without re-querying Supabase
OrgFieldConfigLoader — forced refresh: calling `reload(orgId)` bypasses cache and fetches fresh config even within TTL
OrgFieldConfigLoader — fallback to defaults: when no config row exists for org_id (empty result), returns a predefined default FieldConfig list without throwing
OrgFieldConfigLoader — malformed JSONB: when Supabase returns a non-null but unparseable JSON blob, catches the parse error and returns the default config with a logged warning (does not crash)
All tests use constructor-injected mocks — no `Supabase.instance` calls
Test file names: `post_session_report_repository_test.dart`, `org_field_config_loader_test.dart`

Technical Requirements

frameworks
flutter_test
mockito
fake_async
apis
Supabase Dart client (mocked)
Supabase PostgREST error codes (23503 FK violation, PGRST301 RLS)
data models
PostSessionReport (status, activity_id, field_values, created_at, updated_at)
FieldConfig (field_id, field_type, label, validation_rules)
OrgFieldConfig (org_id, fields JSONB)
InvalidReferenceException
PermissionException
performance requirements
Full suite completes in < 8 seconds
No network I/O in any test path
security requirements
Fixtures must not contain real org IDs, activity IDs, or personal health data
Confirm that the malformed JSONB test does not log raw JSONB content (which may contain PII) — log only a sanitised error code

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

The partial update behaviour requires careful attention: if the repository fetches the existing record and merges before updating, the test must stub both the fetch and the update calls. If it uses Supabase's `.update()` with a delta map, the test is simpler. Clarify and document the chosen approach in a comment. For OrgFieldConfigLoader, wrap all `jsonDecode` calls in try/catch and log via the app's logger abstraction (not `print`) so test output stays clean — this also means the logger should be injectable or mockable.

The fallback-to-defaults path is critical for org onboarding (new orgs have no config row yet); ensure the default config list is defined as a constant in the loader, not hardcoded strings in the test.

Testing Requirements

Use `group()` to separate PostSessionReportRepository and OrgFieldConfigLoader tests. For the partial-update test, use an `ArgumentCaptor` (mockito's `captureAny`) to inspect the exact payload sent to the Supabase mock — assert that unchanged fields are absent from the map. For the FK violation test, configure the mock to throw a `PostgrestException` with code '23503' and verify the repository converts it correctly. For the malformed JSONB test, return a raw string `'{invalid json'` from the mock and assert the loader returns defaults and does not rethrow.

Reuse fixtures from `test/fixtures/` created in task-009.

Component
Post-Session Report Repository
data medium
Epic Risks (3)
high impact medium prob security

Supabase RLS policies for multi-org report access may be more complex than anticipated — coordinators need cross-peer-mentor access within their org but not across orgs, and draft reports should be invisible to coordinators until submitted. Misconfigured RLS could expose sensitive health data or block legitimate access.

Mitigation & Contingency

Mitigation: Define and test RLS policies in isolation before writing repository code. Create a dedicated SQL migration file with policy definitions and an automated integration test suite that verifies each role's access boundaries using real Supabase auth tokens.

Contingency: If RLS proves too complex to express declaratively, implement application-level access control in the repository layer with explicit org and role checks, and add a security audit task before the feature goes to production.

high impact medium prob integration

The org field config JSON stored in Supabase may lack a stable, versioned schema contract. If different organisations have drifted to different field-definition formats, org-field-config-loader will fail silently or crash, breaking form rendering for those orgs.

Mitigation & Contingency

Mitigation: Define a canonical JSON Schema for field config and validate all existing org configs against it before implementation begins. Store a schema version field in every config record and handle version migrations explicitly in the loader.

Contingency: If existing configs are too heterogeneous, implement a config normalisation pass in org-field-config-loader that coerces known variants to the canonical format, logging warnings for fields that cannot be normalised so operations can fix them in the admin console.

medium impact low prob technical

TTL-based schema cache invalidation may cause peer mentors to use stale field definitions for up to the TTL window after an admin updates the org config, potentially collecting data against outdated field structures.

Mitigation & Contingency

Mitigation: Set a conservative TTL (e.g. 15 minutes) and expose a manual cache-bust mechanism triggered on app foreground-resume. Document the maximum staleness window in the admin console so org admins know to plan config changes outside active reporting windows.

Contingency: If stale schema causes a data quality incident, add a Supabase Realtime subscription to the org config table that invalidates the cache immediately on any config update.