From f2446946c1d1bca4ae77b42bd776b907b43a643a Mon Sep 17 00:00:00 2001 From: Stef Date: Thu, 2 Jan 2025 09:20:27 +0100 Subject: [PATCH 1/9] Fix google protobuf WellKnownTypes: Timestamp and Duration --- src/WireMock.Net/WireMock.Net.csproj | 2 +- .../Grpc/WireMockServerTests.Grpc.cs | 108 ++++++++++++++---- 2 files changed, 88 insertions(+), 22 deletions(-) diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj index 17711cbc..c9cf13f9 100644 --- a/src/WireMock.Net/WireMock.Net.csproj +++ b/src/WireMock.Net/WireMock.Net.csproj @@ -146,7 +146,7 @@ - + diff --git a/test/WireMock.Net.Tests/Grpc/WireMockServerTests.Grpc.cs b/test/WireMock.Net.Tests/Grpc/WireMockServerTests.Grpc.cs index 063a2c69..b3e2f222 100644 --- a/test/WireMock.Net.Tests/Grpc/WireMockServerTests.Grpc.cs +++ b/test/WireMock.Net.Tests/Grpc/WireMockServerTests.Grpc.cs @@ -48,6 +48,8 @@ message HelloReply { service Greeter { rpc SayNothing (google.protobuf.Empty) returns (google.protobuf.Empty); + rpc SayTimestamp (MyMessageTimestamp) returns (MyMessageTimestamp); + rpc SayDuration (MyMessageDuration) returns (MyMessageDuration); } message MyMessageTimestamp { @@ -128,8 +130,6 @@ public async Task WireMockServer_WithBodyAsProtoBuf_JsonPartialWildcardMatcher() // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); - - server.Stop(); } [Theory] @@ -171,16 +171,12 @@ public async Task WireMockServer_WithBodyAsProtoBuf(string data) var responseBytes = await response.Content.ReadAsByteArrayAsync(); Convert.ToBase64String(responseBytes).Should().Be("AAAAAAcKBWhlbGxv"); - - server.Stop(); } [Fact] - public async Task WireMockServer_WithBodyAsProtoBuf_WithWellKnownTypes() + public async Task WireMockServer_WithBodyAsProtoBuf_WithWellKnownTypes_Empty() { // Arrange - var bytes = Convert.FromBase64String("CgRzdGVm"); - using var server = WireMockServer.Start(); server @@ -198,6 +194,7 @@ public async Task WireMockServer_WithBodyAsProtoBuf_WithWellKnownTypes() ); // Act + var bytes = Convert.FromBase64String("CgRzdGVm"); var protoBuf = new ByteArrayContent(bytes); protoBuf.Headers.ContentType = new MediaTypeHeaderValue("application/grpc-web"); @@ -209,8 +206,90 @@ public async Task WireMockServer_WithBodyAsProtoBuf_WithWellKnownTypes() var responseBytes = await response.Content.ReadAsByteArrayAsync(); Convert.ToBase64String(responseBytes).Should().Be(""); + } + + [Fact] + public async Task WireMockServer_WithBodyAsProtoBuf_WithWellKnownTypes_Timestamp() + { + // Arrange + using var server = WireMockServer.Start(); + + server + .Given(Request.Create() + .UsingPost() + .WithPath("/grpc/Greeter/SayTimestamp") + .WithBody(new NotNullOrEmptyMatcher()) + ) + .RespondWith(Response.Create() + .WithBodyAsProtoBuf(ProtoDefinitionWithWellKnownTypes, "communication.api.v1.MyMessageTimestamp", + new + { + ts = new + { + seconds = 1722301323, + nanos = 12300 + } + } + ) + .WithTrailingHeader("grpc-status", "0") + .WithTransformer() + ); + + // Act + var bytes = Convert.FromBase64String("CgkIi/egtQYQuWA="); + var protoBuf = new ByteArrayContent(bytes); + protoBuf.Headers.ContentType = new MediaTypeHeaderValue("application/grpc-web"); + + var client = server.CreateClient(); + var response = await client.PostAsync("/grpc/Greeter/SayTimestamp", protoBuf); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.OK); + var responseBytes = await response.Content.ReadAsByteArrayAsync(); + + Convert.ToBase64String(responseBytes).Should().Be("AAAAAAsKCQiL96C1BhCMYA=="); + } - server.Stop(); + [Fact] + public async Task WireMockServer_WithBodyAsProtoBuf_WithWellKnownTypes_Duration() + { + // Arrange + using var server = WireMockServer.Start(); + + server + .Given(Request.Create() + .UsingPost() + .WithPath("/grpc/Greeter/SayDuration") + .WithBody(new NotNullOrEmptyMatcher()) + ) + .RespondWith(Response.Create() + .WithBodyAsProtoBuf(ProtoDefinitionWithWellKnownTypes, "communication.api.v1.MyMessageDuration", + new + { + du = new + { + seconds = 1722301323, + nanos = 12300 + } + } + ) + .WithTrailingHeader("grpc-status", "0") + .WithTransformer() + ); + + // Act + var bytes = Convert.FromBase64String("CgkIi/egtQYQuWA="); + var protoBuf = new ByteArrayContent(bytes); + protoBuf.Headers.ContentType = new MediaTypeHeaderValue("application/grpc-web"); + + var client = server.CreateClient(); + var response = await client.PostAsync("/grpc/Greeter/SayDuration", protoBuf); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.OK); + var responseBytes = await response.Content.ReadAsByteArrayAsync(); + + Convert.ToBase64String(responseBytes).Should().Be("AAAAAAsKCQiL96C1BhCMYA=="); } [Fact] @@ -251,8 +330,6 @@ public async Task WireMockServer_WithBodyAsProtoBuf_ServerProtoDefinition_WithWe var responseBytes = await response.Content.ReadAsByteArrayAsync(); Convert.ToBase64String(responseBytes).Should().Be(""); - - server.Stop(); } [Fact] @@ -294,8 +371,6 @@ public async Task WireMockServer_WithBodyAsProtoBuf_MultipleFiles() var responseBytes = await response.Content.ReadAsByteArrayAsync(); Convert.ToBase64String(responseBytes).Should().Be("AAAAAAcKBWhlbGxv"); - - server.Stop(); } [Fact] @@ -326,15 +401,12 @@ public async Task WireMockServer_WithBodyAsProtoBuf_InlineProtoDefinition_UsingG // Act var channel = GrpcChannel.ForAddress(server.Url!); - var client = new Greeter.GreeterClient(channel); var reply = await client.SayHelloAsync(new HelloRequest { Name = "stef" }); // Assert reply.Message.Should().Be("hello stef POST"); - - server.Stop(); } [Fact] @@ -367,15 +439,12 @@ public async Task WireMockServer_WithBodyAsProtoBuf_MappingProtoDefinition_Using // Act var channel = GrpcChannel.ForAddress(server.Url!); - var client = new Greeter.GreeterClient(channel); var reply = await client.SayHelloAsync(new HelloRequest { Name = "stef" }); // Assert reply.Message.Should().Be("hello stef POST"); - - server.Stop(); } [Fact] @@ -411,15 +480,12 @@ public async Task WireMockServer_WithBodyAsProtoBuf_ServerProtoDefinition_UsingG // Act var channel = GrpcChannel.ForAddress(server.Url!); - var client = new Greeter.GreeterClient(channel); var reply = await client.SayHelloAsync(new HelloRequest { Name = "stef" }); // Assert reply.Message.Should().Be("hello stef POST"); - - server.Stop(); } } #endif \ No newline at end of file From 38d78fc9b66dbb404607d216e1a33581f671ab7e Mon Sep 17 00:00:00 2001 From: Stef Date: Fri, 3 Jan 2025 11:02:12 +0100 Subject: [PATCH 2/9] Fix protobuf Empty --- .../Assertions/WireMockAssertions.AtUrl.cs | 2 +- .../Owin/Mappers/OwinResponseMapper.cs | 2 +- .../Request.WithBodyAsProtoBuf.cs | 1 - .../ResponseBuilders/Response.WithBody.cs | 10 +- src/WireMock.Net/Util/ProtoBufUtils.cs | 2 +- src/WireMock.Net/WireMock.Net.csproj | 3 +- .../IgnoreOnContinuousIntegrationFact.cs | 2 + .../Facts/RunOnDockerPlatformFact.cs | 2 + .../Grpc/WireMockServerTests.Grpc.cs | 121 +++++++++++++++++- test/WireMock.Net.Tests/Grpc/greet.proto | 38 +++--- .../WireMock.Net.Tests.csproj | 3 +- .../WireMock.Net.Tests/WireMockServerTests.cs | 10 +- 12 files changed, 160 insertions(+), 36 deletions(-) diff --git a/src/WireMock.Net.FluentAssertions/Assertions/WireMockAssertions.AtUrl.cs b/src/WireMock.Net.FluentAssertions/Assertions/WireMockAssertions.AtUrl.cs index b9e7ef14..2fe9b0ce 100644 --- a/src/WireMock.Net.FluentAssertions/Assertions/WireMockAssertions.AtUrl.cs +++ b/src/WireMock.Net.FluentAssertions/Assertions/WireMockAssertions.AtUrl.cs @@ -1,12 +1,12 @@ // Copyright © WireMock.Net -#pragma warning disable CS1591 using WireMock.Extensions; using WireMock.Matchers; // ReSharper disable once CheckNamespace namespace WireMock.FluentAssertions; +#pragma warning disable CS1591 public partial class WireMockAssertions { [CustomAssertion] diff --git a/src/WireMock.Net/Owin/Mappers/OwinResponseMapper.cs b/src/WireMock.Net/Owin/Mappers/OwinResponseMapper.cs index 85700942..b9645268 100644 --- a/src/WireMock.Net/Owin/Mappers/OwinResponseMapper.cs +++ b/src/WireMock.Net/Owin/Mappers/OwinResponseMapper.cs @@ -152,7 +152,7 @@ private bool IsFault(IResponseMessage responseMessage) #if PROTOBUF case BodyType.ProtoBuf: var protoDefinitions = bodyData.ProtoDefinition?.Invoke().Texts; - return await ProtoBufUtils.GetProtoBufMessageWithHeaderAsync(protoDefinitions, responseMessage.BodyData.ProtoBufMessageType, responseMessage.BodyData.BodyAsJson).ConfigureAwait(false); + return await ProtoBufUtils.GetProtoBufMessageWithHeaderAsync(protoDefinitions, bodyData.ProtoBufMessageType, bodyData.BodyAsJson).ConfigureAwait(false); #endif case BodyType.Bytes: diff --git a/src/WireMock.Net/RequestBuilders/Request.WithBodyAsProtoBuf.cs b/src/WireMock.Net/RequestBuilders/Request.WithBodyAsProtoBuf.cs index 61259644..a5c5e1db 100644 --- a/src/WireMock.Net/RequestBuilders/Request.WithBodyAsProtoBuf.cs +++ b/src/WireMock.Net/RequestBuilders/Request.WithBodyAsProtoBuf.cs @@ -1,7 +1,6 @@ // Copyright © WireMock.Net using System.Collections.Generic; -using System.Linq; using WireMock.Matchers; using WireMock.Matchers.Request; using WireMock.Models; diff --git a/src/WireMock.Net/ResponseBuilders/Response.WithBody.cs b/src/WireMock.Net/ResponseBuilders/Response.WithBody.cs index 7faedb1e..24135273 100644 --- a/src/WireMock.Net/ResponseBuilders/Response.WithBody.cs +++ b/src/WireMock.Net/ResponseBuilders/Response.WithBody.cs @@ -242,7 +242,7 @@ public IResponseBuilder WithBodyAsProtoBuf( Guard.NotNull(value); #if !PROTOBUF - throw new System.NotSupportedException("The WithBodyAsProtoBuf method can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower."); + throw new NotSupportedException("The WithBodyAsProtoBuf method can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower."); #else ResponseMessage.BodyDestination = null; ResponseMessage.BodyData = new BodyData @@ -252,8 +252,9 @@ public IResponseBuilder WithBodyAsProtoBuf( ProtoDefinition = () => new IdOrTexts(null, protoDefinitions), ProtoBufMessageType = messageType }; -#endif + return this; +#endif } /// @@ -268,7 +269,7 @@ public IResponseBuilder WithBodyAsProtoBuf( Guard.NotNull(value); #if !PROTOBUF - throw new System.NotSupportedException("The WithBodyAsProtoBuf method can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower."); + throw new NotSupportedException("The WithBodyAsProtoBuf method can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower."); #else ResponseMessage.BodyDestination = null; ResponseMessage.BodyData = new BodyData @@ -278,7 +279,8 @@ public IResponseBuilder WithBodyAsProtoBuf( ProtoDefinition = () => Mapping.ProtoDefinition ?? throw new WireMockException("ProtoDefinition cannot be resolved. You probably forgot to call .WithProtoDefinition(...) on the mapping."), ProtoBufMessageType = messageType }; -#endif + return this; +#endif } } \ No newline at end of file diff --git a/src/WireMock.Net/Util/ProtoBufUtils.cs b/src/WireMock.Net/Util/ProtoBufUtils.cs index 4fc8393b..a76d2171 100644 --- a/src/WireMock.Net/Util/ProtoBufUtils.cs +++ b/src/WireMock.Net/Util/ProtoBufUtils.cs @@ -26,7 +26,7 @@ internal static async Task GetProtoBufMessageWithHeaderAsync( } var resolver = new WireMockProtoFileResolver(protoDefinitions); - var request = new ConvertToProtoBufRequest(protoDefinitions[0], messageType, value, true) + var request = new ConvertToProtoBufRequest(protoDefinitions[0], messageType!, value, true) .WithProtoFileResolver(resolver); return await SingletonFactory diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj index c9cf13f9..6edee489 100644 --- a/src/WireMock.Net/WireMock.Net.csproj +++ b/src/WireMock.Net/WireMock.Net.csproj @@ -146,7 +146,7 @@ - + @@ -154,6 +154,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/test/WireMock.Net.Tests/Facts/IgnoreOnContinuousIntegrationFact.cs b/test/WireMock.Net.Tests/Facts/IgnoreOnContinuousIntegrationFact.cs index f8f70818..c740c700 100644 --- a/test/WireMock.Net.Tests/Facts/IgnoreOnContinuousIntegrationFact.cs +++ b/test/WireMock.Net.Tests/Facts/IgnoreOnContinuousIntegrationFact.cs @@ -1,10 +1,12 @@ // Copyright © WireMock.Net using System; +using System.Diagnostics.CodeAnalysis; using Xunit; namespace WireMock.Net.Tests.Facts; +[ExcludeFromCodeCoverage] public sealed class IgnoreOnContinuousIntegrationFact : FactAttribute { private const string SkipReason = "Ignore when run via CI/CD"; diff --git a/test/WireMock.Net.Tests/Facts/RunOnDockerPlatformFact.cs b/test/WireMock.Net.Tests/Facts/RunOnDockerPlatformFact.cs index 9d479a0f..d48f4433 100644 --- a/test/WireMock.Net.Tests/Facts/RunOnDockerPlatformFact.cs +++ b/test/WireMock.Net.Tests/Facts/RunOnDockerPlatformFact.cs @@ -1,11 +1,13 @@ // Copyright © WireMock.Net #if NET6_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using WireMock.Net.Testcontainers.Utils; using Xunit; namespace WireMock.Net.Tests.Facts; +[ExcludeFromCodeCoverage] public sealed class RunOnDockerPlatformFact : FactAttribute { public RunOnDockerPlatformFact(string platform) diff --git a/test/WireMock.Net.Tests/Grpc/WireMockServerTests.Grpc.cs b/test/WireMock.Net.Tests/Grpc/WireMockServerTests.Grpc.cs index b3e2f222..9061d738 100644 --- a/test/WireMock.Net.Tests/Grpc/WireMockServerTests.Grpc.cs +++ b/test/WireMock.Net.Tests/Grpc/WireMockServerTests.Grpc.cs @@ -7,6 +7,7 @@ using System.Net.Http.Headers; using System.Threading.Tasks; using FluentAssertions; +using Google.Protobuf.WellKnownTypes; using Greet; using Grpc.Net.Client; using WireMock.Matchers; @@ -17,6 +18,7 @@ // ReSharper disable once CheckNamespace namespace WireMock.Net.Tests; + public partial class WireMockServerTests { private const string ProtoDefinition = @" @@ -183,7 +185,6 @@ public async Task WireMockServer_WithBodyAsProtoBuf_WithWellKnownTypes_Empty() .Given(Request.Create() .UsingPost() .WithPath("/grpc/Greeter/SayNothing") - .WithBody(new NotNullOrEmptyMatcher()) ) .RespondWith(Response.Create() .WithBodyAsProtoBuf(ProtoDefinitionWithWellKnownTypes, "google.protobuf.Empty", @@ -205,7 +206,7 @@ public async Task WireMockServer_WithBodyAsProtoBuf_WithWellKnownTypes_Empty() response.StatusCode.Should().Be(HttpStatusCode.OK); var responseBytes = await response.Content.ReadAsByteArrayAsync(); - Convert.ToBase64String(responseBytes).Should().Be(""); + Convert.ToBase64String(responseBytes).Should().Be("AAAAAAA="); } [Fact] @@ -307,7 +308,6 @@ public async Task WireMockServer_WithBodyAsProtoBuf_ServerProtoDefinition_WithWe .Given(Request.Create() .UsingPost() .WithPath("/grpc/Greeter/SayNothing") - .WithBody(new NotNullOrEmptyMatcher()) ) .WithProtoDefinition(id) .RespondWith(Response.Create() @@ -329,7 +329,7 @@ public async Task WireMockServer_WithBodyAsProtoBuf_ServerProtoDefinition_WithWe response.StatusCode.Should().Be(HttpStatusCode.OK); var responseBytes = await response.Content.ReadAsByteArrayAsync(); - Convert.ToBase64String(responseBytes).Should().Be(""); + Convert.ToBase64String(responseBytes).Should().Be("AAAAAAA="); } [Fact] @@ -487,5 +487,118 @@ public async Task WireMockServer_WithBodyAsProtoBuf_ServerProtoDefinition_UsingG // Assert reply.Message.Should().Be("hello stef POST"); } + + [Fact] + public async Task WireMockServer_WithBodyAsProtoBuf_WithWellKnownTypes_Empty_UsingGrpcGeneratedClient() + { + // Arrange + var definition = await System.IO.File.ReadAllTextAsync("./Grpc/greet.proto"); + + using var server = WireMockServer.Start(useHttp2: true); + + server + .Given(Request.Create() + .UsingPost() + .WithPath("/greet.Greeter/SayNothing") + ) + .RespondWith(Response.Create() + .WithHeader("Content-Type", "application/grpc") + .WithTrailingHeader("grpc-status", "0") + .WithBodyAsProtoBuf(definition, "google.protobuf.Empty", + new { } + ) + ); + + // Act + var channel = GrpcChannel.ForAddress(server.Url!); + var client = new Greeter.GreeterClient(channel); + + var reply = await client.SayNothingAsync(new Empty()); + + // Assert + reply.Should().Be(new Empty()); + } + + [Fact] + public async Task WireMockServer_WithBodyAsProtoBuf_WithWellKnownTypes_Timestamp_UsingGrpcGeneratedClient() + { + // Arrange + const int seconds = 1722301323; + const int nanos = 12300; + var definition = await System.IO.File.ReadAllTextAsync("./Grpc/greet.proto"); + + using var server = WireMockServer.Start(useHttp2: true); + + server + .Given(Request.Create() + .UsingPost() + .WithPath("/greet.Greeter/SayTimestamp") + .WithBody(new NotNullOrEmptyMatcher()) + ) + .RespondWith(Response.Create() + .WithHeader("Content-Type", "application/grpc") + .WithTrailingHeader("grpc-status", "0") + .WithBodyAsProtoBuf(definition, "greet.MyMessageTimestamp", + new + { + ts = new + { + seconds, + nanos + } + } + ) + ); + + // Act + var channel = GrpcChannel.ForAddress(server.Url!); + var client = new Greeter.GreeterClient(channel); + + var reply = await client.SayTimestampAsync(new MyMessageTimestamp { Ts = Timestamp.FromDateTime(DateTime.UtcNow) }); + + // Assert + reply.Ts.Should().Be(new Timestamp { Seconds = seconds, Nanos = nanos }); + } + + [Fact] + public async Task WireMockServer_WithBodyAsProtoBuf_WithWellKnownTypes_Duration_UsingGrpcGeneratedClient() + { + // Arrange + const int seconds = 1722301323; + const int nanos = 12300; + var definition = await System.IO.File.ReadAllTextAsync("./Grpc/greet.proto"); + + using var server = WireMockServer.Start(useHttp2: true); + + server + .Given(Request.Create() + .UsingPost() + .WithPath("/greet.Greeter/SayDuration") + .WithBody(new NotNullOrEmptyMatcher()) + ) + .RespondWith(Response.Create() + .WithHeader("Content-Type", "application/grpc") + .WithTrailingHeader("grpc-status", "0") + .WithBodyAsProtoBuf(definition, "greet.MyMessageDuration", + new + { + du = new + { + seconds, + nanos + } + } + ) + ); + + // Act + var channel = GrpcChannel.ForAddress(server.Url!); + var client = new Greeter.GreeterClient(channel); + + var reply = await client.SayDurationAsync(new MyMessageDuration { Du = Duration.FromTimeSpan(TimeSpan.MinValue) }); + + // Assert + reply.Du.Should().Be(new Duration { Seconds = seconds, Nanos = nanos }); + } } #endif \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Grpc/greet.proto b/test/WireMock.Net.Tests/Grpc/greet.proto index 6f9e10fa..6f0c7765 100644 --- a/test/WireMock.Net.Tests/Grpc/greet.proto +++ b/test/WireMock.Net.Tests/Grpc/greet.proto @@ -1,33 +1,35 @@ -// Copyright 2019 The gRPC Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - syntax = "proto3"; package greet; -// The greeting service definition. +import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/duration.proto"; + service Greeter { - // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply); + rpc SayEmpty (MyMessageEmpty) returns (MyMessageEmpty); + rpc SayNothing (google.protobuf.Empty) returns (google.protobuf.Empty); + rpc SayTimestamp (MyMessageTimestamp) returns (MyMessageTimestamp); + rpc SayDuration (MyMessageDuration) returns (MyMessageDuration); } -// The request message containing the user's name. message HelloRequest { string name = 1; } -// The response message containing the greetings message HelloReply { string message = 1; +} + +message MyMessageTimestamp { + google.protobuf.Timestamp ts = 1; +} + +message MyMessageDuration { + google.protobuf.Duration du = 1; +} + +message MyMessageEmpty { + google.protobuf.Empty e = 1; } \ No newline at end of file diff --git a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj index 2468b7e4..7ae9ebe1 100644 --- a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj +++ b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj @@ -2,7 +2,7 @@ Stef Heyenrath - net452;net461;netcoreapp3.1;net6.0;net7.0;net8.0 + net452;net461;netcoreapp3.1;net6.0;net8.0 enable false full @@ -134,6 +134,7 @@ Client + PreserveNewest PreserveNewest diff --git a/test/WireMock.Net.Tests/WireMockServerTests.cs b/test/WireMock.Net.Tests/WireMockServerTests.cs index 909ff660..acd4d44d 100644 --- a/test/WireMock.Net.Tests/WireMockServerTests.cs +++ b/test/WireMock.Net.Tests/WireMockServerTests.cs @@ -215,10 +215,10 @@ public async Task WireMockServer_WithUrl0000_Should_Listen_On_All_IPs_IPv4() { // Arrange var port = PortUtils.FindFreeTcpPort(); - var IPv4 = GetIPAddressesByFamily(System.Net.Sockets.AddressFamily.InterNetwork); + var IPv4 = GetIPAddressesByFamily(AddressFamily.InterNetwork); var settings = new WireMockServerSettings { - Urls = ["http://0.0.0.0:" + port], + Urls = ["http://0.0.0.0:" + port] }; using var server = WireMockServer.Start(settings); @@ -234,15 +234,16 @@ public async Task WireMockServer_WithUrl0000_Should_Listen_On_All_IPs_IPv4() } } +#if NET8_0_OR_GREATER [IgnoreOnContinuousIntegrationFact] public async Task WireMockServer_WithUrl0000_Should_Listen_On_All_IPs_IPv6() { // Arrange var port = PortUtils.FindFreeTcpPort(); - var IPv6 = GetIPAddressesByFamily(System.Net.Sockets.AddressFamily.InterNetworkV6); + var IPv6 = GetIPAddressesByFamily(AddressFamily.InterNetworkV6); var settings = new WireMockServerSettings { - Urls = ["http://0.0.0.0:" + port], + Urls = ["http://0.0.0.0:" + port] }; using var server = WireMockServer.Start(settings); @@ -257,6 +258,7 @@ public async Task WireMockServer_WithUrl0000_Should_Listen_On_All_IPs_IPv6() response.Should().Be("x"); } } +#endif #endif [Fact] From a2aff80d28e4a81be6d033a1f7279588d1352475 Mon Sep 17 00:00:00 2001 From: Stef Date: Fri, 3 Jan 2025 12:30:18 +0100 Subject: [PATCH 3/9] . --- test/WireMock.Net.Tests/Grpc/greet.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/WireMock.Net.Tests/Grpc/greet.proto b/test/WireMock.Net.Tests/Grpc/greet.proto index 6f0c7765..f6cea8c7 100644 --- a/test/WireMock.Net.Tests/Grpc/greet.proto +++ b/test/WireMock.Net.Tests/Grpc/greet.proto @@ -7,9 +7,9 @@ import "google/protobuf/timestamp.proto"; import "google/protobuf/duration.proto"; service Greeter { + rpc SayNothing (google.protobuf.Empty) returns (google.protobuf.Empty); rpc SayHello (HelloRequest) returns (HelloReply); rpc SayEmpty (MyMessageEmpty) returns (MyMessageEmpty); - rpc SayNothing (google.protobuf.Empty) returns (google.protobuf.Empty); rpc SayTimestamp (MyMessageTimestamp) returns (MyMessageTimestamp); rpc SayDuration (MyMessageDuration) returns (MyMessageDuration); } From 16d0fdef8659caeea3ab36bb49ac4896861389cd Mon Sep 17 00:00:00 2001 From: Stef Date: Fri, 3 Jan 2025 12:42:50 +0100 Subject: [PATCH 4/9] small refactor --- .../Handlers/IFileSystemHandler.cs | 33 +++++++++---------- src/WireMock.Net/Json/JObjectExtensions.cs | 10 +++--- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/WireMock.Net.Abstractions/Handlers/IFileSystemHandler.cs b/src/WireMock.Net.Abstractions/Handlers/IFileSystemHandler.cs index 6e0dfe3b..d07ad96c 100644 --- a/src/WireMock.Net.Abstractions/Handlers/IFileSystemHandler.cs +++ b/src/WireMock.Net.Abstractions/Handlers/IFileSystemHandler.cs @@ -1,6 +1,5 @@ // Copyright © WireMock.Net -using JetBrains.Annotations; using System.Collections.Generic; namespace WireMock.Handlers; @@ -21,69 +20,69 @@ public interface IFileSystemHandler /// /// The path. /// true if path refers to an existing directory; false if the directory does not exist or an error occurs when trying to determine if the specified directory exists. - bool FolderExists([NotNull] string path); + bool FolderExists(string path); /// /// Creates all directories and subdirectories in the specified path unless they already exist. /// /// The path. - void CreateFolder([NotNull] string path); + void CreateFolder(string path); /// /// Returns an enumerable collection of file names in a specified path. /// /// The path. - /// A value indicating whether subdirectories should also included when enumerating files. + /// A value indicating whether subdirectories should also be included when enumerating files. /// An enumerable collection of the full names (including paths) for the files in the directory (and optionally subdirectories) specified by path. - IEnumerable EnumerateFiles([NotNull] string path, bool includeSubdirectories); + IEnumerable EnumerateFiles(string path, bool includeSubdirectories); /// /// Read a static mapping file as text. /// /// The path (folder + filename with .json extension). /// The file content as text. - string ReadMappingFile([NotNull] string path); + string ReadMappingFile(string path); /// /// Write the static mapping file. /// /// The path (folder + filename with .json extension). /// The text. - void WriteMappingFile([NotNull] string path, [NotNull] string text); + void WriteMappingFile(string path, string text); /// /// Read a response body file as byte[]. /// /// The path or filename from the file to read. /// The file content as bytes. - byte[] ReadResponseBodyAsFile([NotNull] string path); + byte[] ReadResponseBodyAsFile(string path); /// /// Read a response body file as text. /// /// The path or filename from the file to read. /// The file content as text. - string ReadResponseBodyAsString([NotNull] string path); + string ReadResponseBodyAsString(string path); /// /// Delete a file. /// /// The filename. - void DeleteFile([NotNull] string filename); + void DeleteFile(string filename); /// /// Determines whether the given path refers to an existing file on disk. /// /// The filename. /// true if path refers to an existing file; false if the file does not exist. - bool FileExists([NotNull] string filename); + bool FileExists(string filename); /// /// Write a file. /// /// The filename. /// The bytes. - void WriteFile([NotNull] string filename, [NotNull] byte[] bytes); + void WriteFile(string filename, byte[] bytes); /// /// Write a file. @@ -91,21 +90,21 @@ public interface IFileSystemHandler /// The folder. /// The filename. /// The bytes. - void WriteFile([NotNull] string folder, [NotNull] string filename, [NotNull] byte[] bytes); + void WriteFile(string folder, string filename, byte[] bytes); /// /// Read a file as bytes. /// /// The filename. /// The file content as bytes. - byte[] ReadFile([NotNull] string filename); + byte[] ReadFile(string filename); /// /// Read a file as string. /// /// The filename. /// The file content as a string. - string ReadFileAsString([NotNull] string filename); + string ReadFileAsString(string filename); /// /// Gets the folder where the unmatched requests should be stored. For local file system, this would be `{CurrentFolder}/requests/unmatched`. @@ -114,9 +113,9 @@ public interface IFileSystemHandler string GetUnmatchedRequestsFolder(); /// - /// Write a unmatched request to the Unmatched RequestsFolder. + /// Write an unmatched request to the Unmatched RequestsFolder. /// /// The filename. /// The text. - void WriteUnmatchedRequest([NotNull] string filename, [NotNull] string text); + void WriteUnmatchedRequest(string filename, string text); } \ No newline at end of file diff --git a/src/WireMock.Net/Json/JObjectExtensions.cs b/src/WireMock.Net/Json/JObjectExtensions.cs index 0d0c40ae..203ceb09 100644 --- a/src/WireMock.Net/Json/JObjectExtensions.cs +++ b/src/WireMock.Net/Json/JObjectExtensions.cs @@ -1,6 +1,6 @@ // Copyright © WireMock.Net -// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq +// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq which is copied from https://github.com/StefH/JsonConverter using System; using System.Collections; @@ -14,9 +14,7 @@ namespace WireMock.Json; internal static class JObjectExtensions { - private class JTokenResolvers : Dictionary> - { - } + private class JTokenResolvers : Dictionary>; private static readonly JTokenResolvers Resolvers = new() { @@ -180,7 +178,7 @@ private static IEnumerable ConvertJTokenArray(JToken arg, DynamicJsonClassOption private static IEnumerable ConvertToTypedArray(IEnumerable src, Type newType) { var method = ConvertToTypedArrayGenericMethod.MakeGenericMethod(newType); - return (IEnumerable)method.Invoke(null, new object[] { src })!; + return (IEnumerable)method.Invoke(null, [src])!; } private static readonly MethodInfo ConvertToTypedArrayGenericMethod = typeof(JObjectExtensions).GetMethod(nameof(ConvertToTypedArrayGeneric), BindingFlags.NonPublic | BindingFlags.Static)!; @@ -193,7 +191,7 @@ private static T[] ConvertToTypedArrayGeneric(IEnumerable src) public static DynamicClass CreateInstance(IList dynamicPropertiesWithValue, bool createParameterCtor = true) { var type = DynamicClassFactory.CreateType(dynamicPropertiesWithValue.Cast().ToArray(), createParameterCtor); - var dynamicClass = (DynamicClass)Activator.CreateInstance(type); + var dynamicClass = (DynamicClass)Activator.CreateInstance(type)!; foreach (var dynamicPropertyWithValue in dynamicPropertiesWithValue.Where(p => p.Value != null)) { dynamicClass.SetDynamicPropertyValue(dynamicPropertyWithValue.Name, dynamicPropertyWithValue.Value!); From 11aea23aa5266603cdbf4cf7f9d0be76bbd6d54c Mon Sep 17 00:00:00 2001 From: Stef Date: Fri, 3 Jan 2025 17:43:40 +0100 Subject: [PATCH 5/9] 006 --- src/WireMock.Net/WireMock.Net.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj index 6edee489..46b26473 100644 --- a/src/WireMock.Net/WireMock.Net.csproj +++ b/src/WireMock.Net/WireMock.Net.csproj @@ -146,7 +146,7 @@ - + From cc827ea3ec43ec6a0c9af55f25520353f3d99fdf Mon Sep 17 00:00:00 2001 From: Stef Date: Tue, 7 Jan 2025 05:09:29 +0100 Subject: [PATCH 6/9] fix --- src/WireMock.Net/WireMock.Net.csproj | 2 +- .../Grpc/WireMockServerTests.Grpc.cs | 24 +++++----- test/WireMock.Net.Tests/Grpc/policy.proto | 48 +++++++++++++++++++ .../WireMock.Net.Tests.csproj | 5 ++ 4 files changed, 66 insertions(+), 13 deletions(-) create mode 100644 test/WireMock.Net.Tests/Grpc/policy.proto diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj index 46b26473..ab481d56 100644 --- a/src/WireMock.Net/WireMock.Net.csproj +++ b/src/WireMock.Net/WireMock.Net.csproj @@ -146,7 +146,7 @@ - + diff --git a/test/WireMock.Net.Tests/Grpc/WireMockServerTests.Grpc.cs b/test/WireMock.Net.Tests/Grpc/WireMockServerTests.Grpc.cs index 9061d738..f1d18d15 100644 --- a/test/WireMock.Net.Tests/Grpc/WireMockServerTests.Grpc.cs +++ b/test/WireMock.Net.Tests/Grpc/WireMockServerTests.Grpc.cs @@ -227,8 +227,8 @@ public async Task WireMockServer_WithBodyAsProtoBuf_WithWellKnownTypes_Timestamp { ts = new { - seconds = 1722301323, - nanos = 12300 + Seconds = 1722301323, + Nanos = 12300 } } ) @@ -269,8 +269,8 @@ public async Task WireMockServer_WithBodyAsProtoBuf_WithWellKnownTypes_Duration( { du = new { - seconds = 1722301323, - nanos = 12300 + Seconds = 1722301323, + Nanos = 12300 } } ) @@ -539,12 +539,12 @@ public async Task WireMockServer_WithBodyAsProtoBuf_WithWellKnownTypes_Timestamp .WithHeader("Content-Type", "application/grpc") .WithTrailingHeader("grpc-status", "0") .WithBodyAsProtoBuf(definition, "greet.MyMessageTimestamp", - new + new MyMessageTimestamp { - ts = new + Ts = new Timestamp { - seconds, - nanos + Seconds = seconds, + Nanos = nanos } } ) @@ -580,12 +580,12 @@ public async Task WireMockServer_WithBodyAsProtoBuf_WithWellKnownTypes_Duration_ .WithHeader("Content-Type", "application/grpc") .WithTrailingHeader("grpc-status", "0") .WithBodyAsProtoBuf(definition, "greet.MyMessageDuration", - new + new MyMessageDuration { - du = new + Du = new Duration { - seconds, - nanos + Seconds = seconds, + Nanos = nanos } } ) diff --git a/test/WireMock.Net.Tests/Grpc/policy.proto b/test/WireMock.Net.Tests/Grpc/policy.proto new file mode 100644 index 00000000..5d814390 --- /dev/null +++ b/test/WireMock.Net.Tests/Grpc/policy.proto @@ -0,0 +1,48 @@ +syntax = "proto3"; + +import "google/protobuf/timestamp.proto"; + +package Policy2; + +service PolicyService2 { + rpc GetVersion (GetVersionRequest) returns (GetVersionResponse); + rpc GetVersion2 (GetVersion2Request) returns (GetVersion2Response); +} + +// REQUEST/RESPONSE DEFINITIONS + +message GetVersion2Request { + Client Client = 1; + +} +message GetVersion2Response { + string Version = 1; + +} +message GetVersionRequest { + Client Client = 1; + +} +message GetVersionResponse { + string Version = 1; + google.protobuf.Timestamp DateHired = 2; +} + +message Client { + string CorrelationId = 1; + enum Clients { + Unknown = 0; + QMS = 1; + BillingCenter = 2; + PAS = 3; + Payroll = 4; + Portal = 5; + SFO = 6; + QuoteAndBind = 7; + LegacyConversion = 8; + BindNow = 9; + PaymentPortal = 10 ; + PricingEngine = 11; + } + Clients ClientName = 2; +} \ No newline at end of file diff --git a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj index 7ae9ebe1..40b38c66 100644 --- a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj +++ b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj @@ -113,6 +113,7 @@ + @@ -132,6 +133,10 @@ PreserveNewest + + PreserveNewest + Client + Client PreserveNewest From 822206cd60b1ffa7de72f23d2ed3b41ccb903c0f Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 8 Jan 2025 14:20:27 +0100 Subject: [PATCH 7/9] policy --- .../Grpc/WireMockServerTests.Grpc.cs | 96 +++++++++++++++++++ test/WireMock.Net.Tests/Grpc/greet.proto | 6 ++ test/WireMock.Net.Tests/Grpc/policy.proto | 18 +--- 3 files changed, 107 insertions(+), 13 deletions(-) diff --git a/test/WireMock.Net.Tests/Grpc/WireMockServerTests.Grpc.cs b/test/WireMock.Net.Tests/Grpc/WireMockServerTests.Grpc.cs index f1d18d15..f9eb3bc5 100644 --- a/test/WireMock.Net.Tests/Grpc/WireMockServerTests.Grpc.cs +++ b/test/WireMock.Net.Tests/Grpc/WireMockServerTests.Grpc.cs @@ -10,6 +10,7 @@ using Google.Protobuf.WellKnownTypes; using Greet; using Grpc.Net.Client; +using NarrowIntegrationTest.Lookup; using WireMock.Matchers; using WireMock.RequestBuilders; using WireMock.ResponseBuilders; @@ -36,6 +37,12 @@ message HelloRequest { message HelloReply { string message = 1; + enum PhoneType { + none = 0; + mobile = 1; + home = 2; + } + PhoneType phoneType = 2; } "; @@ -600,5 +607,94 @@ public async Task WireMockServer_WithBodyAsProtoBuf_WithWellKnownTypes_Duration_ // Assert reply.Du.Should().Be(new Duration { Seconds = seconds, Nanos = nanos }); } + + [Fact] + public async Task WireMockServer_WithBodyAsProtoBuf_Enum_UsingGrpcGeneratedClient() + { + // Arrange + var definition = await System.IO.File.ReadAllTextAsync("./Grpc/greet.proto"); + + using var server = WireMockServer.Start(useHttp2: true); + + server + .Given(Request.Create() + .UsingPost() + .WithPath("/greet.Greeter/SayHello") + .WithBody(new NotNullOrEmptyMatcher()) + ) + .RespondWith(Response.Create() + .WithHeader("Content-Type", "application/grpc") + .WithTrailingHeader("grpc-status", "0") + .WithBodyAsProtoBuf(definition, "greet.HelloReply", + new HelloReply + { + Message = "hello", + PhoneType = HelloReply.Types.PhoneType.Home + } + ) + ); + + // Act + var channel = GrpcChannel.ForAddress(server.Url!); + var client = new Greeter.GreeterClient(channel); + + var reply = await client.SayHelloAsync(new HelloRequest()); + + // Assert + reply.Message.Should().Be("hello"); + reply.PhoneType.Should().Be(HelloReply.Types.PhoneType.Home); + } + + [Fact] + public async Task WireMockServer_WithBodyAsProtoBuf_Enum_UsingPolicyGrpcGeneratedClient() + { + // Arrange + const int seconds = 1722301323; + const int nanos = 12300; + const string version = "test"; + const string correlationId = "correlation"; + var definition = await System.IO.File.ReadAllTextAsync("./Grpc/policy.proto"); + + using var server = WireMockServer.Start(useHttp2: true); + + server + .Given(Request.Create() + .UsingPost() + .WithPath("/Policy.PolicyService/GetVersion") + .WithBody(new NotNullOrEmptyMatcher()) + ) + .RespondWith(Response.Create() + .WithHeader("Content-Type", "application/grpc") + .WithTrailingHeader("grpc-status", "0") + .WithBodyAsProtoBuf(definition, "NarrowIntegrationTest.Lookup.GetVersionResponse", + new GetVersionResponse + { + Version = version, + DateHired = new Timestamp + { + Seconds = seconds, + Nanos = nanos + }, + Client = new NarrowIntegrationTest.Lookup.Client + { + ClientName = NarrowIntegrationTest.Lookup.Client.Types.Clients.BillingCenter, + CorrelationId = correlationId + } + } + ) + ); + + // Act + var channel = GrpcChannel.ForAddress(server.Url!); + var client = new PolicyService.PolicyServiceClient(channel); + + var reply = await client.GetVersionAsync(new GetVersionRequest()); + + // Assert + reply.Version.Should().Be(version); + reply.DateHired.Should().Be(new Timestamp { Seconds = seconds, Nanos = nanos }); + reply.Client.ClientName.Should().Be(NarrowIntegrationTest.Lookup.Client.Types.Clients.BillingCenter); + reply.Client.CorrelationId.Should().Be(correlationId); + } } #endif \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Grpc/greet.proto b/test/WireMock.Net.Tests/Grpc/greet.proto index f6cea8c7..78e903be 100644 --- a/test/WireMock.Net.Tests/Grpc/greet.proto +++ b/test/WireMock.Net.Tests/Grpc/greet.proto @@ -20,6 +20,12 @@ message HelloRequest { message HelloReply { string message = 1; + enum PhoneType { + none = 0; + mobile = 1; + home = 2; + } + PhoneType phoneType = 2; } message MyMessageTimestamp { diff --git a/test/WireMock.Net.Tests/Grpc/policy.proto b/test/WireMock.Net.Tests/Grpc/policy.proto index 5d814390..ac7e67c8 100644 --- a/test/WireMock.Net.Tests/Grpc/policy.proto +++ b/test/WireMock.Net.Tests/Grpc/policy.proto @@ -1,24 +1,15 @@ syntax = "proto3"; +option csharp_namespace = "NarrowIntegrationTest.Lookup"; + import "google/protobuf/timestamp.proto"; -package Policy2; +package Policy; -service PolicyService2 { +service PolicyService { rpc GetVersion (GetVersionRequest) returns (GetVersionResponse); - rpc GetVersion2 (GetVersion2Request) returns (GetVersion2Response); -} - -// REQUEST/RESPONSE DEFINITIONS - -message GetVersion2Request { - Client Client = 1; - } -message GetVersion2Response { - string Version = 1; -} message GetVersionRequest { Client Client = 1; @@ -26,6 +17,7 @@ message GetVersionRequest { message GetVersionResponse { string Version = 1; google.protobuf.Timestamp DateHired = 2; + Client Client = 3; } message Client { From 17df99603c454eac38bbda7aa9cc394cb97cdc29 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 8 Jan 2025 14:22:13 +0100 Subject: [PATCH 8/9] --- --- test/WireMock.Net.Tests/Grpc/policy.proto | 32 +++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/WireMock.Net.Tests/Grpc/policy.proto b/test/WireMock.Net.Tests/Grpc/policy.proto index ac7e67c8..57551fda 100644 --- a/test/WireMock.Net.Tests/Grpc/policy.proto +++ b/test/WireMock.Net.Tests/Grpc/policy.proto @@ -21,20 +21,20 @@ message GetVersionResponse { } message Client { - string CorrelationId = 1; - enum Clients { - Unknown = 0; - QMS = 1; - BillingCenter = 2; - PAS = 3; - Payroll = 4; - Portal = 5; - SFO = 6; - QuoteAndBind = 7; - LegacyConversion = 8; - BindNow = 9; - PaymentPortal = 10 ; - PricingEngine = 11; - } - Clients ClientName = 2; + string CorrelationId = 1; + enum Clients { + Unknown = 0; + QMS = 1; + BillingCenter = 2; + PAS = 3; + Payroll = 4; + Portal = 5; + SFO = 6; + QuoteAndBind = 7; + LegacyConversion = 8; + BindNow = 9; + PaymentPortal = 10 ; + PricingEngine = 11; + } + Clients ClientName = 2; } \ No newline at end of file From a8b9e45cc98839bb099eaf29c77f7251cbb62c79 Mon Sep 17 00:00:00 2001 From: Stef Date: Wed, 8 Jan 2025 23:19:48 +0100 Subject: [PATCH 9/9] --- src/WireMock.Net/WireMock.Net.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj index ab481d56..8b030489 100644 --- a/src/WireMock.Net/WireMock.Net.csproj +++ b/src/WireMock.Net/WireMock.Net.csproj @@ -146,7 +146,7 @@ - +