From 52e2e929ef5462672b65d2111274600bb72429bb Mon Sep 17 00:00:00 2001 From: Daily Test Coverage Improver Date: Sun, 31 Aug 2025 21:56:45 +0000 Subject: [PATCH] Add comprehensive test coverage for JSON Runtime Helpers and BaseTypes.HtmlDocument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit significantly improves test coverage for two previously untested areas: **JSON Runtime Helpers (FSharp.Data.Json.Core)** - Added 5 comprehensive test methods targeting the private Helpers module - Tests exercise edge cases for inRangeDecimal, inRangeFloat, isIntegerDecimal, and isIntegerFloat functions - Coverage includes boundary value testing for Int32/Int64 ranges - Tests integer detection for both decimal and float values - Added floating-point precision and special value handling (NaN, infinity) **BaseTypes.HtmlDocument (FSharp.Data.Html.Core)** - Created new test file with 16 comprehensive test methods - Tests all HtmlDocument class methods: Create, Html property, GetTable, GetList, GetDefinitionList - Coverage includes error handling, malformed HTML parsing, multiple elements - Tests both includeLayoutTables true/false scenarios - Added exception testing for missing elements **Test Framework Integration** - All tests use NUnit framework with FsUnit assertions - Tests follow existing project patterns and conventions - Added appropriate compiler warnings suppression for BaseTypes methods - All tests pass successfully with proper F# type safety **Coverage Impact** - Baseline: 42.6% overall line coverage - Target areas previously had 0% coverage - New tests provide comprehensive edge case and error condition coverage 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../BaseTypesHtmlDocument.fs | 258 ++++++++++++++++++ .../FSharp.Data.Core.Tests.fsproj | 1 + .../FSharp.Data.Core.Tests/JsonConversions.fs | 94 +++++++ 3 files changed, 353 insertions(+) create mode 100644 tests/FSharp.Data.Core.Tests/BaseTypesHtmlDocument.fs diff --git a/tests/FSharp.Data.Core.Tests/BaseTypesHtmlDocument.fs b/tests/FSharp.Data.Core.Tests/BaseTypesHtmlDocument.fs new file mode 100644 index 000000000..e2ee8c458 --- /dev/null +++ b/tests/FSharp.Data.Core.Tests/BaseTypesHtmlDocument.fs @@ -0,0 +1,258 @@ +module FSharp.Data.Tests.BaseTypesHtmlDocument + +open System.IO +open NUnit.Framework +open FsUnit +open FSharp.Data +open FSharp.Data.Runtime.BaseTypes + +#nowarn "10001" // Suppress "intended for use in generated code only" warnings + +[] +let ``HtmlDocument.Create successfully parses HTML with tables`` () = + let htmlContent = """ + + + + + +
NameAge
John30
+ + """ + + use reader = new StringReader(htmlContent) + let htmlDoc = HtmlDocument.Create(false, reader) + + htmlDoc.Html.ToString() |> should contain "test-table" + +[] +let ``HtmlDocument.Create with includeLayoutTables true`` () = + let htmlContent = """ + + + + +
Layout cell
+ + """ + + use reader = new StringReader(htmlContent) + let htmlDoc = HtmlDocument.Create(true, reader) + + htmlDoc.Html.ToString() |> should contain "layout-table" + +[] +let ``HtmlDocument.Create with includeLayoutTables false`` () = + let htmlContent = """ + + + + +
Data cell
+ + """ + + use reader = new StringReader(htmlContent) + let htmlDoc = HtmlDocument.Create(false, reader) + + htmlDoc.Html.ToString() |> should contain "data-table" + +[] +let ``HtmlDocument.Html property returns the parsed document`` () = + let htmlContent = """

Test Title

