diff --git a/src/WireMock.Net.Minimal/Matchers/Request/RequestMessageParamMatcher.cs b/src/WireMock.Net.Minimal/Matchers/Request/RequestMessageParamMatcher.cs index f7be7fb1..5ca2f6d8 100644 --- a/src/WireMock.Net.Minimal/Matchers/Request/RequestMessageParamMatcher.cs +++ b/src/WireMock.Net.Minimal/Matchers/Request/RequestMessageParamMatcher.cs @@ -1,6 +1,5 @@ // Copyright © WireMock.Net -using System.Linq; using Stef.Validation; using WireMock.Types; @@ -54,7 +53,10 @@ public class RequestMessageParamMatcher : IRequestMatcher /// Defines if the key should be matched using case-ignore. /// The values. public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, string key, bool ignoreCase, params string[]? values) : - this(matchBehaviour, key, ignoreCase, values?.Select(value => new ExactMatcher(matchBehaviour, ignoreCase, MatchOperator.And, value)).Cast().ToArray()) + // Note: the inner ExactMatcher must use AcceptOnMatch. + // The MatchBehaviour (e.g. RejectOnMatch) is applied once by this matcher's GetMatchingScore. + // Passing matchBehaviour here as well would apply the conversion twice and invert RejectOnMatch. + this(matchBehaviour, key, ignoreCase, values?.Select(value => new ExactMatcher(MatchBehaviour.AcceptOnMatch, ignoreCase, MatchOperator.And, value)).Cast().ToArray()) { } diff --git a/test/WireMock.Net.Tests/RequestMatchers/RequestMessageParamMatcherTests.cs b/test/WireMock.Net.Tests/RequestMatchers/RequestMessageParamMatcherTests.cs index 0ca30f13..c5db79a1 100644 --- a/test/WireMock.Net.Tests/RequestMatchers/RequestMessageParamMatcherTests.cs +++ b/test/WireMock.Net.Tests/RequestMatchers/RequestMessageParamMatcherTests.cs @@ -209,5 +209,79 @@ public class RequestMessageParamMatcherTests // Assert score.Should().Be(1.0); } -} + [Fact] + public void RequestMessageParamMatcher_RejectOnMatch_WhenValuePresentMatchesPattern_ReturnsMismatch() + { + // Assign: the param value in the URL matches the reject pattern -> the mapping must be rejected (score 0.0). + var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=abc"), "GET", "127.0.0.1"); + var matcher = new RequestMessageParamMatcher(MatchBehaviour.RejectOnMatch, "key", false, new[] { "abc" }); + + // Act + var result = new RequestMatchResult(); + var score = matcher.GetMatchingScore(requestMessage, result); + + // Assert + score.Should().Be(0.0d); + } + + [Fact] + public void RequestMessageParamMatcher_RejectOnMatch_WhenValuePresentDoesNotMatchPattern_ReturnsPerfect() + { + // Assign: the param value in the URL does NOT match the reject pattern -> the mapping is accepted (score 1.0). + var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=xyz"), "GET", "127.0.0.1"); + var matcher = new RequestMessageParamMatcher(MatchBehaviour.RejectOnMatch, "key", false, new[] { "abc" }); + + // Act + var result = new RequestMatchResult(); + var score = matcher.GetMatchingScore(requestMessage, result); + + // Assert + score.Should().Be(1.0d); + } + + [Fact] + public void RequestMessageParamMatcher_RejectOnMatch_WithIgnoreCase_WhenValueMatchesCaseInsensitively_ReturnsMismatch() + { + // Assign: ignoreCase must still be honored on the inner matcher after the fix. + var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=ABC"), "GET", "127.0.0.1"); + var matcher = new RequestMessageParamMatcher(MatchBehaviour.RejectOnMatch, "key", true, new[] { "abc" }); + + // Act + var result = new RequestMatchResult(); + var score = matcher.GetMatchingScore(requestMessage, result); + + // Assert + score.Should().Be(0.0d); + } + + [Fact] + public void RequestMessageParamMatcher_AcceptOnMatch_WhenValuePresentMatchesPattern_ReturnsPerfect() + { + // Assign + var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=abc"), "GET", "127.0.0.1"); + var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, new[] { "abc" }); + + // Act + var result = new RequestMatchResult(); + var score = matcher.GetMatchingScore(requestMessage, result); + + // Assert + score.Should().Be(1.0d); + } + + [Fact] + public void RequestMessageParamMatcher_AcceptOnMatch_WhenValuePresentDoesNotMatchPattern_ReturnsMismatch() + { + // Assign + var requestMessage = new RequestMessage(new UrlDetails("http://localhost?key=xyz"), "GET", "127.0.0.1"); + var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "key", false, new[] { "abc" }); + + // Act + var result = new RequestMatchResult(); + var score = matcher.GetMatchingScore(requestMessage, result); + + // Assert + score.Should().Be(0.0d); + } +} diff --git a/test/WireMock.Net.Tests/WireMockServerTests.WithParam.cs b/test/WireMock.Net.Tests/WireMockServerTests.WithParam.cs index dce6f73d..bd5ea67c 100644 --- a/test/WireMock.Net.Tests/WireMockServerTests.WithParam.cs +++ b/test/WireMock.Net.Tests/WireMockServerTests.WithParam.cs @@ -93,6 +93,54 @@ public partial class WireMockServerTests server.Stop(); } + [Fact] + public async Task WireMockServer_WithParam_RejectOnMatch_WithValue_WhenValueMatches_ShouldNotMatch_Returns404() + { + // Arrange + var cancelationToken = TestContext.Current.CancellationToken; + var server = WireMockServer.Start(); + server.Given( + Request.Create() + .WithPath("/x") + .WithParam("k", MatchBehaviour.RejectOnMatch, "abc") + .UsingGet() + ) + .ThenRespondWithOK(); + + // Act + var requestUri = new Uri($"http://localhost:{server.Port}/x?k=abc"); + var response = await server.CreateClient().GetAsync(requestUri, cancelationToken); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.NotFound); + + server.Stop(); + } + + [Fact] + public async Task WireMockServer_WithParam_RejectOnMatch_WithValue_WhenValueDoesNotMatch_ShouldMatch_Returns200() + { + // Arrange + var cancelationToken = TestContext.Current.CancellationToken; + var server = WireMockServer.Start(); + server.Given( + Request.Create() + .WithPath("/x") + .WithParam("k", MatchBehaviour.RejectOnMatch, "abc") + .UsingGet() + ) + .ThenRespondWithOK(); + + // Act + var requestUri = new Uri($"http://localhost:{server.Port}/x?k=xyz"); + var response = await server.CreateClient().GetAsync(requestUri, cancelationToken); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.OK); + + server.Stop(); + } + [Fact] public async Task WireMockServer_WithParam_AcceptOnMatch_OnNonMatchingParam_ShouldReturnMappingOk() {