diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 87b987112..c3da60bcb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -16,6 +16,9 @@ updates: MicrosoftExtensions: patterns: - "Microsoft.Extensions.*" + coverlet: + patterns: + - "coverlet.*" - package-ecosystem: dotnet-sdk directory: / schedule: diff --git a/.github/workflows/promote-shipped-apis.yml b/.github/workflows/promote-shipped-apis.yml index aa611b7a0..d3f2df683 100644 --- a/.github/workflows/promote-shipped-apis.yml +++ b/.github/workflows/promote-shipped-apis.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Generate GitHub App token id: app-token - uses: actions/create-github-app-token@v2 + uses: actions/create-github-app-token@v3 with: app-id: ${{ vars.RELEASE_PLEASE_TOKEN_PROVIDER_APP_ID }} private-key: ${{ secrets.RELEASE_PLEASE_TOKEN_PROVIDER_PEM }} diff --git a/.github/workflows/release-please-gha.yml b/.github/workflows/release-please-gha.yml index 6e2c9825d..f20403d3a 100644 --- a/.github/workflows/release-please-gha.yml +++ b/.github/workflows/release-please-gha.yml @@ -53,7 +53,7 @@ jobs: - name: Generate GitHub App token id: app-token - uses: actions/create-github-app-token@v2 + uses: actions/create-github-app-token@v3 with: app-id: ${{ vars.RELEASE_PLEASE_TOKEN_PROVIDER_APP_ID }} private-key: ${{ secrets.RELEASE_PLEASE_TOKEN_PROVIDER_PEM }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2437b4193..bf0d03617 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.4.0" + ".": "3.5.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 033c3f970..e0cc37e18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## [3.5.0](https://github.com/microsoft/OpenAPI.NET/compare/v3.4.0...v3.5.0) (2026-03-20) + + +### Features + +* **library:** add Extensions support for schema references in v3.1/v3.2; add SerializeAsV32 with loop detection ([9b422bf](https://github.com/microsoft/OpenAPI.NET/commit/9b422bfbe1459588821ab011ba470336c63e4958)) + + +### Bug Fixes + +* a bug where path parameter validation would fail if they contained forbidden JSON pointer characters ([ef55b2c](https://github.com/microsoft/OpenAPI.NET/commit/ef55b2ce664014a854dbd4275e3fed783bc53385)) +* a bug where path parameter validation would fail if they contained forbidden JSON pointer characters ([4b3164a](https://github.com/microsoft/OpenAPI.NET/commit/4b3164a76b316951cb043aebf950663a17e51c90)) +* double encoding of json pointer for invalid reference rule ([b246cd0](https://github.com/microsoft/OpenAPI.NET/commit/b246cd0bafde78892aa22e6ffe046cf5a438eb7d)) +* encoding of special characters for JSON paths ([4c757e1](https://github.com/microsoft/OpenAPI.NET/commit/4c757e1e9806f95ed598f349e4fc35a0307b9342)) +* **library:** do not emit unevaluatedProperties for non-object schemas ([852fb4c](https://github.com/microsoft/OpenAPI.NET/commit/852fb4c744c51099a629d90797a2803d3bbdfc2a)) +* **library:** do not emit unevaluatedProperties for non-object schemas ([19538aa](https://github.com/microsoft/OpenAPI.NET/commit/19538aa169b708dac3baf04a1315bed1dd25eac6)) +* **library:** enforce spec-compliant $ref serialization; add Extensions support for schema references in v3.1/v3.2 ([9bf61de](https://github.com/microsoft/OpenAPI.NET/commit/9bf61de9c10092f744ab38fe0a41bd6adb8351d4)) +* potential double encoding of paths ([471a61a](https://github.com/microsoft/OpenAPI.NET/commit/471a61a2567bb517ede94f0bec1a53ef806b2db3)) + ## [3.4.0](https://github.com/microsoft/OpenAPI.NET/compare/v3.3.1...v3.4.0) (2026-03-04) diff --git a/Directory.Build.props b/Directory.Build.props index 9129d4c5e..034c59b03 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -12,13 +12,13 @@ https://github.com/Microsoft/OpenAPI.NET © Microsoft Corporation. All rights reserved. OpenAPI .NET - 3.4.0 + 3.5.0 true - + \ No newline at end of file diff --git a/global.json b/global.json index 459fe3e6e..d72c3f669 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "8.0.418" + "version": "8.0.419" } } \ No newline at end of file diff --git a/performance/resultsComparer/resultsComparer.csproj b/performance/resultsComparer/resultsComparer.csproj index 892a4b627..847f570d5 100644 --- a/performance/resultsComparer/resultsComparer.csproj +++ b/performance/resultsComparer/resultsComparer.csproj @@ -8,12 +8,12 @@ - - - - - - + + + + + + diff --git a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj index 475f4ce67..3352b5a21 100644 --- a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj +++ b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj @@ -29,17 +29,17 @@ - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + - + diff --git a/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj b/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj index f00b8d79b..89cf78d37 100644 --- a/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj +++ b/src/Microsoft.OpenApi.Workbench/Microsoft.OpenApi.Workbench.csproj @@ -25,7 +25,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/src/Microsoft.OpenApi/Models/JsonSchemaReference.cs b/src/Microsoft.OpenApi/Models/JsonSchemaReference.cs index 933a4a85b..2893a7d22 100644 --- a/src/Microsoft.OpenApi/Models/JsonSchemaReference.cs +++ b/src/Microsoft.OpenApi/Models/JsonSchemaReference.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -52,6 +52,12 @@ public class JsonSchemaReference : OpenApiReferenceWithDescription /// public IList? Examples { get; set; } + /// + /// Extension data for this schema reference. Only allowed in OpenAPI 3.1 and later. + /// Extensions are NOT written when serializing for OpenAPI 2.0 or 3.0. + /// + public IDictionary? Extensions { get; set; } + /// /// Parameterless constructor /// @@ -69,6 +75,7 @@ public JsonSchemaReference(JsonSchemaReference reference) : base(reference) ReadOnly = reference.ReadOnly; WriteOnly = reference.WriteOnly; Examples = reference.Examples; + Extensions = reference.Extensions != null ? new Dictionary(reference.Extensions) : null; } /// @@ -106,6 +113,7 @@ private void SerializeAdditionalV3XProperties(IOpenApiWriter writer, Action w.WriteAny(e)); } + writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_1); } /// @@ -146,5 +154,15 @@ protected override void SetAdditional31MetadataFromMapNode(JsonObject jsonObject { Examples = examplesArray.OfType().ToList(); } + + // Extensions (properties starting with "x-") + foreach (var property in jsonObject + .Where(static p => p.Key.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix, StringComparison.OrdinalIgnoreCase) + && p.Value is not null)) + { + var extensionValue = property.Value!; + Extensions ??= new Dictionary(StringComparer.OrdinalIgnoreCase); + Extensions[property.Key] = new JsonNodeExtension(extensionValue.DeepClone()); + } } } diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs index 795ee18ee..11252d3c4 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs @@ -547,19 +547,24 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version // For versions < 3.1, write unevaluatedProperties as an extension if (version < OpenApiSpecVersion.OpenApi3_1) { - // Write UnevaluatedPropertiesSchema as extension if present - if (UnevaluatedPropertiesSchema is not null) + // Only emit unevaluatedProperties when the type could include objects. + // Skip when type is explicitly set to a non-object type (array, string, number, integer, boolean, null). + if (!Type.HasValue || (Type.Value & JsonSchemaType.Object) != 0) { - writer.WriteOptionalObject( - OpenApiConstants.UnevaluatedPropertiesExtension, - UnevaluatedPropertiesSchema, - callback); - } - // Write boolean false as extension if explicitly set to false - else if (!UnevaluatedProperties) - { - writer.WritePropertyName(OpenApiConstants.UnevaluatedPropertiesExtension); - writer.WriteValue(false); + // Write UnevaluatedPropertiesSchema as extension if present + if (UnevaluatedPropertiesSchema is not null) + { + writer.WriteOptionalObject( + OpenApiConstants.UnevaluatedPropertiesExtension, + UnevaluatedPropertiesSchema, + callback); + } + // Write boolean false as extension if explicitly set to false + else if (!UnevaluatedProperties) + { + writer.WritePropertyName(OpenApiConstants.UnevaluatedPropertiesExtension); + writer.WriteValue(false); + } } // Write patternProperties as an extension @@ -599,19 +604,23 @@ internal void WriteJsonSchemaKeywords(IOpenApiWriter writer) writer.WriteProperty(OpenApiConstants.DynamicRef, DynamicRef); writer.WriteProperty(OpenApiConstants.DynamicAnchor, DynamicAnchor); - // UnevaluatedProperties: similar to AdditionalProperties, serialize as schema if present, else as boolean - if (UnevaluatedPropertiesSchema is not null) - { - writer.WriteOptionalObject( - OpenApiConstants.UnevaluatedProperties, - UnevaluatedPropertiesSchema, - (w, s) => s.SerializeAsV31(w)); - } - else if (!UnevaluatedProperties) + // UnevaluatedProperties: similar to AdditionalProperties, serialize as schema if present, else as boolean. + // Only emit when the type could include objects. + // Skip when type is explicitly set to a non-object type (array, string, number, integer, boolean, null). + if (!Type.HasValue || (Type.Value & JsonSchemaType.Object) != 0) { - writer.WriteProperty(OpenApiConstants.UnevaluatedProperties, UnevaluatedProperties); + if (UnevaluatedPropertiesSchema is not null) + { + writer.WriteOptionalObject( + OpenApiConstants.UnevaluatedProperties, + UnevaluatedPropertiesSchema, + (w, s) => s.SerializeAsV31(w)); + } + else if (!UnevaluatedProperties) + { + writer.WriteProperty(OpenApiConstants.UnevaluatedProperties, UnevaluatedProperties); + } } - // true is the default, no need to write it out writer.WriteOptionalCollection(OpenApiConstants.Examples, Examples, (nodeWriter, s) => nodeWriter.WriteAny(s)); writer.WriteOptionalMap(OpenApiConstants.PatternProperties, PatternProperties, (w, s) => s.SerializeAsV31(w)); writer.WriteOptionalMap(OpenApiConstants.DependentRequired, DependentRequired, (w, s) => w.WriteValue(s)); @@ -832,19 +841,24 @@ private void SerializeAsV2( // x-nullable extension SerializeNullable(writer, OpenApiSpecVersion.OpenApi2_0); - // Write UnevaluatedPropertiesSchema as extension if present - if (UnevaluatedPropertiesSchema is not null) + // Write UnevaluatedPropertiesSchema as extension if present. + // Only emit when the type could include objects. + // Skip when type is explicitly set to a non-object type (array, string, number, integer, boolean, null). + if (!Type.HasValue || (Type.Value & JsonSchemaType.Object) != 0) { - writer.WriteOptionalObject( - OpenApiConstants.UnevaluatedPropertiesExtension, - UnevaluatedPropertiesSchema, - (w, s) => s.SerializeAsV2(w)); - } - // Write boolean false as extension if explicitly set to false - else if (!UnevaluatedProperties) - { - writer.WritePropertyName(OpenApiConstants.UnevaluatedPropertiesExtension); - writer.WriteValue(false); + if (UnevaluatedPropertiesSchema is not null) + { + writer.WriteOptionalObject( + OpenApiConstants.UnevaluatedPropertiesExtension, + UnevaluatedPropertiesSchema, + (w, s) => s.SerializeAsV2(w)); + } + // Write boolean false as extension if explicitly set to false + else if (!UnevaluatedProperties) + { + writer.WritePropertyName(OpenApiConstants.UnevaluatedPropertiesExtension); + writer.WriteValue(false); + } } // Write patternProperties as an extension diff --git a/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs b/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs index 55be30a51..67eb79645 100644 --- a/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs +++ b/src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs @@ -10,7 +10,7 @@ namespace Microsoft.OpenApi /// /// Schema reference object /// - public class OpenApiSchemaReference : BaseOpenApiReferenceHolder, IOpenApiSchema, IOpenApiSchemaWithUnevaluatedProperties + public class OpenApiSchemaReference : BaseOpenApiReferenceHolder, IOpenApiSchema, IOpenApiSchemaWithUnevaluatedProperties, IOpenApiExtensible { /// @@ -158,7 +158,11 @@ public bool Deprecated /// public OpenApiXml? Xml { get => Target?.Xml; } /// - public IDictionary? Extensions { get => Target?.Extensions; } + public IDictionary? Extensions + { + get => Reference.Extensions ?? Target?.Extensions; + set => Reference.Extensions = value; + } /// public IDictionary? UnrecognizedKeywords { get => Target?.UnrecognizedKeywords; } @@ -172,6 +176,12 @@ public override void SerializeAsV31(IOpenApiWriter writer) SerializeAsWithoutLoops(writer, (w, element) => (element is IOpenApiSchema s ? CopyReferenceAsTargetElementWithOverrides(s) : element).SerializeAsV31(w)); } + /// + public override void SerializeAsV32(IOpenApiWriter writer) + { + SerializeAsWithoutLoops(writer, (w, element) => (element is IOpenApiSchema s ? CopyReferenceAsTargetElementWithOverrides(s) : element).SerializeAsV32(w)); + } + /// public override void SerializeAsV3(IOpenApiWriter writer) { diff --git a/src/Microsoft.OpenApi/PublicAPI.Shipped.txt b/src/Microsoft.OpenApi/PublicAPI.Shipped.txt index e560dba7f..4424e5862 100644 --- a/src/Microsoft.OpenApi/PublicAPI.Shipped.txt +++ b/src/Microsoft.OpenApi/PublicAPI.Shipped.txt @@ -2034,3 +2034,7 @@ Microsoft.OpenApi.OpenApiSecurityScheme.OAuth2MetadataUrl.get -> System.Uri? Microsoft.OpenApi.OpenApiSecurityScheme.OAuth2MetadataUrl.set -> void Microsoft.OpenApi.OpenApiSecuritySchemeReference.OAuth2MetadataUrl.get -> System.Uri? const Microsoft.OpenApi.OpenApiConstants.PatternPropertiesExtension = "x-jsonschema-patternProperties" -> string! +Microsoft.OpenApi.JsonSchemaReference.Extensions.get -> System.Collections.Generic.IDictionary? +Microsoft.OpenApi.JsonSchemaReference.Extensions.set -> void +Microsoft.OpenApi.OpenApiSchemaReference.Extensions.set -> void +override Microsoft.OpenApi.OpenApiSchemaReference.SerializeAsV32(Microsoft.OpenApi.IOpenApiWriter! writer) -> void diff --git a/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs b/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs index 397062aca..8e0699ebc 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using System.Net.Http; using System.Text.Json.Nodes; @@ -27,7 +28,25 @@ public abstract class OpenApiVisitorBase /// Identifier for context public virtual void Enter(string segment) { - this._path.Push(segment); + if (string.IsNullOrEmpty(segment)) + { + this._path.Push(string.Empty); + return; + } + this._path.Push(EncodeJsonPointerSegment(segment)); + } + + internal static string EncodeJsonPointerSegment(string? segment) + { + if (string.IsNullOrEmpty(segment)) + { + return string.Empty; + } +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP1_0_OR_GREATER + return segment.Replace("~", "~0", StringComparison.Ordinal).Replace("/", "~1", StringComparison.OrdinalIgnoreCase); +#else + return segment!.Replace("~", "~0").Replace("/", "~1"); +#endif } /// @@ -41,7 +60,7 @@ public virtual void Exit() /// /// Pointer to source of validation error in document /// - public string PathString { get => "#/" + String.Join("/", _path.Reverse()); } + public string PathString { get => "#/" + string.Join("/", _path.Reverse()); } /// /// Visits diff --git a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs index c8e5d72d7..01172cdd1 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs @@ -1297,15 +1297,6 @@ internal void Walk(IOpenApiElement element) } } - private static string ReplaceSlashes(string value) - { -#if NET8_0_OR_GREATER - return value.Replace("/", "~1", StringComparison.Ordinal); -#else - return value.Replace("/", "~1"); -#endif - } - /// /// Adds a segment to the context path to enable pointing to the current location in the document /// @@ -1315,7 +1306,7 @@ private static string ReplaceSlashes(string value) /// An action that walks objects within the context. private void WalkItem(string context, T state, Action walk) { - _visitor.Enter(ReplaceSlashes(context)); + _visitor.Enter(context); walk(this, state); _visitor.Exit(); } @@ -1330,7 +1321,7 @@ private void WalkItem(string context, T state, Action walk) /// An action that walks objects within the context. private void WalkItem(string context, T state, Action walk, bool isComponent) { - _visitor.Enter(ReplaceSlashes(context)); + _visitor.Enter(context); walk(this, state, isComponent); _visitor.Exit(); } @@ -1351,7 +1342,7 @@ private void WalkDictionary( { if (state != null && state.Count > 0) { - _visitor.Enter(ReplaceSlashes(context)); + _visitor.Enter(context); foreach (var item in state) { diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs index a1f166ee3..38767035f 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs @@ -2,6 +2,8 @@ // Licensed under the MIT license. using System; +using System.Collections.Generic; +using System.Linq; namespace Microsoft.OpenApi { @@ -74,28 +76,49 @@ private void ValidateSchemaReference(OpenApiSchemaReference reference) { if (reference.RecursiveTarget is null) { + var segments = GetSegments().ToArray(); + EnterSegments(segments); // The reference was not followed to a valid schema somewhere in the document - context.Enter(GetSegment()); context.CreateWarning(ruleName, string.Format(SRResource.Validation_SchemaReferenceDoesNotExist, reference.Reference.ReferenceV3)); - context.Exit(); + ExitSegments(segments.Length); } } catch (InvalidOperationException ex) { - context.Enter(GetSegment()); + var segments = GetSegments().ToArray(); + EnterSegments(segments); context.CreateWarning(ruleName, ex.Message); - context.Exit(); + ExitSegments(segments.Length); } - string GetSegment() + void ExitSegments(int length) { - // Trim off the leading "#/" as the context is already at the root of the document - return -#if NET8_0_OR_GREATER - $"{PathString[2..]}/$ref"; + for (var i = 0; i < length; i++) + { + context.Exit(); + } + } + + void EnterSegments(string[] segments) + { + foreach (var segment in segments) + { + context.Enter(segment); + } + } + + IEnumerable GetSegments() + { + foreach (var segment in this.PathString.Substring(2).Split('/')) + { +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP1_0_OR_GREATER + yield return segment.Replace("~1", "/", StringComparison.OrdinalIgnoreCase).Replace("~0", "~", StringComparison.OrdinalIgnoreCase); #else - PathString.Substring(2) + "/$ref"; + yield return segment.Replace("~1", "/").Replace("~0", "~"); #endif + } + yield return "$ref"; + // Trim off the leading "#/" as the context is already at the root of the document } } } diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiParameterRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiParameterRules.cs index a84ea3255..c00e1dcce 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiParameterRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiParameterRules.cs @@ -61,7 +61,7 @@ public static class OpenApiParameterRules (context, parameter) => { if (parameter.In == ParameterLocation.Path && - !(context.PathString.Contains("{" + parameter.Name + "}") || context.PathString.Contains("#/components"))) + !(context.PathString.Contains("{" + OpenApiVisitorBase.EncodeJsonPointerSegment(parameter.Name) + "}") || context.PathString.Contains("#/components"))) { context.Enter("in"); context.CreateError( diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj b/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj index 0e9bad64c..b2cb4de59 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj +++ b/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj @@ -13,12 +13,12 @@ - + - + diff --git a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj index d56e5157f..9e7baf988 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj +++ b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj @@ -15,11 +15,11 @@ - - + + - - + + diff --git a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj index 44fe71dc6..4ae22970e 100644 --- a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj +++ b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj @@ -8,13 +8,13 @@ - - - + + + - - + + diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs index 3cb5af201..64874fb37 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs @@ -1312,6 +1312,111 @@ public async Task SerializeUnevaluatedPropertiesTrueNotEmittedInEarlierVersions( Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); } + [Theory] + [InlineData(JsonSchemaType.Array, "array")] + [InlineData(JsonSchemaType.String, "string")] + [InlineData(JsonSchemaType.Number, "number")] + [InlineData(JsonSchemaType.Integer, "integer")] + [InlineData(JsonSchemaType.Boolean, "boolean")] + [InlineData(JsonSchemaType.Null, "null")] + public async Task SerializeUnevaluatedPropertiesFalseNotEmittedForNonObjectType(JsonSchemaType nonObjectType, string typeName) + { + var expected = $@"{{ ""type"": ""{typeName}"" }}"; + // Given - unevaluatedProperties should not be emitted when type is explicitly set to a non-object type + var schema = new OpenApiSchema + { + Type = nonObjectType, + UnevaluatedProperties = false + }; + + // When + var actual = await schema.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_1); + + // Then + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); + } + + [Theory] + [InlineData(JsonSchemaType.Array, "array")] + [InlineData(JsonSchemaType.String, "string")] + [InlineData(JsonSchemaType.Number, "number")] + [InlineData(JsonSchemaType.Integer, "integer")] + [InlineData(JsonSchemaType.Boolean, "boolean")] + [InlineData(JsonSchemaType.Null, "null")] + public async Task SerializeUnevaluatedPropertiesSchemaNotEmittedForNonObjectType(JsonSchemaType nonObjectType, string typeName) + { + var expected = $@"{{ ""type"": ""{typeName}"" }}"; + // Given - unevaluatedProperties schema should not be emitted when type is explicitly set to a non-object type + var schema = new OpenApiSchema + { + Type = nonObjectType, + UnevaluatedPropertiesSchema = new OpenApiSchema { Type = JsonSchemaType.String } + }; + + // When + var actual = await schema.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_1); + + // Then + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); + } + + [Theory] + [InlineData(OpenApiSpecVersion.OpenApi2_0, JsonSchemaType.Array)] + [InlineData(OpenApiSpecVersion.OpenApi2_0, JsonSchemaType.String)] + [InlineData(OpenApiSpecVersion.OpenApi3_0, JsonSchemaType.Array)] + [InlineData(OpenApiSpecVersion.OpenApi3_0, JsonSchemaType.String)] + public async Task SerializeUnevaluatedPropertiesNotEmittedAsExtensionForNonObjectType(OpenApiSpecVersion version, JsonSchemaType nonObjectType) + { + // Given - unevaluatedProperties should not be emitted as extension when type is a non-object type + var schema = new OpenApiSchema + { + Type = nonObjectType, + UnevaluatedProperties = false + }; + + // When + var actual = await schema.SerializeAsJsonAsync(version); + + // Then - should not contain unevaluatedProperties extension + var parsed = JsonNode.Parse(actual)!.AsObject(); + Assert.False(parsed.ContainsKey(OpenApiConstants.UnevaluatedPropertiesExtension)); + } + + [Fact] + public async Task SerializeUnevaluatedPropertiesFalseStillEmittedForObjectType() + { + var expected = @"{ ""type"": ""object"", ""unevaluatedProperties"": false }"; + // Given - unevaluatedProperties should still be emitted for object type + var schema = new OpenApiSchema + { + Type = JsonSchemaType.Object, + UnevaluatedProperties = false + }; + + // When + var actual = await schema.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_1); + + // Then + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); + } + + [Fact] + public async Task SerializeUnevaluatedPropertiesFalseStillEmittedWhenTypeNotSet() + { + var expected = @"{ ""unevaluatedProperties"": false }"; + // Given - unevaluatedProperties should still be emitted when type is not explicitly set + var schema = new OpenApiSchema + { + UnevaluatedProperties = false + }; + + // When + var actual = await schema.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_1); + + // Then + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(expected), JsonNode.Parse(actual))); + } + // PatternProperties tests [Theory] [InlineData(OpenApiSpecVersion.OpenApi3_1)] diff --git a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV2JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV2JsonWorks_produceTerseOutput=False.verified.txt new file mode 100644 index 000000000..ddb324ad6 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV2JsonWorks_produceTerseOutput=False.verified.txt @@ -0,0 +1,3 @@ +{ + "$ref": "#/definitions/Pet" +} diff --git a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV2JsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV2JsonWorks_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..b36112c01 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV2JsonWorks_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"$ref":"#/definitions/Pet"} diff --git a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV31JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV31JsonWorks_produceTerseOutput=False.verified.txt index 3d7372e1b..87dbbe91f 100644 --- a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV31JsonWorks_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV31JsonWorks_produceTerseOutput=False.verified.txt @@ -7,5 +7,6 @@ "examples": [ "reference example" ], + "x-custom": "custom value", "$ref": "#/components/schemas/Pet" } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV31JsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV31JsonWorks_produceTerseOutput=True.verified.txt index 2a7cc8e44..c7f7f3cd0 100644 --- a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV31JsonWorks_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV31JsonWorks_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"description":"Reference Description","default":"reference default","title":"Reference Title","deprecated":true,"readOnly":true,"examples":["reference example"],"$ref":"#/components/schemas/Pet"} \ No newline at end of file +{"description":"Reference Description","default":"reference default","title":"Reference Title","deprecated":true,"readOnly":true,"examples":["reference example"],"x-custom":"custom value","$ref":"#/components/schemas/Pet"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV32JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV32JsonWorks_produceTerseOutput=False.verified.txt new file mode 100644 index 000000000..87dbbe91f --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV32JsonWorks_produceTerseOutput=False.verified.txt @@ -0,0 +1,12 @@ +{ + "description": "Reference Description", + "default": "reference default", + "title": "Reference Title", + "deprecated": true, + "readOnly": true, + "examples": [ + "reference example" + ], + "x-custom": "custom value", + "$ref": "#/components/schemas/Pet" +} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV32JsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV32JsonWorks_produceTerseOutput=True.verified.txt new file mode 100644 index 000000000..c7f7f3cd0 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.SerializeSchemaReferenceAsV32JsonWorks_produceTerseOutput=True.verified.txt @@ -0,0 +1 @@ +{"description":"Reference Description","default":"reference default","title":"Reference Title","deprecated":true,"readOnly":true,"examples":["reference example"],"x-custom":"custom value","$ref":"#/components/schemas/Pet"} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.cs b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.cs index 57ccae0cb..488488518 100644 --- a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiSchemaReferenceTests.cs @@ -133,7 +133,11 @@ public async Task SerializeSchemaReferenceAsV31JsonWorks(bool produceTerseOutput WriteOnly = false, Deprecated = true, Default = JsonValue.Create("reference default"), - Examples = new List { JsonValue.Create("reference example") } + Examples = new List { JsonValue.Create("reference example") }, + Extensions = new Dictionary + { + ["x-custom"] = new JsonNodeExtension(JsonValue.Create("custom value")) + } }; var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -150,7 +154,7 @@ public async Task SerializeSchemaReferenceAsV31JsonWorks(bool produceTerseOutput [Theory] [InlineData(true)] [InlineData(false)] - public async Task SerializeSchemaReferenceAsV3JsonWorks(bool produceTerseOutput) + public async Task SerializeSchemaReferenceAsV32JsonWorks(bool produceTerseOutput) { // Arrange var reference = new OpenApiSchemaReference("Pet", null) @@ -161,7 +165,43 @@ public async Task SerializeSchemaReferenceAsV3JsonWorks(bool produceTerseOutput) WriteOnly = false, Deprecated = true, Default = JsonValue.Create("reference default"), - Examples = new List { JsonValue.Create("reference example") } + Examples = new List { JsonValue.Create("reference example") }, + Extensions = new Dictionary + { + ["x-custom"] = new JsonNodeExtension(JsonValue.Create("custom value")) + } + }; + + var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); + var writer = new OpenApiJsonWriter(outputStringWriter, new OpenApiJsonWriterSettings { Terse = produceTerseOutput }); + + // Act + reference.SerializeAsV32(writer); + await writer.FlushAsync(); + + // Assert + await Verifier.Verify(outputStringWriter).UseParameters(produceTerseOutput); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task SerializeSchemaReferenceAsV3JsonWorks(bool produceTerseOutput) + { + // Arrange - Extensions should NOT appear in v3.0 output + var reference = new OpenApiSchemaReference("Pet", null) + { + Title = "Reference Title", + Description = "Reference Description", + ReadOnly = true, + WriteOnly = false, + Deprecated = true, + Default = JsonValue.Create("reference default"), + Examples = new List { JsonValue.Create("reference example") }, + Extensions = new Dictionary + { + ["x-custom"] = new JsonNodeExtension(JsonValue.Create("custom value")) + } }; var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); @@ -175,6 +215,38 @@ public async Task SerializeSchemaReferenceAsV3JsonWorks(bool produceTerseOutput) await Verifier.Verify(outputStringWriter).UseParameters(produceTerseOutput); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task SerializeSchemaReferenceAsV2JsonWorks(bool produceTerseOutput) + { + // Arrange - Extensions should NOT appear in v2 output + var reference = new OpenApiSchemaReference("Pet", null) + { + Title = "Reference Title", + Description = "Reference Description", + ReadOnly = true, + WriteOnly = false, + Deprecated = true, + Default = JsonValue.Create("reference default"), + Examples = new List { JsonValue.Create("reference example") }, + Extensions = new Dictionary + { + ["x-custom"] = new JsonNodeExtension(JsonValue.Create("custom value")) + } + }; + + var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); + var writer = new OpenApiJsonWriter(outputStringWriter, new OpenApiJsonWriterSettings { Terse = produceTerseOutput }); + + // Act + reference.SerializeAsV2(writer); + await writer.FlushAsync(); + + // Assert + await Verifier.Verify(outputStringWriter).UseParameters(produceTerseOutput); + } + [Fact] public void ParseSchemaReferenceWithAnnotationsWorks() { @@ -256,5 +328,120 @@ public void ParseSchemaReferenceWithAnnotationsWorks() Assert.Equal("Original Pet Title", targetSchema.Title); Assert.Equal("Original Pet Description", targetSchema.Description); } + + [Fact] + public void ParseSchemaReferenceWithExtensionsWorks() + { + // Arrange + var jsonContent = @"{ + ""openapi"": ""3.1.0"", + ""info"": { + ""title"": ""Test API"", + ""version"": ""1.0.0"" + }, + ""paths"": { + ""/test"": { + ""get"": { + ""responses"": { + ""200"": { + ""description"": ""OK"", + ""content"": { + ""application/json"": { + ""schema"": { + ""$ref"": ""#/components/schemas/Pet"", + ""description"": ""A pet object"", + ""x-custom-extension"": ""custom value"", + ""x-another-extension"": 42 + } + } + } + } + } + } + } + }, + ""components"": { + ""schemas"": { + ""Pet"": { + ""type"": ""object"", + ""properties"": { + ""name"": { + ""type"": ""string"" + } + } + } + } + } +}"; + + // Act + var readResult = OpenApiDocument.Parse(jsonContent, "json"); + var document = readResult.Document; + + // Assert + Assert.NotNull(document); + Assert.Empty(readResult.Diagnostic.Errors); + + var schema = document.Paths["/test"].Operations[HttpMethod.Get] + .Responses["200"].Content["application/json"].Schema; + + Assert.IsType(schema); + var schemaRef = (OpenApiSchemaReference)schema; + + // Test that reference-level extensions are parsed + Assert.NotNull(schemaRef.Extensions); + Assert.Contains("x-custom-extension", schemaRef.Extensions.Keys); + Assert.Contains("x-another-extension", schemaRef.Extensions.Keys); + } + + [Fact] + public async Task SchemaReferenceExtensionsNotWrittenInV30() + { + // Arrange + var reference = new OpenApiSchemaReference("Pet", null) + { + Description = "Local description", + Extensions = new Dictionary + { + ["x-custom"] = new JsonNodeExtension(JsonValue.Create("custom value")) + } + }; + + var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); + var writer = new OpenApiJsonWriter(outputStringWriter, new OpenApiJsonWriterSettings { Terse = true }); + + // Act + reference.SerializeAsV3(writer); + await writer.FlushAsync(); + var output = outputStringWriter.ToString(); + + // Assert: In v3.0, ONLY $ref should appear - no description, no extensions + Assert.Equal(@"{""$ref"":""#/components/schemas/Pet""}", output); + } + + [Fact] + public async Task SchemaReferenceExtensionsNotWrittenInV2() + { + // Arrange + var reference = new OpenApiSchemaReference("Pet", null) + { + Description = "Local description", + Extensions = new Dictionary + { + ["x-custom"] = new JsonNodeExtension(JsonValue.Create("custom value")) + } + }; + + var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); + var writer = new OpenApiJsonWriter(outputStringWriter, new OpenApiJsonWriterSettings { Terse = true }); + + // Act + reference.SerializeAsV2(writer); + await writer.FlushAsync(); + var output = outputStringWriter.ToString(); + + // Assert: In v2, ONLY $ref should appear - no description, no extensions + Assert.Equal(@"{""$ref"":""#/definitions/Pet""}", output); + } } } diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiVisitorBaseTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiVisitorBaseTests.cs new file mode 100644 index 000000000..ce92819f3 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiVisitorBaseTests.cs @@ -0,0 +1,80 @@ +using System.Collections.Generic; +using Xunit; + +namespace Microsoft.OpenApi.Tests.Services; + +public class OpenApiVisitorBaseTests +{ + [Fact] + public void EncodesReservedCharacters() + { + // Given + var openApiDocument = new OpenApiDocument + { + Info = new() + { + Title = "foo", + Version = "1.2.2" + }, + Paths = new() + { + }, + Components = new() + { + Schemas = new Dictionary() + { + ["Pet~"] = new OpenApiSchema() + { + Type = JsonSchemaType.Object + }, + ["Pet/"] = new OpenApiSchema() + { + Type = JsonSchemaType.Object + }, + } + } + }; + var visitor = new LocatorVisitor(); + + // When + visitor.Visit(openApiDocument); + + // Then + Assert.Equivalent( + new List + { + "#/components/schemas/Pet~0", + "#/components/schemas/Pet~1" + }, visitor.Locations); + } + + private class LocatorVisitor : OpenApiVisitorBase + { + public List Locations { get; } = new List(); + + public override void Visit(IOpenApiSchema openApiSchema) + { + Locations.Add(this.PathString); + } + public override void Visit(OpenApiComponents components) + { + Enter("schemas"); + if (components.Schemas != null) + { + foreach (var schemaKvp in components.Schemas) + { + Enter(schemaKvp.Key); + this.Visit(schemaKvp.Value); + Exit(); + } + } + Exit(); + } + public override void Visit(OpenApiDocument doc) + { + Enter("components"); + Visit(doc.Components); + Exit(); + } + } +} diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiHeaderValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiHeaderValidationTests.cs index a7a0cba0a..98e59928c 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiHeaderValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiHeaderValidationTests.cs @@ -86,7 +86,7 @@ public void ValidateExamplesShouldNotHaveDataTypeMismatchForSimpleSchema() // Act var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet()); var walker = new OpenApiWalker(validator); - walker.Walk(header); + walker.Walk((IOpenApiHeader)header); warnings = validator.Warnings; var result = !warnings.Any(); diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiParameterValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiParameterValidationTests.cs index 207db74b6..1d2882033 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiParameterValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiParameterValidationTests.cs @@ -75,7 +75,7 @@ public void ValidateExampleShouldNotHaveDataTypeMismatchForSimpleSchema() var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet()); validator.Enter("{parameter1}"); var walker = new OpenApiWalker(validator); - walker.Walk(parameter); + walker.Walk((IOpenApiParameter)parameter); warnings = validator.Warnings; var result = !warnings.Any(); @@ -203,7 +203,7 @@ public void PathParameterInThePathShouldBeOk() validator.Enter("1"); var walker = new OpenApiWalker(validator); - walker.Walk(parameter); + walker.Walk((IOpenApiParameter)parameter); errors = validator.Errors; var result = errors.Any(); @@ -211,5 +211,70 @@ public void PathParameterInThePathShouldBeOk() // Assert Assert.False(result); } + + [Fact] + public void PathParameterInThePathShouldBeOkWithSlashInParameterName() + { + // Arrange + IEnumerable errors; + + var parameter = new OpenApiParameter + { + Name = "parameter/1", + In = ParameterLocation.Path, + Required = true, + Schema = new OpenApiSchema() + { + Type = JsonSchemaType.String, + } + }; + + // Act + var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet()); + validator.Enter("paths"); + validator.Enter("/{parameter/1}"); + validator.Enter("get"); + validator.Enter("parameters"); + validator.Enter("1"); + + var walker = new OpenApiWalker(validator); + walker.Walk((IOpenApiParameter)parameter); + + errors = validator.Errors; + var result = errors.Any(); + + // Assert + Assert.False(result); + } + + [Fact] + public void PathParameterValidationShouldNotThrowWithEmptyParameterName() + { + // Arrange + var parameter = new OpenApiParameter + { + Name = string.Empty, + In = ParameterLocation.Path, + Required = true, + Schema = new OpenApiSchema() + { + Type = JsonSchemaType.String, + } + }; + + // Act + var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet()); + validator.Enter("paths"); + validator.Enter("/{}"); + validator.Enter("get"); + validator.Enter("parameters"); + validator.Enter("1"); + + var walker = new OpenApiWalker(validator); + var exception = Record.Exception(() => walker.Walk((IOpenApiParameter)parameter)); + + // Assert + Assert.Null(exception); + } } } diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiRecommendedRulesTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiRecommendedRulesTests.cs index c5aa3533d..13d710909 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiRecommendedRulesTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiRecommendedRulesTests.cs @@ -200,7 +200,7 @@ public static void GetOperationWithRequestBodyIsInvalid() Assert.NotNull(warnings); var warning = Assert.Single(warnings); Assert.Equal("GET operations should not have a request body.", warning.Message); - Assert.Equal("#/paths//people/get/requestBody", warning.Pointer); + Assert.Equal("#/paths/~1people/get/requestBody", warning.Pointer); } [Fact] diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs index fdf36e0b4..0cb229534 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs @@ -26,7 +26,7 @@ public void ValidateDefaultShouldNotHaveDataTypeMismatchForSimpleSchema() // Act var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet()); var walker = new OpenApiWalker(validator); - walker.Walk(schema); + walker.Walk((IOpenApiSchema)schema); warnings = validator.Warnings; var result = !warnings.Any(); @@ -50,7 +50,7 @@ public void ValidateExampleAndDefaultShouldNotHaveDataTypeMismatchForSimpleSchem // Act var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet()); var walker = new OpenApiWalker(validator); - walker.Walk(schema); + walker.Walk((IOpenApiSchema)schema); warnings = validator.Warnings; bool result = !warnings.Any(); @@ -92,7 +92,7 @@ public void ValidateEnumShouldNotHaveDataTypeMismatchForSimpleSchema() // Act var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet()); var walker = new OpenApiWalker(validator); - walker.Walk(schema); + walker.Walk((IOpenApiSchema)schema); warnings = validator.Warnings; var result = !warnings.Any();