From e20a90b615636c50eb5d531ab4ed5aa15a68897f Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 8 May 2024 17:40:53 +0200 Subject: [PATCH] Fix MappingConverter to support Body with JsonMatcher (#1101) * Fix MappingBuilder for Body * . * . * Fix MappingConverter --- .../Serialization/MappingConverter.cs | 26 +++---- .../Server/WireMockServer.Admin.cs | 6 +- src/WireMock.Net/Util/CSharpFormatter.cs | 10 +-- ...eMockAdminApi_GetMappingsCode.verified.txt | 61 ++++++++++----- .../AdminApi/WireMockAdminApiTests.cs | 78 ++++++++++++++----- ...ppingBuilderTests.GetMappings.verified.txt | 30 +++++++ ...derTests.ToCSharpCode_Builder.verified.txt | 30 +++++++ ...lderTests.ToCSharpCode_Server.verified.txt | 30 +++++++ .../MappingBuilderTests.ToJson.verified.txt | 29 +++++++ .../WireMock.Net.Tests/MappingBuilderTests.cs | 50 ++++++++++-- .../WireMockServer.Admin.cs | 20 +++++ 11 files changed, 300 insertions(+), 70 deletions(-) create mode 100644 test/WireMock.Net.Tests/MappingBuilderTests.ToCSharpCode_Builder.verified.txt create mode 100644 test/WireMock.Net.Tests/MappingBuilderTests.ToCSharpCode_Server.verified.txt diff --git a/src/WireMock.Net/Serialization/MappingConverter.cs b/src/WireMock.Net/Serialization/MappingConverter.cs index a83eabf1c..27676b249 100644 --- a/src/WireMock.Net/Serialization/MappingConverter.cs +++ b/src/WireMock.Net/Serialization/MappingConverter.cs @@ -143,25 +143,21 @@ public string ToCSharpCode(IMapping mapping, MappingConverterSettings? settings if (requestMessageBodyMatcher is { Matchers: { } }) { - if (requestMessageBodyMatcher.Matchers.OfType().FirstOrDefault() is { } wildcardMatcher && wildcardMatcher.GetPatterns().Any()) + var firstMatcher = requestMessageBodyMatcher.Matchers.FirstOrDefault(); + + if (firstMatcher is WildcardMatcher wildcardMatcher && wildcardMatcher.GetPatterns().Any()) { sb.AppendLine($" .WithBody({GetString(wildcardMatcher)})"); } - else if (requestMessageBodyMatcher.Matchers.OfType().FirstOrDefault() is { Value: { } } jsonPartialMatcher) - { - sb.AppendLine(@$" .WithBody(new JsonPartialMatcher( - value: {ToCSharpStringLiteral(jsonPartialMatcher.Value.ToString())}, - ignoreCase: {ToCSharpBooleanLiteral(jsonPartialMatcher.IgnoreCase)}, - regex: {ToCSharpBooleanLiteral(jsonPartialMatcher.Regex)} - ))"); - } - else if (requestMessageBodyMatcher.Matchers.OfType().FirstOrDefault() is { Value: { } } jsonPartialWildcardMatcher) + + if (firstMatcher is JsonMatcher jsonMatcher) { - sb.AppendLine(@$" .WithBody(new JsonPartialWildcardMatcher( - value: {ToCSharpStringLiteral(jsonPartialWildcardMatcher.Value.ToString())}, - ignoreCase: {ToCSharpBooleanLiteral(jsonPartialWildcardMatcher.IgnoreCase)}, - regex: {ToCSharpBooleanLiteral(jsonPartialWildcardMatcher.Regex)} - ))"); + var matcherType = jsonMatcher.GetType().Name; + sb.AppendLine($" .WithBody(new {matcherType}("); + sb.AppendLine($" value: {ConvertToAnonymousObjectDefinition(jsonMatcher.Value, 3)},"); + sb.AppendLine($" ignoreCase: {ToCSharpBooleanLiteral(jsonMatcher.IgnoreCase)},"); + sb.AppendLine($" regex: {ToCSharpBooleanLiteral(jsonMatcher.Regex)}"); + sb.AppendLine(@" ))"); } } diff --git a/src/WireMock.Net/Server/WireMockServer.Admin.cs b/src/WireMock.Net/Server/WireMockServer.Admin.cs index 01b436833..afb725182 100644 --- a/src/WireMock.Net/Server/WireMockServer.Admin.cs +++ b/src/WireMock.Net/Server/WireMockServer.Admin.cs @@ -344,15 +344,13 @@ private IResponseMessage MappingCodeGet(IRequestMessage requestMessage) private static MappingConverterType GetMappingConverterType(IRequestMessage requestMessage) { - var mappingConverterType = MappingConverterType.Server; - if (requestMessage.QueryIgnoreCase?.TryGetValue(nameof(MappingConverterType), out var values) == true && Enum.TryParse(values.FirstOrDefault(), true, out MappingConverterType parsed)) { - mappingConverterType = parsed; + return parsed; } - return mappingConverterType; + return MappingConverterType.Server; } private IMapping? FindMappingByGuid(IRequestMessage requestMessage) diff --git a/src/WireMock.Net/Util/CSharpFormatter.cs b/src/WireMock.Net/Util/CSharpFormatter.cs index 63a17a205..425c8e4dc 100644 --- a/src/WireMock.Net/Util/CSharpFormatter.cs +++ b/src/WireMock.Net/Util/CSharpFormatter.cs @@ -10,6 +10,8 @@ namespace WireMock.Util; internal static class CSharpFormatter { + private const string Null = "null"; + #region Reserved Keywords private static readonly HashSet CSharpReservedKeywords = new(new[] { @@ -92,17 +94,15 @@ internal static class CSharpFormatter "while" }); #endregion - - private const string Null = "null"; - - public static object ConvertToAnonymousObjectDefinition(object jsonBody) + + public static object ConvertToAnonymousObjectDefinition(object jsonBody, int ind = 2) { var serializedBody = JsonConvert.SerializeObject(jsonBody); using var jsonReader = new JsonTextReader(new StringReader(serializedBody)); jsonReader.DateParseHandling = DateParseHandling.None; var deserializedBody = JObject.Load(jsonReader); - return ConvertJsonToAnonymousObjectDefinition(deserializedBody, 2); + return ConvertJsonToAnonymousObjectDefinition(deserializedBody, ind); } public static string ConvertJsonToAnonymousObjectDefinition(JToken token, int ind = 0) diff --git a/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.IWireMockAdminApi_GetMappingsCode.verified.txt b/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.IWireMockAdminApi_GetMappingsCode.verified.txt index 2b7410d83..d424aca8c 100644 --- a/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.IWireMockAdminApi_GetMappingsCode.verified.txt +++ b/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.IWireMockAdminApi_GetMappingsCode.verified.txt @@ -1,11 +1,49 @@ var server = WireMockServer.Start(); +server + .Given(Request.Create() + .UsingMethod("POST") + .WithPath("/users/post1") + .WithBody(new JsonMatcher( + value: new + { + city = "Amsterdam", + country = "The Netherlands" + }, + ignoreCase: false, + regex: false + )) + ) + .WithGuid("90356dba-b36c-469a-a17e-669cd84f1f05") + .RespondWith(Response.Create() + ); + +server + .Given(Request.Create() + .UsingMethod("POST") + .WithPath("/users/post2") + .WithBody(new JsonPartialMatcher( + value: new + { + city = "City", + country = "Country" + }, + ignoreCase: false, + regex: false + )) + ) + .WithGuid("1b731398-4a5b-457f-a6e3-d65e541c428f") + .RespondWith(Response.Create() + .WithBody(@"Line1 +Some ""value"" in Line2") + ); + server .Given(Request.Create() .UsingMethod("GET") .WithPath("/foo1") .WithParam("p1", "xyz") ) - .WithGuid("90356dba-b36c-469a-a17e-669cd84f1f05") + .WithGuid("f74fd144-df53-404f-8e35-da22a640bd5f") .RespondWith(Response.Create() .WithStatusCode(200) .WithBody("1") @@ -18,7 +56,7 @@ server .WithParam("p2", "abc") .WithHeader("h1", "W/\"234f2q3r\"", true) ) - .WithGuid("1b731398-4a5b-457f-a6e3-d65e541c428f") + .WithGuid("4126dec8-470b-4eff-93bb-c24f83b8b1fd") .RespondWith(Response.Create() .WithStatusCode("201") .WithHeader("hk", "hv") @@ -31,7 +69,7 @@ server .UsingMethod("DELETE") .WithUrl("https://localhost/test") ) - .WithGuid("f74fd144-df53-404f-8e35-da22a640bd5f") + .WithGuid("c9929240-7ae8-4a5d-8ed8-0913479f6eeb") .RespondWith(Response.Create() .WithStatusCode(208) .WithBodyAsJson(new @@ -70,20 +108,3 @@ text }) ); -server - .Given(Request.Create() - .UsingMethod("POST") - .WithPath("/foo3") - .WithBody(new JsonPartialMatcher( - value: "{ a = 1, b = 2 }", - ignoreCase: false, - regex: false - )) - ) - .WithGuid("4126dec8-470b-4eff-93bb-c24f83b8b1fd") - .RespondWith(Response.Create() - .WithStatusCode(200) - .WithBody(@"Line1 -Some ""value"" in Line2") - ); - diff --git a/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.cs b/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.cs index 7eaaccbdd..dde12f4e1 100644 --- a/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.cs +++ b/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.cs @@ -491,7 +491,7 @@ public async Task IWireMockAdminApi_GetMappingAsync_WithProxy_And_ProxyUrlReplac server.Stop(); } - + [Fact] public async Task IWireMockAdminApi_GetRequestsAsync_Json() { @@ -862,8 +862,37 @@ public async Task IWireMockAdminApi_GetMappingsCode() var guid2 = Guid.Parse("1b731398-4a5b-457f-a6e3-d65e541c428f"); var guid3 = Guid.Parse("f74fd144-df53-404f-8e35-da22a640bd5f"); var guid4 = Guid.Parse("4126DEC8-470B-4EFF-93BB-C24F83B8B1FD"); + var guid5 = Guid.Parse("c9929240-7ae8-4a5d-8ed8-0913479f6eeb"); var server = WireMockServer.StartWithAdminInterface(); + server + .Given( + Request.Create() + .WithPath("/users/post1") + .UsingPost() + .WithBody(new JsonMatcher(new + { + city = "Amsterdam", + country = "The Netherlands" + })) + ) + .WithGuid(guid1) + .RespondWith(Response.Create()); + + server + .Given( + Request.Create() + .WithPath("/users/post2") + .UsingPost() + .WithBody(new JsonPartialMatcher(new + { + city = "City", + country = "Country" + })) + ) + .WithGuid(guid2) + .RespondWith(Response.Create().WithBody("Line1\r\nSome \"value\" in Line2")); + server .Given( Request.Create() @@ -871,7 +900,7 @@ public async Task IWireMockAdminApi_GetMappingsCode() .WithParam("p1", "xyz") .UsingGet() ) - .WithGuid(guid1) + .WithGuid(guid3) .RespondWith( Response.Create() .WithStatusCode(200) @@ -886,7 +915,7 @@ public async Task IWireMockAdminApi_GetMappingsCode() .WithHeader("h1", "W/\"234f2q3r\"") .UsingPost() ) - .WithGuid(guid2) + .WithGuid(guid4) .RespondWith( Response.Create() .WithStatusCode("201") @@ -901,36 +930,43 @@ public async Task IWireMockAdminApi_GetMappingsCode() .WithUrl("https://localhost/test") .UsingDelete() ) - .WithGuid(guid3) + .WithGuid(guid5) .RespondWith( Response.Create() .WithStatusCode(HttpStatusCode.AlreadyReported) - .WithBodyAsJson(new { @as = 1, b = 1.2, d = true, e = false, f = new[] { 1, 2, 3, 4 }, g = new { z1 = 1, z2 = 2, z3 = new[] { "a", "b", "c" }, z4 = new[] { new { a = 1, b = 2 }, new { a = 2, b = 3 } } }, date_field = new DateTime(2023, 05, 08, 11, 20, 19), string_field_with_date = "2021-03-13T21:04:00Z", multiline_text = @"This + .WithBodyAsJson(new + { + @as = 1, + b = 1.2, + d = true, + e = false, + f = new[] { 1, 2, 3, 4 }, + g = new + { + z1 = 1, + z2 = 2, + z3 = new[] { "a", "b", "c" }, + z4 = new[] + { + new { a = 1, b = 2 }, + new { a = 2, b = 3 } + } + }, + date_field = new DateTime(2023, 05, 08, 11, 20, 19), + string_field_with_date = "2021-03-13T21:04:00Z", + multiline_text = @"This is multiline text -" }) - ); - - server - .Given( - Request.Create() - .WithPath("/foo3") - .WithBody(new JsonPartialMatcher(new { a = 1, b = 2 })) - .UsingPost() - ) - .WithGuid(guid4) - .RespondWith( - Response.Create() - .WithStatusCode(200) - .WithBody("Line1\r\nSome \"value\" in Line2") +" + }) ); // Act var api = RestClient.For(server.Url); var mappings = await api.GetMappingsAsync().ConfigureAwait(false); - mappings.Should().HaveCount(4); + mappings.Should().HaveCount(5); var code = await api.GetMappingsCodeAsync().ConfigureAwait(false); diff --git a/test/WireMock.Net.Tests/MappingBuilderTests.GetMappings.verified.txt b/test/WireMock.Net.Tests/MappingBuilderTests.GetMappings.verified.txt index f038ae5ec..8812a2fff 100644 --- a/test/WireMock.Net.Tests/MappingBuilderTests.GetMappings.verified.txt +++ b/test/WireMock.Net.Tests/MappingBuilderTests.GetMappings.verified.txt @@ -31,5 +31,35 @@ BodyDestination: SameAsSource, Body: { msg: "Hello world!"} } + }, + { + Guid: Guid_2, + UpdatedAt: 2023-01-14 15:16:17, + Request: { + Path: { + Matchers: [ + { + Name: WildcardMatcher, + Pattern: /users/post2, + IgnoreCase: false + } + ] + }, + Methods: [ + POST + ], + Body: { + Matcher: { + Name: JsonMatcher, + Pattern: { + city: Amsterdam, + country: The Netherlands + }, + IgnoreCase: false, + Regex: false + } + } + }, + Response: {} } ] \ No newline at end of file diff --git a/test/WireMock.Net.Tests/MappingBuilderTests.ToCSharpCode_Builder.verified.txt b/test/WireMock.Net.Tests/MappingBuilderTests.ToCSharpCode_Builder.verified.txt new file mode 100644 index 000000000..be439d3bb --- /dev/null +++ b/test/WireMock.Net.Tests/MappingBuilderTests.ToCSharpCode_Builder.verified.txt @@ -0,0 +1,30 @@ +var builder = new MappingBuilder(); +builder + .Given(Request.Create() + .UsingMethod("GET") + .WithPath("/foo") + .WithParam("test", "it.Length < 10") + ) + .WithGuid("41372914-1838-4c67-916b-b9aacdd096ce") + .RespondWith(Response.Create() + .WithBody("{ msg: \"Hello world!\"}") + ); + +builder + .Given(Request.Create() + .UsingMethod("POST") + .WithPath("/users/post2") + .WithBody(new JsonMatcher( + value: new + { + city = "Amsterdam", + country = "The Netherlands" + }, + ignoreCase: false, + regex: false + )) + ) + .WithGuid("98fae52e-76df-47d9-876f-2ee32e931d9b") + .RespondWith(Response.Create() + ); + diff --git a/test/WireMock.Net.Tests/MappingBuilderTests.ToCSharpCode_Server.verified.txt b/test/WireMock.Net.Tests/MappingBuilderTests.ToCSharpCode_Server.verified.txt new file mode 100644 index 000000000..6091acbd2 --- /dev/null +++ b/test/WireMock.Net.Tests/MappingBuilderTests.ToCSharpCode_Server.verified.txt @@ -0,0 +1,30 @@ +var server = WireMockServer.Start(); +server + .Given(Request.Create() + .UsingMethod("GET") + .WithPath("/foo") + .WithParam("test", "it.Length < 10") + ) + .WithGuid("41372914-1838-4c67-916b-b9aacdd096ce") + .RespondWith(Response.Create() + .WithBody("{ msg: \"Hello world!\"}") + ); + +server + .Given(Request.Create() + .UsingMethod("POST") + .WithPath("/users/post2") + .WithBody(new JsonMatcher( + value: new + { + city = "Amsterdam", + country = "The Netherlands" + }, + ignoreCase: false, + regex: false + )) + ) + .WithGuid("98fae52e-76df-47d9-876f-2ee32e931d9b") + .RespondWith(Response.Create() + ); + diff --git a/test/WireMock.Net.Tests/MappingBuilderTests.ToJson.verified.txt b/test/WireMock.Net.Tests/MappingBuilderTests.ToJson.verified.txt index e277b97af..bf66f9df4 100644 --- a/test/WireMock.Net.Tests/MappingBuilderTests.ToJson.verified.txt +++ b/test/WireMock.Net.Tests/MappingBuilderTests.ToJson.verified.txt @@ -31,5 +31,34 @@ BodyDestination: SameAsSource, Body: { msg: "Hello world!"} } + }, + { + Guid: Guid_2, + UpdatedAt: 2023-01-14T15:16:17, + Request: { + Path: { + Matchers: [ + { + Name: WildcardMatcher, + Pattern: /users/post2, + IgnoreCase: false + } + ] + }, + Methods: [ + POST + ], + Body: { + Matcher: { + Name: JsonMatcher, + Pattern: { + city: Amsterdam, + country: The Netherlands + }, + IgnoreCase: false, + Regex: false + } + } + } } ] \ No newline at end of file diff --git a/test/WireMock.Net.Tests/MappingBuilderTests.cs b/test/WireMock.Net.Tests/MappingBuilderTests.cs index 9762ce0dd..f07708549 100644 --- a/test/WireMock.Net.Tests/MappingBuilderTests.cs +++ b/test/WireMock.Net.Tests/MappingBuilderTests.cs @@ -13,6 +13,7 @@ using WireMock.ResponseBuilders; using WireMock.Serialization; using WireMock.Settings; +using WireMock.Types; using WireMock.Util; using Xunit; @@ -73,6 +74,25 @@ public MappingBuilderTests() .RespondWith(Response.Create() .WithBody(@"{ msg: ""Hello world!""}") ); + + _sut.Given(Request.Create() + .WithPath("/users/post1") + .UsingPost() + .WithBodyAsJson(new + { + Request = "Hello?" + }) + ).RespondWith(Response.Create()); + + _sut.Given(Request.Create() + .WithPath("/users/post2") + .UsingPost() + .WithBody(new JsonMatcher(new + { + city = "Amsterdam", + country = "The Netherlands" + })) + ).RespondWith(Response.Create()); } [Fact] @@ -95,6 +115,26 @@ public Task ToJson() return Verifier.VerifyJson(json, VerifySettings); } + [Fact] + public Task ToCSharpCode_Server() + { + // Act + var code = _sut.ToCSharpCode(MappingConverterType.Server); + + // Verify + return Verifier.Verify(code, VerifySettings); + } + + [Fact] + public Task ToCSharpCode_Builder() + { + // Act + var code = _sut.ToCSharpCode(MappingConverterType.Builder); + + // Verify + return Verifier.Verify(code, VerifySettings); + } + [Fact] public void SaveMappingsToFile_FolderExists_IsFalse() { @@ -141,9 +181,9 @@ public void SaveMappingsToFolder_FolderIsNull() _sut.SaveMappingsToFolder(null); // Verify - _fileSystemHandlerMock.Verify(fs => fs.GetMappingFolder(), Times.Once); - _fileSystemHandlerMock.Verify(fs => fs.FolderExists(mappingFolder), Times.Once); - _fileSystemHandlerMock.Verify(fs => fs.WriteMappingFile(It.IsAny(), It.IsAny()), Times.Once); + _fileSystemHandlerMock.Verify(fs => fs.GetMappingFolder(), Times.Exactly(2)); + _fileSystemHandlerMock.Verify(fs => fs.FolderExists(mappingFolder), Times.Exactly(2)); + _fileSystemHandlerMock.Verify(fs => fs.WriteMappingFile(It.IsAny(), It.IsAny()), Times.Exactly(2)); _fileSystemHandlerMock.VerifyNoOtherCalls(); } @@ -159,8 +199,8 @@ public void SaveMappingsToFolder_FolderExists_IsTrue() // Verify _fileSystemHandlerMock.Verify(fs => fs.GetMappingFolder(), Times.Never); - _fileSystemHandlerMock.Verify(fs => fs.FolderExists(path), Times.Once); - _fileSystemHandlerMock.Verify(fs => fs.WriteMappingFile(It.IsAny(), It.IsAny()), Times.Once); + _fileSystemHandlerMock.Verify(fs => fs.FolderExists(path), Times.Exactly(2)); + _fileSystemHandlerMock.Verify(fs => fs.WriteMappingFile(It.IsAny(), It.IsAny()), Times.Exactly(2)); _fileSystemHandlerMock.VerifyNoOtherCalls(); } } diff --git a/test/WireMock.Net.Tests/WireMockServer.Admin.cs b/test/WireMock.Net.Tests/WireMockServer.Admin.cs index 40c54165d..df376b5aa 100644 --- a/test/WireMock.Net.Tests/WireMockServer.Admin.cs +++ b/test/WireMock.Net.Tests/WireMockServer.Admin.cs @@ -499,4 +499,24 @@ public async Task WireMockServer_Admin_DeleteMappings() Check.That(response.StatusCode).Equals(HttpStatusCode.OK); Check.That(await response.Content.ReadAsStringAsync().ConfigureAwait(false)).Equals($"{{\"Status\":\"Mappings deleted. Affected GUIDs: [{guid1}, {guid2}]\"}}"); } + + [Fact] + public async Task WireMockServer_Admin_() + { + // given + var server = WireMockServer.Start(); + + server.CreateClient(); + + // when + await new HttpClient().GetAsync("http://localhost:" + server.Ports[0] + "/foo").ConfigureAwait(false); + + // then + Check.That(server.LogEntries).HasSize(1); + var requestLogged = server.LogEntries.First(); + Check.That(requestLogged.RequestMessage.Method).IsEqualTo("GET"); + Check.That(requestLogged.RequestMessage.BodyData).IsNull(); + + server.Stop(); + } } \ No newline at end of file