Skip to content

Commit 134f57d

Browse files
committed
Add comprehensive TextRuntime test coverage
- Adds 33 new test methods for FSharp.Data.Runtime.TextRuntime module - Tests cover all conversion methods (Convert* and Convert*Back) - Tests cover culture handling (GetCulture with caching) - Tests cover missing values parsing (GetMissingValues) - Tests cover nullable/option conversions - Tests cover async mapping functionality - Tests cover error handling for GetNonOptionalValue - All 2442 tests now passing (+33 new tests) 🤖 Generated with Claude Code
1 parent 70733c0 commit 134f57d

2 files changed

Lines changed: 248 additions & 0 deletions

File tree

tests/FSharp.Data.Core.Tests/FSharp.Data.Core.Tests.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
<Compile Include="NameUtils.fs" />
2323
<Compile Include="IOTests.fs" />
2424
<Compile Include="TextConversions.fs" />
25+
<Compile Include="TextRuntime.fs" />
2526
<Compile Include="JsonValue.fs" />
2627
<Compile Include="JsonParserProperties.fs" />
2728
<Compile Include="JsonConversions.fs" />
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
module FSharp.Data.Tests.TextRuntime
2+
3+
open NUnit.Framework
4+
open FsUnit
5+
open System
6+
open System.Globalization
7+
open FSharp.Data
8+
open FSharp.Data.Runtime
9+
10+
[<Test>]
11+
let ``GetCulture returns InvariantCulture for null or empty``() =
12+
TextRuntime.GetCulture(null) |> should equal CultureInfo.InvariantCulture
13+
TextRuntime.GetCulture("") |> should equal CultureInfo.InvariantCulture
14+
TextRuntime.GetCulture(" ") |> should equal CultureInfo.InvariantCulture
15+
16+
[<Test>]
17+
let ``GetCulture returns correct culture for valid string``() =
18+
let culture = TextRuntime.GetCulture("en-US")
19+
culture.Name |> should equal "en-US"
20+
21+
let culture2 = TextRuntime.GetCulture("de-DE")
22+
culture2.Name |> should equal "de-DE"
23+
24+
[<Test>]
25+
let ``GetCulture caches cultures correctly``() =
26+
let culture1 = TextRuntime.GetCulture("fr-FR")
27+
let culture2 = TextRuntime.GetCulture("fr-FR")
28+
// Should be same reference (cached)
29+
Object.ReferenceEquals(culture1, culture2) |> should equal true
30+
31+
[<Test>]
32+
let ``GetMissingValues returns default for null or empty``() =
33+
let defaultValues = TextRuntime.GetMissingValues(null)
34+
defaultValues |> should equal TextConversions.DefaultMissingValues
35+
36+
let emptyValues = TextRuntime.GetMissingValues("")
37+
emptyValues |> should equal TextConversions.DefaultMissingValues
38+
39+
let whitespaceValues = TextRuntime.GetMissingValues(" ")
40+
whitespaceValues |> should equal TextConversions.DefaultMissingValues
41+
42+
[<Test>]
43+
let ``GetMissingValues parses comma-separated values``() =
44+
let values = TextRuntime.GetMissingValues("NA,NULL,#N/A")
45+
values |> should equal [|"NA"; "NULL"; "#N/A"|]
46+
47+
let valuesWithSpaces = TextRuntime.GetMissingValues("N/A, null , missing")
48+
valuesWithSpaces |> should equal [|"N/A"; " null "; " missing"|]
49+
50+
[<Test>]
51+
let ``ConvertString returns the option as-is``() =
52+
TextRuntime.ConvertString(Some "test") |> should equal (Some "test")
53+
TextRuntime.ConvertString(None) |> should equal None
54+
55+
[<Test>]
56+
let ``ConvertInteger with culture``() =
57+
TextRuntime.ConvertInteger("en-US", Some "123") |> should equal (Some 123)
58+
TextRuntime.ConvertInteger("de-DE", Some "456") |> should equal (Some 456)
59+
TextRuntime.ConvertInteger("en-US", Some "invalid") |> should equal None
60+
TextRuntime.ConvertInteger("en-US", None) |> should equal None
61+
62+
[<Test>]
63+
let ``ConvertInteger64 with culture``() =
64+
TextRuntime.ConvertInteger64("en-US", Some "9223372036854775807") |> should equal (Some 9223372036854775807L)
65+
TextRuntime.ConvertInteger64("en-US", Some "invalid") |> should equal None
66+
TextRuntime.ConvertInteger64("en-US", None) |> should equal None
67+
68+
[<Test>]
69+
let ``ConvertDecimal with culture``() =
70+
TextRuntime.ConvertDecimal("en-US", Some "123.45") |> should equal (Some 123.45m)
71+
TextRuntime.ConvertDecimal("en-US", Some "67.89") |> should equal (Some 67.89m)
72+
TextRuntime.ConvertDecimal("en-US", Some "invalid") |> should equal None
73+
TextRuntime.ConvertDecimal("en-US", None) |> should equal None
74+
75+
[<Test>]
76+
let ``ConvertFloat with culture and missing values``() =
77+
TextRuntime.ConvertFloat("en-US", "NA,NULL", Some "123.45") |> should equal (Some 123.45)
78+
TextRuntime.ConvertFloat("en-US", "NA,NULL", Some "NA") |> should equal None
79+
TextRuntime.ConvertFloat("en-US", "NA,NULL", Some "NULL") |> should equal None
80+
TextRuntime.ConvertFloat("en-US", "NA,NULL", Some "invalid") |> should equal None
81+
TextRuntime.ConvertFloat("en-US", "NA,NULL", None) |> should equal None
82+
83+
[<Test>]
84+
let ``ConvertBoolean``() =
85+
TextRuntime.ConvertBoolean(Some "true") |> should equal (Some true)
86+
TextRuntime.ConvertBoolean(Some "false") |> should equal (Some false)
87+
TextRuntime.ConvertBoolean(Some "1") |> should equal (Some true)
88+
TextRuntime.ConvertBoolean(Some "0") |> should equal (Some false)
89+
TextRuntime.ConvertBoolean(Some "invalid") |> should equal None
90+
TextRuntime.ConvertBoolean(None) |> should equal None
91+
92+
[<Test>]
93+
let ``ConvertDateTime with culture``() =
94+
let result = TextRuntime.ConvertDateTime("en-US", Some "2023-12-25")
95+
result |> should not' (equal None)
96+
result.Value.Year |> should equal 2023
97+
result.Value.Month |> should equal 12
98+
result.Value.Day |> should equal 25
99+
100+
TextRuntime.ConvertDateTime("en-US", Some "invalid") |> should equal None
101+
TextRuntime.ConvertDateTime("en-US", None) |> should equal None
102+
103+
[<Test>]
104+
let ``ConvertDateTimeOffset with culture``() =
105+
let result = TextRuntime.ConvertDateTimeOffset("en-US", Some "2023-12-25T10:30:00+02:00")
106+
result |> should not' (equal None)
107+
result.Value.Year |> should equal 2023
108+
result.Value.Offset.Hours |> should equal 2
109+
110+
TextRuntime.ConvertDateTimeOffset("en-US", Some "invalid") |> should equal None
111+
TextRuntime.ConvertDateTimeOffset("en-US", None) |> should equal None
112+
113+
[<Test>]
114+
let ``ConvertTimeSpan with culture``() =
115+
let result = TextRuntime.ConvertTimeSpan("en-US", Some "02:30:45")
116+
result |> should not' (equal None)
117+
result.Value.Hours |> should equal 2
118+
result.Value.Minutes |> should equal 30
119+
result.Value.Seconds |> should equal 45
120+
121+
TextRuntime.ConvertTimeSpan("en-US", Some "invalid") |> should equal None
122+
TextRuntime.ConvertTimeSpan("en-US", None) |> should equal None
123+
124+
[<Test>]
125+
let ``ConvertGuid``() =
126+
let guid = Guid.NewGuid()
127+
let result = TextRuntime.ConvertGuid(Some (guid.ToString()))
128+
result |> should equal (Some guid)
129+
130+
TextRuntime.ConvertGuid(Some "invalid") |> should equal None
131+
TextRuntime.ConvertGuid(None) |> should equal None
132+
133+
[<Test>]
134+
let ``ConvertStringBack``() =
135+
TextRuntime.ConvertStringBack(Some "test") |> should equal "test"
136+
TextRuntime.ConvertStringBack(None) |> should equal ""
137+
138+
[<Test>]
139+
let ``ConvertIntegerBack with culture``() =
140+
TextRuntime.ConvertIntegerBack("en-US", Some 1234) |> should equal "1234"
141+
TextRuntime.ConvertIntegerBack("de-DE", Some 1234) |> should equal "1234"
142+
TextRuntime.ConvertIntegerBack("en-US", None) |> should equal ""
143+
144+
[<Test>]
145+
let ``ConvertInteger64Back with culture``() =
146+
TextRuntime.ConvertInteger64Back("en-US", Some 1234567890123456789L) |> should equal "1234567890123456789"
147+
TextRuntime.ConvertInteger64Back("en-US", None) |> should equal ""
148+
149+
[<Test>]
150+
let ``ConvertDecimalBack with culture``() =
151+
let result = TextRuntime.ConvertDecimalBack("en-US", Some 123.45m)
152+
result |> should equal "123.45"
153+
TextRuntime.ConvertDecimalBack("en-US", None) |> should equal ""
154+
155+
[<Test>]
156+
let ``ConvertFloatBack with NaN and missing values``() =
157+
TextRuntime.ConvertFloatBack("en-US", "NA,NULL", Some 123.45) |> should equal "123.45"
158+
TextRuntime.ConvertFloatBack("en-US", "NA,NULL", Some Double.NaN) |> should equal "NA"
159+
TextRuntime.ConvertFloatBack("en-US", "", Some Double.NaN) |> should equal "NaN"
160+
TextRuntime.ConvertFloatBack("en-US", "NA,NULL", None) |> should equal ""
161+
162+
[<Test>]
163+
let ``ConvertBooleanBack with different formats``() =
164+
TextRuntime.ConvertBooleanBack(Some true, false) |> should equal "true"
165+
TextRuntime.ConvertBooleanBack(Some false, false) |> should equal "false"
166+
TextRuntime.ConvertBooleanBack(Some true, true) |> should equal "1"
167+
TextRuntime.ConvertBooleanBack(Some false, true) |> should equal "0"
168+
TextRuntime.ConvertBooleanBack(None, false) |> should equal ""
169+
TextRuntime.ConvertBooleanBack(None, true) |> should equal ""
170+
171+
[<Test>]
172+
let ``ConvertDateTimeBack with ISO format``() =
173+
let dateTime = DateTime(2023, 12, 25, 15, 30, 45, DateTimeKind.Utc)
174+
let result = TextRuntime.ConvertDateTimeBack("en-US", Some dateTime)
175+
result |> should startWith "2023-12-25T15:30:45"
176+
TextRuntime.ConvertDateTimeBack("en-US", None) |> should equal ""
177+
178+
[<Test>]
179+
let ``ConvertDateTimeOffsetBack with ISO format``() =
180+
let dto = DateTimeOffset(2023, 12, 25, 15, 30, 45, TimeSpan.FromHours(2))
181+
let result = TextRuntime.ConvertDateTimeOffsetBack("en-US", Some dto)
182+
result |> should contain "2023-12-25T15:30:45"
183+
result |> should contain "+02:00"
184+
TextRuntime.ConvertDateTimeOffsetBack("en-US", None) |> should equal ""
185+
186+
[<Test>]
187+
let ``ConvertTimeSpanBack``() =
188+
let timeSpan = TimeSpan(2, 30, 45)
189+
let result = TextRuntime.ConvertTimeSpanBack("en-US", Some timeSpan)
190+
result |> should equal "2:30:45"
191+
TextRuntime.ConvertTimeSpanBack("en-US", None) |> should equal ""
192+
193+
[<Test>]
194+
let ``ConvertGuidBack``() =
195+
let guid = Guid.NewGuid()
196+
TextRuntime.ConvertGuidBack(Some guid) |> should equal (guid.ToString())
197+
TextRuntime.ConvertGuidBack(None) |> should equal ""
198+
199+
[<Test>]
200+
let ``GetNonOptionalValue with Some values``() =
201+
TextRuntime.GetNonOptionalValue("test", Some "value", None) |> should equal "value"
202+
TextRuntime.GetNonOptionalValue("test", Some 42, None) |> should equal 42
203+
204+
[<Test>]
205+
let ``GetNonOptionalValue with None for string returns empty``() =
206+
TextRuntime.GetNonOptionalValue<string>("test", None, None) |> should equal ""
207+
208+
[<Test>]
209+
let ``GetNonOptionalValue with None for float returns NaN``() =
210+
let result = TextRuntime.GetNonOptionalValue<float>("test", None, None)
211+
Double.IsNaN(result) |> should equal true
212+
213+
[<Test>]
214+
let ``GetNonOptionalValue throws for missing non-special types``() =
215+
(fun () -> TextRuntime.GetNonOptionalValue<int>("test", None, None) |> ignore)
216+
|> should throw typeof<Exception>
217+
218+
[<Test>]
219+
let ``GetNonOptionalValue with original value provides helpful error``() =
220+
(fun () -> TextRuntime.GetNonOptionalValue<int>("test", None, Some "invalid") |> ignore)
221+
|> should throw typeof<Exception>
222+
223+
[<Test>]
224+
let ``OptionToNullable conversion``() =
225+
let nullable = TextRuntime.OptionToNullable(Some 42)
226+
nullable.HasValue |> should equal true
227+
nullable.Value |> should equal 42
228+
229+
let nullableNone = TextRuntime.OptionToNullable<int>(None)
230+
nullableNone.HasValue |> should equal false
231+
232+
[<Test>]
233+
let ``NullableToOption conversion``() =
234+
let nullable = Nullable(42)
235+
TextRuntime.NullableToOption(nullable) |> should equal (Some 42)
236+
237+
let nullableEmpty = Nullable<int>()
238+
TextRuntime.NullableToOption(nullableEmpty) |> should equal None
239+
240+
[<Test>]
241+
let ``AsyncMap transformation``() =
242+
async {
243+
let valueAsync = async { return 5 }
244+
let mapping = Func<int, string>(fun x -> sprintf "Value: %d" x)
245+
let! result = TextRuntime.AsyncMap(valueAsync, mapping)
246+
result |> should equal "Value: 5"
247+
} |> Async.RunSynchronously

0 commit comments

Comments
 (0)