Skip to content

Commit 122e8fe

Browse files
Fix OpenApiTagComparer behaviour
Update `OpenApiTagComparer` to behave intuitively with `OpenApiTagReference` instances pointing to tags not defined in an `OpenApiDocument`. Contributes to #2319.
1 parent 25136af commit 122e8fe

File tree

3 files changed

+128
-2
lines changed

3 files changed

+128
-2
lines changed

src/Microsoft.OpenApi/Models/References/OpenApiTagReference.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

4-
using System;
54
using System.Collections.Generic;
65
using System.Linq;
76
using Microsoft.OpenApi.Interfaces;

src/Microsoft.OpenApi/OpenApiTagComparer.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using Microsoft.OpenApi.Models.Interfaces;
4+
using Microsoft.OpenApi.Models.References;
45

56
namespace Microsoft.OpenApi;
67

@@ -31,6 +32,10 @@ public bool Equals(IOpenApiTag? x, IOpenApiTag? y)
3132
{
3233
return true;
3334
}
35+
if (x is OpenApiTagReference referenceX && y is OpenApiTagReference referenceY)
36+
{
37+
return StringComparer.Equals(referenceX.Name ?? referenceX.Reference.Id, referenceY.Name ?? referenceY.Reference.Id);
38+
}
3439
return StringComparer.Equals(x.Name, y.Name);
3540
}
3641

@@ -41,5 +46,13 @@ public bool Equals(IOpenApiTag? x, IOpenApiTag? y)
4146
internal static readonly StringComparer StringComparer = StringComparer.Ordinal;
4247

4348
/// <inheritdoc/>
44-
public int GetHashCode(IOpenApiTag obj) => string.IsNullOrEmpty(obj?.Name) ? 0 : StringComparer.GetHashCode(obj!.Name);
49+
public int GetHashCode(IOpenApiTag obj)
50+
{
51+
string? value = obj?.Name;
52+
if (value is null && obj is OpenApiTagReference reference)
53+
{
54+
value = reference.Reference.Id;
55+
}
56+
return string.IsNullOrEmpty(value) ? 0 : StringComparer.GetHashCode(value);
57+
}
4558
}

test/Microsoft.OpenApi.Tests/OpenApiTagComparerTests.cs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
13
using Microsoft.OpenApi.Models;
4+
using Microsoft.OpenApi.Models.References;
25
using Xunit;
36

47
namespace Microsoft.OpenApi.Tests;
58

69
public class OpenApiTagComparerTests
710
{
811
private readonly OpenApiTagComparer _comparer = OpenApiTagComparer.Instance;
12+
913
[Fact]
1014
public void Defensive()
1115
{
@@ -16,13 +20,15 @@ public void Defensive()
1620
Assert.Equal(0, _comparer.GetHashCode(null));
1721
Assert.Equal(0, _comparer.GetHashCode(new OpenApiTag()));
1822
}
23+
1924
[Fact]
2025
public void SameNamesAreEqual()
2126
{
2227
var openApiTag1 = new OpenApiTag { Name = "tag" };
2328
var openApiTag2 = new OpenApiTag { Name = "tag" };
2429
Assert.True(_comparer.Equals(openApiTag1, openApiTag2));
2530
}
31+
2632
[Fact]
2733
public void SameInstanceAreEqual()
2834
{
@@ -37,4 +43,112 @@ public void DifferentCasingAreNotEquals()
3743
var openApiTag2 = new OpenApiTag { Name = "TAG" };
3844
Assert.False(_comparer.Equals(openApiTag1, openApiTag2));
3945
}
46+
47+
[Fact]
48+
public void WorksCorrectlyWithHashSetOfTags()
49+
{
50+
var tags = new HashSet<OpenApiTag>(_comparer)
51+
{
52+
new() { Name = "one" },
53+
new() { Name = "two" },
54+
new() { Name = "two" },
55+
new() { Name = "three" }
56+
};
57+
58+
Assert.Equal(["one", "two", "three"], [.. tags.Select(t => t.Name)]);
59+
}
60+
61+
[Fact]
62+
public void SameReferenceInstanceAreEqual()
63+
{
64+
var openApiTag = new OpenApiTagReference("tag");
65+
Assert.True(_comparer.Equals(openApiTag, openApiTag));
66+
}
67+
68+
[Fact]
69+
public void SameReferenceIdsAreEqual()
70+
{
71+
var openApiTag1 = new OpenApiTagReference("tag");
72+
var openApiTag2 = new OpenApiTagReference("tag");
73+
Assert.True(_comparer.Equals(openApiTag1, openApiTag2));
74+
}
75+
76+
[Fact]
77+
public void SameReferenceIdAreEqualWithValidTagReferences()
78+
{
79+
var document = new OpenApiDocument
80+
{
81+
Tags = [new() { Name = "tag" }]
82+
};
83+
84+
var openApiTag1 = new OpenApiTagReference("tag", document);
85+
var openApiTag2 = new OpenApiTagReference("tag", document);
86+
Assert.True(_comparer.Equals(openApiTag1, openApiTag2));
87+
}
88+
89+
[Fact]
90+
public void DifferentReferenceIdAreNotEqualWithValidTagReferences()
91+
{
92+
var document = new OpenApiDocument
93+
{
94+
Tags =
95+
[
96+
new() { Name = "one" },
97+
new() { Name = "two" },
98+
]
99+
};
100+
101+
var openApiTag1 = new OpenApiTagReference("one", document);
102+
var openApiTag2 = new OpenApiTagReference("two", document);
103+
Assert.False(_comparer.Equals(openApiTag1, openApiTag2));
104+
}
105+
106+
[Fact]
107+
public void DifferentCasingReferenceIdsAreNotEqual()
108+
{
109+
var openApiTag1 = new OpenApiTagReference("tag");
110+
var openApiTag2 = new OpenApiTagReference("TAG");
111+
Assert.False(_comparer.Equals(openApiTag1, openApiTag2));
112+
}
113+
114+
[Fact] // See https://github.com/microsoft/OpenAPI.NET/issues/2319
115+
public void WorksCorrectlyWithHashSetOfReferences()
116+
{
117+
// The document intentionally does not contain the actual tags
118+
var document = new OpenApiDocument();
119+
120+
var tags = new HashSet<OpenApiTagReference>(_comparer)
121+
{
122+
new("one", document),
123+
new("two", document),
124+
new("two", document),
125+
new("three", document)
126+
};
127+
128+
Assert.Equal(["one", "two", "three"], [..tags.Select(t => t.Reference.Id)]);
129+
}
130+
131+
[Fact]
132+
public void WorksCorrectlyWithHashSetOfReferencesToValidTags()
133+
{
134+
var document = new OpenApiDocument
135+
{
136+
Tags =
137+
[
138+
new() { Name = "one" },
139+
new() { Name = "two" },
140+
new() { Name = "three" }
141+
]
142+
};
143+
144+
var tags = new HashSet<OpenApiTagReference>(_comparer)
145+
{
146+
new("one", document),
147+
new("two", document),
148+
new("two", document),
149+
new("three", document)
150+
};
151+
152+
Assert.Equal(["one", "two", "three"], [.. tags.Select(t => t.Reference.Id)]);
153+
}
40154
}

0 commit comments

Comments
 (0)