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 GetAllPorts(string pipelineId, INodeFactory nodeFactory, List allNodes)"; var methodSignatureV2 = "private IReadOnlyDictionary GetAllPorts(string pipelineId, INodeFactory nodeFactory," + Environment.NewLine + " List 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 GetNodeFieldValidator(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 nodePortConnections, IReadOnlyDictionary ports, out Dictionary inputPortValidationResults, CultureInfo cultureInfo)"; var methodSignatureV2 = "private ValidationResult ValidateNodePorts(string pipelineId, INodeFactory nodeFactory, string nodeType, string nodeId," + Environment.NewLine + " IReadOnlyDictionary nodePortConnections, IReadOnlyDictionary ports," + Environment.NewLine + " out Dictionary> 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 ports, CultureInfo cultureInfo)"; var methodSignatureV2 = "private NodeFieldValidationResult ValidateInputPort(string nodeId, NodePortInfo inputPort, InputSource connection," + Environment.NewLine + " IReadOnlyDictionary 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(string nodeId, NodePipeline.Abstractions.Models.NodeField 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> fieldResults, Dictionary> 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> fieldResults, Dictionary> 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 allNodes, Dictionary parameters, CultureInfo cultureInfo)"; var methodSignatureV2 = "public NodeValidationResult ValidateNode(string pipelineId, INodeFactory nodeFactory, NodeConfig nodeConfig," + Environment.NewLine + "List allNodes, Dictionary parameters, CultureInfo cultureInfo)"; // var methodSignature = $"private static Dictionary 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(); 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(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(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 nodePortConnections, IReadOnlyDictionary ports, CultureInfo cultureInfo)"; var methodSignatureV2 = $"private NodeValidationResult Validate__{nodeDescriptor.TypeNameShortSanitized}__Node(string pipelineId, INodeFactory nodeFactory, {nodeDescriptor.TypeNameFull} node, string nodeId, IReadOnlyDictionary nodePortConnections, IReadOnlyDictionary 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 }