Skip to content

Add E2E flow tests for core audio capture and transcription#5696

Closed
beastoin wants to merge 7 commits intomainfrom
test/e2e-capture-flows
Closed

Add E2E flow tests for core audio capture and transcription#5696
beastoin wants to merge 7 commits intomainfrom
test/e2e-capture-flows

Conversation

@beastoin
Copy link
Collaborator

Summary

Flow Definitions (YAML v2)

Flow File Preconditions Steps
Phone Mic Capture phone-mic-capture.yaml auth_ready, microphone_permission 6 — record → capturing page → transcript → stop → conversation
BLE Audio Capture ble-audio-capture.yaml auth_ready, ble_on, omi_device_connected 5 — device → BLE stream → transcript → lifecycle → conversation
Conversation Lifecycle conversation-lifecycle.yaml auth_ready, microphone_permission 6 — speech → silence timeout → processing → completed
Offline Persistence (WAL) offline-persistence-wal.yaml auth_ready, ble_on, omi_device_connected 6 — record → WS drop → WAL buffers → restore → sync
WebSocket Reconnection ws-reconnection.yaml auth_ready, microphone_permission 5 — recording → WS drop → reconnect → resume

Provider Flow Tests (27 tests)

Flow Tests What's Verified
Phone mic state machine 5 stop → init → record → stop transitions, segment accumulation
Conversation lifecycle 4 segments flow, processing → completed, empty title handling
WS reconnection 6 close code handling (null/1012/1013/4002), connectivity state, transcriptServiceReady
Offline persistence (WAL) 4 WAL defaults, phone mic exclusion, device WAL persistence through WS drop
BLE device recording 4 deviceRecord state, WS reconnection persistence, pause/resume, disconnect
Integration (cross-cutting) 4 Full phone mic flow, full BLE flow, conversation lifecycle, multi-close-code matrix

Bug Fix

capture_provider.dart was missing import 'dart:math' after the revert of #5618 dropped it while #5617's Random() usage remained. Added the import back.

Emulator Evidence

Phone mic capture flow verified on VPS emulator:

  • Home screen → tap record → "Listening" status active with red recording dot
  • Capturing page shows "Waiting for transcript or photos..."
  • Recording state persists across page navigation

Test Results

00:05 +27: All tests passed!

Test plan

  • All 27 flow tests pass (flutter test test/providers/capture_flow_test.dart)
  • Registered in app/test.sh
  • Phone mic capture flow verified on emulator with agent-flutter
  • BLE, WAL, reconnection flows documented as YAML (require hardware/network manipulation)

🤖 Generated with Claude Code

beastoin and others added 7 commits March 16, 2026 06:53
Revert of PR #5618 dropped the import but left Random() usage from PR #5617.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
27 tests covering 5 flows: phone mic state machine, conversation
lifecycle, WS reconnection handling, WAL offline persistence, and
BLE device recording. Tests exercise the actual provider state
transitions through complete user flow scenarios.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
YAML v2 flow: tap record → capturing page → live transcript →
stop → conversation processed. Covers bottom_nav_bar.dart record
button, ConversationCapturingPage, and CaptureProvider state machine.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
YAML v2 flow: Omi device connects → BLE Opus audio streams →
WS to backend → transcription appears → conversation lifecycle.
Requires ble_on + omi_device_connected preconditions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
YAML v2 flow: speech segments → silence timeout (120s) →
backend lifecycle manager triggers → LLM summarizes →
conversation with title/summary appears in list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
YAML v2 flow: device records → WS disconnects → WAL buffers
audio locally → network restores → WAL syncs to server.
Requires omi_device_connected. Documents phone mic WAL gap.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
YAML v2 flow: recording active → WS drops mid-recording →
close code handling (null/1012/1013/4002) → exponential backoff
reconnect → audio streaming resumes. Documents Flaw 11 gap.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 16, 2026

Greptile Summary

