Skip to content

test: lock in that "disconnecting" outlives the audio pipeline teardown - #1010

Open
kraenhansen wants to merge 2 commits into
mainfrom
kh/cursor/await-stop-audio-session-before-disconnected-873f
Open

kraenhansen wants to merge 2 commits into
mainfrom
kh/cursor/await-stop-audio-session-before-disconnected-873f

Conversation

@kraenhansen

Copy link
Copy Markdown
Member

What

Makes the "the client stays in disconnecting until AudioSession.stopAudioSession() has resolved" guarantee enforced rather than incidental.

The behaviour already holds on main: the React Native detach awaits stopAudioSession() in a finally, VoiceConversation.handleEndSession awaits that detach as cleanUp(), and BaseConversation.endSessionWithDetails only calls updateStatus("disconnected") after handleEndSession() settles. Nothing in the test suite covered that chain, though, and every link in it is a plain await that a refactor could drop without any test noticing.

Changes

  • packages/react-native: added Vitest (the package had no test setup) plus src/index.react-native.test.ts, which drives the real React Native setup strategy through VoiceConversation.startSession() / endSession() with a mocked native audio session. One case gates stopAudioSession() on a deferred and asserts the status sequence stops at disconnecting (and onDisconnect has not fired) until it resolves; the other asserts the audio session is still stopped when the connection detach throws.
  • packages/client: added src/VoiceConversation.test.ts covering the platform-agnostic half — a setup strategy whose detach never settles must keep the conversation in disconnecting, with the input/output controllers still open.
  • packages/react-native/src/index.react-native.ts: a comment recording why the stopAudioSession() call is awaited rather than fire-and-forget.

No published behaviour changes, so no changeset.

Testing

Both new tests were mutation-checked against the regressions they exist to catch.

Replacing await AudioSession.stopAudioSession() with void AudioSession.stopAudioSession() fails the React Native test:

 FAIL  src/index.react-native.test.ts > React Native session teardown > holds the session in disconnecting until the audio session has stopped
AssertionError: expected [ 'connecting', 'connected', …(2) ] to deeply equal [ 'connecting', 'connected', …(1) ]

  [
    "connecting",
    "connected",
    "disconnecting",
+   "disconnected",
  ]

Replacing await this.cleanUp() with void this.cleanUp() in VoiceConversation fails both the client test and the React Native test the same way, confirming the React Native case covers the whole chain and not just the wrapper.

Unmutated runs:

  • packages/react-native: 2/2 pass (pnpm exec vitest run)
  • packages/client: 260/260 pass across 19 files (pnpm exec vitest run --browser.headless)
  • pnpm exec turbo test lint check-types for client, react, and react-native: all green except the client browser project, which cannot launch a headed Chromium in this VM (no XServer) and passes with --browser.headless.
Open in Web Open in Cursor 

Lock in that a session stays in "disconnecting" until
AudioSession.stopAudioSession() has resolved, so the status never claims
the session is gone while the platform still owns the microphone.

Adds Vitest to the package (there was no test setup) and drives the real
React Native setup strategy through VoiceConversation.startSession/
endSession against a mocked native audio session.
VoiceConversation must not report "disconnected" until the platform
strategy's detach has settled, since that is where each platform releases
its audio pipeline (wake lock on web, native audio session on React
Native).
@kraenhansen
kraenhansen marked this pull request as ready for review September 10, 2026 11:20
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.

2 participants