medium priority low complexity testing pending testing specialist Tier 2

Acceptance Criteria

LocalStorageAdapter: read on a missing key returns null without throwing
LocalStorageAdapter: write then read returns the written value
LocalStorageAdapter: delete removes the key such that a subsequent read returns null
LocalStorageAdapter: clear removes all keys managed by the adapter
LocalStorageAdapter: writing and reading different data types (String, int, bool) all behave correctly if the adapter is generic, or String-only if typed
RegistrationPreferencesStore: getLastUsedActivityTypeId returns null when SharedPreferences mock has no initial values
RegistrationPreferencesStore: after saveLastUsedActivityTypeId('activity-type-123'), getLastUsedActivityTypeId returns 'activity-type-123'
RegistrationPreferencesStore: overwriting the saved value returns the latest value, not the original
All tests pass with `flutter test` and zero failures
SharedPreferences.setMockInitialValues({}) is called in setUp() to reset state between tests

Technical Requirements

frameworks
flutter_test
shared_preferences (test utilities)
data models
ActivityType (id only)
performance requirements
Full test suite for both files must complete in under 3 seconds
security requirements
Test fixtures must not contain real activity type IDs from production — use clearly synthetic values

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

The key insight for SharedPreferences testing in Flutter is that `SharedPreferences.setMockInitialValues()` must be called before any `SharedPreferences.getInstance()` call in the test. If LocalStorageAdapter caches the SharedPreferences instance, ensure the test either creates a fresh adapter per test or exposes a way to inject the instance. The simplest pattern is to accept `SharedPreferences` as a constructor parameter in LocalStorageAdapter (dependency injection), then tests can pass `await SharedPreferences.getInstance()` after setting mock values. For RegistrationPreferencesStore tests, test the full round-trip through a real LocalStorageAdapter rather than mocking it — the store is a thin wrapper and the integration is what matters.

Two test files are better than one combined file for readability and separation of concerns.

Testing Requirements

Unit tests only using flutter_test. Use `SharedPreferences.setMockInitialValues({})` from the `shared_preferences` package test utilities — this does not require a Flutter binding or platform channel stubbing in recent versions. Organize into two test files: `local_storage_adapter_test.dart` and `registration_preferences_store_test.dart`. Each file uses a `setUp()` that resets mock values.

For RegistrationPreferencesStore tests, instantiate the store with a real LocalStorageAdapter backed by the mock SharedPreferences — this tests the integration between the two classes rather than mocking LocalStorageAdapter, which would only test delegation and not actual behavior.

Component
Local Storage Adapter
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.