mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-01-11 13:00:33 +01:00
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>
This commit is contained in:
@@ -267,7 +267,7 @@ namespace WireMock.Net.ConsoleApplication
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Run()
|
public static async Task RunAsync()
|
||||||
{
|
{
|
||||||
//RunSse();
|
//RunSse();
|
||||||
//RunOnLocal();
|
//RunOnLocal();
|
||||||
@@ -290,25 +290,56 @@ namespace WireMock.Net.ConsoleApplication
|
|||||||
|
|
||||||
var server = WireMockServer.Start();
|
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
|
server
|
||||||
.Given(Request.Create()
|
.Given(Request.Create().UsingGet().WithPath("/p"))
|
||||||
.WithPath("todos")
|
.WithProbability(pX)
|
||||||
.UsingGet()
|
.RespondWith(Response.Create().WithStatusCode(200).WithBody("X"));
|
||||||
)
|
|
||||||
.RespondWith(Response.Create()
|
|
||||||
.WithBodyAsJson(todos.Values)
|
|
||||||
);
|
|
||||||
|
|
||||||
server
|
server
|
||||||
.Given(Request.Create()
|
.Given(Request.Create().UsingGet().WithPath("/p"))
|
||||||
.UsingGet()
|
.RespondWith(Response.Create().WithStatusCode(200).WithBody("default"));
|
||||||
.WithPath("todos")
|
|
||||||
.WithParam("id")
|
|
||||||
)
|
|
||||||
.RespondWith(Response.Create()
|
|
||||||
.WithBodyAsJson(rm => todos[int.Parse(rm.Query!["id"].ToString())])
|
|
||||||
);
|
|
||||||
|
|
||||||
|
// 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
|
using var httpAndHttpsWithPort = WireMockServer.Start(new WireMockServerSettings
|
||||||
{
|
{
|
||||||
HostingScheme = HostingScheme.HttpAndHttps,
|
HostingScheme = HostingScheme.HttpAndHttps,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using log4net;
|
using log4net;
|
||||||
using log4net.Config;
|
using log4net.Config;
|
||||||
using log4net.Repository;
|
using log4net.Repository;
|
||||||
@@ -14,10 +15,10 @@ static class Program
|
|||||||
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
|
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
|
||||||
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
|
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"));
|
XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config"));
|
||||||
|
|
||||||
MainApp.Run();
|
await MainApp.RunAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,16 +9,10 @@ using WireMock.Services;
|
|||||||
|
|
||||||
namespace WireMock.Owin;
|
namespace WireMock.Owin;
|
||||||
|
|
||||||
internal class MappingMatcher : IMappingMatcher
|
internal class MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDoubleBetween0And1 randomizerDoubleBetween0And1) : IMappingMatcher
|
||||||
{
|
{
|
||||||
private readonly IWireMockMiddlewareOptions _options;
|
private readonly IWireMockMiddlewareOptions _options = Guard.NotNull(options);
|
||||||
private readonly IRandomizerDoubleBetween0And1 _randomizerDoubleBetween0And1;
|
private readonly IRandomizerDoubleBetween0And1 _randomizerDoubleBetween0And1 = Guard.NotNull(randomizerDoubleBetween0And1);
|
||||||
|
|
||||||
public MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDoubleBetween0And1 randomizerDoubleBetween0And1)
|
|
||||||
{
|
|
||||||
_options = Guard.NotNull(options);
|
|
||||||
_randomizerDoubleBetween0And1 = Guard.NotNull(randomizerDoubleBetween0And1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public (MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request)
|
public (MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request)
|
||||||
{
|
{
|
||||||
@@ -28,7 +22,7 @@ internal class MappingMatcher : IMappingMatcher
|
|||||||
|
|
||||||
var mappings = _options.Mappings.Values
|
var mappings = _options.Mappings.Values
|
||||||
.Where(m => m.TimeSettings.IsValid())
|
.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();
|
.ToArray();
|
||||||
|
|
||||||
foreach (var mapping in mappings)
|
foreach (var mapping in mappings)
|
||||||
@@ -41,10 +35,10 @@ internal class MappingMatcher : IMappingMatcher
|
|||||||
|
|
||||||
var exceptions = mappingMatcherResult.RequestMatchResult.MatchDetails
|
var exceptions = mappingMatcherResult.RequestMatchResult.MatchDetails
|
||||||
.Where(md => md.Exception != null)
|
.Where(md => md.Exception != null)
|
||||||
.Select(md => md.Exception)
|
.Select(md => md.Exception!)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
if (!exceptions.Any())
|
if (exceptions.Length == 0)
|
||||||
{
|
{
|
||||||
possibleMappings.Add(mappingMatcherResult);
|
possibleMappings.Add(mappingMatcherResult);
|
||||||
}
|
}
|
||||||
@@ -52,7 +46,7 @@ internal class MappingMatcher : IMappingMatcher
|
|||||||
{
|
{
|
||||||
foreach (var ex in exceptions)
|
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)
|
.Where(pm => (pm.Mapping.IsAdminInterface && pm.RequestMatchResult.IsPerfectMatch) || !pm.Mapping.IsAdminInterface)
|
||||||
.OrderBy(m => m.RequestMatchResult)
|
.OrderBy(m => m.RequestMatchResult)
|
||||||
.ThenBy(m => m.RequestMatchResult.TotalNumber)
|
.ThenBy(m => m.RequestMatchResult.TotalNumber)
|
||||||
.ThenBy(m => m.Mapping.Priority)
|
.ThenBy(m => m.Mapping.Priority)
|
||||||
|
.ThenByDescending(m => m.Mapping.Probability)
|
||||||
.ThenByDescending(m => m.Mapping.UpdatedAt)
|
.ThenByDescending(m => m.Mapping.UpdatedAt)
|
||||||
.ToList();
|
.Where(pm => pm.RequestMatchResult.AverageTotalScore > 0.0)
|
||||||
var partialMatch = partialMappings.FirstOrDefault(pm => pm.RequestMatchResult.AverageTotalScore > 0.0);
|
.ToArray();
|
||||||
|
var partialMatch = partialMatches.FirstOrDefault();
|
||||||
|
|
||||||
if (_options.AllowPartialMapping == true)
|
if (_options.AllowPartialMapping == true)
|
||||||
{
|
{
|
||||||
@@ -78,7 +74,11 @@ internal class MappingMatcher : IMappingMatcher
|
|||||||
|
|
||||||
var match = possibleMappings
|
var match = possibleMappings
|
||||||
.Where(m => m.RequestMatchResult.IsPerfectMatch)
|
.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();
|
.FirstOrDefault();
|
||||||
|
|
||||||
return (match, partialMatch);
|
return (match, partialMatch);
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ using WireMock.Matchers.Request;
|
|||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
using WireMock.Owin;
|
using WireMock.Owin;
|
||||||
using WireMock.Services;
|
using WireMock.Services;
|
||||||
using WireMock.Util;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace WireMock.Net.Tests.Owin;
|
namespace WireMock.Net.Tests.Owin;
|
||||||
@@ -26,7 +25,7 @@ public class MappingMatcherTests
|
|||||||
_optionsMock = new Mock<IWireMockMiddlewareOptions>();
|
_optionsMock = new Mock<IWireMockMiddlewareOptions>();
|
||||||
_optionsMock.SetupAllProperties();
|
_optionsMock.SetupAllProperties();
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(new ConcurrentDictionary<Guid, IMapping>());
|
_optionsMock.Setup(o => o.Mappings).Returns(new ConcurrentDictionary<Guid, IMapping>());
|
||||||
_optionsMock.Setup(o => o.LogEntries).Returns(new ConcurrentObservableCollection<LogEntry>());
|
_optionsMock.Setup(o => o.LogEntries).Returns([]);
|
||||||
_optionsMock.Setup(o => o.Scenarios).Returns(new ConcurrentDictionary<string, ScenarioState>());
|
_optionsMock.Setup(o => o.Scenarios).Returns(new ConcurrentDictionary<string, ScenarioState>());
|
||||||
|
|
||||||
var loggerMock = new Mock<IWireMockLogger>();
|
var loggerMock = new Mock<IWireMockLogger>();
|
||||||
@@ -35,7 +34,7 @@ public class MappingMatcherTests
|
|||||||
_optionsMock.Setup(o => o.Logger).Returns(loggerMock.Object);
|
_optionsMock.Setup(o => o.Logger).Returns(loggerMock.Object);
|
||||||
|
|
||||||
_randomizerDoubleBetween0And1Mock = new Mock<IRandomizerDoubleBetween0And1>();
|
_randomizerDoubleBetween0And1Mock = new Mock<IRandomizerDoubleBetween0And1>();
|
||||||
_randomizerDoubleBetween0And1Mock.Setup(r => r.Generate()).Returns(0.0);
|
_randomizerDoubleBetween0And1Mock.Setup(r => r.Generate()).Returns(0.5);
|
||||||
|
|
||||||
_sut = new MappingMatcher(_optionsMock.Object, _randomizerDoubleBetween0And1Mock.Object);
|
_sut = new MappingMatcher(_optionsMock.Object, _randomizerDoubleBetween0And1Mock.Object);
|
||||||
}
|
}
|
||||||
@@ -84,8 +83,8 @@ public class MappingMatcherTests
|
|||||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
var mappings = InitMappings
|
var mappings = InitMappings
|
||||||
(
|
(
|
||||||
(guid1, new[] { 0.1 }, null),
|
(guid1, [0.1], null),
|
||||||
(guid2, new[] { 1.0 }, null)
|
(guid2, [1.0], null)
|
||||||
);
|
);
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
@@ -112,8 +111,8 @@ public class MappingMatcherTests
|
|||||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
var mappings = InitMappings
|
var mappings = InitMappings
|
||||||
(
|
(
|
||||||
(guid1, new[] { 0.1 }, null),
|
(guid1, [0.1], null),
|
||||||
(guid2, new[] { 0.9 }, null)
|
(guid2, [0.9], null)
|
||||||
);
|
);
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
@@ -139,8 +138,8 @@ public class MappingMatcherTests
|
|||||||
|
|
||||||
_optionsMock.SetupGet(o => o.AllowPartialMapping).Returns(true);
|
_optionsMock.SetupGet(o => o.AllowPartialMapping).Returns(true);
|
||||||
var mappings = InitMappings(
|
var mappings = InitMappings(
|
||||||
(guid1, new[] { 0.1 }, null),
|
(guid1, [0.1], null),
|
||||||
(guid2, new[] { 0.9 }, null)
|
(guid2, [0.9], null)
|
||||||
);
|
);
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
@@ -166,8 +165,8 @@ public class MappingMatcherTests
|
|||||||
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
var mappings = InitMappings(
|
var mappings = InitMappings(
|
||||||
(guid1, new[] { 1.0 }, null),
|
(guid1, [1.0], null),
|
||||||
(guid2, new[] { 1.0, 1.0 }, null)
|
(guid2, [1.0, 1.0], null)
|
||||||
);
|
);
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
@@ -187,15 +186,15 @@ public class MappingMatcherTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void MappingMatcher_FindBestMatch_WhenProbabilityFailsFirst_ShouldReturnSecondMatch()
|
public void MappingMatcher_FindBestMatch_WhenProbabilityDoesNotMatch_ShouldReturnNormalMatch()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
var withProbability = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
var noProbability = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
var mappings = InitMappings
|
var mappings = InitMappings
|
||||||
(
|
(
|
||||||
(guid1, new[] { 1.0 }, 1.0),
|
(withProbability, [1.0], 0.4),
|
||||||
(guid2, new[] { 1.0 }, null)
|
(noProbability, [1.0], null)
|
||||||
);
|
);
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
@@ -206,8 +205,30 @@ public class MappingMatcherTests
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Match.Should().NotBeNull();
|
result.Match.Should().NotBeNull();
|
||||||
result.Match!.Mapping.Guid.Should().Be(guid2);
|
result.Match!.Mapping.Guid.Should().Be(noProbability);
|
||||||
result.Match.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
}
|
||||||
|
|
||||||
|
[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<Guid, IMapping> InitMappings(params (Guid guid, double[] scores, double? probability)[] matches)
|
private static ConcurrentDictionary<Guid, IMapping> InitMappings(params (Guid guid, double[] scores, double? probability)[] matches)
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public partial class WireMockServerTests
|
|||||||
var response = await server.CreateClient().GetAsync(requestUri).ConfigureAwait(false);
|
var response = await server.CreateClient().GetAsync(requestUri).ConfigureAwait(false);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(new[] { HttpStatusCode.OK, HttpStatusCode.InternalServerError }.Contains(response.StatusCode));
|
Assert.Contains(response.StatusCode, [HttpStatusCode.OK, HttpStatusCode.InternalServerError]);
|
||||||
|
|
||||||
server.Stop();
|
server.Stop();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user