From 54ebe440ad4ce52425e16d3cd33b6dc3d14121e6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Oct 2025 18:08:56 +0000 Subject: [PATCH 1/7] Initial plan From aebb9dbf540cc168f3cc9a31b183bc813d30902b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Oct 2025 18:18:39 +0000 Subject: [PATCH 2/7] Add encoding Dictionary field to OpenApiEncoding class for OpenAPI 3.2 support Co-authored-by: baywet <7905502+baywet@users.noreply.github.com> --- .../Models/OpenApiEncoding.cs | 11 +++ .../Reader/V32/OpenApiEncodingDeserializer.cs | 6 ++ .../V32Tests/OpenApiEncodingTests.cs | 22 +++++ .../encodingWithNestedEncoding.yaml | 14 +++ .../Models/OpenApiEncodingTests.cs | 92 +++++++++++++++++++ .../PublicApi/PublicApi.approved.txt | 1 + 6 files changed, 146 insertions(+) create mode 100644 test/Microsoft.OpenApi.Readers.Tests/V32Tests/Samples/OpenApiEncoding/encodingWithNestedEncoding.yaml diff --git a/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs b/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs index f5df9026f..b603177c8 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; namespace Microsoft.OpenApi { @@ -27,6 +28,12 @@ public class OpenApiEncoding : IOpenApiSerializable, IOpenApiExtensible /// public IDictionary? Headers { get; set; } + /// + /// A map of property names to their encoding information. + /// The key is the property name and the value is the encoding object. + /// + public IDictionary? Encoding { get; set; } + /// /// Describes how a specific property value will be serialized depending on its type. /// @@ -70,6 +77,7 @@ public OpenApiEncoding(OpenApiEncoding encoding) { ContentType = encoding?.ContentType ?? ContentType; Headers = encoding?.Headers != null ? new Dictionary(encoding.Headers) : null; + Encoding = encoding?.Encoding != null ? new Dictionary(encoding.Encoding.ToDictionary(kvp => kvp.Key, kvp => new OpenApiEncoding(kvp.Value))) : null; Style = encoding?.Style ?? Style; Explode = encoding?._explode; AllowReserved = encoding?.AllowReserved ?? AllowReserved; @@ -119,6 +127,9 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version // headers writer.WriteOptionalMap(OpenApiConstants.Headers, Headers, callback); + // encoding + writer.WriteOptionalMap(OpenApiConstants.Encoding, Encoding, callback); + // style writer.WriteProperty(OpenApiConstants.Style, Style?.GetDisplayName()); diff --git a/src/Microsoft.OpenApi/Reader/V32/OpenApiEncodingDeserializer.cs b/src/Microsoft.OpenApi/Reader/V32/OpenApiEncodingDeserializer.cs index ab2891e38..a5b2340c4 100644 --- a/src/Microsoft.OpenApi/Reader/V32/OpenApiEncodingDeserializer.cs +++ b/src/Microsoft.OpenApi/Reader/V32/OpenApiEncodingDeserializer.cs @@ -22,6 +22,12 @@ internal static partial class OpenApiV32Deserializer o.Headers = n.CreateMap(LoadHeader, t); } }, + { + "encoding", (o, n, t) => + { + o.Encoding = n.CreateMap(LoadEncoding, t); + } + }, { "style", (o, n, _) => { diff --git a/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiEncodingTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiEncodingTests.cs index ac761c991..60fb54529 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiEncodingTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiEncodingTests.cs @@ -4,6 +4,7 @@ using System.IO; using System.Threading.Tasks; using Microsoft.OpenApi.Reader; +using System.Collections.Generic; using Xunit; namespace Microsoft.OpenApi.Readers.Tests.V32Tests @@ -29,5 +30,26 @@ public async Task ParseEncodingWithAllowReservedShouldSucceed() AllowReserved = true }, encoding); } + + [Fact] + public async Task ParseEncodingWithNestedEncodingShouldSucceed() + { + // Act + var encoding = await OpenApiModelFactory.LoadAsync(Path.Combine(SampleFolderPath, "encodingWithNestedEncoding.yaml"), OpenApiSpecVersion.OpenApi3_2, new(), SettingsFixture.ReaderSettings); + + // Assert + Assert.NotNull(encoding); + Assert.Equal("application/json", encoding.ContentType); + Assert.NotNull(encoding.Headers); + Assert.Single(encoding.Headers); + Assert.NotNull(encoding.Encoding); + Assert.Equal(2, encoding.Encoding.Count); + Assert.True(encoding.Encoding.ContainsKey("nestedField")); + Assert.Equal("application/xml", encoding.Encoding["nestedField"].ContentType); + Assert.Equal(ParameterStyle.Form, encoding.Encoding["nestedField"].Style); + Assert.True(encoding.Encoding["nestedField"].Explode); + Assert.True(encoding.Encoding.ContainsKey("anotherField")); + Assert.Equal("text/plain", encoding.Encoding["anotherField"].ContentType); + } } } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V32Tests/Samples/OpenApiEncoding/encodingWithNestedEncoding.yaml b/test/Microsoft.OpenApi.Readers.Tests/V32Tests/Samples/OpenApiEncoding/encodingWithNestedEncoding.yaml new file mode 100644 index 000000000..7a1d51443 --- /dev/null +++ b/test/Microsoft.OpenApi.Readers.Tests/V32Tests/Samples/OpenApiEncoding/encodingWithNestedEncoding.yaml @@ -0,0 +1,14 @@ +# https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.2.0.md#encodingObject +contentType: application/json +headers: + X-Rate-Limit-Limit: + description: The number of allowed requests in the current period + schema: + type: integer +encoding: + nestedField: + contentType: application/xml + style: form + explode: true + anotherField: + contentType: text/plain diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiEncodingTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiEncodingTests.cs index 81d6426ad..ead562f2f 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiEncodingTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiEncodingTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System.Collections.Generic; using System.Threading.Tasks; using Xunit; @@ -126,5 +127,96 @@ public async Task WhenExplodeIsSetOutputShouldHaveExplode(bool? expectedExplode, expected = expected.MakeLineBreaksEnvironmentNeutral(); Assert.Equal(actual, expected); } + + [Fact] + public async Task SerializeEncodingWithNestedEncodingAsV32JsonWorks() + { + // Arrange + var encoding = new OpenApiEncoding + { + ContentType = "application/json", + Encoding = new Dictionary + { + ["nestedField"] = new OpenApiEncoding + { + ContentType = "application/xml", + Style = ParameterStyle.Form, + Explode = true + }, + ["anotherField"] = new OpenApiEncoding + { + ContentType = "text/plain" + } + } + }; + + var expected = + """ + { + "contentType": "application/json", + "encoding": { + "nestedField": { + "contentType": "application/xml", + "style": "form", + "explode": true + }, + "anotherField": { + "contentType": "text/plain" + } + } + } + """; + + // Act + var actual = await encoding.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_2); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + Assert.Equal(expected, actual); + } + + [Fact] + public async Task SerializeEncodingWithNestedEncodingAsV32YamlWorks() + { + // Arrange + var encoding = new OpenApiEncoding + { + ContentType = "application/json", + Encoding = new Dictionary + { + ["nestedField"] = new OpenApiEncoding + { + ContentType = "application/xml", + Style = ParameterStyle.Form, + Explode = true + }, + ["anotherField"] = new OpenApiEncoding + { + ContentType = "text/plain" + } + } + }; + + var expected = + """ + contentType: application/json + encoding: + nestedField: + contentType: application/xml + style: form + explode: true + anotherField: + contentType: text/plain + """; + + // Act + var actual = await encoding.SerializeAsYamlAsync(OpenApiSpecVersion.OpenApi3_2); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + Assert.Equal(expected, actual); + } } } diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index ab6f8d89b..bec292cc9 100644 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -679,6 +679,7 @@ namespace Microsoft.OpenApi public OpenApiEncoding(Microsoft.OpenApi.OpenApiEncoding encoding) { } public bool? AllowReserved { get; set; } public string? ContentType { get; set; } + public System.Collections.Generic.IDictionary? Encoding { get; set; } public bool? Explode { get; set; } public System.Collections.Generic.IDictionary? Extensions { get; set; } public System.Collections.Generic.IDictionary? Headers { get; set; } From a5840145f9304dd441c7f96a9387ba24cc3b0134 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Oct 2025 19:18:02 +0000 Subject: [PATCH 3/7] Add itemEncoding and prefixEncoding properties, fix version checks for encoding serialization Co-authored-by: baywet <7905502+baywet@users.noreply.github.com> --- .../Models/OpenApiEncoding.cs | 52 ++++++++- .../Reader/V32/OpenApiEncodingDeserializer.cs | 12 ++ .../V32Tests/OpenApiEncodingTests.cs | 20 ++++ .../encodingWithItemAndPrefixEncoding.yaml | 10 ++ .../Models/OpenApiEncodingTests.cs | 107 ++++++++++++++++++ .../PublicApi/PublicApi.approved.txt | 2 + 6 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 test/Microsoft.OpenApi.Readers.Tests/V32Tests/Samples/OpenApiEncoding/encodingWithItemAndPrefixEncoding.yaml diff --git a/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs b/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs index b603177c8..00c888c34 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs @@ -34,6 +34,16 @@ public class OpenApiEncoding : IOpenApiSerializable, IOpenApiExtensible /// public IDictionary? Encoding { get; set; } + /// + /// Encoding object for array items. + /// + public OpenApiEncoding? ItemEncoding { get; set; } + + /// + /// Encoding objects for tuple-style arrays. + /// + public IList? PrefixEncoding { get; set; } + /// /// Describes how a specific property value will be serialized depending on its type. /// @@ -78,6 +88,8 @@ public OpenApiEncoding(OpenApiEncoding encoding) ContentType = encoding?.ContentType ?? ContentType; Headers = encoding?.Headers != null ? new Dictionary(encoding.Headers) : null; Encoding = encoding?.Encoding != null ? new Dictionary(encoding.Encoding.ToDictionary(kvp => kvp.Key, kvp => new OpenApiEncoding(kvp.Value))) : null; + ItemEncoding = encoding?.ItemEncoding != null ? new OpenApiEncoding(encoding.ItemEncoding) : null; + PrefixEncoding = encoding?.PrefixEncoding != null ? new List(encoding.PrefixEncoding.Select(e => new OpenApiEncoding(e))) : null; Style = encoding?.Style ?? Style; Explode = encoding?._explode; AllowReserved = encoding?.AllowReserved ?? AllowReserved; @@ -127,8 +139,44 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version // headers writer.WriteOptionalMap(OpenApiConstants.Headers, Headers, callback); - // encoding - writer.WriteOptionalMap(OpenApiConstants.Encoding, Encoding, callback); + // encoding - serialize as native field in v3.2+, as extension in earlier versions + if (Encoding != null) + { + if (version >= OpenApiSpecVersion.OpenApi3_2) + { + writer.WriteOptionalMap(OpenApiConstants.Encoding, Encoding, callback); + } + else + { + writer.WriteOptionalMap(OpenApiConstants.ExtensionFieldNamePrefix + "oai-" + OpenApiConstants.Encoding, Encoding, callback); + } + } + + // itemEncoding - serialize as native field in v3.2+, as extension in earlier versions + if (ItemEncoding != null) + { + if (version >= OpenApiSpecVersion.OpenApi3_2) + { + writer.WriteOptionalObject(OpenApiConstants.ItemEncoding, ItemEncoding, callback); + } + else + { + writer.WriteOptionalObject(OpenApiConstants.ExtensionFieldNamePrefix + "oai-" + OpenApiConstants.ItemEncoding, ItemEncoding, callback); + } + } + + // prefixEncoding - serialize as native field in v3.2+, as extension in earlier versions + if (PrefixEncoding != null) + { + if (version >= OpenApiSpecVersion.OpenApi3_2) + { + writer.WriteOptionalCollection(OpenApiConstants.PrefixEncoding, PrefixEncoding, callback); + } + else + { + writer.WriteOptionalCollection(OpenApiConstants.ExtensionFieldNamePrefix + "oai-" + OpenApiConstants.PrefixEncoding, PrefixEncoding, callback); + } + } // style writer.WriteProperty(OpenApiConstants.Style, Style?.GetDisplayName()); diff --git a/src/Microsoft.OpenApi/Reader/V32/OpenApiEncodingDeserializer.cs b/src/Microsoft.OpenApi/Reader/V32/OpenApiEncodingDeserializer.cs index a5b2340c4..8f2777961 100644 --- a/src/Microsoft.OpenApi/Reader/V32/OpenApiEncodingDeserializer.cs +++ b/src/Microsoft.OpenApi/Reader/V32/OpenApiEncodingDeserializer.cs @@ -28,6 +28,18 @@ internal static partial class OpenApiV32Deserializer o.Encoding = n.CreateMap(LoadEncoding, t); } }, + { + "itemEncoding", (o, n, t) => + { + o.ItemEncoding = LoadEncoding(n, t); + } + }, + { + "prefixEncoding", (o, n, t) => + { + o.PrefixEncoding = n.CreateList(LoadEncoding, t); + } + }, { "style", (o, n, _) => { diff --git a/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiEncodingTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiEncodingTests.cs index 60fb54529..166f6fd12 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiEncodingTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiEncodingTests.cs @@ -51,5 +51,25 @@ public async Task ParseEncodingWithNestedEncodingShouldSucceed() Assert.True(encoding.Encoding.ContainsKey("anotherField")); Assert.Equal("text/plain", encoding.Encoding["anotherField"].ContentType); } + + [Fact] + public async Task ParseEncodingWithItemAndPrefixEncodingShouldSucceed() + { + // Act + var encoding = await OpenApiModelFactory.LoadAsync(Path.Combine(SampleFolderPath, "encodingWithItemAndPrefixEncoding.yaml"), OpenApiSpecVersion.OpenApi3_2, new(), SettingsFixture.ReaderSettings); + + // Assert + Assert.NotNull(encoding); + Assert.Equal("application/json", encoding.ContentType); + Assert.NotNull(encoding.ItemEncoding); + Assert.Equal("application/xml", encoding.ItemEncoding.ContentType); + Assert.Equal(ParameterStyle.Form, encoding.ItemEncoding.Style); + Assert.True(encoding.ItemEncoding.Explode); + Assert.NotNull(encoding.PrefixEncoding); + Assert.Equal(2, encoding.PrefixEncoding.Count); + Assert.Equal("text/plain", encoding.PrefixEncoding[0].ContentType); + Assert.Equal(ParameterStyle.Simple, encoding.PrefixEncoding[0].Style); + Assert.Equal("application/octet-stream", encoding.PrefixEncoding[1].ContentType); + } } } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V32Tests/Samples/OpenApiEncoding/encodingWithItemAndPrefixEncoding.yaml b/test/Microsoft.OpenApi.Readers.Tests/V32Tests/Samples/OpenApiEncoding/encodingWithItemAndPrefixEncoding.yaml new file mode 100644 index 000000000..b2f3d4e12 --- /dev/null +++ b/test/Microsoft.OpenApi.Readers.Tests/V32Tests/Samples/OpenApiEncoding/encodingWithItemAndPrefixEncoding.yaml @@ -0,0 +1,10 @@ +# https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.2.0.md#encodingObject +contentType: application/json +itemEncoding: + contentType: application/xml + style: form + explode: true +prefixEncoding: + - contentType: text/plain + style: simple + - contentType: application/octet-stream diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiEncodingTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiEncodingTests.cs index ead562f2f..8bc84018a 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiEncodingTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiEncodingTests.cs @@ -218,5 +218,112 @@ public async Task SerializeEncodingWithNestedEncodingAsV32YamlWorks() expected = expected.MakeLineBreaksEnvironmentNeutral(); Assert.Equal(expected, actual); } + + [Fact] + public async Task SerializeEncodingWithItemAndPrefixEncodingAsV32JsonWorks() + { + // Arrange + var encoding = new OpenApiEncoding + { + ContentType = "application/json", + ItemEncoding = new OpenApiEncoding + { + ContentType = "application/xml", + Style = ParameterStyle.Form, + Explode = true + }, + PrefixEncoding = new List + { + new OpenApiEncoding + { + ContentType = "text/plain", + Style = ParameterStyle.Simple + }, + new OpenApiEncoding + { + ContentType = "application/octet-stream" + } + } + }; + + var expected = + """ + { + "contentType": "application/json", + "itemEncoding": { + "contentType": "application/xml", + "style": "form", + "explode": true + }, + "prefixEncoding": [ + { + "contentType": "text/plain", + "style": "simple" + }, + { + "contentType": "application/octet-stream" + } + ] + } + """; + + // Act + var actual = await encoding.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_2); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + Assert.Equal(expected, actual); + } + + [Fact] + public async Task SerializeEncodingAsV31ShouldUseExtensionsForNewFields() + { + // Arrange + var encoding = new OpenApiEncoding + { + ContentType = "application/json", + Encoding = new Dictionary + { + ["nested"] = new OpenApiEncoding { ContentType = "text/plain" } + }, + ItemEncoding = new OpenApiEncoding + { + ContentType = "application/xml" + }, + PrefixEncoding = new List + { + new OpenApiEncoding { ContentType = "text/csv" } + } + }; + + var expected = + """ + { + "contentType": "application/json", + "x-oai-encoding": { + "nested": { + "contentType": "text/plain" + } + }, + "x-oai-itemEncoding": { + "contentType": "application/xml" + }, + "x-oai-prefixEncoding": [ + { + "contentType": "text/csv" + } + ] + } + """; + + // Act + var actual = await encoding.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_1); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + Assert.Equal(expected, actual); + } } } diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index bec292cc9..4570515c6 100644 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -683,6 +683,8 @@ namespace Microsoft.OpenApi public bool? Explode { get; set; } public System.Collections.Generic.IDictionary? Extensions { get; set; } public System.Collections.Generic.IDictionary? Headers { get; set; } + public Microsoft.OpenApi.OpenApiEncoding? ItemEncoding { get; set; } + public System.Collections.Generic.IList? PrefixEncoding { get; set; } public Microsoft.OpenApi.ParameterStyle? Style { get; set; } public virtual void SerializeAsV2(Microsoft.OpenApi.IOpenApiWriter writer) { } public virtual void SerializeAsV3(Microsoft.OpenApi.IOpenApiWriter writer) { } From 4c8a3809e41d3f391e4b17380c656ea9d25320d0 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 9 Oct 2025 19:18:26 -0400 Subject: [PATCH 4/7] Apply suggestions from code review --- src/Microsoft.OpenApi/Models/OpenApiEncoding.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs b/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs index 00c888c34..367107a52 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs @@ -87,9 +87,9 @@ public OpenApiEncoding(OpenApiEncoding encoding) { ContentType = encoding?.ContentType ?? ContentType; Headers = encoding?.Headers != null ? new Dictionary(encoding.Headers) : null; - Encoding = encoding?.Encoding != null ? new Dictionary(encoding.Encoding.ToDictionary(kvp => kvp.Key, kvp => new OpenApiEncoding(kvp.Value))) : null; + Encoding = encoding?.Encoding != null ? new Dictionary(encoding.Encoding, StringComparer.Ordinal) : null; ItemEncoding = encoding?.ItemEncoding != null ? new OpenApiEncoding(encoding.ItemEncoding) : null; - PrefixEncoding = encoding?.PrefixEncoding != null ? new List(encoding.PrefixEncoding.Select(e => new OpenApiEncoding(e))) : null; + PrefixEncoding = encoding?.PrefixEncoding != null ? new List(encoding.PrefixEncoding) : null; Style = encoding?.Style ?? Style; Explode = encoding?._explode; AllowReserved = encoding?.AllowReserved ?? AllowReserved; From 865295ade7bc7a8742981e0377d893fd447a56bf Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 9 Oct 2025 19:22:02 -0400 Subject: [PATCH 5/7] Apply suggestions from code review --- .../Models/OpenApiEncodingTests.cs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiEncodingTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiEncodingTests.cs index 8bc84018a..4b44fd3e0 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiEncodingTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiEncodingTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System.Collections.Generic; +using System.Text.Json.Nodes; using System.Threading.Tasks; using Xunit; @@ -171,9 +172,7 @@ public async Task SerializeEncodingWithNestedEncodingAsV32JsonWorks() var actual = await encoding.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_2); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.Equal(expected, actual); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(actual), JsonNode.Parse(expected))); } [Fact] @@ -214,9 +213,7 @@ public async Task SerializeEncodingWithNestedEncodingAsV32YamlWorks() var actual = await encoding.SerializeAsYamlAsync(OpenApiSpecVersion.OpenApi3_2); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.Equal(expected, actual); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(actual), JsonNode.Parse(expected))); } [Fact] @@ -271,9 +268,7 @@ public async Task SerializeEncodingWithItemAndPrefixEncodingAsV32JsonWorks() var actual = await encoding.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_2); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.Equal(expected, actual); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(actual), JsonNode.Parse(expected))); } [Fact] @@ -321,9 +316,7 @@ public async Task SerializeEncodingAsV31ShouldUseExtensionsForNewFields() var actual = await encoding.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_1); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.Equal(expected, actual); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(actual), JsonNode.Parse(expected))); } } } From 54f10eb7a466b65004b48617f0c6286fd51e5507 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 9 Oct 2025 19:25:07 -0400 Subject: [PATCH 6/7] test: reverts yaml parsing to json --- test/Microsoft.OpenApi.Tests/Models/OpenApiEncodingTests.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiEncodingTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiEncodingTests.cs index 4b44fd3e0..a1280d48e 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiEncodingTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiEncodingTests.cs @@ -213,7 +213,9 @@ public async Task SerializeEncodingWithNestedEncodingAsV32YamlWorks() var actual = await encoding.SerializeAsYamlAsync(OpenApiSpecVersion.OpenApi3_2); // Assert - Assert.True(JsonNode.DeepEquals(JsonNode.Parse(actual), JsonNode.Parse(expected))); + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + Assert.Equal(actual, expected); } [Fact] From 82552fe2723a70e2e73b342d5778f667256d8989 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 10 Oct 2025 08:24:50 -0400 Subject: [PATCH 7/7] chore: refreshes benchmarks --- .../performance.Descriptions-report-github.md | 12 ++-- .../performance.Descriptions-report.csv | 8 +-- .../performance.Descriptions-report.html | 12 ++-- .../performance.Descriptions-report.json | 2 +- .../performance.EmptyModels-report-github.md | 60 +++++++++---------- .../performance.EmptyModels-report.csv | 58 +++++++++--------- .../performance.EmptyModels-report.html | 60 +++++++++---------- .../performance.EmptyModels-report.json | 2 +- 8 files changed, 107 insertions(+), 107 deletions(-) diff --git a/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report-github.md b/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report-github.md index 51834bbcf..a03160f95 100644 --- a/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report-github.md +++ b/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report-github.md @@ -10,9 +10,9 @@ Job=ShortRun IterationCount=3 LaunchCount=1 WarmupCount=3 ``` -| Method | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | -|------------- |---------------:|-------------:|-------------:|-----------:|-----------:|----------:|-------------:| -| PetStoreYaml | 525.7 μs | 1,392.5 μs | 76.33 μs | 62.5000 | 11.7188 | - | 387.72 KB | -| PetStoreJson | 241.1 μs | 706.2 μs | 38.71 μs | 40.0391 | 8.7891 | - | 249.86 KB | -| GHESYaml | 1,126,435.6 μs | 361,261.8 μs | 19,801.98 μs | 66000.0000 | 22000.0000 | 4000.0000 | 384551.24 KB | -| GHESJson | 970,225.6 μs | 187,943.8 μs | 10,301.84 μs | 40000.0000 | 16000.0000 | 3000.0000 | 246022.93 KB | +| Method | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | +|------------- |---------------:|---------------:|-------------:|-----------:|-----------:|----------:|-------------:| +| PetStoreYaml | 524.4 μs | 176.0 μs | 9.65 μs | 62.5000 | 11.7188 | - | 387.72 KB | +| PetStoreJson | 214.4 μs | 531.5 μs | 29.13 μs | 40.0391 | 8.7891 | - | 249.86 KB | +| GHESYaml | 1,118,727.3 μs | 1,445,393.2 μs | 79,226.88 μs | 66000.0000 | 22000.0000 | 4000.0000 | 384551.19 KB | +| GHESJson | 586,923.9 μs | 475,182.0 μs | 26,046.33 μs | 40000.0000 | 16000.0000 | 3000.0000 | 246021.88 KB | diff --git a/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report.csv b/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report.csv index 2d7c82729..3617b70f1 100644 --- a/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report.csv +++ b/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report.csv @@ -1,5 +1,5 @@ Method,Job,AnalyzeLaunchVariance,EvaluateOverhead,MaxAbsoluteError,MaxRelativeError,MinInvokeCount,MinIterationTime,OutlierMode,Affinity,EnvironmentVariables,Jit,LargeAddressAware,Platform,PowerPlanMode,Runtime,AllowVeryLargeObjects,Concurrent,CpuGroups,Force,HeapAffinitizeMask,HeapCount,NoAffinitize,RetainVm,Server,Arguments,BuildConfiguration,Clock,EngineFactory,NuGetReferences,Toolchain,IsMutator,InvocationCount,IterationCount,IterationTime,LaunchCount,MaxIterationCount,MaxWarmupIterationCount,MemoryRandomization,MinIterationCount,MinWarmupIterationCount,RunStrategy,UnrollFactor,WarmupCount,Mean,Error,StdDev,Gen0,Gen1,Gen2,Allocated -PetStoreYaml,ShortRun,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,3,Default,1,Default,Default,Default,Default,Default,Default,16,3,525.7 μs,"1,392.5 μs",76.33 μs,62.5000,11.7188,0.0000,387.72 KB -PetStoreJson,ShortRun,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,3,Default,1,Default,Default,Default,Default,Default,Default,16,3,241.1 μs,706.2 μs,38.71 μs,40.0391,8.7891,0.0000,249.86 KB -GHESYaml,ShortRun,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,3,Default,1,Default,Default,Default,Default,Default,Default,16,3,"1,126,435.6 μs","361,261.8 μs","19,801.98 μs",66000.0000,22000.0000,4000.0000,384551.24 KB -GHESJson,ShortRun,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,3,Default,1,Default,Default,Default,Default,Default,Default,16,3,"970,225.6 μs","187,943.8 μs","10,301.84 μs",40000.0000,16000.0000,3000.0000,246022.93 KB +PetStoreYaml,ShortRun,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,3,Default,1,Default,Default,Default,Default,Default,Default,16,3,524.4 μs,176.0 μs,9.65 μs,62.5000,11.7188,0.0000,387.72 KB +PetStoreJson,ShortRun,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,3,Default,1,Default,Default,Default,Default,Default,Default,16,3,214.4 μs,531.5 μs,29.13 μs,40.0391,8.7891,0.0000,249.86 KB +GHESYaml,ShortRun,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,3,Default,1,Default,Default,Default,Default,Default,Default,16,3,"1,118,727.3 μs","1,445,393.2 μs","79,226.88 μs",66000.0000,22000.0000,4000.0000,384551.19 KB +GHESJson,ShortRun,False,Default,Default,Default,Default,Default,Default,11111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,3,Default,1,Default,Default,Default,Default,Default,Default,16,3,"586,923.9 μs","475,182.0 μs","26,046.33 μs",40000.0000,16000.0000,3000.0000,246021.88 KB diff --git a/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report.html b/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report.html index f76302db4..2acff85ed 100644 --- a/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report.html +++ b/performance/benchmark/BenchmarkDotNet.Artifacts/results/performance.Descriptions-report.html @@ -2,7 +2,7 @@ -performance.Descriptions-20251003-105951 +performance.Descriptions-20251010-081406