Adds 5 YAML v2 E2E flow definitions and 27 provider-level flow tests covering the core audio capture, persistence, and transcription paths. Also fixes a missing dart:math import in capture_provider.dart that was broken by a prior revert.

  • 5 YAML flow definitions (phone-mic-capture, ble-audio-capture, conversation-lifecycle, offline-persistence-wal, ws-reconnection) document end-to-end test scenarios with preconditions, steps, and expected evidence
  • 27 provider flow tests in capture_flow_test.dart exercise CaptureProvider and ConversationProvider state machines through complete user flow scenarios (state transitions, WS close code handling, WAL support, BLE recording)
  • Bug fix: Restores import 'dart:math' in capture_provider.dart needed by Random() at line 1330, which was dropped when PR fix: handle WebSocket close codes 1012/1013 for server restart/overload #5618 was reverted while fix: replace fixed 15s WebSocket reconnect with exponential backoff + jitter #5617's usage remained
  • Some test names/comments misrepresent actual behavior — notably the 4002 close code test claims reconnection is suppressed, but onClosed() unconditionally calls _startKeepAliveServices() for all codes

Confidence Score: 4/5

  • This PR is safe to merge — it adds tests and flow definitions with a single-line import fix, posing minimal risk to production code.
  • The only production code change is a one-line import fix that is clearly correct (restoring a missing dart:math import required by existing Random() usage). The test file is well-structured but has misleading test names around 4002 close code behavior that could cause confusion. The YAML files are documentation-only. Score reduced from 5 due to the test accuracy concern.
  • Pay close attention to app/test/providers/capture_flow_test.dart — test names around WS close code 4002 behavior are misleading and may encode incorrect assumptions about reconnection logic.

Important Files Changed

Filename Overview
app/test/providers/capture_flow_test.dart New 526-line test file with 27 provider-level flow tests. Well-structured across 6 groups covering state machine transitions, WS reconnection, WAL support, and BLE flows. Some test names/comments misrepresent actual behavior (4002 close code test name says "does NOT trigger reconnect" but implementation always reconnects).
app/lib/providers/capture_provider.dart Single-line fix adding missing import 'dart:math' required by Random() usage at line 1330. Correct and necessary bug fix.
app/e2e/flows/phone-mic-capture.yaml New YAML v2 E2E flow definition for phone mic capture. 6 steps covering record → capturing page → transcript → stop → conversation. Well-documented with evidence screenshots, notes, and preconditions.
app/e2e/flows/ble-audio-capture.yaml New YAML v2 E2E flow definition for BLE audio capture. 5 steps covering device connection → BLE stream → transcription → lifecycle timeout → conversation. Consistent format with other flow files.
app/e2e/flows/conversation-lifecycle.yaml New YAML v2 E2E flow definition for conversation lifecycle. 6 steps covering speech → silence timeout → processing → completed with structured data.
app/e2e/flows/offline-persistence-wal.yaml New YAML v2 E2E flow definition for offline persistence (WAL). 6 steps covering device recording → network drop → WAL buffering → restore → sync. Good documentation of WAL behavior.
app/e2e/flows/ws-reconnection.yaml New YAML v2 E2E flow definition for WebSocket reconnection. 5 steps covering recording → WS drop → reconnect → resume → conversation. Documents known gaps in reconnection handling.
app/test.sh Adds new test file to the test runner script. Single line addition, correctly placed.
app/pubspec.lock Routine transitive dependency version bumps (characters, matcher, material_color_utilities, meta, test_api). No direct dependency changes.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[RecordingState.stop] -->|"updateRecordingState(initialising)"| B[RecordingState.initialising]
    B -->|"updateRecordingState(record)"| C[RecordingState.record]
    C -->|"updateRecordingState(stop)"| A
    A -->|"updateRecordingState(deviceRecord)"| D[RecordingState.deviceRecord]
    D -->|"updateRecordingState(pause)"| E[RecordingState.pause]
    E -->|"updateRecordingState(deviceRecord)"| D
    D -->|"updateRecordingState(stop)"| A

    C -->|"onClosed(any code)"| F{_startKeepAliveServices}
    D -->|"onClosed(any code)"| F
    F -->|"Timer fires, recordingState=record"| G[Reconnect WS - phone mic]
    F -->|"Timer fires, _recordingDevice!=null"| H[Reconnect WS - BLE device]
    F -->|"Not ready / already connected"| I[Skip / Reschedule]

    C -.->|"segments arrive"| J[hasTranscripts = true]
    D -.->|"segments arrive"| J
    J -.->|"silence timeout"| K[Backend processes conversation]
    K -.->|"opcode 201"| L[ConversationProvider.conversations]
