Skip to content

Commit 9808e55

Browse files
committed
fixup! ObjectListFilter filter values to target type coercion (#589)
1 parent cf54384 commit 9808e55

2 files changed

Lines changed: 112 additions & 8 deletions

File tree

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,16 +212,16 @@ module ObjectListFilter =
212212
let mutable compareOptions = Globalization.CompareOptions.None
213213

214214
if StringComparer.IsWellKnownCultureAwareComparer (sc, &compareInfo, &compareOptions) then
215-
let isCurrentCulture = compareInfo.Equals Globalization.CultureInfo.CurrentCulture.CompareInfo
216215
let isInvariantCulture = compareInfo.Equals Globalization.CultureInfo.InvariantCulture.CompareInfo
216+
let isCurrentCulture = compareInfo.Equals Globalization.CultureInfo.CurrentCulture.CompareInfo
217217

218218
match compareOptions with
219-
| Globalization.CompareOptions.None when isCurrentCulture -> ValueSome StringComparison.CurrentCulture
220-
| Globalization.CompareOptions.IgnoreCase when isCurrentCulture ->
221-
ValueSome StringComparison.CurrentCultureIgnoreCase
222219
| Globalization.CompareOptions.None when isInvariantCulture -> ValueSome StringComparison.InvariantCulture
223220
| Globalization.CompareOptions.IgnoreCase when isInvariantCulture ->
224221
ValueSome StringComparison.InvariantCultureIgnoreCase
222+
| Globalization.CompareOptions.None when isCurrentCulture -> ValueSome StringComparison.CurrentCulture
223+
| Globalization.CompareOptions.IgnoreCase when isCurrentCulture ->
224+
ValueSome StringComparison.CurrentCultureIgnoreCase
225225
| _ -> ValueNone
226226
else
227227
ValueNone

tests/FSharp.Data.GraphQL.Tests/ObjectListFilter/ObjectListFilterComparerMappingTests.fs

Lines changed: 108 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
[<Xunit.Trait (Tests.TraitType.Category, Tests.TraitName.Linq)>]
1+
[<Xunit.Trait (Tests.TraitType.Category, Tests.TraitName.Linq)>]
22
[<Xunit.Trait (Tests.TraitType.Category, Tests.TraitName.ObjectListFilter)>]
33
module FSharp.Data.GraphQL.Tests.ObjectListFilter.ComparerMapping.Tests
44

55
open System
66
open System.Collections
7+
open System.Globalization
78
open Xunit
89
open FSharp.Data.GraphQL.Server.Middleware
910

11+
// ─────────────────────────────────────────────────────────────────────────────
12+
// Singleton reference-equality branch
13+
// ─────────────────────────────────────────────────────────────────────────────
14+
1015
[<Fact>]
1116
let ``comparerToStringComparison maps well-known StringComparer instances`` () =
1217
let testCases =
@@ -23,12 +28,111 @@ let ``comparerToStringComparison maps well-known StringComparer instances`` () =
2328
let actual = ObjectListFilter.comparerToStringComparison comparer |> wantValueSome
2429
actual |> equals expected
2530

31+
// ─────────────────────────────────────────────────────────────────────────────
32+
// Each singleton must map to a distinct StringComparison value
33+
// ─────────────────────────────────────────────────────────────────────────────
34+
35+
[<Fact>]
36+
let ``comparerToStringComparison singleton mappings are all distinct`` () =
37+
let singletons : IComparer list =
38+
[
39+
StringComparer.OrdinalIgnoreCase
40+
StringComparer.InvariantCultureIgnoreCase
41+
StringComparer.CurrentCultureIgnoreCase
42+
StringComparer.Ordinal
43+
StringComparer.InvariantCulture
44+
StringComparer.CurrentCulture
45+
]
46+
47+
let results =
48+
singletons
49+
|> List.map (fun c -> ObjectListFilter.comparerToStringComparison c |> wantValueSome)
50+
51+
let distinct = results |> List.distinct
52+
List.length distinct |> equals (List.length results)
53+
54+
// ─────────────────────────────────────────────────────────────────────────────
55+
// IsWellKnownCultureAwareComparer fallback path
56+
// StringComparer.Create produces a non-singleton comparer that is still
57+
// well-known, so it falls through the ReferenceEquals branch and hits the
58+
// IsWellKnownCultureAwareComparer fallback.
59+
// ─────────────────────────────────────────────────────────────────────────────
60+
61+
[<Fact>]
62+
let ``comparerToStringComparison maps non-singleton InvariantCulture comparer`` () =
63+
let comparer = StringComparer.Create (CultureInfo.InvariantCulture, false) :> IComparer
64+
// must NOT be the same object as the singleton
65+
Assert.False (obj.ReferenceEquals (comparer, StringComparer.InvariantCulture :> obj))
66+
let result = ObjectListFilter.comparerToStringComparison comparer |> wantValueSome
67+
result |> equals StringComparison.InvariantCulture
68+
69+
[<Fact>]
70+
let ``comparerToStringComparison maps non-singleton InvariantCultureIgnoreCase comparer`` () =
71+
let comparer = StringComparer.Create (CultureInfo.InvariantCulture, true) :> IComparer
72+
Assert.False (obj.ReferenceEquals (comparer, StringComparer.InvariantCultureIgnoreCase :> obj))
73+
let result = ObjectListFilter.comparerToStringComparison comparer |> wantValueSome
74+
result |> equals StringComparison.InvariantCultureIgnoreCase
75+
76+
[<Fact>]
77+
let ``comparerToStringComparison maps non-singleton CurrentCulture comparer`` () =
78+
let comparer = StringComparer.Create (CultureInfo.CurrentCulture, false) :> IComparer
79+
Assert.False (obj.ReferenceEquals (comparer, StringComparer.CurrentCulture :> obj))
80+
let result = ObjectListFilter.comparerToStringComparison comparer |> wantValueSome
81+
result |> equals StringComparison.CurrentCulture
82+
2683
[<Fact>]
27-
let ``comparerToStringComparison returns ValueNone for unsupported comparers`` () =
84+
let ``comparerToStringComparison maps non-singleton CurrentCultureIgnoreCase comparer`` () =
85+
let comparer = StringComparer.Create (CultureInfo.CurrentCulture, true) :> IComparer
86+
Assert.False (obj.ReferenceEquals (comparer, StringComparer.CurrentCultureIgnoreCase :> obj))
87+
let result = ObjectListFilter.comparerToStringComparison comparer |> wantValueSome
88+
result |> equals StringComparison.CurrentCultureIgnoreCase
89+
90+
// ─────────────────────────────────────────────────────────────────────────────
91+
// Unknown / unsupported cases → ValueNone
92+
// ─────────────────────────────────────────────────────────────────────────────
93+
94+
[<Fact>]
95+
let ``comparerToStringComparison returns ValueNone for null`` () =
96+
ObjectListFilter.comparerToStringComparison null |> wantValueNone
97+
98+
[<Fact>]
99+
let ``comparerToStringComparison returns ValueNone for non-StringComparer IComparer`` () =
28100
let customComparer =
29101
{ new IComparer with
30102
member _.Compare (_, _) = 0
31103
}
32-
33-
ObjectListFilter.comparerToStringComparison null |> wantValueNone
34104
ObjectListFilter.comparerToStringComparison customComparer |> wantValueNone
105+
106+
[<Fact>]
107+
let ``comparerToStringComparison returns ValueNone for non-standard culture comparer`` () =
108+
// A comparer for a specific non-current, non-invariant culture — the
109+
// IsWellKnownCultureAwareComparer fallback cannot map it to any of the six
110+
// StringComparison values, so it must return ValueNone.
111+
let trCulture = CultureInfo.GetCultureInfo "tr-TR"
112+
// Only run this test when the test host is not Turkish (otherwise CurrentCulture == tr-TR
113+
// and the result would legitimately be CurrentCulture).
114+
if not (CultureInfo.CurrentCulture.Name.StartsWith "tr") then
115+
let comparer = StringComparer.Create (trCulture, false) :> IComparer
116+
ObjectListFilter.comparerToStringComparison comparer |> wantValueNone
117+
118+
// ─────────────────────────────────────────────────────────────────────────────
119+
// Determinism: calling comparerToStringComparison twice on the same instance
120+
// must return the same result
121+
// ─────────────────────────────────────────────────────────────────────────────
122+
123+
[<Fact>]
124+
let ``comparerToStringComparison is deterministic for singletons`` () =
125+
let singletons : IComparer list =
126+
[
127+
StringComparer.OrdinalIgnoreCase
128+
StringComparer.InvariantCultureIgnoreCase
129+
StringComparer.CurrentCultureIgnoreCase
130+
StringComparer.Ordinal
131+
StringComparer.InvariantCulture
132+
StringComparer.CurrentCulture
133+
]
134+
135+
for comparer in singletons do
136+
let first = ObjectListFilter.comparerToStringComparison comparer
137+
let second = ObjectListFilter.comparerToStringComparison comparer
138+
first |> equals second

0 commit comments

Comments
 (0)