123 lines
4.2 KiB
C#
123 lines
4.2 KiB
C#
using System.Text.Json.Nodes;
|
|
using w4c_workflows.Models.Nodes;
|
|
using w4c_workflows.Services.Nodes;
|
|
using Xunit;
|
|
|
|
namespace w4c_workflows.Tests;
|
|
|
|
/// <summary>
|
|
/// S6: hot structures are precomputed once — adjacency, the display-name index,
|
|
/// the catalog's flattened views and the per-blueprint parameter index — instead
|
|
/// of being rebuilt per lookup. These assert identity so a regression to LINQ
|
|
/// <c>Where</c>/<c>ToList</c> per call is caught.
|
|
/// </summary>
|
|
public class NodeGraphPrecomputeTests
|
|
{
|
|
private static NodeBlueprint Blueprint(string type, string displayName) => new()
|
|
{
|
|
Type = type,
|
|
DisplayName = displayName,
|
|
};
|
|
|
|
private static NodeGraph Graph()
|
|
{
|
|
var a = new NodeGraphNode { Id = "a", Blueprint = Blueprint("core.noop", "Alpha"), Parameters = new JsonObject() };
|
|
var b = new NodeGraphNode { Id = "b", Blueprint = Blueprint("core.set", "Beta"), Parameters = new JsonObject() };
|
|
return new NodeGraph
|
|
{
|
|
Nodes = new[] { a, b },
|
|
Edges = new[]
|
|
{
|
|
new NodeGraphEdge { FromNodeId = "a", FromOutput = 0, ToNodeId = "b", ToInput = 0 },
|
|
new NodeGraphEdge { FromNodeId = "a", FromOutput = 0, ToNodeId = "b", ToInput = 1 },
|
|
new NodeGraphEdge { FromNodeId = "b", FromOutput = 0, ToNodeId = "a", ToInput = 0, IsLoopBack = true },
|
|
},
|
|
EntryNodeId = "a",
|
|
};
|
|
}
|
|
|
|
[Fact]
|
|
public void EdgesFrom_and_EdgesTo_are_cached_and_filter_correctly()
|
|
{
|
|
var graph = Graph();
|
|
|
|
Assert.Same(graph.EdgesFrom("a", 0), graph.EdgesFrom("a", 0));
|
|
Assert.Same(graph.EdgesTo("b"), graph.EdgesTo("b"));
|
|
|
|
Assert.Equal(2, graph.EdgesFrom("a", 0).Count());
|
|
Assert.Empty(graph.EdgesFrom("a", 1));
|
|
Assert.Equal(2, graph.EdgesTo("b").Count());
|
|
Assert.Single(graph.EdgesTo("a"));
|
|
}
|
|
|
|
[Fact]
|
|
public void FindByDisplayName_resolves_the_first_matching_node()
|
|
{
|
|
var graph = Graph();
|
|
|
|
Assert.Equal("b", graph.FindByDisplayName("Beta")?.Id);
|
|
Assert.Equal("a", graph.FindByDisplayName("Alpha")?.Id);
|
|
Assert.Null(graph.FindByDisplayName("Missing"));
|
|
}
|
|
|
|
[Fact]
|
|
public void Catalog_flattened_views_are_cached()
|
|
{
|
|
var zip = new NodeBlueprint
|
|
{
|
|
Type = "z.zip",
|
|
DisplayName = "Zip",
|
|
Categories = new List<string> { "Utility" },
|
|
};
|
|
var api = new NodeBlueprint
|
|
{
|
|
Type = "a.api",
|
|
DisplayName = "Api",
|
|
Categories = new List<string> { "Utility", "Network" },
|
|
};
|
|
var catalog = new NodeBlueprintCatalog(new[] { zip, api });
|
|
|
|
Assert.Same(catalog.All, catalog.All);
|
|
Assert.Same(catalog.Categories, catalog.Categories);
|
|
Assert.Equal(new[] { "Network", "Utility" }, catalog.Categories);
|
|
}
|
|
|
|
[Fact]
|
|
public void Blueprint_parameter_index_resolves_top_level_and_nested()
|
|
{
|
|
var blueprint = new NodeBlueprint
|
|
{
|
|
Type = "x.collection",
|
|
DisplayName = "Collection",
|
|
Parameters = new List<NodeParameter>
|
|
{
|
|
new()
|
|
{
|
|
Name = "mode",
|
|
DisplayName = "Mode",
|
|
Type = NodeParameterType.String,
|
|
},
|
|
new()
|
|
{
|
|
Name = "fields",
|
|
DisplayName = "Fields",
|
|
Type = NodeParameterType.FixedCollection,
|
|
Fields = new List<NodeParameter>
|
|
{
|
|
new() { Name = "nestedName", DisplayName = "Name", Type = NodeParameterType.String },
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
Assert.True(blueprint.HasParameter("mode"));
|
|
Assert.True(blueprint.HasParameter("nestedName"));
|
|
Assert.False(blueprint.HasParameter("missing"));
|
|
|
|
// FindParameter stays top-level so nested fields keep their existing
|
|
// (unvalidated) compile behaviour.
|
|
Assert.Equal("mode", blueprint.FindParameter("mode")?.Name);
|
|
Assert.Null(blueprint.FindParameter("nestedName"));
|
|
}
|
|
}
|