Unit tests for all state machine transitions
epic-speech-to-text-input-state-management-task-007 — Write comprehensive unit tests for TranscriptionStateManager covering: (1) happy-path transitions idle → requesting-permission → recording → processing → complete, (2) scope guard rejection resulting in error state, (3) recognition error mid-session, (4) session interruption with partial persistence and recovery on re-init, (5) concurrent field instances via provider.family maintaining independent state, (6) disposal cancels subscriptions without data loss. Use flutter_test with mocktail mocks for SpeechRecognitionService, DictationScopeGuard, and PartialTranscriptionRepository.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 5 - 253 tasks
Can start after Tier 4 completes
Implementation Notes
Prefer `ProviderContainer` over full widget tests for state machine logic — it is faster and avoids widget tree overhead. Pattern for collecting state history: `final states =
For stream-based errors: expose a `StreamController` from the mock and `addError()` to it during the recognition error test. Ensure tearDown() calls `container.dispose()` to prevent test pollution.
Testing Requirements
This task IS the testing task. Organize tests into a single file `transcription_state_manager_test.dart` with clearly named group() blocks for each scenario. Use ProviderContainer (not WidgetTester) for pure state unit tests to keep them fast. Use mocktail: `registerFallbackValue` for all custom types passed to mocked methods.
Assert state transitions by collecting a list of emitted states via container.listen() into a local list and then checking the list against expected sequence. For the concurrent test, use two separate ProviderContainers with the same family key but assert they hold different state objects after diverging mutations.
If a peer mentor rapidly switches between dictation-enabled fields while a session is still processing, the Riverpod family provider may share state or the SpeechRecognitionService may receive conflicting start/stop commands, causing orphaned recording sessions or state corruption.
Mitigation & Contingency
Mitigation: Design the state manager to enforce a single active dictation session globally via a shared active-field-key notifier. When a new field requests dictation while another session is active, automatically issue a stop command to the existing session and await completion before starting the new one. Test this concurrency scenario explicitly.
Contingency: If concurrency issues persist in integration testing, add a global dictation mutex at the SpeechRecognitionService level that serialises all start requests, with a short debounce to handle rapid field switching.
The recovery flow for interrupted dictation sessions (app restart with persisted partials) has ambiguous UX requirements. If recovery is automatic and the user has already typed different content into the field, the merge logic may overwrite or duplicate user input, causing data loss.
Mitigation & Contingency
Mitigation: Define the recovery behaviour explicitly: recovery should surface as an opt-in prompt ('You have unsaved dictation — insert or discard?') rather than an automatic merge. The state manager emits a dedicated recoverable-partial state that the UI layer renders as a non-blocking action sheet.
Contingency: If the recovery UI adds too much complexity for the initial release, scope recovery to display only the partial text in the preview field on re-open without auto-insertion, letting the user manually copy if needed, and defer the automatic recovery prompt to a subsequent iteration.