Skip to content

Commit 21488c6

Browse files
authored
Merge pull request #2870 from microsoft/chore/additional-coverage
test(coverage): raise merged coverage with targeted unit tests
2 parents a9bc177 + b65bbe5 commit 21488c6

15 files changed

Lines changed: 1212 additions & 0 deletions
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#nullable enable
2+
using System;
3+
using Microsoft.OpenApi.Hidi;
4+
using Xunit;
5+
6+
namespace Microsoft.OpenApi.Hidi.Tests;
7+
8+
public class OpenApiSpecVersionHelperTests
9+
{
10+
[Theory]
11+
[InlineData("2.0", OpenApiSpecVersion.OpenApi2_0)]
12+
[InlineData("3.0", OpenApiSpecVersion.OpenApi3_0)]
13+
[InlineData("3.1", OpenApiSpecVersion.OpenApi3_1)]
14+
[InlineData("3.2", OpenApiSpecVersion.OpenApi3_2)]
15+
[InlineData("4.0", OpenApiSpecVersion.OpenApi3_2)]
16+
public void TryParseOpenApiSpecVersionReturnsExpectedVersion(string version, OpenApiSpecVersion expectedVersion)
17+
{
18+
var result = OpenApiSpecVersionHelper.TryParseOpenApiSpecVersion(version);
19+
20+
Assert.Equal(expectedVersion, result);
21+
}
22+
23+
[Theory]
24+
[InlineData(null)]
25+
[InlineData("")]
26+
[InlineData("abc")]
27+
public void TryParseOpenApiSpecVersionThrowsForInvalidValues(string? version)
28+
{
29+
Assert.Throws<InvalidOperationException>(() => OpenApiSpecVersionHelper.TryParseOpenApiSpecVersion(version!));
30+
}
31+
}

test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,72 @@ public void ThrowsInvalidOperationExceptionInCreatePredicateWhenInvalidArguments
221221
Assert.Equal("Cannot specify both operationIds and tags at the same time.", message2);
222222
}
223223