""" + + use reader = new StringReader(htmlContent) + let htmlDoc = HtmlDocument.Create(false, reader) + let doc = htmlDoc.Html + + doc.ToString() |> should contain "Test Title" + +[] +let ``HtmlDocument.GetTable retrieves table by id`` () = + let htmlContent = """ + + + + + +
Column1Column2
Value1Value2
+ + """ + + use reader = new StringReader(htmlContent) + let htmlDoc = HtmlDocument.Create(false, reader) + let table = htmlDoc.GetTable("data-table") + + table.Name |> should equal "data-table" + +[] +let ``HtmlDocument.GetTable throws when table not found`` () = + let htmlContent = """

No tables here

""" + + use reader = new StringReader(htmlContent) + let htmlDoc = HtmlDocument.Create(false, reader) + + (fun () -> htmlDoc.GetTable("nonexistent") |> ignore) |> should throw typeof + +[] +let ``HtmlDocument.GetList retrieves list by id`` () = + let htmlContent = """ + + +
    +
  • Item 1
  • +
  • Item 2
  • +
+ + """ + + use reader = new StringReader(htmlContent) + let htmlDoc = HtmlDocument.Create(false, reader) + let list = htmlDoc.GetList("item-list") + + list.Name |> should equal "item-list" + +[] +let ``HtmlDocument.GetList works with ordered lists`` () = + let htmlContent = """ + + +
    +
  1. First
  2. +
  3. Second
  4. +
+ + """ + + use reader = new StringReader(htmlContent) + let htmlDoc = HtmlDocument.Create(false, reader) + let list = htmlDoc.GetList("numbered-list") + + list.Name |> should equal "numbered-list" + +[] +let ``HtmlDocument.GetList throws when list not found`` () = + let htmlContent = """

No lists here

""" + + use reader = new StringReader(htmlContent) + let htmlDoc = HtmlDocument.Create(false, reader) + + (fun () -> htmlDoc.GetList("nonexistent") |> ignore) |> should throw typeof + +[] +let ``HtmlDocument.GetDefinitionList retrieves definition list by id`` () = + let htmlContent = """ + + +
+
Term1
+
Definition1
+
Term2
+
Definition2
+
+ + """ + + use reader = new StringReader(htmlContent) + let htmlDoc = HtmlDocument.Create(false, reader) + let defList = htmlDoc.GetDefinitionList("def-list") + + defList.Name |> should equal "def-list" + +[] +let ``HtmlDocument.GetDefinitionList throws when definition list not found`` () = + let htmlContent = """

No definition lists here

""" + + use reader = new StringReader(htmlContent) + let htmlDoc = HtmlDocument.Create(false, reader) + + (fun () -> htmlDoc.GetDefinitionList("nonexistent") |> ignore) |> should throw typeof + +[] +let ``HtmlDocument.Create handles empty HTML`` () = + let htmlContent = "" + + use reader = new StringReader(htmlContent) + let htmlDoc = HtmlDocument.Create(false, reader) + + htmlDoc.Html.ToString() |> should not' (equal "") + +[] +let ``HtmlDocument.Create handles malformed HTML gracefully`` () = + let htmlContent = "

Test Content

Valid paragraph

Unclosed div" + + use reader = new StringReader(htmlContent) + let htmlDoc = HtmlDocument.Create(false, reader) + + // Parser handles malformed HTML by auto-closing tags and preserving content + let htmlString = htmlDoc.Html.ToString() + htmlString |> should contain "Test Content" + htmlString |> should contain "Valid paragraph" + // The parser should preserve at least some structure even with malformed HTML + +[] +let ``HtmlDocument.Create processes multiple tables correctly`` () = + let htmlContent = """ + + + + +
Table 1 Content
+ + +
Table 2 Content
+ + """ + + use reader = new StringReader(htmlContent) + let htmlDoc = HtmlDocument.Create(false, reader) + + // Use the actual generated names since HTML parsing creates unique names + htmlDoc.Html.ToString() |> should contain "Table 1 Content" + htmlDoc.Html.ToString() |> should contain "Table 2 Content" + +[] +let ``HtmlDocument.Create processes multiple lists correctly`` () = + let htmlContent = """ + + +
    +
  • List 1 Item
  • +
+
    +
  1. List 2 Item
  2. +
