using w4c_workflows.Models.Nodes; using w4c_workflows.Services.Nodes; using Xunit; namespace w4c_workflows.Tests; /// /// The legacy lowering bridge: a script definition (language + entry, next/onError) /// becomes a node graph of synthetic core.code nodes while script execution /// still happens in the language runtime behind the node. /// public class LegacyWorkflowLowererTests { private static NodeGraphCompileResult CompileLowered(string yaml) { var lowered = LegacyWorkflowLowerer.ToNodeDefinition(NodeTestData.Parse(yaml)); return new NodeGraphCompiler(NodeTestData.CoreCatalog()).Compile(lowered); } [Fact] public void Lowers_the_header_entry_and_tasks_into_core_code_nodes() { var yaml = """ name: legacy mode: function language: shell entry: { file: root.sh } tasks: - id: a parent: root next: b language: python entry: { file: a.py } - id: b parent: root entry: { file: b.sh } """; var result = CompileLowered(yaml); Assert.True(result.Success, string.Join("\n", result.Errors)); var graph = result.Graph!; Assert.Equal(LegacyWorkflowLowerer.EntryStepId, graph.EntryNodeId); Assert.All(graph.Nodes, n => Assert.Equal("core.code", n.Blueprint.Type)); var entry = graph.Find(LegacyWorkflowLowerer.EntryStepId)!; Assert.Equal("shell", entry.Parameters["language"]!.GetValue()); Assert.Equal("root.sh", entry.Parameters["entryFile"]!.GetValue()); var a = graph.Find("a")!; Assert.Equal("python", a.Parameters["language"]!.GetValue()); Assert.Equal("a.py", a.Parameters["entryFile"]!.GetValue()); } [Fact] public void Preserves_next_as_main_and_onError_as_the_error_port() { var yaml = """ name: legacy-error mode: function language: shell entry: { file: root.sh } tasks: - id: a parent: root next: b onError: undo entry: { file: a.sh } - id: b parent: root entry: { file: b.sh } - id: undo parent: root entry: { file: undo.sh } """; var result = CompileLowered(yaml); Assert.True(result.Success, string.Join("\n", result.Errors)); var graph = result.Graph!; Assert.Contains( graph.Edges, e => e.FromNodeId == LegacyWorkflowLowerer.EntryStepId && e.FromOutput == 0 && e.ToNodeId == "a"); Assert.Contains(graph.Edges, e => e.FromNodeId == "a" && e.FromOutput == 0 && e.ToNodeId == "b"); // core.code's error output is port 1. Assert.Contains(graph.Edges, e => e.FromNodeId == "a" && e.FromOutput == 1 && e.ToNodeId == "undo"); } [Fact] public void Lowers_a_root_only_workflow_to_a_single_entry_node() { var yaml = """ name: ping mode: function language: shell entry: { file: ping.sh } """; var result = CompileLowered(yaml); Assert.True(result.Success, string.Join("\n", result.Errors)); Assert.Single(result.Graph!.Nodes); Assert.Equal(LegacyWorkflowLowerer.EntryStepId, result.Graph!.EntryNodeId); Assert.Empty(result.Graph!.Edges); } [Fact] public void Avoids_colliding_with_a_task_named_like_the_synthetic_entry() { var yaml = """ name: collision mode: function language: shell entry: { file: root.sh } tasks: - id: __entry parent: root entry: { file: a.sh } """; var result = CompileLowered(yaml); Assert.True(result.Success, string.Join("\n", result.Errors)); Assert.Equal(2, result.Graph!.Nodes.Count); Assert.NotNull(result.Graph!.Find("__entry")); Assert.NotNull(result.Graph!.Find("__entry1")); } }