Loading

Last reviewed commit: 6f2d1ae

Comment on lines +251 to +263
test('onClosed with 4002 (out of credits) does NOT trigger reconnect', () {
final provider = CaptureProvider();
provider.updateRecordingState(RecordingState.record);
provider.segments = [_segment('s1', 'talking')];
provider.hasTranscripts = true;

// 4002 = out of credits — no reconnect
provider.onClosed(4002);

// BUG (Flaw 3): recording state stays as record, not reset to stop
expect(provider.recordingState, RecordingState.record);
expect(provider.hasTranscripts, true);
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Misleading test name — 4002 DOES trigger reconnect

The test is named "onClosed with 4002 (out of credits) does NOT trigger reconnect", but looking at capture_provider.dart:1316-1326, onClosed() unconditionally calls _startKeepAliveServices() for all close codes, including 4002. The only additional behavior for 4002 is calling usageProvider?.markAsOutOfCreditsAndRefresh().

The test currently only verifies that recordingState stays as record — which is identical behavior to every other close code. The test name/comment implies 4002 prevents reconnection, which is inaccurate. Consider either:

  1. Renaming the test to reflect actual behavior (e.g., "onClosed with 4002 marks out of credits and still reconnects"), or
  2. If the intent is that 4002 should NOT reconnect, filing this as a real bug in onClosed() where 4002 should skip _startKeepAliveServices().

Comment on lines +507 to +524
test('multiple WS close codes handled differently', () {
// Test that different close codes produce different behaviors
final states = <int?, String>{};

for (final code in [null, 1000, 1012, 1013, 4002]) {
final provider = CaptureProvider();
provider.updateRecordingState(RecordingState.record);
provider.onClosed(code);
states[code] = provider.recordingState.toString();
}

// All should preserve recording state (for reconnect or not)
expect(states[null], 'RecordingState.record'); // Normal reconnect
expect(states[1000], 'RecordingState.record'); // Normal close
expect(states[1012], 'RecordingState.record'); // Server restart
expect(states[1013], 'RecordingState.record'); // Overloaded
expect(states[4002], 'RecordingState.record'); // Out of credits (Flaw 3: not reset)
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test names contradict actual behavior — close codes don't differ

The test comment says "Test that different close codes produce different behaviors", but all 5 close codes produce identical results (RecordingState.record). The test actually proves that all codes behave the same — the assertion at line 523 even acknowledges this with "Flaw 3: not reset". Consider renaming to "all WS close codes preserve recording state" to match what's actually being verified, or adding assertions that differentiate behavior (e.g., verifying that usageProvider is notified for 4002).

@beastoin beastoin closed this Mar 19, 2026
@github-actions
Copy link
Contributor

Hey @beastoin 👋

Thank you so much for taking the time to contribute to Omi! We truly appreciate you putting in the effort to submit this pull request.

After careful review, we've decided not to merge this particular PR. Please don't take this personally — we genuinely try to merge as many contributions as possible, but sometimes we have to make tough calls based on:

  • Project standards — Ensuring consistency across the codebase
  • User needs — Making sure changes align with what our users need
  • Code best practices — Maintaining code quality and maintainability
  • Project direction — Keeping aligned with our roadmap and vision

Your contribution is still valuable to us, and we'd love to see you contribute again in the future! If you'd like feedback on how to improve this PR or want to discuss alternative approaches, please don't hesitate to reach out.

Thank you for being part of the Omi community! 💜

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant