Skip to content

Commit b3dfb9e

Browse files
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>
1 parent ca9d297 commit b3dfb9e

File tree

3 files changed

+197
-3
lines changed

3 files changed

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

0 commit comments

Comments
 (0)