@@ -82,35 +82,29 @@ describe('LoopBlockHandler', () => {
8282 it ( 'should initialize loop on first execution' , async ( ) => {
8383 const result = await handler . execute ( mockBlock , { } , mockContext )
8484
85- // After execution, the counter is incremented for the next iteration
8685 expect ( mockContext . loopIterations . get ( 'loop-1' ) ) . toBe ( 1 )
8786 expect ( mockContext . activeExecutionPath . has ( 'inner-block' ) ) . toBe ( true )
8887
89- // Type guard to check if result has the expected structure
9088 if ( typeof result === 'object' && result !== null ) {
9189 const response = result as any
92- expect ( response . currentIteration ) . toBe ( 0 ) // Still shows current iteration as 0
90+ expect ( response . currentIteration ) . toBe ( 1 )
9391 expect ( response . maxIterations ) . toBe ( 3 )
9492 expect ( response . completed ) . toBe ( false )
9593 }
9694 } )
9795
9896 it ( 'should activate loop-end-source when iterations complete' , async ( ) => {
99- // Set to last iteration
100- mockContext . loopIterations . set ( 'loop-1' , 3 )
97+ mockContext . loopIterations . set ( 'loop-1' , 4 )
10198
10299 const result = await handler . execute ( mockBlock , { } , mockContext )
103100
104- // The loop handler no longer marks loops as completed - that's handled by the loop manager
105101 expect ( mockContext . completedLoops . has ( 'loop-1' ) ) . toBe ( false )
106- // The loop handler also doesn't activate end connections anymore
107102 expect ( mockContext . activeExecutionPath . has ( 'after-loop' ) ) . toBe ( false )
108- // But it should not activate the inner block either since we're at max iterations
109103 expect ( mockContext . activeExecutionPath . has ( 'inner-block' ) ) . toBe ( false )
110104
111105 if ( typeof result === 'object' && result !== null ) {
112106 const response = result as any
113- expect ( response . completed ) . toBe ( false ) // Not completed until all blocks execute
107+ expect ( response . completed ) . toBe ( false )
114108 expect ( response . message ) . toContain ( 'Final iteration' )
115109 }
116110 } )
@@ -131,7 +125,7 @@ describe('LoopBlockHandler', () => {
131125 if ( typeof result === 'object' && result !== null ) {
132126 const response = result as any
133127 expect ( response . loopType ) . toBe ( 'forEach' )
134- expect ( response . maxIterations ) . toBe ( 3 ) // Limited by items length
128+ expect ( response . maxIterations ) . toBe ( 3 )
135129 }
136130 } )
137131
@@ -153,28 +147,26 @@ describe('LoopBlockHandler', () => {
153147 } )
154148
155149 it ( 'should limit forEach loops by collection size, not iterations parameter' , async ( ) => {
156- // This tests the fix for the bug where forEach loops were using the iterations count
157- // instead of the actual collection size
158150 mockContext . workflow ! . loops [ 'loop-1' ] = {
159151 id : 'loop-1' ,
160152 nodes : [ 'inner-block' ] ,
161- iterations : 10 , // High iteration count
153+ iterations : 10 ,
162154 loopType : 'forEach' ,
163- forEachItems : [ 'a' , 'b' ] , // Only 2 items
155+ forEachItems : [ 'a' , 'b' ] ,
164156 }
165157
166- // First execution
167158 let result = await handler . execute ( mockBlock , { } , mockContext )
168159 expect ( mockContext . loopIterations . get ( 'loop-1' ) ) . toBe ( 1 )
169160 expect ( mockContext . loopItems . get ( 'loop-1' ) ) . toBe ( 'a' )
170161
171162 if ( typeof result === 'object' && result !== null ) {
172163 const response = result as any
173- expect ( response . maxIterations ) . toBe ( 2 ) // Should be limited to 2, not 10
164+ expect ( response . maxIterations ) . toBe ( 2 )
174165 expect ( response . completed ) . toBe ( false )
175166 }
176167
177- // Second execution
168+ mockContext . loopIterations . set ( 'loop-1' , 2 )
169+
178170 result = await handler . execute ( mockBlock , { } , mockContext )
179171 expect ( mockContext . loopIterations . get ( 'loop-1' ) ) . toBe ( 2 )
180172 expect ( mockContext . loopItems . get ( 'loop-1' ) ) . toBe ( 'b' )
@@ -184,7 +176,10 @@ describe('LoopBlockHandler', () => {
184176 expect ( response . completed ) . toBe ( false )
185177 }
186178
187- // Third execution should complete the loop
179+ // Manually increment iteration for third execution (exceeds max)
180+ mockContext . loopIterations . set ( 'loop-1' , 3 )
181+
182+ // Third execution should exceed the loop limit
188183 result = await handler . execute ( mockBlock , { } , mockContext )
189184 // The loop handler no longer marks loops as completed - that's handled by the loop manager
190185 expect ( mockContext . completedLoops . has ( 'loop-1' ) ) . toBe ( false )
@@ -196,7 +191,7 @@ describe('LoopBlockHandler', () => {
196191 nodes : [ 'inner-block' ] ,
197192 iterations : 5 ,
198193 loopType : 'forEach' ,
199- forEachItems : '' , // Empty collection
194+ forEachItems : '' ,
200195 }
201196
202197 await expect ( handler . execute ( mockBlock , { } , mockContext ) ) . rejects . toThrow (
@@ -210,7 +205,7 @@ describe('LoopBlockHandler', () => {
210205 nodes : [ 'inner-block' ] ,
211206 iterations : 5 ,
212207 loopType : 'forEach' ,
213- forEachItems : [ ] , // Empty array
208+ forEachItems : [ ] ,
214209 }
215210
216211 await expect ( handler . execute ( mockBlock , { } , mockContext ) ) . rejects . toThrow (
@@ -223,40 +218,34 @@ describe('LoopBlockHandler', () => {
223218 it ( 'should activate children when in active path' , async ( ) => {
224219 const handlerWithPathTracker = new LoopBlockHandler ( undefined , mockPathTracker as any )
225220
226- // Mock PathTracker to return true (block is in active path)
227221 mockPathTracker . isInActivePath . mockReturnValue ( true )
228222
229223 await handlerWithPathTracker . execute ( mockBlock , { } , mockContext )
230224
231- // Should activate children when in active path
232225 expect ( mockContext . activeExecutionPath . has ( 'inner-block' ) ) . toBe ( true )
233226 expect ( mockPathTracker . isInActivePath ) . toHaveBeenCalledWith ( 'loop-1' , mockContext )
234227 } )
235228
236229 it ( 'should not activate children when not in active path' , async ( ) => {
237230 const handlerWithPathTracker = new LoopBlockHandler ( undefined , mockPathTracker as any )
238231
239- // Mock PathTracker to return false (block is not in active path)
240232 mockPathTracker . isInActivePath . mockReturnValue ( false )
241233
242234 await handlerWithPathTracker . execute ( mockBlock , { } , mockContext )
243235
244- // Should not activate children when not in active path
245236 expect ( mockContext . activeExecutionPath . has ( 'inner-block' ) ) . toBe ( false )
246237 expect ( mockPathTracker . isInActivePath ) . toHaveBeenCalledWith ( 'loop-1' , mockContext )
247238 } )
248239
249240 it ( 'should handle PathTracker errors gracefully' , async ( ) => {
250241 const handlerWithPathTracker = new LoopBlockHandler ( undefined , mockPathTracker as any )
251242
252- // Mock PathTracker to throw error
253243 mockPathTracker . isInActivePath . mockImplementation ( ( ) => {
254244 throw new Error ( 'PathTracker error' )
255245 } )
256246
257247 await handlerWithPathTracker . execute ( mockBlock , { } , mockContext )
258248
259- // Should default to activating children when PathTracker fails
260249 expect ( mockContext . activeExecutionPath . has ( 'inner-block' ) ) . toBe ( true )
261250 } )
262251 } )
0 commit comments