From 34325050bb1c029dcf6d7d16d58f9d2225500939 Mon Sep 17 00:00:00 2001 From: John Gathogo Date: Tue, 20 Feb 2024 11:36:12 +0300 Subject: [PATCH] Tests for single quotes on in expression (#1078) --- .../DollarFilter/DollarFilterController.cs | 23 +++++ .../DollarFilter/DollarFilterDataModel.cs | 15 +++ .../DollarFilter/DollarFilterDataSource.cs | 29 ++++++ .../DollarFilter/DollarFilterEdmModel.cs | 23 +++++ .../DollarFilter/DollarFilterTests.cs | 98 +++++++++++++++++++ 5 files changed, 188 insertions(+) create mode 100644 test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterController.cs create mode 100644 test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterDataModel.cs create mode 100644 test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterDataSource.cs create mode 100644 test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterEdmModel.cs create mode 100644 test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterTests.cs diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterController.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterController.cs new file mode 100644 index 000000000..f0070c87d --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterController.cs @@ -0,0 +1,23 @@ +//----------------------------------------------------------------------------- +// +// Copyright (c) .NET Foundation and Contributors. All rights reserved. +// See License.txt in the project root for license information. +// +//------------------------------------------------------------------------------ + +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.OData.Query; +using Microsoft.AspNetCore.OData.Routing.Controllers; + +namespace Microsoft.AspNetCore.OData.E2E.Tests.DollarFilter +{ + public class PeopleController : ODataController + { + [EnableQuery] + public ActionResult> Get() + { + return Ok(DollarFilterDataSource.People); + } + } +} diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterDataModel.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterDataModel.cs new file mode 100644 index 000000000..fbb9ad80b --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterDataModel.cs @@ -0,0 +1,15 @@ +//----------------------------------------------------------------------------- +// +// Copyright (c) .NET Foundation and Contributors. All rights reserved. +// See License.txt in the project root for license information. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.AspNetCore.OData.E2E.Tests.DollarFilter +{ + public class Person + { + public int Id { get; set; } + public string SSN { get; set; } + } +} diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterDataSource.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterDataSource.cs new file mode 100644 index 000000000..a839cc4d1 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterDataSource.cs @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------------- +// +// Copyright (c) .NET Foundation and Contributors. All rights reserved. +// See License.txt in the project root for license information. +// +//------------------------------------------------------------------------------ + +using System.Collections.Generic; + +namespace Microsoft.AspNetCore.OData.E2E.Tests.DollarFilter +{ + public class DollarFilterDataSource + { + private static IList people; + + static DollarFilterDataSource() + { + people = new List + { + new Person { Id = 1, SSN = "a'bc" }, + new Person { Id = 2, SSN = "'def" }, + new Person { Id = 3, SSN = "xyz'" }, + new Person { Id = 4, SSN = "'pqr'" } + }; + } + + public static IList People => people; + } +} diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterEdmModel.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterEdmModel.cs new file mode 100644 index 000000000..6882766ae --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterEdmModel.cs @@ -0,0 +1,23 @@ +//----------------------------------------------------------------------------- +// +// 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.ModelBuilder; + +namespace Microsoft.AspNetCore.OData.E2E.Tests.DollarFilter +{ + public class DollarFilterEdmModel + { + public static IEdmModel GetEdmModel() + { + var builder = new ODataConventionModelBuilder(); + builder.EntitySet("People"); + + return builder.GetEdmModel(); + } + } +} diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterTests.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterTests.cs new file mode 100644 index 000000000..0b76cf5bc --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarFilter/DollarFilterTests.cs @@ -0,0 +1,98 @@ +//----------------------------------------------------------------------------- +// +// Copyright (c) .NET Foundation and Contributors. All rights reserved. +// See License.txt in the project root for license information. +// +//------------------------------------------------------------------------------ + +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Microsoft.AspNetCore.OData.TestCommon; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.OData.Edm; +using Xunit; + +namespace Microsoft.AspNetCore.OData.E2E.Tests.DollarFilter +{ + public class DollarFilterTests : WebApiTestBase + { + public DollarFilterTests(WebApiTestFixture fixture) + : base(fixture) + { + } + + protected static void UpdateConfigureServices(IServiceCollection services) + { + IEdmModel model = DollarFilterEdmModel.GetEdmModel(); + + services.ConfigureControllers(typeof(PeopleController)); + + services.AddControllers().AddOData(opt => + opt.Filter().Select().AddRouteComponents("odata", model)); + } + + [Theory] + [InlineData("('a''bc')", "[{\"Id\":1,\"SSN\":\"a'bc\"}]")] + [InlineData("('''def')", "[{\"Id\":2,\"SSN\":\"'def\"}]")] + [InlineData("('xyz''')", "[{\"Id\":3,\"SSN\":\"xyz'\"}]")] + [InlineData("('''pqr''')", "[{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('a''bc','''def')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":2,\"SSN\":\"'def\"}]")] + [InlineData("('a''bc','xyz''')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":3,\"SSN\":\"xyz'\"}]")] + [InlineData("('a''bc','''pqr''')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('''def','a''bc')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":2,\"SSN\":\"'def\"}]")] + [InlineData("('''def','xyz''')", "[{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":3,\"SSN\":\"xyz'\"}]")] + [InlineData("('''def','''pqr''')", "[{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('xyz''','a''bc')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":3,\"SSN\":\"xyz'\"}]")] + [InlineData("('xyz''','''def')", "[{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":3,\"SSN\":\"xyz'\"}]")] + [InlineData("('xyz''','''pqr''')", "[{\"Id\":3,\"SSN\":\"xyz'\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('''pqr''','a''bc')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('''pqr''','''def')", "[{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('''pqr''','xyz''')", "[{\"Id\":3,\"SSN\":\"xyz'\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('a''bc','''def','xyz''')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":3,\"SSN\":\"xyz'\"}]")] + [InlineData("('a''bc','''def','''pqr''')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('a''bc','xyz''','''def')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":3,\"SSN\":\"xyz'\"}]")] + [InlineData("('a''bc','xyz''','''pqr''')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":3,\"SSN\":\"xyz'\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('a''bc','''pqr''','''def')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('a''bc','''pqr''','xyz''')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":3,\"SSN\":\"xyz'\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('''def','a''bc','xyz''')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":3,\"SSN\":\"xyz'\"}]")] + [InlineData("('''def','a''bc','''pqr''')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('''def','xyz''','a''bc')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":3,\"SSN\":\"xyz'\"}]")] + [InlineData("('''def','xyz''','''pqr''')", "[{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":3,\"SSN\":\"xyz'\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('''def','''pqr''','a''bc')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('''def','''pqr''','xyz''')", "[{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":3,\"SSN\":\"xyz'\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('xyz''','a''bc','''def')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":3,\"SSN\":\"xyz'\"}]")] + [InlineData("('xyz''','a''bc','''pqr''')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":3,\"SSN\":\"xyz'\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('xyz''','''def','''pqr''')", "[{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":3,\"SSN\":\"xyz'\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('xyz''','''def','a''bc')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":3,\"SSN\":\"xyz'\"}]")] + [InlineData("('xyz''','''pqr''','a''bc')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":3,\"SSN\":\"xyz'\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('xyz''','''pqr''','''def')", "[{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":3,\"SSN\":\"xyz'\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('''pqr''','a''bc','''def')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('''pqr''','a''bc','xyz''')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":3,\"SSN\":\"xyz'\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('''pqr''','''def','a''bc')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('''pqr''','''def','xyz''')", "[{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":3,\"SSN\":\"xyz'\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('''pqr''','xyz''','a''bc')", "[{\"Id\":1,\"SSN\":\"a'bc\"},{\"Id\":3,\"SSN\":\"xyz'\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + [InlineData("('''pqr''','xyz''','''def')", "[{\"Id\":2,\"SSN\":\"'def\"},{\"Id\":3,\"SSN\":\"xyz'\"},{\"Id\":4,\"SSN\":\"'pqr'\"}]")] + public async Task TestSingleQuotesOnInExpression(string inExpr, string partialResult) + { + // Arrange + var queryUrl = $"odata/People?$filter=SSN in {inExpr}"; + var request = new HttpRequestMessage(HttpMethod.Get, queryUrl); + request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json;odata.metadata=minimal")); + var client = CreateClient(); + + // Act + var response = await client.SendAsync(request); + + // Assert + Assert.NotNull(response); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + + var result = await response.Content.ReadAsStringAsync(); + + Assert.EndsWith($"$metadata#People\",\"value\":{partialResult}}}", result); + } + } +}