public void FilterBinderTest() { IEdmModel model = QueryTestMetadata.BuildTestMetadata(this.PrimitiveTypeResolver, this.UntypedDataServiceProviderFactory); this.CombinatorialEngineProvider.RunCombinations( LiteralTestCases() .Concat(UnaryOperatorTestCases()) .Concat(BinaryOperatorTestCases()) .Concat(PropertyAccessTestCases(model)) .Concat(TopFilterExpressionTestCases(model)) .Concat(BuiltInStringFunctionCallTestCases(model)) .Concat(BuiltInDateTimeOffsetFunctionCallTestCases(model)) .Concat(BuiltInMathFunctionCallTestCases(model)), (testCase) => { string query = "/" + testCase.EntitySetName + "?$filter=" + testCase.Filter; ODataUri actual = QueryNodeUtils.BindQuery(query, model); // construct the expected filter node var entitySet = model.FindDeclaredEntitySet(testCase.EntitySetName); CollectionResourceNode entityCollectionNode = new EntitySetNode(entitySet); var expectedFilter = new FilterClause( testCase.ExpectedFilterCondition, new ResourceRangeVariable(ExpressionConstants.It, entitySet.EntityType().ToTypeReference(false).AsEntity(), entityCollectionNode) ); QueryNodeUtils.VerifyFilterClausesAreEqual( expectedFilter, actual.Filter, this.Assert); }); }
public void RelationalOperatorTypePromotion() { IEdmModel model = QueryTestMetadata.BuildTestMetadata(this.PrimitiveTypeResolver, this.StronglyTypedDataServiceProviderFactory); BinaryOperatorKind[] relationalOperatorKinds = new BinaryOperatorKind[] { BinaryOperatorKind.Equal, BinaryOperatorKind.NotEqual, BinaryOperatorKind.GreaterThan, BinaryOperatorKind.GreaterThanOrEqual, BinaryOperatorKind.LessThan, BinaryOperatorKind.LessThanOrEqual }; var testCases = ComputeRelationalTestCases().Concat(ComputeRelationalErrorTestCases()); this.CombinatorialEngineProvider.RunCombinations( testCases, relationalOperatorKinds, (testCase, relationalOperatorKind) => { string filter = testCase.Arguments[0] + " " + QueryTestUtils.ToOperatorName(relationalOperatorKind) + " " + testCase.Arguments[1]; string errorMessage = null; if (testCase.ExpectedErrorMessage != null) { errorMessage = string.Format(CultureInfo.InvariantCulture, testCase.ExpectedErrorMessage, relationalOperatorKind); } var actualFilter = this.BindFilter(model, filter, errorMessage); if (errorMessage == null) { this.Assert.IsNotNull(actualFilter, "Filter must not be null."); BinaryOperatorNode binaryOperatorNode = null; if (actualFilter.Expression.InternalKind == InternalQueryNodeKind.Convert) { binaryOperatorNode = ((ConvertNode)actualFilter.Expression).Source as BinaryOperatorNode; } else { binaryOperatorNode = actualFilter.Expression as BinaryOperatorNode; } this.Assert.IsNotNull(binaryOperatorNode, "Expected a binary operator at the top of the filter."); QueryTestUtils.VerifyTypesAreEqual( testCase.ExpectedResultType, binaryOperatorNode.Left.TypeReference, this.Assert); QueryTestUtils.VerifyTypesAreEqual( testCase.ExpectedResultType, binaryOperatorNode.Right.TypeReference, this.Assert); } }); }
public void UnsupportedTokenBinderErrorTest() { IEdmModel model = QueryTestMetadata.BuildTestMetadata(this.PrimitiveTypeResolver, this.UntypedDataServiceProviderFactory); var binder = new ErrorMetadataBinder(model); var token = new LiteralToken("d"); Action action = () => binder.Bind(token); action.ShouldThrow <ODataException>().WithMessage(ODataErrorStrings.MetadataBinder_BoundNodeCannotBeNull(token.Kind)); }
public void BindExtensionTest() { var metadata = QueryTestMetadata.BuildTestMetadata(this.PrimitiveTypeResolver, this.UntypedDataServiceProviderFactory); MetadataBinder binder = new MetadataBinder(metadata); SyntacticTree syntax = new SyntacticTree(new TestExtensionQueryToken(), null, null, null, null, null, null, null, null, null); this.Assert.ExpectedException <ODataException>( () => binder.BindQuery(syntax), "An unsupported extension query token was found.", "BindExtensionTest"); }
public void NegateTypePromotion() { var metadata = QueryTestMetadata.BuildTestMetadata(this.PrimitiveTypeResolver, this.StronglyTypedDataServiceProviderFactory); this.CombinatorialEngineProvider.RunCombinations( ComputeUnaryTestCases(UnaryOperatorKind.Negate).Concat(ComputeUnaryErrorTestCases(UnaryOperatorKind.Negate)), (testCase) => { string filter = "-" + testCase.Arguments[0] + " le 0"; string errorMessage = null; if (testCase.ExpectedErrorMessage != null) { errorMessage = string.Format(CultureInfo.InvariantCulture, testCase.ExpectedErrorMessage, "Negate"); } var actualFilter = this.BindFilter(metadata, filter, errorMessage); if (errorMessage == null) { this.Assert.IsNotNull(actualFilter, "Filter must not be null."); BinaryOperatorNode binaryOperatorNode = null; if (actualFilter.Expression.InternalKind == InternalQueryNodeKind.Convert) { binaryOperatorNode = ((ConvertNode)actualFilter.Expression).Source as BinaryOperatorNode; } else { binaryOperatorNode = actualFilter.Expression as BinaryOperatorNode; } this.Assert.IsNotNull(binaryOperatorNode, "Expected a binary operator at the top of the filter."); UnaryOperatorNode unaryOperatorNode = null; if (binaryOperatorNode.Left.InternalKind == InternalQueryNodeKind.Convert) { unaryOperatorNode = ((ConvertNode)binaryOperatorNode.Left).Source as UnaryOperatorNode; } else { unaryOperatorNode = binaryOperatorNode.Left as UnaryOperatorNode; } this.Assert.IsNotNull(unaryOperatorNode, "Expected a unary operator as the left argument of the binary operator."); QueryTestUtils.VerifyTypesAreEqual( testCase.ExpectedResultType, unaryOperatorNode.Operand.TypeReference, this.Assert); } }); }
public void InvalidFilterBinderTest() { var metadata = QueryTestMetadata.BuildTestMetadata(this.PrimitiveTypeResolver, this.UntypedDataServiceProviderFactory); this.CombinatorialEngineProvider.RunCombinations( InvalidBinaryOperatorTestCases() .Concat(InvalidUnaryOperatorTestCases()) .Concat(InvalidPropertyAccessTestCases()), (testCase) => { string query = "/Customers?$filter=" + testCase.Filter; this.Assert.ExpectedException <ODataException>( () => QueryNodeUtils.BindQuery(query, metadata), testCase.ExpectedExceptionMessage, "Invalid filter binder test."); }); }
public void NotTypePromotion() { var metadata = QueryTestMetadata.BuildTestMetadata(this.PrimitiveTypeResolver, this.StronglyTypedDataServiceProviderFactory); // run over all operator kinds (not, negate) // use all combinations with the same argument types (plain and nullable) this.CombinatorialEngineProvider.RunCombinations( ComputeUnaryTestCases(UnaryOperatorKind.Not).Concat(ComputeUnaryErrorTestCases(UnaryOperatorKind.Not)), (testCase) => { string filter = "not " + testCase.Arguments[0]; string errorMessage = null; if (testCase.ExpectedErrorMessage != null) { errorMessage = string.Format(CultureInfo.InvariantCulture, testCase.ExpectedErrorMessage, "Not"); } var actualFilter = this.BindFilter(metadata, filter, errorMessage); if (errorMessage == null) { this.Assert.IsNotNull(actualFilter, "Filter must not be null."); UnaryOperatorNode unaryOperatorNode = null; if (actualFilter.Expression.InternalKind == InternalQueryNodeKind.Convert) { unaryOperatorNode = ((ConvertNode)actualFilter.Expression).Source as UnaryOperatorNode; } else { unaryOperatorNode = actualFilter.Expression as UnaryOperatorNode; } this.Assert.IsNotNull(unaryOperatorNode, "Expected a unary operator at the top of the filter."); QueryTestUtils.VerifyTypesAreEqual( testCase.ExpectedResultType, unaryOperatorNode.Operand.TypeReference, this.Assert); } }); }
public void KeyLookupBinderErrorTest() { IEdmModel model = QueryTestMetadata.BuildTestMetadata(this.PrimitiveTypeResolver, this.UntypedDataServiceProviderFactory); var testCases = new[] { new { CaseUri = new Uri("Customers(ID=1,ID=2)", UriKind.Relative), ExpectedMessage = "A key property 'ID' was found twice in a key lookup. Each key property can be specified just once in a key lookup." }, new { CaseUri = new Uri("Customers(Name='Bob')", UriKind.Relative), ExpectedMessage = "Property 'Name' is not declared on type 'TestNS.Customer' or is not a key property. Only key properties can be used in key lookups." }, new { CaseUri = new Uri("Customers(UndeclaredProperty='Bob')", UriKind.Relative), ExpectedMessage = "Property 'UndeclaredProperty' is not declared on type 'TestNS.Customer' or is not a key property. Only key properties can be used in key lookups.", }, new { CaseUri = new Uri("MultiKeys(KeyA='Bob')", UriKind.Relative), ExpectedMessage = "A key lookup on type 'TestNS.MultiKey' didn't specify values for all key properties. All key properties must be specified in a key lookup.", }, new { CaseUri = new Uri("MultiKeys('Bob')", UriKind.Relative), ExpectedMessage = "An unnamed key value was used in a key lookup on a type 'TestNS.MultiKey' which has more than one key property. Unnamed key value can only be used on a type with one key property.", }, new { CaseUri = new Uri("Customers(ID='Bob')", UriKind.Relative), ExpectedMessage = "Expression of type 'Edm.String' cannot be converted to type 'Edm.Int32'.", }, }; this.CombinatorialEngineProvider.RunCombinations( testCases, (testCase) => { var parser = new ODataUriParser(model, testCase.CaseUri); Action action = () => parser.ParsePath(); action.ShouldThrow <ODataException>().WithMessage(testCase.ExpectedMessage); }); }
public void OrderByBinderTest() { IEdmModel model = QueryTestMetadata.BuildTestMetadata(this.PrimitiveTypeResolver, this.UntypedDataServiceProviderFactory); // TODO: add an error test where the input collection to the order-by is a singleton ResourceRangeVariable entityRangeVariable = new ResourceRangeVariable("dummy", model.ResolveTypeReference("TestNS.Customer", false).AsEntity(), model.FindEntityContainer("BinderTestMetadata").FindEntitySet("Customers")); OrderByTestCase[] testCases = new OrderByTestCase[] { new OrderByTestCase() { OrderBy = new string[] { "true" }, ExpectedOrderByExpressions = new SingleValueNode[] { new ConstantNode(true) } }, new OrderByTestCase() { OrderBy = new string[] { "Name" }, ExpectedOrderByExpressions = new SingleValueNode[] { new SingleValuePropertyAccessNode( new ResourceRangeVariableReferenceNode(entityRangeVariable.Name, entityRangeVariable), model.ResolveProperty("TestNS.Customer.Name") ) } }, new OrderByTestCase() { OrderBy = new string[] { "3" }, ExpectedOrderByExpressions = new SingleValueNode[] { new ConstantNode(3) } }, new OrderByTestCase() { OrderBy = new string[] { "null" }, ExpectedOrderByExpressions = new SingleValueNode[] { new ConstantNode(null) } }, new OrderByTestCase() { OrderBy = new string[] { "Address" }, ExpectedExceptionMessage = "The $orderby expression must evaluate to a single value of primitive type." }, new OrderByTestCase() { OrderBy = new string[] { "Emails" }, ExpectedExceptionMessage = "The $orderby expression must evaluate to a single value of primitive type." }, new OrderByTestCase() { OrderBy = new string[] { "NonExistant" }, ExpectedExceptionMessage = "Could not find a property named 'NonExistant' on type 'TestNS.Customer'." }, new OrderByTestCase() { OrderBy = new string[] { "Name", "ID" }, ExpectedOrderByExpressions = new SingleValueNode[] { new SingleValuePropertyAccessNode( new ResourceRangeVariableReferenceNode(entityRangeVariable.Name, entityRangeVariable), model.ResolveProperty("TestNS.Customer.Name") ), new SingleValuePropertyAccessNode( new ResourceRangeVariableReferenceNode(entityRangeVariable.Name, entityRangeVariable), model.ResolveProperty("TestNS.Customer.ID") ), } }, new OrderByTestCase() { OrderBy = new string[] { "Name", "Address" }, ExpectedExceptionMessage = "The $orderby expression must evaluate to a single value of primitive type." }, new OrderByTestCase() { OrderBy = new string[] { "Name", "Emails" }, ExpectedExceptionMessage = "The $orderby expression must evaluate to a single value of primitive type." }, new OrderByTestCase() { OrderBy = new string[] { "Name", "NonExistant" }, ExpectedExceptionMessage = "Could not find a property named 'NonExistant' on type 'TestNS.Customer'." }, }; this.CombinatorialEngineProvider.RunCombinations( testCases, new OrderByDirection?[] { null, OrderByDirection.Ascending, OrderByDirection.Descending }, (testCase, direction) => { string orderByDirection = direction == null ? string.Empty : direction == OrderByDirection.Ascending ? " asc" : " desc"; this.Assert.IsTrue(testCase.OrderBy.Length > 0, "Need at least one order-by expression"); StringBuilder queryBuilder = new StringBuilder("/Customers?$orderby="); for (int i = 0; i < testCase.OrderBy.Length; ++i) { if (i > 0) { queryBuilder.Append(", "); } queryBuilder.Append(testCase.OrderBy[i]); queryBuilder.Append(orderByDirection); } TestExceptionUtils.ExpectedException <ODataException>( this.Assert, () => { ODataUri actual = QueryNodeUtils.BindQuery(queryBuilder.ToString(), model); // construct the expected order-by node OrderByClause orderByNode = null; for (int i = testCase.ExpectedOrderByExpressions.Length - 1; i >= 0; --i) { orderByNode = new OrderByClause( orderByNode, testCase.ExpectedOrderByExpressions[i], direction ?? OrderByDirection.Ascending, new ResourceRangeVariable(ExpressionConstants.It, model.ResolveTypeReference("TestNS.Customer", false).AsEntity(), model.FindEntityContainer("BinderTestMetadata").FindEntitySet("Customers")) ); } QueryNodeUtils.VerifyOrderByClauseAreEqual( orderByNode, actual.OrderBy, this.Assert); }, testCase.ExpectedExceptionMessage, null); }); }
public void CustomQueryOptionBinderTest() { var metadata = QueryTestMetadata.BuildTestMetadata(this.PrimitiveTypeResolver, this.UntypedDataServiceProviderFactory); var testCases = new[] { new QueryOptionTestCase { Query = "/Customers?$foo=12", ExpectedExceptionMessage = "The system query option '$foo' is not supported." }, new QueryOptionTestCase { Query = "/Customers?a=b", ExpectedQueryOptions = new CustomQueryOptionNode[] { new CustomQueryOptionNode("a", "b") } }, new QueryOptionTestCase { Query = "/Customers?a=b&c=d", ExpectedQueryOptions = new CustomQueryOptionNode[] { new CustomQueryOptionNode("a", "b"), new CustomQueryOptionNode("c", "d") } }, new QueryOptionTestCase { Query = "/Customers?a=b&a=c", ExpectedQueryOptions = new CustomQueryOptionNode[] { new CustomQueryOptionNode("a", "b"), new CustomQueryOptionNode("a", "c") } }, new QueryOptionTestCase { Query = "/Customers?foo", ExpectedQueryOptions = new CustomQueryOptionNode[] { new CustomQueryOptionNode(null, "foo") } }, new QueryOptionTestCase { Query = "/Customers?foo=", ExpectedQueryOptions = new CustomQueryOptionNode[] { new CustomQueryOptionNode("foo", string.Empty) } }, new QueryOptionTestCase { Query = "/Customers?&", ExpectedQueryOptions = new CustomQueryOptionNode[] { new CustomQueryOptionNode(null, string.Empty), new CustomQueryOptionNode(null, string.Empty) } }, new QueryOptionTestCase { Query = "/Customers?a=b&", ExpectedQueryOptions = new CustomQueryOptionNode[] { new CustomQueryOptionNode("a", "b"), new CustomQueryOptionNode(null, string.Empty) } }, new QueryOptionTestCase { Query = "/Customers?&&", ExpectedQueryOptions = new CustomQueryOptionNode[] { new CustomQueryOptionNode(null, string.Empty), new CustomQueryOptionNode(null, string.Empty), new CustomQueryOptionNode(null, string.Empty) } }, new QueryOptionTestCase { Query = "/Customers?a=b&&", ExpectedQueryOptions = new CustomQueryOptionNode[] { new CustomQueryOptionNode("a", "b"), new CustomQueryOptionNode(null, string.Empty), new CustomQueryOptionNode(null, string.Empty) } }, }; this.CombinatorialEngineProvider.RunCombinations( testCases, (testCase) => { TestExceptionUtils.ExpectedException <ODataException>( this.Assert, () => { SemanticTree actual = QueryNodeUtils.BindQuery(testCase.Query, metadata); QueryNodeUtils.VerifyQueryNodesAreEqual( testCase.ExpectedQueryOptions, actual.CustomQueryOptions, this.Assert); }, testCase.ExpectedExceptionMessage, null); }); }
public void SkipAndTopBinderTest() { IEdmModel model = QueryTestMetadata.BuildTestMetadata(this.PrimitiveTypeResolver, this.UntypedDataServiceProviderFactory); var testCases = new[] { new SkipTopTestCase { Query = "/Customers?$skip=2", ExpectedSkipClause = 2 }, new SkipTopTestCase { Query = "/Customers?$top=2", ExpectedTopClause = 2 }, new SkipTopTestCase { Query = "/Customers?$skip=3&$top=2", ExpectedTopClause = 2, ExpectedSkipClause = 3 }, new SkipTopTestCase { Query = "/Customers?$top=2&$skip=3", ExpectedTopClause = 2, ExpectedSkipClause = 3 }, // TODO: enable those? /* * new SkipTopTestCase * { * Query = "/Customers(0)?$top=100", * ExpectedExceptionMessage = "The $top query option cannot be applied to the query path. Top can only be applied to a collection of entities." * }, * new SkipTopTestCase * { * Query = "/Customers(0)?$skip=100", * ExpectedExceptionMessage = "The $skip query option cannot be applied to the query path. Skip can only be applied to a collection of entities." * }, * new SkipTopTestCase * { * Query = "/Customers(0)?$skip=100&top=100", * ExpectedExceptionMessage = "The $skip query option cannot be applied to the query path. Skip can only be applied to a collection of entities." * }, * new SkipTopTestCase * { * Query = "/Customers(0)?top=100&$skip=100", * ExpectedExceptionMessage = "The $skip query option cannot be applied to the query path. Skip can only be applied to a collection of entities." * }, */ }; this.CombinatorialEngineProvider.RunCombinations( testCases, (testCase) => { TestExceptionUtils.ExpectedException <ODataException>( this.Assert, () => { ODataUri actual = QueryNodeUtils.BindQuery(testCase.Query, model); if (testCase.ExpectedSkipClause != null) { Assert.AreEqual(testCase.ExpectedSkipClause, actual.Skip, "Skip amounts are not equal!"); } if (testCase.ExpectedTopClause != null) { Assert.AreEqual(testCase.ExpectedTopClause, actual.Top, "Top amounts are not equal!"); } }, testCase.ExpectedExceptionMessage, null); }); }
public void ArithmeticOperatorTypePromotion() { var metadata = QueryTestMetadata.BuildTestMetadata(this.PrimitiveTypeResolver, this.StronglyTypedDataServiceProviderFactory); BinaryOperatorKind[] arithmeticOperatorKinds = new BinaryOperatorKind[] { BinaryOperatorKind.Add, BinaryOperatorKind.Subtract, BinaryOperatorKind.Multiply, BinaryOperatorKind.Divide, BinaryOperatorKind.Modulo, }; var testCases = ComputeArithmeticTestCases().Concat(ComputeArithmeticErrorTestCases()); this.CombinatorialEngineProvider.RunCombinations( testCases, arithmeticOperatorKinds, (testCase, arithmeticOperatorKind) => { string filter = testCase.Arguments[0] + " " + QueryTestUtils.ToOperatorName(arithmeticOperatorKind) + " " + testCase.Arguments[1] + " le 0"; string errorMessage = null; if (testCase.ExpectedErrorMessage != null) { errorMessage = string.Format(CultureInfo.InvariantCulture, testCase.ExpectedErrorMessage, arithmeticOperatorKind); } var actualFilter = this.BindFilter(metadata, filter, errorMessage); if (errorMessage == null) { this.Assert.IsNotNull(actualFilter, "Filter must not be null."); BinaryOperatorNode binaryOperatorNode = null; if (actualFilter.Expression.InternalKind == InternalQueryNodeKind.Convert) { binaryOperatorNode = ((ConvertNode)actualFilter.Expression).Source as BinaryOperatorNode; } else { binaryOperatorNode = actualFilter.Expression as BinaryOperatorNode; } this.Assert.IsNotNull(binaryOperatorNode, "Expected a binary operator at the top of the filter."); if (binaryOperatorNode.Left.InternalKind == InternalQueryNodeKind.Convert) { binaryOperatorNode = ((ConvertNode)binaryOperatorNode.Left).Source as BinaryOperatorNode; } else { binaryOperatorNode = binaryOperatorNode.Left as BinaryOperatorNode; } this.Assert.IsNotNull(binaryOperatorNode, "Expected a binary operator as the left argument of the top-level binary operator."); QueryTestUtils.VerifyTypesAreEqual( testCase.ExpectedResultType, binaryOperatorNode.Left.TypeReference, this.Assert); QueryTestUtils.VerifyTypesAreEqual( testCase.ExpectedResultType, binaryOperatorNode.Right.TypeReference, this.Assert); } }); }