From c25d8f33d21e8e272605d109987c443dae6c4317 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sun, 7 Dec 2025 10:55:07 +0100 Subject: [PATCH 1/2] 1.17.0 --- CHANGELOG.md | 9 +++++++++ Directory.Build.props | 2 +- Generate-ReleaseNotes.cmd | 2 +- PackageReleaseNotes.txt | 17 +++++++++-------- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe4cc65d..14dc5e1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 1.17.0 (07 December 2025) +- [#1383](https://github.com/wiremock/WireMock.Net/pull/1383) - Aspire: Add WithProtoDefinition to support proto definition at server level [feature] contributed by [StefH](https://github.com/StefH) +- [#1386](https://github.com/wiremock/WireMock.Net/pull/1386) - Fix random delay in mapping json file [bug] contributed by [StefH](https://github.com/StefH) +- [#1274](https://github.com/wiremock/WireMock.Net/issues/1274) - .WithMappings to mount volume is not working for GRPC [bug] +- [#1381](https://github.com/wiremock/WireMock.Net/issues/1381) - Downstream dependencies missing after 1.16.0 release [bug] +- [#1382](https://github.com/wiremock/WireMock.Net/issues/1382) - Does Aspire support enabling HTTP/2? [feature] +- [#1385](https://github.com/wiremock/WireMock.Net/issues/1385) - Do delays and probabilities show in saved static mappings? [bug] +- [#1387](https://github.com/wiremock/WireMock.Net/issues/1387) - Tests failing with TaskCanceledException on Windows Server 2025 Build 7171 [bug] + # 1.16.0 (18 November 2025) - [#1366](https://github.com/wiremock/WireMock.Net/pull/1366) - WireMock.Net.OpenApiParser : support Examples [feature] contributed by [StefH](https://github.com/StefH) - [#1375](https://github.com/wiremock/WireMock.Net/pull/1375) - Add WireMockHealthCheck in WireMock.Net.Aspire [feature] contributed by [Zguy](https://github.com/Zguy) diff --git a/Directory.Build.props b/Directory.Build.props index 97bd50cb..f48c7ecb 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,7 +4,7 @@ - 1.16.0 + 1.17.0 WireMock.Net-Logo.png https://github.com/wiremock/WireMock.Net Apache-2.0 diff --git a/Generate-ReleaseNotes.cmd b/Generate-ReleaseNotes.cmd index ad8f9459..23ba6396 100644 --- a/Generate-ReleaseNotes.cmd +++ b/Generate-ReleaseNotes.cmd @@ -1,6 +1,6 @@ rem https://github.com/StefH/GitHubReleaseNotes -SET version=1.16.0 +SET version=1.17.0 GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels wontfix test question invalid doc duplicate example environment --version %version% --token %GH_TOKEN% diff --git a/PackageReleaseNotes.txt b/PackageReleaseNotes.txt index 066c2fd2..2329f0e3 100644 --- a/PackageReleaseNotes.txt +++ b/PackageReleaseNotes.txt @@ -1,10 +1,11 @@ -# 1.16.0 (18 November 2025) -- #1366 WireMock.Net.OpenApiParser : support Examples [feature] -- #1375 Add WireMockHealthCheck in WireMock.Net.Aspire [feature] -- #1377 Check if the path is valid when using WithPath(...) [feature] -- #1380 Add WireMock.Net.xUnit.v3 project [feature] -- #1364 Choosing examples from open api specification for responses. [feature] -- #1376 AdminApiMappingBuilder `WithPath` should add the starting `/` if missing [feature] -- #1379 xUnit v3 [feature] +# 1.17.0 (07 December 2025) +- #1383 Aspire: Add WithProtoDefinition to support proto definition at server level [feature] +- #1386 Fix random delay in mapping json file [bug] +- #812 Wiki page for using WireMock.Net with appium for mobile automation testing [wontfix] +- #1274 .WithMappings to mount volume is not working for GRPC [bug] +- #1381 Downstream dependencies missing after 1.16.0 release [bug] +- #1382 Does Aspire support enabling HTTP/2? [feature] +- #1385 Do delays and probabilities show in saved static mappings? [bug] +- #1387 Tests failing with TaskCanceledException on Windows Server 2025 Build 7171 [bug] The full release notes can be found here: https://github.com/wiremock/WireMock.Net/blob/master/CHANGELOG.md \ No newline at end of file From f8e2c7ee903e317db63ec64923e8babc3bfa679d Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 8 Dec 2025 19:15:14 +0100 Subject: [PATCH 2/2] Add WithBodyAsType to RequestMatcher (#1388) * Add WithBody * . * t * t2 --- .../Request/RequestMessageBodyMatcher`1.cs | 60 ++++++++++++++++++ .../RequestBuilders/Request.WithBody.cs | 23 ++++--- .../RequestBuilders/Request.cs | 2 +- .../RequestBuilders/IBodyRequestBuilder.cs | 8 +++ .../RequestBuilderWithBodyTests.cs | 62 +++++++++++++++++-- 5 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 src/WireMock.Net.Minimal/Matchers/Request/RequestMessageBodyMatcher`1.cs diff --git a/src/WireMock.Net.Minimal/Matchers/Request/RequestMessageBodyMatcher`1.cs b/src/WireMock.Net.Minimal/Matchers/Request/RequestMessageBodyMatcher`1.cs new file mode 100644 index 00000000..2ee8a4ab --- /dev/null +++ b/src/WireMock.Net.Minimal/Matchers/Request/RequestMessageBodyMatcher`1.cs @@ -0,0 +1,60 @@ +// Copyright © WireMock.Net + +using System; +using Newtonsoft.Json.Linq; +using Stef.Validation; + +namespace WireMock.Matchers.Request; + +/// +/// The request body matcher. +/// +public class RequestMessageBodyMatcher : IRequestMatcher +{ + /// + /// The body data function for type T + /// + public Func? Func { get; } + + /// + /// The + /// + public MatchOperator MatchOperator { get; } = MatchOperator.Or; + + /// + /// Initializes a new instance of the class. + /// + /// The function. + public RequestMessageBodyMatcher(Func func) + { + Func = Guard.NotNull(func); + } + + /// + public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult) + { + var (score, exception) = CalculateMatchScore(requestMessage).Expand(); + return requestMatchResult.AddScore(GetType(), score, exception); + } + + private MatchResult CalculateMatchScore(IRequestMessage requestMessage) + { + if (Func != null) + { + if (requestMessage.BodyData?.BodyAsJson is JObject jsonObject) + { + try + { + var bodyAsT = jsonObject.ToObject(); + return MatchScores.ToScore(Func(bodyAsT)); + } + catch (Exception ex) + { + return new MatchResult(ex); + } + } + } + + return default; + } +} \ No newline at end of file diff --git a/src/WireMock.Net.Minimal/RequestBuilders/Request.WithBody.cs b/src/WireMock.Net.Minimal/RequestBuilders/Request.WithBody.cs index fd840aae..1eec96da 100644 --- a/src/WireMock.Net.Minimal/RequestBuilders/Request.WithBody.cs +++ b/src/WireMock.Net.Minimal/RequestBuilders/Request.WithBody.cs @@ -34,13 +34,6 @@ public partial class Request return this; } - /// - public IRequestBuilder WithBodyAsJson(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) - { - var matcher = body as IMatcher ?? new JsonMatcher(matchBehaviour, body); - return WithBody([matcher]); - } - /// public IRequestBuilder WithBody(IMatcher matcher) { @@ -98,4 +91,20 @@ public partial class Request _requestMatchers.Add(new RequestMessageBodyMatcher(Guard.NotNull(func))); return this; } + + /// + public IRequestBuilder WithBodyAsJson(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) + { + var matcher = body as IMatcher ?? new JsonMatcher(matchBehaviour, body); + return WithBody([matcher]); + } + + /// + public IRequestBuilder WithBodyAsType(Func func) + { + Guard.NotNull(func); + + _requestMatchers.Add(new RequestMessageBodyMatcher(func)); + return this; + } } \ No newline at end of file diff --git a/src/WireMock.Net.Minimal/RequestBuilders/Request.cs b/src/WireMock.Net.Minimal/RequestBuilders/Request.cs index 4071cf95..8b0d80c1 100644 --- a/src/WireMock.Net.Minimal/RequestBuilders/Request.cs +++ b/src/WireMock.Net.Minimal/RequestBuilders/Request.cs @@ -29,7 +29,7 @@ public partial class Request : RequestMessageCompositeMatcher, IRequestBuilder /// The . public static IRequestBuilder Create() { - return new Request(new List()); + return new Request([]); } /// diff --git a/src/WireMock.Net.Shared/RequestBuilders/IBodyRequestBuilder.cs b/src/WireMock.Net.Shared/RequestBuilders/IBodyRequestBuilder.cs index f22c1b33..1e049b91 100644 --- a/src/WireMock.Net.Shared/RequestBuilders/IBodyRequestBuilder.cs +++ b/src/WireMock.Net.Shared/RequestBuilders/IBodyRequestBuilder.cs @@ -80,6 +80,14 @@ public interface IBodyRequestBuilder : IMultiPartRequestBuilder /// The . IRequestBuilder WithBody(Func func); + /// + /// WithBody: func (type) + /// + /// The type. + /// The function. + /// The . + IRequestBuilder WithBodyAsType(Func func); + /// /// WithBody: func (BodyData object) /// diff --git a/test/WireMock.Net.Tests/RequestBuilders/RequestBuilderWithBodyTests.cs b/test/WireMock.Net.Tests/RequestBuilders/RequestBuilderWithBodyTests.cs index 7244147f..09cab484 100644 --- a/test/WireMock.Net.Tests/RequestBuilders/RequestBuilderWithBodyTests.cs +++ b/test/WireMock.Net.Tests/RequestBuilders/RequestBuilderWithBodyTests.cs @@ -1,11 +1,12 @@ // Copyright © WireMock.Net using System; -using FluentAssertions; using System.Collections.Generic; using System.Linq; using System.Text; +using FluentAssertions; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using NFluent; using WireMock.Matchers; using WireMock.Matchers.Request; @@ -72,15 +73,17 @@ public class RequestBuilderWithBodyTests } [Fact] - public void Request_WithBody_FuncJson() + public void Request_WithBody_FuncObject() { // Assign - var requestBuilder = Request.Create().UsingAnyMethod().WithBody(b => b != null); + var requestBuilder = Request.Create() + .UsingAnyMethod() + .WithBody(b => b != null); // Act var body = new BodyData { - BodyAsJson = 123, + BodyAsJson = JObject.Parse("""{ "X": 123, "Y": "a" }"""), DetectedBodyType = BodyType.Json }; var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body); @@ -90,6 +93,57 @@ public class RequestBuilderWithBodyTests Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); } + [Theory] + [InlineData("""{ "X": 123, "Y": "a" }""", 1.0)] + [InlineData("""{ "X": 123, "Y": "b" }""", 0.0)] + public void Request_WithBodyAsType_Func(string json, double expected) + { + // Assign + var requestBuilder = Request.Create() + .UsingAnyMethod() + .WithBodyAsType(ft => ft != null && ft.X == 123 && ft.Y == "a"); + + // Act + var body = new BodyData + { + BodyAsJson = JObject.Parse(json), + DetectedBodyType = BodyType.Json + }; + var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body); + + // Assert + var requestMatchResult = new RequestMatchResult(); + Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(expected); + } + + [Fact] + public void Request_WithBodyAsType_Func_IncorrectType() + { + // Assign + var requestBuilder = Request.Create() + .UsingAnyMethod() + .WithBodyAsType(ft => ft != null); + + // Act + var body = new BodyData + { + BodyAsJson = JObject.Parse("""{ "X": 123, "Y": "a" }"""), + DetectedBodyType = BodyType.Json + }; + var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body); + + // Assert + var requestMatchResult = new RequestMatchResult(); + Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(0.0); + } + + private class FuncType + { + public int X { get; set; } = 42; + + public string Y { get; set; } = string.Empty; + } + [Fact] public void Request_WithBody_FuncFormUrlEncoded() {