using System.Reflection; 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 NodeFactoryGeneratorTests { #region CreateNode [Fact] public void CreateNodeMethod_ContainsAllNodeCases() { // Arrange var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, "CreateNode"); Assert.False(string.IsNullOrEmpty(methodCode), "CreateNode method not found in generated code."); // Act & Assert foreach (var node in model.Nodes) { var caseLabel = $"case var t when t == NodeNamePrefixSettings.TrimNodeType(\"{node.Type}\"):"; var caseBlock = TestHelper.ExtractSwitchCase(methodCode, caseLabel); Assert.False(string.IsNullOrEmpty(caseBlock), $"Case block for node {node.Type} not found."); if (node.HasParameterlessConstructor) { Assert.Contains($"node = new {node.TypeNameFull}();", caseBlock); Assert.Contains("break;", caseBlock); } else { Assert.Contains( "throw new NodeCanNotBeInitializedWithoutFactoryException(pipelineId, config.Type, config.Id);", caseBlock); } } Assert.Contains("InitializeNodeFields(pipelineId, node, config.Id);", generatedCode); Assert.Contains("return node ?? throw new NodeWasNotInitializedException(pipelineId, config.Type, config.Id);", generatedCode); } #endregion #region GetAllPorts [Fact] public void GetAllPortsMethod_ShouldExistInGeneratedCode() { const string methodSignature = "public Dictionary GetAllPorts(string pipelineId, HashSet nodeTypes, Dictionary> nodeList)"; var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } #endregion #region GetNodeInputPorts [Fact] public void GetNodeInputPortsMethod_ShouldExistInGeneratedCode() { const string methodSignature = "public IEnumerable GetNodeInputPorts(string pipelineId, string nodeType, string nodeId)"; var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } #endregion #region ConnectPortsStruct [Fact] public void ConnectPortsStruct_ShouldExistInGeneratedCode() { const string methodSignatureV1 = "private void ConnectPortsStruct(string pipelineId, string nodeId,string nodeType, INodeField? nodeInput, string nodeInputName, Dictionary inputs, string inputKey, IReadOnlyDictionary createdNodes, bool disallowNullableOutput = false, bool isRequired = false) where T : struct"; const string methodSignatureV2 = "private void ConnectPortsStruct(string pipelineId, string nodeId,string nodeType, INodeField? nodeInput, string nodeInputName, Dictionary inputs, string inputKey, IReadOnlyDictionary createdNodes, bool disallowNullableOutput = false, bool isRequired = false)\n where T : struct"; var methodSignatureV3 = "private void ConnectPortsStruct(string pipelineId, string nodeId,string nodeType, INodeField? nodeInput, string nodeInputName, Dictionary inputs," + Environment.NewLine + " string inputKey, IReadOnlyDictionary createdNodes, bool disallowNullableOutput = false, bool isRequired = false)" + Environment.NewLine + " where T : struct"; var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.True(generatedCode.Contains(methodSignatureV1) || generatedCode.Contains(methodSignatureV2) || generatedCode.Contains(methodSignatureV3)); } #endregion #region ConnectPortsClass [Fact] public void ConnectPortsClass_ShouldExistInGeneratedCode() { const string methodSignatureV1 = "private void ConnectPortsClass(string pipelineId, string nodeId,string nodeType, INodeField? nodeInput, string nodeInputName, Dictionary inputs, string inputKey, IReadOnlyDictionary createdNodes, bool disallowNullableOutput = false, bool isRequired = false) where T : class"; const string methodSignatureV2 = "private void ConnectPortsClass(string pipelineId, string nodeId,string nodeType, INodeField? nodeInput, string nodeInputName, Dictionary inputs, string inputKey, IReadOnlyDictionary createdNodes, bool disallowNullableOutput = false, bool isRequired = false)\n where T : class"; var methodSignatureV3 = "private void ConnectPortsClass(string pipelineId, string nodeId,string nodeType, INodeField? nodeInput, string nodeInputName, Dictionary inputs," + Environment.NewLine + " string inputKey, IReadOnlyDictionary createdNodes, bool disallowNullableOutput = false, bool isRequired = false)" + Environment.NewLine + " where T : class"; var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.True(generatedCode.Contains(methodSignatureV1) || generatedCode.Contains(methodSignatureV2) || generatedCode.Contains(methodSignatureV3)); } #endregion #region TryConnectPorts [Fact] public void TryConnectPorts_ShouldExistInGeneratedCode() { const string methodSignatureV1 = "private PortConnectionInfo? TryConnectPorts(string pipelineId, string nodeId, string nodeType, INodeField? nodeInput, string nodeInputName, Dictionary inputs, string inputKey, IReadOnlyDictionary createdNodes, bool isRequired = false) where T : notnull"; const string methodSignatureV2 = "private PortConnectionInfo? TryConnectPorts(string pipelineId, string nodeId, string nodeType, INodeField? nodeInput, string nodeInputName, Dictionary inputs, string inputKey, IReadOnlyDictionary createdNodes, bool isRequired = false)\n where T : notnull"; var methodSignatureV3 = "private PortConnectionInfo? TryConnectPorts(string pipelineId, string nodeId, string nodeType, INodeField? nodeInput, string nodeInputName," + Environment.NewLine + " Dictionary inputs, string inputKey, IReadOnlyDictionary createdNodes, bool isRequired = false)" + Environment.NewLine + " where T : notnull"; var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.True(generatedCode.Contains(methodSignatureV1) || generatedCode.Contains(methodSignatureV2) || generatedCode.Contains(methodSignatureV3)); } #endregion #region GetDefaultValue [Fact] public void GetDefaultValue_ShouldExistInGeneratedCode() { const string methodSignature = "private static T? GetDefaultValue()"; var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } #endregion [Fact] public void PortConnectionInfo_ShouldBeDeclaredInGeneratedCode() { const string recordDeclaration = "private record PortConnectionInfo(bool Connected, object? OutputFieldObj, string FromPortName, string FromNodeType, string FromNodeId);"; var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(recordDeclaration, generatedCode); } #region InitializeNodeFields [Fact] public void InitializeNodeFieldsMethod_ShouldExistInGeneratedCode() { var methodSignature = "private void InitializeNodeFields(string pipelineId, INode node, string nodeId)"; // var methodSignature = $"private static Dictionary Get__{GetNodeDescriptorFromMockNode(mockNodeType).TypeNameShortSanitized}__NodePorts(string nodeId)"; var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } [Fact] public void InitializeNodeFieldsMethod_ContainsSwitchCases() { var model = TestNodeModelFactory.CreateMultipleNodesModel(); var methodName = "InitializeNodeFields"; var tCountByNodes = new Dictionary(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); foreach (var node in model.Nodes) { Assert.Contains($"case {node.TypeNameFull} t", methodCode); if (!tCountByNodes.TryGetValue(node, out var value)) tCountByNodes.Add(node, 1); else tCountByNodes[node] = ++value; } Assert.Contains( "default:" + Environment.NewLine + " throw new NodeNotFoundException(pipelineId, node.GetType().Name, nodeId);", methodCode); Assert.DoesNotContain(tCountByNodes.Values, q => q > 1); } #endregion #region InitializeNodeFieldsSpecific [Theory] [InlineData(typeof(ThreeParameterNode), "CreateThreeParameterNodeModel")] [InlineData(typeof(ThreePortNode), "CreateThreePortNodeModel")] [InlineData(typeof(SimpleNamedNode), "CreateSimpleNamedNodeModel")] public void SpecificInitializeNodeFieldsMethod_ShouldExistInGeneratedCode(Type mockNodeType, string factoryMethod) { var nodeDescriptor = TestHelper.GetNodeDescriptorFromMockNode(mockNodeType); var methodSignature = $"private static void Initialize__{nodeDescriptor.TypeNameShortSanitized}__NodeFields({nodeDescriptor.TypeNameFull} node, string nodeId)"; var model = TestHelper.GetNodeModel(factoryMethod); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } [Theory] [InlineData(typeof(ThreeParameterNode), "CreateThreeParameterNodeModel")] [InlineData(typeof(ThreePortNode), "CreateThreePortNodeModel")] [InlineData(typeof(SimpleNamedNode), "CreateSimpleNamedNodeModel")] public void SpecificInitializeNodeFieldsMethod_InitializesAllFields(Type mockNodeType, string factoryMethod) { var nodeDescriptor = TestHelper.GetNodeDescriptorFromMockNode(mockNodeType); var methodName = $"Initialize__{nodeDescriptor.TypeNameShortSanitized}__NodeFields"; var model = TestHelper.GetNodeModel(factoryMethod); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); foreach (var nodeField in nodeDescriptor.Fields) { var nodeFieldType = string.Concat(nodeField.ValueType, nodeField.IsNullableValueType ? "?" : string.Empty); var initNodeFieldCodeV1 = $"node.{nodeField.PropertyName} = new NodePipeline.Abstractions.Models.NodeField<{nodeFieldType}>()" + Environment.NewLine + " {" + Environment.NewLine + $" Name = \"{nodeField.FieldName}\"," + Environment.NewLine + $" Code = NodeFieldCodeGenerator.GenerateFieldCode(nodeId, \"{nodeField.FieldName}\", FieldDirection.{nodeField.Direction.ToString()})," + Environment.NewLine + $" Direction = FieldDirection.{nodeField.Direction.ToString()}," + Environment.NewLine + " };"; var initNodeFieldCodeV2 = $"node.{nodeField.PropertyName} = new NodePipeline.Abstractions.Models.NodeField<{nodeFieldType}>()" + Environment.NewLine + " {" + Environment.NewLine + $" Name = \"{nodeField.FieldName}\"," + Environment.NewLine + $" Code = NodeFieldCodeGenerator.GenerateFieldCode(nodeId, \"{nodeField.FieldName}\", FieldDirection.{nodeField.Direction.ToString()})," + Environment.NewLine + $" Direction = FieldDirection.{nodeField.Direction.ToString()}," + Environment.NewLine + " // Description intentionally omitted for runtime" + Environment.NewLine + " };"; Assert.True(methodCode.Contains(initNodeFieldCodeV1) || methodCode.Contains(initNodeFieldCodeV2)); } } #endregion #region GetParameterCodes [Fact] public void GetParameterCodesMethod_ContainsAllNodeCases() { var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, "GetParameterCodes"); Assert.False(string.IsNullOrEmpty(methodCode), "GetParameterCodes method not found in generated code."); foreach (var node in model.Nodes) { var caseLabel = $"case var t when t == NodeNamePrefixSettings.TrimNodeType(\"{node.Type}\"):"; var caseBlock = TestHelper.ExtractSwitchCase(methodCode, caseLabel); Assert.False(string.IsNullOrEmpty(caseBlock), $"Case block for node {(string.IsNullOrEmpty(node.Type) ? node.TypeNameFull : node.Type)} not found."); Assert.Contains($"return GetParameterCodesFor__{node.TypeNameShortSanitized}__Node();", caseBlock); } } [Fact] public void Generate_GetParameterCodesMethod_ContainsAllNodeCasesWithCorrectReturnMethods() { var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, "GetParameterCodes"); Assert.False(string.IsNullOrEmpty(methodCode), "GetParameterCodes method not found in generated code."); foreach (var node in model.Nodes) { var caseLabel = $"case var t when t == NodeNamePrefixSettings.TrimNodeType(\"{node.Type}\"):"; var caseBlock = TestHelper.ExtractSwitchCase(methodCode, caseLabel); Assert.False(string.IsNullOrEmpty(caseBlock), $"Case block for node {(string.IsNullOrEmpty(node.Type) ? node.TypeNameFull : node.Type)} not found."); var expectedMethodName = $"return GetParameterCodesFor__{node.TypeNameShortSanitized}__Node();"; Assert.Contains(expectedMethodName, caseBlock); } Assert.Contains( "default:" + Environment.NewLine + " throw new NodeTypeNotFoundException(nodeType);", methodCode); } #endregion #region GetParameterCodesSpecific [Theory] [InlineData(typeof(OneParameterNode), "CreateMultipleNodesModel")] [InlineData(typeof(SimpleNamedNode), "CreateSimpleNamedNodeModel")] public void SpecificGetParameterCodesMethod_ShouldExistInGeneratedCode(Type mockNodeType, string factoryMethod) { var methodSignature = $"private IEnumerable GetParameterCodesFor__{TestHelper.GetNodeDescriptorFromMockNode(mockNodeType).TypeNameShortSanitized}__Node()"; var model = TestHelper.GetNodeModel(factoryMethod); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } [Fact] public void SpecificGetParameterCodesMethod_HasCorrectName() { var nodeDescriptor = SimpleNamedNode.GetDescriptor(); var methodSignature = $"private IEnumerable GetParameterCodesFor__{nodeDescriptor.TypeNameShortSanitized}__Node()"; var model = TestNodeModelFactory.CreateSimpleNamedNodeModel(); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } [Fact] public void SpecificGetParameterCodesMethod_ContainsAllParameters() { var methodName = $"GetParameterCodesFor__{typeof(ThreeParameterNode).FullName}__Node"; var generatedCode = NodeFactoryGenerator.Generate(TestNodeModelFactory.CreateThreeParameterNodeModel()); var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); Assert.False(string.IsNullOrEmpty(methodCode), $"{methodName} method not found."); var model = TestNodeModelFactory.CreateThreeParameterNodeModel(); var node = model.Nodes.First(n => n.Type == nameof(ThreeParameterNode)); Assert.NotNull(node); var nodeParameters = node.Fields.Where(q => q.Direction == FieldDirection.Parameter); foreach (var param in nodeParameters) Assert.Contains($"\"{param.FieldName}\"", methodCode); } [Fact] public void SpecificGetParameterCodesMethod_ContainsAllParameterNamesWithAttributeSupport() { var model = TestNodeModelFactory.CreateThreeParameterNodeModel(); var node = model.Nodes[0]; var methodName = $"GetParameterCodesFor__{node.TypeNameShortSanitized}__Node"; var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); Assert.False(string.IsNullOrEmpty(methodCode), $"Method {methodName} not found in generated code."); foreach (var param in node.Fields.Where(q => q.Direction == FieldDirection.Parameter)) Assert.Contains($"\"{param.FieldName}\"", methodCode); } #endregion #region GetNodePorts [Fact] public void GetNodePortsMethod_ShouldExistInGeneratedCode() { const string methodSignature = "private Dictionary GetNodePorts(string pipelineId, string nodeType, string nodeId)"; var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } [Fact] public void GetNodePortsMethod_ContainsAllNodeCases() { var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, "GetNodePorts"); Assert.False(string.IsNullOrEmpty(methodCode), "GetNodePorts method not found in generated code."); foreach (var node in model.Nodes) { var nodeType = node.Type; var caseLabel = $"case var t when t == NodeNamePrefixSettings.TrimNodeType(\"{nodeType}\"):"; var caseBlock = TestHelper.ExtractSwitchCase(methodCode, caseLabel); Assert.False(string.IsNullOrEmpty(caseBlock), $"Case block for node {nodeType} not found."); Assert.Contains($"return Get__{node.TypeNameShortSanitized}__NodePorts(nodeId);", caseBlock); } Assert.Contains( "default:" + Environment.NewLine + " throw new NodeNotFoundException(pipelineId, nodeType, nodeId);", methodCode); } #endregion #region GetNodePortsSpecific [Theory] [InlineData(typeof(ThreeParameterNode), "CreateThreeParameterNodeModel")] [InlineData(typeof(SimpleNamedNode), "CreateSimpleNamedNodeModel")] public void SpecificGetNodePortsMethod_ShouldExistInGeneratedCode(Type mockNodeType, string factoryMethod) { var methodSignature = $"private static Dictionary Get__{TestHelper.GetNodeDescriptorFromMockNode(mockNodeType).TypeNameShortSanitized}__NodePorts(string nodeId)"; var model = TestHelper.GetNodeModel(factoryMethod); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } [Fact] public void SpecificGetNodePortsMethod_ContainsAllNodeCases() { var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, "GetNodePorts"); Assert.False(string.IsNullOrEmpty(methodCode), "GetNodePorts method not found in generated code."); foreach (var node in model.Nodes) { var nodeType = string.IsNullOrEmpty(node.Type) ? node.TypeNameFull : node.Type; var caseLabel = $"case var t when t == NodeNamePrefixSettings.TrimNodeType(\"{nodeType}\"):"; var caseBlock = TestHelper.ExtractSwitchCase(methodCode, caseLabel); Assert.False(string.IsNullOrEmpty(caseBlock), $"Case block for node {nodeType} not found."); Assert.Contains($"return Get__{node.TypeNameShortSanitized}__NodePorts(nodeId);", caseBlock); } Assert.Contains( "default:" + Environment.NewLine + " throw new NodeNotFoundException(pipelineId, nodeType, nodeId);", methodCode); } [Fact] public void SpecificGetNodePortsMethod_ContainsCorrectInputProperty() { var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); foreach (var node in model.Nodes) { var methodName = $"Get__{node.TypeNameShortSanitized}__NodePorts"; var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); Assert.False(string.IsNullOrEmpty(methodCode), $"Method {methodName} not found in generated code."); foreach (var field in node.Fields.Where(f => f.Direction is FieldDirection.Input or FieldDirection.Output)) { var isInputString = (field.Direction == FieldDirection.Input).ToString().ToLower(); Assert.Contains($"new NodePortKey(nodeId, \"{field.FieldName}\", {isInputString})", methodCode); var portInfoParameters = ExtractNodePortInfoParametersFromMethodCode(methodCode, field.FieldName); Assert.Equal(isInputString, portInfoParameters.ElementAtOrDefault(5)); } } } [Fact] public void SpecificGetNodePortsMethod_ContainsCorrectPortValueType() { var model = TestNodeModelFactory.CreateThreePortNodeModel(); var generatedCode = NodeFactoryGenerator.Generate(model); foreach (var node in model.Nodes) { var methodName = $"Get__{node.TypeNameShortSanitized}__NodePorts"; var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); Assert.False(string.IsNullOrEmpty(methodCode), $"Method {methodName} not found in generated code."); foreach (var field in node.Fields.Where(f => f.Direction is FieldDirection.Input or FieldDirection.Output)) { var portInfoParameters = ExtractNodePortInfoParametersFromMethodCode(methodCode, field.FieldName); Assert.Equal($"typeof({field.ValueType})", portInfoParameters.ElementAtOrDefault(1)); Assert.Equal(field.IsNullableValueType.ToString().ToLower(), portInfoParameters.ElementAtOrDefault(2)); } } } [Theory] [InlineData("CreateThreePortNodeModel")] [InlineData("CreateThreePortNode2Model")] public void SpecificGetNodePortsMethod_ContainsCorrectRequiredProperty(string factoryMethod) { var model = TestHelper.GetNodeModel(factoryMethod); var generatedCode = NodeFactoryGenerator.Generate(model); foreach (var node in model.Nodes) { var methodName = $"Get__{node.TypeNameShortSanitized}__NodePorts"; var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); Assert.False(string.IsNullOrEmpty(methodCode), $"Method {methodName} not found in generated code."); foreach (var field in node.Fields.Where(f => f.Direction is FieldDirection.Input or FieldDirection.Output)) { var isRequired = field.Metadata?.IsRequired == true; var portInfoParameters = ExtractNodePortInfoParametersFromMethodCode(methodCode, field.FieldName); Assert.Equal(isRequired.ToString().ToLower(), portInfoParameters.ElementAtOrDefault(3)); } } } [Theory] [InlineData("CreateThreePortNodeModel")] [InlineData("CreateThreePortNode2Model")] public void SpecificGetNodePortsMethod_ContainsCorrectDisallowNullableInputProperty(string factoryMethod) { var model = TestHelper.GetNodeModel(factoryMethod); var generatedCode = NodeFactoryGenerator.Generate(model); foreach (var node in model.Nodes) { var methodName = $"Get__{node.TypeNameShortSanitized}__NodePorts"; var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); Assert.False(string.IsNullOrEmpty(methodCode), $"Method {methodName} not found in generated code."); foreach (var field in node.Fields.Where(f => f.Direction is FieldDirection.Input or FieldDirection.Output)) { var disallowNullableOutput = field.Metadata?.DisallowNullableOutput == true; var portInfoParameters = ExtractNodePortInfoParametersFromMethodCode(methodCode, field.FieldName); Assert.Equal(disallowNullableOutput.ToString().ToLower(), portInfoParameters.ElementAtOrDefault(4)); } } } private static string[] ExtractNodePortInfoParameters(string nodePortInfoCode) { const string prefix = "new NodePortInfo("; if (nodePortInfoCode.Length < prefix.Length + 2) throw new Exception(); var parametersString = nodePortInfoCode.Substring(prefix.Length, nodePortInfoCode.Length - (prefix.Length + 1)); return parametersString.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); } private static string[] ExtractNodePortInfoParametersFromMethodCode(string methodCode, string fieldName) { var portInfoIndex = methodCode.IndexOf($"new NodePortInfo(\"{fieldName}\"", StringComparison.Ordinal); var endIndex = methodCode.IndexOf(Environment.NewLine, portInfoIndex + 1, StringComparison.Ordinal); return ExtractNodePortInfoParameters(methodCode.Substring(portInfoIndex + 1, endIndex - portInfoIndex - 1)); } #endregion #region ReadParameterValue [Fact] public void ReadParameterValue_ShouldExistInGeneratedCode() { const string methodSignature = "public object? ReadParameterValue(string pipelineId, string nodeId, string nodeType, string parameterName, string? valueString)"; var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } [Fact] public void ReadParameterValue_ContainsAllNodeCases() { var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, "ReadParameterValue"); Assert.False(string.IsNullOrEmpty(methodCode), "ReadParameterValue method not found in generated code."); foreach (var node in model.Nodes) { var nodeType = node.Type; var caseLabel = $"case var t when t == NodeNamePrefixSettings.TrimNodeType(\"{nodeType}\"):"; var caseBlock = TestHelper.ExtractSwitchCase(methodCode, caseLabel); Assert.False(string.IsNullOrEmpty(caseBlock), $"Case block for node {nodeType} not found."); Assert.Contains( $"return ReadParameterValueFrom__{node.TypeNameShortSanitized}__Node(pipelineId, nodeType, nodeId, parameterName, valueString);", caseBlock); } Assert.Contains( "default:" + Environment.NewLine + " throw new NodeTypeNotFoundException(nodeType);", methodCode); } #endregion #region ReadParameterValueSpecific [Theory] [InlineData(typeof(ThreeParameterNode), "CreateThreeParameterNodeModel")] [InlineData(typeof(SimpleNamedNode), "CreateSimpleNamedNodeModel")] public void SpecificReadParameterValueMethod_ShouldExistInGeneratedCode(Type mockNodeType, string factoryMethod) { var methodSignature = $"private static object? ReadParameterValueFrom__{TestHelper.GetNodeDescriptorFromMockNode(mockNodeType).TypeNameShortSanitized}__Node(string pipelineId, string nodeType, string nodeId, string parameterName, string? valueString)"; var model = TestHelper.GetNodeModel(factoryMethod); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } [Fact] public void SpecificReadParameterValueMethod_ContainsAllParameters() { var nodeDescriptor = ThreeParameterNode.GetDescriptor(); var methodName = $"ReadParameterValueFrom__{nodeDescriptor.TypeNameShortSanitized}__Node"; var model = TestNodeModelFactory.CreateThreeParameterNodeModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); Assert.False(string.IsNullOrEmpty(methodCode), $"{methodName} method not found in generated code."); foreach (var parameter in nodeDescriptor.Fields.Where(f => f.Direction == FieldDirection.Parameter)) { var parameterName = parameter.FieldName; var caseLabel = $"case \"{parameterName}\":"; var caseBlock = TestHelper.ExtractSwitchCase(methodCode, caseLabel); Assert.False(string.IsNullOrEmpty(caseBlock), $"Case block for parameter {parameter} not found."); } Assert.Contains( "default:" + Environment.NewLine + " throw new NodeParameterNotFoundException(pipelineId, parameterName, nodeType, nodeId);", methodCode); } [Fact] public void SpecificReadParameterValueMethod_ReturnsCallsMethodWithAppropriateType() { var nodeDescriptor = ThreeParameterNode.GetDescriptor(); var methodName = $"ReadParameterValueFrom__{nodeDescriptor.TypeNameShortSanitized}__Node"; var model = TestNodeModelFactory.CreateThreeParameterNodeModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); Assert.False(string.IsNullOrEmpty(methodCode), $"{methodName} method not found in generated code."); foreach (var parameter in nodeDescriptor.Fields.Where(f => f.Direction == FieldDirection.Parameter)) { var parameterName = parameter.FieldName; var caseLabel = $"case \"{parameterName}\":"; var caseBlock = TestHelper.ExtractSwitchCase(methodCode, caseLabel); Assert.False(string.IsNullOrEmpty(caseBlock), $"Case block for parameter {parameter} not found."); Assert.Contains($"return ParseParameter<{parameter.ValueType}>(valueString);", caseBlock); } } #endregion #region GetNodeOutputs [Fact] public void GetNodeOutputs_ShouldExistInGeneratedCode() { const string methodSignature = "private IReadOnlyDictionary GetNodeOutputs(string pipelineId, string nodeId, INode node)"; var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } [Fact] public void GetNodeOutputs_ContainsAllNodeCases() { var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, "GetNodeOutputs"); Assert.False(string.IsNullOrEmpty(methodCode), "ReadParameterValue method not found in generated code."); 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, caseLabel); Assert.False(string.IsNullOrEmpty(caseBlock), $"Case block for node {node.Type} not found."); Assert.Contains($"return Get__{node.TypeNameShortSanitized}__NodeOutputs(nodeId, t{tNumberString});", caseBlock); } Assert.Contains( "default:" + Environment.NewLine + " throw new NodeNotFoundException(pipelineId, node.GetType().Name, nodeId);", methodCode); } #endregion #region GetNodeOutputsSpecific [Theory] [InlineData(typeof(ThreeParameterNode), "CreateThreeParameterNodeModel")] [InlineData(typeof(SimpleNamedNode), "CreateSimpleNamedNodeModel")] public void SpecificGetNodeOutputsMethod_ShouldExistInGeneratedCode(Type mockNodeType, string factoryMethod) { var nodeDescriptor = TestHelper.GetNodeDescriptorFromMockNode(mockNodeType); var methodSignature = $"private IReadOnlyDictionary Get__{nodeDescriptor.TypeNameShortSanitized}__NodeOutputs(string nodeId, {nodeDescriptor.TypeNameFull} node)"; var model = TestHelper.GetNodeModel(factoryMethod); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } [Fact] public void SpecificGetNodeOutputsMethod_ContainsAllOutputPorts() { var nodeDescriptor = ThreePortNode.GetDescriptor(); var methodName = $"Get__{nodeDescriptor.TypeNameShortSanitized}__NodeOutputs"; var model = TestNodeModelFactory.CreateThreePortNodeModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); Assert.False(string.IsNullOrEmpty(methodCode), $"{methodName} method not found in generated code."); foreach (var outputPort in nodeDescriptor.Fields.Where(f => f.Direction == FieldDirection.Output)) Assert.Contains( $"{{ NodeFieldCodeGenerator.GenerateFieldCode(nodeId, \"{outputPort.FieldName}\", FieldDirection.Output), node.{outputPort.PropertyName} }},", methodCode); } #endregion #region SetNodeParametersValues [Fact] public void SetNodeParametersValues_ShouldExistInGeneratedCode() { const string methodSignature = "public void SetNodeParametersValues(string pipelineId, TNode node, string nodeId, Dictionary parameters) where TNode : INode"; var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } [Fact] public void SetNodeParametersValues_ContainsAllNodeCases() { var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, "SetNodeParametersValues"); Assert.False(string.IsNullOrEmpty(methodCode), "SetNodeParametersValues method not found in generated code."); 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, caseLabel); Assert.False(string.IsNullOrEmpty(caseBlock), $"Case block for node {node.Type} not found."); var expectedString = $"Set__{node.TypeNameShortSanitized}__NodeParametersValues(t{tNumberString}, nodeId, parameters);" + Environment.NewLine + " break;"; Assert.Contains(expectedString, caseBlock); } Assert.Contains( "default:" + Environment.NewLine + " throw new NodeNotFoundException(pipelineId, node.GetType().Name, nodeId);", methodCode); } #endregion #region SetNodeParametersValuesSpecific [Theory] [InlineData(typeof(ThreeParameterNode), "CreateThreeParameterNodeModel")] [InlineData(typeof(SimpleNamedNode), "CreateSimpleNamedNodeModel")] public void SpecificSetNodeParametersValuesMethod_ShouldExistInGeneratedCode(Type mockNodeType, string factoryMethod) { var nodeDescriptor = TestHelper.GetNodeDescriptorFromMockNode(mockNodeType); var methodSignature = $"private static void Set__{nodeDescriptor.TypeNameShortSanitized}__NodeParametersValues({nodeDescriptor.TypeNameFull} node, string nodeId, Dictionary parameters)"; var model = TestHelper.GetNodeModel(factoryMethod); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } [Fact] public void SpecificSetNodeParametersValuesMethod_ContainsAllParameters() { var nodeDescriptor = ThreeParameterNode.GetDescriptor(); var methodName = $"Set__{nodeDescriptor.TypeNameShortSanitized}__NodeParametersValues"; var model = TestNodeModelFactory.CreateThreeParameterNodeModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); Assert.False(string.IsNullOrEmpty(methodCode), $"{methodName} method not found in generated code."); foreach (var parameter in nodeDescriptor.Fields.Where(f => f.Direction == FieldDirection.Parameter)) { // var ifString = methodCode[methodCode.IndexOf($"if (parameters.TryGetValue(\"{parameter.FieldName}\", out var val{i})")..] var ifString = methodCode[ methodCode.IndexOf($"if (parameters.TryGetValue(\"{parameter.FieldName}\", out var val", StringComparison.Ordinal)..] .Split(Environment.NewLine)[0]; var valueNumberString = ifString[ifString.IndexOf("out var ", StringComparison.Ordinal)..].Split(')')[0][11..]; var valueNumber = int.Parse(valueNumberString); // var parameterType = string.Concat(parameter.ValueType, parameter.IsNullableValueType ? "?" : string.Empty); var parameterType = parameter.ValueType; var setParameterCode = $"if (parameters.TryGetValue(\"{parameter.FieldName}\", out var val{valueNumber}) && val{valueNumber} is {parameterType} typedVal{valueNumber})" + Environment.NewLine + $" node.{parameter.PropertyName}.Value = typedVal{valueNumberString};"; Assert.Contains(setParameterCode, methodCode); } } #endregion #region GetParameterDefaultValue [Fact] public void GetParameterDefaultValue_ShouldExistInGeneratedCode() { const string methodSignature = "public object? GetParameterDefaultValue(string nodeType, string parameterName)"; var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } [Fact] public void GetParameterDefaultValue_ContainsAllNodeCases() { var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var codeSubstring = generatedCode[ generatedCode.IndexOf("public object? GetParameterDefaultValue(", StringComparison.Ordinal)..]; var methodCode = TestHelper.ExtractMethod(codeSubstring, "GetParameterDefaultValue"); Assert.False(string.IsNullOrEmpty(methodCode), "GetParameterDefaultValue method not found in generated code."); foreach (var node in model.Nodes) { var caseLabel = $"case var t when t == NodeNamePrefixSettings.TrimNodeType(\"{node.Type}\")"; var caseBlock = TestHelper.ExtractSwitchCase(methodCode, caseLabel); Assert.False(string.IsNullOrEmpty(caseBlock), $"Case block for node {node.Type} not found."); Assert.Contains($"return GetParameterDefaultValueFor__{node.TypeNameShortSanitized}__Node(parameterName);", caseBlock); } Assert.Contains( "default:" + Environment.NewLine + " throw new NodeParameterNotFoundException(parameterName, nodeType);", methodCode); } #endregion #region GetParameterDefaultValueSpecific [Theory] [InlineData(typeof(ThreeParameterNode), "CreateThreeParameterNodeModel")] [InlineData(typeof(SimpleNamedNode), "CreateSimpleNamedNodeModel")] public void SpecificGetParameterDefaultValueMethod_ShouldExistInGeneratedCode(Type mockNodeType, string factoryMethod) { var methodSignature = $"private static object? GetParameterDefaultValueFor__{TestHelper.GetNodeDescriptorFromMockNode(mockNodeType).TypeNameShortSanitized}__Node(string parameterName)"; var model = TestHelper.GetNodeModel(factoryMethod); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } [Fact] public void SpecificGetParameterDefaultValueMethod_ContainsAllParameters() { var nodeDescriptor = ThreeParameterNode.GetDescriptor(); var methodName = $"GetParameterDefaultValueFor__{nodeDescriptor.TypeNameShortSanitized}__Node"; var model = TestNodeModelFactory.CreateThreeParameterNodeModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); Assert.False(string.IsNullOrEmpty(methodCode), $"{methodName} method not found in generated code."); foreach (var parameter in nodeDescriptor.Fields.Where(f => f.Direction == FieldDirection.Parameter)) { var parameterName = parameter.FieldName; var caseLabel = $"case \"{parameterName}\":"; var caseBlock = TestHelper.ExtractSwitchCase(methodCode, caseLabel); Assert.False(string.IsNullOrEmpty(caseBlock), $"Case block for parameter {parameter} not found."); } Assert.Contains( "default:" + Environment.NewLine + $" throw new NodeParameterNotFoundException(parameterName, \"{nodeDescriptor.Type}\");", methodCode); } [Fact] public void SpecificGetParameterDefaultValueMethod_CallMethodWithAppropriateType() { var nodeDescriptor = ThreeParameterNode.GetDescriptor(); var methodName = $"GetParameterDefaultValueFor__{nodeDescriptor.TypeNameShortSanitized}__Node"; var model = TestNodeModelFactory.CreateThreeParameterNodeModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); Assert.False(string.IsNullOrEmpty(methodCode), $"{methodName} method not found in generated code."); foreach (var parameter in nodeDescriptor.Fields.Where(f => f.Direction == FieldDirection.Parameter)) { var parameterName = parameter.FieldName; var caseLabel = $"case \"{parameterName}\":"; var caseBlock = TestHelper.ExtractSwitchCase(methodCode, caseLabel); //TODO: or without "?"? var parameterType = string.Concat(parameter.ValueType, parameter.IsNullableValueType ? "?" : string.Empty); if (parameter.Metadata?.DefaultValue == null) Assert.Contains($"return GetDefaultValue<{parameterType}>();", caseBlock); } } [Fact] public void SpecificGetParameterDefaultValueMethod_ReturnsCallsMethodWithAppropriateType() { var nodeDescriptor = ThreeParameterNode.GetDescriptor(); var methodName = $"GetParameterDefaultValueFor__{nodeDescriptor.TypeNameShortSanitized}__Node"; var model = TestNodeModelFactory.CreateThreeParameterNodeModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); Assert.False(string.IsNullOrEmpty(methodCode), $"{methodName} method not found in generated code."); var parametersWithDefaultValues = nodeDescriptor.Fields .Where(f => f is { Direction: FieldDirection.Parameter, Metadata.DefaultValue: not null }); foreach (var parameter in parametersWithDefaultValues) { var parameterName = parameter.FieldName; var caseLabel = $"case \"{parameterName}\":"; var caseBlock = TestHelper.ExtractSwitchCase(methodCode, caseLabel); Assert.Contains($"return {parameter.Metadata!.DefaultValue};", caseBlock); } } //TODO: test OneParameterWithoutParameterlessConstructorNode and parameter without ParameterlessConstructor #endregion #region GetNodeType [Fact] public void GetNodeType_ShouldExistInGeneratedCode() { const string methodSignature = "private static string GetNodeType(string pipelineId, string nodeId, INode node)"; var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } [Fact] public void GetNodeType_ContainsAllNodeCases() { var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, "GetNodeType"); Assert.False(string.IsNullOrEmpty(methodCode), "GetNodeType method not found in generated code."); foreach (var node in model.Nodes) { var @case = $"case {node.TypeNameFull}:"; var caseLabel = methodCode[methodCode.IndexOf(@case, StringComparison.Ordinal)..].Split(':')[0]; var caseBlock = TestHelper.ExtractSwitchCase(methodCode, caseLabel); Assert.False(string.IsNullOrEmpty(caseBlock), $"Case block for node {node.Type} not found."); Assert.Contains($"return NodeNamePrefixSettings.TrimNodeType(\"{node.Type}\");", caseBlock); } Assert.Contains( "default:" + Environment.NewLine + " throw new NodeNotFoundException(pipelineId, node.GetType().Name, nodeId);", methodCode); } #endregion #region ConnectPorts [Fact] public void ConnectPorts_ShouldExistInGeneratedCode() { const string methodSignature = "public void ConnectPorts(string pipelineId, TNode node, string nodeId, Dictionary inputs, IReadOnlyDictionary createdNodes) where TNode : INode"; var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } [Fact] public void ConnectPorts_ContainsAllNodeCases() { var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, "ConnectPorts"); Assert.False(string.IsNullOrEmpty(methodCode), "ConnectPorts method not found in generated code."); 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, caseLabel); Assert.False(string.IsNullOrEmpty(caseBlock), $"Case block for node {node.Type} not found."); var caseText = $"Connect__{node.TypeNameShortSanitized}__NodePorts(pipelineId, t{tNumberString}, nodeId, inputs, createdNodes);" + Environment.NewLine + " break;"; Assert.Contains(caseText, caseBlock); } Assert.Contains( "default:" + Environment.NewLine + " throw new NodeNotFoundException(pipelineId, node.GetType().Name, nodeId);", methodCode); } #endregion #region ConnectPortsSpecific [Theory] [InlineData(typeof(ThreePortNode), "CreateThreePortNodeModel")] [InlineData(typeof(ThreePortNode2), "CreateThreePortNode2Model")] [InlineData(typeof(SimpleNamedNode), "CreateSimpleNamedNodeModel")] public void SpecificConnectPortsMethod_ShouldExistInGeneratedCode(Type mockNodeType, string factoryMethod) { var nodeDescriptor = TestHelper.GetNodeDescriptorFromMockNode(mockNodeType); var methodSignature = $"private void Connect__{nodeDescriptor.TypeNameShortSanitized}__NodePorts(string pipelineId, {nodeDescriptor.TypeNameFull} node, string nodeId, Dictionary inputs, IReadOnlyDictionary createdNodes)"; var model = TestHelper.GetNodeModel(factoryMethod); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } [Fact] public void SpecificMethod_CallsBindingMethodForAllNodeOutputPorts() { var nodeDescriptor = ThreePortNode.GetDescriptor(); var methodName = $"Connect__{nodeDescriptor.TypeNameShortSanitized}__NodePorts"; var model = TestNodeModelFactory.CreateThreePortNodeModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); Assert.False(string.IsNullOrEmpty(methodCode), $"{methodName} method not found in generated code."); foreach (var port in nodeDescriptor.Fields.Where(f => f.Direction == FieldDirection.Input || f.Direction == FieldDirection.Output)) { var connectPortMethodCallStringV1 = $"ConnectPortsStruct(pipelineId, nodeId, \"{nodeDescriptor.TypeNameShort}\", node.{port.PropertyName}, nameof(node.{port.PropertyName}), inputs, \"{port.FieldName}\", createdNodes"; var connectPortMethodCallStringV2 = $"ConnectPortsClass(pipelineId, nodeId, \"{nodeDescriptor.TypeNameShort}\", node.{port.PropertyName}, nameof(node.{port.PropertyName}), inputs, \"{port.FieldName}\", createdNodes"; if (port.Direction == FieldDirection.Input) Assert.True(methodCode.Contains(connectPortMethodCallStringV1) || methodCode.Contains(connectPortMethodCallStringV2)); else Assert.False(methodCode.Contains(connectPortMethodCallStringV1) || methodCode.Contains(connectPortMethodCallStringV2)); } } [Theory] [InlineData(typeof(ThreePortNode), "CreateThreePortNodeModel")] [InlineData(typeof(ThreePortNode2), "CreateThreePortNode2Model")] [InlineData(typeof(SimpleNamedNode), "CreateSimpleNamedNodeModel")] public void SpecificMethod_ConnectPortsMethodCallOfRequiredInputPortShouldHaveIsRequiredParameterSet( Type mockNodeType, string factoryMethod) { var nodeDescriptor = TestHelper.GetNodeDescriptorFromMockNode(mockNodeType); var methodName = $"Connect__{nodeDescriptor.TypeNameShortSanitized}__NodePorts"; var model = TestHelper.GetNodeModel(factoryMethod); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); Assert.False(string.IsNullOrEmpty(methodCode), $"{methodName} method not found in generated code."); foreach (var port in nodeDescriptor.Fields.Where(f => f.Direction == FieldDirection.Input)) { var connectPortMethodCallString = $"ConnectPortsStruct(pipelineId, nodeId, \"{nodeDescriptor.TypeNameShort}\", node.{port.PropertyName}, nameof(node.{port.PropertyName}), inputs, \"{port.FieldName}\", createdNodes"; var parametersString = methodCode[ (methodCode.IndexOf(connectPortMethodCallString, StringComparison.Ordinal) + connectPortMethodCallString.Length)..].Split(';')[0]; if (port.Metadata?.IsRequired == true) Assert.Contains("isRequired: true", parametersString); } } [Theory] [InlineData(typeof(ThreePortNode), "CreateThreePortNodeModel")] [InlineData(typeof(ThreePortNode2), "CreateThreePortNode2Model")] [InlineData(typeof(SimpleNamedNode), "CreateSimpleNamedNodeModel")] public void SpecificMethod_ConnectPortsMethodCallOfRequiredInputPortShouldHaveDisallowNullableOutputParameterSet( Type mockNodeType, string factoryMethod) { var nodeDescriptor = TestHelper.GetNodeDescriptorFromMockNode(mockNodeType); var methodName = $"Connect__{nodeDescriptor.TypeNameShortSanitized}__NodePorts"; var model = TestHelper.GetNodeModel(factoryMethod); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); Assert.False(string.IsNullOrEmpty(methodCode), $"{methodName} method not found in generated code."); foreach (var port in nodeDescriptor.Fields.Where(f => f.Direction == FieldDirection.Input)) { var connectPortMethodCallStringV1 = $"ConnectPortsStruct(pipelineId, nodeId, \"{nodeDescriptor.TypeNameShort}\", node.{port.PropertyName}, nameof(node.{port.PropertyName}), inputs, \"{port.FieldName}\", createdNodes"; var connectPortMethodCallStringV2 = $"ConnectPortsClass(pipelineId, nodeId, \"{nodeDescriptor.TypeNameShort}\", node.{port.PropertyName}, nameof(node.{port.PropertyName}), inputs, \"{port.FieldName}\", createdNodes"; var parametersString = (methodCode.Contains(connectPortMethodCallStringV1) ? methodCode[ (methodCode.IndexOf(connectPortMethodCallStringV1, StringComparison.Ordinal) + connectPortMethodCallStringV1.Length)..] : methodCode.Contains(connectPortMethodCallStringV2) ? methodCode[ (methodCode.IndexOf(connectPortMethodCallStringV2, StringComparison.Ordinal) + connectPortMethodCallStringV2.Length)..] : null )?.Split(';')[0] ?? string.Empty; if (port.Metadata?.DisallowNullableOutput == true) Assert.True(parametersString.Contains("disallowNullableOutput:true") || parametersString.Contains("disallowNullableOutput: true")); } } [Theory] [InlineData(typeof(ThreePortNodeWithReferenceAndStructInputs), "CreateThreePortNodeWithReferenceAndStructInputsModel")] [InlineData(typeof(ThreePortNode), "CreateThreePortNodeModel")] [InlineData(typeof(ThreePortNode2), "CreateThreePortNode2Model")] [InlineData(typeof(SimpleNamedNode), "CreateSimpleNamedNodeModel")] public void SpecificMethod_CallAppropriateConnectPortsMethodBasedOnPortValueType(Type mockNodeType, string factoryMethod) { var nodeDescriptor = TestHelper.GetNodeDescriptorFromMockNode(mockNodeType); var methodName = $"Connect__{nodeDescriptor.TypeNameShortSanitized}__NodePorts"; var model = TestHelper.GetNodeModel(factoryMethod); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); Assert.False(string.IsNullOrEmpty(methodCode), $"{methodName} method not found in generated code."); foreach (var port in nodeDescriptor.Fields.Where(f => f.Direction == FieldDirection.Input)) { var methodSuffix = port.IsValueReferenceType ? "Class" : "Struct"; var connectPortMethodCallString = // $"ConnectPorts{methodSuffix}<{port.ValueType.TrimEnd('?')}>(pipelineId, nodeId, \"{nodeDescriptor.TypeNameShort}\", node.{port.PropertyName}, nameof(node.{port.PropertyName}), inputs, \"{port.FieldName}\", createdNodes"; $"ConnectPorts{methodSuffix}(pipelineId, nodeId, \"{nodeDescriptor.TypeNameShort}\", node.{port.PropertyName}, nameof(node.{port.PropertyName}), inputs, \"{port.FieldName}\", createdNodes"; Assert.Contains(connectPortMethodCallString, methodCode); } } #endregion #region ParseParameter [Fact] public void ParseParameter_ShouldExistInGeneratedCode() { const string methodSignature = "private static T? ParseParameter(string? valueString)"; var model = TestNodeModelFactory.CreateMultipleNodesModel(); var generatedCode = NodeFactoryGenerator.Generate(model); Assert.Contains(methodSignature, generatedCode); } [Fact] public void ParseParameter_ShouldContainAllEnumsMentionedInModelNodes() { const string methodName = "ParseParameter"; var model = TestNodeModelFactory.CreateMultipleNodesWithEnumParametersModel(); var generatedCode = NodeFactoryGenerator.Generate(model); var methodCode = TestHelper.ExtractMethod(generatedCode, methodName); var assemblyEnumTypes = Assembly.GetAssembly(GetType())! .GetTypes() .Where(q => q.IsEnum) .Select(q => q.FullName) .Distinct().ToHashSet(); var allEnums = model.Nodes .SelectMany(q => q.Fields) .Where(q => q.Direction == FieldDirection.Parameter && assemblyEnumTypes.Contains(q.ValueType)) .Select(q => q.ValueType) .Distinct(); foreach (var enumTypeString in allEnums) { var expectedString = $"if (type == typeof({enumTypeString})) return (T)(object)Enum.Parse<{enumTypeString}>(valueString);"; Assert.Contains(expectedString, methodCode); } } #endregion //service //TODO: test usings //TODO: test public properties }