@@ -21,9 +21,18 @@ vi.mock('@/tools', () => ({
2121 executeTool : vi . fn ( ) ,
2222} ) )
2323
24+ vi . mock ( '@/executor/utils/block-data' , ( ) => ( {
25+ collectBlockData : vi . fn ( ( ) => ( {
26+ blockData : { 'source-block-1' : { value : 10 , text : 'hello' } } ,
27+ blockNameMapping : { 'Source Block' : 'source-block-1' } ,
28+ } ) ) ,
29+ } ) )
30+
31+ import { collectBlockData } from '@/executor/utils/block-data'
2432import { executeTool } from '@/tools'
2533
2634const mockExecuteTool = executeTool as ReturnType < typeof vi . fn >
35+ const mockCollectBlockData = collectBlockData as ReturnType < typeof vi . fn >
2736
2837/**
2938 * Simulates what the function_execute tool does when evaluating condition code
@@ -55,8 +64,6 @@ describe('ConditionBlockHandler', () => {
5564 let mockSourceBlock : SerializedBlock
5665 let mockTargetBlock1 : SerializedBlock
5766 let mockTargetBlock2 : SerializedBlock
58- let mockResolver : any
59- let mockPathTracker : any
6067
6168 beforeEach ( ( ) => {
6269 mockSourceBlock = {
@@ -113,18 +120,11 @@ describe('ConditionBlockHandler', () => {
113120 ] ,
114121 }
115122
116- mockResolver = {
117- resolveVariableReferences : vi . fn ( ( expr ) => expr ) ,
118- resolveBlockReferences : vi . fn ( ( expr ) => expr ) ,
119- resolveEnvVariables : vi . fn ( ( expr ) => expr ) ,
120- }
121-
122- mockPathTracker = { }
123-
124- handler = new ConditionBlockHandler ( mockPathTracker , mockResolver )
123+ handler = new ConditionBlockHandler ( )
125124
126125 mockContext = {
127126 workflowId : 'test-workflow-id' ,
127+ workspaceId : 'test-workspace-id' ,
128128 blockStates : new Map < string , BlockState > ( [
129129 [
130130 mockSourceBlock . id ,
@@ -137,7 +137,8 @@ describe('ConditionBlockHandler', () => {
137137 ] ) ,
138138 blockLogs : [ ] ,
139139 metadata : { duration : 0 } ,
140- environmentVariables : { } ,
140+ environmentVariables : { API_KEY : 'test-key' } ,
141+ workflowVariables : { userName : { name : 'userName' , value : 'john' , type : 'plain' } } ,
141142 decisions : { router : new Map ( ) , condition : new Map ( ) } ,
142143 loopExecutions : new Map ( ) ,
143144 executedBlocks : new Set ( [ mockSourceBlock . id ] ) ,
@@ -178,26 +179,41 @@ describe('ConditionBlockHandler', () => {
178179 selectedOption : 'cond1' ,
179180 }
180181
181- mockResolver . resolveVariableReferences . mockReturnValue ( 'context.value > 5' )
182- mockResolver . resolveBlockReferences . mockReturnValue ( 'context.value > 5' )
183- mockResolver . resolveEnvVariables . mockReturnValue ( 'context.value > 5' )
184-
185182 const result = await handler . execute ( mockContext , mockBlock , inputs )
186183
187- expect ( mockResolver . resolveVariableReferences ) . toHaveBeenCalledWith (
188- 'context.value > 5' ,
189- mockBlock
190- )
191- expect ( mockResolver . resolveBlockReferences ) . toHaveBeenCalledWith (
192- 'context.value > 5' ,
193- mockContext ,
194- mockBlock
195- )
196- expect ( mockResolver . resolveEnvVariables ) . toHaveBeenCalledWith ( 'context.value > 5' )
197184 expect ( result ) . toEqual ( expectedOutput )
198185 expect ( mockContext . decisions . condition . get ( mockBlock . id ) ) . toBe ( 'cond1' )
199186 } )
200187
188+ it ( 'should pass correct parameters to function_execute tool' , async ( ) => {
189+ const conditions = [
190+ { id : 'cond1' , title : 'if' , value : 'context.value > 5' } ,
191+ { id : 'else1' , title : 'else' , value : '' } ,
192+ ]
193+ const inputs = { conditions : JSON . stringify ( conditions ) }
194+
195+ await handler . execute ( mockContext , mockBlock , inputs )
196+
197+ expect ( mockExecuteTool ) . toHaveBeenCalledWith (
198+ 'function_execute' ,
199+ expect . objectContaining ( {
200+ code : expect . stringContaining ( 'context.value > 5' ) ,
201+ timeout : 5000 ,
202+ envVars : mockContext . environmentVariables ,
203+ workflowVariables : mockContext . workflowVariables ,
204+ blockData : { 'source-block-1' : { value : 10 , text : 'hello' } } ,
205+ blockNameMapping : { 'Source Block' : 'source-block-1' } ,
206+ _context : {
207+ workflowId : 'test-workflow-id' ,
208+ workspaceId : 'test-workspace-id' ,
209+ } ,
210+ } ) ,
211+ false ,
212+ false ,
213+ mockContext
214+ )
215+ } )
216+
201217 it ( 'should select the else path if other conditions fail' , async ( ) => {
202218 const conditions = [
203219 { id : 'cond1' , title : 'if' , value : 'context.value < 0' } , // Should fail (10 < 0 is false)
@@ -217,22 +233,8 @@ describe('ConditionBlockHandler', () => {
217233 selectedOption : 'else1' ,
218234 }
219235
220- mockResolver . resolveVariableReferences . mockReturnValue ( 'context.value < 0' )
221- mockResolver . resolveBlockReferences . mockReturnValue ( 'context.value < 0' )
222- mockResolver . resolveEnvVariables . mockReturnValue ( 'context.value < 0' )
223-
224236 const result = await handler . execute ( mockContext , mockBlock , inputs )
225237
226- expect ( mockResolver . resolveVariableReferences ) . toHaveBeenCalledWith (
227- 'context.value < 0' ,
228- mockBlock
229- )
230- expect ( mockResolver . resolveBlockReferences ) . toHaveBeenCalledWith (
231- 'context.value < 0' ,
232- mockContext ,
233- mockBlock
234- )
235- expect ( mockResolver . resolveEnvVariables ) . toHaveBeenCalledWith ( 'context.value < 0' )
236238 expect ( result ) . toEqual ( expectedOutput )
237239 expect ( mockContext . decisions . condition . get ( mockBlock . id ) ) . toBe ( 'else1' )
238240 } )
@@ -245,114 +247,13 @@ describe('ConditionBlockHandler', () => {
245247 )
246248 } )
247249
248- it ( 'should resolve references in conditions before evaluation' , async ( ) => {
249- const conditions = [
250- { id : 'cond1' , title : 'if' , value : '{{source-block-1.value}} > 5' } ,
251- { id : 'else1' , title : 'else' , value : '' } ,
252- ]
253- const inputs = { conditions : JSON . stringify ( conditions ) }
254-
255- mockResolver . resolveVariableReferences . mockReturnValue ( '{{source-block-1.value}} > 5' )
256- mockResolver . resolveBlockReferences . mockReturnValue ( '10 > 5' )
257- mockResolver . resolveEnvVariables . mockReturnValue ( '10 > 5' )
258-
259- await handler . execute ( mockContext , mockBlock , inputs )
260-
261- expect ( mockResolver . resolveVariableReferences ) . toHaveBeenCalledWith (
262- '{{source-block-1.value}} > 5' ,
263- mockBlock
264- )
265- expect ( mockResolver . resolveBlockReferences ) . toHaveBeenCalledWith (
266- '{{source-block-1.value}} > 5' ,
267- mockContext ,
268- mockBlock
269- )
270- expect ( mockResolver . resolveEnvVariables ) . toHaveBeenCalledWith ( '10 > 5' )
271- expect ( mockContext . decisions . condition . get ( mockBlock . id ) ) . toBe ( 'cond1' )
272- } )
273-
274- it ( 'should resolve variable references in conditions' , async ( ) => {
275- const conditions = [
276- { id : 'cond1' , title : 'if' , value : '<variable.userName> !== null' } ,
277- { id : 'else1' , title : 'else' , value : '' } ,
278- ]
279- const inputs = { conditions : JSON . stringify ( conditions ) }
280-
281- mockResolver . resolveVariableReferences . mockReturnValue ( '"john" !== null' )
282- mockResolver . resolveBlockReferences . mockReturnValue ( '"john" !== null' )
283- mockResolver . resolveEnvVariables . mockReturnValue ( '"john" !== null' )
284-
285- await handler . execute ( mockContext , mockBlock , inputs )
286-
287- expect ( mockResolver . resolveVariableReferences ) . toHaveBeenCalledWith (
288- '<variable.userName> !== null' ,
289- mockBlock
290- )
291- expect ( mockResolver . resolveBlockReferences ) . toHaveBeenCalledWith (
292- '"john" !== null' ,
293- mockContext ,
294- mockBlock
295- )
296- expect ( mockResolver . resolveEnvVariables ) . toHaveBeenCalledWith ( '"john" !== null' )
297- expect ( mockContext . decisions . condition . get ( mockBlock . id ) ) . toBe ( 'cond1' )
298- } )
299-
300- it ( 'should resolve environment variables in conditions' , async ( ) => {
301- const conditions = [
302- { id : 'cond1' , title : 'if' , value : '{{POOP}} === "hi"' } ,
303- { id : 'else1' , title : 'else' , value : '' } ,
304- ]
305- const inputs = { conditions : JSON . stringify ( conditions ) }
306-
307- mockResolver . resolveVariableReferences . mockReturnValue ( '{{POOP}} === "hi"' )
308- mockResolver . resolveBlockReferences . mockReturnValue ( '{{POOP}} === "hi"' )
309- mockResolver . resolveEnvVariables . mockReturnValue ( '"hi" === "hi"' )
310-
311- await handler . execute ( mockContext , mockBlock , inputs )
312-
313- expect ( mockResolver . resolveVariableReferences ) . toHaveBeenCalledWith (
314- '{{POOP}} === "hi"' ,
315- mockBlock
316- )
317- expect ( mockResolver . resolveBlockReferences ) . toHaveBeenCalledWith (
318- '{{POOP}} === "hi"' ,
319- mockContext ,
320- mockBlock
321- )
322- expect ( mockResolver . resolveEnvVariables ) . toHaveBeenCalledWith ( '{{POOP}} === "hi"' )
323- expect ( mockContext . decisions . condition . get ( mockBlock . id ) ) . toBe ( 'cond1' )
324- } )
325-
326- it ( 'should throw error if reference resolution fails' , async ( ) => {
327- const conditions = [
328- { id : 'cond1' , title : 'if' , value : '{{invalid-ref}}' } ,
329- { id : 'else1' , title : 'else' , value : '' } ,
330- ]
331- const inputs = { conditions : JSON . stringify ( conditions ) }
332-
333- const resolutionError = new Error ( 'Could not resolve reference: invalid-ref' )
334- mockResolver . resolveVariableReferences . mockImplementation ( ( ) => {
335- throw resolutionError
336- } )
337-
338- await expect ( handler . execute ( mockContext , mockBlock , inputs ) ) . rejects . toThrow (
339- 'Failed to resolve references in condition: Could not resolve reference: invalid-ref'
340- )
341- } )
342-
343250 it ( 'should handle evaluation errors gracefully' , async ( ) => {
344251 const conditions = [
345252 { id : 'cond1' , title : 'if' , value : 'context.nonExistentProperty.doSomething()' } ,
346253 { id : 'else1' , title : 'else' , value : '' } ,
347254 ]
348255 const inputs = { conditions : JSON . stringify ( conditions ) }
349256
350- mockResolver . resolveVariableReferences . mockReturnValue (
351- 'context.nonExistentProperty.doSomething()'
352- )
353- mockResolver . resolveBlockReferences . mockReturnValue ( 'context.nonExistentProperty.doSomething()' )
354- mockResolver . resolveEnvVariables . mockReturnValue ( 'context.nonExistentProperty.doSomething()' )
355-
356257 await expect ( handler . execute ( mockContext , mockBlock , inputs ) ) . rejects . toThrow (
357258 / E v a l u a t i o n e r r o r i n c o n d i t i o n " i f " .* d o S o m e t h i n g /
358259 )
@@ -367,10 +268,6 @@ describe('ConditionBlockHandler', () => {
367268 blockStates : new Map < string , BlockState > ( ) ,
368269 }
369270
370- mockResolver . resolveVariableReferences . mockReturnValue ( 'true' )
371- mockResolver . resolveBlockReferences . mockReturnValue ( 'true' )
372- mockResolver . resolveEnvVariables . mockReturnValue ( 'true' )
373-
374271 const result = await handler . execute ( contextWithoutSource , mockBlock , inputs )
375272
376273 expect ( result ) . toHaveProperty ( 'conditionResult' , true )
@@ -383,10 +280,6 @@ describe('ConditionBlockHandler', () => {
383280
384281 mockContext . workflow ! . blocks = [ mockSourceBlock , mockBlock , mockTargetBlock2 ]
385282
386- mockResolver . resolveVariableReferences . mockReturnValue ( 'true' )
387- mockResolver . resolveBlockReferences . mockReturnValue ( 'true' )
388- mockResolver . resolveEnvVariables . mockReturnValue ( 'true' )
389-
390283 await expect ( handler . execute ( mockContext , mockBlock , inputs ) ) . rejects . toThrow (
391284 `Target block ${ mockTargetBlock1 . id } not found`
392285 )
@@ -408,16 +301,6 @@ describe('ConditionBlockHandler', () => {
408301 } ,
409302 ]
410303
411- mockResolver . resolveVariableReferences
412- . mockReturnValueOnce ( 'false' )
413- . mockReturnValueOnce ( 'context.value === 99' )
414- mockResolver . resolveBlockReferences
415- . mockReturnValueOnce ( 'false' )
416- . mockReturnValueOnce ( 'context.value === 99' )
417- mockResolver . resolveEnvVariables
418- . mockReturnValueOnce ( 'false' )
419- . mockReturnValueOnce ( 'context.value === 99' )
420-
421304 const result = await handler . execute ( mockContext , mockBlock , inputs )
422305
423306 expect ( ( result as any ) . conditionResult ) . toBe ( false )
@@ -433,13 +316,38 @@ describe('ConditionBlockHandler', () => {
433316 ]
434317 const inputs = { conditions : JSON . stringify ( conditions ) }
435318
436- mockResolver . resolveVariableReferences . mockReturnValue ( 'context.item === "apple"' )
437- mockResolver . resolveBlockReferences . mockReturnValue ( 'context.item === "apple"' )
438- mockResolver . resolveEnvVariables . mockReturnValue ( 'context.item === "apple"' )
439-
440319 const result = await handler . execute ( mockContext , mockBlock , inputs )
441320
442321 expect ( mockContext . decisions . condition . get ( mockBlock . id ) ) . toBe ( 'else1' )
443322 expect ( ( result as any ) . selectedOption ) . toBe ( 'else1' )
444323 } )
324+
325+ it ( 'should use collectBlockData to gather block state' , async ( ) => {
326+ const conditions = [
327+ { id : 'cond1' , title : 'if' , value : 'true' } ,
328+ { id : 'else1' , title : 'else' , value : '' } ,
329+ ]
330+ const inputs = { conditions : JSON . stringify ( conditions ) }
331+
332+ await handler . execute ( mockContext , mockBlock , inputs )
333+
334+ expect ( mockCollectBlockData ) . toHaveBeenCalledWith ( mockContext )
335+ } )
336+
337+ it ( 'should handle function_execute tool failure' , async ( ) => {
338+ const conditions = [
339+ { id : 'cond1' , title : 'if' , value : 'context.value > 5' } ,
340+ { id : 'else1' , title : 'else' , value : '' } ,
341+ ]
342+ const inputs = { conditions : JSON . stringify ( conditions ) }
343+
344+ mockExecuteTool . mockResolvedValueOnce ( {
345+ success : false ,
346+ error : 'Execution timeout' ,
347+ } )
348+
349+ await expect ( handler . execute ( mockContext , mockBlock , inputs ) ) . rejects . toThrow (
350+ / E v a l u a t i o n e r r o r i n c o n d i t i o n " i f " .* E x e c u t i o n t i m e o u t /
351+ )
352+ } )
445353} )
0 commit comments