Skip to content

Commit 2a9971d

Browse files
github-actions[bot]Copilotsergey-tihonCopilotCopilot
authored
[Repo Assist] Test: add v3 DefinitionCompiler type-mapping unit tests; drop unused FAKE imports (#331)
* 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<bool>, Option<int32>, Option<int64>, Option<float32>, Option<double>, Option<DateTimeOffset>, Option<Guid> - 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> * ci: trigger checks * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix: formattin * 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> --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Sergey Tihon <sergey.tihon@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com>
1 parent af3f158 commit 2a9971d

File tree

3 files changed

+211
-3
lines changed

3 files changed

+211
-3
lines changed

build.fsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@
33
#r "nuget: Fake.Core.ReleaseNotes"
44
#r "nuget: Fake.IO.FileSystem"
55
#r "nuget: Fake.DotNet.Cli"
6-
#r "nuget: Fake.DotNet.MSBuild"
76
#r "nuget: Fake.DotNet.AssemblyInfoFile"
87
#r "nuget: Fake.DotNet.Paket"
9-
#r "nuget: Fake.DotNet.FSFormatting"
108
#r "nuget: Fake.Tools.Git"
11-
#r "nuget: Fake.Api.GitHub"
129

1310
// Boilerplate - https://github.com/fsprojects/FAKE/issues/2719#issuecomment-1470687052
1411
System.Environment.GetCommandLineArgs()

tests/SwaggerProvider.Tests/SwaggerProvider.Tests.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<Compile Include="v2\Schema.Spec.Yaml.Tests.fs" />
1717
<Compile Include="APIs.guru.fs" />
1818
<Compile Include="Schema.Parser.Tests.fs" />
19+
<Compile Include="v3\Schema.TypeMappingTests.fs" />
1920
<Compile Include="PathResolutionTests.fs" />
2021
<Compile Include="SsrfSecurityTests.fs" />
2122
<Compile Include="RuntimeHelpersTests.fs" />
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
module SwaggerProvider.Tests.v3_Schema_TypeMappingTests
2+
3+
open System
4+
open Microsoft.OpenApi.Reader
5+
open SwaggerProvider.Internal.v3.Compilers
6+
open Xunit
7+
open FsUnitTyped
8+
9+
/// Compile a minimal OpenAPI v3 schema containing one "TestType" object with a single
10+
/// "Value" property defined by `propYaml`, and return that property's compiled .NET type.
11+
let private compilePropertyType (propYaml: string) (required: bool) : Type =
12+
let requiredBlock =
13+
if required then
14+
" required:\n - Value\n"
15+
else
16+
""
17+
18+
let schemaStr =
19+
sprintf
20+
"""openapi: "3.0.0"
21+
info:
22+
title: TypeMappingTest
23+
version: "1.0.0"
24+
paths: {}
25+
components:
26+
schemas:
27+
TestType:
28+
type: object
29+
%s properties:
30+
Value:
31+
%s"""
32+
requiredBlock
33+
propYaml
34+
35+
let settings = OpenApiReaderSettings()
36+
settings.AddYamlReader()
37+
38+
let readResult =
39+
Microsoft.OpenApi.OpenApiDocument.Parse(schemaStr, settings = settings)
40+
41+
// Ensure the schema was parsed successfully before using it
42+
match readResult.Diagnostic with
43+
| null -> ()
44+
| diagnostic when diagnostic.Errors |> Seq.isEmpty |> not ->
45+
let errorText =
46+
diagnostic.Errors
47+
|> Seq.map string
48+
|> String.concat Environment.NewLine
49+
50+
failwithf "Failed to parse OpenAPI schema:%s%s" Environment.NewLine errorText
51+
| _ -> ()
52+
53+
let schema =
54+
match readResult.Document with
55+
| null -> failwith "Failed to parse OpenAPI schema: Document is null."
56+
| doc -> doc
57+
58+
let defCompiler = DefinitionCompiler(schema, false)
59+
let opCompiler = OperationCompiler(schema, defCompiler, true, false, true)
60+
opCompiler.CompileProvidedClients(defCompiler.Namespace)
61+
62+
let types = defCompiler.Namespace.GetProvidedTypes()
63+
let testType = types |> List.find(fun t -> t.Name = "TestType")
64+
65+
match testType.GetDeclaredProperty("Value") with
66+
| null -> failwith "Property 'Value' not found on TestType"
67+
| prop -> prop.PropertyType
68+
69+
// ── Required primitive types ─────────────────────────────────────────────────
70+
71+
[<Fact>]
72+
let ``required boolean maps to bool``() =
73+
let ty = compilePropertyType " type: boolean\n" true
74+
75+
ty |> shouldEqual typeof<bool>
76+
77+
[<Fact>]
78+
let ``required integer (no format) maps to int32``() =
79+
let ty = compilePropertyType " type: integer\n" true
80+
81+
ty |> shouldEqual typeof<int32>
82+
83+
[<Fact>]
84+
let ``required integer int64 format maps to int64``() =
85+
let ty =
86+
compilePropertyType " type: integer\n format: int64\n" true
87+
88+
ty |> shouldEqual typeof<int64>
89+
90+
[<Fact>]
91+
let ``required number (no format) maps to float32``() =
92+
let ty = compilePropertyType " type: number\n" true
93+
94+
ty |> shouldEqual typeof<float32>
95+
96+
[<Fact>]
97+
let ``required number double format maps to double``() =
98+
let ty =
99+
compilePropertyType " type: number\n format: double\n" true
100+
101+
ty |> shouldEqual typeof<double>
102+
103+
// ── Required string formats ───────────────────────────────────────────────────
104+
105+
[<Fact>]
106+
let ``required string (no format) maps to string``() =
107+
let ty = compilePropertyType " type: string\n" true
108+
109+
ty |> shouldEqual typeof<string>
110+
111+
[<Fact>]
112+
let ``required string date-time format maps to DateTimeOffset``() =
113+
let ty =
114+
compilePropertyType " type: string\n format: date-time\n" true
115+
116+
ty |> shouldEqual typeof<DateTimeOffset>
117+
118+
[<Fact>]
119+
let ``required string date format maps to DateTimeOffset``() =
120+
let ty = compilePropertyType " type: string\n format: date\n" true
121+
122+
ty |> shouldEqual typeof<DateTimeOffset>
123+
124+
[<Fact>]
125+
let ``required string uuid format maps to Guid``() =
126+
let ty = compilePropertyType " type: string\n format: uuid\n" true
127+
128+
ty |> shouldEqual typeof<Guid>
129+
130+
[<Fact>]
131+
let ``required string byte format maps to byte array``() =
132+
let ty = compilePropertyType " type: string\n format: byte\n" true
133+
134+
// DefinitionCompiler creates a rank-1 explicit array via MakeArrayType(1)
135+
ty |> shouldEqual(typeof<byte>.MakeArrayType(1))
136+
137+
[<Fact>]
138+
let ``required string binary format maps to Stream``() =
139+
let ty =
140+
compilePropertyType " type: string\n format: binary\n" true
141+
142+
ty |> shouldEqual typeof<IO.Stream>
143+
144+
// ── Optional (non-required) value types are wrapped in Option<T> ─────────────
145+
146+
[<Fact>]
147+
let ``optional boolean maps to Option<bool>``() =
148+
let ty = compilePropertyType " type: boolean\n" false
149+
150+
ty |> shouldEqual(typedefof<Option<_>>.MakeGenericType(typeof<bool>))
151+
152+
[<Fact>]
153+
let ``optional integer maps to Option<int32>``() =
154+
let ty = compilePropertyType " type: integer\n" false
155+
156+
ty |> shouldEqual(typedefof<Option<_>>.MakeGenericType(typeof<int32>))
157+
158+
[<Fact>]
159+
let ``optional integer int64 maps to Option<int64>``() =
160+
let ty =
161+
compilePropertyType " type: integer\n format: int64\n" false
162+
163+
ty |> shouldEqual(typedefof<Option<_>>.MakeGenericType(typeof<int64>))
164+
165+
[<Fact>]
166+
let ``optional number maps to Option<float32>``() =
167+
let ty = compilePropertyType " type: number\n" false
168+
169+
ty
170+
|> shouldEqual(typedefof<Option<_>>.MakeGenericType(typeof<float32>))
171+
172+
[<Fact>]
173+
let ``optional number double maps to Option<double>``() =
174+
let ty =
175+
compilePropertyType " type: number\n format: double\n" false
176+
177+
ty
178+
|> shouldEqual(typedefof<Option<_>>.MakeGenericType(typeof<double>))
179+
180+
[<Fact>]
181+
let ``optional DateTimeOffset maps to Option<DateTimeOffset>``() =
182+
let ty =
183+
compilePropertyType " type: string\n format: date-time\n" false
184+
185+
ty
186+
|> shouldEqual(typedefof<Option<_>>.MakeGenericType(typeof<DateTimeOffset>))
187+
188+
[<Fact>]
189+
let ``optional Guid maps to Option<Guid>``() =
190+
let ty =
191+
compilePropertyType " type: string\n format: uuid\n" false
192+
193+
ty |> shouldEqual(typedefof<Option<_>>.MakeGenericType(typeof<Guid>))
194+
195+
// ── Optional reference types are NOT wrapped (they are already nullable) ─────
196+
197+
[<Fact>]
198+
let ``optional string is not wrapped in Option``() =
199+
let ty = compilePropertyType " type: string\n" false
200+
201+
// string is a reference type — not wrapped in Option<T> even when non-required
202+
ty |> shouldEqual typeof<string>
203+
204+
[<Fact>]
205+
let ``optional byte array is not wrapped in Option``() =
206+
let ty =
207+
compilePropertyType " type: string\n format: byte\n" false
208+
209+
// byte[*] is a reference type — not wrapped in Option<T>
210+
ty |> shouldEqual(typeof<byte>.MakeArrayType(1))

0 commit comments

Comments
 (0)