166 lines
5.4 KiB
C#
166 lines
5.4 KiB
C#
using System.Text.Json.Nodes;
|
|
using w4c_workflows.Models.Nodes;
|
|
using w4c_workflows.Services.Nodes.Interpolation;
|
|
using Xunit;
|
|
|
|
namespace w4c_workflows.Tests;
|
|
|
|
public class NodeParameterInterpolatorTests
|
|
{
|
|
private readonly NodeParameterInterpolator _interpolator = new();
|
|
|
|
private static InterpolationScope Scope(
|
|
string itemJson = "{}",
|
|
string parametersJson = "{}",
|
|
string environmentJson = "{}",
|
|
Func<string, IReadOnlyList<FlowItem>>? nodeItems = null) =>
|
|
new()
|
|
{
|
|
Item = JsonNode.Parse(itemJson)!.AsObject(),
|
|
Parameters = JsonNode.Parse(parametersJson)!.AsObject(),
|
|
Environment = JsonNode.Parse(environmentJson)!.AsObject(),
|
|
ItemIndex = 2,
|
|
RunIndex = 1,
|
|
NodeItems = nodeItems,
|
|
};
|
|
|
|
[Fact]
|
|
public void Plain_string_is_a_literal()
|
|
{
|
|
var result = _interpolator.ResolveString("hello", Scope());
|
|
Assert.Equal("hello", result!.GetValue<string>());
|
|
}
|
|
|
|
[Fact]
|
|
public void Equals_without_braces_is_still_literal_text()
|
|
{
|
|
var result = _interpolator.ResolveString("=plain text", Scope());
|
|
Assert.Equal("plain text", result!.GetValue<string>());
|
|
}
|
|
|
|
[Fact]
|
|
public void Whole_value_expression_preserves_type_string()
|
|
{
|
|
var result = _interpolator.ResolveString("={{ $json.name }}", Scope("""{"name":"Alice"}"""));
|
|
Assert.Equal("Alice", result!.GetValue<string>());
|
|
}
|
|
|
|
[Fact]
|
|
public void Whole_value_expression_preserves_type_number()
|
|
{
|
|
var result = _interpolator.ResolveString("={{ $json.count }}", Scope("""{"count":3}"""));
|
|
Assert.Equal("3", result!.ToJsonString());
|
|
}
|
|
|
|
[Fact]
|
|
public void Whole_value_expression_preserves_type_boolean()
|
|
{
|
|
var result = _interpolator.ResolveString("={{ $json.ok }}", Scope("""{"ok":true}"""));
|
|
Assert.Equal("true", result!.ToJsonString());
|
|
}
|
|
|
|
[Fact]
|
|
public void Embedded_expression_produces_an_interpolated_string()
|
|
{
|
|
var result = _interpolator.ResolveString("=Hi {{ $json.name }}, #{{ $json.count }}", Scope("""{"name":"Alice","count":3}"""));
|
|
Assert.Equal("Hi Alice, #3", result!.GetValue<string>());
|
|
}
|
|
|
|
[Fact]
|
|
public void Nested_paths_and_array_indexes_resolve()
|
|
{
|
|
var result = _interpolator.ResolveString(
|
|
"={{ $json.orders[0].id }}",
|
|
Scope("""{"orders":[{"id":42}]}"""));
|
|
Assert.Equal("42", result!.ToJsonString());
|
|
}
|
|
|
|
[Fact]
|
|
public void Bracket_notation_resolves()
|
|
{
|
|
var result = _interpolator.ResolveString(
|
|
"""={{ $json["odd name"] }}""",
|
|
Scope("""{"odd name":"value"}"""));
|
|
Assert.Equal("value", result!.GetValue<string>());
|
|
}
|
|
|
|
[Fact]
|
|
public void Parameter_and_environment_roots_resolve()
|
|
{
|
|
var scope = Scope(parametersJson: """{"mode":"manual"}""", environmentJson: """{"REGION":"eu"}""");
|
|
|
|
Assert.Equal("manual", _interpolator.ResolveString("={{ $parameter.mode }}", scope)!.GetValue<string>());
|
|
Assert.Equal("eu", _interpolator.ResolveString("={{ $env.REGION }}", scope)!.GetValue<string>());
|
|
}
|
|
|
|
[Fact]
|
|
public void Index_roots_resolve()
|
|
{
|
|
var scope = Scope();
|
|
Assert.Equal("2", _interpolator.ResolveString("={{ $itemIndex }}", scope)!.ToJsonString());
|
|
Assert.Equal("1", _interpolator.ResolveString("={{ $runIndex }}", scope)!.ToJsonString());
|
|
}
|
|
|
|
[Fact]
|
|
public void Node_reference_returns_the_first_item_json()
|
|
{
|
|
var scope = Scope(nodeItems: _ => new List<FlowItem>
|
|
{
|
|
FlowItem.FromJson(new JsonObject { ["id"] = 7 }),
|
|
});
|
|
|
|
Assert.Equal("7", _interpolator.ResolveString("""={{ $("Prep").id }}""", scope)!.ToJsonString());
|
|
}
|
|
|
|
[Fact]
|
|
public void Items_reference_returns_an_array()
|
|
{
|
|
var scope = Scope(nodeItems: _ => new List<FlowItem>
|
|
{
|
|
FlowItem.FromJson(new JsonObject { ["id"] = 7 }),
|
|
FlowItem.FromJson(new JsonObject { ["id"] = 8 }),
|
|
});
|
|
|
|
Assert.Equal("""[{"id":7},{"id":8}]""", _interpolator.ResolveString("""={{ $items("Prep") }}""", scope)!.ToJsonString());
|
|
}
|
|
|
|
[Fact]
|
|
public void Object_parameters_are_resolved_recursively()
|
|
{
|
|
var parameters = JsonNode.Parse("""
|
|
{
|
|
"url": "https://x/{{ $json.id }}",
|
|
"options": { "flag": "={{ $json.ok }}" },
|
|
"list": ["={{ $json.id }}"]
|
|
}
|
|
""")!.AsObject();
|
|
|
|
var resolved = _interpolator.ResolveObject(parameters, Scope("""{"id":"abc","ok":true}"""));
|
|
|
|
Assert.Equal("https://x/abc", resolved["url"]!.GetValue<string>());
|
|
Assert.Equal("true", resolved["options"]!["flag"]!.ToJsonString());
|
|
Assert.Equal("abc", resolved["list"]![0]!.GetValue<string>());
|
|
}
|
|
|
|
[Fact]
|
|
public void Unknown_accessor_throws()
|
|
{
|
|
Assert.Throws<NodeInterpolationException>(() =>
|
|
_interpolator.ResolveString("={{ $nope.x }}", Scope()));
|
|
}
|
|
|
|
[Fact]
|
|
public void Missing_path_resolves_to_null_without_throwing()
|
|
{
|
|
var result = _interpolator.ResolveString("={{ $json.missing.deep }}", Scope("""{"other":1}"""));
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Case_sensitive_literal_stays_lowercase()
|
|
{
|
|
var result = _interpolator.ResolveString("={{ $json.name }}", Scope("""{"name":"aB"}"""));
|
|
Assert.Equal("aB", result!.GetValue<string>());
|
|
}
|
|
}
|