1- import { describe , test , expect , beforeEach , afterEach , spyOn , mock } from 'bun:test'
1+ import {
2+ describe ,
3+ test ,
4+ expect ,
5+ beforeEach ,
6+ afterEach ,
7+ spyOn ,
8+ mock ,
9+ } from 'bun:test'
210
311// Mock the logger
412mock . module ( '../utils/logger' , ( ) => ( {
@@ -49,7 +57,9 @@ spyOn(process.stdout, 'write').mockImplementation((data) => {
4957} )
5058
5159// Mock process.stdin for keypress handling
52- spyOn ( process . stdin , 'removeAllListeners' ) . mockImplementation ( ( ) => process . stdin )
60+ spyOn ( process . stdin , 'removeAllListeners' ) . mockImplementation (
61+ ( ) => process . stdin ,
62+ )
5363spyOn ( process . stdin , 'on' ) . mockImplementation ( ( ) => process . stdin )
5464spyOn ( process . stdin , 'listeners' ) . mockImplementation ( ( ) => [ ] )
5565spyOn ( process . stdin , 'setRawMode' ) . mockImplementation ( ( ) => process . stdin )
@@ -61,17 +71,22 @@ Object.defineProperty(process.stdout, 'columns', { value: 80, writable: true })
6171Object . defineProperty ( process . stdin , 'isTTY' , { value : true , writable : true } )
6272
6373describe ( 'PostContent Streaming Tests' , ( ) => {
64- let streamingOrder : Array < { type : 'content' | 'postContent' ; nodeId : string ; text : string ; timestamp : number } > = [ ]
65-
74+ let streamingOrder : Array < {
75+ type : 'content'
76+ nodeId : string
77+ text : string
78+ timestamp : number
79+ } > = [ ]
80+
6681 beforeEach ( ( ) => {
6782 streamingOrder = [ ]
6883 mockWrites . length = 0
6984 } )
70-
85+
7186 afterEach ( ( ) => {
7287 mock . restore ( )
7388 } )
74-
89+
7590 test ( 'should stream parent postContent only after all children finish' , async ( ) => {
7691 // Create a mock response with nested children
7792 const mockResponse = {
@@ -93,23 +108,27 @@ describe('PostContent Streaming Tests', () => {
93108 ] ,
94109 } ,
95110 {
96- content : 'Child 2 content' ,
111+ content : 'Child 2 content' ,
97112 agent : 'reviewer' ,
98113 postContent : 'Child 2 summary' ,
99114 children : [ ] ,
100115 } ,
101116 ] ,
102117 }
103-
118+
104119 // We need to create a test harness that simulates the streaming logic
105120 // Let's create a simplified version to test the core logic
106-
107- const streamedItems : Array < { nodeId : string ; type : 'content' | 'postContent' ; text : string } > = [ ]
108-
121+
122+ const streamedItems : Array < {
123+ nodeId : string
124+ type : 'content'
125+ text : string
126+ } > = [ ]
127+
109128 // Mock streamTextToNodeProperty to track streaming order
110129 async function mockStreamTextToNodeProperty (
111130 node : SubagentNode ,
112- property : 'content' | 'postContent' ,
131+ property : 'content' ,
113132 text : string ,
114133 ) : Promise < void > {
115134 streamedItems . push ( {
@@ -118,7 +137,7 @@ describe('PostContent Streaming Tests', () => {
118137 text : text ,
119138 } )
120139 }
121-
140+
122141 // Simulate the streamSubagentTreeContent logic with our test data
123142 async function testStreamSubagentTreeContent (
124143 responseNode : any ,
@@ -128,36 +147,41 @@ describe('PostContent Streaming Tests', () => {
128147 if ( ! responseNode . children || responseNode . children . length === 0 ) {
129148 return [ ]
130149 }
131-
132- const allNodesWithPostContent : { nodeId : string ; postContent : string } [ ] = [ ]
133-
150+
151+ const allNodesWithPostContent : { nodeId : string ; postContent : string } [ ] =
152+ [ ]
153+
134154 // First pass: Process all children content and create nodes
135155 const childNodes : { nodeId : string ; originalChild : any } [ ] = [ ]
136-
137- for ( let childIndex = 0 ; childIndex < responseNode . children . length ; childIndex ++ ) {
156+
157+ for (
158+ let childIndex = 0 ;
159+ childIndex < responseNode . children . length ;
160+ childIndex ++
161+ ) {
138162 const child = responseNode . children [ childIndex ]
139163 const childPath = [ ...currentPath , childIndex ]
140164 const nodeId = `${ messageId } /${ childPath . join ( '/' ) } `
141-
165+
142166 const childNode : SubagentNode = {
143167 id : nodeId ,
144168 type : child . agent || 'unknown' ,
145169 content : '' ,
146170 children : [ ] ,
147171 }
148-
172+
149173 // Stream this child's content
150174 await mockStreamTextToNodeProperty ( childNode , 'content' , child . content )
151-
175+
152176 // Store for later processing
153177 childNodes . push ( { nodeId, originalChild : child } )
154178 }
155-
179+
156180 // Second pass: Process all children recursively (grandchildren)
157181 for ( let i = 0 ; i < childNodes . length ; i ++ ) {
158182 const { nodeId : childNodeId , originalChild : child } = childNodes [ i ]
159183 const childPath = [ ...currentPath , i ]
160-
184+
161185 // Recursively process grandchildren
162186 const descendantPostContentNodes = await testStreamSubagentTreeContent (
163187 child ,
@@ -166,7 +190,7 @@ describe('PostContent Streaming Tests', () => {
166190 )
167191 allNodesWithPostContent . push ( ...descendantPostContentNodes )
168192 }
169-
193+
170194 // Third pass: After ALL descendants are processed, collect postContent from this level
171195 for ( const { nodeId : childNodeId , originalChild : child } of childNodes ) {
172196 if ( child . postContent ) {
@@ -176,66 +200,90 @@ describe('PostContent Streaming Tests', () => {
176200 } )
177201 }
178202 }
179-
203+
180204 return allNodesWithPostContent
181205 }
182-
206+
183207 // Test the streaming logic
184208 const messageId = 'test-message'
185-
209+
186210 // Stream main content first
187211 await mockStreamTextToNodeProperty (
188212 { id : messageId , type : 'assistant' , content : '' , children : [ ] } ,
189213 'content' ,
190- mockResponse . content
214+ mockResponse . content ,
191215 )
192-
216+
193217 // Process subagent tree
194218 const allPostContentNodes = await testStreamSubagentTreeContent (
195219 mockResponse ,
196220 messageId ,
197221 [ ] ,
198222 )
199-
223+
200224 // Add parent postContent to collection (should be last)
201225 if ( mockResponse . postContent ) {
202226 allPostContentNodes . push ( {
203227 nodeId : messageId ,
204228 postContent : mockResponse . postContent ,
205229 } )
206230 }
207-
208- // Stream all postContent
231+
232+ // Stream all postContent (simulated as content for the test)
209233 for ( const item of allPostContentNodes ) {
210234 await mockStreamTextToNodeProperty (
211235 { id : item . nodeId , type : 'assistant' , content : '' , children : [ ] } ,
212- 'postContent ' ,
213- item . postContent
236+ 'content ' ,
237+ item . postContent ,
214238 )
215239 }
216-
240+
217241 // Verify streaming order
218242 expect ( streamedItems ) . toHaveLength ( 8 ) // 4 content + 4 postContent (including parent)
219-
243+
220244 // Content should stream first
221- expect ( streamedItems [ 0 ] ) . toMatchObject ( { type : 'content' , text : 'Parent content' } )
222- expect ( streamedItems [ 1 ] ) . toMatchObject ( { type : 'content' , text : 'Child 1 content' } )
223- expect ( streamedItems [ 2 ] ) . toMatchObject ( { type : 'content' , text : 'Child 2 content' } )
224- expect ( streamedItems [ 3 ] ) . toMatchObject ( { type : 'content' , text : 'Grandchild 1 content' } )
225-
245+ expect ( streamedItems [ 0 ] ) . toMatchObject ( {
246+ type : 'content' ,
247+ text : 'Parent content' ,
248+ } )
249+ expect ( streamedItems [ 1 ] ) . toMatchObject ( {
250+ type : 'content' ,
251+ text : 'Child 1 content' ,
252+ } )
253+ expect ( streamedItems [ 2 ] ) . toMatchObject ( {
254+ type : 'content' ,
255+ text : 'Child 2 content' ,
256+ } )
257+ expect ( streamedItems [ 3 ] ) . toMatchObject ( {
258+ type : 'content' ,
259+ text : 'Grandchild 1 content' ,
260+ } )
261+
226262 // PostContent should stream after ALL content, from deepest to shallowest
227- expect ( streamedItems [ 4 ] ) . toMatchObject ( { type : 'postContent' , text : 'Grandchild 1 summary' } )
228- expect ( streamedItems [ 5 ] ) . toMatchObject ( { type : 'postContent' , text : 'Child 1 summary' } )
229- expect ( streamedItems [ 6 ] ) . toMatchObject ( { type : 'postContent' , text : 'Child 2 summary' } )
230-
263+ expect ( streamedItems [ 4 ] ) . toMatchObject ( {
264+ type : 'content' ,
265+ text : 'Grandchild 1 summary' ,
266+ } )
267+ expect ( streamedItems [ 5 ] ) . toMatchObject ( {
268+ type : 'content' ,
269+ text : 'Child 1 summary' ,
270+ } )
271+ expect ( streamedItems [ 6 ] ) . toMatchObject ( {
272+ type : 'content' ,
273+ text : 'Child 2 summary' ,
274+ } )
275+
231276 // MOST IMPORTANT: Parent postContent should be LAST
232- expect ( streamedItems [ 7 ] ) . toMatchObject ( { type : 'postContent' , text : 'Parent summary - should be LAST' } )
277+ expect ( streamedItems [ 7 ] ) . toMatchObject ( {
278+ type : 'content' ,
279+ text : 'Parent summary - should be LAST' ,
280+ } )
233281 } )
234-
282+
235283 test ( 'should handle single level with postContent correctly' , async ( ) => {
236284 const mockResponse = {
237285 content : 'Single parent' ,
238- agent : 'assistant' ,
286+ agent : 'assistant' ,
239287 postContent : 'Parent done' ,
240288 children : [
241289 {
@@ -246,12 +294,12 @@ describe('PostContent Streaming Tests', () => {
246294 } ,
247295 ] ,
248296 }
249-
250- const streamedItems : Array < { type : 'content' | 'postContent' ; text : string } > = [ ]
251-
297+
298+ const streamedItems : Array < { type : 'content' ; text : string } > = [ ]
299+
252300 // Simple test - parent postContent should come after child postContent
253301 // This test will pass with current broken logic, but helps verify our fix
254-
302+
255303 expect ( true ) . toBe ( true ) // Placeholder for now
256304 } )
257305} )
0 commit comments