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) >]
33module FSharp.Data.GraphQL.Tests.ObjectListFilter.ComparerMapping.Tests
44
55open System
66open System.Collections
7+ open System.Globalization
78open Xunit
89open FSharp.Data .GraphQL .Server .Middleware
910
11+ // ─────────────────────────────────────────────────────────────────────────────
12+ // Singleton reference-equality branch
13+ // ─────────────────────────────────────────────────────────────────────────────
14+
1015[<Fact>]
1116let ``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