diff --git a/src/Microsoft.OpenApi/Services/OpenApiComparerBase.cs b/src/Microsoft.OpenApi/Services/OpenApiComparerBase.cs index d08b5e644..1fe80577e 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiComparerBase.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiComparerBase.cs @@ -2,6 +2,8 @@ // Licensed under the MIT license. using System; +using System.Collections.Generic; +using System.Linq; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Services @@ -46,6 +48,32 @@ internal void Compare(string source, string target, ComparisonContext comparison } } + /// + /// Compares two Uri object. + /// + /// The source. + /// The target. + /// The context under which to compare the objects. + internal void Compare(Uri source, Uri target, ComparisonContext comparisonContext) + { + if (source == null && target == null) + { + return; + } + + if (source != target) + { + comparisonContext.AddOpenApiDifference(new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(Uri), + SourceValue = source, + TargetValue = target, + Pointer = comparisonContext.PathString + }); + } + } + /// /// Compares two boolean object. /// @@ -138,6 +166,67 @@ internal void Compare(Enum source, Enum target, ComparisonContext compari } } + /// + /// Compares where TKey is and TValue is + /// . + /// + /// The source. + /// The target. + /// The context under which to compare the objects. + internal void Compare(IDictionary source, IDictionary 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(IDictionary), + Pointer = comparisonContext.PathString + }); + + return; + } + + var newKeysInTarget = target.Keys.Except(source.Keys).ToList(); + + foreach (var newKeyInTarget in newKeysInTarget) + { + WalkAndAddOpenApiDifference( + comparisonContext, + newKeyInTarget, + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, + TargetValue = target[newKeyInTarget], + OpenApiComparedElementType = typeof(string) + }); + } + + var removedKeysFromSource = source.Keys.Except(target.Keys).ToList(); + + foreach (var removedKeyFromSource in removedKeysFromSource) + { + WalkAndAddOpenApiDifference( + comparisonContext, + removedKeyFromSource, + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, + SourceValue = source[removedKeyFromSource], + OpenApiComparedElementType = typeof(string) + }); + } + } + /// /// Adds a segment to the context path to enable pointing to the current location in the document. /// diff --git a/src/Microsoft.OpenApi/Services/OpenApiComparerFactory.cs b/src/Microsoft.OpenApi/Services/OpenApiComparerFactory.cs index e10f0aa5f..e5c9a7da5 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiComparerFactory.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiComparerFactory.cs @@ -33,6 +33,10 @@ public class OpenApiComparerFactory {typeof(IDictionary), new OpenApiDictionaryComparer()}, {typeof(IDictionary), new OpenApiDictionaryComparer()}, {typeof(IDictionary), new OpenApiDictionaryComparer()}, + { + typeof(IDictionary), + new OpenApiDictionaryComparer() + }, {typeof(OpenApiHeader), new OpenApiHeaderComparer()}, {typeof(OpenApiRequestBody), new OpenApiRequestBodyComparer()}, {typeof(OpenApiResponse), new OpenApiResponseComparer()}, @@ -40,7 +44,18 @@ public class OpenApiComparerFactory {typeof(OpenApiEncoding), new OpenApiEncodingComparer()}, {typeof(IList), new OpenApiServersComparer()}, {typeof(OpenApiServer), new OpenApiServerComparer()}, - {typeof(OpenApiServerVariable), new OpenApiServerVariableComparer()} + {typeof(OpenApiServerVariable), new OpenApiServerVariableComparer()}, + {typeof(OpenApiOAuthFlow), new OpenApiOAuthFlowComparer()}, + {typeof(OpenApiOAuthFlows), new OpenApiOAuthFlowsComparer()}, + {typeof(OpenApiSecurityRequirement), new OpenApiSecurityRequirementComparer()}, + {typeof(OpenApiInfo), new OpenApiInfoComparer()}, + {typeof(OpenApiContact), new OpenApiContactComparer()}, + {typeof(OpenApiLicense), new OpenApiLicenseComparer()}, + {typeof(IList), new OpenApiOrderedListComparer()}, + {typeof(IList), new OpenApiOrderedListComparer()}, + {typeof(OpenApiExternalDocs), new OpenApiExternalDocsComparer()}, + {typeof(OpenApiTag), new OpenApiTagComparer()}, + {typeof(OpenApiSecurityScheme), new OpenApiSecuritySchemeComparer()} }; private readonly Dictionary _typeToComparerMap = new Dictionary(); diff --git a/src/Microsoft.OpenApi/Services/OpenApiComponentsComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiComponentsComparer.cs index bdb2ed3e5..23bdc629f 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiComponentsComparer.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiComponentsComparer.cs @@ -77,8 +77,14 @@ public override void Compare( .GetComparer>() .Compare(sourceComponents.Headers, targetComponents.Headers, comparisonContext)); + WalkAndCompare( + comparisonContext, + OpenApiConstants.SecuritySchemes, + () => comparisonContext + .GetComparer>() + .Compare(sourceComponents.SecuritySchemes, targetComponents.SecuritySchemes, comparisonContext)); + // To Do compare Examples - // To Do compare SecuritySchemes // To Do compare Links // To Do compare Callbacks // To Do compare Extensions diff --git a/src/Microsoft.OpenApi/Services/OpenApiContactComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiContactComparer.cs new file mode 100644 index 000000000..06f65c62c --- /dev/null +++ b/src/Microsoft.OpenApi/Services/OpenApiContactComparer.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Services +{ + /// + /// Defines behavior for comparing properties of . + /// + public class OpenApiContactComparer : OpenApiComparerBase + { + /// + /// Executes comparision against source and target . + /// + /// The source. + /// The target. + /// Context under which to compare the source and target. + public override void Compare( + OpenApiContact sourceContact, + OpenApiContact targetContact, + ComparisonContext comparisonContext) + { + if (sourceContact == null && targetContact == null) + { + return; + } + + if (sourceContact == null || targetContact == null) + { + comparisonContext.AddOpenApiDifference( + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + SourceValue = sourceContact, + TargetValue = targetContact, + OpenApiComparedElementType = typeof(OpenApiContact), + Pointer = comparisonContext.PathString + }); + + return; + } + + WalkAndCompare(comparisonContext, OpenApiConstants.Name, + () => Compare(sourceContact.Name, targetContact.Name, comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.Email, + () => Compare(sourceContact.Email, targetContact.Email, comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.Url, + () => Compare(sourceContact.Url, targetContact.Url, comparisonContext)); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiDictionaryComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiDictionaryComparer.cs index 0a82ad0b1..34c797f14 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiDictionaryComparer.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiDictionaryComparer.cs @@ -56,10 +56,8 @@ public override void Compare( new OpenApiDifference { OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - TargetValue = new KeyValuePair( - newKeyInTarget, - targetFragment[newKeyInTarget]), - OpenApiComparedElementType = typeof(KeyValuePair) + TargetValue = targetFragment[newKeyInTarget], + OpenApiComparedElementType = typeof(T) }); } @@ -80,8 +78,8 @@ public override void Compare( new OpenApiDifference { OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - SourceValue = source, - OpenApiComparedElementType = typeof(KeyValuePair) + SourceValue = source.Value, + OpenApiComparedElementType = typeof(T) }); } } diff --git a/src/Microsoft.OpenApi/Services/OpenApiDocumentComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiDocumentComparer.cs index 41e3dc046..5e1c6ecf5 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiDocumentComparer.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiDocumentComparer.cs @@ -43,11 +43,34 @@ public override void Compare( .GetComparer>() .Compare(sourceDocument.Servers, targetDocument.Servers, comparisonContext)); - // To Do Compare Info - // To Do Compare Security Requirements - // To Do Compare Tags - // To Do Compare External Docs - // To Do Compare Extensions + WalkAndCompare( + comparisonContext, + OpenApiConstants.Info, + () => comparisonContext + .GetComparer() + .Compare(sourceDocument.Info, targetDocument.Info, comparisonContext)); + + WalkAndCompare( + comparisonContext, + OpenApiConstants.Security, + () => comparisonContext + .GetComparer>() + .Compare(sourceDocument.SecurityRequirements, targetDocument.SecurityRequirements, + comparisonContext)); + + WalkAndCompare( + comparisonContext, + OpenApiConstants.Tags, + () => comparisonContext + .GetComparer>() + .Compare(sourceDocument.Tags, targetDocument.Tags, comparisonContext)); + + WalkAndCompare( + comparisonContext, + OpenApiConstants.ExternalDocs, + () => comparisonContext + .GetComparer() + .Compare(sourceDocument.ExternalDocs, targetDocument.ExternalDocs, comparisonContext)); } } } \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiExternalDocsComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiExternalDocsComparer.cs new file mode 100644 index 000000000..31e9d5bcf --- /dev/null +++ b/src/Microsoft.OpenApi/Services/OpenApiExternalDocsComparer.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Services +{ + /// + /// Defines behavior for comparing properties of . + /// + public class OpenApiExternalDocsComparer : OpenApiComparerBase + { + /// + /// Executes comparision against source and target . + /// + /// The source. + /// The target. + /// Context under which to compare the source and target. + public override void Compare(OpenApiExternalDocs sourceDocs, OpenApiExternalDocs targetDocs, + ComparisonContext comparisonContext) + { + if (sourceDocs == null && targetDocs == null) + { + return; + } + + if (sourceDocs == null || targetDocs == null) + { + comparisonContext.AddOpenApiDifference( + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + SourceValue = sourceDocs, + TargetValue = targetDocs, + OpenApiComparedElementType = typeof(OpenApiExternalDocs), + Pointer = comparisonContext.PathString + }); + return; + } + + WalkAndCompare(comparisonContext, OpenApiConstants.Description, + () => Compare(sourceDocs.Description, targetDocs.Description, comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.Url, + () => Compare(sourceDocs.Url, targetDocs.Url, comparisonContext)); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiInfoComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiInfoComparer.cs new file mode 100644 index 000000000..ec71059dc --- /dev/null +++ b/src/Microsoft.OpenApi/Services/OpenApiInfoComparer.cs @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Services +{ + /// + /// Defines behavior for comparing properties of . + /// + public class OpenApiInfoComparer : OpenApiComparerBase + { + /// + /// Executes comparision against source and target . + /// + /// The source. + /// The target. + /// Context under which to compare the source and target. + public override void Compare( + OpenApiInfo sourceInfo, + OpenApiInfo targetInfo, + ComparisonContext comparisonContext) + { + if (sourceInfo == null && targetInfo == null) + { + return; + } + + if (sourceInfo == null || targetInfo == null) + { + comparisonContext.AddOpenApiDifference( + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + SourceValue = sourceInfo, + TargetValue = targetInfo, + OpenApiComparedElementType = typeof(OpenApiInfo), + Pointer = comparisonContext.PathString + }); + + return; + } + + WalkAndCompare(comparisonContext, OpenApiConstants.Title, + () => Compare(sourceInfo.Title, targetInfo.Title, comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.Description, + () => Compare(sourceInfo.Description, targetInfo.Description, comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.TermsOfService, + () => Compare(sourceInfo.TermsOfService, targetInfo.TermsOfService, comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.Version, + () => Compare(sourceInfo.Version, targetInfo.Version, comparisonContext)); + + WalkAndCompare( + comparisonContext, + OpenApiConstants.Contact, + () => comparisonContext + .GetComparer() + .Compare(sourceInfo.Contact, targetInfo.Contact, comparisonContext)); + + WalkAndCompare( + comparisonContext, + OpenApiConstants.License, + () => comparisonContext + .GetComparer() + .Compare(sourceInfo.License, targetInfo.License, comparisonContext)); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiLicenseComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiLicenseComparer.cs new file mode 100644 index 000000000..be61db7c8 --- /dev/null +++ b/src/Microsoft.OpenApi/Services/OpenApiLicenseComparer.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Services +{ + /// + /// Defines behavior for comparing properties of . + /// + public class OpenApiLicenseComparer : OpenApiComparerBase + { + /// + /// Executes comparision against source and target . + /// + /// The source. + /// The target. + /// Context under which to compare the source and target. + public override void Compare( + OpenApiLicense sourceLicense, + OpenApiLicense targetLicense, + ComparisonContext comparisonContext) + { + if (sourceLicense == null && targetLicense == null) + { + return; + } + + if (sourceLicense == null || targetLicense == null) + { + comparisonContext.AddOpenApiDifference( + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + SourceValue = sourceLicense, + TargetValue = targetLicense, + OpenApiComparedElementType = typeof(OpenApiLicense), + Pointer = comparisonContext.PathString + }); + + return; + } + + WalkAndCompare(comparisonContext, OpenApiConstants.Name, + () => Compare(sourceLicense.Name, targetLicense.Name, comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.Url, + () => Compare(sourceLicense.Url, targetLicense.Url, comparisonContext)); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiOAuthFlowComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiOAuthFlowComparer.cs new file mode 100644 index 000000000..cfee18a60 --- /dev/null +++ b/src/Microsoft.OpenApi/Services/OpenApiOAuthFlowComparer.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Services +{ + /// + /// Defines behavior for comparing properties of . + /// + public class OpenApiOAuthFlowComparer : OpenApiComparerBase + { + /// + /// Executes comparision against source and target . + /// + /// The source. + /// The target. + /// Context under which to compare the source and target. + public override void Compare(OpenApiOAuthFlow sourceFlow, OpenApiOAuthFlow targetFlow, + ComparisonContext comparisonContext) + { + if (sourceFlow == null && targetFlow == null) + { + return; + } + + if (sourceFlow == null || targetFlow == null) + { + comparisonContext.AddOpenApiDifference( + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + SourceValue = sourceFlow, + TargetValue = targetFlow, + OpenApiComparedElementType = typeof(OpenApiOAuthFlow), + Pointer = comparisonContext.PathString + }); + + return; + } + + WalkAndCompare(comparisonContext, OpenApiConstants.AuthorizationUrl, + () => Compare(sourceFlow.AuthorizationUrl, targetFlow.AuthorizationUrl, comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.TokenUrl, + () => Compare(sourceFlow.TokenUrl, targetFlow.TokenUrl, comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.RefreshUrl, + () => Compare(sourceFlow.RefreshUrl, targetFlow.RefreshUrl, comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.Scopes, + () => Compare(sourceFlow.Scopes, targetFlow.Scopes, comparisonContext)); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiOAuthFlowsComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiOAuthFlowsComparer.cs new file mode 100644 index 000000000..9334bc9b0 --- /dev/null +++ b/src/Microsoft.OpenApi/Services/OpenApiOAuthFlowsComparer.cs @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Services +{ + /// + /// Defines behavior for comparing properties of . + /// + public class OpenApiOAuthFlowsComparer : OpenApiComparerBase + { + /// + /// Executes comparision against source and target . + /// + /// The source. + /// The target. + /// Context under which to compare the source and target. + public override void Compare( + OpenApiOAuthFlows sourceFlows, + OpenApiOAuthFlows targetFlows, + ComparisonContext comparisonContext) + { + if (sourceFlows == null && targetFlows == null) + { + return; + } + + if (sourceFlows == null || targetFlows == null) + { + comparisonContext.AddOpenApiDifference( + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + SourceValue = sourceFlows, + TargetValue = targetFlows, + OpenApiComparedElementType = typeof(OpenApiOAuthFlows), + Pointer = comparisonContext.PathString + }); + + return; + } + + WalkAndCompare( + comparisonContext, + OpenApiConstants.Implicit, + () => comparisonContext + .GetComparer() + .Compare(sourceFlows.Implicit, targetFlows.Implicit, comparisonContext)); + + WalkAndCompare( + comparisonContext, + OpenApiConstants.Password, + () => comparisonContext + .GetComparer() + .Compare(sourceFlows.Password, targetFlows.Password, comparisonContext)); + + WalkAndCompare( + comparisonContext, + OpenApiConstants.ClientCredentials, + () => comparisonContext + .GetComparer() + .Compare(sourceFlows.ClientCredentials, targetFlows.ClientCredentials, comparisonContext)); + + WalkAndCompare( + comparisonContext, + OpenApiConstants.AuthorizationCode, + () => comparisonContext + .GetComparer() + .Compare(sourceFlows.AuthorizationCode, targetFlows.AuthorizationCode, comparisonContext)); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiOperationComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiOperationComparer.cs index 98879e013..354e36906 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiOperationComparer.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiOperationComparer.cs @@ -69,11 +69,28 @@ public override void Compare( .GetComparer>() .Compare(sourceOperation?.Servers, targetOperation?.Servers, comparisonContext)); + WalkAndCompare( + comparisonContext, + OpenApiConstants.Tags, + () => comparisonContext + .GetComparer>() + .Compare(sourceOperation?.Tags, targetOperation?.Tags, comparisonContext)); + + WalkAndCompare( + comparisonContext, + OpenApiConstants.Security, + () => comparisonContext + .GetComparer>() + .Compare(sourceOperation?.Security, targetOperation?.Security, comparisonContext)); + + WalkAndCompare( + comparisonContext, + OpenApiConstants.ExternalDocs, + () => comparisonContext + .GetComparer() + .Compare(sourceOperation?.ExternalDocs, targetOperation?.ExternalDocs, comparisonContext)); + // Compare CallBack - // Compare Security Requirements - // Compare Extensions - // Compare External Docs - // Compare Tags } } } \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiOperationsComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiOperationsComparer.cs index 0aace1a3e..d64351302 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiOperationsComparer.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiOperationsComparer.cs @@ -56,10 +56,8 @@ public override void Compare( new OpenApiDifference { OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - TargetValue = new KeyValuePair( - newOperationKeyInTarget, - targetOperations[newOperationKeyInTarget]), - OpenApiComparedElementType = typeof(KeyValuePair) + TargetValue = targetOperations[newOperationKeyInTarget], + OpenApiComparedElementType = typeof(OpenApiOperation) }); } @@ -80,8 +78,8 @@ public override void Compare( new OpenApiDifference { OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - SourceValue = sourceOperation, - OpenApiComparedElementType = typeof(KeyValuePair) + SourceValue = sourceOperation.Value, + OpenApiComparedElementType = typeof(OpenApiOperation) }); } } diff --git a/src/Microsoft.OpenApi/Services/OpenApiOrderedListComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiOrderedListComparer.cs new file mode 100644 index 000000000..405f06e21 --- /dev/null +++ b/src/Microsoft.OpenApi/Services/OpenApiOrderedListComparer.cs @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; +using Microsoft.OpenApi.Interfaces; + +namespace Microsoft.OpenApi.Services +{ + /// + /// Defines behavior for comparing where T is . + /// + public class OpenApiOrderedListComparer : OpenApiComparerBase> where T : IOpenApiSerializable + { + /// + /// Executes comparision against based on the order of the list for source and target + /// where T is . + /// + /// The source. + /// The target. + /// Context under which to compare the source and target. + public override void Compare( + IList sourceFragment, + IList targetFragment, + ComparisonContext comparisonContext) + { + if (sourceFragment == null && targetFragment == null) + { + return; + } + + if (sourceFragment == null || targetFragment == null) + { + comparisonContext.AddOpenApiDifference( + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + SourceValue = sourceFragment, + TargetValue = sourceFragment, + OpenApiComparedElementType = typeof(IList), + Pointer = comparisonContext.PathString + }); + + return; + } + + for (var i = 0; i < sourceFragment.Count; i++) + { + if (i >= targetFragment.Count) + { + WalkAndAddOpenApiDifference( + comparisonContext, + i.ToString(), + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, + SourceValue = sourceFragment[i], + OpenApiComparedElementType = typeof(T) + }); + } + else + { + WalkAndCompare(comparisonContext, + i.ToString(), + () => comparisonContext + .GetComparer() + .Compare(sourceFragment[i], targetFragment[i], comparisonContext)); + } + } + + if (targetFragment.Count <= sourceFragment.Count) + { + return; + } + + // Loop through remaining elements in target that are not in source. + for (var i = sourceFragment.Count; i < targetFragment.Count; i++) + { + WalkAndAddOpenApiDifference( + comparisonContext, + i.ToString(), + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, + TargetValue = targetFragment[i], + OpenApiComparedElementType = typeof(T) + }); + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiParameterComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiParameterComparer.cs index 53d3e6619..ee4df45cf 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiParameterComparer.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiParameterComparer.cs @@ -42,6 +42,9 @@ public override void Compare( return; } + new OpenApiReferenceComparer() + .Compare(sourceParameter.Reference, targetParameter.Reference, comparisonContext); + WalkAndCompare( comparisonContext, OpenApiConstants.Content, @@ -83,7 +86,6 @@ public override void Compare( .GetComparer() .Compare(sourceParameter.Schema, targetParameter.Schema, comparisonContext)); - // To Do Add compare for reference object // To Do Compare Examples // To Do Compare parameter as IOpenApiExtensible } diff --git a/src/Microsoft.OpenApi/Services/OpenApiReferenceComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiReferenceComparer.cs new file mode 100644 index 000000000..9a819db08 --- /dev/null +++ b/src/Microsoft.OpenApi/Services/OpenApiReferenceComparer.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Services +{ + /// + /// Defines behavior for comparing properties of . + /// + public class OpenApiReferenceComparer : OpenApiComparerBase where T : IOpenApiReferenceable + { + /// + /// Compares object. + /// + /// The source. + /// The target. + /// The context under which to compare the objects. + public override void Compare( + OpenApiReference sourceReference, + OpenApiReference targetReference, + ComparisonContext comparisonContext) + { + if (sourceReference == null && targetReference == null) + { + return; + } + + if (sourceReference == null || targetReference == null) + { + comparisonContext.AddOpenApiDifference( + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + SourceValue = sourceReference, + TargetValue = targetReference, + OpenApiComparedElementType = typeof(OpenApiReference), + Pointer = comparisonContext.PathString + }); + + return; + } + + if (sourceReference.Id != targetReference.Id || sourceReference.Type != targetReference.Type) + { + WalkAndAddOpenApiDifference( + comparisonContext, + OpenApiConstants.DollarRef, + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + SourceValue = sourceReference, + TargetValue = targetReference, + OpenApiComparedElementType = typeof(OpenApiReference) + }); + + return; + } + + var source = (T) comparisonContext.SourceDocument.ResolveReference( + sourceReference); + + var target = (T) comparisonContext.TargetDocument.ResolveReference( + targetReference); + + comparisonContext + .GetComparer() + .Compare(source, target, comparisonContext); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiRequestBodyComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiRequestBodyComparer.cs index 880c654cf..d3ca3fc65 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiRequestBodyComparer.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiRequestBodyComparer.cs @@ -42,35 +42,8 @@ public override void Compare( return; } - if (sourceRequestBody.Reference != null - && targetRequestBody.Reference != null - && sourceRequestBody.Reference.Id != targetRequestBody.Reference.Id) - { - WalkAndAddOpenApiDifference( - comparisonContext, - OpenApiConstants.DollarRef, - new OpenApiDifference - { - OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, - SourceValue = sourceRequestBody.Reference, - TargetValue = targetRequestBody.Reference, - OpenApiComparedElementType = typeof(OpenApiReference) - }); - - return; - } - - if (sourceRequestBody.Reference != null) - { - sourceRequestBody = (OpenApiRequestBody) comparisonContext.SourceDocument.ResolveReference( - sourceRequestBody.Reference); - } - - if (targetRequestBody.Reference != null) - { - targetRequestBody = (OpenApiRequestBody) comparisonContext.TargetDocument.ResolveReference( - targetRequestBody.Reference); - } + new OpenApiReferenceComparer() + .Compare(sourceRequestBody.Reference, targetRequestBody.Reference, comparisonContext); WalkAndCompare(comparisonContext, OpenApiConstants.Description, () => Compare(sourceRequestBody.Description, targetRequestBody.Description, comparisonContext)); diff --git a/src/Microsoft.OpenApi/Services/OpenApiSchemaComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiSchemaComparer.cs index e320854fd..5d9561209 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiSchemaComparer.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiSchemaComparer.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. using System.Collections.Generic; -using System.Linq; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Services @@ -52,6 +51,36 @@ public override void Compare( comparisonContext.SourceSchemaLoop.Push(sourceSchema); comparisonContext.TargetSchemaLoop.Push(targetSchema); + if (sourceSchema.Reference != null + && targetSchema.Reference != null + && sourceSchema.Reference.Id != targetSchema.Reference.Id) + { + WalkAndAddOpenApiDifference( + comparisonContext, + OpenApiConstants.DollarRef, + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + SourceValue = sourceSchema.Reference?.Id, + TargetValue = targetSchema.Reference?.Id, + OpenApiComparedElementType = typeof(string) + }); + + return; + } + + if (sourceSchema.Reference != null) + { + sourceSchema = (OpenApiSchema) comparisonContext.SourceDocument.ResolveReference( + sourceSchema.Reference); + } + + if (targetSchema.Reference != null) + { + targetSchema = (OpenApiSchema) comparisonContext.TargetDocument.ResolveReference( + targetSchema.Reference); + } + WalkAndCompare( comparisonContext, OpenApiConstants.Title, @@ -115,96 +144,23 @@ public override void Compare( .Compare(sourceSchema.Items, targetSchema.Items, comparisonContext)); } - if (sourceSchema.Reference != null - && targetSchema.Reference != null - && sourceSchema.Reference.Id != targetSchema.Reference.Id) - { - WalkAndAddOpenApiDifference( - comparisonContext, - OpenApiConstants.DollarRef, - new OpenApiDifference - { - OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, - SourceValue = sourceSchema.Reference?.Id, - TargetValue = targetSchema.Reference?.Id, - OpenApiComparedElementType = typeof(string) - }); - - return; - } - - if (sourceSchema.Reference != null) - { - sourceSchema = (OpenApiSchema) comparisonContext.SourceDocument.ResolveReference( - sourceSchema.Reference); - } - - if (targetSchema.Reference != null) - { - targetSchema = (OpenApiSchema) comparisonContext.TargetDocument.ResolveReference( - targetSchema.Reference); - } - - if (targetSchema.Properties != null) - { - IEnumerable newPropertiesInTarget = sourceSchema.Properties == null - ? targetSchema.Properties.Keys - : targetSchema.Properties.Keys.Except(sourceSchema.Properties.Keys) - .ToList(); - - WalkAndCompare(comparisonContext, OpenApiConstants.Properties, () => - { - foreach (var newPropertyInTarget in newPropertiesInTarget) - { - WalkAndAddOpenApiDifference( - comparisonContext, - newPropertyInTarget, - new OpenApiDifference - { - OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - TargetValue = new KeyValuePair(newPropertyInTarget, - targetSchema.Properties[newPropertyInTarget]), - OpenApiComparedElementType = typeof(KeyValuePair) - }); - } - }); - } + WalkAndCompare( + comparisonContext, + OpenApiConstants.Properties, + () => comparisonContext + .GetComparer>() + .Compare(sourceSchema.Properties, + targetSchema.Properties, comparisonContext)); - if (sourceSchema.Properties != null) - { - WalkAndCompare(comparisonContext, OpenApiConstants.Properties, () => - { - foreach (var sourceSchemaProperty in sourceSchema.Properties) - { - if (targetSchema.Properties.ContainsKey(sourceSchemaProperty.Key)) - { - WalkAndCompare( - comparisonContext, - sourceSchemaProperty.Key, - () => comparisonContext - .GetComparer() - .Compare(sourceSchemaProperty.Value, - targetSchema.Properties[sourceSchemaProperty.Key], comparisonContext)); - } - else - { - WalkAndAddOpenApiDifference( - comparisonContext, - sourceSchemaProperty.Key, - new OpenApiDifference - { - OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - SourceValue = sourceSchemaProperty, - OpenApiComparedElementType = typeof(KeyValuePair) - }); - } - } - }); - } + WalkAndCompare( + comparisonContext, + OpenApiConstants.ExternalDocs, + () => comparisonContext + .GetComparer() + .Compare(sourceSchema?.ExternalDocs, targetSchema?.ExternalDocs, comparisonContext)); // To Do Compare schema.AllOf // To Do Compare schema.AnyOf - // To Do compare external Docs // To Do compare schema as IOpenApiExtensible comparisonContext.SourceSchemaLoop.Pop(); diff --git a/src/Microsoft.OpenApi/Services/OpenApiSecurityRequirementComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiSecurityRequirementComparer.cs new file mode 100644 index 000000000..c9e5422e7 --- /dev/null +++ b/src/Microsoft.OpenApi/Services/OpenApiSecurityRequirementComparer.cs @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; +using System.Linq; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Services +{ + /// + /// Defines behavior for comparing properties of . + /// + public class OpenApiSecurityRequirementComparer : OpenApiComparerBase + { + /// + /// Executes comparision against source and target . + /// + /// The source. + /// The target. + /// Context under which to compare the source and target. + public override void Compare( + OpenApiSecurityRequirement sourceSecurityRequirement, + OpenApiSecurityRequirement targetSecurityRequirement, + ComparisonContext comparisonContext) + { + if (sourceSecurityRequirement == null && targetSecurityRequirement == null) + { + return; + } + + if (sourceSecurityRequirement == null || targetSecurityRequirement == null) + { + comparisonContext.AddOpenApiDifference( + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + SourceValue = sourceSecurityRequirement, + TargetValue = targetSecurityRequirement, + OpenApiComparedElementType = typeof(OpenApiSecurityRequirement), + Pointer = comparisonContext.PathString + }); + + return; + } + + var newSecuritySchemesInTarget = targetSecurityRequirement.Keys + .Where(targetReq => sourceSecurityRequirement.Keys.All( + sourceReq => sourceReq.Reference.Id != targetReq.Reference.Id)).ToList(); + + foreach (var newSecuritySchemeInTarget in newSecuritySchemesInTarget) + { + WalkAndAddOpenApiDifference( + comparisonContext, + newSecuritySchemeInTarget.Reference.Id, + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, + TargetValue = targetSecurityRequirement[newSecuritySchemeInTarget], + OpenApiComparedElementType = typeof(IList) + }); + } + + foreach (var sourceSecurityScheme in sourceSecurityRequirement.Keys) + { + var targetSecurityScheme = + targetSecurityRequirement.Keys.FirstOrDefault( + i => i.Reference.Id == sourceSecurityScheme.Reference.Id); + + if (targetSecurityScheme == null) + { + WalkAndAddOpenApiDifference( + comparisonContext, + sourceSecurityScheme.Reference.Id, + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, + SourceValue = sourceSecurityRequirement[sourceSecurityScheme], + OpenApiComparedElementType = typeof(IList) + }); + } + else + { + WalkAndCompare(comparisonContext, + sourceSecurityScheme.Reference.Id, + () => comparisonContext + .GetComparer() + .Compare(sourceSecurityScheme, targetSecurityScheme, comparisonContext)); + } + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiSecuritySchemeComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiSecuritySchemeComparer.cs new file mode 100644 index 000000000..bdee35bf4 --- /dev/null +++ b/src/Microsoft.OpenApi/Services/OpenApiSecuritySchemeComparer.cs @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Services +{ + /// + /// Defines behavior for comparing properties of . + /// + public class OpenApiSecuritySchemeComparer : OpenApiComparerBase + { + /// + /// Executes comparision against source and target . + /// + /// The source. + /// The target. + /// Context under which to compare the source and target. + public override void Compare( + OpenApiSecurityScheme sourcecSecurityScheme, + OpenApiSecurityScheme targetSecurityScheme, + ComparisonContext comparisonContext) + { + if (sourcecSecurityScheme == null && targetSecurityScheme == null) + { + return; + } + + if (sourcecSecurityScheme == null || targetSecurityScheme == null) + { + comparisonContext.AddOpenApiDifference( + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + SourceValue = sourcecSecurityScheme, + TargetValue = targetSecurityScheme, + OpenApiComparedElementType = typeof(OpenApiSecurityScheme), + Pointer = comparisonContext.PathString + }); + + return; + } + + new OpenApiReferenceComparer() + .Compare(sourcecSecurityScheme.Reference, targetSecurityScheme.Reference, + comparisonContext); + + WalkAndCompare(comparisonContext, OpenApiConstants.Description, + () => Compare(sourcecSecurityScheme.Description, targetSecurityScheme.Description, comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.Type, + () => Compare(sourcecSecurityScheme.Type, targetSecurityScheme.Type, + comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.Name, + () => Compare(sourcecSecurityScheme.Name, targetSecurityScheme.Name, comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.In, + () => Compare(sourcecSecurityScheme.In, targetSecurityScheme.In, comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.Scheme, + () => Compare(sourcecSecurityScheme.Scheme, targetSecurityScheme.Scheme, comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.BearerFormat, + () => Compare(sourcecSecurityScheme.BearerFormat, targetSecurityScheme.BearerFormat, + comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.OpenIdConnectUrl, + () => Compare(sourcecSecurityScheme.OpenIdConnectUrl, targetSecurityScheme.OpenIdConnectUrl, + comparisonContext)); + + WalkAndCompare( + comparisonContext, + OpenApiConstants.Flows, + () => comparisonContext + .GetComparer() + .Compare(sourcecSecurityScheme.Flows, targetSecurityScheme.Flows, comparisonContext)); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiTagComparer.cs b/src/Microsoft.OpenApi/Services/OpenApiTagComparer.cs new file mode 100644 index 000000000..6290f38f3 --- /dev/null +++ b/src/Microsoft.OpenApi/Services/OpenApiTagComparer.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Services +{ + /// + /// Defines behavior for comparing properties of . + /// + public class OpenApiTagComparer : OpenApiComparerBase + { + /// + /// Executes comparision against source and target . + /// + /// The source. + /// The target. + /// Context under which to compare the source and target. + public override void Compare(OpenApiTag sourceTag, OpenApiTag targetTag, ComparisonContext comparisonContext) + { + if (sourceTag == null && targetTag == null) + { + return; + } + + if (sourceTag == null || targetTag == null) + { + comparisonContext.AddOpenApiDifference( + new OpenApiDifference + { + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + SourceValue = sourceTag, + TargetValue = targetTag, + OpenApiComparedElementType = typeof(OpenApiTag), + Pointer = comparisonContext.PathString + }); + + return; + } + + WalkAndCompare( + comparisonContext, + OpenApiConstants.ExternalDocs, + () => comparisonContext + .GetComparer() + .Compare(sourceTag.ExternalDocs, targetTag.ExternalDocs, comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.Description, + () => Compare(sourceTag.Description, targetTag.Description, comparisonContext)); + + WalkAndCompare(comparisonContext, OpenApiConstants.Name, + () => Compare(sourceTag.Name, targetTag.Name, comparisonContext)); + } + } +} \ 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 7728a7932..7471440a5 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiComparerTestCases.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiComparerTestCases.cs @@ -150,21 +150,17 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( { Pointer = "#/paths/~1test/patch", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiOperation), SourceValue = null, - TargetValue = - new KeyValuePair(OperationType.Patch, - new OpenApiOperation()) + TargetValue = new OpenApiOperation() }, new OpenApiDifference { Pointer = "#/paths/~1test/post", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiOperation), TargetValue = null, - SourceValue = - new KeyValuePair(OperationType.Post, - new OpenApiOperation()) + SourceValue = new OpenApiOperation() } } }; @@ -319,20 +315,17 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( { Pointer = "#/paths/~1test/get", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiOperation), TargetValue = null, - SourceValue = - new KeyValuePair(OperationType.Get, new OpenApiOperation()) + SourceValue = new OpenApiOperation() }, new OpenApiDifference { Pointer = "#/paths/~1test/post", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiOperation), TargetValue = null, - SourceValue = - new KeyValuePair(OperationType.Post, - new OpenApiOperation()) + SourceValue = new OpenApiOperation() } } }; @@ -383,20 +376,17 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( { Pointer = "#/paths/~1test/get", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiOperation), SourceValue = null, - TargetValue = - new KeyValuePair(OperationType.Get, new OpenApiOperation()) + TargetValue = new OpenApiOperation() }, new OpenApiDifference { Pointer = "#/paths/~1test/patch", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiOperation), SourceValue = null, - TargetValue = - new KeyValuePair(OperationType.Patch, - new OpenApiOperation()) + TargetValue = new OpenApiOperation() } } }; @@ -737,24 +727,24 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( { Pointer = "#/paths/~1test/get/parameters/0/schema/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/paths/~1test/get/parameters/0/schema/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null }, new OpenApiDifference @@ -762,49 +752,49 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( Pointer = "#/paths/~1test/get/parameters/0/schema/properties/property6/properties/property6/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/paths/~1test/get/parameters/0/schema/properties/property6/properties/property6/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null }, new OpenApiDifference { Pointer = "#/components/schemas/schemaObject1/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/components/schemas/schemaObject1/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null }, new OpenApiDifference @@ -812,25 +802,25 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( Pointer = "#/components/schemas/schemaObject1/properties/property6/properties/property6/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/components/schemas/schemaObject1/properties/property6/properties/property6/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null }, new OpenApiDifference @@ -838,25 +828,25 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( Pointer = "#/components/schemas/schemaObject2/properties/property6/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/components/schemas/schemaObject2/properties/property6/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null } } @@ -1129,24 +1119,24 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( { Pointer = "#/paths/~1test/get/requestBody/content/application~1xml/schema/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/paths/~1test/get/requestBody/content/application~1xml/schema/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null }, new OpenApiDifference @@ -1154,34 +1144,34 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( Pointer = "#/paths/~1test/get/requestBody/content/application~1xml/schema/properties/property6/properties/property6/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/paths/~1test/get/requestBody/content/application~1xml/schema/properties/property6/properties/property6/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null }, new OpenApiDifference { Pointer = "#/paths/~1test/get/responses/400", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiResponse), SourceValue = null, - TargetValue = new KeyValuePair("400", new OpenApiResponse + TargetValue = new OpenApiResponse { Description = "An updated complex object array response", Content = @@ -1194,32 +1184,32 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( } } } - }) + } }, new OpenApiDifference { Pointer = "#/paths/~1test/get/responses/200/content/application~1json/schema/items/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/paths/~1test/get/responses/200/content/application~1json/schema/items/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null }, new OpenApiDifference @@ -1227,49 +1217,49 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( Pointer = "#/paths/~1test/get/responses/200/content/application~1json/schema/items/properties/property6/properties/property6/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/paths/~1test/get/responses/200/content/application~1json/schema/items/properties/property6/properties/property6/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null }, new OpenApiDifference { Pointer = "#/components/schemas/schemaObject1/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/components/schemas/schemaObject1/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null }, new OpenApiDifference @@ -1277,25 +1267,25 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( Pointer = "#/components/schemas/schemaObject1/properties/property6/properties/property6/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/components/schemas/schemaObject1/properties/property6/properties/property6/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null }, new OpenApiDifference @@ -1303,25 +1293,721 @@ public static IEnumerable GetTestCasesForOpenApiComparerShouldSucceed( Pointer = "#/components/schemas/schemaObject2/properties/property6/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/components/schemas/schemaObject2/properties/property6/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, + TargetValue = null + } + } + }; + + // Differences in tags and security requirements + yield return new object[] + { + "Differences in tags and security requirements", + new OpenApiDocument + { + Paths = new OpenApiPaths + { + { + "/test", new OpenApiPathItem + { + Summary = "test", + Description = "test", + Operations = new Dictionary + { + { + OperationType.Get, new OpenApiOperation + { + RequestBody = new OpenApiRequestBody + { + Description = "description", + Required = true, + Content = + { + ["application/xml"] = new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Reference = new OpenApiReference + { + Id = "schemaObject1", + Type = ReferenceType.Schema + } + } + } + } + }, + Responses = new OpenApiResponses + { + { + "200", + new OpenApiResponse + { + Description = "An updated complex object array response", + Content = + { + ["application/json"] = new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Type = "array", + Items = new OpenApiSchema + { + Reference = new OpenApiReference + { + Type = ReferenceType.Schema, + Id = "schemaObject1" + } + } + } + } + } + } + } + }, + Security = new List + { + new OpenApiSecurityRequirement + { + [ + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "scheme1" + } + } + ] = new List() + } + } + } + } + } + } + } + }, + Tags = new List + { + new OpenApiTag + { + Description = "test description", + Name = "Tag1", + ExternalDocs = new OpenApiExternalDocs + { + Description = "test description", + Url = new Uri("http://localhost/doc") + } + }, + new OpenApiTag + { + Description = "test description", + Name = "Tag2", + ExternalDocs = new OpenApiExternalDocs + { + Description = "test description", + Url = new Uri("http://localhost/doc") + } + } + }, + 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" + } + } + } + } + }, + SecuritySchemes = new Dictionary + { + { + "scheme1", new OpenApiSecurityScheme + { + Description = "Test", + Name = "Test", + Flows = new OpenApiOAuthFlows + { + Implicit = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri("http://localhost/1") + }, + AuthorizationCode = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri("http://localhost/2") + } + } + } + }, + { + "scheme2", new OpenApiSecurityScheme + { + Description = "Test", + Name = "Test" + } + }, + { + "scheme3", new OpenApiSecurityScheme + { + Description = "Test", + Name = "Test" + } + } + } + }, + SecurityRequirements = new List + { + new OpenApiSecurityRequirement + { + [ + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "scheme1" + } + } + ] = new List() + }, + new OpenApiSecurityRequirement + { + [ + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "scheme2" + } + } + ] = new List() + }, + new OpenApiSecurityRequirement + { + [ + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "scheme3" + } + } + ] = new List() + } + } + }, + new OpenApiDocument + { + Paths = new OpenApiPaths + { + { + "/test", new OpenApiPathItem + { + Summary = "test", + Description = "test", + Operations = new Dictionary + { + { + OperationType.Get, new OpenApiOperation + { + RequestBody = new OpenApiRequestBody + { + Description = "description", + Required = true, + Content = + { + ["application/xml"] = new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Reference = new OpenApiReference + { + Id = "schemaObject1", + Type = ReferenceType.Schema + } + } + } + } + }, + Responses = new OpenApiResponses + { + { + "200", + new OpenApiResponse + { + Description = "An updated complex object array response", + Content = + { + ["application/json"] = new OpenApiMediaType + { + Schema = new OpenApiSchema + { + Type = "array", + Items = new OpenApiSchema + { + Reference = new OpenApiReference + { + Type = ReferenceType.Schema, + Id = "schemaObject1" + } + } + } + } + } + } + } + }, + Security = new List + { + new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "scheme1" + } + }, + new List() + } + }, + new OpenApiSecurityRequirement + { + [ + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "scheme4" + } + } + ] = new List() + } + } + } + } + } + } + } + }, + Tags = new List + { + new OpenApiTag + { + Description = "test description updated", + Name = "Tag1", + ExternalDocs = new OpenApiExternalDocs + { + Description = "test description", + Url = new Uri("http://localhost/doc") + } + } + }, + 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" + } + } + } + } + }, + SecuritySchemes = new Dictionary + { + { + "scheme1", new OpenApiSecurityScheme + { + Description = "Test", + Name = "Test", + Flows = new OpenApiOAuthFlows + { + Implicit = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri("http://localhost/3") + }, + ClientCredentials = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri("http://localhost/2") + } + } + } + }, + { + "scheme2", new OpenApiSecurityScheme + { + Description = "Test", + Name = "Test" + } + }, + { + "scheme4", new OpenApiSecurityScheme + { + Description = "Test", + Name = "Test" + } + } + } + }, + SecurityRequirements = new List + { + new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "scheme1" + } + }, + new List() + }, + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "scheme2" + } + }, + new List() + } + }, + new OpenApiSecurityRequirement + { + [ + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "scheme4" + } + } + ] = new List() + } + } + }, + new List + { + new OpenApiDifference + { + Pointer = "#/security/0/scheme2", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, + OpenApiComparedElementType = typeof(IList), + SourceValue = null, + TargetValue = new List() + }, + new OpenApiDifference + { + Pointer = "#/security/1/scheme4", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, + OpenApiComparedElementType = typeof(IList), + SourceValue = null, + TargetValue = new List() + }, + new OpenApiDifference + { + Pointer = + "#/components/securitySchemes/scheme4", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, + OpenApiComparedElementType = typeof(OpenApiSecurityScheme), + SourceValue = null, + TargetValue = new OpenApiSecurityScheme + { + Description = "Test", + Name = "Test" + } + }, + new OpenApiDifference + { + Pointer = + "#/paths/~1test/get/security/1", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, + OpenApiComparedElementType = typeof(OpenApiSecurityRequirement), + SourceValue = null, + TargetValue = new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "scheme4" + } + }, + new List() + } + } + }, + new OpenApiDifference + { + Pointer = "#/tags/0/description", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(string), + SourceValue = "test description", + TargetValue = "test description updated" + }, + new OpenApiDifference + { + Pointer = "#/components/securitySchemes/scheme1/flows/implicit/authorizationUrl", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(Uri), + SourceValue = "http://localhost/1", + TargetValue = "http://localhost/3" + }, + new OpenApiDifference + { + Pointer = "#/paths/~1test/get/security/0/scheme1/flows/implicit/authorizationUrl", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(Uri), + SourceValue = "http://localhost/1", + TargetValue = "http://localhost/3" + }, + new OpenApiDifference + { + Pointer = "#/security/0/scheme1/flows/implicit/authorizationUrl", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(Uri), + SourceValue = "http://localhost/1", + TargetValue = "http://localhost/3" + }, + new OpenApiDifference + { + Pointer = + "#/components/securitySchemes/scheme1/flows/clientCredentials", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(OpenApiOAuthFlow), + SourceValue = null, + TargetValue = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri("http://localhost/2") + } + }, + new OpenApiDifference + { + Pointer = + "#/paths/~1test/get/security/0/scheme1/flows/clientCredentials", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(OpenApiOAuthFlow), + SourceValue = null, + TargetValue = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri("http://localhost/2") + } + }, + new OpenApiDifference + { + Pointer = + "#/security/0/scheme1/flows/clientCredentials", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(OpenApiOAuthFlow), + SourceValue = null, + TargetValue = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri("http://localhost/2") + } + }, + new OpenApiDifference + { + Pointer = "#/components/securitySchemes/scheme1/flows/authorizationCode", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(OpenApiOAuthFlow), + SourceValue = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri("http://localhost/2") + }, + TargetValue = null + }, + new OpenApiDifference + { + Pointer = "#/paths/~1test/get/security/0/scheme1/flows/authorizationCode", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(OpenApiOAuthFlow), + SourceValue = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri("http://localhost/2") + }, + TargetValue = null + }, + new OpenApiDifference + { + Pointer = "#/security/0/scheme1/flows/authorizationCode", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(OpenApiOAuthFlow), + SourceValue = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri("http://localhost/2") + }, + TargetValue = null + }, + new OpenApiDifference + { + Pointer = + "#/components/securitySchemes/scheme3", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, + OpenApiComparedElementType = typeof(OpenApiSecurityScheme), + SourceValue = new OpenApiSecurityScheme + { + Description = "Test", + Name = "Test" + }, + TargetValue = null + }, + new OpenApiDifference + { + Pointer = "#/tags/1", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, + OpenApiComparedElementType = typeof(OpenApiTag), + SourceValue = new OpenApiTag + { + Description = "test description", + Name = "Tag2", + ExternalDocs = new OpenApiExternalDocs + { + Description = "test description", + Url = new Uri("http://localhost/doc") + } + }, + TargetValue = null + }, + new OpenApiDifference + { + Pointer = + "#/security/2", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, + OpenApiComparedElementType = typeof(OpenApiSecurityRequirement), + SourceValue = new OpenApiSecurityRequirement + { + [ + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "scheme3" + } + } + ] = new List() + }, + TargetValue = null + }, + new OpenApiDifference + { + Pointer = + "#/security/1/scheme2", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, + OpenApiComparedElementType = typeof(IList), + SourceValue = new List(), TargetValue = null } } diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiComponentsTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiComponentsTests.cs index 2b2fcdfe3..0f44d2d43 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiComponentsTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiComponentsTests.cs @@ -375,24 +375,24 @@ public static IEnumerable GetTestCasesForOpenApiComponentsComparerShou { Pointer = "#/requestBodies/requestBody1/content/application~1json/schema/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/requestBodies/requestBody1/content/application~1json/schema/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null }, new OpenApiDifference @@ -400,49 +400,49 @@ public static IEnumerable GetTestCasesForOpenApiComponentsComparerShou Pointer = "#/requestBodies/requestBody1/content/application~1json/schema/properties/property6/properties/property6/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/requestBodies/requestBody1/content/application~1json/schema/properties/property6/properties/property6/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null }, new OpenApiDifference { Pointer = "#/schemas/schemaObject1/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/schemas/schemaObject1/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null }, new OpenApiDifference @@ -450,49 +450,49 @@ public static IEnumerable GetTestCasesForOpenApiComponentsComparerShou Pointer = "#/schemas/schemaObject1/properties/property6/properties/property6/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/schemas/schemaObject1/properties/property6/properties/property6/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null }, new OpenApiDifference { Pointer = "#/schemas/schemaObject2/properties/property6/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/schemas/schemaObject2/properties/property6/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null }, new OpenApiDifference @@ -500,25 +500,25 @@ public static IEnumerable GetTestCasesForOpenApiComponentsComparerShou Pointer = "#/schemas/schemaObject2/properties/property6/properties/property6/properties/property6/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/schemas/schemaObject2/properties/property6/properties/property6/properties/property6/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null } } @@ -637,33 +637,32 @@ public static IEnumerable GetTestCasesForOpenApiComponentsComparerShou { Pointer = "#/requestBodies/requestBody2", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiRequestBody), SourceValue = null, - TargetValue = new KeyValuePair("requestBody2", - new OpenApiRequestBody + TargetValue = new OpenApiRequestBody + { + Description = "description", + Required = true, + Content = { - Description = "description", - Required = true, - Content = + ["application/json"] = new OpenApiMediaType { - ["application/json"] = new OpenApiMediaType + Schema = new OpenApiSchema { - Schema = new OpenApiSchema - { - Type = "string" - } + Type = "string" } } - }) + } + } }, new OpenApiDifference { Pointer = "#/schemas/schemaObject2", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("schemaObject2", new OpenApiSchema + TargetValue = new OpenApiSchema { Properties = new Dictionary { @@ -672,7 +671,7 @@ public static IEnumerable GetTestCasesForOpenApiComponentsComparerShou Type = "integer" } } - }) + } } } }; diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiInfoComparerTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiInfoComparerTests.cs new file mode 100644 index 000000000..d221eb87b --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiInfoComparerTests.cs @@ -0,0 +1,297 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Services; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.OpenApi.Tests.Services +{ + [Collection("DefaultSettings")] + public class OpenApiInfoComparerTests + { + 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 OpenApiInfoComparerTests(ITestOutputHelper output) + { + _output = output; + } + + public static IEnumerable GetTestCasesForOpenApiInfoComparerShouldSucceed() + { + yield return new object[] + { + "Differences in title, description, version and tos", + new OpenApiInfo + { + Title = "Test title", + Description = "Test description", + Version = "Test version", + TermsOfService = new Uri("http://localhost/1") + }, + new OpenApiInfo + { + Title = "Test title updated", + Description = "Test description updated", + Version = "Test version updated", + TermsOfService = new Uri("http://localhost/2") + }, + new List + { + new OpenApiDifference + { + Pointer = "#/title", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(string), + TargetValue = "Test title updated", + SourceValue = "Test title" + }, + new OpenApiDifference + { + Pointer = "#/description", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(string), + TargetValue = "Test description updated", + SourceValue = "Test description" + }, + new OpenApiDifference + { + Pointer = "#/version", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(string), + TargetValue = "Test version updated", + SourceValue = "Test version" + }, + new OpenApiDifference + { + Pointer = "#/termsOfService", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(Uri), + TargetValue = new Uri("http://localhost/2"), + SourceValue = new Uri("http://localhost/1") + } + } + }; + } + + [Theory] + [MemberData(nameof(GetTestCasesForOpenApiInfoComparerShouldSucceed))] + public void OpenApiInfoComparerShouldSucceed( + string testCaseName, + OpenApiInfo source, + OpenApiInfo target, + List expectedDifferences) + { + _output.WriteLine(testCaseName); + + var comparisonContext = new ComparisonContext(new OpenApiComparerFactory(), _sourceDocument, + _targetDocument); + var comparer = new OpenApiInfoComparer(); + comparer.Compare(source, target, comparisonContext); + + var differences = comparisonContext.OpenApiDifferences.ToList(); + differences.Count().ShouldBeEquivalentTo(expectedDifferences.Count); + + differences.ShouldBeEquivalentTo(expectedDifferences); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiParameterComparerTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiParameterComparerTests.cs index 4bcc4df02..c5460f886 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiParameterComparerTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiParameterComparerTests.cs @@ -344,29 +344,28 @@ public static IEnumerable GetTestCasesForOpenApiParameterComparerShoul { Pointer = "#/content/text~1plain", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), - TargetValue = new KeyValuePair("text/plain", new OpenApiMediaType + OpenApiComparedElementType = typeof(OpenApiMediaType), + TargetValue = new OpenApiMediaType { Schema = new OpenApiSchema { Type = "string" } - }), + }, SourceValue = null }, new OpenApiDifference { Pointer = "#/content/application~1json", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("application/json", - new OpenApiMediaType + OpenApiComparedElementType = typeof(OpenApiMediaType), + SourceValue = new OpenApiMediaType + { + Schema = new OpenApiSchema { - Schema = new OpenApiSchema - { - Type = "string" - } - }), + Type = "string" + } + }, TargetValue = null }, new OpenApiDifference diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiRequestBodyComparerTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiRequestBodyComparerTests.cs index 211df3c1e..1748dc82c 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiRequestBodyComparerTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiRequestBodyComparerTests.cs @@ -312,30 +312,29 @@ public static IEnumerable GetTestCasesForOpenApiRequestBodyComparerSho { Pointer = "#/content/application~1xml", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiMediaType), SourceValue = null, - TargetValue = new KeyValuePair("application/xml", new OpenApiMediaType + TargetValue = new OpenApiMediaType { Schema = new OpenApiSchema { Type = "string" } - }) + } }, new OpenApiDifference { Pointer = "#/content/application~1json", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiMediaType), TargetValue = null, - SourceValue = new KeyValuePair("application/json", - new OpenApiMediaType + SourceValue = new OpenApiMediaType + { + Schema = new OpenApiSchema { - Schema = new OpenApiSchema - { - Type = "string" - } - }) + Type = "string" + } + } } } }; @@ -513,24 +512,24 @@ public static IEnumerable GetTestCasesForOpenApiRequestBodyComparerSho { Pointer = "#/content/application~1json/schema/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/content/application~1json/schema/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null }, new OpenApiDifference @@ -538,25 +537,25 @@ public static IEnumerable GetTestCasesForOpenApiRequestBodyComparerSho Pointer = "#/content/application~1json/schema/properties/property6/properties/property6/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/content/application~1json/schema/properties/property6/properties/property6/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null } } diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiResponsesComparerTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiResponsesComparerTests.cs index 8e61fb3f9..c55897eb9 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiResponsesComparerTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiResponsesComparerTests.cs @@ -336,9 +336,9 @@ public static IEnumerable GetTestCasesForOpenApiResponsesComparerShoul { Pointer = "#/400", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiResponse), SourceValue = null, - TargetValue = new KeyValuePair("400", new OpenApiResponse + TargetValue = new OpenApiResponse { Description = "An updated complex object array response", Content = @@ -359,15 +359,15 @@ public static IEnumerable GetTestCasesForOpenApiResponsesComparerShoul } } } - }) + } }, new OpenApiDifference { Pointer = "#/200", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiResponse), TargetValue = null, - SourceValue = new KeyValuePair("200", new OpenApiResponse + SourceValue = new OpenApiResponse { Description = "A complex object array response", Content = @@ -388,7 +388,7 @@ public static IEnumerable GetTestCasesForOpenApiResponsesComparerShoul } } } - }) + } } } }; @@ -459,32 +459,31 @@ public static IEnumerable GetTestCasesForOpenApiResponsesComparerShoul { Pointer = "#/200/content/application~1json", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiMediaType), SourceValue = null, - TargetValue = new KeyValuePair("application/json", - new OpenApiMediaType + TargetValue = new OpenApiMediaType + { + Schema = new OpenApiSchema { - Schema = new OpenApiSchema + Type = "array", + Items = new OpenApiSchema { - Type = "array", - Items = new OpenApiSchema + Reference = new OpenApiReference { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "schemaObject1" - } + Type = ReferenceType.Schema, + Id = "schemaObject1" } } - }) + } + } }, new OpenApiDifference { Pointer = "#/200/content/text~1plain", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiMediaType), TargetValue = null, - SourceValue = new KeyValuePair("text/plain", new OpenApiMediaType + SourceValue = new OpenApiMediaType { Schema = new OpenApiSchema { @@ -498,7 +497,7 @@ public static IEnumerable GetTestCasesForOpenApiResponsesComparerShoul } } } - }) + } } } }; @@ -744,24 +743,24 @@ public static IEnumerable GetTestCasesForOpenApiResponsesComparerShoul { Pointer = "#/200/content/application~1json/schema/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/200/content/application~1json/schema/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null }, new OpenApiDifference @@ -769,25 +768,25 @@ public static IEnumerable GetTestCasesForOpenApiResponsesComparerShoul Pointer = "#/200/content/application~1json/schema/properties/property6/properties/property6/properties/property5", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, - OpenApiComparedElementType = typeof(KeyValuePair), + OpenApiComparedElementType = typeof(OpenApiSchema), SourceValue = null, - TargetValue = new KeyValuePair("property5", new OpenApiSchema + TargetValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }) + } }, new OpenApiDifference { Pointer = "#/200/content/application~1json/schema/properties/property6/properties/property6/properties/property7", OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, - OpenApiComparedElementType = typeof(KeyValuePair), - SourceValue = new KeyValuePair("property7", new OpenApiSchema + OpenApiComparedElementType = typeof(OpenApiSchema), + SourceValue = new OpenApiSchema { Type = "string", MaxLength = 15 - }), + }, TargetValue = null } } diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiSecurityRequirementComparerTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiSecurityRequirementComparerTests.cs new file mode 100644 index 000000000..0c1d6f8a0 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiSecurityRequirementComparerTests.cs @@ -0,0 +1,289 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Services; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.OpenApi.Tests.Services +{ + [Collection("DefaultSettings")] + public class OpenApiSecurityRequirementComparerTests + { + private readonly ITestOutputHelper _output; + + private readonly OpenApiDocument _sourceDocument = new OpenApiDocument + { + Components = new OpenApiComponents + { + SecuritySchemes = new Dictionary + { + { + "scheme1", new OpenApiSecurityScheme + { + Description = "Test", + Name = "Test" + } + }, + { + "scheme2", new OpenApiSecurityScheme + { + Description = "Test", + Name = "Test" + } + }, + { + "scheme3", new OpenApiSecurityScheme + { + Description = "Test", + Name = "Test" + } + } + } + } + }; + + private readonly OpenApiDocument _targetDocument = new OpenApiDocument + { + Components = new OpenApiComponents + { + SecuritySchemes = new Dictionary + { + { + "scheme1", new OpenApiSecurityScheme + { + Description = "Test Updated", + Name = "Test" + } + }, + { + "scheme2", new OpenApiSecurityScheme + { + Description = "Test", + Name = "Test" + } + }, + { + "scheme4", new OpenApiSecurityScheme + { + Description = "Test", + Name = "Test" + } + } + } + } + }; + + public OpenApiSecurityRequirementComparerTests(ITestOutputHelper output) + { + _output = output; + } + + public static IEnumerable GetTestCasesForOpenApiSecurityRequirementComparerShouldSucceed() + { + yield return new object[] + { + "New Removed And updated schemes", + new OpenApiSecurityRequirement + { + [ + new OpenApiSecurityScheme + { + Reference = new OpenApiReference {Type = ReferenceType.SecurityScheme, Id = "scheme1"} + } + ] = new List + { + "scope1", + "scope2", + "scope3" + }, + [ + new OpenApiSecurityScheme + { + Reference = new OpenApiReference {Type = ReferenceType.SecurityScheme, Id = "scheme2"} + } + ] = new List + { + "scope4", + "scope5" + }, + [ + new OpenApiSecurityScheme + { + Reference = new OpenApiReference {Type = ReferenceType.SecurityScheme, Id = "scheme3"} + } + ] = new List() + }, + new OpenApiSecurityRequirement + { + [ + new OpenApiSecurityScheme + { + Reference = new OpenApiReference {Type = ReferenceType.SecurityScheme, Id = "scheme1"} + } + ] = new List + { + "scope1", + "scope2", + "scope3" + }, + [ + new OpenApiSecurityScheme + { + Reference = new OpenApiReference {Type = ReferenceType.SecurityScheme, Id = "scheme2"} + } + ] = new List + { + "scope4", + "scope5" + }, + [ + new OpenApiSecurityScheme + { + Reference = new OpenApiReference {Type = ReferenceType.SecurityScheme, Id = "scheme4"} + } + ] = new List() + }, + new List + { + new OpenApiDifference + { + Pointer = "#/scheme4", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Add, + OpenApiComparedElementType = typeof(IList), + SourceValue = null, + TargetValue = new List() + }, + new OpenApiDifference + { + Pointer = "#/scheme1/description", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(string), + SourceValue = "Test", + TargetValue = "Test Updated" + }, + new OpenApiDifference + { + Pointer = "#/scheme3", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Remove, + OpenApiComparedElementType = typeof(IList), + SourceValue = new List(), + TargetValue = null + } + } + }; + + yield return new object[] + { + "Source and target are null", + null, + null, + new List() + }; + + yield return new object[] + { + "Source is null", + null, + new OpenApiSecurityRequirement + { + [ + new OpenApiSecurityScheme + { + Reference = new OpenApiReference {Type = ReferenceType.SecurityScheme, Id = "scheme1"} + } + ] = new List() + }, + new List + { + new OpenApiDifference + { + Pointer = "#/", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(OpenApiSecurityRequirement), + SourceValue = null, + TargetValue = new OpenApiSecurityRequirement + { + [ + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "scheme1" + } + } + ] = new List() + } + } + } + }; + + yield return new object[] + { + "Target is null", + new OpenApiSecurityRequirement + { + [ + new OpenApiSecurityScheme + { + Reference = new OpenApiReference {Type = ReferenceType.SecurityScheme, Id = "scheme1"} + } + ] = new List() + }, + null, + new List + { + new OpenApiDifference + { + Pointer = "#/", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(OpenApiSecurityRequirement), + SourceValue = new OpenApiSecurityRequirement + { + [ + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "scheme1" + } + } + ] = new List() + }, + TargetValue = null + } + } + }; + } + + [Theory] + [MemberData(nameof(GetTestCasesForOpenApiSecurityRequirementComparerShouldSucceed))] + public void OpenApiSecurityRequirementComparerShouldSucceed( + string testCaseName, + OpenApiSecurityRequirement source, + OpenApiSecurityRequirement target, + List expectedDifferences) + + + { + _output.WriteLine(testCaseName); + + var comparisonContext = new ComparisonContext(new OpenApiComparerFactory(), _sourceDocument, + _targetDocument); + var comparer = new OpenApiSecurityRequirementComparer(); + comparer.Compare(source, target, comparisonContext); + + var differences = comparisonContext.OpenApiDifferences.ToList(); + + differences.Count().ShouldBeEquivalentTo(expectedDifferences.Count); + + differences.ShouldBeEquivalentTo(expectedDifferences); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiSecuritySchemeComparerTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiSecuritySchemeComparerTests.cs new file mode 100644 index 000000000..d576845c3 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiSecuritySchemeComparerTests.cs @@ -0,0 +1,308 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Services; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.OpenApi.Tests.Services +{ + [Collection("DefaultSettings")] + public class OpenApiSecuritySchemeComparerTests + { + private readonly ITestOutputHelper _output; + + private readonly OpenApiDocument _sourceDocument = new OpenApiDocument + { + Components = new OpenApiComponents + { + SecuritySchemes = new Dictionary + { + { + "scheme1", new OpenApiSecurityScheme + { + Description = "Test", + Name = "Test", + Flows = new OpenApiOAuthFlows + { + Implicit = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri("http://localhost/1") + }, + AuthorizationCode = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri("http://localhost/2") + } + } + } + }, + { + "scheme2", new OpenApiSecurityScheme + { + Description = "Test", + Name = "Test" + } + }, + { + "scheme3", new OpenApiSecurityScheme + { + Description = "Test", + Name = "Test" + } + } + } + } + }; + + private readonly OpenApiDocument _targetDocument = new OpenApiDocument + { + Components = new OpenApiComponents + { + SecuritySchemes = new Dictionary + { + { + "scheme1", new OpenApiSecurityScheme + { + Description = "Test", + Name = "Test", + Flows = new OpenApiOAuthFlows + { + Implicit = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri("http://localhost/3") + }, + ClientCredentials = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri("http://localhost/2") + } + } + } + }, + { + "scheme2", new OpenApiSecurityScheme + { + Description = "Test", + Name = "Test" + } + }, + { + "scheme4", new OpenApiSecurityScheme + { + Description = "Test", + Name = "Test" + } + } + } + } + }; + + public OpenApiSecuritySchemeComparerTests(ITestOutputHelper output) + { + _output = output; + } + + public static IEnumerable GetTestCasesForOpenApiSecuritySchemeComparerShouldSucceed() + { + yield return new object[] + { + "Updated Type, Description, Name, In, BearerFormat, OpenIdConnectUrl", + new OpenApiSecurityScheme + { + Type = SecuritySchemeType.ApiKey, + Description = "Test Description", + Name = "Test Name", + In = ParameterLocation.Path, + OpenIdConnectUrl = new Uri("http://localhost:1"), + BearerFormat = "Test Format" + }, + new OpenApiSecurityScheme + { + Type = SecuritySchemeType.Http, + Description = "Test Description Updated", + Name = "Test Name Updated", + Scheme = "basic" + }, + new List + { + new OpenApiDifference + { + Pointer = "#/type", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(SecuritySchemeType), + SourceValue = SecuritySchemeType.ApiKey, + TargetValue = SecuritySchemeType.Http + }, + new OpenApiDifference + { + Pointer = "#/description", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(string), + SourceValue = "Test Description", + TargetValue = "Test Description Updated" + }, + new OpenApiDifference + { + Pointer = "#/name", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(string), + SourceValue = "Test Name", + TargetValue = "Test Name Updated" + }, + new OpenApiDifference + { + Pointer = "#/in", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(ParameterLocation), + SourceValue = ParameterLocation.Path, + TargetValue = ParameterLocation.Query + }, + new OpenApiDifference + { + Pointer = "#/bearerFormat", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(string), + SourceValue = "Test Format", + TargetValue = null + }, + new OpenApiDifference + { + Pointer = "#/openIdConnectUrl", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(Uri), + SourceValue = new Uri("http://localhost:1"), + TargetValue = null + }, + new OpenApiDifference + { + Pointer = "#/scheme", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(string), + SourceValue = null, + TargetValue = "basic" + } + } + }; + + yield return new object[] + { + "Difference in reference id", + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Id = "scheme1", + Type = ReferenceType.SecurityScheme + } + }, + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Id = "scheme2", + Type = ReferenceType.SecurityScheme + } + }, + new List + { + new OpenApiDifference + { + Pointer = "#/$ref", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(OpenApiReference), + SourceValue = new OpenApiReference + { + Id = "scheme1", + Type = ReferenceType.SecurityScheme + }, + TargetValue = new OpenApiReference + { + Id = "scheme2", + Type = ReferenceType.SecurityScheme + } + } + } + }; + + yield return new object[] + { + "New, Removed and Updated OAuthFlows", + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Id = "scheme1", + Type = ReferenceType.SecurityScheme + } + }, + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Id = "scheme1", + Type = ReferenceType.SecurityScheme + } + }, + new List + { + new OpenApiDifference + { + Pointer = "#/flows/implicit/authorizationUrl", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(Uri), + SourceValue = new Uri("http://localhost/1"), + TargetValue = new Uri("http://localhost/3") + }, + new OpenApiDifference + { + Pointer = "#/flows/authorizationCode", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(OpenApiOAuthFlow), + SourceValue = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri("http://localhost/2") + }, + TargetValue = null + }, + new OpenApiDifference + { + Pointer = "#/flows/clientCredentials", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(OpenApiOAuthFlow), + SourceValue = null, + TargetValue = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri("http://localhost/2") + } + } + } + }; + } + + [Theory] + [MemberData(nameof(GetTestCasesForOpenApiSecuritySchemeComparerShouldSucceed))] + public void OpenApiSecuritySchemeComparerShouldSucceed( + string testCaseName, + OpenApiSecurityScheme source, + OpenApiSecurityScheme target, + List expectedDifferences) + { + _output.WriteLine(testCaseName); + + var comparisonContext = new ComparisonContext(new OpenApiComparerFactory(), _sourceDocument, + _targetDocument); + var comparer = new OpenApiSecuritySchemeComparer(); + comparer.Compare(source, target, comparisonContext); + + var differences = comparisonContext.OpenApiDifferences.ToList(); + + differences.Count().ShouldBeEquivalentTo(expectedDifferences.Count); + + differences.ShouldBeEquivalentTo(expectedDifferences); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiTagComparerTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiTagComparerTests.cs new file mode 100644 index 000000000..ea6be3b0e --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiTagComparerTests.cs @@ -0,0 +1,304 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Services; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.OpenApi.Tests.Services +{ + [Collection("DefaultSettings")] + public class OpenApiTagComparerTests + { + 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 OpenApiTagComparerTests(ITestOutputHelper output) + { + _output = output; + } + + public static IEnumerable GetTestCasesForOpenApiTagComparerShouldSucceed() + { + // Differences in name, description and external docs + yield return new object[] + { + "Differences in name, description and external docs", + new OpenApiTag + { + Description = "test description", + Name = "test name", + ExternalDocs = new OpenApiExternalDocs + { + Description = "test description", + Url = new Uri("http://localhost/doc") + } + }, + new OpenApiTag + { + Description = "test description updated", + Name = "test name updated", + ExternalDocs = new OpenApiExternalDocs + { + Description = "test description updated", + Url = new Uri("http://localhost/updated") + } + }, + new List + { + new OpenApiDifference + { + Pointer = "#/description", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(string), + SourceValue = "test description", + TargetValue = "test description updated" + }, + new OpenApiDifference + { + Pointer = "#/name", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(string), + SourceValue = "test name", + TargetValue = "test name updated" + }, + new OpenApiDifference + { + Pointer = "#/externalDocs/description", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(string), + SourceValue = "test description", + TargetValue = "test description updated" + }, + new OpenApiDifference + { + Pointer = "#/externalDocs/url", + OpenApiDifferenceOperation = OpenApiDifferenceOperation.Update, + OpenApiComparedElementType = typeof(Uri), + SourceValue = new Uri("http://localhost/doc"), + TargetValue = new Uri("http://localhost/updated") + } + } + }; + } + + [Theory] + [MemberData(nameof(GetTestCasesForOpenApiTagComparerShouldSucceed))] + public void OpenApiTagServerVariableComparerShouldSucceed( + string testCaseName, + OpenApiTag source, + OpenApiTag target, + List expectedDifferences) + { + _output.WriteLine(testCaseName); + + var comparisonContext = new ComparisonContext(new OpenApiComparerFactory(), _sourceDocument, + _targetDocument); + var comparer = new OpenApiTagComparer(); + comparer.Compare(source, target, comparisonContext); + + var differences = comparisonContext.OpenApiDifferences.ToList(); + differences.Count().ShouldBeEquivalentTo(expectedDifferences.Count); + + differences.ShouldBeEquivalentTo(expectedDifferences); + } + } +} \ No newline at end of file