diff --git a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs index 20ce8bf53..0a79481f0 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs @@ -28,21 +28,22 @@ public ISet? Tags } set { - if (value is null) - { - _tags = null; - return; - } - _tags = value switch - { - HashSet tags when !ReferenceEquals(tags.Comparer, EqualityComparer.Default) => value, - SortedSet => value, + _tags = CopyTags(value); + } + } + + private static ISet? CopyTags(ISet? tags, bool copy = false) + { + return tags switch + { + null => null, + HashSet hashSet when !ReferenceEquals(hashSet.Comparer, EqualityComparer.Default) => copy ? new HashSet(hashSet, hashSet.Comparer) : hashSet, + SortedSet sorted => copy ? new SortedSet(sorted, sorted.Comparer) : sorted, #if NET - ImmutableSortedSet => value, + ImmutableSortedSet => tags, #endif - _ => new HashSet(value, OpenApiTagComparer.Instance), - }; - } + _ => new HashSet(tags, OpenApiTagComparer.Instance), + }; } /// @@ -139,7 +140,7 @@ public OpenApiOperation() { } public OpenApiOperation(OpenApiOperation operation) { Utils.CheckArgumentNull(operation); - Tags = operation.Tags != null ? new HashSet(operation.Tags) : null; + Tags = CopyTags(operation.Tags, copy: true); Summary = operation.Summary ?? Summary; Description = operation.Description ?? Description; ExternalDocs = operation.ExternalDocs != null ? new(operation.ExternalDocs) : null; diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs index 46e25c183..b43ef7b53 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs @@ -939,5 +939,61 @@ public void TagsSupportsImmutableSortedSets() Assert.Equal(["tagC", "tagB", "tagA"], names); Assert.IsType>(operation.Tags); } + + [Fact] + public void OpenApiOperationCopyConstructorPreservesCustomComparer() + { + var operation = new OpenApiOperation + { + Tags = new HashSet(new CaseInsensitiveOpenApiTagReferenceEqualityComparer()) + { + new("Tag1") + } + }; + + var copy = new OpenApiOperation(operation); + Assert.NotNull(copy.Tags); + Assert.False(copy.Tags.Add(new OpenApiTagReference("tag1"))); + Assert.Single(copy.Tags); + } + + [Fact] + public void OpenApiOperationCopyConstructorPreservesSortedSet() + { + var operation = new OpenApiOperation + { + Tags = new SortedSet(new DescendingOpenApiTagReferenceComparer()) + { + new("tagB"), + new("tagA"), + new("tagC"), + } + }; + + var copy = new OpenApiOperation(operation); + Assert.IsType>(copy.Tags); + var copyNames = copy.Tags.Select(t => t.Name); + Assert.Equal(["tagC", "tagB", "tagA"], copyNames); + } + + [Fact] + public void OpenApiOperationCopyConstructorPreservesImmutableSortedSet() + { + var operation = new OpenApiOperation + { + Tags = ImmutableSortedSet.Create( + new DescendingOpenApiTagReferenceComparer(), + [ + new OpenApiTagReference("tagB"), + new OpenApiTagReference("tagA"), + new OpenApiTagReference("tagC"), + ]), + }; + + var copy = new OpenApiOperation(operation); + Assert.IsType>(copy.Tags); + var copyNames = copy.Tags.Select(t => t.Name); + Assert.Equal(["tagC", "tagB", "tagA"], copyNames); + } } }