From b3dfb9ea382dc047a47fcb06ab672956edffbbcb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 20 Mar 2026 16:39:15 +0000 Subject: [PATCH 1/5] test: add v3 DefinitionCompiler type-mapping unit tests; remove unused FAKE imports Add 20 unit tests for the v3 DefinitionCompiler type mapping in a new tests/SwaggerProvider.Tests/v3/Schema.TypeMappingTests.fs. Each test compiles a minimal inline OpenAPI 3.0 schema through the DefinitionCompiler pipeline and asserts the resulting .NET property type: - Primitives: bool, int32, int64, float32, double - String formats: string, DateTimeOffset (date-time), DateTimeOffset (date), Guid (uuid), byte[*] (byte), IO.Stream (binary) - Optional value-type wrapping: Option, Option, Option, Option, Option, Option, Option - Optional reference-type passthrough: string and byte[*] remain unwrapped Previously there were no unit tests covering the type-mapping logic inside v3/DefinitionCompiler.fs; these tests make the mapping observable and will catch regressions when the mapping changes (e.g. the DateOnly PR #321). Also remove three unused FAKE package imports from build.fsx: - Fake.DotNet.MSBuild (no MSBuild-specific targets used) - Fake.DotNet.FSFormatting (fantomas is invoked via DotNet.exec, not FSFormatting.xxx) - Fake.Api.GitHub (GitHub release code is fully commented out in the Release target) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- build.fsx | 3 - .../SwaggerProvider.Tests.fsproj | 1 + .../v3/Schema.TypeMappingTests.fs | 196 ++++++++++++++++++ 3 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 tests/SwaggerProvider.Tests/v3/Schema.TypeMappingTests.fs diff --git a/build.fsx b/build.fsx index 07295623..f66eec09 100644 --- a/build.fsx +++ b/build.fsx @@ -3,12 +3,9 @@ #r "nuget: Fake.Core.ReleaseNotes" #r "nuget: Fake.IO.FileSystem" #r "nuget: Fake.DotNet.Cli" -#r "nuget: Fake.DotNet.MSBuild" #r "nuget: Fake.DotNet.AssemblyInfoFile" #r "nuget: Fake.DotNet.Paket" -#r "nuget: Fake.DotNet.FSFormatting" #r "nuget: Fake.Tools.Git" -#r "nuget: Fake.Api.GitHub" // Boilerplate - https://github.com/fsprojects/FAKE/issues/2719#issuecomment-1470687052 System.Environment.GetCommandLineArgs() diff --git a/tests/SwaggerProvider.Tests/SwaggerProvider.Tests.fsproj b/tests/SwaggerProvider.Tests/SwaggerProvider.Tests.fsproj index 36790a6e..0b57e743 100644 --- a/tests/SwaggerProvider.Tests/SwaggerProvider.Tests.fsproj +++ b/tests/SwaggerProvider.Tests/SwaggerProvider.Tests.fsproj @@ -16,6 +16,7 @@ + diff --git a/tests/SwaggerProvider.Tests/v3/Schema.TypeMappingTests.fs b/tests/SwaggerProvider.Tests/v3/Schema.TypeMappingTests.fs new file mode 100644 index 00000000..ece4c0ab --- /dev/null +++ b/tests/SwaggerProvider.Tests/v3/Schema.TypeMappingTests.fs @@ -0,0 +1,196 @@ +module SwaggerProvider.Tests.V3TypeMappingTests + +open System +open System.IO +open Microsoft.OpenApi.Reader +open SwaggerProvider.Internal.v3.Compilers +open Xunit +open FsUnitTyped + +/// Compile a minimal OpenAPI v3 schema containing one "TestType" object with a single +/// "Value" property defined by `propYaml`, and return that property's compiled .NET type. +let private compilePropertyType (propYaml: string) (required: bool) : Type = + let requiredBlock = + if required then + " required:\n - Value\n" + else + "" + + let schemaStr = + sprintf + """openapi: "3.0.0" +info: + title: TypeMappingTest + version: "1.0.0" +paths: {} +components: + schemas: + TestType: + type: object +%s properties: + Value: +%s""" + requiredBlock + propYaml + + let settings = OpenApiReaderSettings() + settings.AddYamlReader() + + let readResult = + Microsoft.OpenApi.OpenApiDocument.Parse(schemaStr, settings = settings) + + let schema = readResult.Document + + let defCompiler = DefinitionCompiler(schema, false) + let opCompiler = OperationCompiler(schema, defCompiler, true, false, true) + opCompiler.CompileProvidedClients(defCompiler.Namespace) + + let types = defCompiler.Namespace.GetProvidedTypes() + let testType = types |> List.find(fun t -> t.Name = "TestType") + + match testType.GetDeclaredProperty("Value") with + | null -> failwith "Property 'Value' not found on TestType" + | prop -> prop.PropertyType + +// ── Required primitive types ───────────────────────────────────────────────── + +[] +let ``required boolean maps to bool``() = + let ty = compilePropertyType " type: boolean\n" true + + ty |> shouldEqual typeof + +[] +let ``required integer (no format) maps to int32``() = + let ty = compilePropertyType " type: integer\n" true + + ty |> shouldEqual typeof + +[] +let ``required integer int64 format maps to int64``() = + let ty = + compilePropertyType " type: integer\n format: int64\n" true + + ty |> shouldEqual typeof + +[] +let ``required number (no format) maps to float32``() = + let ty = compilePropertyType " type: number\n" true + + ty |> shouldEqual typeof + +[] +let ``required number double format maps to double``() = + let ty = + compilePropertyType " type: number\n format: double\n" true + + ty |> shouldEqual typeof + +// ── Required string formats ─────────────────────────────────────────────────── + +[] +let ``required string (no format) maps to string``() = + let ty = compilePropertyType " type: string\n" true + + ty |> shouldEqual typeof + +[] +let ``required string date-time format maps to DateTimeOffset``() = + let ty = + compilePropertyType " type: string\n format: date-time\n" true + + ty |> shouldEqual typeof + +[] +let ``required string date format maps to DateTimeOffset``() = + let ty = compilePropertyType " type: string\n format: date\n" true + + ty |> shouldEqual typeof + +[] +let ``required string uuid format maps to Guid``() = + let ty = compilePropertyType " type: string\n format: uuid\n" true + + ty |> shouldEqual typeof + +[] +let ``required string byte format maps to byte array``() = + let ty = compilePropertyType " type: string\n format: byte\n" true + + // DefinitionCompiler creates a rank-1 explicit array via MakeArrayType(1) + ty |> shouldEqual(typeof.MakeArrayType(1)) + +[] +let ``required string binary format maps to Stream``() = + let ty = + compilePropertyType " type: string\n format: binary\n" true + + ty |> shouldEqual typeof + +// ── Optional (non-required) value types are wrapped in Option ───────────── + +[] +let ``optional boolean maps to Option``() = + let ty = compilePropertyType " type: boolean\n" false + + ty |> shouldEqual(typedefof>.MakeGenericType(typeof)) + +[] +let ``optional integer maps to Option``() = + let ty = compilePropertyType " type: integer\n" false + + ty |> shouldEqual(typedefof>.MakeGenericType(typeof)) + +[] +let ``optional integer int64 maps to Option``() = + let ty = + compilePropertyType " type: integer\n format: int64\n" false + + ty |> shouldEqual(typedefof>.MakeGenericType(typeof)) + +[] +let ``optional number maps to Option``() = + let ty = compilePropertyType " type: number\n" false + + ty + |> shouldEqual(typedefof>.MakeGenericType(typeof)) + +[] +let ``optional number double maps to Option``() = + let ty = + compilePropertyType " type: number\n format: double\n" false + + ty + |> shouldEqual(typedefof>.MakeGenericType(typeof)) + +[] +let ``optional DateTimeOffset maps to Option``() = + let ty = + compilePropertyType " type: string\n format: date-time\n" false + + ty + |> shouldEqual(typedefof>.MakeGenericType(typeof)) + +[] +let ``optional Guid maps to Option``() = + let ty = + compilePropertyType " type: string\n format: uuid\n" false + + ty |> shouldEqual(typedefof>.MakeGenericType(typeof)) + +// ── Optional reference types are NOT wrapped (they are already nullable) ───── + +[] +let ``optional string is not wrapped in Option``() = + let ty = compilePropertyType " type: string\n" false + + // string is a reference type — not wrapped in Option even when non-required + ty |> shouldEqual typeof + +[] +let ``optional byte array is not wrapped in Option``() = + let ty = + compilePropertyType " type: string\n format: byte\n" false + + // byte[*] is a reference type — not wrapped in Option + ty |> shouldEqual(typeof.MakeArrayType(1)) From 4634de25898373f9e041d228abb66c2a68af0f5e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 20 Mar 2026 16:44:21 +0000 Subject: [PATCH 2/5] ci: trigger checks From a276b018a6015d4dd1975d400e1f5b858b8b2925 Mon Sep 17 00:00:00 2001 From: Sergey Tihon Date: Fri, 20 Mar 2026 18:09:58 +0100 Subject: [PATCH 3/5] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../v3/Schema.TypeMappingTests.fs | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/SwaggerProvider.Tests/v3/Schema.TypeMappingTests.fs b/tests/SwaggerProvider.Tests/v3/Schema.TypeMappingTests.fs index ece4c0ab..67ec107a 100644 --- a/tests/SwaggerProvider.Tests/v3/Schema.TypeMappingTests.fs +++ b/tests/SwaggerProvider.Tests/v3/Schema.TypeMappingTests.fs @@ -1,7 +1,6 @@ -module SwaggerProvider.Tests.V3TypeMappingTests +module SwaggerProvider.Tests.v3.Schema.TypeMappingTests open System -open System.IO open Microsoft.OpenApi.Reader open SwaggerProvider.Internal.v3.Compilers open Xunit @@ -39,8 +38,21 @@ components: let readResult = Microsoft.OpenApi.OpenApiDocument.Parse(schemaStr, settings = settings) - let schema = readResult.Document - + // Ensure the schema was parsed successfully before using it + match readResult.Diagnostic with + | null -> () + | diagnostic when diagnostic.Errors |> Seq.isEmpty |> not -> + let errorText = + diagnostic.Errors + |> Seq.map string + |> String.concat Environment.NewLine + failwithf "Failed to parse OpenAPI schema:%s%s" Environment.NewLine errorText + | _ -> () + + let schema = + match readResult.Document with + | null -> failwith "Failed to parse OpenAPI schema: Document is null." + | doc -> doc let defCompiler = DefinitionCompiler(schema, false) let opCompiler = OperationCompiler(schema, defCompiler, true, false, true) opCompiler.CompileProvidedClients(defCompiler.Namespace) From 807d74687dc27555a40964bd0a7ca611494f6c16 Mon Sep 17 00:00:00 2001 From: Sergey Tihon Date: Sun, 22 Mar 2026 16:40:22 +0100 Subject: [PATCH 4/5] fix: formattin --- tests/SwaggerProvider.Tests/v3/Schema.TypeMappingTests.fs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/SwaggerProvider.Tests/v3/Schema.TypeMappingTests.fs b/tests/SwaggerProvider.Tests/v3/Schema.TypeMappingTests.fs index 67ec107a..581a69bc 100644 --- a/tests/SwaggerProvider.Tests/v3/Schema.TypeMappingTests.fs +++ b/tests/SwaggerProvider.Tests/v3/Schema.TypeMappingTests.fs @@ -46,6 +46,7 @@ components: diagnostic.Errors |> Seq.map string |> String.concat Environment.NewLine + failwithf "Failed to parse OpenAPI schema:%s%s" Environment.NewLine errorText | _ -> () @@ -53,6 +54,7 @@ components: match readResult.Document with | null -> failwith "Failed to parse OpenAPI schema: Document is null." | doc -> doc + let defCompiler = DefinitionCompiler(schema, false) let opCompiler = OperationCompiler(schema, defCompiler, true, false, true) opCompiler.CompileProvidedClients(defCompiler.Namespace) From c2066bc52aa635850187254aa92f1b81f8adede5 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Mar 2026 16:53:33 +0100 Subject: [PATCH 5/5] fix(tests): resolve FS0247 namespace/module conflict in v3 TypeMappingTests (#334) * Initial plan * fix FS0247: rename TypeMappingTests module to avoid namespace/module conflict with Schema.Parser.Tests.fs Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com> Agent-Logs-Url: https://github.com/fsprojects/SwaggerProvider/sessions/c877bfb7-1289-4e88-8838-7ce910aca59c --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com> --- tests/SwaggerProvider.Tests/v3/Schema.TypeMappingTests.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/SwaggerProvider.Tests/v3/Schema.TypeMappingTests.fs b/tests/SwaggerProvider.Tests/v3/Schema.TypeMappingTests.fs index 581a69bc..6b77d7a1 100644 --- a/tests/SwaggerProvider.Tests/v3/Schema.TypeMappingTests.fs +++ b/tests/SwaggerProvider.Tests/v3/Schema.TypeMappingTests.fs @@ -1,4 +1,4 @@ -module SwaggerProvider.Tests.v3.Schema.TypeMappingTests +module SwaggerProvider.Tests.v3_Schema_TypeMappingTests open System open Microsoft.OpenApi.Reader