diff --git a/src/WireMock.Net/Owin/MappingMatcher.cs b/src/WireMock.Net/Owin/MappingMatcher.cs index 230fe4d4..bc3342a8 100644 --- a/src/WireMock.Net/Owin/MappingMatcher.cs +++ b/src/WireMock.Net/Owin/MappingMatcher.cs @@ -35,11 +35,7 @@ internal class MappingMatcher : IMappingMatcher { var nextState = GetNextState(mapping); - var mappingMatcherResult = new MappingMatcherResult - { - Mapping = mapping, - RequestMatchResult = mapping.GetRequestMatchResult(request, nextState) - }; + var mappingMatcherResult = new MappingMatcherResult(mapping, mapping.GetRequestMatchResult(request, nextState)); var exceptions = mappingMatcherResult.RequestMatchResult.MatchDetails .Where(md => md.Exception != null) @@ -66,7 +62,10 @@ internal class MappingMatcher : IMappingMatcher var partialMappings = possibleMappings .Where(pm => (pm.Mapping.IsAdminInterface && pm.RequestMatchResult.IsPerfectMatch) || !pm.Mapping.IsAdminInterface) - .OrderBy(m => m.RequestMatchResult).ThenBy(m => m.Mapping.Priority).ThenByDescending(m => m.Mapping.UpdatedAt) + .OrderBy(m => m.RequestMatchResult) + .ThenBy(m => m.RequestMatchResult.TotalNumber) + .ThenBy(m => m.Mapping.Priority) + .ThenByDescending(m => m.Mapping.UpdatedAt) .ToList(); var partialMatch = partialMappings.FirstOrDefault(pm => pm.RequestMatchResult.AverageTotalScore > 0.0); diff --git a/src/WireMock.Net/Owin/MappingMatcherResult.cs b/src/WireMock.Net/Owin/MappingMatcherResult.cs index 34448303..848a69bb 100644 --- a/src/WireMock.Net/Owin/MappingMatcherResult.cs +++ b/src/WireMock.Net/Owin/MappingMatcherResult.cs @@ -1,10 +1,17 @@ +using Stef.Validation; using WireMock.Matchers.Request; namespace WireMock.Owin; internal class MappingMatcherResult { - public IMapping Mapping { get; set; } + public IMapping Mapping { get; } - public IRequestMatchResult RequestMatchResult { get; set; } + public IRequestMatchResult RequestMatchResult { get; } + + public MappingMatcherResult(IMapping mapping, IRequestMatchResult requestMatchResult) + { + Mapping = Guard.NotNull(mapping); + RequestMatchResult = Guard.NotNull(requestMatchResult); + } } \ No newline at end of file diff --git a/src/WireMock.Net/Server/IRespondWithAProvider.cs b/src/WireMock.Net/Server/IRespondWithAProvider.cs index b62840b5..ea3bec77 100644 --- a/src/WireMock.Net/Server/IRespondWithAProvider.cs +++ b/src/WireMock.Net/Server/IRespondWithAProvider.cs @@ -61,7 +61,7 @@ public interface IRespondWithAProvider /// /// Define the priority for this mapping. /// - /// The priority. + /// The priority. (A lower value means a higher priority.) /// The . IRespondWithAProvider AtPriority(int priority); diff --git a/test/WireMock.Net.Tests/Owin/WireMockMiddlewareTests.cs b/test/WireMock.Net.Tests/Owin/WireMockMiddlewareTests.cs index 7bd2913f..b7e85b89 100644 --- a/test/WireMock.Net.Tests/Owin/WireMockMiddlewareTests.cs +++ b/test/WireMock.Net.Tests/Owin/WireMockMiddlewareTests.cs @@ -16,6 +16,7 @@ using WireMock.Admin.Requests; using WireMock.Settings; using FluentAssertions; using WireMock.Handlers; +using WireMock.Matchers.Request; using WireMock.ResponseBuilders; using WireMock.RequestBuilders; #if NET452 @@ -43,6 +44,7 @@ public class WireMockMiddlewareTests private readonly Mock _responseMapperMock; private readonly Mock _matcherMock; private readonly Mock _mappingMock; + private readonly Mock _requestMatchResultMock; private readonly Mock _contextMock; private readonly WireMockMiddleware _sut; @@ -72,12 +74,16 @@ public class WireMockMiddlewareTests _matcherMock = new Mock(); _matcherMock.SetupAllProperties(); - _matcherMock.Setup(m => m.FindBestMatch(It.IsAny())).Returns((new MappingMatcherResult(), new MappingMatcherResult())); + // _matcherMock.Setup(m => m.FindBestMatch(It.IsAny())).Returns((new MappingMatcherResult(), new MappingMatcherResult())); _contextMock = new Mock(); _mappingMock = new Mock(); + _requestMatchResultMock = new Mock(); + _requestMatchResultMock.Setup(r => r.TotalNumber).Returns(1); + _requestMatchResultMock.Setup(r => r.MatchDetails).Returns(new List()); + _sut = new WireMockMiddleware( null, _optionsMock.Object, @@ -133,7 +139,7 @@ public class WireMockMiddlewareTests _optionsMock.SetupGet(o => o.AuthenticationMatcher).Returns(new ExactMatcher()); _mappingMock.SetupGet(m => m.IsAdminInterface).Returns(true); - var result = new MappingMatcherResult { Mapping = _mappingMock.Object }; + var result = new MappingMatcherResult(_mappingMock.Object, _requestMatchResultMock.Object); _matcherMock.Setup(m => m.FindBestMatch(It.IsAny())).Returns((result, result)); // Act @@ -156,7 +162,7 @@ public class WireMockMiddlewareTests _optionsMock.SetupGet(o => o.AuthenticationMatcher).Returns(new ExactMatcher()); _mappingMock.SetupGet(m => m.IsAdminInterface).Returns(true); - var result = new MappingMatcherResult { Mapping = _mappingMock.Object }; + var result = new MappingMatcherResult(_mappingMock.Object, _requestMatchResultMock.Object); _matcherMock.Setup(m => m.FindBestMatch(It.IsAny())).Returns((result, result)); // Act @@ -216,7 +222,7 @@ public class WireMockMiddlewareTests var requestBuilder = Request.Create().UsingAnyMethod(); _mappingMock.SetupGet(m => m.RequestMatcher).Returns(requestBuilder); - var result = new MappingMatcherResult { Mapping = _mappingMock.Object }; + var result = new MappingMatcherResult(_mappingMock.Object, _requestMatchResultMock.Object); _matcherMock.Setup(m => m.FindBestMatch(It.IsAny())).Returns((result, result)); // Act @@ -270,7 +276,7 @@ public class WireMockMiddlewareTests var requestBuilder = Request.Create().UsingAnyMethod(); _mappingMock.SetupGet(m => m.RequestMatcher).Returns(requestBuilder); - var result = new MappingMatcherResult { Mapping = _mappingMock.Object }; + var result = new MappingMatcherResult (_mappingMock.Object, _requestMatchResultMock.Object); _matcherMock.Setup(m => m.FindBestMatch(It.IsAny())).Returns((result, result)); // Act diff --git a/test/WireMock.Net.Tests/RequestMatchers/RequestMessageBodyMatcherTests.cs b/test/WireMock.Net.Tests/RequestMatchers/RequestMessageBodyMatcherTests.cs index 26812d12..7fa47186 100644 --- a/test/WireMock.Net.Tests/RequestMatchers/RequestMessageBodyMatcherTests.cs +++ b/test/WireMock.Net.Tests/RequestMatchers/RequestMessageBodyMatcherTests.cs @@ -300,6 +300,31 @@ public class RequestMessageBodyMatcherTests Check.That(score).IsEqualTo(1.0d); } + [Fact] + public void RequestMessageBodyMatcher_GetMatchingScore_BodyAsJson_JmesPathMatchers() + { + // Arrange + var body = new BodyData + { + BodyAsJson = new { requestId = "1", value = "A" }, + DetectedBodyType = BodyType.Json + }; + + var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body); + + var jmesMatcher1 = new JmesPathMatcher("requestId == '1'"); + var jmesMatcher2 = new JmesPathMatcher("value == 'A'"); + + var bodyMatcher = new RequestMessageBodyMatcher(MatchOperator.And, jmesMatcher1, jmesMatcher2); + + // Act + var result = new RequestMatchResult(); + double score = bodyMatcher.GetMatchingScore(requestMessage, result); + + // Assert + score.Should().Be(MatchScores.Perfect); + } + [Theory] [InlineData(null, 0.0)] [InlineData(new byte[0], 0.0)] diff --git a/test/WireMock.Net.Tests/Util/StringUtilsTests.cs b/test/WireMock.Net.Tests/Util/StringUtilsTests.cs index 4a9ca6c9..657dd369 100644 --- a/test/WireMock.Net.Tests/Util/StringUtilsTests.cs +++ b/test/WireMock.Net.Tests/Util/StringUtilsTests.cs @@ -1,4 +1,5 @@ using FluentAssertions; +using WireMock.Matchers; using WireMock.Util; using Xunit; @@ -6,13 +7,39 @@ namespace WireMock.Net.Tests.Util; public class StringUtilsTests { + [Theory] + [InlineData("And", MatchOperator.And)] + [InlineData("Or", MatchOperator.Or)] + public void ParseMatchOperator_ShouldReturnCorrectEnumValue_WhenValidStringIsProvided(string value, MatchOperator expected) + { + // Arrange & Act + var result = StringUtils.ParseMatchOperator(value); + + // Assert + result.Should().Be(expected); + } + + [Theory] + [InlineData(null, MatchOperator.Or)] + [InlineData("", MatchOperator.Or)] + [InlineData("and", MatchOperator.Or)] + [InlineData("InvalidValue", MatchOperator.Or)] + public void ParseMatchOperator_ShouldReturnDefaultEnumValue_WhenInvalidOrNullStringIsProvided(string? value, MatchOperator expected) + { + // Arrange & Act + var result = StringUtils.ParseMatchOperator(value); + + // Assert + result.Should().Be(expected); + } + [Theory] [InlineData("'s")] [InlineData("\"s")] public void StringUtils_TryParseQuotedString_With_UnexpectedUnclosedString_Returns_False(string input) { // Act - bool valid = StringUtils.TryParseQuotedString(input, out var result, out var quote); + var valid = StringUtils.TryParseQuotedString(input, out _, out _); // Assert valid.Should().BeFalse(); @@ -25,7 +52,7 @@ public class StringUtilsTests public void StringUtils_TryParseQuotedString_With_InvalidStringLength_Returns_False(string input) { // Act - bool valid = StringUtils.TryParseQuotedString(input, out var result, out var quote); + var valid = StringUtils.TryParseQuotedString(input, out _, out _); // Assert valid.Should().BeFalse(); @@ -37,7 +64,7 @@ public class StringUtilsTests public void StringUtils_TryParseQuotedString_With_InvalidStringQuoteCharacter_Returns_False(string input) { // Act - bool valid = StringUtils.TryParseQuotedString(input, out var result, out var quote); + var valid = StringUtils.TryParseQuotedString(input, out _, out _); // Assert valid.Should().BeFalse(); @@ -47,10 +74,10 @@ public class StringUtilsTests public void StringUtils_TryParseQuotedString_With_UnexpectedUnrecognizedEscapeSequence_Returns_False() { // Arrange - string input = new string(new[] { '"', '\\', 'u', '?', '"' }); + var input = new string(new[] { '"', '\\', 'u', '?', '"' }); // Act - bool valid = StringUtils.TryParseQuotedString(input, out var result, out var quote); + var valid = StringUtils.TryParseQuotedString(input, out _, out _); // Assert valid.Should().BeFalse(); @@ -64,7 +91,7 @@ public class StringUtilsTests public void StringUtils_TryParseQuotedString_SingleQuotedString(string input, string expectedResult) { // Act - bool valid = StringUtils.TryParseQuotedString(input, out var result, out var quote); + var valid = StringUtils.TryParseQuotedString(input, out var result, out var quote); // Assert valid.Should().BeTrue(); @@ -93,7 +120,7 @@ public class StringUtilsTests public void StringUtils_TryParseQuotedString_DoubleQuotedString(string input, string expectedResult) { // Act - bool valid = StringUtils.TryParseQuotedString(input, out var result, out var quote); + var valid = StringUtils.TryParseQuotedString(input, out var result, out var quote); // Assert valid.Should().BeTrue(); diff --git a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj index f6a3fb59..3fa60e0c 100644 --- a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj +++ b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj @@ -3,6 +3,8 @@ Stef Heyenrath net452;net461;netcoreapp3.1;net6.0;net7.0 + 11 + enable false full WireMock.Net.Tests diff --git a/test/WireMock.Net.Tests/WireMockServerTests.WithBody.cs b/test/WireMock.Net.Tests/WireMockServerTests.WithBody.cs index a891dfd3..cc90bef7 100644 --- a/test/WireMock.Net.Tests/WireMockServerTests.WithBody.cs +++ b/test/WireMock.Net.Tests/WireMockServerTests.WithBody.cs @@ -1,9 +1,8 @@ #if !NET452 -//using System; +using System; using System.Collections.Generic; using System.Net; using System.Net.Http; -//using System.Net.Http.Json; using System.Threading.Tasks; using FluentAssertions; using WireMock.Matchers; @@ -21,6 +20,103 @@ public partial class WireMockServerTests public string? Hi { get; set; } } + [Fact] + public async Task WireMockServer_WithBodyAsJson_Using_PostAsJsonAsync_And_MultipleJmesPathMatchers_ShouldMatch() + { + // Arrange + var server = WireMockServer.Start(); + server.Given( + Request.Create() + .WithPath("/a") + .WithBody( + new IMatcher[] + { + new JmesPathMatcher("requestId == '1'"), + new JmesPathMatcher("value == 'A'") + }, + MatchOperator.And + ) + .UsingPost() + ) + .RespondWith(Response.Create().WithStatusCode(HttpStatusCode.OK)); + + server.Given( + Request.Create() + .WithPath("/a") + .WithBody( + new IMatcher[] + { + new JmesPathMatcher("requestId == '2'"), + new JmesPathMatcher("value == 'A'") + }, + MatchOperator.And + ) + .UsingPost() + ) + .RespondWith(Response.Create().WithStatusCode(HttpStatusCode.Moved)); + + // Act + var requestUri = new Uri($"http://localhost:{server.Port}/a"); + + var json = new { requestId = "1", value = "A" }; + var response = await server.CreateClient().PostAsJsonAsync(requestUri, json).ConfigureAwait(false); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.OK); + + server.Stop(); + } + + [Fact] + public async Task WireMockServer_WithBodyAsJson_Using_PostAsJsonAsync_And_MultipleJmesPathMatchers_ShouldMatch_BestMatching() + { + // Arrange + var server = WireMockServer.Start(); + server.Given( + Request.Create() + .WithPath("/a") + .WithBody( + new IMatcher[] + { + new JmesPathMatcher("extra == 'X'"), + new JmesPathMatcher("requestId == '1'"), + new JmesPathMatcher("value == 'A'") + }, + MatchOperator.And + ) + .UsingPost() + ) + .AtPriority(1) // Higher priority + .RespondWith(Response.Create().WithStatusCode(HttpStatusCode.OK)); + + server.Given( + Request.Create() + .WithPath("/a") + .WithBody( + new IMatcher[] + { + new JmesPathMatcher("requestId == '1'"), + new JmesPathMatcher("value == 'A'") + }, + MatchOperator.And + ) + .UsingPost() + ) + .AtPriority(2) + .RespondWith(Response.Create().WithStatusCode(HttpStatusCode.Moved)); + + // Act + var requestUri = new Uri($"http://localhost:{server.Port}/a"); + + var json = new { extra = "X", requestId = "1", value = "A" }; + var response = await server.CreateClient().PostAsJsonAsync(requestUri, json).ConfigureAwait(false); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.OK); + + server.Stop(); + } + [Fact] public async Task WireMockServer_WithBodyAsJson_Using_PostAsJsonAsync_And_WildcardMatcher_ShouldMatch() {