@@ -141,6 +141,42 @@ describe('devframeTerminalHost stream lifecycle', () => {
141141 } )
142142 expect ( session . getChildProcess ( ) ) . toBeUndefined ( )
143143 } )
144+
145+ it ( 'bounds the session scrollback buffer' , async ( ) => {
146+ const { host } = createTerminalHost ( )
147+ let controller : ReadableStreamDefaultController < string >
148+ const stream = new ReadableStream < string > ( {
149+ start ( _controller ) {
150+ controller = _controller
151+ } ,
152+ } )
153+ const session : DevframeTerminalSession = { id : 't' , title : 'T' , status : 'running' , stream }
154+ host . register ( session )
155+ for ( let i = 0 ; i < 1200 ; i ++ ) controller ! . enqueue ( `line-${ i } ` )
156+ // Wait for the read loop to drain every enqueued chunk (each read is its
157+ // own microtask tick) before asserting the trimmed shape.
158+ await waitUntil ( ( ) => {
159+ expect ( session . buffer ! . at ( - 1 ) ) . toBe ( 'line-1199' )
160+ } )
161+ // Keeps the newest, drops the oldest.
162+ expect ( session . buffer ! . length ) . toBeLessThanOrEqual ( 1000 )
163+ expect ( session . buffer ! . includes ( 'line-0' ) ) . toBe ( false )
164+ } )
165+
166+ it ( 'does not restart a terminated child-process session' , async ( ) => {
167+ const { host, sinks } = createTerminalHost ( )
168+ const session = await host . startChildProcess (
169+ { command : process . execPath , args : [ '-e' , 'setInterval(() => {}, 1000)' ] } ,
170+ { id : 'child' , title : 'Child' } ,
171+ )
172+ await session . terminate ( )
173+ await waitUntil ( ( ) => {
174+ expect ( sinks . get ( 'child' ) ?. closed ) . toBe ( true )
175+ } )
176+ await session . restart ( )
177+ // Stream stays closed; no orphan output stream.
178+ expect ( sinks . get ( 'child' ) ?. closed ) . toBe ( true )
179+ } )
144180} )
145181
146182describe ( 'devframeTerminalHost interactive PTY sessions' , ( ) => {
0 commit comments