high priority low complexity testing pending testing specialist Tier 2

Acceptance Criteria

Test suite passes with `flutter test` and zero failures
Successful insert test: mock returns a valid JSON response; assert the returned ActivityRecord fields match the mocked response exactly
Insert error test: mock throws a PostgrestException; assert SupabaseActivityClient throws the project's domain exception (not a raw PostgrestException)
fetchActivities test: assert the Supabase query builder is called with an `.eq('peer_id', peerId)` filter (or equivalent column name)
deleteActivity test: assert the delete query is called with the correct row ID and no additional filters
All four branches (success insert, error insert, fetch, delete) are covered — coverage report shows 100% on SupabaseActivityClient
Tests use mocktail or mockito mocks — no real network calls, no Supabase emulator required
Test file is located at `test/features/activity_registration/data/supabase_activity_client_test.dart`

Technical Requirements

frameworks
flutter_test
mocktail or mockito
apis
Supabase PostgREST (mocked)
data models
ActivityRecord
performance requirements
Full test suite for this file must complete in under 5 seconds
security requirements
Test fixtures must not contain real user IDs, real peer IDs, or any PII — use clearly fake values like 'test-peer-id-001'

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Mocking the Supabase fluent query builder chain (`.from('activities').insert({...}).select()`) is the main challenge. Mocktail's `when().thenAnswer()` works for async methods but requires each builder method to return a mock of the next type in the chain. A pragmatic alternative is to inject a thin abstract interface (e.g., `ISupabaseQueryRunner`) that wraps the actual Supabase calls, making it trivially mockable. If SupabaseActivityClient was already implemented with this abstraction in mind, use it.

Otherwise, use mocktail's `registerFallbackValue` for complex builder types. Verify the domain exception wrapping: the client should catch `PostgrestException` and throw a typed `ActivityClientException` — the test for the error branch should use `throwsA(isA())`, not `throwsA(isA())`. This confirms the exception translation is tested, not just the throw behavior.

Testing Requirements

Unit tests only — no widget tests, no integration tests. Use flutter_test as the test runner. Mock the SupabaseClient (and its query builder chain) using mocktail. The Supabase query builder uses a fluent interface (`.from().insert().select()` etc.) — mock each step of the chain.

If the chain is difficult to mock directly, consider wrapping the raw Supabase calls in a thin private method that can be overridden in tests via a test subclass. Each test should be in its own `test()` block with a descriptive name. Group related tests under a `group('SupabaseActivityClient', ...)` block.

Component
Supabase Activity Client
infrastructure low
Epic Risks (3)
high impact medium prob technical

The optimistic insert pattern requires reconciling temporary local IDs with server-assigned IDs after the async Supabase write completes. If reconciliation logic is incorrect, the UI may display stale records, duplicate entries may appear, or subsequent operations (edit, delete) may target the wrong record ID, corrupting data integrity.

Mitigation & Contingency

Mitigation: Define a clear contract for temporary ID generation (e.g., UUID prefixed with 'local-') and implement a dedicated reconciliation method in ActivityRepository that atomically swaps the temporary ID. Write integration tests that simulate the full optimistic → confirm cycle.

Contingency: If reconciliation proves too complex, fall back to a simpler non-optimistic insert with a loading spinner for the network round-trip. The UX degrades slightly but correctness is preserved. Re-introduce optimistic behaviour once the pattern is stable.

high impact medium prob integration

Supabase row-level security policies on the activities table may not be configured to match the access patterns required by the client. If RLS blocks inserts or selects for the authenticated peer mentor session, all activity registration operations will silently fail or return empty results, which is difficult to diagnose in production.

Mitigation & Contingency

Mitigation: Define and test RLS policies in a dedicated Supabase migration script as part of this epic. Create integration tests that execute against a local Supabase instance with RLS enabled, covering insert, select by peer mentor ID, and denial of cross-mentor access.

Contingency: Maintain a fallback service-role client path (server-side only) that can be activated via a feature flag if client-side RLS is blocking legitimate operations while policies are corrected.

medium impact low prob technical

SharedPreferences on Flutter can become corrupted if the app crashes mid-write or if the device runs out of storage. A corrupted last-used activity type preference would cause the defaults manager to return null or an invalid ID, breaking the zero-interaction happy path.

Mitigation & Contingency

Mitigation: Wrap all LocalStorageAdapter reads in try/catch with typed safe defaults. Validate the retrieved activity type ID against the known list before returning it. Use atomic write operations where the platform supports them.

Contingency: If the preference store is corrupted, silently reset to the hardcoded default (first activity type alphabetically or 'general') and log a warning. The user loses their last-used preference but the app remains functional.