@@ -38,6 +38,7 @@ const socketFrameDurations = [];
3838const diagnostics = [ ] ;
3939let backendStops = 0 ;
4040let socketCloses = 0 ;
41+ let duringBackendStop = ( ) => { } ;
4142
4243class MockAudioV2Socket {
4344 constructor ( options ) {
@@ -66,6 +67,7 @@ class MockAudioV2Socket {
6667
6768 async stopCapture ( ) {
6869 backendStops += 1 ;
70+ await duringBackendStop ( ) ;
6971 this . activeBinding = null ;
7072 }
7173
@@ -165,7 +167,19 @@ const { useAudioStreamer } = loadTypeScript(sourcePath, {
165167 assert . deepEqual ( Array . from ( sentPackets [ 0 ] . opus ) , [ 1 , 2 , 3 ] ) ;
166168 assert . deepEqual ( diagnostics , [ [ 'sent' , 3 ] , [ 'accepted' , 0 ] ] ) ;
167169
170+ duringBackendStop = async ( ) => {
171+ const before = sentPackets . length ;
172+ streamer . sendFrame ( 'phone' , {
173+ captureEpoch : 7 ,
174+ capturedAtMs : 1_780_000_000_040 ,
175+ monotonicTimestampMs : now + 40 ,
176+ opus : new Uint8Array ( [ 4 ] ) ,
177+ } ) ;
178+ assert . equal ( sentPackets . length , before , 'queued native callbacks must not send after stop' ) ;
179+ assert . equal ( nativeStops , 1 , 'microphone must stop before waiting for the backend' ) ;
180+ } ;
168181 await streamer . stopStreaming ( ) ;
182+ duringBackendStop = ( ) => { } ;
169183 assert . equal ( backendStops , 1 , 'the capture has one stop owner' ) ;
170184 assert . equal ( nativeStops , 1 , 'stopping the stream also stops the native phone session' ) ;
171185 assert . equal ( socketCloses , 1 ) ;
@@ -180,6 +194,28 @@ const { useAudioStreamer } = loadTypeScript(sourcePath, {
180194 assert . equal ( beginCaptureCalls [ 1 ] . deliveryClass , DeliveryClass . LIVE ) ;
181195 await wearableStreamer . stopStreaming ( ) ;
182196
197+ for ( const failureAt of [ 'native' , 'backend' ] ) {
198+ const failingStreamer = useAudioStreamer ( ) ;
199+ const expected = new Error ( `${ failureAt } stop failed` ) ;
200+ await failingStreamer . startStreaming ( 'wss://chronicle.invalid/ws/audio' , {
201+ kind : 'phone' ,
202+ ...phoneVoice ,
203+ stopCapture : async ( ) => {
204+ if ( failureAt === 'native' ) throw expected ;
205+ } ,
206+ } ) ;
207+ const closesBefore = socketCloses ;
208+ duringBackendStop = async ( ) => { throw expected ; } ;
209+ await assert . rejects ( failingStreamer . stopStreaming ( ) , error => error === expected ) ;
210+ assert . equal ( socketCloses , closesBefore + 1 , 'stop failure must still close the transport' ) ;
211+ const packetsBefore = sentPackets . length ;
212+ failingStreamer . sendFrame ( 'phone' , {
213+ captureEpoch : 7 , capturedAtMs : 1_780_000_000_060 ,
214+ monotonicTimestampMs : now + 60 , opus : new Uint8Array ( [ 5 ] ) ,
215+ } ) ;
216+ assert . equal ( sentPackets . length , packetsBefore , 'failed stop must leave capture inactive' ) ;
217+ }
218+
183219 console . log ( 'phone audio streaming tests passed' ) ;
184220} ) ( ) . catch ( error => {
185221 console . error ( error ) ;
0 commit comments