@@ -958,7 +958,15 @@ steps:
958958// split into individual KEY=VALUE pairs after expansion.
959959// See: https://github.com/dagu-org/dagu/issues/1658
960960func TestIssue1658_ParallelCallExpandedParamsSplitting (t * testing.T ) {
961- const dagContent = `steps:
961+ cases := []struct {
962+ name string
963+ dag string
964+ expectedSubRuns int
965+ verify func (* testing.T , parallelResultsPayload )
966+ }{
967+ {
968+ name : "positional_expands_to_multiple_params" ,
969+ dag : `steps:
962970 - command: |
963971 echo '[{"name": "test", "extra": "A=1 B=2"}]'
964972 output: ITEMS
@@ -982,34 +990,160 @@ steps:
982990 fi
983991 echo "OK: NAME=$NAME A=$A B=$B"
984992 output: CHECK_RESULT
985- `
993+ ` ,
994+ expectedSubRuns : 1 ,
995+ verify : func (t * testing.T , results parallelResultsPayload ) {
996+ require .Equal (t , 1 , results .Summary .Succeeded )
997+ outputs := collectOutputs (results .Outputs , "CHECK_RESULT" )
998+ require .Len (t , outputs , 1 )
999+ require .Contains (t , outputs [0 ], "OK: NAME=test A=1 B=2" )
1000+ },
1001+ },
1002+ {
1003+ name : "multiple_items_different_expansions" ,
1004+ dag : `steps:
1005+ - command: |
1006+ echo '[{"name":"alpha","extra":"X=10 Y=20"}, {"name":"beta","extra":"X=30 Y=40"}]'
1007+ output: ITEMS
9861008
987- th := test .Setup (t )
988- dag := th .DAG (t , dagContent )
989- agent := dag .Agent ()
990- err := agent .Run (agent .Context )
991- require .NoError (t , err )
992- dag .AssertLatestStatus (t , core .Succeeded )
1009+ - call: child-multi-expand
1010+ parallel:
1011+ items: ${ITEMS}
1012+ params: "NAME=${ITEM.name} ${ITEM.extra}"
1013+ output: RESULTS
1014+ ---
1015+ name: child-multi-expand
1016+ params:
1017+ - NAME: ""
1018+ - X: ""
1019+ - Y: ""
1020+ steps:
1021+ - script: |
1022+ if [ -z "$X" ] || [ -z "$Y" ]; then
1023+ echo "FAIL: NAME='$NAME' X='$X' Y='$Y'"
1024+ exit 1
1025+ fi
1026+ echo "OK: NAME=$NAME X=$X Y=$Y"
1027+ output: CHECK_RESULT
1028+ ` ,
1029+ expectedSubRuns : 2 ,
1030+ verify : func (t * testing.T , results parallelResultsPayload ) {
1031+ require .Equal (t , 2 , results .Summary .Succeeded )
1032+ outputs := collectOutputs (results .Outputs , "CHECK_RESULT" )
1033+ require .Len (t , outputs , 2 )
1034+
1035+ var foundAlpha , foundBeta bool
1036+ for _ , out := range outputs {
1037+ if strings .Contains (out , "OK: NAME=alpha X=10 Y=20" ) {
1038+ foundAlpha = true
1039+ }
1040+ if strings .Contains (out , "OK: NAME=beta X=30 Y=40" ) {
1041+ foundBeta = true
1042+ }
1043+ }
1044+ require .True (t , foundAlpha , "expected output for alpha item" )
1045+ require .True (t , foundBeta , "expected output for beta item" )
1046+ },
1047+ },
1048+ {
1049+ name : "named_param_with_spaces_preserved" ,
1050+ dag : `steps:
1051+ - command: |
1052+ echo '[{"label": "hello world", "id": "1"}]'
1053+ output: ITEMS
9931054
994- dagStatus , statusErr := dag .DAGRunMgr .GetLatestStatus (dag .Context , dag .DAG )
995- require .NoError (t , statusErr )
1055+ - call: child-named-spaces
1056+ parallel:
1057+ items: ${ITEMS}
1058+ params: "LABEL=${ITEM.label} ID=${ITEM.id}"
1059+ output: RESULTS
1060+ ---
1061+ name: child-named-spaces
1062+ params:
1063+ - LABEL: ""
1064+ - ID: ""
1065+ steps:
1066+ - script: |
1067+ if [ "$LABEL" != "hello world" ]; then
1068+ echo "FAIL: LABEL='$LABEL' (expected 'hello world')"
1069+ exit 1
1070+ fi
1071+ if [ "$ID" != "1" ]; then
1072+ echo "FAIL: ID='$ID' (expected '1')"
1073+ exit 1
1074+ fi
1075+ echo "OK: LABEL=$LABEL ID=$ID"
1076+ output: CHECK_RESULT
1077+ ` ,
1078+ expectedSubRuns : 1 ,
1079+ verify : func (t * testing.T , results parallelResultsPayload ) {
1080+ require .Equal (t , 1 , results .Summary .Succeeded )
1081+ outputs := collectOutputs (results .Outputs , "CHECK_RESULT" )
1082+ require .Len (t , outputs , 1 )
1083+ require .Contains (t , outputs [0 ], "OK: LABEL=hello world ID=1" )
1084+ },
1085+ },
1086+ {
1087+ name : "positional_single_value_no_split" ,
1088+ dag : `steps:
1089+ - command: |
1090+ echo '[{"tag": "simple"}]'
1091+ output: ITEMS
9961092
997- require .Greater (t , len (dagStatus .Nodes ), 1 , "expected at least 2 nodes" )
998- parallelNode := dagStatus .Nodes [1 ]
999- require .Equal (t , core .NodeSucceeded , parallelNode .Status )
1000- require .Len (t , parallelNode .SubRuns , 1 )
1093+ - call: child-positional-single
1094+ parallel:
1095+ items: ${ITEMS}
1096+ params: "${ITEM.tag}"
1097+ output: RESULTS
1098+ ---
1099+ name: child-positional-single
1100+ params:
1101+ - TAG: ""
1102+ steps:
1103+ - script: |
1104+ if [ "$1" != "simple" ]; then
1105+ echo "FAIL: \$1='$1' (expected 'simple')"
1106+ exit 1
1107+ fi
1108+ echo "OK: TAG=$1"
1109+ output: CHECK_RESULT
1110+ ` ,
1111+ expectedSubRuns : 1 ,
1112+ verify : func (t * testing.T , results parallelResultsPayload ) {
1113+ require .Equal (t , 1 , results .Summary .Succeeded )
1114+ outputs := collectOutputs (results .Outputs , "CHECK_RESULT" )
1115+ require .Len (t , outputs , 1 )
1116+ require .Contains (t , outputs [0 ], "OK: TAG=simple" )
1117+ },
1118+ },
1119+ }
10011120
1002- require .NotNil (t , parallelNode .OutputVariables , "no outputs recorded" )
1003- rawRaw , ok := parallelNode .OutputVariables .Load ("RESULTS" )
1004- require .True (t , ok , "output RESULTS not found" )
1005- raw , ok := rawRaw .(string )
1006- require .True (t , ok , "output RESULTS is not a string" )
1007- results := parseParallelResults (t , raw )
1008- require .Equal (t , 1 , results .Summary .Succeeded )
1121+ for _ , tc := range cases {
1122+ t .Run (tc .name , func (t * testing.T ) {
1123+ th := test .Setup (t )
1124+ dag := th .DAG (t , tc .dag )
1125+ agent := dag .Agent ()
1126+ err := agent .Run (agent .Context )
1127+ require .NoError (t , err )
1128+ dag .AssertLatestStatus (t , core .Succeeded )
10091129
1010- outputs := collectOutputs (results .Outputs , "CHECK_RESULT" )
1011- require .Len (t , outputs , 1 )
1012- require .Contains (t , outputs [0 ], "OK: NAME=test A=1 B=2" )
1130+ dagStatus , statusErr := dag .DAGRunMgr .GetLatestStatus (dag .Context , dag .DAG )
1131+ require .NoError (t , statusErr )
1132+
1133+ require .Greater (t , len (dagStatus .Nodes ), 1 , "expected at least 2 nodes" )
1134+ parallelNode := dagStatus .Nodes [1 ]
1135+ require .Equal (t , core .NodeSucceeded , parallelNode .Status )
1136+ require .Len (t , parallelNode .SubRuns , tc .expectedSubRuns )
1137+
1138+ require .NotNil (t , parallelNode .OutputVariables , "no outputs recorded" )
1139+ rawRaw , ok := parallelNode .OutputVariables .Load ("RESULTS" )
1140+ require .True (t , ok , "output RESULTS not found" )
1141+ raw , ok := rawRaw .(string )
1142+ require .True (t , ok , "output RESULTS is not a string" )
1143+ results := parseParallelResults (t , raw )
1144+ tc .verify (t , results )
1145+ })
1146+ }
10131147}
10141148
10151149type parallelSummary struct {
0 commit comments