diff --git a/src/Microsoft.OpenApi/Services/OpenApiAnyComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiAnyComparer.cs new file mode 100644 index 000000000..2b3d2ad11 --- /dev/null +++ b/src/Microsoft.OpenApi/Services/OpenApiAnyComparer.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using System.IO; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Writers; + +namespace Microsoft.OpenApi.Services +{ + /// + /// Defines behavior for comparing properties of . + /// + public class OpenApiAnyComparer : OpenApiComparerBase + { + /// + /// Executes comparision against source and target . + /// + /// The source. + /// The target. + /// Context under which to compare the source and target. + public override void Compare( + IOpenApiAny source, + IOpenApiAny target, + ComparisonContext comparisonContext) + { + if (source == null && target == null) + { + return; + } + + if (source == null || target == null) + { + comparisonContext.AddOpenApiDifference( + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + SourceValue = source, + TargetValue = target, + OpenApiComparedElementType = typeof(IOpenApiAny), + Pointer = comparisonContext.PathString + }); + + return; + } + + var sourceStringWriter = new StringWriter(); + var sourceWriter = new OpenApiJsonWriter(sourceStringWriter); + + source.Write(sourceWriter, OpenApiSpecVersion.OpenApi3_0); + var sourceValue = sourceStringWriter.GetStringBuilder().ToString(); + + var targetStringWriter = new StringWriter(); + var targetWriter = new OpenApiJsonWriter(targetStringWriter); + + target.Write(targetWriter, OpenApiSpecVersion.OpenApi3_0); + var targetValue = targetStringWriter.GetStringBuilder().ToString(); + + if (string.Compare(sourceValue, targetValue, StringComparison.InvariantCulture) != 0) + { + comparisonContext.AddOpenApiDifference(new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(IOpenApiAny), + SourceValue = source, + TargetValue = target, + Pointer = comparisonContext.PathString + }); + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiComparerFactory.cs b/src/Microsoft.OpenApi/Services/OpenApiComparerFactory.cs index e5c9a7da5..b1ad1e269 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiComparerFactory.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiComparerFactory.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Services @@ -55,7 +56,10 @@ public class OpenApiComparerFactory {typeof(IList), new OpenApiOrderedListComparer()}, {typeof(OpenApiExternalDocs), new OpenApiExternalDocsComparer()}, {typeof(OpenApiTag), new OpenApiTagComparer()}, - {typeof(OpenApiSecurityScheme), new OpenApiSecuritySchemeComparer()} + {typeof(OpenApiSecurityScheme), new OpenApiSecuritySchemeComparer()}, + {typeof(OpenApiExample), new OpenApiExampleComparer()}, + {typeof(IDictionary), new OpenApiDictionaryComparer()}, + {typeof(IOpenApiAny), new OpenApiAnyComparer()} }; private readonly Dictionary _typeToComparerMap = new Dictionary(); diff --git a/src/Microsoft.OpenApi/Services/OpenApiComponentsComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiComponentsComparer.cs index 23bdc629f..e1b04dae4 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiComponentsComparer.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiComponentsComparer.cs @@ -84,10 +84,15 @@ public override void Compare( .GetComparer>() .Compare(sourceComponents.SecuritySchemes, targetComponents.SecuritySchemes, comparisonContext)); - // To Do compare Examples + WalkAndCompare( + comparisonContext, + OpenApiConstants.Examples, + () => comparisonContext + .GetComparer>() + .Compare(sourceComponents.Examples, targetComponents.Examples, comparisonContext)); + // To Do compare Links // To Do compare Callbacks - // To Do compare Extensions } } } \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiEncodingComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiEncodingComparer.cs index 923ab1420..1e1aa7fef 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiEncodingComparer.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiEncodingComparer.cs @@ -60,8 +60,6 @@ public override void Compare( () => comparisonContext .GetComparer>() .Compare(sourceEncoding.Headers, targetEncoding.Headers, comparisonContext)); - - // To Do Compare Extensions } } } \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiExampleComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiExampleComparer.cs new file mode 100644 index 000000000..70ab5082f --- /dev/null +++ b/src/Microsoft.OpenApi/Services/OpenApiExampleComparer.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Services +{ + /// + /// Defines behavior for comparing properties of . + /// + public class OpenApiExampleComparer : OpenApiComparerBase + { + /// + /// Executes comparision against source and target . + /// + /// The source. + /// The target. + /// Context under which to compare the source and target. + public override void Compare( + OpenApiExample sourceExample, + OpenApiExample targetExample, + ComparisonContext comparisonContext) + { + if (sourceExample == null && targetExample == null) + { + return; + } + + if (sourceExample == null || targetExample == null) + { + comparisonContext.AddOpenApiDifference( + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + SourceValue = sourceExample, + TargetValue = targetExample, + OpenApiComparedElementType = typeof(OpenApiExample), + Pointer = comparisonContext.PathString + }); + + return; + } + + new OpenApiReferenceComparer() + .Compare(sourceExample.Reference, targetExample.Reference, comparisonContext); + + WalkAndCompare(comparisonContext, OpenApiConstants.Description, + () => Compare(sourceExample.Description, targetExample.Description, comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.Summary, + () => Compare(sourceExample.Summary, targetExample.Summary, comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.ExternalValue, + () => Compare(sourceExample.ExternalValue, targetExample.ExternalValue, comparisonContext)); + + WalkAndCompare( + comparisonContext, + OpenApiConstants.Value, + () => comparisonContext + .GetComparer() + .Compare(sourceExample.Value, targetExample.Value, comparisonContext)); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiHeaderComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiHeaderComparer.cs index b57ff4071..9f25751b2 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiHeaderComparer.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiHeaderComparer.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System.Collections.Generic; +using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Services @@ -104,9 +105,19 @@ public override void Compare( .GetComparer() .Compare(sourceHeader.Schema, targetHeader.Schema, comparisonContext)); - // To do compare example - // To do compare examples - // To do compare extensions + WalkAndCompare( + comparisonContext, + OpenApiConstants.Examples, + () => comparisonContext + .GetComparer>() + .Compare(sourceHeader.Examples, targetHeader.Examples, comparisonContext)); + + WalkAndCompare( + comparisonContext, + OpenApiConstants.Example, + () => comparisonContext + .GetComparer() + .Compare(sourceHeader.Example, targetHeader.Example, comparisonContext)); } } } \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiMediaTypeComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiMediaTypeComparer.cs index 9578e211d..b93b33e45 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiMediaTypeComparer.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiMediaTypeComparer.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System.Collections.Generic; +using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Services @@ -47,7 +48,7 @@ public override void Compare( OpenApiConstants.Schema, () => comparisonContext .GetComparer() - .Compare( sourceMediaType.Schema, targetMediaType.Schema, comparisonContext ) ); + .Compare(sourceMediaType.Schema, targetMediaType.Schema, comparisonContext)); WalkAndCompare( comparisonContext, @@ -56,9 +57,19 @@ public override void Compare( .GetComparer>() .Compare(sourceMediaType.Encoding, sourceMediaType.Encoding, comparisonContext)); - // To Do Compare Example - // To Do Compare Examples - // To Do Compare Extensions + WalkAndCompare( + comparisonContext, + OpenApiConstants.Examples, + () => comparisonContext + .GetComparer>() + .Compare(sourceMediaType.Examples, targetMediaType.Examples, comparisonContext)); + + WalkAndCompare( + comparisonContext, + OpenApiConstants.Example, + () => comparisonContext + .GetComparer() + .Compare(sourceMediaType.Example, targetMediaType.Example, comparisonContext)); } } } \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiParameterComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiParameterComparer.cs index ee4df45cf..43b185e4e 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiParameterComparer.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiParameterComparer.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System.Collections.Generic; +using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Services @@ -86,8 +87,19 @@ public override void Compare( .GetComparer() .Compare(sourceParameter.Schema, targetParameter.Schema, comparisonContext)); - // To Do Compare Examples - // To Do Compare parameter as IOpenApiExtensible + WalkAndCompare( + comparisonContext, + OpenApiConstants.Examples, + () => comparisonContext + .GetComparer>() + .Compare(sourceParameter.Examples, targetParameter.Examples, comparisonContext)); + + WalkAndCompare( + comparisonContext, + OpenApiConstants.Example, + () => comparisonContext + .GetComparer() + .Compare(sourceParameter.Example, targetParameter.Example, comparisonContext)); } } } \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiPathItemComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiPathItemComparer.cs index 84c590eee..6a5657e0f 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiPathItemComparer.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiPathItemComparer.cs @@ -53,8 +53,6 @@ public override void Compare( () => comparisonContext .GetComparer>() .Compare(sourcePathItem?.Servers, targetPathItem?.Servers, comparisonContext)); - - // To Do Compare Extensions } } } \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiRequestBodyComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiRequestBodyComparer.cs index d3ca3fc65..cce69dad2 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiRequestBodyComparer.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiRequestBodyComparer.cs @@ -57,8 +57,6 @@ public override void Compare( () => comparisonContext .GetComparer>() .Compare(sourceRequestBody.Content, targetRequestBody.Content, comparisonContext)); - - //To Do Compare Extensions } } } \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiResponseComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiResponseComparer.cs index 805ad2743..4f47516b7 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiResponseComparer.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiResponseComparer.cs @@ -90,7 +90,6 @@ public override void Compare( .Compare(sourceResponse.Headers, targetResponse.Headers, comparisonContext)); // To Do Compare Link - // To Do Compare Extensions } } } \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiSchemaComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiSchemaComparer.cs index 5d9561209..2fbb98694 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiSchemaComparer.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiSchemaComparer.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System.Collections.Generic; +using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Services @@ -157,11 +158,17 @@ public override void Compare( OpenApiConstants.ExternalDocs, () => comparisonContext .GetComparer() - .Compare(sourceSchema?.ExternalDocs, targetSchema?.ExternalDocs, comparisonContext)); + .Compare(sourceSchema.ExternalDocs, targetSchema.ExternalDocs, comparisonContext)); + + WalkAndCompare( + comparisonContext, + OpenApiConstants.Example, + () => comparisonContext + .GetComparer() + .Compare(sourceSchema.Example, targetSchema.Example, comparisonContext)); // To Do Compare schema.AllOf // To Do Compare schema.AnyOf - // To Do compare schema as IOpenApiExtensible comparisonContext.SourceSchemaLoop.Pop(); comparisonContext.TargetSchemaLoop.Pop(); diff --git a/src/Microsoft.OpenApi/Services/OpenApiServerComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiServerComparer.cs index 47245bd71..c5d17d443 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiServerComparer.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiServerComparer.cs @@ -54,8 +54,6 @@ public override void Compare( () => comparisonContext .GetComparer>() .Compare(sourceServer.Variables, sourceServer.Variables, comparisonContext)); - - // To Do compare extensions } } } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiComparerTestCases.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiComparerTestCases.cs index 7471440a5..76df51c25 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiComparerTestCases.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiComparerTestCases.cs @@ -590,6 +590,19 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( Id = "schemaObject2" } } + }, + Example = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } } }, ["schemaObject2"] = new OpenApiSchema @@ -693,6 +706,19 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( Id = "schemaObject2" } } + }, + Example = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["relupdate"] = new OpenApiString("sampleRel1") + } + } } }, ["schemaObject2"] = new OpenApiSchema @@ -848,6 +874,168 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( MaxLength = 15 }, TargetValue = null + }, + new OpenApiDifference + { + Pointer = "#/components/schemas/schemaObject1/example", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(IOpenApiAny), + SourceValue = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + }, + TargetValue = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["relupdate"] = new OpenApiString("sampleRel1") + } + } + } + }, + new OpenApiDifference + { + Pointer = "#/components/schemas/schemaObject2/properties/property6/example", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(IOpenApiAny), + SourceValue = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + }, + TargetValue = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["relupdate"] = new OpenApiString("sampleRel1") + } + } + } + }, + new OpenApiDifference + { + Pointer = + "#/paths/~1test/get/parameters/0/schema/properties/property6/properties/property6/example", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(IOpenApiAny), + SourceValue = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + }, + TargetValue = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["relupdate"] = new OpenApiString("sampleRel1") + } + } + } + }, + new OpenApiDifference + { + Pointer = "#/paths/~1test/get/parameters/0/schema/example", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(IOpenApiAny), + SourceValue = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + }, + TargetValue = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["relupdate"] = new OpenApiString("sampleRel1") + } + } + } + }, + new OpenApiDifference + { + Pointer = + "#/components/schemas/schemaObject1/properties/property6/properties/property6/example", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(IOpenApiAny), + SourceValue = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + }, + TargetValue = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["relupdate"] = new OpenApiString("sampleRel1") + } + } + } } } }; @@ -885,6 +1073,19 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( Id = "schemaObject1", Type = ReferenceType.Schema } + }, + Examples = new Dictionary + { + { + "example1", new OpenApiExample + { + Reference = new OpenApiReference + { + Id = "example1", + Type = ReferenceType.Example + } + } + } } } } @@ -973,6 +1174,50 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( } } } + }, + Examples = new Dictionary + { + ["example1"] = new OpenApiExample + { + Value = new OpenApiObject + { + ["versions"] = new OpenApiArray + { + new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + } + } + } + }, + ["example3"] = new OpenApiExample + { + Value = new OpenApiObject + { + ["versions"] = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + } + } + } } } }, @@ -1005,6 +1250,19 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( Id = "schemaObject1", Type = ReferenceType.Schema } + }, + Examples = new Dictionary + { + { + "example1", new OpenApiExample + { + Reference = new OpenApiReference + { + Id = "example1", + Type = ReferenceType.Example + } + } + } } } } @@ -1110,6 +1368,50 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( } } } + }, + Examples = new Dictionary + { + ["example1"] = new OpenApiExample + { + Value = new OpenApiObject + { + ["versions"] = new OpenApiArray + { + new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["relupdate"] = new OpenApiString("sampleRel1") + } + } + } + } + } + }, + ["example3"] = new OpenApiExample + { + Value = new OpenApiObject + { + ["versions"] = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + } + } + } } } }, @@ -1313,6 +1615,94 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( MaxLength = 15 }, TargetValue = null + }, + new OpenApiDifference + { + Pointer = "#/paths/~1test/get/requestBody/content/application~1xml/examples/example1/value", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(IOpenApiAny), + SourceValue = new OpenApiObject + { + ["versions"] = new OpenApiArray + { + new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + } + } + }, + TargetValue = new OpenApiObject + { + ["versions"] = new OpenApiArray + { + new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["relupdate"] = new OpenApiString("sampleRel1") + } + } + } + } + } + }, + new OpenApiDifference + { + Pointer = "#/components/examples/example1/value", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(IOpenApiAny), + SourceValue = new OpenApiObject + { + ["versions"] = new OpenApiArray + { + new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + } + } + }, + TargetValue = new OpenApiObject + { + ["versions"] = new OpenApiArray + { + new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["relupdate"] = new OpenApiString("sampleRel1") + } + } + } + } + } } } }; diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiComparerTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiComparerTests.cs index 306a40abe..277e03da4 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiComparerTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiComparerTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using FluentAssertions; +using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Services; using Xunit; @@ -14,6 +15,43 @@ namespace Microsoft.OpenApi.Tests.Services [Collection("DefaultSettings")] public class OpenApiComparerTests { + public static OpenApiExample AdvancedExample = new OpenApiExample + { + Value = new OpenApiObject + { + ["versions"] = new OpenApiArray + { + new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + }, + + new OpenApiObject + { + ["status"] = new OpenApiString("Status2"), + ["id"] = new OpenApiString("v2"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/2"), + ["rel"] = new OpenApiString("sampleRel2") + } + } + } + } + } + }; + private readonly ITestOutputHelper _output; public OpenApiComparerTests(ITestOutputHelper output) @@ -33,6 +71,8 @@ public void OpenApiComparerShouldSucceed( { _output.WriteLine(testCaseName); + new OpenApiExampleComparer().Compare(AdvancedExample, AdvancedExample, + new ComparisonContext(new OpenApiComparerFactory(), new OpenApiDocument(), new OpenApiDocument())); var differences = OpenApiComparer.Compare(source, target).ToList(); differences.Count().ShouldBeEquivalentTo(expectedDifferences.Count); diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiComponentsTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiComponentsTests.cs index 0f44d2d43..06e5d0caa 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiComponentsTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiComponentsTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using FluentAssertions; +using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Services; using Xunit; @@ -675,6 +676,211 @@ public static IEnumerable GetTestCasesForOpenApiComponentsComparerShou } } }; + + // New, removed and updated examples + yield return new object[] + { + "New, removed and updated examples", + new OpenApiComponents + { + Examples = new Dictionary + { + ["example1"] = new OpenApiExample + { + Value = new OpenApiObject + { + ["versions"] = new OpenApiArray + { + new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + } + } + } + }, + ["example3"] = new OpenApiExample + { + Value = new OpenApiObject + { + ["versions"] = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + } + } + } + } + }, + new OpenApiComponents + { + Examples = new Dictionary + { + ["example2"] = new OpenApiExample + { + Value = new OpenApiObject + { + ["versions"] = new OpenApiArray + { + new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + } + } + } + }, + ["example3"] = new OpenApiExample + { + Value = new OpenApiObject + { + ["versions"] = new OpenApiArray + { + new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + } + } + } + } + } + }, + new List + { + new OpenApiDifference + { + Pointer = "#/examples/example2", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, + OpenApiComparedElementType = typeof(OpenApiExample), + SourceValue = null, + TargetValue = new OpenApiExample + { + Value = new OpenApiObject + { + ["versions"] = new OpenApiArray + { + new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + } + } + } + } + }, + new OpenApiDifference + { + Pointer = "#/examples/example1", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, + OpenApiComparedElementType = typeof(OpenApiExample), + SourceValue = new OpenApiExample + { + Value = new OpenApiObject + { + ["versions"] = new OpenApiArray + { + new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + } + } + } + }, + TargetValue = null + }, + new OpenApiDifference + { + Pointer = "#/examples/example3/value", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(IOpenApiAny), + SourceValue = new OpenApiObject + { + ["versions"] = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + } + }, + TargetValue = new OpenApiObject + { + ["versions"] = new OpenApiArray + { + new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + } + } + } + } + } + }; } [Theory] diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiExampleComparerTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiExampleComparerTests.cs new file mode 100644 index 000000000..472c609f5 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiExampleComparerTests.cs @@ -0,0 +1,461 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Services; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.OpenApi.Tests.Services +{ + [Collection("DefaultSettings")] + public class OpenApiExampleComparerTests + { + private readonly ITestOutputHelper _output; + + private readonly OpenApiDocument _sourceDocument = new OpenApiDocument + { + Components = new OpenApiComponents + { + Schemas = new Dictionary + { + ["schemaObject1"] = new OpenApiSchema + { + Properties = new Dictionary + { + ["property2"] = new OpenApiSchema + { + Type = "integer" + }, + ["property7"] = new OpenApiSchema + { + Type = "string", + MaxLength = 15 + }, + ["property6"] = new OpenApiSchema + { + Reference = new OpenApiReference + { + Type = ReferenceType.Schema, + Id = "schemaObject2" + } + } + } + }, + ["schemaObject2"] = new OpenApiSchema + { + Properties = new Dictionary + { + ["property2"] = new OpenApiSchema + { + Type = "integer" + }, + ["property5"] = new OpenApiSchema + { + Type = "string", + MaxLength = 15 + }, + ["property6"] = new OpenApiSchema + { + Reference = new OpenApiReference + { + Type = ReferenceType.Schema, + Id = "schemaObject1" + } + } + } + } + }, + RequestBodies = new Dictionary + { + ["requestBody1"] = new OpenApiRequestBody + { + Description = "description", + Required = true, + Content = + { + ["application/json"] = new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Reference = new OpenApiReference + { + Id = "schemaObject1", + Type = ReferenceType.Schema + } + } + } + } + }, + ["requestBody2"] = new OpenApiRequestBody + { + Description = "description", + Required = true, + Content = + { + ["application/json"] = new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Reference = new OpenApiReference + { + Id = "schemaObject1", + Type = ReferenceType.Schema + } + } + } + } + } + } + } + }; + + private readonly OpenApiDocument _targetDocument = new OpenApiDocument + { + Components = new OpenApiComponents + { + Schemas = new Dictionary + { + ["schemaObject1"] = new OpenApiSchema + { + Properties = new Dictionary + { + ["property2"] = new OpenApiSchema + { + Type = "integer" + }, + ["property5"] = new OpenApiSchema + { + Type = "string", + MaxLength = 15 + }, + ["property6"] = new OpenApiSchema + { + Reference = new OpenApiReference + { + Type = ReferenceType.Schema, + Id = "schemaObject2" + } + } + } + }, + ["schemaObject2"] = new OpenApiSchema + { + Properties = new Dictionary + { + ["property2"] = new OpenApiSchema + { + Type = "integer" + }, + ["property5"] = new OpenApiSchema + { + Type = "string", + MaxLength = 15 + }, + ["property6"] = new OpenApiSchema + { + Reference = new OpenApiReference + { + Type = ReferenceType.Schema, + Id = "schemaObject1" + } + } + } + } + }, + RequestBodies = new Dictionary + { + ["requestBody1"] = new OpenApiRequestBody + { + Description = "description", + Required = true, + Content = + { + ["application/json"] = new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Reference = new OpenApiReference + { + Id = "schemaObject1", + Type = ReferenceType.Schema + } + } + } + } + }, + ["requestBody2"] = new OpenApiRequestBody + { + Description = "description", + Required = true, + Content = + { + ["application/json"] = new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Reference = new OpenApiReference + { + Id = "schemaObject1", + Type = ReferenceType.Schema + } + } + } + } + } + } + } + }; + + public OpenApiExampleComparerTests(ITestOutputHelper output) + { + _output = output; + } + + public static IEnumerable GetTestCasesForOpenApiExampleComparerShouldSucceed() + { + yield return new object[] + { + "Differences in description, summary and external value", + new OpenApiExample + { + Description = "Test description", + Summary = "Test summary", + ExternalValue = "http://localhost/1" + }, + new OpenApiExample + { + Description = "Test description updated", + Summary = "Test summary updated", + ExternalValue = "http://localhost/2" + }, + new List + { + new OpenApiDifference + { + Pointer = "#/description", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(string), + SourceValue = "Test description", + TargetValue = "Test description updated" + }, + new OpenApiDifference + { + Pointer = "#/summary", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(string), + SourceValue = "Test summary", + TargetValue = "Test summary updated" + }, + new OpenApiDifference + { + Pointer = "#/externalValue", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(string), + SourceValue = "http://localhost/1", + TargetValue = "http://localhost/2" + } + } + }; + + yield return new object[] + { + "Null source", + null, + new OpenApiExample + { + Description = "Test description", + Summary = "Test summary", + ExternalValue = "http://localhost/1" + }, + new List + { + new OpenApiDifference + { + Pointer = "#/", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(OpenApiExample), + SourceValue = null, + TargetValue = new OpenApiExample + { + Description = "Test description", + Summary = "Test summary", + ExternalValue = "http://localhost/1" + } + } + } + }; + + yield return new object[] + { + "Null target", + new OpenApiExample + { + Description = "Test description", + Summary = "Test summary", + ExternalValue = "http://localhost/1" + }, + null, + new List + { + new OpenApiDifference + { + Pointer = "#/", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(OpenApiExample), + TargetValue = null, + SourceValue = new OpenApiExample + { + Description = "Test description", + Summary = "Test summary", + ExternalValue = "http://localhost/1" + } + } + } + }; + + yield return new object[] + { + "Difference in value", + new OpenApiExample + { + Description = "Test description", + Summary = "Test summary", + Value = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + } + }, + new OpenApiExample + { + Description = "Test description", + Summary = "Test summary", + Value = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["relUpdated"] = new OpenApiString("sampleRel1") + } + } + } + }, + new List + { + new OpenApiDifference + { + Pointer = "#/value", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(IOpenApiAny), + TargetValue = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["relUpdated"] = new OpenApiString("sampleRel1") + } + } + }, + SourceValue = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + } + } + } + }; + + yield return new object[] + { + "No differences", + new OpenApiExample + { + Description = "Test description", + Summary = "Test summary", + Value = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + } + }, + new OpenApiExample + { + Description = "Test description", + Summary = "Test summary", + Value = new OpenApiObject + { + ["status"] = new OpenApiString("Status1"), + ["id"] = new OpenApiString("v1"), + ["links"] = new OpenApiArray + { + new OpenApiObject + { + ["href"] = new OpenApiString("http://example.com/1"), + ["rel"] = new OpenApiString("sampleRel1") + } + } + } + }, + new List() + }; + } + + [Theory] + [MemberData(nameof(GetTestCasesForOpenApiExampleComparerShouldSucceed))] + public void OpenApiExampleComparerShouldSucceed( + string testCaseName, + OpenApiExample source, + OpenApiExample target, + List expectedDifferences) + { + _output.WriteLine(testCaseName); + + var comparisonContext = new ComparisonContext(new OpenApiComparerFactory(), _sourceDocument, + _targetDocument); + var comparer = new OpenApiExampleComparer(); + comparer.Compare(source, target, comparisonContext); + + var differences = comparisonContext.OpenApiDifferences.ToList(); + differences.Count().ShouldBeEquivalentTo(expectedDifferences.Count); + + differences.ShouldBeEquivalentTo(expectedDifferences); + } + } +} \ No newline at end of file