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

1269 lines
59 KiB
C#

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<NodePortKey, NodePortInfo> GetAllPorts(string pipelineId, HashSet<string> nodeTypes, Dictionary<string, IEnumerable<string>> 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<NodePortInfo> 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<T>(string pipelineId, string nodeId,string nodeType, INodeField<T>? nodeInput, string nodeInputName, Dictionary<string, InputSource> inputs, string inputKey, IReadOnlyDictionary<string, INode> createdNodes, bool disallowNullableOutput = false, bool isRequired = false) where T : struct";
const string methodSignatureV2 =
"private void ConnectPortsStruct<T>(string pipelineId, string nodeId,string nodeType, INodeField<T>? nodeInput, string nodeInputName, Dictionary<string, InputSource> inputs, string inputKey, IReadOnlyDictionary<string, INode> createdNodes, bool disallowNullableOutput = false, bool isRequired = false)\n where T : struct";
var methodSignatureV3 =
"private void ConnectPortsStruct<T>(string pipelineId, string nodeId,string nodeType, INodeField<T>? nodeInput, string nodeInputName, Dictionary<string, InputSource> inputs,"
+ Environment.NewLine +
" string inputKey, IReadOnlyDictionary<string, INode> 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<T>(string pipelineId, string nodeId,string nodeType, INodeField<T>? nodeInput, string nodeInputName, Dictionary<string, InputSource> inputs, string inputKey, IReadOnlyDictionary<string, INode> createdNodes, bool disallowNullableOutput = false, bool isRequired = false) where T : class";
const string methodSignatureV2 =
"private void ConnectPortsClass<T>(string pipelineId, string nodeId,string nodeType, INodeField<T>? nodeInput, string nodeInputName, Dictionary<string, InputSource> inputs, string inputKey, IReadOnlyDictionary<string, INode> createdNodes, bool disallowNullableOutput = false, bool isRequired = false)\n where T : class";
var methodSignatureV3 =
"private void ConnectPortsClass<T>(string pipelineId, string nodeId,string nodeType, INodeField<T>? nodeInput, string nodeInputName, Dictionary<string, InputSource> inputs,"
+ Environment.NewLine +
" string inputKey, IReadOnlyDictionary<string, INode> 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<T>(string pipelineId, string nodeId, string nodeType, INodeField<T>? nodeInput, string nodeInputName, Dictionary<string, InputSource> inputs, string inputKey, IReadOnlyDictionary<string, INode> createdNodes, bool isRequired = false) where T : notnull";
const string methodSignatureV2 =
"private PortConnectionInfo? TryConnectPorts<T>(string pipelineId, string nodeId, string nodeType, INodeField<T>? nodeInput, string nodeInputName, Dictionary<string, InputSource> inputs, string inputKey, IReadOnlyDictionary<string, INode> createdNodes, bool isRequired = false)\n where T : notnull";
var methodSignatureV3 =
"private PortConnectionInfo? TryConnectPorts<T>(string pipelineId, string nodeId, string nodeType, INodeField<T>? nodeInput, string nodeInputName,"
+ Environment.NewLine +
" Dictionary<string, InputSource> inputs, string inputKey, IReadOnlyDictionary<string, INode> 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<T>()";
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<NodePortKey, NodePortInfo> 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<NodeDescriptor, int>();
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<string> 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<string> 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<NodePortKey, NodePortInfo> 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<NodePortKey, NodePortInfo> 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<string, object> 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<string, object> 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<TNode>(string pipelineId, TNode node, string nodeId, Dictionary<string, object?> 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<string, object?> 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<TNode>(string pipelineId, TNode node, string nodeId, Dictionary<string, InputSource> inputs, IReadOnlyDictionary<string, INode> 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<string, InputSource> inputs, IReadOnlyDictionary<string, INode> 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<T>(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
}