Skip to content

Commit 1e372c7

Browse files
VectorTetraViktor Tochonovxperiandri
committed
Implemented tests for scalar wrappers and improved logic for Contains, StartsWith and EndsWith operators (#524)
--------- Co-authored-by: Viktor Tochonov <[email protected]> Co-authored-by: Andrii Chebukin <[email protected]>
1 parent 7fd53c2 commit 1e372c7

File tree

4 files changed

+176
-7
lines changed

4 files changed

+176
-7
lines changed

Packages.props

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
<PackageReference Update="GraphQL.Server.Ui.Voyager" Version="8.*" />
7676
<PackageReference Update="HotChocolate.AspNetCore" Version="15.*" />
7777
<PackageReference Update="Iced" Version="1.17.*" />
78+
<PackageReference Update="Microsoft.Azure.Cosmos" Version="3.*" />
7879
<PackageReference Update="Microsoft.CodeCoverage" Version="17.3.*" />
7980
<PackageReference Update="Microsoft.Data.Sqlite" Version="$(MicrosoftExtensionsVersion)" />
8081
<PackageReference Update="Microsoft.Diagnostics.NETCore.Client" Version="0.2.*" />

src/FSharp.Data.GraphQL.Server.Middleware/ObjectListFilter.fs

+22-7
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,10 @@ module ObjectListFilter =
138138
let whereMethod = genericWhereMethod.MakeGenericMethod ([| typeof<'T> |])
139139
Expression.Call (whereMethod, [| query.Expression; Expression.Lambda<Func<'T, bool>> (predicate, param) |])
140140

141-
let private StringStartsWithMethod = typeof<string>.GetMethod ("StartsWith", [| typeof<string> |])
142-
let private StringEndsWithMethod = typeof<string>.GetMethod ("EndsWith", [| typeof<string> |])
143-
let private StringContainsMethod = typeof<string>.GetMethod ("Contains", [| typeof<string> |])
141+
let private stringType = typeof<string>
142+
let private StringStartsWithMethod = stringType.GetMethod ("StartsWith", [| stringType |])
143+
let private StringEndsWithMethod = stringType.GetMethod ("EndsWith", [| stringType |])
144+
let private StringContainsMethod = stringType.GetMethod ("Contains", [| stringType |])
144145
let private getEnumerableContainsMethod (memberType : Type) =
145146
match
146147
typeof<Enumerable>
@@ -183,12 +184,22 @@ module ObjectListFilter =
183184
| LessThan f -> Expression.LessThan (Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value))
184185
| GreaterThanOrEqual f -> Expression.GreaterThanOrEqual (Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value))
185186
| LessThanOrEqual f -> Expression.LessThanOrEqual (Expression.PropertyOrField (param, f.FieldName), Expression.Constant (f.Value))
186-
| StartsWith f -> Expression.Call (Expression.PropertyOrField (param, f.FieldName), StringStartsWithMethod, Expression.Constant (f.Value))
187-
| EndsWith f -> Expression.Call (Expression.PropertyOrField (param, f.FieldName), StringEndsWithMethod, Expression.Constant (f.Value))
187+
| StartsWith f ->
188+
let ``member`` = Expression.PropertyOrField (param, f.FieldName)
189+
if ``member``.Type = stringType then
190+
Expression.Call (``member``, StringStartsWithMethod, Expression.Constant (f.Value))
191+
else
192+
Expression.Call (Expression.Convert (``member``, stringType), StringStartsWithMethod, Expression.Constant (f.Value))
193+
| EndsWith f ->
194+
let ``member`` = Expression.PropertyOrField (param, f.FieldName)
195+
if ``member``.Type = stringType then
196+
Expression.Call (``member``, StringEndsWithMethod, Expression.Constant (f.Value))
197+
else
198+
Expression.Call (Expression.Convert (``member``, stringType), StringEndsWithMethod, Expression.Constant (f.Value))
188199
| Contains f ->
189200
let ``member`` = Expression.PropertyOrField (param, f.FieldName)
190201
let isEnumerable (memberType : Type) =
191-
not (Type.(=) (memberType, typeof<string>))
202+
not (Type.(=) (memberType, stringType))
192203
&& typeof<System.Collections.IEnumerable>.IsAssignableFrom (memberType)
193204
&& memberType
194205
.GetInterfaces()
@@ -214,7 +225,11 @@ module ObjectListFilter =
214225
Expression.PropertyOrField (param, f.FieldName),
215226
Expression.Constant (f.Value)
216227
)
217-
| _ -> Expression.Call (``member``, StringContainsMethod, Expression.Constant (f.Value))
228+
| _ ->
229+
if ``member``.Type = stringType then
230+
Expression.Call (``member``, StringContainsMethod, Expression.Constant (f.Value))
231+
else
232+
Expression.Call (Expression.Convert (``member``, stringType), StringContainsMethod, Expression.Constant (f.Value))
218233
| In f ->
219234
let ``member`` = Expression.PropertyOrField (param, f.FieldName)
220235
f.Value

tests/FSharp.Data.GraphQL.Tests/FSharp.Data.GraphQL.Tests.fsproj

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
</PropertyGroup>
1616

1717
<ItemGroup>
18+
<PackageReference Include="Microsoft.Azure.Cosmos" />
1819
<PackageReference Include="Microsoft.NET.Test.Sdk" />
1920
<PackageReference Include="BenchmarkDotNet" />
2021
<PackageReference Include="Validus" />
@@ -65,6 +66,7 @@
6566
<Compile Include="Variables and Inputs\InputListTests.fs" />
6667
<Compile Include="SelectLinqTests.fs" />
6768
<Compile Include="ObjectListFilterLinqTests.fs" />
69+
<Compile Include="ObjectListFilterLinqGenerateTests.fs" />
6870
<Compile Include="DeferredTests.fs" />
6971
<Compile Include="SubscriptionTests.fs" />
7072
<Compile Include="MiddlewareTests.fs" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
module FSharp.Data.GraphQL.Tests.ObjectListFilterLinqGenerateTests
2+
3+
open Xunit
4+
open System
5+
open System.Numerics
6+
open Microsoft.Azure.Cosmos.Linq
7+
open Microsoft.Azure.Cosmos
8+
open FSharp.Data.GraphQL.Shared
9+
open FSharp.Data.GraphQL.Server.Middleware
10+
11+
[<Struct>]
12+
type ValidStringStruct =
13+
internal
14+
| ValidStringStruct of string
15+
16+
static member op_Explicit (ValidStringStruct str) = str
17+
static member op_Equality (ValidStringStruct left, ValidStringStruct right) = left = right
18+
static member op_Inequality (ValidStringStruct left, ValidStringStruct right) = left <> right
19+
20+
static member internal op_Equality (ValidStringStruct left, right) = left = right
21+
static member internal op_Inequality (ValidStringStruct left, right) = left <> right
22+
static member internal op_GreaterThan (ValidStringStruct left, right) = left > right
23+
static member internal op_GreaterThanOrEqual (ValidStringStruct left, right) = left >= right
24+
static member internal op_LessThan (ValidStringStruct left, right) = left < right
25+
static member internal op_LessThanOrEqual (ValidStringStruct left, right) = left <= right
26+
member str.StartsWith (value : string) = let (ValidStringStruct str) = str in str.StartsWith(value)
27+
member str.EndsWith (value : string) = let (ValidStringStruct str) = str in str.EndsWith(value)
28+
member str.Contains (value : string) = let (ValidStringStruct str) = str in str.Contains(value)
29+
// Just for demo purposes
30+
interface IEqualityOperators<ValidStringStruct, ValidStringStruct, bool> with
31+
static member op_Equality (ValidStringStruct left, ValidStringStruct right) = left = right
32+
static member op_Inequality (ValidStringStruct left, ValidStringStruct right) = left <> right
33+
interface IComparisonOperators<ValidStringStruct, ValidStringStruct, bool> with
34+
static member op_GreaterThan (ValidStringStruct left, ValidStringStruct right) = left > right
35+
static member op_GreaterThanOrEqual (ValidStringStruct left, ValidStringStruct right) = left >= right
36+
static member op_LessThan (ValidStringStruct left, ValidStringStruct right) = left < right
37+
static member op_LessThanOrEqual (ValidStringStruct left, ValidStringStruct right) = left <= right
38+
39+
type ValidStringObject =
40+
internal
41+
| ValidStringObject of string
42+
43+
static member op_Explicit (ValidStringObject str) = str
44+
static member op_Equality (ValidStringObject left, ValidStringObject right) = left = right
45+
static member op_Inequality (ValidStringObject left, ValidStringObject right) = left <> right
46+
47+
static member internal op_Equality (ValidStringObject left, right) = left = right
48+
static member internal op_Inequality (ValidStringObject left, right) = left <> right
49+
static member internal op_GreaterThan (ValidStringObject left, right) = left > right
50+
static member internal op_GreaterThanOrEqual (ValidStringObject left, right) = left >= right
51+
static member internal op_LessThan (ValidStringObject left, right) = left < right
52+
static member internal op_LessThanOrEqual (ValidStringObject left, right) = left <= right
53+
54+
// Just for demo purposes
55+
interface IEqualityOperators<ValidStringObject, ValidStringObject, bool> with
56+
static member op_Equality (ValidStringObject left, ValidStringObject right) = left = right
57+
static member op_Inequality (ValidStringObject left, ValidStringObject right) = left <> right
58+
interface IComparisonOperators<ValidStringObject, ValidStringObject, bool> with
59+
static member op_GreaterThan (ValidStringObject left, ValidStringObject right) = left > right
60+
static member op_GreaterThanOrEqual (ValidStringObject left, ValidStringObject right) = left >= right
61+
static member op_LessThan (ValidStringObject left, ValidStringObject right) = left < right
62+
static member op_LessThanOrEqual (ValidStringObject left, ValidStringObject right) = left <= right
63+
64+
[<Struct>]
65+
type ValidIntStruct =
66+
internal
67+
| ValidIntStruct of Int64
68+
static member internal op_Equality (ValidIntStruct left, ValidIntStruct right) = left = right
69+
static member internal op_Inequality (ValidIntStruct left, ValidIntStruct right) = left <> right
70+
static member internal op_GreaterThan (ValidIntStruct left, right : Int64) = left > right
71+
72+
type ValidIntObject =
73+
internal
74+
| ValidIntObject of Int64
75+
static member internal op_Equality (ValidIntObject left, ValidIntObject right) = left = right
76+
static member internal op_Inequality (ValidIntObject left, ValidIntObject right) = left <> right
77+
static member internal op_GreaterThan (ValidIntObject left, right : Int64) = left > right
78+
79+
type FakeEntity = {
80+
ValidStringStruct : ValidStringStruct
81+
ValidStringObject : ValidStringObject
82+
string : string
83+
ValidIntStruct : ValidIntStruct
84+
ValidIntObject : ValidIntObject
85+
int : Int64
86+
}
87+
88+
let jsonOptions = Json.getSerializerOptions Seq.empty
89+
let cosmosClient =
90+
let options = CosmosClientOptions(UseSystemTextJsonSerializerWithOptions = jsonOptions)
91+
new CosmosClient ("https://localhost:8081/", "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==", options)
92+
let container = cosmosClient.GetContainer("database", "container")
93+
let filterOptions =
94+
ObjectListFilterLinqOptions<FakeEntity, obj>.None
95+
96+
[<Fact>]
97+
let ``ObjectListFilter works with Equals operator for ValidStringStruct`` () =
98+
let queryable = container.GetItemLinqQueryable<FakeEntity> ()
99+
let filter = Equals { FieldName = "validStringStruct"; Value = "Jonathan"}
100+
let filterQuery = queryable.Apply (filter, filterOptions)
101+
let queryDefinition = CosmosLinqExtensions.ToQueryDefinition filterQuery
102+
equals queryDefinition.QueryText """SELECT VALUE root FROM root WHERE (root["validStringStruct"] = "Jonathan")"""
103+
104+
[<Fact>]
105+
let ``ObjectListFilter works with StartsWith operator for ValidStringStruct`` () =
106+
let queryable = container.GetItemLinqQueryable<FakeEntity> ()
107+
let filter = StartsWith { FieldName = "validStringStruct"; Value = "J" }
108+
let filterQuery = queryable.Apply (filter, filterOptions)
109+
let queryDefinition = CosmosLinqExtensions.ToQueryDefinition filterQuery
110+
equals queryDefinition.QueryText, """SELECT VALUE root FROM root WHERE STARTSWITH(root["validStringStruct"], "J")"""
111+
112+
[<Fact>]
113+
let ``ObjectListFilter works with EndsWith operator for ValidStringStruct`` () =
114+
let queryable = container.GetItemLinqQueryable<FakeEntity> ()
115+
let filter = EndsWith { FieldName = "validStringStruct"; Value = "n" }
116+
let filterQuery = queryable.Apply (filter, filterOptions)
117+
let queryDefinition = CosmosLinqExtensions.ToQueryDefinition filterQuery
118+
equals queryDefinition.QueryText, """SELECT VALUE root FROM root WHERE ENDSWITH(root["validStringStruct"], "n")"""
119+
120+
[<Fact>]
121+
let ``ObjectListFilter works with Contains operator for ValidStringStruct`` () =
122+
let queryable = container.GetItemLinqQueryable<FakeEntity> ()
123+
let filter = Contains { FieldName = "validStringStruct"; Value = "athan" }
124+
let filterQuery = queryable.Apply (filter, filterOptions)
125+
let queryDefinition = CosmosLinqExtensions.ToQueryDefinition filterQuery
126+
equals queryDefinition.QueryText, """SELECT VALUE root FROM root WHERE CONTAINS(root["validStringStruct"], "athan")"""
127+
128+
[<Fact>]
129+
let ``ObjectListFilter works with Equals operator for ValidStringObject`` () =
130+
let filter = Equals { FieldName = "validStringObject"; Value = ValidStringObject "Jonathan" }
131+
let queryable = container.GetItemLinqQueryable<FakeEntity> ()
132+
let filterQuery = queryable.Apply (filter)
133+
let queryDefinition = CosmosLinqExtensions.ToQueryDefinition filterQuery
134+
equals queryDefinition.QueryText, """SELECT VALUE root FROM root WHERE (root["validStringObject"] = "Jonathan")"""
135+
136+
[<Fact>]
137+
let ``ObjectListFilter works with GreaterThan operator for ValidIntStruct`` () =
138+
let queryable = container.GetItemLinqQueryable<FakeEntity> ()
139+
let filter = GreaterThan { FieldName = "validIntStruct"; Value = 6L }
140+
let filterQuery = queryable.Apply (filter, filterOptions)
141+
let queryDefinition = CosmosLinqExtensions.ToQueryDefinition filterQuery
142+
equals queryDefinition.QueryText, """SELECT VALUE root FROM root WHERE (root["validIntStruct"] > 6)"""
143+
144+
[<Fact>]
145+
let ``ObjectListFilter works with GreaterThan operator for ValidIntObject`` () =
146+
let filter = GreaterThan { FieldName = "validIntObject"; Value = 6L }
147+
let queryable = container.GetItemLinqQueryable<FakeEntity> ()
148+
let filterQuery = queryable.Apply (filter)
149+
let queryDefinition = CosmosLinqExtensions.ToQueryDefinition filterQuery
150+
equals queryDefinition.QueryText, """SELECT VALUE root FROM root WHERE (root["validIntObject"] > 6)"""
151+

0 commit comments

Comments
 (0)