high priority low complexity testing pending testing specialist Tier 2

Acceptance Criteria

saveTabIndex followed by loadTabIndex returns the same TabIndex for all 5 valid tab values (0–4)
loadTabIndex returns null when SharedPreferences is empty (cold start simulation via SharedPreferences.setMockInitialValues({}))
saveStackSnapshot + loadStackSnapshot round-trips correctly for each of the 5 tab indices with a multi-item route stack
loadStackSnapshot returns null for a tab that has no stored snapshot
clearAll removes all nav_state keys and subsequent load calls return null
loadTabIndex gracefully returns null (not throws) when stored value is a malformed/corrupted string
loadStackSnapshot gracefully returns null (not throws) when stored JSON is invalid
Tests are hermetic: each test calls SharedPreferences.setMockInitialValues({}) in setUp to reset state
All tests pass with flutter test and 0 skipped
Test coverage for NavigationStateRepository reaches ≥ 95% branch coverage

Technical Requirements

frameworks
Flutter
flutter_test
shared_preferences
data models
NavigationStateRepository
TabIndex
NavigationStackSnapshot
performance requirements
All unit tests complete in under 2 seconds total
security requirements
No real device storage accessed during tests — all I/O goes through the fake SharedPreferences

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

The test file should be placed at test/navigation/navigation_state_repository_test.dart. Since SharedPreferences is injected into the repository constructor, no mockito mocks are needed — just use the fake instance from setMockInitialValues. For the 5-tab loop test, use a for loop with test('tab $i round-trip', ...) inside a group to avoid code duplication. For the corrupted data graceful-handling tests, pre-seed the raw SharedPreferences key directly (bypassing the repository's write path) to simulate real-world corruption scenarios.

Ensure tearDown calls SharedPreferences.setMockInitialValues({}) even if test throws, or rely on setUp being called before every test. Keep fixtures (sample route stacks) in a local const or a test fixture file shared with task-001 model tests.

Testing Requirements

Use SharedPreferences.setMockInitialValues({}) in a setUp() block to guarantee a clean store before each test. Instantiate NavigationStateRepository by passing the fake SharedPreferences.getInstance() result obtained after setMockInitialValues. Write one test group per method: 'saveTabIndex / loadTabIndex', 'saveStackSnapshot / loadStackSnapshot', 'clearAll', 'corrupted data handling'. For corrupted data tests, use SharedPreferences.setMockInitialValues({'nav_state.tab_index': 'not-a-number'}) to pre-seed invalid data.

Use expect(result, isNull) assertions — never expect an exception for graceful degradation paths. Run flutter test --coverage and confirm branch coverage with a coverage report.

Component
Navigation State Repository
data low
Epic Risks (2)
high impact medium prob technical

StatefulShellRoute branch navigator state can interact unexpectedly with GoRouter's imperative navigation (go, push, replace), causing state snapshots to desync from actual route stacks. This could manifest as a user returning to a tab and seeing a different screen than expected, breaking the core motor-fatigue promise.

Mitigation & Contingency

Mitigation: Write integration tests that simulate cross-tab navigation with nested pushes before any UI layer is built. Pin go_router to a tested minor version and review the StatefulShellRoute changelog before upgrading.

Contingency: If branch navigator state consistently desyncs, fall back to a manual stack snapshot strategy using a custom NavigatorObserver that records and replays navigation events independently of StatefulShellRoute internals.

medium impact medium prob technical

Persisted navigation stacks in shared_preferences can become stale or corrupt if route paths are renamed during development, causing app crashes or infinite redirect loops on cold start for users who have an old snapshot.

Mitigation & Contingency

Mitigation: Version the persisted schema with a format key. On app start, validate that all stored route paths exist in the current route config before restoring; silently discard invalid entries rather than crashing.

Contingency: Implement a safe-mode cold start that skips state restoration after a detected crash (via a dirty-launch flag written at startup and cleared on successful first frame), falling back to the default root tab.