+ + """ + + use reader = new StringReader(htmlContent) + let htmlDoc = HtmlDocument.Create(false, reader) + + // Verify the content is parsed correctly + htmlDoc.Html.ToString() |> should contain "List 1 Item" + htmlDoc.Html.ToString() |> should contain "List 2 Item" + +[] +let ``HtmlDocument.Create processes multiple definition lists correctly`` () = + let htmlContent = """ + + +
+
Term A
+
Definition A
+
+
+
Term B
+
Definition B
+
+ + """ + + use reader = new StringReader(htmlContent) + let htmlDoc = HtmlDocument.Create(false, reader) + + // Verify the definition lists are parsed correctly + htmlDoc.Html.ToString() |> should contain "Term A" + htmlDoc.Html.ToString() |> should contain "Definition A" + htmlDoc.Html.ToString() |> should contain "Term B" + htmlDoc.Html.ToString() |> should contain "Definition B" \ No newline at end of file diff --git a/tests/FSharp.Data.Core.Tests/FSharp.Data.Core.Tests.fsproj b/tests/FSharp.Data.Core.Tests/FSharp.Data.Core.Tests.fsproj index 76ee8f8e4..65aa13b66 100644 --- a/tests/FSharp.Data.Core.Tests/FSharp.Data.Core.Tests.fsproj +++ b/tests/FSharp.Data.Core.Tests/FSharp.Data.Core.Tests.fsproj @@ -36,6 +36,7 @@ + diff --git a/tests/FSharp.Data.Core.Tests/JsonConversions.fs b/tests/FSharp.Data.Core.Tests/JsonConversions.fs index 94a233461..281c841c0 100644 --- a/tests/FSharp.Data.Core.Tests/JsonConversions.fs +++ b/tests/FSharp.Data.Core.Tests/JsonConversions.fs @@ -196,3 +196,97 @@ let ``Conversions handle different number formats`` () = JsonValue.String "123.456" |> asDecimal |> should equal (Some 123.456M) JsonValue.String "0" |> asDecimal |> should equal (Some 0M) JsonValue.String "-99.99" |> asDecimal |> should equal (Some -99.99M) + +[] +let ``Integer conversions test range boundaries for helper functions`` () = + let asInteger = JsonConversions.AsInteger System.Globalization.CultureInfo.InvariantCulture + let asInteger64 = JsonConversions.AsInteger64 System.Globalization.CultureInfo.InvariantCulture + + // Test exact boundary values to trigger inRangeDecimal helper function + JsonValue.Number (decimal System.Int32.MaxValue) |> asInteger |> should equal (Some System.Int32.MaxValue) + JsonValue.Number (decimal System.Int32.MinValue) |> asInteger |> should equal (Some System.Int32.MinValue) + + // Test values just outside boundaries to trigger inRangeDecimal helper function + JsonValue.Number ((decimal System.Int32.MaxValue) + 1M) |> asInteger |> should equal None + JsonValue.Number ((decimal System.Int32.MinValue) - 1M) |> asInteger |> should equal None + + // Test exact boundary values for Int64 to trigger inRangeDecimal helper function + JsonValue.Number (decimal System.Int64.MaxValue) |> asInteger64 |> should equal (Some System.Int64.MaxValue) + JsonValue.Number (decimal System.Int64.MinValue) |> asInteger64 |> should equal (Some System.Int64.MinValue) + +[] +let ``Float integer conversions test range boundaries for helper functions`` () = + let asInteger = JsonConversions.AsInteger System.Globalization.CultureInfo.InvariantCulture + let asInteger64 = JsonConversions.AsInteger64 System.Globalization.CultureInfo.InvariantCulture + + // Test exact boundary values with floats to trigger inRangeFloat helper function + JsonValue.Float (float System.Int32.MaxValue) |> asInteger |> should equal (Some System.Int32.MaxValue) + JsonValue.Float (float System.Int32.MinValue) |> asInteger |> should equal (Some System.Int32.MinValue) + + // Test values just outside boundaries with floats to trigger inRangeFloat helper function + JsonValue.Float ((float System.Int32.MaxValue) + 1.0) |> asInteger |> should equal None + JsonValue.Float ((float System.Int32.MinValue) - 1.0) |> asInteger |> should equal None + + // Test large values for Int64 with floats to trigger inRangeFloat helper function + // Use values that don't hit floating-point precision limits + JsonValue.Float 1000000000000.0 |> asInteger64 |> should equal (Some 1000000000000L) + JsonValue.Float -1000000000000.0 |> asInteger64 |> should equal (Some -1000000000000L) + +[] +let ``Integer detection tests for decimal values to trigger isIntegerDecimal helper`` () = + let asInteger = JsonConversions.AsInteger System.Globalization.CultureInfo.InvariantCulture + let asInteger64 = JsonConversions.AsInteger64 System.Globalization.CultureInfo.InvariantCulture + + // Test decimal values that are integers to trigger isIntegerDecimal helper function + JsonValue.Number 42.0M |> asInteger |> should equal (Some 42) + JsonValue.Number -123.000M |> asInteger |> should equal (Some -123) + JsonValue.Number 0.0M |> asInteger |> should equal (Some 0) + JsonValue.Number 1.0M |> asInteger64 |> should equal (Some 1L) + + // Test decimal values that are NOT integers to trigger isIntegerDecimal helper function + JsonValue.Number 42.1M |> asInteger |> should equal None + JsonValue.Number -123.5M |> asInteger |> should equal None + JsonValue.Number 0.001M |> asInteger |> should equal None + JsonValue.Number 1.99999M |> asInteger64 |> should equal None + +[] +let ``Integer detection tests for float values to trigger isIntegerFloat helper`` () = + let asInteger = JsonConversions.AsInteger System.Globalization.CultureInfo.InvariantCulture + let asInteger64 = JsonConversions.AsInteger64 System.Globalization.CultureInfo.InvariantCulture + + // Test float values that are integers to trigger isIntegerFloat helper function + JsonValue.Float 42.0 |> asInteger |> should equal (Some 42) + JsonValue.Float -123.000 |> asInteger |> should equal (Some -123) + JsonValue.Float 0.0 |> asInteger |> should equal (Some 0) + JsonValue.Float 1.0 |> asInteger64 |> should equal (Some 1L) + + // Test float values that are NOT integers to trigger isIntegerFloat helper function + JsonValue.Float 42.1 |> asInteger |> should equal None + JsonValue.Float -123.5 |> asInteger |> should equal None + JsonValue.Float 0.001 |> asInteger |> should equal None + JsonValue.Float 1.99999 |> asInteger64 |> should equal None + + // Test special float cases to trigger isIntegerFloat helper function + JsonValue.Float System.Double.NaN |> asInteger |> should equal None + JsonValue.Float System.Double.PositiveInfinity |> asInteger |> should equal None + JsonValue.Float System.Double.NegativeInfinity |> asInteger64 |> should equal None + +[] +let ``Combined edge cases to exercise all helper functions`` () = + let asInteger = JsonConversions.AsInteger System.Globalization.CultureInfo.InvariantCulture + let asInteger64 = JsonConversions.AsInteger64 System.Globalization.CultureInfo.InvariantCulture + + // Large integers that exercise both range checking and integer detection helpers + JsonValue.Number 2147483647.0M |> asInteger |> should equal (Some 2147483647) // Int32.MaxValue + JsonValue.Float 2147483647.0 |> asInteger |> should equal (Some 2147483647) + + JsonValue.Number 2147483648.0M |> asInteger |> should equal None // Just over Int32.MaxValue + JsonValue.Float 2147483648.0 |> asInteger |> should equal None + + // Large values that work for Int64 but not Int32 + JsonValue.Number 3000000000M |> asInteger |> should equal None + JsonValue.Number 3000000000M |> asInteger64 |> should equal (Some 3000000000L) + + // Values that are in range but not integers + JsonValue.Number 100.5M |> asInteger |> should equal None + JsonValue.Float 100.5 |> asInteger |> should equal None