@@ -532,4 +532,73 @@ describeE2E('livekit-rtc data streams e2e', () => {
532532 } ,
533533 testTimeoutMs ,
534534 ) ;
535+
536+ it (
537+ 'reads a closed reader as an empty stream' ,
538+ async ( ) => {
539+ const { rooms } = await connectTestRooms ( 2 ) ;
540+ const [ receivingRoom , sendingRoom ] = rooms ;
541+ const topic = 'read-after-close-topic' ;
542+
543+ const handed = withTimeout (
544+ new Promise < TextStreamReader > ( ( resolve ) => {
545+ receivingRoom ! . registerTextStreamHandler ( topic , async ( reader ) => {
546+ resolve ( reader ) ; // never read
547+ } ) ;
548+ } ) ,
549+ testTimeoutMs ,
550+ 'Timed out waiting for the stream handler to be called' ,
551+ ) ;
552+
553+ const writer = await sendingRoom ! . localParticipant ! . streamText ( { topic } ) ;
554+ await writer . write ( 'never consumed' ) ;
555+
556+ const reader = await handed ;
557+ await reader . close ( ) ;
558+
559+ const text = await withTimeout (
560+ reader . readAll ( ) ,
561+ testTimeoutMs ,
562+ 'Timed out reading a closed reader' ,
563+ ) ;
564+ expect ( text ) . toBe ( '' ) ;
565+
566+ await writer . close ( ) ;
567+ await Promise . all ( rooms . map ( ( r ) => r . disconnect ( ) ) ) ;
568+ } ,
569+ testTimeoutMs ,
570+ ) ;
571+
572+ // `dataStream.maxPayloadByteLength` caps what a receiver will accept (5gb by
573+ // default). The native side emits the stream to the handler first and then
574+ // fails it, so the error surfaces on the read rather than as a missing stream.
575+ it (
576+ 'fails the read when an incoming stream exceeds maxPayloadByteLength' ,
577+ async ( ) => {
578+ const maxPayloadByteLength = 1000 ;
579+ const { rooms } = await connectTestRooms ( 2 , { dataStream : { maxPayloadByteLength } } ) ;
580+ const [ receivingRoom , sendingRoom ] = rooms ;
581+ const topic = 'oversized-topic' ;
582+
583+ const handed = withTimeout (
584+ new Promise < TextStreamReader > ( ( resolve ) => {
585+ receivingRoom ! . registerTextStreamHandler ( topic , resolve ) ;
586+ } ) ,
587+ testTimeoutMs ,
588+ 'Timed out waiting for the oversized stream to be handed to the handler' ,
589+ ) ;
590+
591+ // sendText declares the payload's total length up front, so the receiver
592+ // rejects the stream on the header without reading any of it.
593+ await sendingRoom ! . localParticipant ! . sendText ( pseudoRandomText ( maxPayloadByteLength * 5 ) , {
594+ topic,
595+ } ) ;
596+
597+ const reader = await handed ;
598+ await expect ( reader . readAll ( ) ) . rejects . toThrow ( / p a y l o a d e x c e e d s m a x i m u m s i z e / ) ;
599+
600+ await Promise . all ( rooms . map ( ( r ) => r . disconnect ( ) ) ) ;
601+ } ,
602+ testTimeoutMs ,
603+ ) ;
535604} ) ;
0 commit comments