high priority medium complexity integration pending testing specialist Tier 4

Acceptance Criteria

Integration test boots the full Riverpod ProviderContainer without throwing, using a mocked Supabase client and a real SharedPreferences mock backed by an in-memory store
Test reads a preference key that has not been set and receives null without error
Test writes an activity_type_id preference, reads it back, and asserts the returned value equals what was written
Test performs an optimistic insert via activityRepositoryProvider and asserts the stream emits an optimistic (local) record followed by a confirmed (server-echoed) record — two distinct emissions in order
Test verifies that a Supabase network error during insert causes the optimistic record to be rolled back and the stream emits the pre-insert state again
SCHEMA_NOTES.md exists at the repository root of the epic and documents: table name (activities), required columns (id, organization_id, peer_mentor_id, activity_type_id, date, duration_minutes, notes, created_at), and expected RLS policy (peer mentor can insert/select own rows; coordinator can select all rows in their org)
SCHEMA_NOTES.md documents the SharedPreferences key convention used for last-used activity type (e.g., 'pref_last_activity_type_id')
All tests pass with flutter_test on both debug and release profiles
Test file is located under test/integration/ and named activity_provider_smoke_test.dart

Technical Requirements

frameworks
Flutter
Riverpod
flutter_test
apis
Supabase PostgreSQL 15 (mocked)
Supabase Auth (mocked JWT)
data models
activity
activity_type
performance requirements
Full provider graph boot completes in under 500ms in test environment
Stream emissions arrive within 100ms of insert in mocked environment
security requirements
Mocked Supabase client must never make real network calls — use package:mocktail or equivalent
RLS expectations in SCHEMA_NOTES.md must specify that service role key is never used client-side
Test JWT fixture must not contain real user PII

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Use ProviderContainer (not WidgetTester) for these tests — no widget tree needed. Override activityRepositoryProvider and sharedPreferencesProvider in the container. For the optimistic stream test, use a StreamController> in the mock; emit the optimistic record immediately on insert(), then emit the confirmed record 50ms later using Future.delayed in the stub. For rollback, emit the optimistic record then throw — the repository should catch and re-emit the previous state.

SCHEMA_NOTES.md should be a plain Markdown file, not code — write it as developer-facing documentation with a table of columns, types, nullable flags, and one RLS policy example in SQL. Keep the file under 80 lines. Ensure the mock Supabase client conforms to the SupabaseClient interface so no real HTTP is initiated.

Testing Requirements

Integration tests only (no unit tests needed for this task). Use flutter_test with a ProviderContainer override pattern. Mock SupabaseClient using mocktail; stub the .from('activities').insert() call to return a success response then emit on a stream controller. Use a FakeSharedPreferences (in-memory map) to avoid platform channel calls.

Assert stream emissions using expectLater with emitsInOrder. Test the rollback scenario by having the mock throw a PostgrestException after the optimistic emit. Minimum: 5 test cases covering null-read, write-read, optimistic-success, optimistic-rollback, and full-graph-boot.

Component
Activity Repository
data medium
Dependencies (4)
Write unit tests for LocalStorageAdapter using SharedPreferences.setMockInitialValues. Cover read/write/delete/clear operations and null-safety for missing keys. Then test RegistrationPreferencesStore: verify getLastUsedActivityTypeId returns null on first run, returns saved value after saveLastUsedActivityTypeId, and round-trips correctly. Use flutter_test. epic-quick-activity-registration-data-infrastructure-task-008 Define Riverpod providers for all data layer components: localStorageAdapterProvider, supabaseActivityClientProvider (using supabaseClientProvider), registrationPreferencesStoreProvider, and activityRepositoryProvider. Use provider dependencies to wire the graph correctly. Ensure providers are scoped appropriately and override-friendly for testing. Place providers in a dedicated providers file co-located with each component. epic-quick-activity-registration-data-infrastructure-task-006 Write unit tests for SupabaseActivityClient using a mocked Supabase client. Cover: successful insert returns mapped ActivityRecord, insert error throws domain exception, fetchActivities filters by peerId correctly, and deleteActivity sends correct row ID. Use flutter_test and Mockito/mocktail. Aim for 100% branch coverage on the client class. epic-quick-activity-registration-data-infrastructure-task-007 Write unit tests for ActivityRepository focusing on the optimistic insert Stream contract: verify first emission has a 'local_' prefixed ID and SyncStatus.pending, verify second emission has a real server ID and SyncStatus.synced on success, verify second emission has SyncStatus.failed when Supabase client throws. Mock SupabaseActivityClient. Also test getActivities and deleteActivity delegation. Use flutter_test with StreamMatcher assertions. epic-quick-activity-registration-data-infrastructure-task-009
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.