Skip to content

Commit ffd32bc

Browse files
authored
Merge pull request #1764 from fsprojects/repo-assist/test-xmlruntime-2026-04-27-clean-97d54c741a188241
[Repo Assist] test: add 39 unit tests for XmlRuntime methods
2 parents 26fffc6 + a86857c commit ffd32bc

1 file changed

Lines changed: 295 additions & 1 deletion

File tree

tests/FSharp.Data.Core.Tests/XmlRuntime.fs

Lines changed: 295 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ open System.IO
77
open System.Xml.Linq
88
open System.Reflection
99
open FSharp.Data.Runtime.BaseTypes
10+
open FSharp.Data.Runtime
11+
open FSharp.Data
1012

1113
// These tests use reflection to test the XmlElement methods that are marked as "generated code only"
1214
// This approach allows us to test the functionality while respecting the compiler constraints
@@ -64,4 +66,296 @@ let ``XmlElement _Print property handles short strings`` () =
6466
let printProperty = typeof<XmlElement>.GetProperty("_Print")
6567
let printed = printProperty.GetValue(xmlElement) :?> string
6668
printed |> should not' (endWith "...")
67-
printed |> should contain "short"
69+
printed |> should contain "short"
70+
71+
// Helper: wrap an XElement in XmlElement
72+
let private mkXml (xelem: XElement) : XmlElement = { XElement = xelem }
73+
74+
// ─── XmlRuntime.TryGetValue ────────────────────────────────────────────────
75+
76+
[<Test>]
77+
let ``TryGetValue returns None for element with no text content`` () =
78+
let xml = mkXml (XElement(XName.Get("empty")))
79+
XmlRuntime.TryGetValue(xml) |> should equal None
80+
81+
[<Test>]
82+
let ``TryGetValue returns Some for element with text content`` () =
83+
let xml = mkXml (XElement(XName.Get("item"), "hello"))
84+
XmlRuntime.TryGetValue(xml) |> should equal (Some "hello")
85+
86+
[<Test>]
87+
let ``TryGetValue returns None for element whose value is empty string`` () =
88+
let xelem = XElement(XName.Get("item"))
89+
xelem.Value <- ""
90+
let xml = mkXml xelem
91+
XmlRuntime.TryGetValue(xml) |> should equal None
92+
93+
// ─── XmlRuntime.TryGetAttribute ───────────────────────────────────────────
94+
95+
[<Test>]
96+
let ``TryGetAttribute returns None when attribute is absent`` () =
97+
let xml = mkXml (XElement(XName.Get("item")))
98+
XmlRuntime.TryGetAttribute(xml, "id") |> should equal None
99+
100+
[<Test>]
101+
let ``TryGetAttribute returns Some with attribute value when present`` () =
102+
let xelem = XElement(XName.Get("item"), XAttribute(XName.Get("id"), "42"))
103+
let xml = mkXml xelem
104+
XmlRuntime.TryGetAttribute(xml, "id") |> should equal (Some "42")
105+
106+
[<Test>]
107+
let ``TryGetAttribute handles namespace-qualified attribute name`` () =
108+
let ns = XNamespace.Get("http://example.com")
109+
let xelem = XElement(XName.Get("item"), XAttribute(ns + "type", "foo"))
110+
let xml = mkXml xelem
111+
XmlRuntime.TryGetAttribute(xml, "{http://example.com}type") |> should equal (Some "foo")
112+
113+
// ─── XmlRuntime.GetChild ──────────────────────────────────────────────────
114+
115+
[<Test>]
116+
let ``GetChild returns the single matching child element`` () =
117+
let parent = XElement(XName.Get("root"), XElement(XName.Get("child"), "val"))
118+
let xml = mkXml parent
119+
let child = XmlRuntime.GetChild(xml, "child")
120+
child.XElement.Value |> should equal "val"
121+
122+
[<Test>]
123+
let ``GetChild throws when no matching child exists`` () =
124+
let xml = mkXml (XElement(XName.Get("root")))
125+
(fun () -> XmlRuntime.GetChild(xml, "missing") |> ignore)
126+
|> should throw typeof<Exception>
127+
128+
[<Test>]
129+
let ``GetChild throws when multiple matching children exist`` () =
130+
let parent =
131+
XElement(
132+
XName.Get("root"),
133+
XElement(XName.Get("child"), "1"),
134+
XElement(XName.Get("child"), "2")
135+
)
136+
let xml = mkXml parent
137+
(fun () -> XmlRuntime.GetChild(xml, "child") |> ignore)
138+
|> should throw typeof<Exception>
139+
140+
// ─── XmlRuntime.ConvertArray ──────────────────────────────────────────────
141+
142+
[<Test>]
143+
let ``ConvertArray returns empty array when no matching children`` () =
144+
let xml = mkXml (XElement(XName.Get("root")))
145+
let result = XmlRuntime.ConvertArray(xml, "item", System.Func<XmlElement, string>(fun e -> e.XElement.Value))
146+
result |> should equal [||]
147+
148+
[<Test>]
149+
let ``ConvertArray returns converted values for all matching children`` () =
150+
let parent =
151+
XElement(
152+
XName.Get("root"),
153+
XElement(XName.Get("item"), "1"),
154+
XElement(XName.Get("item"), "2"),
155+
XElement(XName.Get("item"), "3")
156+
)
157+
let xml = mkXml parent
158+
let result = XmlRuntime.ConvertArray(xml, "item", System.Func<XmlElement, int>(fun e -> int e.XElement.Value))
159+
result |> should equal [| 1; 2; 3 |]
160+
161+
[<Test>]
162+
let ``ConvertArray ignores children with different names`` () =
163+
let parent =
164+
XElement(
165+
XName.Get("root"),
166+
XElement(XName.Get("item"), "a"),
167+
XElement(XName.Get("other"), "b")
168+
)
169+
let xml = mkXml parent
170+
let result = XmlRuntime.ConvertArray(xml, "item", System.Func<XmlElement, string>(fun e -> e.XElement.Value))
171+
result |> should equal [| "a" |]
172+
173+
// ─── XmlRuntime.ConvertOptional ───────────────────────────────────────────
174+
175+
[<Test>]
176+
let ``ConvertOptional returns None when no matching child exists`` () =
177+
let xml = mkXml (XElement(XName.Get("root")))
178+
let result = XmlRuntime.ConvertOptional(xml, "opt", System.Func<XmlElement, string>(fun e -> e.XElement.Value))
179+
result |> should equal None
180+
181+
[<Test>]
182+
let ``ConvertOptional returns Some when exactly one matching child exists`` () =
183+
let parent = XElement(XName.Get("root"), XElement(XName.Get("opt"), "42"))
184+
let xml = mkXml parent
185+
let result = XmlRuntime.ConvertOptional(xml, "opt", System.Func<XmlElement, int>(fun e -> int e.XElement.Value))
186+
result |> should equal (Some 42)
187+
188+
[<Test>]
189+
let ``ConvertOptional throws when more than one matching child exists`` () =
190+
let parent =
191+
XElement(
192+
XName.Get("root"),
193+
XElement(XName.Get("opt"), "1"),
194+
XElement(XName.Get("opt"), "2")
195+
)
196+
let xml = mkXml parent
197+
(fun () -> XmlRuntime.ConvertOptional(xml, "opt", System.Func<XmlElement, string>(fun e -> e.XElement.Value)) |> ignore)
198+
|> should throw typeof<Exception>
199+
200+
// ─── XmlRuntime.ConvertOptional2 ──────────────────────────────────────────
201+
202+
[<Test>]
203+
let ``ConvertOptional2 returns None when no matching child`` () =
204+
let xml = mkXml (XElement(XName.Get("root")))
205+
let result =
206+
XmlRuntime.ConvertOptional2(xml, "opt", System.Func<XmlElement, string option>(fun e -> Some e.XElement.Value))
207+
result |> should equal None
208+
209+
[<Test>]
210+
let ``ConvertOptional2 returns None when child exists but inner function returns None`` () =
211+
let parent = XElement(XName.Get("root"), XElement(XName.Get("opt")))
212+
let xml = mkXml parent
213+
let result =
214+
XmlRuntime.ConvertOptional2(xml, "opt", System.Func<XmlElement, string option>(fun _ -> None))
215+
result |> should equal None
216+
217+
[<Test>]
218+
let ``ConvertOptional2 returns Some when child exists and inner function returns Some`` () =
219+
let parent = XElement(XName.Get("root"), XElement(XName.Get("opt"), "hello"))
220+
let xml = mkXml parent
221+
let result =
222+
XmlRuntime.ConvertOptional2(xml, "opt", System.Func<XmlElement, string option>(fun e -> Some e.XElement.Value))
223+
result |> should equal (Some "hello")
224+
225+
// ─── XmlRuntime.ConvertAsName ─────────────────────────────────────────────
226+
227+
[<Test>]
228+
let ``ConvertAsName returns None when element name does not match`` () =
229+
let xml = mkXml (XElement(XName.Get("foo")))
230+
let result =
231+
XmlRuntime.ConvertAsName(xml, "bar", System.Func<XmlElement, string>(fun e -> e.XElement.Name.LocalName))
232+
result |> should equal None
233+
234+
[<Test>]
235+
let ``ConvertAsName returns Some when element name matches`` () =
236+
let xml = mkXml (XElement(XName.Get("foo"), "data"))
237+
let result =
238+
XmlRuntime.ConvertAsName(xml, "foo", System.Func<XmlElement, string>(fun e -> e.XElement.Value))
239+
result |> should equal (Some "data")
240+
241+
// ─── XmlRuntime.GetJsonValue / TryGetJsonValue ────────────────────────────
242+
243+
[<Test>]
244+
let ``GetJsonValue parses embedded JSON string as JsonDocument`` () =
245+
let xml = mkXml (XElement(XName.Get("item"), """{"x":1}"""))
246+
let doc = XmlRuntime.GetJsonValue(xml)
247+
doc.JsonValue.["x"].AsInteger() |> should equal 1
248+
249+
[<Test>]
250+
let ``GetJsonValue throws when element has no text content`` () =
251+
let xml = mkXml (XElement(XName.Get("item")))
252+
(fun () -> XmlRuntime.GetJsonValue(xml) |> ignore)
253+
|> should throw typeof<Exception>
254+
255+
[<Test>]
256+
let ``TryGetJsonValue returns None when element has no text content`` () =
257+
let xml = mkXml (XElement(XName.Get("item")))
258+
XmlRuntime.TryGetJsonValue(xml) |> should equal None
259+
260+
[<Test>]
261+
let ``TryGetJsonValue returns None when element text is not valid JSON`` () =
262+
let xml = mkXml (XElement(XName.Get("item"), "not-json!"))
263+
XmlRuntime.TryGetJsonValue(xml) |> should equal None
264+
265+
[<Test>]
266+
let ``TryGetJsonValue returns Some for valid JSON content`` () =
267+
let xml = mkXml (XElement(XName.Get("item"), "[1,2,3]"))
268+
let result = XmlRuntime.TryGetJsonValue(xml)
269+
result |> should not' (equal None)
270+
result.Value.JsonValue.AsArray().Length |> should equal 3
271+
272+
// ─── XmlRuntime.CreateValue ───────────────────────────────────────────────
273+
274+
[<Test>]
275+
let ``CreateValue creates element with string value`` () =
276+
let xml = XmlRuntime.CreateValue("greeting", box "hello", "")
277+
xml.XElement.Name.LocalName |> should equal "greeting"
278+
xml.XElement.Value |> should equal "hello"
279+
280+
[<Test>]
281+
let ``CreateValue creates element with integer value`` () =
282+
let xml = XmlRuntime.CreateValue("count", box 42, "")
283+
xml.XElement.Name.LocalName |> should equal "count"
284+
xml.XElement.Value |> should equal "42"
285+
286+
[<Test>]
287+
let ``CreateValue creates element with boolean true`` () =
288+
let xml = XmlRuntime.CreateValue("flag", box true, "")
289+
xml.XElement.Value |> should equal "true"
290+
291+
[<Test>]
292+
let ``CreateValue creates element with boolean false`` () =
293+
let xml = XmlRuntime.CreateValue("flag", box false, "")
294+
xml.XElement.Value |> should equal "false"
295+
296+
// ─── XmlRuntime.CreateRecord ──────────────────────────────────────────────
297+
298+
[<Test>]
299+
let ``CreateRecord creates element with no attributes or children`` () =
300+
let xml = XmlRuntime.CreateRecord("record", [||], [||], "")
301+
xml.XElement.Name.LocalName |> should equal "record"
302+
xml.XElement.HasAttributes |> should equal false
303+
xml.XElement.HasElements |> should equal false
304+
305+
[<Test>]
306+
let ``CreateRecord creates element with attributes`` () =
307+
let xml = XmlRuntime.CreateRecord("record", [| "id", box "99" |], [||], "")
308+
xml.XElement.Attribute(XName.Get("id")).Value |> should equal "99"
309+
310+
[<Test>]
311+
let ``CreateRecord creates element with text content via empty-string name`` () =
312+
let xml = XmlRuntime.CreateRecord("record", [||], [| "", box "body text" |], "")
313+
xml.XElement.Value |> should equal "body text"
314+
315+
[<Test>]
316+
let ``CreateRecord creates element with named child element`` () =
317+
let xml = XmlRuntime.CreateRecord("root", [||], [| "child", box "42" |], "")
318+
let child = xml.XElement.Element(XName.Get("child"))
319+
child |> should not' (equal null)
320+
child.Value |> should equal "42"
321+
322+
[<Test>]
323+
let ``CreateRecord serialises DateTime value as ISO date string`` () =
324+
let dt = DateTime(2024, 3, 15)
325+
let xml = XmlRuntime.CreateValue("date", box dt, "")
326+
xml.XElement.Value |> should equal "2024-03-15"
327+
328+
[<Test>]
329+
let ``CreateRecord serialises DateTime with time as ISO-8601 string`` () =
330+
let dt = DateTime(2024, 3, 15, 10, 30, 0)
331+
let xml = XmlRuntime.CreateValue("ts", box dt, "")
332+
xml.XElement.Value |> should startWith "2024-03-15T"
333+
334+
[<Test>]
335+
let ``CreateRecord handles optional string element Some value`` () =
336+
let xml = XmlRuntime.CreateRecord("root", [||], [| "opt", box (Some "present") |], "")
337+
xml.XElement.Element(XName.Get("opt")).Value |> should equal "present"
338+
339+
[<Test>]
340+
let ``CreateRecord handles optional string element None omits child`` () =
341+
let xml = XmlRuntime.CreateRecord("root", [||], [| "opt", box (None: string option) |], "")
342+
xml.XElement.Element(XName.Get("opt")) |> should equal null
343+
344+
// ─── XmlRuntime — path navigation via pipe-separated names ────────────────
345+
346+
[<Test>]
347+
let ``ConvertArray navigates path with pipe-separated names`` () =
348+
// <root><parent><child>A</child><child>B</child></parent></root>
349+
let grandchild1 = XElement(XName.Get("child"), "A")
350+
let grandchild2 = XElement(XName.Get("child"), "B")
351+
let parentElem = XElement(XName.Get("parent"), grandchild1, grandchild2)
352+
let root = XElement(XName.Get("root"), parentElem)
353+
let xml = mkXml root
354+
let result = XmlRuntime.ConvertArray(xml, "parent|child", System.Func<XmlElement, string>(fun e -> e.XElement.Value))
355+
result |> should equal [| "A"; "B" |]
356+
357+
[<Test>]
358+
let ``ConvertArray returns empty array when intermediate path element is missing`` () =
359+
let xml = mkXml (XElement(XName.Get("root")))
360+
let result = XmlRuntime.ConvertArray(xml, "missing|child", System.Func<XmlElement, string>(fun e -> e.XElement.Value))
361+
result |> should equal [||]

0 commit comments

Comments
 (0)