NodePipeline/NodePipeline.Engine.Tests/CodeGeneratorTests/NodeValidatorGeneratorTests.cs
2026-01-02 20:55:25 +03:00

380 lines
18 KiB
C#

using NodePipeline.Abstractions.Interfaces.Nodes;
using NodePipeline.Engine.CodeGeneration;
using NodePipeline.Engine.CodeGeneration.Abstractions.Models;
using NodePipeline.Engine.Tests.CodeGeneratorTests.Fixtures;
using NodePipeline.Engine.Tests.CodeGeneratorTests.Fixtures.MockNodes;
namespace NodePipeline.Engine.Tests.CodeGeneratorTests;
public class NodeValidatorGeneratorTests
{
#region GetAllPorts
[Fact]
public void GetAllPortsMethod_ShouldExistInGeneratedCode()
{
var methodSignatureV1 =
"private IReadOnlyDictionary<NodePortKey, NodePortInfo> GetAllPorts(string pipelineId, INodeFactory nodeFactory, List<NodeConfig> allNodes)";
var methodSignatureV2 =
"private IReadOnlyDictionary<NodePortKey, NodePortInfo> GetAllPorts(string pipelineId, INodeFactory nodeFactory,"
+ Environment.NewLine + " List<NodeConfig> allNodes)";
var model = TestNodeModelFactory.CreateMultipleNodesModel();
var generatedCode = NodeValidatorGenerator.Generate(model);
Assert.True(generatedCode.Contains(methodSignatureV1)
|| generatedCode.Contains(methodSignatureV2));
}
#endregion
#region GetNodeFieldValidator
[Fact]
public void GetNodeFieldValidatorMethod_ShouldExistInGeneratedCode()
{
var methodSignature = "private INodeFieldValidator<T> GetNodeFieldValidator<T>(Type validatorType)";
var model = TestNodeModelFactory.CreateMultipleNodesModel();
var generatedCode = NodeValidatorGenerator.Generate(model);
Assert.Contains(methodSignature, generatedCode);
}
#endregion
#region ValidateNodePorts
[Fact]
public void ValidateNodePortsMethod_ShouldExistInGeneratedCode()
{
var methodSignatureV1 =
"private ValidationResult ValidateNodePorts(string pipelineId, INodeFactory nodeFactory, string nodeType, string nodeId, IReadOnlyDictionary<string, InputSource> nodePortConnections, IReadOnlyDictionary<NodePortKey, NodePortInfo> ports, out Dictionary<string, NodeFieldValidationResult> inputPortValidationResults, CultureInfo cultureInfo)";
var methodSignatureV2 =
"private ValidationResult ValidateNodePorts(string pipelineId, INodeFactory nodeFactory, string nodeType, string nodeId,"
+ Environment.NewLine +
" IReadOnlyDictionary<string, InputSource> nodePortConnections, IReadOnlyDictionary<NodePortKey, NodePortInfo> ports,"
+ Environment.NewLine +
" out Dictionary<string, List<NodeFieldValidationResult>> inputPortValidationResults, CultureInfo cultureInfo)";
var model = TestNodeModelFactory.CreateMultipleNodesModel();
var generatedCode = NodeValidatorGenerator.Generate(model);
Assert.True(generatedCode.Contains(methodSignatureV1)
|| generatedCode.Contains(methodSignatureV2));
}
#endregion
#region ValidateInputPort
[Fact]
public void ValidateInputPortMethod_ShouldExistInGeneratedCode()
{
var methodSignatureV1 =
"private NodeFieldValidationResult ValidateInputPort(string nodeId, NodePortInfo inputPort, InputSource connection, IReadOnlyDictionary<NodePortKey, NodePortInfo> ports, CultureInfo cultureInfo)";
var methodSignatureV2 =
"private NodeFieldValidationResult ValidateInputPort(string nodeId, NodePortInfo inputPort, InputSource connection,"
+ Environment.NewLine +
" IReadOnlyDictionary<NodePortKey, NodePortInfo> ports, CultureInfo cultureInfo)";
var model = TestNodeModelFactory.CreateMultipleNodesModel();
var generatedCode = NodeValidatorGenerator.Generate(model);
Assert.True(generatedCode.Contains(methodSignatureV1)
|| generatedCode.Contains(methodSignatureV2));
}
#endregion
#region GetNodeValidator
[Fact]
public void GetNodeValidatorMethod_ShouldExistInGeneratedCode()
{
var methodSignature = "private INodeValidator GetNodeValidator(Type validatorType)";
var model = TestNodeModelFactory.CreateMultipleNodesModel();
var generatedCode = NodeValidatorGenerator.Generate(model);
Assert.Contains(methodSignature, generatedCode);
}
#endregion
#region CheckRequiredNodeParameter
[Fact]
public void CheckRequiredNodeParameterMethod_ShouldExistInGeneratedCode()
{
var methodSignature =
"private NodeFieldValidationResult CheckRequiredNodeParameter<T>(string nodeId, NodePipeline.Abstractions.Models.NodeField<T> nodeField, string nodeParameterName, CultureInfo cultureInfo)";
var model = TestNodeModelFactory.CreateMultipleNodesModel();
var generatedCode = NodeValidatorGenerator.Generate(model);
Assert.Contains(methodSignature, generatedCode);
}
#endregion
#region SuccessNodeValidationResult
[Fact]
public void SuccessNodeValidationResultMethod_ShouldExistInGeneratedCode()
{
var methodSignature =
"private static NodeValidationResult SuccessNodeValidationResult(bool hasWarnings, Dictionary<string, List<NodeFieldValidationResult>> fieldResults, Dictionary<string, List<NodeFieldValidationResult>> portResults)";
var model = TestNodeModelFactory.CreateMultipleNodesModel();
var generatedCode = NodeValidatorGenerator.Generate(model);
Assert.Contains(methodSignature, generatedCode);
}
#endregion
#region GetErrorValidationResult
[Fact]
public void GetErrorValidationResultMethod_ShouldExistInGeneratedCode()
{
var methodSignature =
"private NodeValidationResult GetErrorValidationResult(ValidationResult validationResult, string nodeId, Dictionary<string, List<NodeFieldValidationResult>> fieldResults, Dictionary<string, List<NodeFieldValidationResult>> portResults, CultureInfo cultureInfo)";
var model = TestNodeModelFactory.CreateMultipleNodesModel();
var generatedCode = NodeValidatorGenerator.Generate(model);
Assert.Contains(methodSignature, generatedCode);
}
#endregion
#region SuccessNodeFieldValidationResult
[Fact]
public void SuccessNodeFieldValidationResultMethod_ShouldExistInGeneratedCode()
{
var methodSignature = "private static NodeFieldValidationResult SuccessNodeFieldValidationResult()";
var model = TestNodeModelFactory.CreateMultipleNodesModel();
var generatedCode = NodeValidatorGenerator.Generate(model);
Assert.Contains(methodSignature, generatedCode);
}
#endregion
#region UpdateValidationResult
[Fact]
public void UpdateValidationResultMethod_ShouldExistInGeneratedCode()
{
var methodSignatureV1 =
"private static void UpdateValidationResult(ValidationResult validationResult, ref ValidationResult parametersValidationResult)";
var methodSignatureV2 = "private static void UpdateValidationResult(ValidationResult validationResult,"
+ Environment.NewLine + " ref ValidationResult parametersValidationResult)";
var model = TestNodeModelFactory.CreateMultipleNodesModel();
var generatedCode = NodeValidatorGenerator.Generate(model);
Assert.True(generatedCode.Contains(methodSignatureV1)
|| generatedCode.Contains(methodSignatureV2));
}
#endregion
#region ValidateNode
[Fact]
public void ValidateNodeMethod_ShouldExistInGeneratedCode()
{
var methodSignatureV1 =
"public NodeValidationResult ValidateNode(string pipelineId, INodeFactory nodeFactory, NodeConfig nodeConfig, List<NodeConfig> allNodes, Dictionary<string, object?> parameters, CultureInfo cultureInfo)";
var methodSignatureV2 =
"public NodeValidationResult ValidateNode(string pipelineId, INodeFactory nodeFactory, NodeConfig nodeConfig," +
Environment.NewLine +
"List<NodeConfig> allNodes, Dictionary<string, object?> parameters, CultureInfo cultureInfo)";
// var methodSignature = $"private static Dictionary<NodePortKey, NodePortInfo> Get__{GetNodeDescriptorFromMockNode(mockNodeType).TypeNameShortSanitized}__NodePorts(string nodeId)";
var model = TestNodeModelFactory.CreateMultipleNodesModel();
var generatedCode = NodeValidatorGenerator.Generate(model);
Assert.True(generatedCode.Contains(methodSignatureV1)
|| generatedCode.Contains(methodSignatureV2));
}
[Fact]
public void InitializeNodeFieldsMethod_ContainsSwitchCases()
{
var model = TestNodeModelFactory.CreateMultipleNodesModel();
var methodName = "ValidateNode";
var tCountByNodes = new Dictionary<NodeDescriptor, int>();
var generatedCode = NodeValidatorGenerator.Generate(model);
var methodCode = TestHelper.ExtractMethod(generatedCode, methodName);
var firstTextV1 = "var node = nodeFactory.CreateNode(pipelineId, nodeConfig);"
+ Environment.NewLine +
" nodeFactory.SetNodeParametersValues(pipelineId, node, nodeConfig.Id, parameters);"
+ Environment.NewLine + " var nodeConnections = allNodes.SelectMany(q => q.Inputs"
+ Environment.NewLine +
" .Select(x => new KeyValuePair<string, InputSource>(GetNodeInputKey(q.Id, x.Key), x.Value))"
+ Environment.NewLine + " ).ToDictionary(q => q.Key, q => q.Value);"
+ Environment.NewLine + " var ports = GetAllPorts(pipelineId, nodeFactory, allNodes);";
var firstTextV2 = "var node = nodeFactory.CreateNode(pipelineId, nodeConfig);"
+ Environment.NewLine +
" nodeFactory.SetNodeParametersValues(pipelineId, node, nodeConfig.Id, parameters);"
+ Environment.NewLine +
" var nodeConnections = allNodes.SelectMany(q => q.Inputs.Select(x => new KeyValuePair<string, InputSource>(GetNodeInputKey(q.Id, x.Key), x.Value))).ToDictionary(q => q.Key, q => q.Value);"
+ Environment.NewLine + " var ports = GetAllPorts(pipelineId, nodeFactory, allNodes);";
Assert.True(methodCode.Contains(firstTextV1)
|| methodCode.Contains(firstTextV2));
foreach (var node in model.Nodes)
{
var @case = $"case {node.TypeNameFull} t";
var caseLabel = methodCode[methodCode.IndexOf(@case, StringComparison.Ordinal)..].Split(':')[0];
var tNumberString = caseLabel[@case.Length..];
var caseBlock = TestHelper.ExtractSwitchCase(methodCode, @case);
Assert.False(string.IsNullOrEmpty(caseBlock), $"Case block for node {node.Type} not found.");
// var expectedCode = $"return Validate__{node.TypeNameShortSanitized}__Node(pipelineId, nodeFactory, t{tNumberString}, nodeConfig.Id, nodeConnections, ports, cultureInfo);";
// Assert.Contains(expectedCode, caseBlock);
Assert.Contains(
$"return Validate__{node.TypeNameShortSanitized}__Node(pipelineId, nodeFactory, t{tNumberString}, nodeConfig.Id, nodeConnections, ports, cultureInfo);",
caseBlock);
if (!tCountByNodes.TryGetValue(node, out var value)) tCountByNodes.Add(node, 1);
else tCountByNodes[node] = ++value;
}
Assert.Contains(
"default:" + Environment.NewLine +
" throw new UnknownNodeTypeException(nodeConfig.Type, nodeConfig.Id);", methodCode);
Assert.DoesNotContain(tCountByNodes.Values, q => q > 1);
}
#endregion
#region ValidateNodeSpecific
[Theory]
[InlineData(typeof(ThreeParameterNode), "CreateThreeParameterNodeModel")]
[InlineData(typeof(ThreePortNode), "CreateThreePortNodeModel")]
[InlineData(typeof(SimpleNamedNode), "CreateSimpleNamedNodeModel")]
public void SpecificValidateNodeMethod_ShouldExistInGeneratedCode(Type mockNodeType, string factoryMethod)
{
var nodeDescriptor = TestHelper.GetNodeDescriptorFromMockNode(mockNodeType);
var methodSignatureV1 =
$"private NodeValidationResult Validate__{nodeDescriptor.TypeNameShortSanitized}__Node(string pipelineId, INodeFactory nodeFactory, {nodeDescriptor.TypeNameFull} node,"
+ Environment.NewLine +
" string nodeId, IReadOnlyDictionary<string, InputSource> nodePortConnections, IReadOnlyDictionary<NodePortKey, NodePortInfo> ports, CultureInfo cultureInfo)";
var methodSignatureV2 =
$"private NodeValidationResult Validate__{nodeDescriptor.TypeNameShortSanitized}__Node(string pipelineId, INodeFactory nodeFactory, {nodeDescriptor.TypeNameFull} node, string nodeId, IReadOnlyDictionary<string, InputSource> nodePortConnections, IReadOnlyDictionary<NodePortKey, NodePortInfo> ports, CultureInfo cultureInfo)";
var model = TestHelper.GetNodeModel(factoryMethod);
var generatedCode = NodeValidatorGenerator.Generate(model);
Assert.True(generatedCode.Contains(methodSignatureV1)
|| generatedCode.Contains(methodSignatureV2));
}
[Fact]
public void SpecificValidateNodeMethod_ShouldCallCheckRequiredNodeParameterOnlyForRequiredFields()
{
var nodeDescriptor = ThreeParameterNode.GetDescriptor();
var methodName = $"Validate__{nodeDescriptor.TypeNameShortSanitized}__Node";
var model = TestNodeModelFactory.CreateThreeParameterNodeModel();
var generatedCode = NodeValidatorGenerator.Generate(model);
var methodCode = TestHelper.ExtractMethod(generatedCode, methodName);
foreach (var parameter in nodeDescriptor.Fields.Where(q => q.Direction == FieldDirection.Parameter))
{
var expectedSecondPart =
$"CheckRequiredNodeParameter<{parameter.ValueType}>(nodeId, node.{parameter.PropertyName}, \"{parameter.FieldName}\", cultureInfo);";
if (parameter.Metadata?.IsRequired == true)
Assert.Contains(expectedSecondPart, methodCode);
else
Assert.DoesNotContain(expectedSecondPart, methodCode);
}
}
[Fact]
public void SpecificValidateNodeMethod_ShouldContainAppropriateValidatorAtPropertyWithStringLengthConstraint()
{
string MethodName(NodeDescriptor n)
{
return $"Validate__{n.TypeNameShortSanitized}__Node";
}
string ExpectedString(NodeFieldDescriptor f)
{
return
$"= new StringLengthValidator(PipelineLocalizationProvider, {TestHelper.NumberToString(f.Metadata!.StringMinLength)}, {TestHelper.NumberToString(f.Metadata!.StringMaxLength)}).Validate(node.{f.PropertyName});";
}
var nodeDescriptor1 = ThreeParameterNode.GetDescriptor();
var nodeDescriptor2 = ThreeParameterNode2.GetDescriptor();
var nodeDescriptor3 = ThreeParameterNode3.GetDescriptor();
var model = TestNodeModelFactory.CreateMultipleNodesWithStringParameterLengthConstraints();
var generatedCode = NodeValidatorGenerator.Generate(model);
var m1 = TestHelper.ExtractMethod(generatedCode, MethodName(nodeDescriptor2));
var f1 = nodeDescriptor2.Fields.First(q => q.PropertyName == nameof(ThreeParameterNode2.Parameter3));
var m2 = TestHelper.ExtractMethod(generatedCode, MethodName(nodeDescriptor1));
var f2 = nodeDescriptor1.Fields.First(q => q.PropertyName == nameof(ThreeParameterNode.Parameter2));
var m3 = TestHelper.ExtractMethod(generatedCode, MethodName(nodeDescriptor3));
var f3 = nodeDescriptor3.Fields.First(q => q.PropertyName == nameof(ThreeParameterNode3.Parameter3));
var es1 = ExpectedString(f1);
var es2 = ExpectedString(f2);
var es3 = ExpectedString(f3);
//check minlength constraint
Assert.Contains(es1, m1);
//check maxlength constraint
Assert.Contains(es2, m2);
//check length between constraint
Assert.Contains(es3, m3);
}
[Fact]
public void SpecificValidateNodeMethod_ShouldContainAppropriateValidatorAtPropertyWithIntValidationConstraint()
{
string MethodName(NodeDescriptor n)
{
return $"Validate__{n.TypeNameShortSanitized}__Node";
}
string ExpectedString(NodeFieldDescriptor f)
{
return
$"= new NumberRangeValidator<{f.ValueType}>(PipelineLocalizationProvider, {TestHelper.NumberToString(f.Metadata!.NumberMinBound)}, {TestHelper.NumberToString(f.Metadata!.NumberMaxBound)}).Validate(node.{f.PropertyName});";
}
var nodeDescriptor1 = ThreeParameterNode.GetDescriptor();
var nodeDescriptor2 = ThreeParameterNode2.GetDescriptor();
var model = TestNodeModelFactory.CreateMultipleNodesWithStringParameterLengthConstraints();
var generatedCode = NodeValidatorGenerator.Generate(model);
var m1 = TestHelper.ExtractMethod(generatedCode, MethodName(nodeDescriptor2));
var f1 = nodeDescriptor2.Fields.First(q => q.PropertyName == nameof(ThreeParameterNode2.Parameter1));
var m2 = TestHelper.ExtractMethod(generatedCode, MethodName(nodeDescriptor2));
var f2 = nodeDescriptor2.Fields.First(q => q.PropertyName == nameof(ThreeParameterNode2.Parameter2));
var m3 = TestHelper.ExtractMethod(generatedCode, MethodName(nodeDescriptor1));
var f3 = nodeDescriptor1.Fields.First(q => q.PropertyName == nameof(ThreeParameterNode.Parameter1));
var es1 = ExpectedString(f1);
var es2 = ExpectedString(f2);
var es3 = ExpectedString(f3);
//check minlength constraint
Assert.Contains(es1, m1);
//check maxlength constraint
Assert.Contains(es2, m2);
//check length between constraint
Assert.Contains(es3, m3);
}
#endregion
}