From 685d28db0b6abcc7d93177b72355ab9effad382a Mon Sep 17 00:00:00 2001 From: Tom Akehurst Date: Mon, 13 Oct 2025 20:23:18 +0100 Subject: [PATCH 1/5] Added link to new doc site from README.md --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 66b15070..8611d638 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,17 @@ # ![Project Icon](./resources/logo_32x32.png) WireMock.Net -A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) which mimics the functionality from the JAVA based [WireMock](http://wiremock.org). +A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) which mimics functionality from the original Java based WireMock. -For more info, see also this WIKI page: [What is WireMock.Net](https://github.com/wiremock/WireMock.Net/wiki/What-Is-WireMock.Net). +--- + + + + + +
+:books: Full documentation at wiremock.org +
+ +--- ## :star: Key Features * HTTP response stubbing, matchable on URL/Path, headers, cookies and body content patterns From e3f3e0a8f2c01f7c02e16de0bb56e6da5449a33c Mon Sep 17 00:00:00 2001 From: Tom Akehurst Date: Mon, 13 Oct 2025 20:29:29 +0100 Subject: [PATCH 2/5] Tweaked docs message in README --- README.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/README.md b/README.md index 8611d638..f5df8951 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,7 @@ A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) w --- - - - - -
-:books: Full documentation at wiremock.org -
+:books: Full documentation can now be found at wiremock.org --- From 5885324dfb20bee40e4de415e4a2ab0729d0a252 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 22 Oct 2025 10:16:59 +0200 Subject: [PATCH 3/5] Fix WithProbability logic (#1367) * Fix WithProbability logic * . * FIX * Update src/WireMock.Net.Minimal/Owin/MappingMatcher.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- examples/WireMock.Net.Console.NET8/MainApp.cs | 63 ++++++++++++++----- examples/WireMock.Net.Console.NET8/Program.cs | 5 +- .../Owin/MappingMatcher.cs | 34 +++++----- .../Owin/MappingMatcherTests.cs | 57 +++++++++++------ .../WireMockServerTests.WithProbability.cs | 2 +- 5 files changed, 107 insertions(+), 54 deletions(-) diff --git a/examples/WireMock.Net.Console.NET8/MainApp.cs b/examples/WireMock.Net.Console.NET8/MainApp.cs index 942177dc..fa88484c 100644 --- a/examples/WireMock.Net.Console.NET8/MainApp.cs +++ b/examples/WireMock.Net.Console.NET8/MainApp.cs @@ -267,7 +267,7 @@ namespace WireMock.Net.ConsoleApplication } } - public static void Run() + public static async Task RunAsync() { //RunSse(); //RunOnLocal(); @@ -290,25 +290,56 @@ namespace WireMock.Net.ConsoleApplication var server = WireMockServer.Start(); + //server + // .Given(Request.Create() + // .WithPath("todos") + // .UsingGet() + // ) + // .RespondWith(Response.Create() + // .WithBodyAsJson(todos.Values) + // ); + + //server + // .Given(Request.Create() + // .UsingGet() + // .WithPath("todos") + // .WithParam("id") + // ) + // .RespondWith(Response.Create() + // .WithBodyAsJson(rm => todos[int.Parse(rm.Query!["id"].ToString())]) + // ); + + var pX = 0.80; server - .Given(Request.Create() - .WithPath("todos") - .UsingGet() - ) - .RespondWith(Response.Create() - .WithBodyAsJson(todos.Values) - ); + .Given(Request.Create().UsingGet().WithPath("/p")) + .WithProbability(pX) + .RespondWith(Response.Create().WithStatusCode(200).WithBody("X")); server - .Given(Request.Create() - .UsingGet() - .WithPath("todos") - .WithParam("id") - ) - .RespondWith(Response.Create() - .WithBodyAsJson(rm => todos[int.Parse(rm.Query!["id"].ToString())]) - ); + .Given(Request.Create().UsingGet().WithPath("/p")) + .RespondWith(Response.Create().WithStatusCode(200).WithBody("default")); + // Act + var requestUri = new Uri($"http://localhost:{server.Port}/p"); + var c = server.CreateClient(); + var xCount = 0; + var defaultCount = 0; + var tot = 1000; + for (var i = 0; i < tot; i++) + { + var response = await c.GetAsync(requestUri); + var value = await response.Content.ReadAsStringAsync(); + if (value == "X") + { + xCount++; + } + else if (value == "default") + { + defaultCount++; + } + } + System.Console.WriteLine("X = {0} ; default = {1} ; pX = {2:0.00} ; valueX = {3:0.00}", xCount, defaultCount, pX, 1.0 * xCount / tot); + return; using var httpAndHttpsWithPort = WireMockServer.Start(new WireMockServerSettings { HostingScheme = HostingScheme.HttpAndHttps, diff --git a/examples/WireMock.Net.Console.NET8/Program.cs b/examples/WireMock.Net.Console.NET8/Program.cs index 2a8ae4c4..9ca8b200 100644 --- a/examples/WireMock.Net.Console.NET8/Program.cs +++ b/examples/WireMock.Net.Console.NET8/Program.cs @@ -2,6 +2,7 @@ using System.IO; using System.Reflection; +using System.Threading.Tasks; using log4net; using log4net.Config; using log4net.Repository; @@ -14,10 +15,10 @@ static class Program private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly()); private static readonly ILog Log = LogManager.GetLogger(typeof(Program)); - static void Main(params string[] args) + static async Task Main(params string[] args) { XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config")); - MainApp.Run(); + await MainApp.RunAsync(); } } \ No newline at end of file diff --git a/src/WireMock.Net.Minimal/Owin/MappingMatcher.cs b/src/WireMock.Net.Minimal/Owin/MappingMatcher.cs index 87861813..0180e865 100644 --- a/src/WireMock.Net.Minimal/Owin/MappingMatcher.cs +++ b/src/WireMock.Net.Minimal/Owin/MappingMatcher.cs @@ -9,16 +9,10 @@ using WireMock.Services; namespace WireMock.Owin; -internal class MappingMatcher : IMappingMatcher +internal class MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDoubleBetween0And1 randomizerDoubleBetween0And1) : IMappingMatcher { - private readonly IWireMockMiddlewareOptions _options; - private readonly IRandomizerDoubleBetween0And1 _randomizerDoubleBetween0And1; - - public MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDoubleBetween0And1 randomizerDoubleBetween0And1) - { - _options = Guard.NotNull(options); - _randomizerDoubleBetween0And1 = Guard.NotNull(randomizerDoubleBetween0And1); - } + private readonly IWireMockMiddlewareOptions _options = Guard.NotNull(options); + private readonly IRandomizerDoubleBetween0And1 _randomizerDoubleBetween0And1 = Guard.NotNull(randomizerDoubleBetween0And1); public (MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request) { @@ -28,7 +22,7 @@ internal class MappingMatcher : IMappingMatcher var mappings = _options.Mappings.Values .Where(m => m.TimeSettings.IsValid()) - .Where(m => m.Probability is null || m.Probability <= _randomizerDoubleBetween0And1.Generate()) + .Where(m => m.Probability is null || _randomizerDoubleBetween0And1.Generate() <= m.Probability) .ToArray(); foreach (var mapping in mappings) @@ -41,10 +35,10 @@ internal class MappingMatcher : IMappingMatcher var exceptions = mappingMatcherResult.RequestMatchResult.MatchDetails .Where(md => md.Exception != null) - .Select(md => md.Exception) + .Select(md => md.Exception!) .ToArray(); - if (!exceptions.Any()) + if (exceptions.Length == 0) { possibleMappings.Add(mappingMatcherResult); } @@ -52,7 +46,7 @@ internal class MappingMatcher : IMappingMatcher { foreach (var ex in exceptions) { - LogException(mapping, ex!); + LogException(mapping, ex); } } } @@ -62,14 +56,16 @@ internal class MappingMatcher : IMappingMatcher } } - var partialMappings = possibleMappings + var partialMatches = possibleMappings .Where(pm => (pm.Mapping.IsAdminInterface && pm.RequestMatchResult.IsPerfectMatch) || !pm.Mapping.IsAdminInterface) .OrderBy(m => m.RequestMatchResult) .ThenBy(m => m.RequestMatchResult.TotalNumber) .ThenBy(m => m.Mapping.Priority) + .ThenByDescending(m => m.Mapping.Probability) .ThenByDescending(m => m.Mapping.UpdatedAt) - .ToList(); - var partialMatch = partialMappings.FirstOrDefault(pm => pm.RequestMatchResult.AverageTotalScore > 0.0); + .Where(pm => pm.RequestMatchResult.AverageTotalScore > 0.0) + .ToArray(); + var partialMatch = partialMatches.FirstOrDefault(); if (_options.AllowPartialMapping == true) { @@ -78,7 +74,11 @@ internal class MappingMatcher : IMappingMatcher var match = possibleMappings .Where(m => m.RequestMatchResult.IsPerfectMatch) - .OrderBy(m => m.Mapping.Priority).ThenBy(m => m.RequestMatchResult).ThenByDescending(m => m.Mapping.UpdatedAt) + .OrderBy(m => m.Mapping.Priority) + .ThenBy(m => m.RequestMatchResult) + .ThenBy(m => m.RequestMatchResult.TotalNumber) + .ThenByDescending(m => m.Mapping.Probability) + .ThenByDescending(m => m.Mapping.UpdatedAt) .FirstOrDefault(); return (match, partialMatch); diff --git a/test/WireMock.Net.Tests/Owin/MappingMatcherTests.cs b/test/WireMock.Net.Tests/Owin/MappingMatcherTests.cs index 48f325e1..e81bce1e 100644 --- a/test/WireMock.Net.Tests/Owin/MappingMatcherTests.cs +++ b/test/WireMock.Net.Tests/Owin/MappingMatcherTests.cs @@ -9,7 +9,6 @@ using WireMock.Matchers.Request; using WireMock.Models; using WireMock.Owin; using WireMock.Services; -using WireMock.Util; using Xunit; namespace WireMock.Net.Tests.Owin; @@ -26,7 +25,7 @@ public class MappingMatcherTests _optionsMock = new Mock(); _optionsMock.SetupAllProperties(); _optionsMock.Setup(o => o.Mappings).Returns(new ConcurrentDictionary()); - _optionsMock.Setup(o => o.LogEntries).Returns(new ConcurrentObservableCollection()); + _optionsMock.Setup(o => o.LogEntries).Returns([]); _optionsMock.Setup(o => o.Scenarios).Returns(new ConcurrentDictionary()); var loggerMock = new Mock(); @@ -35,7 +34,7 @@ public class MappingMatcherTests _optionsMock.Setup(o => o.Logger).Returns(loggerMock.Object); _randomizerDoubleBetween0And1Mock = new Mock(); - _randomizerDoubleBetween0And1Mock.Setup(r => r.Generate()).Returns(0.0); + _randomizerDoubleBetween0And1Mock.Setup(r => r.Generate()).Returns(0.5); _sut = new MappingMatcher(_optionsMock.Object, _randomizerDoubleBetween0And1Mock.Object); } @@ -84,8 +83,8 @@ public class MappingMatcherTests var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002"); var mappings = InitMappings ( - (guid1, new[] { 0.1 }, null), - (guid2, new[] { 1.0 }, null) + (guid1, [0.1], null), + (guid2, [1.0], null) ); _optionsMock.Setup(o => o.Mappings).Returns(mappings); @@ -112,8 +111,8 @@ public class MappingMatcherTests var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002"); var mappings = InitMappings ( - (guid1, new[] { 0.1 }, null), - (guid2, new[] { 0.9 }, null) + (guid1, [0.1], null), + (guid2, [0.9], null) ); _optionsMock.Setup(o => o.Mappings).Returns(mappings); @@ -139,8 +138,8 @@ public class MappingMatcherTests _optionsMock.SetupGet(o => o.AllowPartialMapping).Returns(true); var mappings = InitMappings( - (guid1, new[] { 0.1 }, null), - (guid2, new[] { 0.9 }, null) + (guid1, [0.1], null), + (guid2, [0.9], null) ); _optionsMock.Setup(o => o.Mappings).Returns(mappings); @@ -166,8 +165,8 @@ public class MappingMatcherTests var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001"); var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002"); var mappings = InitMappings( - (guid1, new[] { 1.0 }, null), - (guid2, new[] { 1.0, 1.0 }, null) + (guid1, [1.0], null), + (guid2, [1.0, 1.0], null) ); _optionsMock.Setup(o => o.Mappings).Returns(mappings); @@ -187,15 +186,15 @@ public class MappingMatcherTests } [Fact] - public void MappingMatcher_FindBestMatch_WhenProbabilityFailsFirst_ShouldReturnSecondMatch() + public void MappingMatcher_FindBestMatch_WhenProbabilityDoesNotMatch_ShouldReturnNormalMatch() { // Assign - var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001"); - var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002"); + var withProbability = Guid.Parse("00000000-0000-0000-0000-000000000001"); + var noProbability = Guid.Parse("00000000-0000-0000-0000-000000000002"); var mappings = InitMappings ( - (guid1, new[] { 1.0 }, 1.0), - (guid2, new[] { 1.0 }, null) + (withProbability, [1.0], 0.4), + (noProbability, [1.0], null) ); _optionsMock.Setup(o => o.Mappings).Returns(mappings); @@ -206,8 +205,30 @@ public class MappingMatcherTests // Assert result.Match.Should().NotBeNull(); - result.Match!.Mapping.Guid.Should().Be(guid2); - result.Match.RequestMatchResult.AverageTotalScore.Should().Be(1.0); + result.Match!.Mapping.Guid.Should().Be(noProbability); + } + + [Fact] + public void MappingMatcher_FindBestMatch_WhenProbabilityDoesMatch_ShouldReturnProbabilityMatch() + { + // Assign + var withProbability = Guid.Parse("00000000-0000-0000-0000-000000000001"); + var noProbability = Guid.Parse("00000000-0000-0000-0000-000000000002"); + var mappings = InitMappings + ( + (withProbability, [1.0], 0.6), + (noProbability, [1.0], null) + ); + _optionsMock.Setup(o => o.Mappings).Returns(mappings); + + var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1"); + + // Act + var result = _sut.FindBestMatch(request); + + // Assert + result.Match.Should().NotBeNull(); + result.Match!.Mapping.Guid.Should().Be(withProbability); } private static ConcurrentDictionary InitMappings(params (Guid guid, double[] scores, double? probability)[] matches) diff --git a/test/WireMock.Net.Tests/WireMockServerTests.WithProbability.cs b/test/WireMock.Net.Tests/WireMockServerTests.WithProbability.cs index 458d7149..e98eb0c5 100644 --- a/test/WireMock.Net.Tests/WireMockServerTests.WithProbability.cs +++ b/test/WireMock.Net.Tests/WireMockServerTests.WithProbability.cs @@ -32,7 +32,7 @@ public partial class WireMockServerTests var response = await server.CreateClient().GetAsync(requestUri).ConfigureAwait(false); // Assert - Assert.True(new[] { HttpStatusCode.OK, HttpStatusCode.InternalServerError }.Contains(response.StatusCode)); + Assert.Contains(response.StatusCode, [HttpStatusCode.OK, HttpStatusCode.InternalServerError]); server.Stop(); } From c1b23b615e62518fdde779fd418bdaf448f548b2 Mon Sep 17 00:00:00 2001 From: Michi Date: Wed, 22 Oct 2025 10:18:16 +0200 Subject: [PATCH 4/5] Support Testcontainers 4.8.0 (#1370) --- .../WireMock.Net.Testcontainers.csproj | 2 +- src/WireMock.Net.Testcontainers/WireMockContainer.cs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/WireMock.Net.Testcontainers/WireMock.Net.Testcontainers.csproj b/src/WireMock.Net.Testcontainers/WireMock.Net.Testcontainers.csproj index f574e744..cf37e666 100644 --- a/src/WireMock.Net.Testcontainers/WireMock.Net.Testcontainers.csproj +++ b/src/WireMock.Net.Testcontainers/WireMock.Net.Testcontainers.csproj @@ -39,7 +39,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/WireMock.Net.Testcontainers/WireMockContainer.cs b/src/WireMock.Net.Testcontainers/WireMockContainer.cs index f87c99be..a01a6add 100644 --- a/src/WireMock.Net.Testcontainers/WireMockContainer.cs +++ b/src/WireMock.Net.Testcontainers/WireMockContainer.cs @@ -128,12 +128,14 @@ public sealed class WireMockContainer : DockerContainer /// /// The source directory or file to be copied. /// The target directory path to copy the files to. + /// The user ID to set for the copied file or directory. Defaults to 0 (root). + /// The group ID to set for the copied file or directory. Defaults to 0 (root). /// The POSIX file mode permission. /// Cancellation token. /// A task that completes when the directory or file has been copied. - public new async Task CopyAsync(string source, string target, UnixFileModes fileMode = Unix.FileMode644, CancellationToken ct = default) + public new async Task CopyAsync(string source, string target, uint uid = 0, uint gid = 0, UnixFileModes fileMode = Unix.FileMode644, CancellationToken ct = default) { - await base.CopyAsync(source, target, fileMode, ct); + await base.CopyAsync(source, target, uid, gid, fileMode, ct); if (_configuration.WatchStaticMappings && await PathStartsWithContainerMappingsPath(target)) { From 8b1bd1b21bd8e89b0a113ec0f93901fd070f897e Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 22 Oct 2025 10:58:44 +0200 Subject: [PATCH 5/5] 1.15.0 --- CHANGELOG.md | 5 +++++ Directory.Build.props | 2 +- Generate-ReleaseNotes.cmd | 2 +- PackageReleaseNotes.txt | 9 ++++----- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f984dd7..985dcf22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.15.0 (22 October 2025) +- [#1367](https://github.com/wiremock/WireMock.Net/pull/1367) - Fix WithProbability logic [bug] contributed by [StefH](https://github.com/StefH) +- [#1370](https://github.com/wiremock/WireMock.Net/pull/1370) - Support Testcontainers 4.8.0 [bug] contributed by [MD-V](https://github.com/MD-V) +- [#1126](https://github.com/wiremock/WireMock.Net/issues/1126) - Request matching WithProbability strange behaviour [bug] + # 1.14.0 (06 October 2025) - [#1362](https://github.com/wiremock/WireMock.Net/pull/1362) - Update ProxyUrlReplaceSettingsModel with TransformTemplate property [feature] contributed by [StefH](https://github.com/StefH) - [#1363](https://github.com/wiremock/WireMock.Net/pull/1363) - Add Tls13 [bug] contributed by [StefH](https://github.com/StefH) diff --git a/Directory.Build.props b/Directory.Build.props index ae634272..00b08eac 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,7 +4,7 @@ - 1.14.0 + 1.15.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 cfd2293c..79d498af 100644 --- a/Generate-ReleaseNotes.cmd +++ b/Generate-ReleaseNotes.cmd @@ -1,6 +1,6 @@ rem https://github.com/StefH/GitHubReleaseNotes -SET version=1.14.0 +SET version=1.15.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 f0fd522d..2f6c012a 100644 --- a/PackageReleaseNotes.txt +++ b/PackageReleaseNotes.txt @@ -1,7 +1,6 @@ -# 1.14.0 (06 October 2025) -- #1362 Update ProxyUrlReplaceSettingsModel with TransformTemplate property [feature] -- #1363 Add Tls13 [bug] -- #1342 Facing SSL certificate validation error when using .NET 5 and above framework for some http services during recording [bug] -- #1360 ProxyURL path configuration [feature] +# 1.15.0 (22 October 2025) +- #1367 Fix WithProbability logic [bug] +- #1370 Support Testcontainers 4.8.0 [bug] +- #1126 Request matching WithProbability strange behaviour [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