From 3c42f65345e5ae507ca7ad116843d571920fb98a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 22:16:33 +0000 Subject: [PATCH 1/4] test: extend RuntimeHelpers unit-test coverage Add missing tests for RuntimeHelpers functions that were previously untested: - toQueryParams: missing Option scalar and array variants (previously only Option were covered) - formatObject: new FormatObjectTests module covering string, int, null, array, and multi-property cases (sorts alphabetically) - toFormUrlEncodedContent: new ToFormUrlEncodedContentTests module covering encoding, null-exclusion, and empty sequence - toMultipartFormDataContent: new ToMultipartFormDataContentTests module covering string values, null skipping, and stream values Total test count increases from 252 to 286. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../RuntimeHelpersTests.fs | 180 ++++++++++++++++++ 1 file changed, 180 insertions(+) diff --git a/tests/SwaggerProvider.Tests/RuntimeHelpersTests.fs b/tests/SwaggerProvider.Tests/RuntimeHelpersTests.fs index 0b1d8e4a..16fc9c56 100644 --- a/tests/SwaggerProvider.Tests/RuntimeHelpersTests.fs +++ b/tests/SwaggerProvider.Tests/RuntimeHelpersTests.fs @@ -236,6 +236,64 @@ module ToQueryParamsTests = let result = toQueryParams "v" (box values) stubClient result |> shouldEqual [ ("v", "1.5"); ("v", "3.5") ] + [] + let ``toQueryParams handles Option Some``() = + let result = toQueryParams "flag" (box(Some true)) stubClient + result |> shouldEqual [ ("flag", "True") ] + + [] + let ``toQueryParams handles Option None``() = + let result = toQueryParams "flag" (box(None: bool option)) stubClient + result |> shouldEqual [] + + [] + let ``toQueryParams handles Option Some``() = + let result = toQueryParams "id" (box(Some 9876543210L)) stubClient + result |> shouldEqual [ ("id", "9876543210") ] + + [] + let ``toQueryParams handles Option None``() = + let result = toQueryParams "id" (box(None: int64 option)) stubClient + result |> shouldEqual [] + + [] + let ``toQueryParams handles Option Some``() = + let result = toQueryParams "rate" (box(Some 3.14f)) stubClient + result |> shouldEqual [ ("rate", (3.14f).ToString()) ] + + [] + let ``toQueryParams handles Option None``() = + let result = toQueryParams "rate" (box(None: float32 option)) stubClient + result |> shouldEqual [] + + [] + let ``toQueryParams handles Option Some``() = + let result = toQueryParams "rate" (box(Some 2.718)) stubClient + result |> shouldEqual [ ("rate", (2.718).ToString()) ] + + [] + let ``toQueryParams handles Option None``() = + let result = toQueryParams "rate" (box(None: double option)) stubClient + result |> shouldEqual [] + + [] + let ``toQueryParams handles Option Some``() = + let dto = DateTimeOffset(2025, 3, 15, 9, 0, 0, TimeSpan.Zero) + let result = toQueryParams "since" (box(Some dto)) stubClient + result |> shouldEqual [ ("since", dto.ToString("O")) ] + + [] + let ``toQueryParams handles Option None``() = + let result = toQueryParams "since" (box(None: DateTimeOffset option)) stubClient + result |> shouldEqual [] + + [] + let ``toQueryParams skips None items in Option array``() = + let dto = DateTimeOffset(2025, 6, 1, 0, 0, 0, TimeSpan.Zero) + let values: Option[] = [| Some dto; None |] + let result = toQueryParams "ts" (box values) stubClient + result |> shouldEqual [ ("ts", dto.ToString("O")) ] + module CombineUrlTests = @@ -528,3 +586,125 @@ module OpenApiExceptionTests = () } + + +/// Test types for formatObject tests — must be plain .NET classes with declared public properties. +type FmtSingle(name: string) = + member _.Name = name + +type FmtInt(count: int) = + member _.Count = count + +type FmtNullable(label: string) = + member _.Label: string = label + +type FmtMulti(age: int, name: string) = + member _.Age = age + member _.Name = name + +type FmtArray(tags: string[]) = + member _.Tags = tags + + +module FormatObjectTests = + + [] + let ``formatObject formats string property with quotes``() = + let obj = FmtSingle("Alice") + formatObject obj |> shouldEqual "{Name=\"Alice\"}" + + [] + let ``formatObject formats integer property without quotes``() = + let obj = FmtInt(42) + formatObject obj |> shouldEqual "{Count=42}" + + [] + let ``formatObject formats null string property as null``() = + let obj = FmtNullable(null) + formatObject obj |> shouldEqual "{Label=null}" + + [] + let ``formatObject formats array property as bracketed list``() = + let obj = FmtArray([| "alpha"; "beta" |]) + formatObject obj |> shouldEqual "{Tags=[alpha; beta]}" + + [] + let ``formatObject formats empty array property as empty brackets``() = + let obj = FmtArray([||]) + formatObject obj |> shouldEqual "{Tags=[]}" + + [] + let ``formatObject sorts properties alphabetically``() = + // Age < Name alphabetically + let obj = FmtMulti(30, "Bob") + formatObject obj |> shouldEqual "{Age=30; Name=\"Bob\"}" + + +module ToFormUrlEncodedContentTests = + + [] + let ``toFormUrlEncodedContent encodes key-value pairs``() = + task { + use content = + toFormUrlEncodedContent( + seq { + ("name", box "Alice") + ("age", box "30") + } + ) + + let! body = content.ReadAsStringAsync() + body |> shouldContainText "name=Alice" + body |> shouldContainText "age=30" + } + + [] + let ``toFormUrlEncodedContent excludes null values``() = + task { + use content = + toFormUrlEncodedContent( + seq { + ("present", box "yes") + ("missing", null) + } + ) + + let! body = content.ReadAsStringAsync() + body |> shouldContainText "present=yes" + body |> shouldNotContainText "missing" + } + + [] + let ``toFormUrlEncodedContent handles empty sequence``() = + task { + use content = toFormUrlEncodedContent Seq.empty + let! body = content.ReadAsStringAsync() + body |> shouldEqual "" + } + + +module ToMultipartFormDataContentTests = + + [] + let ``toMultipartFormDataContent skips null values``() = + use content = + toMultipartFormDataContent( + seq { + ("field", box "value") + ("empty", null) + } + ) + + // Non-null value is present; null value is skipped (content has exactly 1 child part) + content |> Seq.length |> shouldEqual 1 + + [] + let ``toMultipartFormDataContent adds string values as StringContent``() = + use content = toMultipartFormDataContent(seq { ("greeting", box "hello") }) + content |> Seq.length |> shouldEqual 1 + + [] + let ``toMultipartFormDataContent adds stream values with filename``() = + use stream = new MemoryStream([| 1uy; 2uy; 3uy |]) + use content = toMultipartFormDataContent(seq { ("file", box stream) }) + content |> Seq.length |> shouldEqual 1 From 098461260e006e2c94ad2804fec8552aa9dd88d2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 9 Apr 2026 22:16:36 +0000 Subject: [PATCH 2/4] ci: trigger checks From 2ee8974bf8654507c664ac422cfc578c98628c2a Mon Sep 17 00:00:00 2001 From: Sergey Tihon Date: Fri, 10 Apr 2026 09:45:47 +0200 Subject: [PATCH 3/4] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../RuntimeHelpersTests.fs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/SwaggerProvider.Tests/RuntimeHelpersTests.fs b/tests/SwaggerProvider.Tests/RuntimeHelpersTests.fs index 16fc9c56..224d6e27 100644 --- a/tests/SwaggerProvider.Tests/RuntimeHelpersTests.fs +++ b/tests/SwaggerProvider.Tests/RuntimeHelpersTests.fs @@ -259,7 +259,7 @@ module ToQueryParamsTests = [] let ``toQueryParams handles Option Some``() = let result = toQueryParams "rate" (box(Some 3.14f)) stubClient - result |> shouldEqual [ ("rate", (3.14f).ToString()) ] + result |> shouldEqual [ ("rate", "3.14") ] [] let ``toQueryParams handles Option None``() = @@ -269,7 +269,7 @@ module ToQueryParamsTests = [] let ``toQueryParams handles Option Some``() = let result = toQueryParams "rate" (box(Some 2.718)) stubClient - result |> shouldEqual [ ("rate", (2.718).ToString()) ] + result |> shouldEqual [ ("rate", "2.718") ] [] let ``toQueryParams handles Option None``() = @@ -701,10 +701,23 @@ module ToMultipartFormDataContentTests = [] let ``toMultipartFormDataContent adds string values as StringContent``() = use content = toMultipartFormDataContent(seq { ("greeting", box "hello") }) - content |> Seq.length |> shouldEqual 1 + let part = content |> Seq.exactlyOne + Assert.IsType(part) |> ignore [] let ``toMultipartFormDataContent adds stream values with filename``() = use stream = new MemoryStream([| 1uy; 2uy; 3uy |]) use content = toMultipartFormDataContent(seq { ("file", box stream) }) content |> Seq.length |> shouldEqual 1 + + let part = content |> Seq.exactlyOne + let disposition = part.Headers.ContentDisposition + + isNull disposition |> shouldEqual false + disposition.Name.Trim('"') |> shouldEqual "file" + + let hasFileName = + not (String.IsNullOrWhiteSpace disposition.FileName) + || not (String.IsNullOrWhiteSpace disposition.FileNameStar) + + hasFileName |> shouldEqual true From d746ea3d397246ef155d4ba2c2e2f10eedffce9b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Apr 2026 08:20:17 +0000 Subject: [PATCH 4/4] fix: apply Fantomas formatting to RuntimeHelpersTests.fs Agent-Logs-Url: https://github.com/fsprojects/SwaggerProvider/sessions/18e3dd9e-e3f5-4668-92bd-072362d8c4e0 Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com> --- tests/SwaggerProvider.Tests/RuntimeHelpersTests.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/SwaggerProvider.Tests/RuntimeHelpersTests.fs b/tests/SwaggerProvider.Tests/RuntimeHelpersTests.fs index 224d6e27..b67c848a 100644 --- a/tests/SwaggerProvider.Tests/RuntimeHelpersTests.fs +++ b/tests/SwaggerProvider.Tests/RuntimeHelpersTests.fs @@ -717,7 +717,7 @@ module ToMultipartFormDataContentTests = disposition.Name.Trim('"') |> shouldEqual "file" let hasFileName = - not (String.IsNullOrWhiteSpace disposition.FileName) - || not (String.IsNullOrWhiteSpace disposition.FileNameStar) + not(String.IsNullOrWhiteSpace disposition.FileName) + || not(String.IsNullOrWhiteSpace disposition.FileNameStar) hasFileName |> shouldEqual true