@@ -6,6 +6,11 @@ import type { ResolutionContext } from './reference'
66
77vi . mock ( '@sim/logger' , ( ) => loggerMock )
88
9+ interface BlockDef {
10+ id : string
11+ name : string
12+ }
13+
914/**
1015 * Creates a minimal workflow for testing.
1116 */
@@ -18,7 +23,8 @@ function createTestWorkflow(
1823 distribution ?: any
1924 parallelType ?: 'count' | 'collection'
2025 }
21- > = { }
26+ > = { } ,
27+ blockDefs : BlockDef [ ] = [ ]
2228) {
2329 const normalizedParallels : Record <
2430 string ,
@@ -37,9 +43,18 @@ function createTestWorkflow(
3743 parallelType : parallel . parallelType ,
3844 }
3945 }
46+ const blocks = blockDefs . map ( ( b ) => ( {
47+ id : b . id ,
48+ position : { x : 0 , y : 0 } ,
49+ config : { tool : 'test' , params : { } } ,
50+ inputs : { } ,
51+ outputs : { } ,
52+ metadata : { id : 'function' , name : b . name } ,
53+ enabled : true ,
54+ } ) )
4055 return {
4156 version : '1.0' ,
42- blocks : [ ] ,
57+ blocks,
4358 connections : [ ] ,
4459 loops : { } ,
4560 parallels : normalizedParallels ,
@@ -63,13 +78,16 @@ function createParallelScope(items: any[]) {
6378 */
6479function createTestContext (
6580 currentNodeId : string ,
66- parallelExecutions ?: Map < string , any >
81+ parallelExecutions ?: Map < string , any > ,
82+ blockOutputs ?: Record < string , any >
6783) : ResolutionContext {
6884 return {
6985 executionContext : {
7086 parallelExecutions : parallelExecutions ?? new Map ( ) ,
7187 } ,
72- executionState : { } ,
88+ executionState : {
89+ getBlockOutput : ( id : string ) => blockOutputs ?. [ id ] ,
90+ } ,
7391 currentNodeId,
7492 } as ResolutionContext
7593}
@@ -383,4 +401,119 @@ describe('ParallelResolver', () => {
383401 expect ( resolver . resolve ( '<parallel.currentItem>' , createTestContext ( 'block-3₍1₎' ) ) ) . toBe ( 'p4' )
384402 } )
385403 } )
404+
405+ describe ( 'named parallel references' , ( ) => {
406+ it . concurrent ( 'should resolve result from anywhere after parallel completes' , ( ) => {
407+ const workflow = createTestWorkflow (
408+ { 'parallel-1' : { nodes : [ 'block-1' ] , distribution : [ 'a' , 'b' ] } } ,
409+ [ { id : 'parallel-1' , name : 'Parallel 1' } ]
410+ )
411+ const resolver = new ParallelResolver ( workflow )
412+ const results = [ [ { response : 'a' } ] , [ { response : 'b' } ] ]
413+ const ctx = createTestContext ( 'block-outside' , new Map ( ) , {
414+ 'parallel-1' : { results } ,
415+ } )
416+
417+ expect ( resolver . resolve ( '<parallel1.result>' , ctx ) ) . toEqual ( results )
418+ expect ( resolver . resolve ( '<parallel1.results>' , ctx ) ) . toEqual ( results )
419+ } )
420+
421+ it . concurrent ( 'should resolve result with nested path' , ( ) => {
422+ const workflow = createTestWorkflow (
423+ { 'parallel-1' : { nodes : [ 'block-1' ] , distribution : [ 'a' , 'b' ] } } ,
424+ [ { id : 'parallel-1' , name : 'Parallel 1' } ]
425+ )
426+ const resolver = new ParallelResolver ( workflow )
427+ const results = [ [ { response : 'a' } ] , [ { response : 'b' } ] ]
428+ const ctx = createTestContext ( 'block-outside' , new Map ( ) , {
429+ 'parallel-1' : { results } ,
430+ } )
431+
432+ expect ( resolver . resolve ( '<parallel1.result.0>' , ctx ) ) . toEqual ( [ { response : 'a' } ] )
433+ expect ( resolver . resolve ( '<parallel1.result.1.0.response>' , ctx ) ) . toBe ( 'b' )
434+ } )
435+
436+ it . concurrent ( 'should resolve result with empty currentNodeId' , ( ) => {
437+ const workflow = createTestWorkflow (
438+ { 'parallel-1' : { nodes : [ 'block-1' ] , distribution : [ 'a' , 'b' ] } } ,
439+ [ { id : 'parallel-1' , name : 'Parallel 1' } ]
440+ )
441+ const resolver = new ParallelResolver ( workflow )
442+ const results = [ [ { output : 'x' } ] , [ { output : 'y' } ] ]
443+ const ctx = createTestContext ( '' , new Map ( ) , {
444+ 'parallel-1' : { results } ,
445+ } )
446+
447+ expect ( resolver . resolve ( '<parallel1.results>' , ctx ) ) . toEqual ( results )
448+ } )
449+
450+ it . concurrent ( 'should return undefined when no output stored yet' , ( ) => {
451+ const workflow = createTestWorkflow (
452+ { 'parallel-1' : { nodes : [ 'block-1' ] , distribution : [ 'a' ] } } ,
453+ [ { id : 'parallel-1' , name : 'Parallel 1' } ]
454+ )
455+ const resolver = new ParallelResolver ( workflow )
456+ const ctx = createTestContext ( 'block-outside' , new Map ( ) )
457+
458+ expect ( resolver . resolve ( '<parallel1.results>' , ctx ) ) . toBeUndefined ( )
459+ } )
460+
461+ it . concurrent ( 'should resolve iteration properties via named reference' , ( ) => {
462+ const workflow = createTestWorkflow (
463+ {
464+ 'parallel-1' : {
465+ nodes : [ 'block-1' ] ,
466+ distribution : [ 'x' , 'y' , 'z' ] ,
467+ parallelType : 'collection' ,
468+ } ,
469+ } ,
470+ [ { id : 'parallel-1' , name : 'Parallel 1' } ]
471+ )
472+ const resolver = new ParallelResolver ( workflow )
473+ const ctx = createTestContext ( 'block-1₍1₎' )
474+
475+ expect ( resolver . resolve ( '<parallel1.index>' , ctx ) ) . toBe ( 1 )
476+ expect ( resolver . resolve ( '<parallel1.currentItem>' , ctx ) ) . toBe ( 'y' )
477+ expect ( resolver . resolve ( '<parallel1.items>' , ctx ) ) . toEqual ( [ 'x' , 'y' , 'z' ] )
478+ } )
479+
480+ it . concurrent ( 'should throw InvalidFieldError for unknown property on named ref' , ( ) => {
481+ const workflow = createTestWorkflow (
482+ {
483+ 'parallel-1' : {
484+ nodes : [ 'block-1' ] ,
485+ distribution : [ 'a' ] ,
486+ parallelType : 'collection' ,
487+ } ,
488+ } ,
489+ [ { id : 'parallel-1' , name : 'Parallel 1' } ]
490+ )
491+ const resolver = new ParallelResolver ( workflow )
492+ const ctx = createTestContext ( 'block-1₍0₎' )
493+
494+ expect ( ( ) => resolver . resolve ( '<parallel1.unknownProp>' , ctx ) ) . toThrow ( InvalidFieldError )
495+ } )
496+
497+ it . concurrent ( 'should not resolve named ref when no matching block exists' , ( ) => {
498+ const workflow = createTestWorkflow ( { 'parallel-1' : { nodes : [ 'block-1' ] } } , [
499+ { id : 'parallel-1' , name : 'Parallel 1' } ,
500+ ] )
501+ const resolver = new ParallelResolver ( workflow )
502+ expect ( resolver . canResolve ( '<parallel99.index>' ) ) . toBe ( false )
503+ } )
504+
505+ it . concurrent ( 'should resolve generic parallel results from inside a branch' , ( ) => {
506+ const workflow = createTestWorkflow ( {
507+ 'parallel-1' : { nodes : [ 'block-1' ] , distribution : [ 'a' , 'b' ] } ,
508+ } )
509+ const resolver = new ParallelResolver ( workflow )
510+ const results = [ [ { response : 'a' } ] , [ { response : 'b' } ] ]
511+ const ctx = createTestContext ( 'block-1₍0₎' , new Map ( ) , {
512+ 'parallel-1' : { results } ,
513+ } )
514+
515+ expect ( resolver . resolve ( '<parallel.results>' , ctx ) ) . toEqual ( results )
516+ expect ( resolver . resolve ( '<parallel.result>' , ctx ) ) . toEqual ( results )
517+ } )
518+ } )
386519} )
0 commit comments