From e0a09b622c21d0ff17acaef4d948c2eef6d9b922 Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Tue, 3 Sep 2024 16:10:29 +0300 Subject: [PATCH 01/14] Cast queryNode to SingleResourceCastNode as well --- .../Query/Expressions/QueryBinder.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.AspNetCore.OData/Query/Expressions/QueryBinder.cs b/src/Microsoft.AspNetCore.OData/Query/Expressions/QueryBinder.cs index 9b590d4f3..4d5175876 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Expressions/QueryBinder.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Expressions/QueryBinder.cs @@ -642,7 +642,17 @@ public virtual Expression BindSingleResourceCastFunctionCall(SingleResourceFunct IEdmModel model = context.Model; - string targetEdmTypeName = (string)((ConstantNode)node.Parameters.Last()).Value; + string targetEdmTypeName = null; + QueryNode queryNode = node.Parameters.Last(); + if (queryNode is ConstantNode constantNode) + { + targetEdmTypeName = constantNode.Value as string; + } + else if (queryNode is SingleResourceCastNode singleResourceCastNode) + { + targetEdmTypeName = singleResourceCastNode.TypeReference.FullName(); + } + IEdmType targetEdmType = model.FindType(targetEdmTypeName); Type targetClrType = null; From 8277c6d507c7f818da50e79bfe5c28ff85262a23 Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Fri, 13 Sep 2024 17:59:17 +0300 Subject: [PATCH 02/14] Added tests for both ConstantNode and SingleResourceCastNode --- .../Query/Expressions/EdmModelBuilder.cs | 96 +++++++++++ .../Query/Expressions/FakeEdmNodesHelper.cs | 74 +++++++++ .../Query/Expressions/QueryBinderTests.cs | 153 ++++++++++++++++-- 3 files changed, 313 insertions(+), 10 deletions(-) create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/EdmModelBuilder.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/FakeEdmNodesHelper.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/EdmModelBuilder.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/EdmModelBuilder.cs new file mode 100644 index 000000000..0c7bd983e --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/EdmModelBuilder.cs @@ -0,0 +1,96 @@ +using Microsoft.FullyQualified.NS; +using Microsoft.OData.Edm; +using Microsoft.OData.ModelBuilder; + +namespace Microsoft.AspNetCore.OData.Tests.Query.Expressions +{ + internal static class EdmModelBuilder + { + private static readonly IEdmModel Model = BuildAndGetEdmModel(); + + public static IEdmModel TestModel + { + get { return Model; } + } + + public static IEdmEntityType GetEntityType(string entityQualifiedName) + { + return TestModel.FindDeclaredType(entityQualifiedName) as IEdmEntityType; + } + + public static IEdmComplexType GetEdmComplexType(string complexTypeQualifiedName) + { + return TestModel.FindDeclaredType(complexTypeQualifiedName) as IEdmComplexType; + } + + public static IEdmEntityTypeReference GetEntityTypeReference(IEdmEntityType entityType) + { + return new EdmEntityTypeReference(entityType, false); + } + + public static IEdmComplexTypeReference GetComplexTypeReference(IEdmComplexType complexType) + { + // Create a complex type reference using the EdmCoreModel + return new EdmComplexTypeReference(complexType, isNullable: false); + } + + public static IEdmProperty GetPersonLocationProperty() + { + return GetEntityType("Microsoft.FullyQualified.NS.Person").FindProperty("Location"); + } + + /// + /// Get the entity set from the model + /// + /// People Set + public static IEdmEntitySet GetPeopleSet() + { + return TestModel.EntityContainer.FindEntitySet("People"); + } + + public static IEdmModel BuildAndGetEdmModel() + { + var builder = new ODataConventionModelBuilder(); + builder.Namespace = "Microsoft.FullyQualified.NS"; + builder.EntitySet("People"); + builder.ComplexType(); + builder.ComplexType(); + builder.EntitySet("Employees"); + + return builder.GetEdmModel(); + } + } +} + +namespace Microsoft.FullyQualified.NS +{ + public class Person + { + public int Id { get; set; } + public string FullName { get; set; } + public MyAddress Location { get; set; } + } + + public class Employee : Person + { + public string EmployeeNumber { get; set; } + } + + public class MyAddress + { + public string Street { get; set; } + public AddressType AddressType { get; set; } + } + + public class WorkAddress : MyAddress + { + public string OfficeNumber { get; set; } + } + + public enum AddressType + { + Home, + Work, + Other + } +} diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/FakeEdmNodesHelper.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/FakeEdmNodesHelper.cs new file mode 100644 index 000000000..d9b2732e1 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/FakeEdmNodesHelper.cs @@ -0,0 +1,74 @@ +using Microsoft.OData.Edm; +using Microsoft.OData.UriParser; + +namespace Microsoft.AspNetCore.OData.Tests.Query.Expressions +{ + public class FakeSingleEntityNode : SingleEntityNode + { + private readonly IEdmEntityTypeReference typeReference; + private readonly IEdmEntitySetBase set; + + public FakeSingleEntityNode(IEdmEntityTypeReference type, IEdmEntitySetBase set) + { + this.typeReference = type; + this.set = set; + } + + public override IEdmTypeReference TypeReference + { + get { return this.typeReference; } + } + + public override IEdmNavigationSource NavigationSource + { + get { return this.set; } + } + + public override IEdmStructuredTypeReference StructuredTypeReference + { + get { return this.typeReference; } + } + + public override IEdmEntityTypeReference EntityTypeReference + { + get { return this.typeReference; } + } + + public static FakeSingleEntityNode CreateFakeNodeForPerson() + { + var personType = EdmModelBuilder.GetEntityType("Microsoft.FullyQualified.NS.Person"); + return new FakeSingleEntityNode(EdmModelBuilder.GetEntityTypeReference(personType), EdmModelBuilder.GetPeopleSet()); + } + } + + public class FakeCollectionResourceNode : CollectionResourceNode + { + private readonly IEdmStructuredTypeReference _typeReference; + private readonly IEdmNavigationSource _source; + private readonly IEdmTypeReference _itemType; + private readonly IEdmCollectionTypeReference _collectionType; + + public FakeCollectionResourceNode(IEdmStructuredTypeReference type, IEdmNavigationSource source, IEdmTypeReference itemType, IEdmCollectionTypeReference collectionType) + { + _typeReference = type; + _source = source; + _itemType = itemType; + _collectionType = collectionType; + } + + public override IEdmStructuredTypeReference ItemStructuredType => _typeReference; + + public override IEdmNavigationSource NavigationSource => _source; + + public override IEdmTypeReference ItemType => _itemType; + + public override IEdmCollectionTypeReference CollectionType => _collectionType; + + public static FakeCollectionResourceNode CreateFakeNodeForPerson() + { + var singleEntityNode = FakeSingleEntityNode.CreateFakeNodeForPerson(); + return new FakeCollectionResourceNode( + singleEntityNode.EntityTypeReference, singleEntityNode.NavigationSource, singleEntityNode.EntityTypeReference, singleEntityNode.EntityTypeReference.AsCollection()); + } + } +} diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs index 6750f18f7..fdd2eed94 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs @@ -6,13 +6,18 @@ //------------------------------------------------------------------------------ using System; +using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; +using Microsoft.AspNetCore.OData.Edm; +using Microsoft.AspNetCore.OData.Query; using Microsoft.AspNetCore.OData.Query.Expressions; using Microsoft.AspNetCore.OData.Query.Wrapper; using Microsoft.AspNetCore.OData.Tests.Commons; using Microsoft.OData.Edm; +using Microsoft.OData.ModelBuilder; using Microsoft.OData.UriParser; +using Microsoft.FullyQualified.NS; using Moq; using Xunit; @@ -100,16 +105,144 @@ public void BindSingleResourceFunctionCallNode_ThrowsNotSupported_ForNotAcceptNo "Unknown function 'anyUnknown'."); } - [Fact] - public void BindSingleValueFunctionCallNode_ThrowsArgumentNull_ForInputs() - { - // Arrange - QueryBinder binder = new MyQueryBinder(); - Mock type = new Mock(); - type.Setup(t => t.TypeKind).Returns(EdmTypeKind.Primitive); - Mock typeRef = new Mock(); - typeRef.Setup(t => t.Definition).Returns(type.Object); - SingleValueFunctionCallNode node = new SingleValueFunctionCallNode("any", null, typeRef.Object); + [Theory] + [InlineData(typeof(ConstantNode))] + [InlineData(typeof(SingleResourceCastNode))] + public void BindSingleResourceFunctionCallNode_CastingEntityType_ReturnsExpression(Type queryNodeType) + { + // Arrange + var binder = new MyQueryBinder(); + + var model = EdmModelBuilder.BuildAndGetEdmModel(); + + // Create the type reference and navigation source + var personType = EdmModelBuilder.GetEntityType("Microsoft.FullyQualified.NS.Person"); + var personTypeRef = EdmModelBuilder.GetEntityTypeReference(personType); + var collectionNode = FakeCollectionResourceNode.CreateFakeNodeForPerson(); + + var employeeType = EdmModelBuilder.GetEntityType("Microsoft.FullyQualified.NS.Employee"); + + // Create a ResourceRangeVariableReferenceNode for the Person entity + var rangeVariable = new ResourceRangeVariable("$it", personTypeRef, collectionNode); + var personNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; + + // Create the parameters list + int capacity = 2; + var parameters = new List(capacity) + { + personNode // First parameter is the Person entity + }; + + if (queryNodeType == typeof(SingleResourceCastNode)) + { + // Create a SingleResourceCastNode to cast Person to NS.Employee + var singleResourceCastNode = new SingleResourceCastNode(personNode as SingleResourceNode, employeeType); + parameters.Add(singleResourceCastNode); // Second parameter is the SingleResourceCastNode + } + else if (queryNodeType == typeof(ConstantNode)) + { + // Create a ConstantNode to cast Person to NS.Employee + var constantNode = new ConstantNode("Microsoft.FullyQualified.NS.Employee"); + parameters.Add(constantNode); // Second parameter is the ConstantNode + } + + // Create the SingleResourceFunctionCallNode + var node = new SingleResourceFunctionCallNode("cast", parameters, collectionNode.ItemStructuredType, collectionNode.NavigationSource); + + // Create an instance of QueryBinderContext using the real model and settings + Type clrType = model.GetClrType(personType); + var context = new QueryBinderContext(model, new ODataQuerySettings(), clrType); + + // Act + Expression expression = binder.BindSingleResourceFunctionCallNode(node, context); + + // Assert + Assert.NotNull(expression); + Assert.Equal("($it As Employee)", expression.ToString()); + Assert.Equal("Microsoft.FullyQualified.NS.Employee", expression.Type.ToString()); + Assert.Equal(typeof(Employee), expression.Type); + Assert.Equal(ExpressionType.TypeAs, expression.NodeType); + Assert.Equal(personType.FullName(), (expression as UnaryExpression).Operand.Type.FullName); + Assert.Equal(employeeType.FullName(), (expression as UnaryExpression).Type.FullName); + } + + [Theory] + [InlineData(typeof(ConstantNode))] + [InlineData(typeof(SingleResourceCastNode))] + public void BindSingleResourceFunctionCallNode_PropertyCasting_ReturnsExpression(Type queryNodeType) + { + // Arrange + var binder = new MyQueryBinder(); + + var model = EdmModelBuilder.BuildAndGetEdmModel(); + + // Create the type reference and navigation source + var personType = EdmModelBuilder.GetEntityType("Microsoft.FullyQualified.NS.Person"); + var personTypeRef = EdmModelBuilder.GetEntityTypeReference(personType); + var collectionNode = FakeCollectionResourceNode.CreateFakeNodeForPerson(); + + var myAddressType = EdmModelBuilder.GetEdmComplexType("Microsoft.FullyQualified.NS.MyAddress"); + var workAddressType = EdmModelBuilder.GetEdmComplexType("Microsoft.FullyQualified.NS.WorkAddress"); + + // Create a ResourceRangeVariableReferenceNode for the Person entity + var rangeVariable = new ResourceRangeVariable("$it", personTypeRef, collectionNode); + var personNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; + + // Create a SingleComplexNode for the Location property of the Person entity + var locationProperty = EdmModelBuilder.GetPersonLocationProperty(); + var locationNode = new SingleComplexNode(personNode as SingleResourceNode, locationProperty); + + // Create the parameters list + int capacity = 2; + var parameters = new List(capacity) + { + locationNode // First parameter is the Location property + }; + + if(queryNodeType == typeof(SingleResourceCastNode)) + { + // Create a SingleResourceCastNode to cast Location to NS.WorkAddress + var singleResourceCastNode = new SingleResourceCastNode(locationNode, workAddressType); + parameters.Add(singleResourceCastNode); // Second parameter is the SingleResourceCastNode + } + else if (queryNodeType == typeof(ConstantNode)) + { + // Create a ConstantNode to cast Location to NS.WorkAddress + var constantNode = new ConstantNode("Microsoft.FullyQualified.NS.WorkAddress"); + parameters.Add(constantNode); // Second parameter is the ConstantNode + } + + // Create the SingleResourceFunctionCallNode + var node = new SingleResourceFunctionCallNode("cast", parameters, collectionNode.ItemStructuredType, collectionNode.NavigationSource); + + // Create an instance of QueryBinderContext using the real model and settings + Type clrType = model.GetClrType(personType); + var context = new QueryBinderContext(model, new ODataQuerySettings(), clrType); + + // Act + Expression expression = binder.BindSingleResourceFunctionCallNode(node, context); + + // Assert + Assert.NotNull(expression); + Assert.Equal("($it.Location As WorkAddress)", expression.ToString()); + Assert.Equal("Microsoft.FullyQualified.NS.WorkAddress", expression.Type.ToString()); + Assert.Equal("Location", ((expression as UnaryExpression).Operand as MemberExpression).Member.Name); + Assert.Equal(typeof(WorkAddress), expression.Type); + Assert.Equal(ExpressionType.TypeAs, expression.NodeType); + Assert.Equal(workAddressType.FullName(), (expression as UnaryExpression).Type.FullName); + Assert.Equal(myAddressType.FullName(), (expression as UnaryExpression).Operand.Type.FullName); + } + + [Fact] + public void BindSingleValueFunctionCallNode_ThrowsArgumentNull_ForInputs() + { + // Arrange + QueryBinder binder = new MyQueryBinder(); + Mock type = new Mock(); + type.Setup(t => t.TypeKind).Returns(EdmTypeKind.Primitive); + Mock typeRef = new Mock(); + typeRef.Setup(t => t.Definition).Returns(type.Object); + SingleValueFunctionCallNode node = new SingleValueFunctionCallNode("any", null, typeRef.Object); // Act & Assert ExceptionAssert.ThrowsArgumentNull(() => binder.BindSingleValueFunctionCallNode(null, null), "node"); From 3d2779339eb47236832b31749deed2a7a9c04dcb Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Fri, 13 Sep 2024 18:02:51 +0300 Subject: [PATCH 03/14] Renaming --- .../Query/Expressions/FakeEdmNodesHelper.cs | 4 ++-- ...mModelBuilder.cs => HardCodedTestModel.cs} | 4 ++-- .../Query/Expressions/QueryBinderTests.cs | 20 +++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) rename test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/{EdmModelBuilder.cs => HardCodedTestModel.cs} (96%) diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/FakeEdmNodesHelper.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/FakeEdmNodesHelper.cs index d9b2732e1..59fe1a429 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/FakeEdmNodesHelper.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/FakeEdmNodesHelper.cs @@ -36,8 +36,8 @@ public override IEdmEntityTypeReference EntityTypeReference public static FakeSingleEntityNode CreateFakeNodeForPerson() { - var personType = EdmModelBuilder.GetEntityType("Microsoft.FullyQualified.NS.Person"); - return new FakeSingleEntityNode(EdmModelBuilder.GetEntityTypeReference(personType), EdmModelBuilder.GetPeopleSet()); + var personType = HardCodedTestModel.GetEntityType("Microsoft.FullyQualified.NS.Person"); + return new FakeSingleEntityNode(HardCodedTestModel.GetEntityTypeReference(personType), HardCodedTestModel.GetPeopleSet()); } } diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/EdmModelBuilder.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/HardCodedTestModel.cs similarity index 96% rename from test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/EdmModelBuilder.cs rename to test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/HardCodedTestModel.cs index 0c7bd983e..4d646a330 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/EdmModelBuilder.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/HardCodedTestModel.cs @@ -4,7 +4,7 @@ namespace Microsoft.AspNetCore.OData.Tests.Query.Expressions { - internal static class EdmModelBuilder + internal static class HardCodedTestModel { private static readonly IEdmModel Model = BuildAndGetEdmModel(); @@ -48,7 +48,7 @@ public static IEdmEntitySet GetPeopleSet() return TestModel.EntityContainer.FindEntitySet("People"); } - public static IEdmModel BuildAndGetEdmModel() + private static IEdmModel BuildAndGetEdmModel() { var builder = new ODataConventionModelBuilder(); builder.Namespace = "Microsoft.FullyQualified.NS"; diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs index fdd2eed94..aec6729c2 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs @@ -113,14 +113,14 @@ public void BindSingleResourceFunctionCallNode_CastingEntityType_ReturnsExpressi // Arrange var binder = new MyQueryBinder(); - var model = EdmModelBuilder.BuildAndGetEdmModel(); + var model = HardCodedTestModel.TestModel; // Create the type reference and navigation source - var personType = EdmModelBuilder.GetEntityType("Microsoft.FullyQualified.NS.Person"); - var personTypeRef = EdmModelBuilder.GetEntityTypeReference(personType); + var personType = HardCodedTestModel.GetEntityType("Microsoft.FullyQualified.NS.Person"); + var personTypeRef = HardCodedTestModel.GetEntityTypeReference(personType); var collectionNode = FakeCollectionResourceNode.CreateFakeNodeForPerson(); - var employeeType = EdmModelBuilder.GetEntityType("Microsoft.FullyQualified.NS.Employee"); + var employeeType = HardCodedTestModel.GetEntityType("Microsoft.FullyQualified.NS.Employee"); // Create a ResourceRangeVariableReferenceNode for the Person entity var rangeVariable = new ResourceRangeVariable("$it", personTypeRef, collectionNode); @@ -174,22 +174,22 @@ public void BindSingleResourceFunctionCallNode_PropertyCasting_ReturnsExpression // Arrange var binder = new MyQueryBinder(); - var model = EdmModelBuilder.BuildAndGetEdmModel(); + var model = HardCodedTestModel.TestModel; // Create the type reference and navigation source - var personType = EdmModelBuilder.GetEntityType("Microsoft.FullyQualified.NS.Person"); - var personTypeRef = EdmModelBuilder.GetEntityTypeReference(personType); + var personType = HardCodedTestModel.GetEntityType("Microsoft.FullyQualified.NS.Person"); + var personTypeRef = HardCodedTestModel.GetEntityTypeReference(personType); var collectionNode = FakeCollectionResourceNode.CreateFakeNodeForPerson(); - var myAddressType = EdmModelBuilder.GetEdmComplexType("Microsoft.FullyQualified.NS.MyAddress"); - var workAddressType = EdmModelBuilder.GetEdmComplexType("Microsoft.FullyQualified.NS.WorkAddress"); + var myAddressType = HardCodedTestModel.GetEdmComplexType("Microsoft.FullyQualified.NS.MyAddress"); + var workAddressType = HardCodedTestModel.GetEdmComplexType("Microsoft.FullyQualified.NS.WorkAddress"); // Create a ResourceRangeVariableReferenceNode for the Person entity var rangeVariable = new ResourceRangeVariable("$it", personTypeRef, collectionNode); var personNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; // Create a SingleComplexNode for the Location property of the Person entity - var locationProperty = EdmModelBuilder.GetPersonLocationProperty(); + var locationProperty = HardCodedTestModel.GetPersonLocationProperty(); var locationNode = new SingleComplexNode(personNode as SingleResourceNode, locationProperty); // Create the parameters list From 87e562622120c219ee73f4c16d05e9b87801b14a Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Fri, 13 Sep 2024 19:49:35 +0300 Subject: [PATCH 04/14] Indent properly --- .../Query/Expressions/QueryBinderTests.cs | 270 +++++++++--------- 1 file changed, 135 insertions(+), 135 deletions(-) diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs index aec6729c2..44e60287e 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs @@ -105,148 +105,148 @@ public void BindSingleResourceFunctionCallNode_ThrowsNotSupported_ForNotAcceptNo "Unknown function 'anyUnknown'."); } - [Theory] - [InlineData(typeof(ConstantNode))] - [InlineData(typeof(SingleResourceCastNode))] - public void BindSingleResourceFunctionCallNode_CastingEntityType_ReturnsExpression(Type queryNodeType) + [Theory] + [InlineData(typeof(ConstantNode))] + [InlineData(typeof(SingleResourceCastNode))] + public void BindSingleResourceFunctionCallNode_CastingEntityType_ReturnsExpression(Type queryNodeType) + { + // Arrange + var binder = new MyQueryBinder(); + + var model = HardCodedTestModel.TestModel; + + // Create the type reference and navigation source + var personType = HardCodedTestModel.GetEntityType("Microsoft.FullyQualified.NS.Person"); + var personTypeRef = HardCodedTestModel.GetEntityTypeReference(personType); + var collectionNode = FakeCollectionResourceNode.CreateFakeNodeForPerson(); + + var employeeType = HardCodedTestModel.GetEntityType("Microsoft.FullyQualified.NS.Employee"); + + // Create a ResourceRangeVariableReferenceNode for the Person entity + var rangeVariable = new ResourceRangeVariable("$it", personTypeRef, collectionNode); + var personNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; + + // Create the parameters list + int capacity = 2; + var parameters = new List(capacity) { - // Arrange - var binder = new MyQueryBinder(); - - var model = HardCodedTestModel.TestModel; - - // Create the type reference and navigation source - var personType = HardCodedTestModel.GetEntityType("Microsoft.FullyQualified.NS.Person"); - var personTypeRef = HardCodedTestModel.GetEntityTypeReference(personType); - var collectionNode = FakeCollectionResourceNode.CreateFakeNodeForPerson(); - - var employeeType = HardCodedTestModel.GetEntityType("Microsoft.FullyQualified.NS.Employee"); - - // Create a ResourceRangeVariableReferenceNode for the Person entity - var rangeVariable = new ResourceRangeVariable("$it", personTypeRef, collectionNode); - var personNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; - - // Create the parameters list - int capacity = 2; - var parameters = new List(capacity) - { - personNode // First parameter is the Person entity - }; - - if (queryNodeType == typeof(SingleResourceCastNode)) - { - // Create a SingleResourceCastNode to cast Person to NS.Employee - var singleResourceCastNode = new SingleResourceCastNode(personNode as SingleResourceNode, employeeType); - parameters.Add(singleResourceCastNode); // Second parameter is the SingleResourceCastNode - } - else if (queryNodeType == typeof(ConstantNode)) - { - // Create a ConstantNode to cast Person to NS.Employee - var constantNode = new ConstantNode("Microsoft.FullyQualified.NS.Employee"); - parameters.Add(constantNode); // Second parameter is the ConstantNode - } - - // Create the SingleResourceFunctionCallNode - var node = new SingleResourceFunctionCallNode("cast", parameters, collectionNode.ItemStructuredType, collectionNode.NavigationSource); - - // Create an instance of QueryBinderContext using the real model and settings - Type clrType = model.GetClrType(personType); - var context = new QueryBinderContext(model, new ODataQuerySettings(), clrType); - - // Act - Expression expression = binder.BindSingleResourceFunctionCallNode(node, context); - - // Assert - Assert.NotNull(expression); - Assert.Equal("($it As Employee)", expression.ToString()); - Assert.Equal("Microsoft.FullyQualified.NS.Employee", expression.Type.ToString()); - Assert.Equal(typeof(Employee), expression.Type); - Assert.Equal(ExpressionType.TypeAs, expression.NodeType); - Assert.Equal(personType.FullName(), (expression as UnaryExpression).Operand.Type.FullName); - Assert.Equal(employeeType.FullName(), (expression as UnaryExpression).Type.FullName); - } + personNode // First parameter is the Person entity + }; - [Theory] - [InlineData(typeof(ConstantNode))] - [InlineData(typeof(SingleResourceCastNode))] - public void BindSingleResourceFunctionCallNode_PropertyCasting_ReturnsExpression(Type queryNodeType) + if (queryNodeType == typeof(SingleResourceCastNode)) + { + // Create a SingleResourceCastNode to cast Person to NS.Employee + var singleResourceCastNode = new SingleResourceCastNode(personNode as SingleResourceNode, employeeType); + parameters.Add(singleResourceCastNode); // Second parameter is the SingleResourceCastNode + } + else if (queryNodeType == typeof(ConstantNode)) { - // Arrange - var binder = new MyQueryBinder(); - - var model = HardCodedTestModel.TestModel; - - // Create the type reference and navigation source - var personType = HardCodedTestModel.GetEntityType("Microsoft.FullyQualified.NS.Person"); - var personTypeRef = HardCodedTestModel.GetEntityTypeReference(personType); - var collectionNode = FakeCollectionResourceNode.CreateFakeNodeForPerson(); - - var myAddressType = HardCodedTestModel.GetEdmComplexType("Microsoft.FullyQualified.NS.MyAddress"); - var workAddressType = HardCodedTestModel.GetEdmComplexType("Microsoft.FullyQualified.NS.WorkAddress"); - - // Create a ResourceRangeVariableReferenceNode for the Person entity - var rangeVariable = new ResourceRangeVariable("$it", personTypeRef, collectionNode); - var personNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; - - // Create a SingleComplexNode for the Location property of the Person entity - var locationProperty = HardCodedTestModel.GetPersonLocationProperty(); - var locationNode = new SingleComplexNode(personNode as SingleResourceNode, locationProperty); - - // Create the parameters list - int capacity = 2; - var parameters = new List(capacity) - { - locationNode // First parameter is the Location property - }; - - if(queryNodeType == typeof(SingleResourceCastNode)) - { - // Create a SingleResourceCastNode to cast Location to NS.WorkAddress - var singleResourceCastNode = new SingleResourceCastNode(locationNode, workAddressType); - parameters.Add(singleResourceCastNode); // Second parameter is the SingleResourceCastNode - } - else if (queryNodeType == typeof(ConstantNode)) - { - // Create a ConstantNode to cast Location to NS.WorkAddress - var constantNode = new ConstantNode("Microsoft.FullyQualified.NS.WorkAddress"); - parameters.Add(constantNode); // Second parameter is the ConstantNode - } - - // Create the SingleResourceFunctionCallNode - var node = new SingleResourceFunctionCallNode("cast", parameters, collectionNode.ItemStructuredType, collectionNode.NavigationSource); - - // Create an instance of QueryBinderContext using the real model and settings - Type clrType = model.GetClrType(personType); - var context = new QueryBinderContext(model, new ODataQuerySettings(), clrType); - - // Act - Expression expression = binder.BindSingleResourceFunctionCallNode(node, context); - - // Assert - Assert.NotNull(expression); - Assert.Equal("($it.Location As WorkAddress)", expression.ToString()); - Assert.Equal("Microsoft.FullyQualified.NS.WorkAddress", expression.Type.ToString()); - Assert.Equal("Location", ((expression as UnaryExpression).Operand as MemberExpression).Member.Name); - Assert.Equal(typeof(WorkAddress), expression.Type); - Assert.Equal(ExpressionType.TypeAs, expression.NodeType); - Assert.Equal(workAddressType.FullName(), (expression as UnaryExpression).Type.FullName); - Assert.Equal(myAddressType.FullName(), (expression as UnaryExpression).Operand.Type.FullName); + // Create a ConstantNode to cast Person to NS.Employee + var constantNode = new ConstantNode("Microsoft.FullyQualified.NS.Employee"); + parameters.Add(constantNode); // Second parameter is the ConstantNode } - [Fact] - public void BindSingleValueFunctionCallNode_ThrowsArgumentNull_ForInputs() + // Create the SingleResourceFunctionCallNode + var node = new SingleResourceFunctionCallNode("cast", parameters, collectionNode.ItemStructuredType, collectionNode.NavigationSource); + + // Create an instance of QueryBinderContext using the real model and settings + Type clrType = model.GetClrType(personType); + var context = new QueryBinderContext(model, new ODataQuerySettings(), clrType); + + // Act + Expression expression = binder.BindSingleResourceFunctionCallNode(node, context); + + // Assert + Assert.NotNull(expression); + Assert.Equal("($it As Employee)", expression.ToString()); + Assert.Equal("Microsoft.FullyQualified.NS.Employee", expression.Type.ToString()); + Assert.Equal(typeof(Employee), expression.Type); + Assert.Equal(ExpressionType.TypeAs, expression.NodeType); + Assert.Equal(personType.FullName(), (expression as UnaryExpression).Operand.Type.FullName); + Assert.Equal(employeeType.FullName(), (expression as UnaryExpression).Type.FullName); + } + + [Theory] + [InlineData(typeof(ConstantNode))] + [InlineData(typeof(SingleResourceCastNode))] + public void BindSingleResourceFunctionCallNode_PropertyCasting_ReturnsExpression(Type queryNodeType) + { + // Arrange + var binder = new MyQueryBinder(); + + var model = HardCodedTestModel.TestModel; + + // Create the type reference and navigation source + var personType = HardCodedTestModel.GetEntityType("Microsoft.FullyQualified.NS.Person"); + var personTypeRef = HardCodedTestModel.GetEntityTypeReference(personType); + var collectionNode = FakeCollectionResourceNode.CreateFakeNodeForPerson(); + + var myAddressType = HardCodedTestModel.GetEdmComplexType("Microsoft.FullyQualified.NS.MyAddress"); + var workAddressType = HardCodedTestModel.GetEdmComplexType("Microsoft.FullyQualified.NS.WorkAddress"); + + // Create a ResourceRangeVariableReferenceNode for the Person entity + var rangeVariable = new ResourceRangeVariable("$it", personTypeRef, collectionNode); + var personNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; + + // Create a SingleComplexNode for the Location property of the Person entity + var locationProperty = HardCodedTestModel.GetPersonLocationProperty(); + var locationNode = new SingleComplexNode(personNode as SingleResourceNode, locationProperty); + + // Create the parameters list + int capacity = 2; + var parameters = new List(capacity) { - // Arrange - QueryBinder binder = new MyQueryBinder(); - Mock type = new Mock(); - type.Setup(t => t.TypeKind).Returns(EdmTypeKind.Primitive); - Mock typeRef = new Mock(); - typeRef.Setup(t => t.Definition).Returns(type.Object); - SingleValueFunctionCallNode node = new SingleValueFunctionCallNode("any", null, typeRef.Object); + locationNode // First parameter is the Location property + }; - // Act & Assert - ExceptionAssert.ThrowsArgumentNull(() => binder.BindSingleValueFunctionCallNode(null, null), "node"); - ExceptionAssert.ThrowsArgumentNull(() => binder.BindSingleValueFunctionCallNode(node, null), "context"); + if(queryNodeType == typeof(SingleResourceCastNode)) + { + // Create a SingleResourceCastNode to cast Location to NS.WorkAddress + var singleResourceCastNode = new SingleResourceCastNode(locationNode, workAddressType); + parameters.Add(singleResourceCastNode); // Second parameter is the SingleResourceCastNode + } + else if (queryNodeType == typeof(ConstantNode)) + { + // Create a ConstantNode to cast Location to NS.WorkAddress + var constantNode = new ConstantNode("Microsoft.FullyQualified.NS.WorkAddress"); + parameters.Add(constantNode); // Second parameter is the ConstantNode + } + + // Create the SingleResourceFunctionCallNode + var node = new SingleResourceFunctionCallNode("cast", parameters, collectionNode.ItemStructuredType, collectionNode.NavigationSource); + + // Create an instance of QueryBinderContext using the real model and settings + Type clrType = model.GetClrType(personType); + var context = new QueryBinderContext(model, new ODataQuerySettings(), clrType); + + // Act + Expression expression = binder.BindSingleResourceFunctionCallNode(node, context); + + // Assert + Assert.NotNull(expression); + Assert.Equal("($it.Location As WorkAddress)", expression.ToString()); + Assert.Equal("Microsoft.FullyQualified.NS.WorkAddress", expression.Type.ToString()); + Assert.Equal("Location", ((expression as UnaryExpression).Operand as MemberExpression).Member.Name); + Assert.Equal(typeof(WorkAddress), expression.Type); + Assert.Equal(ExpressionType.TypeAs, expression.NodeType); + Assert.Equal(workAddressType.FullName(), (expression as UnaryExpression).Type.FullName); + Assert.Equal(myAddressType.FullName(), (expression as UnaryExpression).Operand.Type.FullName); + } + + [Fact] + public void BindSingleValueFunctionCallNode_ThrowsArgumentNull_ForInputs() + { + // Arrange + QueryBinder binder = new MyQueryBinder(); + Mock type = new Mock(); + type.Setup(t => t.TypeKind).Returns(EdmTypeKind.Primitive); + Mock typeRef = new Mock(); + typeRef.Setup(t => t.Definition).Returns(type.Object); + SingleValueFunctionCallNode node = new SingleValueFunctionCallNode("any", null, typeRef.Object); + + // Act & Assert + ExceptionAssert.ThrowsArgumentNull(() => binder.BindSingleValueFunctionCallNode(null, null), "node"); + ExceptionAssert.ThrowsArgumentNull(() => binder.BindSingleValueFunctionCallNode(node, null), "context"); } [Fact] From 0d199c08d3b1bb2192ab3d0d8c9a297f5ff1aefe Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Wed, 25 Sep 2024 15:48:02 +0300 Subject: [PATCH 05/14] Resuse existing models --- .../Models/Employee.cs | 2 + .../Models/WorkAddress.cs | 14 ++ .../Query/Expressions/HardCodedTestModel.cs | 96 ------------- ...dmNodesHelper.cs => MockEdmNodesHelper.cs} | 29 ++-- .../Query/Expressions/QueryBinderTests.cs | 130 +++++++++++++----- 5 files changed, 127 insertions(+), 144 deletions(-) create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Models/WorkAddress.cs delete mode 100644 test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/HardCodedTestModel.cs rename test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/{FakeEdmNodesHelper.cs => MockEdmNodesHelper.cs} (61%) diff --git a/test/Microsoft.AspNetCore.OData.Tests/Models/Employee.cs b/test/Microsoft.AspNetCore.OData.Tests/Models/Employee.cs index 1420d8488..6cd156fe8 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Models/Employee.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Models/Employee.cs @@ -32,6 +32,8 @@ public class Employee public Address HomeAddress { get; set; } + public Address Location { get; set; } + public IList DirectReports { get; set; } } diff --git a/test/Microsoft.AspNetCore.OData.Tests/Models/WorkAddress.cs b/test/Microsoft.AspNetCore.OData.Tests/Models/WorkAddress.cs new file mode 100644 index 000000000..50c73cb07 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Models/WorkAddress.cs @@ -0,0 +1,14 @@ +//----------------------------------------------------------------------------- +// +// Copyright (c) .NET Foundation and Contributors. All rights reserved. +// See License.txt in the project root for license information. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.AspNetCore.OData.Tests.Models +{ + internal class WorkAddress : Address + { + public string OfficeNumber { get; set; } + } +} diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/HardCodedTestModel.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/HardCodedTestModel.cs deleted file mode 100644 index 4d646a330..000000000 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/HardCodedTestModel.cs +++ /dev/null @@ -1,96 +0,0 @@ -using Microsoft.FullyQualified.NS; -using Microsoft.OData.Edm; -using Microsoft.OData.ModelBuilder; - -namespace Microsoft.AspNetCore.OData.Tests.Query.Expressions -{ - internal static class HardCodedTestModel - { - private static readonly IEdmModel Model = BuildAndGetEdmModel(); - - public static IEdmModel TestModel - { - get { return Model; } - } - - public static IEdmEntityType GetEntityType(string entityQualifiedName) - { - return TestModel.FindDeclaredType(entityQualifiedName) as IEdmEntityType; - } - - public static IEdmComplexType GetEdmComplexType(string complexTypeQualifiedName) - { - return TestModel.FindDeclaredType(complexTypeQualifiedName) as IEdmComplexType; - } - - public static IEdmEntityTypeReference GetEntityTypeReference(IEdmEntityType entityType) - { - return new EdmEntityTypeReference(entityType, false); - } - - public static IEdmComplexTypeReference GetComplexTypeReference(IEdmComplexType complexType) - { - // Create a complex type reference using the EdmCoreModel - return new EdmComplexTypeReference(complexType, isNullable: false); - } - - public static IEdmProperty GetPersonLocationProperty() - { - return GetEntityType("Microsoft.FullyQualified.NS.Person").FindProperty("Location"); - } - - /// - /// Get the entity set from the model - /// - /// People Set - public static IEdmEntitySet GetPeopleSet() - { - return TestModel.EntityContainer.FindEntitySet("People"); - } - - private static IEdmModel BuildAndGetEdmModel() - { - var builder = new ODataConventionModelBuilder(); - builder.Namespace = "Microsoft.FullyQualified.NS"; - builder.EntitySet("People"); - builder.ComplexType(); - builder.ComplexType(); - builder.EntitySet("Employees"); - - return builder.GetEdmModel(); - } - } -} - -namespace Microsoft.FullyQualified.NS -{ - public class Person - { - public int Id { get; set; } - public string FullName { get; set; } - public MyAddress Location { get; set; } - } - - public class Employee : Person - { - public string EmployeeNumber { get; set; } - } - - public class MyAddress - { - public string Street { get; set; } - public AddressType AddressType { get; set; } - } - - public class WorkAddress : MyAddress - { - public string OfficeNumber { get; set; } - } - - public enum AddressType - { - Home, - Work, - Other - } -} diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/FakeEdmNodesHelper.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/MockEdmNodesHelper.cs similarity index 61% rename from test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/FakeEdmNodesHelper.cs rename to test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/MockEdmNodesHelper.cs index 59fe1a429..c6751402f 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/FakeEdmNodesHelper.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/MockEdmNodesHelper.cs @@ -1,14 +1,21 @@ -using Microsoft.OData.Edm; +//----------------------------------------------------------------------------- +// +// Copyright (c) .NET Foundation and Contributors. All rights reserved. +// See License.txt in the project root for license information. +// +//------------------------------------------------------------------------------ + +using Microsoft.OData.Edm; using Microsoft.OData.UriParser; namespace Microsoft.AspNetCore.OData.Tests.Query.Expressions { - public class FakeSingleEntityNode : SingleEntityNode + public class MockSingleEntityNode : SingleEntityNode { private readonly IEdmEntityTypeReference typeReference; private readonly IEdmEntitySetBase set; - public FakeSingleEntityNode(IEdmEntityTypeReference type, IEdmEntitySetBase set) + public MockSingleEntityNode(IEdmEntityTypeReference type, IEdmEntitySetBase set) { this.typeReference = type; this.set = set; @@ -34,21 +41,21 @@ public override IEdmEntityTypeReference EntityTypeReference get { return this.typeReference; } } - public static FakeSingleEntityNode CreateFakeNodeForPerson() + public static MockSingleEntityNode CreateFakeNodeForEmployee() { - var personType = HardCodedTestModel.GetEntityType("Microsoft.FullyQualified.NS.Person"); - return new FakeSingleEntityNode(HardCodedTestModel.GetEntityTypeReference(personType), HardCodedTestModel.GetPeopleSet()); + var employeeType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Employee"); + return new MockSingleEntityNode(HardCodedTestModel.GetEntityTypeReference(employeeType), HardCodedTestModel.GetEmployeeSet()); } } - public class FakeCollectionResourceNode : CollectionResourceNode + public class MockCollectionResourceNode : CollectionResourceNode { private readonly IEdmStructuredTypeReference _typeReference; private readonly IEdmNavigationSource _source; private readonly IEdmTypeReference _itemType; private readonly IEdmCollectionTypeReference _collectionType; - public FakeCollectionResourceNode(IEdmStructuredTypeReference type, IEdmNavigationSource source, IEdmTypeReference itemType, IEdmCollectionTypeReference collectionType) + public MockCollectionResourceNode(IEdmStructuredTypeReference type, IEdmNavigationSource source, IEdmTypeReference itemType, IEdmCollectionTypeReference collectionType) { _typeReference = type; _source = source; @@ -64,10 +71,10 @@ public FakeCollectionResourceNode(IEdmStructuredTypeReference type, IEdmNavigati public override IEdmCollectionTypeReference CollectionType => _collectionType; - public static FakeCollectionResourceNode CreateFakeNodeForPerson() + public static MockCollectionResourceNode CreateFakeNodeForEmployee() { - var singleEntityNode = FakeSingleEntityNode.CreateFakeNodeForPerson(); - return new FakeCollectionResourceNode( + var singleEntityNode = MockSingleEntityNode.CreateFakeNodeForEmployee(); + return new MockCollectionResourceNode( singleEntityNode.EntityTypeReference, singleEntityNode.NavigationSource, singleEntityNode.EntityTypeReference, singleEntityNode.EntityTypeReference.AsCollection()); } } diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs index 44e60287e..715d9ae43 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs @@ -9,6 +9,8 @@ using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; +using Moq; +using Xunit; using Microsoft.AspNetCore.OData.Edm; using Microsoft.AspNetCore.OData.Query; using Microsoft.AspNetCore.OData.Query.Expressions; @@ -17,9 +19,7 @@ using Microsoft.OData.Edm; using Microsoft.OData.ModelBuilder; using Microsoft.OData.UriParser; -using Microsoft.FullyQualified.NS; -using Moq; -using Xunit; +using Microsoft.AspNetCore.OData.Tests.Models; namespace Microsoft.AspNetCore.OData.Tests.Query.Expressions; @@ -116,33 +116,34 @@ public void BindSingleResourceFunctionCallNode_CastingEntityType_ReturnsExpressi var model = HardCodedTestModel.TestModel; // Create the type reference and navigation source - var personType = HardCodedTestModel.GetEntityType("Microsoft.FullyQualified.NS.Person"); - var personTypeRef = HardCodedTestModel.GetEntityTypeReference(personType); - var collectionNode = FakeCollectionResourceNode.CreateFakeNodeForPerson(); + var employeeType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Employee"); + var employeeTypeRef = HardCodedTestModel.GetEntityTypeReference(employeeType); + var collectionNode = MockCollectionResourceNode.CreateFakeNodeForEmployee(); - var employeeType = HardCodedTestModel.GetEntityType("Microsoft.FullyQualified.NS.Employee"); + // Get the entity type for the Manager entity -> Manager is derived from Employee + var managerType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Manager"); - // Create a ResourceRangeVariableReferenceNode for the Person entity - var rangeVariable = new ResourceRangeVariable("$it", personTypeRef, collectionNode); - var personNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; + // Create a ResourceRangeVariableReferenceNode for the Employee entity + var rangeVariable = new ResourceRangeVariable("$it", employeeTypeRef, collectionNode); + var employeeNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; // Create the parameters list int capacity = 2; var parameters = new List(capacity) { - personNode // First parameter is the Person entity + employeeNode // First parameter is the Person entity }; if (queryNodeType == typeof(SingleResourceCastNode)) { - // Create a SingleResourceCastNode to cast Person to NS.Employee - var singleResourceCastNode = new SingleResourceCastNode(personNode as SingleResourceNode, employeeType); + // Create a SingleResourceCastNode to cast Employee to Microsoft.AspNetCore.OData.Tests.Models.Manager + var singleResourceCastNode = new SingleResourceCastNode(employeeNode as SingleResourceNode, managerType); parameters.Add(singleResourceCastNode); // Second parameter is the SingleResourceCastNode } else if (queryNodeType == typeof(ConstantNode)) { - // Create a ConstantNode to cast Person to NS.Employee - var constantNode = new ConstantNode("Microsoft.FullyQualified.NS.Employee"); + // Create a ConstantNode to cast Employee to Microsoft.AspNetCore.OData.Tests.Models.Manager + var constantNode = new ConstantNode("Microsoft.AspNetCore.OData.Tests.Models.Manager"); parameters.Add(constantNode); // Second parameter is the ConstantNode } @@ -150,7 +151,7 @@ public void BindSingleResourceFunctionCallNode_CastingEntityType_ReturnsExpressi var node = new SingleResourceFunctionCallNode("cast", parameters, collectionNode.ItemStructuredType, collectionNode.NavigationSource); // Create an instance of QueryBinderContext using the real model and settings - Type clrType = model.GetClrType(personType); + Type clrType = model.GetClrType(employeeType); var context = new QueryBinderContext(model, new ODataQuerySettings(), clrType); // Act @@ -158,12 +159,12 @@ public void BindSingleResourceFunctionCallNode_CastingEntityType_ReturnsExpressi // Assert Assert.NotNull(expression); - Assert.Equal("($it As Employee)", expression.ToString()); - Assert.Equal("Microsoft.FullyQualified.NS.Employee", expression.Type.ToString()); - Assert.Equal(typeof(Employee), expression.Type); + Assert.Equal("($it As Manager)", expression.ToString()); // cast($it, 'Microsoft.AspNetCore.OData.Tests.Models.Manager') where $it is the Employee entity + Assert.Equal("Microsoft.AspNetCore.OData.Tests.Models.Manager", expression.Type.ToString()); + Assert.Equal(typeof(Manager), expression.Type); Assert.Equal(ExpressionType.TypeAs, expression.NodeType); - Assert.Equal(personType.FullName(), (expression as UnaryExpression).Operand.Type.FullName); - Assert.Equal(employeeType.FullName(), (expression as UnaryExpression).Type.FullName); + Assert.Equal(employeeType.FullName(), (expression as UnaryExpression).Operand.Type.FullName); + Assert.Equal(managerType.FullName(), (expression as UnaryExpression).Type.FullName); } [Theory] @@ -177,20 +178,20 @@ public void BindSingleResourceFunctionCallNode_PropertyCasting_ReturnsExpression var model = HardCodedTestModel.TestModel; // Create the type reference and navigation source - var personType = HardCodedTestModel.GetEntityType("Microsoft.FullyQualified.NS.Person"); - var personTypeRef = HardCodedTestModel.GetEntityTypeReference(personType); - var collectionNode = FakeCollectionResourceNode.CreateFakeNodeForPerson(); + var employeeType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Employee"); + var employeeTypeRef = HardCodedTestModel.GetEntityTypeReference(employeeType); + var collectionNode = MockCollectionResourceNode.CreateFakeNodeForEmployee(); - var myAddressType = HardCodedTestModel.GetEdmComplexType("Microsoft.FullyQualified.NS.MyAddress"); - var workAddressType = HardCodedTestModel.GetEdmComplexType("Microsoft.FullyQualified.NS.WorkAddress"); + var addressType = HardCodedTestModel.GetEdmComplexType("Microsoft.AspNetCore.OData.Tests.Models.Address"); + var workAddressType = HardCodedTestModel.GetEdmComplexType("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress"); - // Create a ResourceRangeVariableReferenceNode for the Person entity - var rangeVariable = new ResourceRangeVariable("$it", personTypeRef, collectionNode); - var personNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; + // Create a ResourceRangeVariableReferenceNode for the Employee entity + var rangeVariable = new ResourceRangeVariable("$it", employeeTypeRef, collectionNode); + var employeeNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; // Create a SingleComplexNode for the Location property of the Person entity - var locationProperty = HardCodedTestModel.GetPersonLocationProperty(); - var locationNode = new SingleComplexNode(personNode as SingleResourceNode, locationProperty); + var locationProperty = HardCodedTestModel.GetEmployeeLocationProperty(); + var locationNode = new SingleComplexNode(employeeNode as SingleResourceNode, locationProperty); // Create the parameters list int capacity = 2; @@ -201,14 +202,14 @@ public void BindSingleResourceFunctionCallNode_PropertyCasting_ReturnsExpression if(queryNodeType == typeof(SingleResourceCastNode)) { - // Create a SingleResourceCastNode to cast Location to NS.WorkAddress + // Create a SingleResourceCastNode to cast Location to Microsoft.AspNetCore.OData.Tests.Models.WorkAddress var singleResourceCastNode = new SingleResourceCastNode(locationNode, workAddressType); parameters.Add(singleResourceCastNode); // Second parameter is the SingleResourceCastNode } else if (queryNodeType == typeof(ConstantNode)) { // Create a ConstantNode to cast Location to NS.WorkAddress - var constantNode = new ConstantNode("Microsoft.FullyQualified.NS.WorkAddress"); + var constantNode = new ConstantNode("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress"); parameters.Add(constantNode); // Second parameter is the ConstantNode } @@ -216,7 +217,7 @@ public void BindSingleResourceFunctionCallNode_PropertyCasting_ReturnsExpression var node = new SingleResourceFunctionCallNode("cast", parameters, collectionNode.ItemStructuredType, collectionNode.NavigationSource); // Create an instance of QueryBinderContext using the real model and settings - Type clrType = model.GetClrType(personType); + Type clrType = model.GetClrType(employeeType); var context = new QueryBinderContext(model, new ODataQuerySettings(), clrType); // Act @@ -224,13 +225,13 @@ public void BindSingleResourceFunctionCallNode_PropertyCasting_ReturnsExpression // Assert Assert.NotNull(expression); - Assert.Equal("($it.Location As WorkAddress)", expression.ToString()); - Assert.Equal("Microsoft.FullyQualified.NS.WorkAddress", expression.Type.ToString()); + Assert.Equal("($it.Location As WorkAddress)", expression.ToString()); // cast($it.Location, 'Microsoft.AspNetCore.OData.Tests.Models.WorkAddress') where $it is the Employee entity + Assert.Equal("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress", expression.Type.ToString()); Assert.Equal("Location", ((expression as UnaryExpression).Operand as MemberExpression).Member.Name); Assert.Equal(typeof(WorkAddress), expression.Type); Assert.Equal(ExpressionType.TypeAs, expression.NodeType); Assert.Equal(workAddressType.FullName(), (expression as UnaryExpression).Type.FullName); - Assert.Equal(myAddressType.FullName(), (expression as UnaryExpression).Operand.Type.FullName); + Assert.Equal(addressType.FullName(), (expression as UnaryExpression).Operand.Type.FullName); } [Fact] @@ -303,3 +304,58 @@ public static PropertyInfo Call_GetDynamicPropertyContainer(SingleValueOpenPrope return GetDynamicPropertyContainer(openNode, context); } } + +public static class HardCodedTestModel +{ + #region Create the model + private static readonly IEdmModel Model = BuildAndGetEdmModel(); + + public static IEdmModel TestModel + { + get { return Model; } + } + + public static IEdmEntityType GetEntityType(string entityQualifiedName) + { + return TestModel.FindDeclaredType(entityQualifiedName) as IEdmEntityType; + } + + public static IEdmComplexType GetEdmComplexType(string complexTypeQualifiedName) + { + return TestModel.FindDeclaredType(complexTypeQualifiedName) as IEdmComplexType; + } + + public static IEdmEntityTypeReference GetEntityTypeReference(IEdmEntityType entityType) + { + return new EdmEntityTypeReference(entityType, false); + } + + public static IEdmComplexTypeReference GetComplexTypeReference(IEdmComplexType complexType) + { + // Create a complex type reference using the EdmCoreModel + return new EdmComplexTypeReference(complexType, isNullable: false); + } + + public static IEdmProperty GetEmployeeLocationProperty() + { + return GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Employee").FindProperty("Location"); + } + + public static IEdmEntitySet GetEmployeeSet() + { + return TestModel.EntityContainer.FindEntitySet("Employee"); + } + + private static IEdmModel BuildAndGetEdmModel() + { + var builder = new ODataConventionModelBuilder(); + builder.Namespace = "Microsoft.AspNetCore.OData.Tests.Models"; + builder.EntitySet("Employees"); + builder.ComplexType
(); + builder.ComplexType(); + builder.EntitySet("Managers"); + + return builder.GetEdmModel(); + } + #endregion +} From c5d58808c055fd2ebd89da327970e545712e7db4 Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Wed, 25 Sep 2024 16:00:41 +0300 Subject: [PATCH 06/14] Indentation --- .../Query/Expressions/QueryBinderTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs index 715d9ae43..51d853214 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs @@ -245,9 +245,9 @@ public void BindSingleValueFunctionCallNode_ThrowsArgumentNull_ForInputs() typeRef.Setup(t => t.Definition).Returns(type.Object); SingleValueFunctionCallNode node = new SingleValueFunctionCallNode("any", null, typeRef.Object); - // Act & Assert - ExceptionAssert.ThrowsArgumentNull(() => binder.BindSingleValueFunctionCallNode(null, null), "node"); - ExceptionAssert.ThrowsArgumentNull(() => binder.BindSingleValueFunctionCallNode(node, null), "context"); + // Act & Assert + ExceptionAssert.ThrowsArgumentNull(() => binder.BindSingleValueFunctionCallNode(null, null), "node"); + ExceptionAssert.ThrowsArgumentNull(() => binder.BindSingleValueFunctionCallNode(node, null), "context"); } [Fact] From 74d291bde408765f506f88a9fbf105d59ef9e8cb Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Wed, 25 Sep 2024 16:14:43 +0300 Subject: [PATCH 07/14] Fix failed test due to addition of "Location" property --- .../Formatter/Serialization/EntityTypeTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Microsoft.AspNetCore.OData.Tests/Formatter/Serialization/EntityTypeTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Formatter/Serialization/EntityTypeTests.cs index e6c0db2b8..be9f34242 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Formatter/Serialization/EntityTypeTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Formatter/Serialization/EntityTypeTests.cs @@ -55,7 +55,8 @@ public async Task EntityTypeSerializesAsODataEntry() "\"BaseSalary\":0," + "\"Birthday\":\"2020-09-10T01:02:03Z\"," + "\"WorkCompanyId\":0," + - "\"HomeAddress\":null" + + "\"HomeAddress\":null," + + "\"Location\":null" + "}", actual); } From 83225fbd6a2abbb0e781cd8aeb88a2a1362873ef Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Fri, 4 Oct 2024 13:26:19 +0300 Subject: [PATCH 08/14] Using TheoryDataSet instead of `if` clauses --- .../Query/Expressions/QueryBinder.cs | 4 + .../Query/Expressions/QueryBinderTests.cs | 195 +++++++++++++----- 2 files changed, 148 insertions(+), 51 deletions(-) diff --git a/src/Microsoft.AspNetCore.OData/Query/Expressions/QueryBinder.cs b/src/Microsoft.AspNetCore.OData/Query/Expressions/QueryBinder.cs index 4d5175876..05e881509 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Expressions/QueryBinder.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Expressions/QueryBinder.cs @@ -652,6 +652,10 @@ public virtual Expression BindSingleResourceCastFunctionCall(SingleResourceFunct { targetEdmTypeName = singleResourceCastNode.TypeReference.FullName(); } + else + { + throw Error.NotSupported(SRResources.QueryNodeBindingNotSupported, queryNode.Kind, "BindSingleResourceCastFunctionCall"); + } IEdmType targetEdmType = model.FindType(targetEdmTypeName); Type targetClrType = null; diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs index 51d853214..978096ce5 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs @@ -20,6 +20,7 @@ using Microsoft.OData.ModelBuilder; using Microsoft.OData.UriParser; using Microsoft.AspNetCore.OData.Tests.Models; +using Microsoft.AspNetCore.OData.TestCommon; namespace Microsoft.AspNetCore.OData.Tests.Query.Expressions; @@ -105,10 +106,50 @@ public void BindSingleResourceFunctionCallNode_ThrowsNotSupported_ForNotAcceptNo "Unknown function 'anyUnknown'."); } + public static TheoryDataSet> BindSingleResourceFunctionCallNodeForEntityTypCasting_Data + { + get + { + // Create the type reference and navigation source + var employeeType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Employee"); + var employeeTypeRef = HardCodedTestModel.GetEntityTypeReference(employeeType); + var collectionNode = MockCollectionResourceNode.CreateFakeNodeForEmployee(); + + // Get the entity type for the Manager entity -> Manager is derived from Employee + var managerType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Manager"); + + // Create a ResourceRangeVariableReferenceNode for the Employee entity + var rangeVariable = new ResourceRangeVariable("$it", employeeTypeRef, collectionNode); + var employeeNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; + + int capacity = 2; + // Create a ConstantNode to cast Employee to Microsoft.AspNetCore.OData.Tests.Models.Manager + var parametersWithConstantNode = new List(capacity) + { + employeeNode, // First parameter is the Person entity + new ConstantNode("Microsoft.AspNetCore.OData.Tests.Models.Manager") // Second parameter is the ConstantNode + }; + + // Create a SingleResourceCastNode to cast Employee to Microsoft.AspNetCore.OData.Tests.Models.Manager + var parametersWithSingleResourceCastNode = new List(capacity) + { + employeeNode, // First parameter is the Person entity + new SingleResourceCastNode(employeeNode as SingleResourceNode, managerType) // Second parameter is the SingleResourceCastNode + }; + + var data = new TheoryDataSet>() + { + { parametersWithConstantNode }, + { parametersWithSingleResourceCastNode } + }; + + return data; + } + } + [Theory] - [InlineData(typeof(ConstantNode))] - [InlineData(typeof(SingleResourceCastNode))] - public void BindSingleResourceFunctionCallNode_CastingEntityType_ReturnsExpression(Type queryNodeType) + [MemberData(nameof(BindSingleResourceFunctionCallNodeForEntityTypCasting_Data))] + public void BindSingleResourceFunctionCallNode_CastingEntityType_ReturnsExpression(List parameters) { // Arrange var binder = new MyQueryBinder(); @@ -117,36 +158,11 @@ public void BindSingleResourceFunctionCallNode_CastingEntityType_ReturnsExpressi // Create the type reference and navigation source var employeeType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Employee"); - var employeeTypeRef = HardCodedTestModel.GetEntityTypeReference(employeeType); var collectionNode = MockCollectionResourceNode.CreateFakeNodeForEmployee(); // Get the entity type for the Manager entity -> Manager is derived from Employee var managerType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Manager"); - // Create a ResourceRangeVariableReferenceNode for the Employee entity - var rangeVariable = new ResourceRangeVariable("$it", employeeTypeRef, collectionNode); - var employeeNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; - - // Create the parameters list - int capacity = 2; - var parameters = new List(capacity) - { - employeeNode // First parameter is the Person entity - }; - - if (queryNodeType == typeof(SingleResourceCastNode)) - { - // Create a SingleResourceCastNode to cast Employee to Microsoft.AspNetCore.OData.Tests.Models.Manager - var singleResourceCastNode = new SingleResourceCastNode(employeeNode as SingleResourceNode, managerType); - parameters.Add(singleResourceCastNode); // Second parameter is the SingleResourceCastNode - } - else if (queryNodeType == typeof(ConstantNode)) - { - // Create a ConstantNode to cast Employee to Microsoft.AspNetCore.OData.Tests.Models.Manager - var constantNode = new ConstantNode("Microsoft.AspNetCore.OData.Tests.Models.Manager"); - parameters.Add(constantNode); // Second parameter is the ConstantNode - } - // Create the SingleResourceFunctionCallNode var node = new SingleResourceFunctionCallNode("cast", parameters, collectionNode.ItemStructuredType, collectionNode.NavigationSource); @@ -167,10 +183,54 @@ public void BindSingleResourceFunctionCallNode_CastingEntityType_ReturnsExpressi Assert.Equal(managerType.FullName(), (expression as UnaryExpression).Type.FullName); } + public static TheoryDataSet> BindSingleResourceFunctionCallNodeForPropertyCasting_Data + { + get + { + // Create the type reference and navigation source + var employeeType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Employee"); + var employeeTypeRef = HardCodedTestModel.GetEntityTypeReference(employeeType); + var collectionNode = MockCollectionResourceNode.CreateFakeNodeForEmployee(); + + var addressType = HardCodedTestModel.GetEdmComplexType("Microsoft.AspNetCore.OData.Tests.Models.Address"); + var workAddressType = HardCodedTestModel.GetEdmComplexType("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress"); + + // Create a ResourceRangeVariableReferenceNode for the Employee entity + var rangeVariable = new ResourceRangeVariable("$it", employeeTypeRef, collectionNode); + var employeeNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; + + // Create a SingleComplexNode for the Location property of the Person entity + var locationProperty = HardCodedTestModel.GetEmployeeLocationProperty(); + var locationNode = new SingleComplexNode(employeeNode as SingleResourceNode, locationProperty); + + int capacity = 2; + // Create a ConstantNode to cast Location to NS.WorkAddress + var parametersWithConstantNode = new List(capacity) + { + locationNode, // First parameter is the Location property + new ConstantNode("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress") // Second parameter is the ConstantNode + }; + + // Create a SingleResourceCastNode to cast Location to Microsoft.AspNetCore.OData.Tests.Models.WorkAddress + var parametersWithSingleResourceCastNode = new List(capacity) + { + locationNode, // First parameter is the Location property + new SingleResourceCastNode(locationNode, workAddressType) // Second parameter is the SingleResourceCastNode + }; + + var data = new TheoryDataSet>() + { + { parametersWithConstantNode }, + { parametersWithSingleResourceCastNode } + }; + + return data; + } + } + [Theory] - [InlineData(typeof(ConstantNode))] - [InlineData(typeof(SingleResourceCastNode))] - public void BindSingleResourceFunctionCallNode_PropertyCasting_ReturnsExpression(Type queryNodeType) + [MemberData(nameof(BindSingleResourceFunctionCallNodeForPropertyCasting_Data))] + public void BindSingleResourceFunctionCallNode_PropertyCasting_ReturnsExpression(List parameters) { // Arrange var binder = new MyQueryBinder(); @@ -193,26 +253,6 @@ public void BindSingleResourceFunctionCallNode_PropertyCasting_ReturnsExpression var locationProperty = HardCodedTestModel.GetEmployeeLocationProperty(); var locationNode = new SingleComplexNode(employeeNode as SingleResourceNode, locationProperty); - // Create the parameters list - int capacity = 2; - var parameters = new List(capacity) - { - locationNode // First parameter is the Location property - }; - - if(queryNodeType == typeof(SingleResourceCastNode)) - { - // Create a SingleResourceCastNode to cast Location to Microsoft.AspNetCore.OData.Tests.Models.WorkAddress - var singleResourceCastNode = new SingleResourceCastNode(locationNode, workAddressType); - parameters.Add(singleResourceCastNode); // Second parameter is the SingleResourceCastNode - } - else if (queryNodeType == typeof(ConstantNode)) - { - // Create a ConstantNode to cast Location to NS.WorkAddress - var constantNode = new ConstantNode("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress"); - parameters.Add(constantNode); // Second parameter is the ConstantNode - } - // Create the SingleResourceFunctionCallNode var node = new SingleResourceFunctionCallNode("cast", parameters, collectionNode.ItemStructuredType, collectionNode.NavigationSource); @@ -234,6 +274,54 @@ public void BindSingleResourceFunctionCallNode_PropertyCasting_ReturnsExpression Assert.Equal(addressType.FullName(), (expression as UnaryExpression).Operand.Type.FullName); } + [Fact] + public void BindSingleResourceFunctionCallNode_ThrowsNotSupported_ForNotAcceptParameterQueryNode() + { + // Arrange + var binder = new MyQueryBinder(); + + var model = HardCodedTestModel.TestModel; + + // Create the type reference and navigation source + var employeeType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Employee"); + var employeeTypeRef = HardCodedTestModel.GetEntityTypeReference(employeeType); + var collectionNode = MockCollectionResourceNode.CreateFakeNodeForEmployee(); + + // Get the entity type for the Manager entity -> Manager is derived from Employee + var managerType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Manager"); + + // Create a ResourceRangeVariableReferenceNode for the Employee entity + var rangeVariable = new ResourceRangeVariable("$it", employeeTypeRef, collectionNode); + var employeeNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; + + // Create a SingleValuePropertyAccessNode for the EmployeeID property of the Person entity + var employeeIDProperty = HardCodedTestModel.GetEmployeeIDProperty(); + var employeeIDNode = new SingleValuePropertyAccessNode(employeeNode, employeeIDProperty); + + // Create ConvertNode that is not SingleResourceNode or ConstantNode + var edmTypeReference = EdmCoreModel.Instance.FindDeclaredType("Edm.Int32").ToEdmTypeReference(false); + var convertNode = new ConvertNode(employeeIDNode, edmTypeReference); + + // Create the parameters list + int capacity = 2; + var parameters = new List(capacity) + { + employeeNode, // First parameter is the Person entity, + convertNode // Second parameter is the ConvertNode + }; + + // Create the SingleResourceFunctionCallNode + var node = new SingleResourceFunctionCallNode("cast", parameters, collectionNode.ItemStructuredType, collectionNode.NavigationSource); + + // Create an instance of QueryBinderContext using the real model and settings + Type clrType = model.GetClrType(employeeType); + var context = new QueryBinderContext(model, new ODataQuerySettings(), clrType); + + // Act & Assert + ExceptionAssert.Throws(() => binder.BindSingleResourceFunctionCallNode(node, context), + "Binding OData QueryNode of kind 'Convert' is not supported by 'BindSingleResourceCastFunctionCall'."); + } + [Fact] public void BindSingleValueFunctionCallNode_ThrowsArgumentNull_ForInputs() { @@ -341,6 +429,11 @@ public static IEdmProperty GetEmployeeLocationProperty() return GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Employee").FindProperty("Location"); } + public static IEdmProperty GetEmployeeIDProperty() + { + return GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Employee").FindProperty("EmployeeID"); + } + public static IEdmEntitySet GetEmployeeSet() { return TestModel.EntityContainer.FindEntitySet("Employee"); From afa3c16351b44c263b25938bf511f16abd0c3088 Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Mon, 7 Oct 2024 18:57:33 +0300 Subject: [PATCH 09/14] Refactor tests --- .../Query/Expressions/MockEdmNodesHelper.cs | 4 +- .../Query/Expressions/QueryBinderTests.cs | 107 ++++++++---------- 2 files changed, 52 insertions(+), 59 deletions(-) diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/MockEdmNodesHelper.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/MockEdmNodesHelper.cs index c6751402f..416d72313 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/MockEdmNodesHelper.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/MockEdmNodesHelper.cs @@ -43,8 +43,8 @@ public override IEdmEntityTypeReference EntityTypeReference public static MockSingleEntityNode CreateFakeNodeForEmployee() { - var employeeType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Employee"); - return new MockSingleEntityNode(HardCodedTestModel.GetEntityTypeReference(employeeType), HardCodedTestModel.GetEmployeeSet()); + var employeeType = HardCodedTestModel.GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Employee"); + return new MockSingleEntityNode(HardCodedTestModel.GetEntityTypeReferenceFor(employeeType), HardCodedTestModel.GetEntitySetFor("Employees")); } } diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs index 978096ce5..c601abb5c 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs @@ -110,31 +110,25 @@ public static TheoryDataSet> BindSingleResourceFunctionCallNodeF { get { - // Create the type reference and navigation source - var employeeType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Employee"); - var employeeTypeRef = HardCodedTestModel.GetEntityTypeReference(employeeType); - var collectionNode = MockCollectionResourceNode.CreateFakeNodeForEmployee(); - // Get the entity type for the Manager entity -> Manager is derived from Employee - var managerType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Manager"); + var managerType = HardCodedTestModel.GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Manager"); // Create a ResourceRangeVariableReferenceNode for the Employee entity - var rangeVariable = new ResourceRangeVariable("$it", employeeTypeRef, collectionNode); - var employeeNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; + var employeeNode = CreateEmployeeRangeVariableReferenceNode(); int capacity = 2; // Create a ConstantNode to cast Employee to Microsoft.AspNetCore.OData.Tests.Models.Manager var parametersWithConstantNode = new List(capacity) { - employeeNode, // First parameter is the Person entity - new ConstantNode("Microsoft.AspNetCore.OData.Tests.Models.Manager") // Second parameter is the ConstantNode + employeeNode, + new ConstantNode("Microsoft.AspNetCore.OData.Tests.Models.Manager") }; // Create a SingleResourceCastNode to cast Employee to Microsoft.AspNetCore.OData.Tests.Models.Manager var parametersWithSingleResourceCastNode = new List(capacity) { - employeeNode, // First parameter is the Person entity - new SingleResourceCastNode(employeeNode as SingleResourceNode, managerType) // Second parameter is the SingleResourceCastNode + employeeNode, + new SingleResourceCastNode(employeeNode as SingleResourceNode, managerType) }; var data = new TheoryDataSet>() @@ -157,11 +151,11 @@ public void BindSingleResourceFunctionCallNode_CastingEntityType_ReturnsExpressi var model = HardCodedTestModel.TestModel; // Create the type reference and navigation source - var employeeType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Employee"); + var employeeType = HardCodedTestModel.GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Employee"); var collectionNode = MockCollectionResourceNode.CreateFakeNodeForEmployee(); // Get the entity type for the Manager entity -> Manager is derived from Employee - var managerType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Manager"); + var managerType = HardCodedTestModel.GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Manager"); // Create the SingleResourceFunctionCallNode var node = new SingleResourceFunctionCallNode("cast", parameters, collectionNode.ItemStructuredType, collectionNode.NavigationSource); @@ -175,7 +169,8 @@ public void BindSingleResourceFunctionCallNode_CastingEntityType_ReturnsExpressi // Assert Assert.NotNull(expression); - Assert.Equal("($it As Manager)", expression.ToString()); // cast($it, 'Microsoft.AspNetCore.OData.Tests.Models.Manager') where $it is the Employee entity + // cast($it, 'Microsoft.AspNetCore.OData.Tests.Models.Manager') where $it is the Employee entity + Assert.Equal("($it As Manager)", expression.ToString()); Assert.Equal("Microsoft.AspNetCore.OData.Tests.Models.Manager", expression.Type.ToString()); Assert.Equal(typeof(Manager), expression.Type); Assert.Equal(ExpressionType.TypeAs, expression.NodeType); @@ -187,35 +182,31 @@ public static TheoryDataSet> BindSingleResourceFunctionCallNodeF { get { - // Create the type reference and navigation source - var employeeType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Employee"); - var employeeTypeRef = HardCodedTestModel.GetEntityTypeReference(employeeType); - var collectionNode = MockCollectionResourceNode.CreateFakeNodeForEmployee(); - - var addressType = HardCodedTestModel.GetEdmComplexType("Microsoft.AspNetCore.OData.Tests.Models.Address"); - var workAddressType = HardCodedTestModel.GetEdmComplexType("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress"); + var addressType = HardCodedTestModel.GetEdmComplexTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Address"); + var workAddressType = HardCodedTestModel.GetEdmComplexTypeFor("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress"); // Create a ResourceRangeVariableReferenceNode for the Employee entity - var rangeVariable = new ResourceRangeVariable("$it", employeeTypeRef, collectionNode); - var employeeNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; + var employeeNode = CreateEmployeeRangeVariableReferenceNode(); // Create a SingleComplexNode for the Location property of the Person entity - var locationProperty = HardCodedTestModel.GetEmployeeLocationProperty(); + var locationProperty = HardCodedTestModel.GetEdmPropertyFor("Microsoft.AspNetCore.OData.Tests.Models.Employee", "Location"); var locationNode = new SingleComplexNode(employeeNode as SingleResourceNode, locationProperty); int capacity = 2; // Create a ConstantNode to cast Location to NS.WorkAddress var parametersWithConstantNode = new List(capacity) { - locationNode, // First parameter is the Location property - new ConstantNode("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress") // Second parameter is the ConstantNode + // First parameter is the Location property + locationNode, + // Second parameter is the ConstantNode + new ConstantNode("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress") }; // Create a SingleResourceCastNode to cast Location to Microsoft.AspNetCore.OData.Tests.Models.WorkAddress var parametersWithSingleResourceCastNode = new List(capacity) { - locationNode, // First parameter is the Location property - new SingleResourceCastNode(locationNode, workAddressType) // Second parameter is the SingleResourceCastNode + locationNode, + new SingleResourceCastNode(locationNode, workAddressType) }; var data = new TheoryDataSet>() @@ -238,19 +229,19 @@ public void BindSingleResourceFunctionCallNode_PropertyCasting_ReturnsExpression var model = HardCodedTestModel.TestModel; // Create the type reference and navigation source - var employeeType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Employee"); - var employeeTypeRef = HardCodedTestModel.GetEntityTypeReference(employeeType); + var employeeType = HardCodedTestModel.GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Employee"); + var employeeTypeRef = HardCodedTestModel.GetEntityTypeReferenceFor(employeeType); var collectionNode = MockCollectionResourceNode.CreateFakeNodeForEmployee(); - var addressType = HardCodedTestModel.GetEdmComplexType("Microsoft.AspNetCore.OData.Tests.Models.Address"); - var workAddressType = HardCodedTestModel.GetEdmComplexType("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress"); + var addressType = HardCodedTestModel.GetEdmComplexTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Address"); + var workAddressType = HardCodedTestModel.GetEdmComplexTypeFor("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress"); // Create a ResourceRangeVariableReferenceNode for the Employee entity var rangeVariable = new ResourceRangeVariable("$it", employeeTypeRef, collectionNode); var employeeNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; // Create a SingleComplexNode for the Location property of the Person entity - var locationProperty = HardCodedTestModel.GetEmployeeLocationProperty(); + var locationProperty = HardCodedTestModel.GetEdmPropertyFor("Microsoft.AspNetCore.OData.Tests.Models.Employee", "Location"); var locationNode = new SingleComplexNode(employeeNode as SingleResourceNode, locationProperty); // Create the SingleResourceFunctionCallNode @@ -265,7 +256,8 @@ public void BindSingleResourceFunctionCallNode_PropertyCasting_ReturnsExpression // Assert Assert.NotNull(expression); - Assert.Equal("($it.Location As WorkAddress)", expression.ToString()); // cast($it.Location, 'Microsoft.AspNetCore.OData.Tests.Models.WorkAddress') where $it is the Employee entity + // cast($it.Location, 'Microsoft.AspNetCore.OData.Tests.Models.WorkAddress') where $it is the Employee entity + Assert.Equal("($it.Location As WorkAddress)", expression.ToString()); Assert.Equal("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress", expression.Type.ToString()); Assert.Equal("Location", ((expression as UnaryExpression).Operand as MemberExpression).Member.Name); Assert.Equal(typeof(WorkAddress), expression.Type); @@ -283,19 +275,19 @@ public void BindSingleResourceFunctionCallNode_ThrowsNotSupported_ForNotAcceptPa var model = HardCodedTestModel.TestModel; // Create the type reference and navigation source - var employeeType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Employee"); - var employeeTypeRef = HardCodedTestModel.GetEntityTypeReference(employeeType); + var employeeType = HardCodedTestModel.GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Employee"); + var employeeTypeRef = HardCodedTestModel.GetEntityTypeReferenceFor(employeeType); var collectionNode = MockCollectionResourceNode.CreateFakeNodeForEmployee(); // Get the entity type for the Manager entity -> Manager is derived from Employee - var managerType = HardCodedTestModel.GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Manager"); + var managerType = HardCodedTestModel.GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Manager"); // Create a ResourceRangeVariableReferenceNode for the Employee entity var rangeVariable = new ResourceRangeVariable("$it", employeeTypeRef, collectionNode); var employeeNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; // Create a SingleValuePropertyAccessNode for the EmployeeID property of the Person entity - var employeeIDProperty = HardCodedTestModel.GetEmployeeIDProperty(); + var employeeIDProperty = HardCodedTestModel.GetEdmPropertyFor("Microsoft.AspNetCore.OData.Tests.Models.Employee", "EmployeeID"); var employeeIDNode = new SingleValuePropertyAccessNode(employeeNode, employeeIDProperty); // Create ConvertNode that is not SingleResourceNode or ConstantNode @@ -383,6 +375,18 @@ public void GetPropertyExpression_Works_ForAggregateOrNonAggregate(bool isAggreg Assert.NotNull(expression); Assert.Equal(expected, expression.ToString()); } + + private static SingleValueNode CreateEmployeeRangeVariableReferenceNode() + { + // Create the type reference and navigation source + var employeeType = HardCodedTestModel.GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Employee"); + var employeeTypeRef = HardCodedTestModel.GetEntityTypeReferenceFor(employeeType); + var collectionNode = MockCollectionResourceNode.CreateFakeNodeForEmployee(); + + // Create a ResourceRangeVariableReferenceNode for the Employee entity + var rangeVariable = new ResourceRangeVariable("$it", employeeTypeRef, collectionNode); + return new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable); + } } public class MyQueryBinder : QueryBinder @@ -403,40 +407,29 @@ public static IEdmModel TestModel get { return Model; } } - public static IEdmEntityType GetEntityType(string entityQualifiedName) + public static IEdmEntityType GetEntityTypeFor(string entityQualifiedName) { return TestModel.FindDeclaredType(entityQualifiedName) as IEdmEntityType; } - public static IEdmComplexType GetEdmComplexType(string complexTypeQualifiedName) + public static IEdmComplexType GetEdmComplexTypeFor(string complexTypeQualifiedName) { return TestModel.FindDeclaredType(complexTypeQualifiedName) as IEdmComplexType; } - public static IEdmEntityTypeReference GetEntityTypeReference(IEdmEntityType entityType) + public static IEdmEntityTypeReference GetEntityTypeReferenceFor(IEdmEntityType entityType) { return new EdmEntityTypeReference(entityType, false); } - public static IEdmComplexTypeReference GetComplexTypeReference(IEdmComplexType complexType) - { - // Create a complex type reference using the EdmCoreModel - return new EdmComplexTypeReference(complexType, isNullable: false); - } - - public static IEdmProperty GetEmployeeLocationProperty() - { - return GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Employee").FindProperty("Location"); - } - - public static IEdmProperty GetEmployeeIDProperty() + public static IEdmProperty GetEdmPropertyFor(string entityQualifiedName, string propertyName) { - return GetEntityType("Microsoft.AspNetCore.OData.Tests.Models.Employee").FindProperty("EmployeeID"); + return GetEntityTypeFor(entityQualifiedName).FindProperty(propertyName); } - public static IEdmEntitySet GetEmployeeSet() + public static IEdmEntitySet GetEntitySetFor(string entityName) { - return TestModel.EntityContainer.FindEntitySet("Employee"); + return TestModel.EntityContainer.FindEntitySet(entityName); } private static IEdmModel BuildAndGetEdmModel() From 13b9ec94a286c1ba82cb145bdad8218a1e8227b4 Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Wed, 9 Oct 2024 11:12:14 +0300 Subject: [PATCH 10/14] Refactor tests --- .../Query/Expressions/QueryBinderTests.cs | 82 +++++++++---------- 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs index c601abb5c..fab8ef8ec 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs @@ -116,28 +116,27 @@ public static TheoryDataSet> BindSingleResourceFunctionCallNodeF // Create a ResourceRangeVariableReferenceNode for the Employee entity var employeeNode = CreateEmployeeRangeVariableReferenceNode(); - int capacity = 2; - // Create a ConstantNode to cast Employee to Microsoft.AspNetCore.OData.Tests.Models.Manager - var parametersWithConstantNode = new List(capacity) + return new TheoryDataSet>() { - employeeNode, - new ConstantNode("Microsoft.AspNetCore.OData.Tests.Models.Manager") + { + // Create a ConstantNode to cast Employee to Microsoft.AspNetCore.OData.Tests.Models.Manager + // This represents the quoted type parameter for cast function. For example: cast('Microsoft.AspNetCore.OData.Tests.Models.Manager') + new List() + { + employeeNode, + new ConstantNode("Microsoft.AspNetCore.OData.Tests.Models.Manager") + } + }, + { + // Create a SingleResourceCastNode to cast Employee to Microsoft.AspNetCore.OData.Tests.Models.Manager + // This represents the unquoted type parameter for cast function. For example: cast(Microsoft.AspNetCore.OData.Tests.Models.Manager) + new List() + { + employeeNode, + new SingleResourceCastNode(employeeNode as SingleResourceNode, managerType) + } + } }; - - // Create a SingleResourceCastNode to cast Employee to Microsoft.AspNetCore.OData.Tests.Models.Manager - var parametersWithSingleResourceCastNode = new List(capacity) - { - employeeNode, - new SingleResourceCastNode(employeeNode as SingleResourceNode, managerType) - }; - - var data = new TheoryDataSet>() - { - { parametersWithConstantNode }, - { parametersWithSingleResourceCastNode } - }; - - return data; } } @@ -192,30 +191,29 @@ public static TheoryDataSet> BindSingleResourceFunctionCallNodeF var locationProperty = HardCodedTestModel.GetEdmPropertyFor("Microsoft.AspNetCore.OData.Tests.Models.Employee", "Location"); var locationNode = new SingleComplexNode(employeeNode as SingleResourceNode, locationProperty); - int capacity = 2; - // Create a ConstantNode to cast Location to NS.WorkAddress - var parametersWithConstantNode = new List(capacity) + return new TheoryDataSet>() { - // First parameter is the Location property - locationNode, - // Second parameter is the ConstantNode - new ConstantNode("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress") + { + // Create a ConstantNode to cast Location to NS.WorkAddress + // This represents the quoted type parameter for cast function. For example: cast($it.Location, 'Microsoft.AspNetCore.OData.Tests.Models.WorkAddress') + new List() + { + // First parameter is the Location property + locationNode, + // Second parameter is the ConstantNode + new ConstantNode("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress") + } + }, + { + // Create a SingleResourceCastNode to cast Location to Microsoft.AspNetCore.OData.Tests.Models.WorkAddress + // This represents the unquoted type parameter for cast function. For example: cast($it.Location, Microsoft.AspNetCore.OData.Tests.Models.WorkAddress) + new List() + { + locationNode, + new SingleResourceCastNode(locationNode, workAddressType) + } + } }; - - // Create a SingleResourceCastNode to cast Location to Microsoft.AspNetCore.OData.Tests.Models.WorkAddress - var parametersWithSingleResourceCastNode = new List(capacity) - { - locationNode, - new SingleResourceCastNode(locationNode, workAddressType) - }; - - var data = new TheoryDataSet>() - { - { parametersWithConstantNode }, - { parametersWithSingleResourceCastNode } - }; - - return data; } } From c140a2b5b1a87b3fd3b9c8889f1ece9b993367fc Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Mon, 28 Oct 2024 14:06:25 +0300 Subject: [PATCH 11/14] Combine the related codes in QueryBindeerTests --- .../Query/Expressions/MockEdmNodesHelper.cs | 81 --------- .../Query/Expressions/QueryBinderTests.cs | 157 ++++++++++++------ 2 files changed, 109 insertions(+), 129 deletions(-) delete mode 100644 test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/MockEdmNodesHelper.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/MockEdmNodesHelper.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/MockEdmNodesHelper.cs deleted file mode 100644 index 416d72313..000000000 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/MockEdmNodesHelper.cs +++ /dev/null @@ -1,81 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (c) .NET Foundation and Contributors. All rights reserved. -// See License.txt in the project root for license information. -// -//------------------------------------------------------------------------------ - -using Microsoft.OData.Edm; -using Microsoft.OData.UriParser; - -namespace Microsoft.AspNetCore.OData.Tests.Query.Expressions -{ - public class MockSingleEntityNode : SingleEntityNode - { - private readonly IEdmEntityTypeReference typeReference; - private readonly IEdmEntitySetBase set; - - public MockSingleEntityNode(IEdmEntityTypeReference type, IEdmEntitySetBase set) - { - this.typeReference = type; - this.set = set; - } - - public override IEdmTypeReference TypeReference - { - get { return this.typeReference; } - } - - public override IEdmNavigationSource NavigationSource - { - get { return this.set; } - } - - public override IEdmStructuredTypeReference StructuredTypeReference - { - get { return this.typeReference; } - } - - public override IEdmEntityTypeReference EntityTypeReference - { - get { return this.typeReference; } - } - - public static MockSingleEntityNode CreateFakeNodeForEmployee() - { - var employeeType = HardCodedTestModel.GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Employee"); - return new MockSingleEntityNode(HardCodedTestModel.GetEntityTypeReferenceFor(employeeType), HardCodedTestModel.GetEntitySetFor("Employees")); - } - } - - public class MockCollectionResourceNode : CollectionResourceNode - { - private readonly IEdmStructuredTypeReference _typeReference; - private readonly IEdmNavigationSource _source; - private readonly IEdmTypeReference _itemType; - private readonly IEdmCollectionTypeReference _collectionType; - - public MockCollectionResourceNode(IEdmStructuredTypeReference type, IEdmNavigationSource source, IEdmTypeReference itemType, IEdmCollectionTypeReference collectionType) - { - _typeReference = type; - _source = source; - _itemType = itemType; - _collectionType = collectionType; - } - - public override IEdmStructuredTypeReference ItemStructuredType => _typeReference; - - public override IEdmNavigationSource NavigationSource => _source; - - public override IEdmTypeReference ItemType => _itemType; - - public override IEdmCollectionTypeReference CollectionType => _collectionType; - - public static MockCollectionResourceNode CreateFakeNodeForEmployee() - { - var singleEntityNode = MockSingleEntityNode.CreateFakeNodeForEmployee(); - return new MockCollectionResourceNode( - singleEntityNode.EntityTypeReference, singleEntityNode.NavigationSource, singleEntityNode.EntityTypeReference, singleEntityNode.EntityTypeReference.AsCollection()); - } - } -} diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs index fab8ef8ec..65de4cd7a 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs @@ -29,6 +29,10 @@ namespace Microsoft.AspNetCore.OData.Tests.Query.Expressions; /// public class QueryBinderTests { + #region Create the model + private static readonly IEdmModel TestModel = BuildAndGetEdmModel(); + #endregion + [Fact] public void Bind_ThrowsArgumentNull_ForInputs() { @@ -111,7 +115,7 @@ public static TheoryDataSet> BindSingleResourceFunctionCallNodeF get { // Get the entity type for the Manager entity -> Manager is derived from Employee - var managerType = HardCodedTestModel.GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Manager"); + var managerType = GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Manager"); // Create a ResourceRangeVariableReferenceNode for the Employee entity var employeeNode = CreateEmployeeRangeVariableReferenceNode(); @@ -147,21 +151,19 @@ public void BindSingleResourceFunctionCallNode_CastingEntityType_ReturnsExpressi // Arrange var binder = new MyQueryBinder(); - var model = HardCodedTestModel.TestModel; - // Create the type reference and navigation source - var employeeType = HardCodedTestModel.GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Employee"); + var employeeType = GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Employee"); var collectionNode = MockCollectionResourceNode.CreateFakeNodeForEmployee(); // Get the entity type for the Manager entity -> Manager is derived from Employee - var managerType = HardCodedTestModel.GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Manager"); + var managerType = GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Manager"); // Create the SingleResourceFunctionCallNode var node = new SingleResourceFunctionCallNode("cast", parameters, collectionNode.ItemStructuredType, collectionNode.NavigationSource); // Create an instance of QueryBinderContext using the real model and settings - Type clrType = model.GetClrType(employeeType); - var context = new QueryBinderContext(model, new ODataQuerySettings(), clrType); + Type clrType = TestModel.GetClrType(employeeType); + var context = new QueryBinderContext(TestModel, new ODataQuerySettings(), clrType); // Act Expression expression = binder.BindSingleResourceFunctionCallNode(node, context); @@ -181,14 +183,14 @@ public static TheoryDataSet> BindSingleResourceFunctionCallNodeF { get { - var addressType = HardCodedTestModel.GetEdmComplexTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Address"); - var workAddressType = HardCodedTestModel.GetEdmComplexTypeFor("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress"); + var addressType = GetEdmComplexTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Address"); + var workAddressType = GetEdmComplexTypeFor("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress"); // Create a ResourceRangeVariableReferenceNode for the Employee entity var employeeNode = CreateEmployeeRangeVariableReferenceNode(); // Create a SingleComplexNode for the Location property of the Person entity - var locationProperty = HardCodedTestModel.GetEdmPropertyFor("Microsoft.AspNetCore.OData.Tests.Models.Employee", "Location"); + var locationProperty = GetEdmPropertyFor("Microsoft.AspNetCore.OData.Tests.Models.Employee", "Location"); var locationNode = new SingleComplexNode(employeeNode as SingleResourceNode, locationProperty); return new TheoryDataSet>() @@ -224,30 +226,28 @@ public void BindSingleResourceFunctionCallNode_PropertyCasting_ReturnsExpression // Arrange var binder = new MyQueryBinder(); - var model = HardCodedTestModel.TestModel; - // Create the type reference and navigation source - var employeeType = HardCodedTestModel.GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Employee"); - var employeeTypeRef = HardCodedTestModel.GetEntityTypeReferenceFor(employeeType); + var employeeType = GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Employee"); + var employeeTypeRef = GetEntityTypeReferenceFor(employeeType); var collectionNode = MockCollectionResourceNode.CreateFakeNodeForEmployee(); - var addressType = HardCodedTestModel.GetEdmComplexTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Address"); - var workAddressType = HardCodedTestModel.GetEdmComplexTypeFor("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress"); + var addressType = GetEdmComplexTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Address"); + var workAddressType = GetEdmComplexTypeFor("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress"); // Create a ResourceRangeVariableReferenceNode for the Employee entity var rangeVariable = new ResourceRangeVariable("$it", employeeTypeRef, collectionNode); var employeeNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; // Create a SingleComplexNode for the Location property of the Person entity - var locationProperty = HardCodedTestModel.GetEdmPropertyFor("Microsoft.AspNetCore.OData.Tests.Models.Employee", "Location"); + var locationProperty = GetEdmPropertyFor("Microsoft.AspNetCore.OData.Tests.Models.Employee", "Location"); var locationNode = new SingleComplexNode(employeeNode as SingleResourceNode, locationProperty); // Create the SingleResourceFunctionCallNode var node = new SingleResourceFunctionCallNode("cast", parameters, collectionNode.ItemStructuredType, collectionNode.NavigationSource); // Create an instance of QueryBinderContext using the real model and settings - Type clrType = model.GetClrType(employeeType); - var context = new QueryBinderContext(model, new ODataQuerySettings(), clrType); + Type clrType = TestModel.GetClrType(employeeType); + var context = new QueryBinderContext(TestModel, new ODataQuerySettings(), clrType); // Act Expression expression = binder.BindSingleResourceFunctionCallNode(node, context); @@ -270,22 +270,20 @@ public void BindSingleResourceFunctionCallNode_ThrowsNotSupported_ForNotAcceptPa // Arrange var binder = new MyQueryBinder(); - var model = HardCodedTestModel.TestModel; - // Create the type reference and navigation source - var employeeType = HardCodedTestModel.GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Employee"); - var employeeTypeRef = HardCodedTestModel.GetEntityTypeReferenceFor(employeeType); + var employeeType = GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Employee"); + var employeeTypeRef = GetEntityTypeReferenceFor(employeeType); var collectionNode = MockCollectionResourceNode.CreateFakeNodeForEmployee(); // Get the entity type for the Manager entity -> Manager is derived from Employee - var managerType = HardCodedTestModel.GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Manager"); + var managerType = GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Manager"); // Create a ResourceRangeVariableReferenceNode for the Employee entity var rangeVariable = new ResourceRangeVariable("$it", employeeTypeRef, collectionNode); var employeeNode = new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable) as SingleValueNode; // Create a SingleValuePropertyAccessNode for the EmployeeID property of the Person entity - var employeeIDProperty = HardCodedTestModel.GetEdmPropertyFor("Microsoft.AspNetCore.OData.Tests.Models.Employee", "EmployeeID"); + var employeeIDProperty = GetEdmPropertyFor("Microsoft.AspNetCore.OData.Tests.Models.Employee", "EmployeeID"); var employeeIDNode = new SingleValuePropertyAccessNode(employeeNode, employeeIDProperty); // Create ConvertNode that is not SingleResourceNode or ConstantNode @@ -304,8 +302,8 @@ public void BindSingleResourceFunctionCallNode_ThrowsNotSupported_ForNotAcceptPa var node = new SingleResourceFunctionCallNode("cast", parameters, collectionNode.ItemStructuredType, collectionNode.NavigationSource); // Create an instance of QueryBinderContext using the real model and settings - Type clrType = model.GetClrType(employeeType); - var context = new QueryBinderContext(model, new ODataQuerySettings(), clrType); + Type clrType = TestModel.GetClrType(employeeType); + var context = new QueryBinderContext(TestModel, new ODataQuerySettings(), clrType); // Act & Assert ExceptionAssert.Throws(() => binder.BindSingleResourceFunctionCallNode(node, context), @@ -377,34 +375,16 @@ public void GetPropertyExpression_Works_ForAggregateOrNonAggregate(bool isAggreg private static SingleValueNode CreateEmployeeRangeVariableReferenceNode() { // Create the type reference and navigation source - var employeeType = HardCodedTestModel.GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Employee"); - var employeeTypeRef = HardCodedTestModel.GetEntityTypeReferenceFor(employeeType); + var employeeType = GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Employee"); + var employeeTypeRef = GetEntityTypeReferenceFor(employeeType); var collectionNode = MockCollectionResourceNode.CreateFakeNodeForEmployee(); // Create a ResourceRangeVariableReferenceNode for the Employee entity var rangeVariable = new ResourceRangeVariable("$it", employeeTypeRef, collectionNode); return new ResourceRangeVariableReferenceNode(rangeVariable.Name, rangeVariable); } -} - -public class MyQueryBinder : QueryBinder -{ - public static PropertyInfo Call_GetDynamicPropertyContainer(SingleValueOpenPropertyAccessNode openNode, QueryBinderContext context) - { - return GetDynamicPropertyContainer(openNode, context); - } -} - -public static class HardCodedTestModel -{ - #region Create the model - private static readonly IEdmModel Model = BuildAndGetEdmModel(); - - public static IEdmModel TestModel - { - get { return Model; } - } + #region Helper methods public static IEdmEntityType GetEntityTypeFor(string entityQualifiedName) { return TestModel.FindDeclaredType(entityQualifiedName) as IEdmEntityType; @@ -443,3 +423,84 @@ private static IEdmModel BuildAndGetEdmModel() } #endregion } + +public class MyQueryBinder : QueryBinder +{ + public static PropertyInfo Call_GetDynamicPropertyContainer(SingleValueOpenPropertyAccessNode openNode, QueryBinderContext context) + { + return GetDynamicPropertyContainer(openNode, context); + } +} + +#region Mock Single Entity Node +public class MockSingleEntityNode : SingleEntityNode +{ + private readonly IEdmEntityTypeReference typeReference; + private readonly IEdmEntitySetBase set; + + public MockSingleEntityNode(IEdmEntityTypeReference type, IEdmEntitySetBase set) + { + this.typeReference = type; + this.set = set; + } + + public override IEdmTypeReference TypeReference + { + get { return this.typeReference; } + } + + public override IEdmNavigationSource NavigationSource + { + get { return this.set; } + } + + public override IEdmStructuredTypeReference StructuredTypeReference + { + get { return this.typeReference; } + } + + public override IEdmEntityTypeReference EntityTypeReference + { + get { return this.typeReference; } + } + + public static MockSingleEntityNode CreateFakeNodeForEmployee() + { + var employeeType = QueryBinderTests.GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Employee"); + return new MockSingleEntityNode(QueryBinderTests.GetEntityTypeReferenceFor(employeeType), QueryBinderTests.GetEntitySetFor("Employees")); + } +} +#endregion + +#region Mock Collection Resource Node +public class MockCollectionResourceNode : CollectionResourceNode +{ + private readonly IEdmStructuredTypeReference _typeReference; + private readonly IEdmNavigationSource _source; + private readonly IEdmTypeReference _itemType; + private readonly IEdmCollectionTypeReference _collectionType; + + public MockCollectionResourceNode(IEdmStructuredTypeReference type, IEdmNavigationSource source, IEdmTypeReference itemType, IEdmCollectionTypeReference collectionType) + { + _typeReference = type; + _source = source; + _itemType = itemType; + _collectionType = collectionType; + } + + public override IEdmStructuredTypeReference ItemStructuredType => _typeReference; + + public override IEdmNavigationSource NavigationSource => _source; + + public override IEdmTypeReference ItemType => _itemType; + + public override IEdmCollectionTypeReference CollectionType => _collectionType; + + public static MockCollectionResourceNode CreateFakeNodeForEmployee() + { + var singleEntityNode = MockSingleEntityNode.CreateFakeNodeForEmployee(); + return new MockCollectionResourceNode( + singleEntityNode.EntityTypeReference, singleEntityNode.NavigationSource, singleEntityNode.EntityTypeReference, singleEntityNode.EntityTypeReference.AsCollection()); + } +} +#endregion From 7b9b9c3dcc5c4c08dd2db422cd3bbc9f53eac699 Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Mon, 28 Oct 2024 14:10:20 +0300 Subject: [PATCH 12/14] Refactor WorkAddress --- .../Models/WorkAddress.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/Microsoft.AspNetCore.OData.Tests/Models/WorkAddress.cs b/test/Microsoft.AspNetCore.OData.Tests/Models/WorkAddress.cs index 50c73cb07..740b64a35 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Models/WorkAddress.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Models/WorkAddress.cs @@ -5,10 +5,10 @@ // //------------------------------------------------------------------------------ -namespace Microsoft.AspNetCore.OData.Tests.Models +namespace Microsoft.AspNetCore.OData.Tests.Models; + +public class WorkAddress : Address { - internal class WorkAddress : Address - { - public string OfficeNumber { get; set; } - } + public string OfficeNumber { get; set; } } + From 4cd1e9743c80a4a243e6d5573c768bc03d21b77d Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Wed, 30 Oct 2024 10:31:20 +0300 Subject: [PATCH 13/14] Refactor code by doing some nit --- .../Query/Expressions/QueryBinderTests.cs | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs index 65de4cd7a..c721071ed 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs @@ -110,7 +110,7 @@ public void BindSingleResourceFunctionCallNode_ThrowsNotSupported_ForNotAcceptNo "Unknown function 'anyUnknown'."); } - public static TheoryDataSet> BindSingleResourceFunctionCallNodeForEntityTypCasting_Data + public static TheoryDataSet> BindSingleResourceFunctionCallNodeForEntityTypeCasting_Data { get { @@ -145,26 +145,27 @@ public static TheoryDataSet> BindSingleResourceFunctionCallNodeF } [Theory] - [MemberData(nameof(BindSingleResourceFunctionCallNodeForEntityTypCasting_Data))] + [MemberData(nameof(BindSingleResourceFunctionCallNodeForEntityTypeCasting_Data))] public void BindSingleResourceFunctionCallNode_CastingEntityType_ReturnsExpression(List parameters) { // Arrange var binder = new MyQueryBinder(); // Create the type reference and navigation source - var employeeType = GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Employee"); var collectionNode = MockCollectionResourceNode.CreateFakeNodeForEmployee(); - // Get the entity type for the Manager entity -> Manager is derived from Employee - var managerType = GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Manager"); - // Create the SingleResourceFunctionCallNode var node = new SingleResourceFunctionCallNode("cast", parameters, collectionNode.ItemStructuredType, collectionNode.NavigationSource); + var employeeType = GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Employee"); + // Create an instance of QueryBinderContext using the real model and settings Type clrType = TestModel.GetClrType(employeeType); var context = new QueryBinderContext(TestModel, new ODataQuerySettings(), clrType); + // Get the entity type for the Manager entity -> Manager is derived from Employee + var managerType = GetEntityTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Manager"); + // Act Expression expression = binder.BindSingleResourceFunctionCallNode(node, context); @@ -183,7 +184,6 @@ public static TheoryDataSet> BindSingleResourceFunctionCallNodeF { get { - var addressType = GetEdmComplexTypeFor("Microsoft.AspNetCore.OData.Tests.Models.Address"); var workAddressType = GetEdmComplexTypeFor("Microsoft.AspNetCore.OData.Tests.Models.WorkAddress"); // Create a ResourceRangeVariableReferenceNode for the Employee entity @@ -435,33 +435,33 @@ public static PropertyInfo Call_GetDynamicPropertyContainer(SingleValueOpenPrope #region Mock Single Entity Node public class MockSingleEntityNode : SingleEntityNode { - private readonly IEdmEntityTypeReference typeReference; - private readonly IEdmEntitySetBase set; + private readonly IEdmEntityTypeReference entityTypeReference; + private readonly IEdmEntitySetBase entitySet; - public MockSingleEntityNode(IEdmEntityTypeReference type, IEdmEntitySetBase set) + public MockSingleEntityNode(IEdmEntityTypeReference entityTypeReference, IEdmEntitySetBase entitySet) { - this.typeReference = type; - this.set = set; + this.entityTypeReference = entityTypeReference; + this.entitySet = entitySet; } public override IEdmTypeReference TypeReference { - get { return this.typeReference; } + get { return this.entityTypeReference; } } public override IEdmNavigationSource NavigationSource { - get { return this.set; } + get { return this.entitySet; } } public override IEdmStructuredTypeReference StructuredTypeReference { - get { return this.typeReference; } + get { return this.entityTypeReference; } } public override IEdmEntityTypeReference EntityTypeReference { - get { return this.typeReference; } + get { return this.entityTypeReference; } } public static MockSingleEntityNode CreateFakeNodeForEmployee() @@ -475,32 +475,32 @@ public static MockSingleEntityNode CreateFakeNodeForEmployee() #region Mock Collection Resource Node public class MockCollectionResourceNode : CollectionResourceNode { - private readonly IEdmStructuredTypeReference _typeReference; - private readonly IEdmNavigationSource _source; - private readonly IEdmTypeReference _itemType; - private readonly IEdmCollectionTypeReference _collectionType; + private readonly IEdmStructuredTypeReference structuredTypeReference; + private readonly IEdmNavigationSource navigationSource; + private readonly IEdmTypeReference typeReference; + private readonly IEdmCollectionTypeReference collectionTypeReference; - public MockCollectionResourceNode(IEdmStructuredTypeReference type, IEdmNavigationSource source, IEdmTypeReference itemType, IEdmCollectionTypeReference collectionType) + public MockCollectionResourceNode(IEdmStructuredTypeReference structuredTypeReference, IEdmNavigationSource navigationSource, IEdmTypeReference typeReference) { - _typeReference = type; - _source = source; - _itemType = itemType; - _collectionType = collectionType; + this.structuredTypeReference = structuredTypeReference; + this.navigationSource = navigationSource; + this.typeReference = typeReference; + this.collectionTypeReference = typeReference.AsCollection(); } - public override IEdmStructuredTypeReference ItemStructuredType => _typeReference; + public override IEdmStructuredTypeReference ItemStructuredType => structuredTypeReference; - public override IEdmNavigationSource NavigationSource => _source; + public override IEdmNavigationSource NavigationSource => navigationSource; - public override IEdmTypeReference ItemType => _itemType; + public override IEdmTypeReference ItemType => typeReference; - public override IEdmCollectionTypeReference CollectionType => _collectionType; + public override IEdmCollectionTypeReference CollectionType => collectionTypeReference; public static MockCollectionResourceNode CreateFakeNodeForEmployee() { var singleEntityNode = MockSingleEntityNode.CreateFakeNodeForEmployee(); return new MockCollectionResourceNode( - singleEntityNode.EntityTypeReference, singleEntityNode.NavigationSource, singleEntityNode.EntityTypeReference, singleEntityNode.EntityTypeReference.AsCollection()); + singleEntityNode.EntityTypeReference, singleEntityNode.NavigationSource, singleEntityNode.EntityTypeReference); } } #endregion From ff5371169060bd4939c1f133c76c205f28aee1dd Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Wed, 30 Oct 2024 10:34:16 +0300 Subject: [PATCH 14/14] Removed unnecessary capacity --- .../Query/Expressions/QueryBinderTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs index c721071ed..343785b5d 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Query/Expressions/QueryBinderTests.cs @@ -291,8 +291,7 @@ public void BindSingleResourceFunctionCallNode_ThrowsNotSupported_ForNotAcceptPa var convertNode = new ConvertNode(employeeIDNode, edmTypeReference); // Create the parameters list - int capacity = 2; - var parameters = new List(capacity) + var parameters = new List() { employeeNode, // First parameter is the Person entity, convertNode // Second parameter is the ConvertNode