From 462062297a0928729491db1f41ff4bf2a13ff7fe Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 20:12:39 +0000 Subject: [PATCH 1/3] =?UTF-8?q?test:=20expand=20V3=20oneOf/anyOf/allOf=20a?= =?UTF-8?q?nd=20V2=20inheritance=20schema=20compilation=20tests=20(+11=20t?= =?UTF-8?q?ests,=20448=E2=86=92459)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - V3: oneOf/anyOf with multiple $refs does not emit a wrapper type (compileNewObject marks it as a name alias and returns obj when no own properties exist) - V3: oneOf/anyOf multi-ref still emits the referenced component types - V3: allOf single $ref with extra wrapper properties falls through to compileNewObject (guard requires Properties.Count = 0) and emits a new named type with the extra prop - V2: required property compiles to non-option type; optional to option type - V2: allOf inheritance ($ref + inline schema) emits both base and derived types; derived type carries its own declared property Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Schema.V2SchemaCompilationTests.fs | 104 ++++++++++++++ .../Schema.V3SchemaCompilationTests.fs | 129 ++++++++++++++++++ 2 files changed, 233 insertions(+) diff --git a/tests/SwaggerProvider.Tests/Schema.V2SchemaCompilationTests.fs b/tests/SwaggerProvider.Tests/Schema.V2SchemaCompilationTests.fs index cb285465..e904912b 100644 --- a/tests/SwaggerProvider.Tests/Schema.V2SchemaCompilationTests.fs +++ b/tests/SwaggerProvider.Tests/Schema.V2SchemaCompilationTests.fs @@ -185,6 +185,110 @@ let ``v2 schema with integer enum property compiles``() = // integer enum — Microsoft.OpenApi maps this to the integer base type codeProp.PropertyType |> shouldEqual typeof +// ── Required vs optional properties ───────────────────────────────────────── + +[] +let ``v2 required property compiles to non-option type``() = + let schema = + """{ + "swagger": "2.0", + "info": { "title": "RequiredTest", "version": "1.0.0" }, + "basePath": "/", + "paths": {}, + "definitions": { + "Order": { + "type": "object", + "required": ["id"], + "properties": { + "id": { "type": "integer" }, + "note": { "type": "string" } + } + } + } +}""" + + let types = compileV2Schema schema + let orderType = types |> List.find(fun t -> t.Name = "Order") + + getProp orderType "Id" + |> (fun p -> p.PropertyType) + |> shouldEqual typeof + +[] +let ``v2 optional property compiles to option type``() = + let schema = + """{ + "swagger": "2.0", + "info": { "title": "RequiredTest", "version": "1.0.0" }, + "basePath": "/", + "paths": {}, + "definitions": { + "Order": { + "type": "object", + "required": ["id"], + "properties": { + "id": { "type": "integer" }, + "note": { "type": "string" } + } + } + } +}""" + + let types = compileV2Schema schema + let orderType = types |> List.find(fun t -> t.Name = "Order") + + getProp orderType "Note" + |> (fun p -> p.PropertyType) + |> shouldEqual typeof + +// ── allOf inheritance: $ref + extra properties ───────────────────────────── + +/// Swagger 2.0 allOf inheritance pattern: Dog extends Animal. +/// allOf contains a $ref to Animal and an inline schema with Dog's own properties. +let private v2InheritanceSchema = + """{ + "swagger": "2.0", + "info": { "title": "InheritanceTest", "version": "1.0.0" }, + "basePath": "/", + "paths": {}, + "definitions": { + "Animal": { + "type": "object", + "required": ["name"], + "properties": { + "name": { "type": "string" } + } + }, + "Dog": { + "allOf": [ + { "$ref": "#/definitions/Animal" }, + { + "type": "object", + "properties": { + "breed": { "type": "string" } + } + } + ] + } + } +}""" + +[] +let ``v2 allOf inheritance emits the base type``() = + let types = compileV2Schema v2InheritanceSchema + types |> List.exists(fun t -> t.Name = "Animal") |> shouldEqual true + +[] +let ``v2 allOf inheritance emits the derived type``() = + let types = compileV2Schema v2InheritanceSchema + types |> List.exists(fun t -> t.Name = "Dog") |> shouldEqual true + +[] +let ``v2 allOf inheritance derived type has its own property``() = + let types = compileV2Schema v2InheritanceSchema + let dogType = types |> List.find(fun t -> t.Name = "Dog") + dogType.GetDeclaredProperty("Breed") |> isNull |> shouldEqual false + // ── ToString tests ─────────────────────────────────────────────────────────── [] diff --git a/tests/SwaggerProvider.Tests/Schema.V3SchemaCompilationTests.fs b/tests/SwaggerProvider.Tests/Schema.V3SchemaCompilationTests.fs index b260e19b..c4d8c088 100644 --- a/tests/SwaggerProvider.Tests/Schema.V3SchemaCompilationTests.fs +++ b/tests/SwaggerProvider.Tests/Schema.V3SchemaCompilationTests.fs @@ -366,3 +366,132 @@ let ``property referencing an additionalProperties schema has Map type``() = // collection types naturally express absence via null/empty. let propType = dataProp.PropertyType propType |> shouldEqual typeof> + +// ── oneOf / anyOf with multiple $refs → no collapse, named object type ──────── + +/// OpenAPI 3.0 schema where Union uses oneOf with two $refs. +/// The single-$ref collapse guard (Count = 1) does not fire, so the compiler +/// falls through to compileNewObject() and emits an empty named object type. +let private oneOfMultiRefSchema = + """{ + "openapi": "3.0.0", + "info": { "title": "Test", "version": "1.0.0" }, + "paths": {}, + "components": { + "schemas": { + "Cat": { + "type": "object", + "properties": { "meow": { "type": "string" } } + }, + "Dog": { + "type": "object", + "properties": { "bark": { "type": "string" } } + }, + "Union": { + "oneOf": [ + { "$ref": "#/components/schemas/Cat" }, + { "$ref": "#/components/schemas/Dog" } + ] + } + } + } +}""" + +[] +let ``oneOf with multiple refs does not emit a named wrapper type``() = + // With no own properties and no allOf, compileNewObject() marks Union as a name alias + // and returns typeof — no separate named type is emitted for the wrapper. + let types = compileV3Schema oneOfMultiRefSchema false + types |> List.exists(fun t -> t.Name = "Union") |> shouldEqual false + +[] +let ``oneOf with multiple refs emits the referenced component types``() = + let types = compileV3Schema oneOfMultiRefSchema false + types |> List.exists(fun t -> t.Name = "Cat") |> shouldEqual true + types |> List.exists(fun t -> t.Name = "Dog") |> shouldEqual true + +/// OpenAPI 3.0 schema where AnyUnion uses anyOf with two $refs. +let private anyOfMultiRefSchema = + """{ + "openapi": "3.0.0", + "info": { "title": "Test", "version": "1.0.0" }, + "paths": {}, + "components": { + "schemas": { + "Fish": { + "type": "object", + "properties": { "fins": { "type": "integer" } } + }, + "Bird": { + "type": "object", + "properties": { "wings": { "type": "integer" } } + }, + "AnyUnion": { + "anyOf": [ + { "$ref": "#/components/schemas/Fish" }, + { "$ref": "#/components/schemas/Bird" } + ] + } + } + } +}""" + +[] +let ``anyOf with multiple refs does not emit a named wrapper type``() = + // Same behaviour as oneOf multi-ref: no own properties → compileNewObject() marks + // AnyUnion as a name alias and returns typeof. + let types = compileV3Schema anyOfMultiRefSchema false + + types + |> List.exists(fun t -> t.Name = "AnyUnion") + |> shouldEqual false + +[] +let ``anyOf with multiple refs emits the referenced component types``() = + let types = compileV3Schema anyOfMultiRefSchema false + types |> List.exists(fun t -> t.Name = "Fish") |> shouldEqual true + types |> List.exists(fun t -> t.Name = "Bird") |> shouldEqual true + +// ── allOf single $ref with extra wrapper properties → new object type ───────── + +/// OpenAPI 3.0 schema where Extended wraps Base via allOf with a single $ref, +/// but also declares its own extra property. The single-$ref collapse guard +/// requires Properties.Count = 0; here it has extra properties, so it fires +/// compileNewObject() instead and emits Extended as a new object type. +let private allOfSingleRefWithExtraPropsSchema = + """{ + "openapi": "3.0.0", + "info": { "title": "Test", "version": "1.0.0" }, + "paths": {}, + "components": { + "schemas": { + "Base": { + "type": "object", + "properties": { + "id": { "type": "integer" } + } + }, + "Extended": { + "allOf": [ { "$ref": "#/components/schemas/Base" } ], + "properties": { + "extra": { "type": "string" } + } + } + } + } +}""" + +[] +let ``allOf single ref with extra wrapper properties emits a new named type``() = + // Because Extended has its own properties, the collapse guard fails and a new object type is emitted. + let types = compileV3Schema allOfSingleRefWithExtraPropsSchema false + types |> List.exists(fun t -> t.Name = "Extended") |> shouldEqual true + +[] +let ``allOf single ref with extra wrapper properties emits the extra property``() = + let types = compileV3Schema allOfSingleRefWithExtraPropsSchema false + let extendedType = types |> List.find(fun t -> t.Name = "Extended") + + extendedType.GetDeclaredProperty("Extra") + |> isNull + |> shouldEqual false From 9eaeb0883ad73545062abfc9e2bc03e81e399a87 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 22 May 2026 20:12:43 +0000 Subject: [PATCH 2/3] ci: trigger checks From 51e6a8fae5b7086c60e7cdb28e934d8762d3a4bf Mon Sep 17 00:00:00 2001 From: Sergey Tihon Date: Fri, 22 May 2026 22:36:55 +0200 Subject: [PATCH 3/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../Schema.V3SchemaCompilationTests.fs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/SwaggerProvider.Tests/Schema.V3SchemaCompilationTests.fs b/tests/SwaggerProvider.Tests/Schema.V3SchemaCompilationTests.fs index c4d8c088..df8f4f82 100644 --- a/tests/SwaggerProvider.Tests/Schema.V3SchemaCompilationTests.fs +++ b/tests/SwaggerProvider.Tests/Schema.V3SchemaCompilationTests.fs @@ -367,11 +367,11 @@ let ``property referencing an additionalProperties schema has Map type``() = let propType = dataProp.PropertyType propType |> shouldEqual typeof> -// ── oneOf / anyOf with multiple $refs → no collapse, named object type ──────── +// ── oneOf / anyOf with multiple $refs → name alias (no wrapper type) ───────── -/// OpenAPI 3.0 schema where Union uses oneOf with two $refs. -/// The single-$ref collapse guard (Count = 1) does not fire, so the compiler -/// falls through to compileNewObject() and emits an empty named object type. +/// OpenAPI 3.0 schema where Union uses oneOf with two $refs and no own properties. +/// Because OneOf.Count <> 1, the single-$ref collapse guard does not fire. +/// compileNewObject() then marks Union as a name alias and returns typeof, so no wrapper type is emitted. let private oneOfMultiRefSchema = """{ "openapi": "3.0.0",