Write unit tests for LocalStorageAdapter
epic-organization-selection-infrastructure-task-004 — Write unit tests for LocalStorageAdapter covering: successful read/write of org ID string, read when key is absent returns null, delete removes key, and concurrent read/write safety. Use SharedPreferences.setMockInitialValues for test isolation. Achieve 100% branch coverage on the adapter.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Keep the test file strictly focused on LocalStorageAdapter behavior — do not test OrgSelectionStorageKeys constants here (those have their own test per task-001). Use descriptive test names that read as sentences: test('returns null when key has not been written', ...) rather than test('readString null', ...). Group tests by method using group('readString', ...) blocks for readability. For the concurrent test, note that SharedPreferences mock in flutter_test is synchronous under the hood — the concurrency test primarily validates that the adapter does not introduce any accidental state mutation between calls.
If the adapter internally caches values, the concurrent test will catch stale-read bugs. Add a comment at the top of the test file referencing the 100% coverage requirement so future contributors know not to reduce coverage.
Testing Requirements
All tests are pure unit tests using flutter_test — no WidgetTester needed. Call SharedPreferences.setMockInitialValues({}) in setUp() to reset state between tests; do not rely on test execution order. For the concurrent write test, use Future.wait([adapter.writeString(key1, val1), adapter.writeString(key2, val2)]) and then read both keys to assert both persisted. For the FormatException test, use SharedPreferences.setMockInitialValues({'org_selection.bad_key': 'not-valid-json{{'}) and then call adapter.readJson('org_selection.bad_key', (m) => m) and expect it to throw FormatException.
Run coverage with: flutter test --coverage test/infrastructure/local_storage_adapter_test.dart and inspect lcov.info to confirm 100% branch coverage. If coverage is below 100%, identify the uncovered branch (likely a null-check or catch block) and add a targeted test case.
SharedPreferences behaves differently on iOS (NSUserDefaults) vs Android (SharedPreferences) for edge cases such as first-launch cold reads, storage quota exceeded, or process kill mid-write. If the adapter does not account for these differences, the persistence layer can silently return null on one platform while returning a stale value on the other, causing incorrect routing decisions downstream.
Mitigation & Contingency
Mitigation: Write platform-specific integration tests using flutter_test device runners for both iOS and Android. Document known platform delta in the adapter's inline comments and encode defensive fallback for null returns at the repository boundary.
Contingency: If platform delta causes persistent issues, replace SharedPreferences with flutter_secure_storage for this key — the LocalStorageAdapter abstraction makes this a single-file swap with no impact on the repository or service layer.
The shared_preferences Flutter plugin may have a version conflict with other plugins already in the project pubspec.yaml. A conflict discovered late in the epic blocks all downstream epics.
Mitigation & Contingency
Mitigation: Resolve and pin the shared_preferences version in pubspec.yaml as the very first task of this epic before writing any implementation code. Run flutter pub get and resolve any version conflicts immediately.
Contingency: If version pinning is impossible due to transitive conflicts, implement LocalStorageAdapter using path_provider + dart:io for JSON file storage as an alternative — the interface contract remains unchanged.