33package executor
44
55import (
6+ "context"
67 "strings"
78 "testing"
89
@@ -1203,6 +1204,9 @@ func TestTraverseFlow_EmptyThenSwap(t *testing.T) {
12031204 if ! strings .Contains (output , "not($Active)" ) {
12041205 t .Errorf ("expected negated condition 'not($Active)', got:\n %s" , output )
12051206 }
1207+ if strings .Count (output , "if " ) != 1 {
1208+ t .Errorf ("expected split header to be emitted once, got:\n %s" , output )
1209+ }
12061210 if ! strings .Contains (output , "log info" ) {
12071211 t .Errorf ("expected 'log info' in output, got:\n %s" , output )
12081212 }
@@ -1211,6 +1215,54 @@ func TestTraverseFlow_EmptyThenSwap(t *testing.T) {
12111215 }
12121216}
12131217
1218+ func TestTraverseFlowUntilMerge_NestedEmptyThenSwapEmitsHeaderOnce (t * testing.T ) {
1219+ e := newTestExecutor ()
1220+
1221+ // Nested empty-then split inside a parent branch:
1222+ // split → (true) → nested merge → parent merge
1223+ // → (false) → log → nested merge
1224+ // The describer negates the condition and must emit only the final
1225+ // swapped header. Emitting the original header first creates invalid MDL.
1226+ activityMap := map [model.ID ]microflows.MicroflowObject {
1227+ mkID ("split" ): & microflows.ExclusiveSplit {
1228+ BaseMicroflowObject : mkObj ("split" ),
1229+ SplitCondition : & microflows.ExpressionSplitCondition {Expression : "$NestedDone" },
1230+ },
1231+ mkID ("log" ): & microflows.ActionActivity {
1232+ BaseActivity : microflows.BaseActivity {BaseMicroflowObject : mkObj ("log" )},
1233+ Action : & microflows.LogMessageAction {
1234+ LogLevel : "Info" ,
1235+ LogNodeName : "'Test'" ,
1236+ },
1237+ },
1238+ mkID ("nestedMerge" ): & microflows.ExclusiveMerge {BaseMicroflowObject : mkObj ("nestedMerge" )},
1239+ mkID ("parentMerge" ): & microflows.ExclusiveMerge {BaseMicroflowObject : mkObj ("parentMerge" )},
1240+ }
1241+
1242+ flowsByOrigin := map [model.ID ][]* microflows.SequenceFlow {
1243+ mkID ("split" ): {
1244+ {OriginID : mkID ("split" ), DestinationID : mkID ("nestedMerge" ), CaseValue : microflows.EnumerationCase {Value : "true" }},
1245+ {OriginID : mkID ("split" ), DestinationID : mkID ("log" ), CaseValue : microflows.EnumerationCase {Value : "false" }},
1246+ },
1247+ mkID ("log" ): {{OriginID : mkID ("log" ), DestinationID : mkID ("nestedMerge" )}},
1248+ mkID ("nestedMerge" ): {{OriginID : mkID ("nestedMerge" ), DestinationID : mkID ("parentMerge" )}},
1249+ }
1250+ splitMergeMap := map [model.ID ]model.ID {mkID ("split" ): mkID ("nestedMerge" )}
1251+
1252+ var lines []string
1253+ visited := map [model.ID ]bool {}
1254+
1255+ traverseFlowUntilMerge (e .newExecContext (context .Background ()), mkID ("split" ), mkID ("parentMerge" ), activityMap , flowsByOrigin , nil , splitMergeMap , visited , nil , nil , & lines , 0 , nil , 0 , nil )
1256+
1257+ output := strings .Join (lines , "\n " )
1258+ if strings .Count (output , "if " ) != 1 {
1259+ t .Fatalf ("expected nested split header to be emitted once, got:\n %s" , output )
1260+ }
1261+ if strings .Count (output , "end if;" ) != 1 {
1262+ t .Fatalf ("expected nested split to close exactly once, got:\n %s" , output )
1263+ }
1264+ }
1265+
12141266func TestTraverseFlow_BothBranchesToMerge_NoSwap (t * testing.T ) {
12151267 e := newTestExecutor ()
12161268
0 commit comments