224+
[Fact]
225+
public void ThrowsInvalidOperationExceptionWhenRequestUrlsAreCombinedWithOtherFilters()
226+
{
227+
var requestUrls = new Dictionary<string, List<string>>
228+
{
229+
["/users"] = ["GET"]
230+
};
231+
232+
var message = Assert.Throws<InvalidOperationException>(() =>
233+
OpenApiFilterService.CreatePredicate("users.user.ListUser", null, requestUrls, _openApiDocumentMock)).Message;
234+
235+
Assert.Equal("Cannot filter by Postman collection and either operationIds and tags at the same time.", message);
236+
}
237+
238+
[Fact]
239+
public void ThrowsWhenPredicateDoesNotMatchAnyPath()
240+
{
241+
var source = new OpenApiDocument
242+
{
243+
Info = new() { Title = "Test", Version = "1.0" },
244+
Paths = new()
245+
{
246+
["/test"] = new OpenApiPathItem
247+
{
248+
Operations = new()
249+
{
250+
[HttpMethod.Get] = new OpenApiOperation { OperationId = "getTest" }
251+
}
252+
}
253+
}
254+
};
255+
256+
var subset = OpenApiFilterService.CreateFilteredDocument(source, static (_, _, _) => false);
257+
258+
Assert.Empty(subset.Paths);
259+
}
260+
261+
[Fact]
262+
public void CreatePredicateMatchesAbsoluteUrlsWhenSourceHasNoServers()
263+
{
264+
var source = new OpenApiDocument
265+
{
266+
Info = new() { Title = "Test", Version = "v1" },
267+
Paths = new()
268+
{
269+
["/users"] = new OpenApiPathItem
270+
{
271+
Operations = new()
272+
{
273+
[HttpMethod.Get] = new OpenApiOperation { OperationId = "listUsers" }
274+
}
275+
}
276+
}
277+
};
278+
var requestUrls = new Dictionary<string, List<string>>
279+
{
280+
["https://graph.contoso.com/users"] = ["GET"]
281+
};
282+
283+
var predicate = OpenApiFilterService.CreatePredicate(requestUrls: requestUrls, source: source);
284+
var subset = OpenApiFilterService.CreateFilteredDocument(source, predicate);
285+
286+
Assert.Single(subset.Paths);
287+
Assert.True(subset.Paths.ContainsKey("/users"));
288+
}
289+
224290
[Fact]
225291
public async Task CopiesOverAllReferencedComponentsToTheSubsetDocumentCorrectly()
226292
{
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Net.Http;
4+
using Xunit;
5+
6+
namespace Microsoft.OpenApi.Hidi.Tests;
7+
8+
public class StatsVisitorTests
9+
{
10+
[Fact]
11+
public void GetStatisticsReportReflectsVisitedElements()
12+
{
13+
var document = new OpenApiDocument
14+
{
15+
Paths = new()
16+
{
17+
["/pets"] = new OpenApiPathItem
18+
{
19+
Operations = new()
20+
{
21+
[HttpMethod.Post] = new OpenApiOperation
22+
{
23+
Parameters =
24+
[
25+
new OpenApiParameter
26+
{
27+
Name = "expand",
28+
In = ParameterLocation.Query,
29+
Schema = new OpenApiSchema { Type = JsonSchemaType.String }
30+
}
31+
],
32+
RequestBody = new OpenApiRequestBody
33+
{
34+
Content = new Dictionary<string, IOpenApiMediaType>
35+
{
36+
["application/json"] = new OpenApiMediaType
37+
{
38+
Schema = new OpenApiSchema
39+
{
40+
Type = JsonSchemaType.Object,
41+
Properties = new Dictionary<string, IOpenApiSchema>
42+
{
43+
["name"] = new OpenApiSchema { Type = JsonSchemaType.String }
44+
}
45+
}
46+
}
47+
}
48+
},
49+
Responses = new OpenApiResponses
50+
{
51+
["200"] = new OpenApiResponse
52+
{
53+
Headers = new Dictionary<string, IOpenApiHeader>
54+
{
55+
["x-rate-limit"] = new OpenApiHeader
56+
{
57+
Schema = new OpenApiSchema { Type = JsonSchemaType.Integer }
58+
}
59+
},
60+
Links = new Dictionary<string, IOpenApiLink>
61+
{
62+
["next"] = new OpenApiLink()
63+
}
64+
}
65+
},
66+
Callbacks = new Dictionary<string, IOpenApiCallback>
67+
{
68+
["onData"] = new OpenApiCallback
69+
{
70+
PathItems = new Dictionary<RuntimeExpression, IOpenApiPathItem>
71+
{
72+
[RuntimeExpression.Build("$request.body#/callbackUrl")] = new OpenApiPathItem
73+
{
74+
Operations = new()
75+
{
76+
[HttpMethod.Post] = new OpenApiOperation
77+
{
78+
Responses = new OpenApiResponses
79+
{
80+
["202"] = new OpenApiResponse { Description = "Accepted" }
81+
}
82+
}
83+
}
84+
}
85+
}
86+
}
87+
}
88+
}
89+
}
90+
}
91+
}
92+
};
93+
94+
var visitor = new StatsVisitor();
95+
new OpenApiWalker(visitor).Walk(document);
96+
var report = visitor.GetStatisticsReport();
97+
98+
Assert.Equal(2, visitor.PathItemCount);
99+
Assert.Equal(2, visitor.OperationCount);
100+
Assert.Equal(1, visitor.ParameterCount);
101+
Assert.Equal(1, visitor.RequestBodyCount);
102+
Assert.Equal(2, visitor.ResponseCount);
103+
Assert.Equal(1, visitor.LinkCount);
104+
Assert.Equal(1, visitor.CallbackCount);
105+
Assert.Equal(4, visitor.SchemaCount);
106+
Assert.Contains("Path Items: 2", report, StringComparison.Ordinal);
107+
Assert.Contains("Callbacks: 1", report, StringComparison.Ordinal);
108+
Assert.Contains("Schemas: 4", report, StringComparison.Ordinal);
109+
}
110+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Microsoft.Extensions.Configuration;
4+
using Microsoft.OpenApi.Hidi.Utilities;
5+
using Microsoft.OpenApi.OData;
6+
using Xunit;
7+
8+
namespace Microsoft.OpenApi.Hidi.Tests;
9+
10+
public class SettingsUtilitiesTests
11+
{
12+
[Fact]
13+
public void GetOpenApiConvertSettingsThrowsWhenConfigurationIsNull()
14+
{
15+
Assert.Throws<ArgumentNullException>(() => SettingsUtilities.GetOpenApiConvertSettings(null!, null));
16+
}
17+
18+
[Fact]
19+
public void GetOpenApiConvertSettingsUsesMetadataVersionWhenSectionIsMissing()
20+
{
21+
var configuration = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string?>()).Build();
22+
23+
var settings = SettingsUtilities.GetOpenApiConvertSettings(configuration, "2.1");
24+
25+
Assert.Equal("2.1", settings.SemVerVersion);
26+
}
27+
28+
[Fact]
29+
public void GetOpenApiConvertSettingsBindsConfiguredValuesOverMetadataVersion()
30+
{
31+
var configuration = new ConfigurationBuilder()
32+
.AddInMemoryCollection(new Dictionary<string, string?>
33+
{
34+
[$"{nameof(OpenApiConvertSettings)}:{nameof(OpenApiConvertSettings.SemVerVersion)}"] = "3.0",
35+
[$"{nameof(OpenApiConvertSettings)}:{nameof(OpenApiConvertSettings.EnablePagination)}"] = bool.TrueString
36+
})
37+
.Build();
38+
39+
var settings = SettingsUtilities.GetOpenApiConvertSettings(configuration, "2.1");
40+
41+
Assert.Equal("3.0", settings.SemVerVersion);
42+
Assert.True(settings.EnablePagination);
43+
}
44+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
using System;
2+
using System.IO;
3+
using System.Text;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using Microsoft.OpenApi.Reader;
7+
using Microsoft.OpenApi.YamlReader;
8+
using Xunit;
9+
10+
namespace Microsoft.OpenApi.Readers.Tests;
11+
12+
public class OpenApiYamlReaderTests
13+
{
14+
private static readonly Uri DocumentLocation = new("https://contoso.test/openapi.yaml");
15+
16+
[Fact]
17+
public async Task ReadAsyncParsesDocumentsFromNonMemoryStreams()
18+
{
19+
var reader = new OpenApiYamlReader();
20+
await using var stream = new NonMemoryStream(CreateStream(
21+
"""
22+
openapi: 3.0.1
23+
info:
24+
title: Sample API
25+
version: 1.0.0
26+
paths: {}
27+
"""));
28+
29+
var result = await reader.ReadAsync(stream, DocumentLocation, SettingsFixture.ReaderSettings, CancellationToken.None);
30+
31+
Assert.NotNull(result.Document);
32+
Assert.Equal("Sample API", result.Document.Info.Title);
33+
Assert.Equal(OpenApiConstants.Yaml, result.Diagnostic.Format);
34+
}
35+
36+
[Fact]
37+
public void ReadThrowsWhenYamlDoesNotContainADocument()
38+
{
39+
var reader = new OpenApiYamlReader();
40+
using var stream = CreateStream(string.Empty);
41+
42+
var exception = Assert.Throws<InvalidOperationException>(() => reader.Read(stream, DocumentLocation, SettingsFixture.ReaderSettings));
43+
44+
Assert.Equal("No documents found in the YAML stream.", exception.Message);
45+
}
46+
47+
[Fact]
48+
public void ReadFragmentParsesSchemaFragments()
49+
{
50+
var reader = new OpenApiYamlReader();
51+
using var stream = CreateStream(
52+
"""
53+
type: string
54+
description: A reusable schema
55+
""");
56+
57+
var schema = reader.ReadFragment<OpenApiSchema>(
58+
stream,
59+
OpenApiSpecVersion.OpenApi3_0,
60+
new OpenApiDocument(),
61+
out var diagnostic);
62+
63+
Assert.NotNull(schema);
64+
Assert.Empty(diagnostic.Errors);
65+
Assert.Equal(JsonSchemaType.String, schema.Type);
66+
Assert.Equal("A reusable schema", schema.Description);
67+
}
68+
69+
[Fact]
70+
public void ReadThrowsWhenSettingsIsNull()
71+
{
72+
var reader = new OpenApiYamlReader();
73+
using var stream = CreateStream("openapi: 3.0.1");
74+
75+
Assert.Throws<ArgumentNullException>(() => reader.Read(stream, DocumentLocation, null!));
76+
}
77+
78+
private static MemoryStream CreateStream(string yaml)
79+
{
80+
return new MemoryStream(Encoding.UTF8.GetBytes(yaml));
81+
}
82+
83+
private sealed class NonMemoryStream(Stream innerStream) : Stream
84+
{
85+
public override bool CanRead => innerStream.CanRead;
86+
public override bool CanSeek => innerStream.CanSeek;
87+
public override bool CanWrite => innerStream.CanWrite;
88+
public override long Length => innerStream.Length;
89+
public override long Position
90+
{
91+
get => innerStream.Position;
92+
set => innerStream.Position = value;
93+
}
94+
95+
public override void Flush() => innerStream.Flush();
96+
public override int Read(byte[] buffer, int offset, int count) => innerStream.Read(buffer, offset, count);
97+
public override long Seek(long offset, SeekOrigin origin) => innerStream.Seek(offset, origin);
98+
public override void SetLength(long value) => innerStream.SetLength(value);
99+
public override void Write(byte[] buffer, int offset, int count) => innerStream.Write(buffer, offset, count);
100+
public override ValueTask DisposeAsync() => innerStream.DisposeAsync();
101+
protected override void Dispose(bool disposing)
102+
{
103+
if (disposing)
104+
{
105+
innerStream.Dispose();
106+
}
107+
108+
base.Dispose(disposing);
109+
}
110+
}
111+
}

0 commit comments

Comments
 (0)