diff --git a/src/WireMock.Net/Matchers/FormUrlEncodedMatcher.cs b/src/WireMock.Net/Matchers/FormUrlEncodedMatcher.cs
new file mode 100644
index 00000000..decbb1d8
--- /dev/null
+++ b/src/WireMock.Net/Matchers/FormUrlEncodedMatcher.cs
@@ -0,0 +1,153 @@
+// Copyright © WireMock.Net
+
+using System.Collections.Generic;
+using AnyOfTypes;
+using Stef.Validation;
+using WireMock.Models;
+using WireMock.Util;
+
+namespace WireMock.Matchers;
+
+///
+/// FormUrl Encoded fields Matcher
+///
+///
+///
+public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
+{
+ private readonly AnyOf[] _patterns;
+
+ ///
+ public MatchBehaviour MatchBehaviour { get; }
+
+ private readonly List<(WildcardMatcher Key, WildcardMatcher? Value)> _pairs = [];
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The pattern.
+ /// Ignore the case from the pattern.
+ /// The to use. (default = "Or")
+ public FormUrlEncodedMatcher(
+ AnyOf pattern,
+ bool ignoreCase = false,
+ MatchOperator matchOperator = MatchOperator.Or) :
+ this(MatchBehaviour.AcceptOnMatch, [pattern], ignoreCase, matchOperator)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The match behaviour.
+ /// The pattern.
+ /// Ignore the case from the pattern.
+ /// The to use. (default = "Or")
+ public FormUrlEncodedMatcher(
+ MatchBehaviour matchBehaviour,
+ AnyOf pattern,
+ bool ignoreCase = false,
+ MatchOperator matchOperator = MatchOperator.Or) :
+ this(matchBehaviour, [pattern], ignoreCase, matchOperator)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The patterns.
+ /// Ignore the case from the pattern.
+ /// The to use. (default = "Or")
+ public FormUrlEncodedMatcher(
+ AnyOf[] patterns,
+ bool ignoreCase = false,
+ MatchOperator matchOperator = MatchOperator.Or) :
+ this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase, matchOperator)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The match behaviour.
+ /// The patterns.
+ /// Ignore the case from the pattern.
+ /// The to use. (default = "Or")
+ public FormUrlEncodedMatcher(
+ MatchBehaviour matchBehaviour,
+ AnyOf[] patterns,
+ bool ignoreCase = false,
+ MatchOperator matchOperator = MatchOperator.Or)
+ {
+ _patterns = Guard.NotNull(patterns);
+ IgnoreCase = ignoreCase;
+ MatchBehaviour = matchBehaviour;
+ MatchOperator = matchOperator;
+
+ foreach (var pattern in _patterns)
+ {
+ if (QueryStringParser.TryParse(pattern, IgnoreCase, out var nameValueCollection))
+ {
+ foreach (var nameValue in nameValueCollection)
+ {
+ var keyMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, [nameValue.Key], ignoreCase, MatchOperator);
+ var valueMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, [nameValue.Value], ignoreCase, MatchOperator);
+ _pairs.Add((keyMatcher, valueMatcher));
+ }
+ }
+ }
+ }
+
+ ///
+ public MatchResult IsMatch(string? input)
+ {
+ // Input is null or empty and if no patterns defined, return Perfect match.
+ if (string.IsNullOrEmpty(input) && _patterns.Length == 0)
+ {
+ return new MatchResult(MatchScores.Perfect);
+ }
+
+ if (!QueryStringParser.TryParse(input, IgnoreCase, out var inputNameValueCollection))
+ {
+ return new MatchResult(MatchScores.Mismatch);
+ }
+
+ var matches = new List();
+ foreach (var inputKeyValuePair in inputNameValueCollection)
+ {
+ var match = false;
+ foreach (var pair in _pairs)
+ {
+ var keyMatchResult = pair.Key.IsMatch(inputKeyValuePair.Key).IsPerfect();
+ if (keyMatchResult)
+ {
+ match = pair.Value?.IsMatch(inputKeyValuePair.Value).IsPerfect() ?? false;
+ if (match)
+ {
+ break;
+ }
+ }
+ }
+
+ matches.Add(match);
+ }
+
+ var score = MatchScores.ToScore(matches.ToArray(), MatchOperator);
+ return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score));
+ }
+
+ ///
+ public virtual AnyOf[] GetPatterns()
+ {
+ return _patterns;
+ }
+
+ ///
+ public virtual string Name => nameof(FormUrlEncodedMatcher);
+
+ ///
+ public bool IgnoreCase { get; }
+
+ ///
+ public MatchOperator MatchOperator { get; }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net/Serialization/MappingConverter.cs b/src/WireMock.Net/Serialization/MappingConverter.cs
index 98e6011a..b67175a9 100644
--- a/src/WireMock.Net/Serialization/MappingConverter.cs
+++ b/src/WireMock.Net/Serialization/MappingConverter.cs
@@ -147,19 +147,22 @@ public string ToCSharpCode(IMapping mapping, MappingConverterSettings? settings
{
var firstMatcher = requestMessageBodyMatcher.Matchers.FirstOrDefault();
- if (firstMatcher is WildcardMatcher wildcardMatcher && wildcardMatcher.GetPatterns().Any())
+ switch (firstMatcher)
{
- sb.AppendLine($" .WithBody({GetString(wildcardMatcher)})");
- }
+ case IStringMatcher stringMatcher when stringMatcher.GetPatterns().Length > 0:
+ sb.AppendLine($" .WithBody({GetString(stringMatcher)})");
+ break;
- if (firstMatcher is JsonMatcher jsonMatcher)
- {
- var matcherType = jsonMatcher.GetType().Name;
- sb.AppendLine($" .WithBody(new {matcherType}(");
- sb.AppendLine($" value: {ConvertToAnonymousObjectDefinition(jsonMatcher.Value, 3)},");
- sb.AppendLine($" ignoreCase: {ToCSharpBooleanLiteral(jsonMatcher.IgnoreCase)},");
- sb.AppendLine($" regex: {ToCSharpBooleanLiteral(jsonMatcher.Regex)}");
- sb.AppendLine(@" ))");
+ case JsonMatcher jsonMatcher:
+ {
+ var matcherType = jsonMatcher.GetType().Name;
+ sb.AppendLine($" .WithBody(new {matcherType}(");
+ sb.AppendLine($" value: {ConvertToAnonymousObjectDefinition(jsonMatcher.Value, 3)},");
+ sb.AppendLine($" ignoreCase: {ToCSharpBooleanLiteral(jsonMatcher.IgnoreCase)},");
+ sb.AppendLine($" regex: {ToCSharpBooleanLiteral(jsonMatcher.Regex)}");
+ sb.AppendLine(@" ))");
+ break;
+ }
}
}
diff --git a/src/WireMock.Net/Serialization/MatcherMapper.cs b/src/WireMock.Net/Serialization/MatcherMapper.cs
index 370c2a57..b4e66194 100644
--- a/src/WireMock.Net/Serialization/MatcherMapper.cs
+++ b/src/WireMock.Net/Serialization/MatcherMapper.cs
@@ -111,6 +111,9 @@ public MatcherMapper(WireMockServerSettings settings)
case nameof(ContentTypeMatcher):
return new ContentTypeMatcher(matchBehaviour, stringPatterns, ignoreCase);
+ case nameof(FormUrlEncodedMatcher):
+ return new FormUrlEncodedMatcher(matchBehaviour, stringPatterns, ignoreCase);
+
case nameof(SimMetricsMatcher):
SimMetricType type = SimMetricType.Levenstein;
if (!string.IsNullOrEmpty(matcherType) && !Enum.TryParse(matcherType, out type))
@@ -224,7 +227,7 @@ private AnyOf[] ParseStringPatterns(MatcherModel matcher)
{
if (matcher.Pattern is string patternAsString)
{
- return new[] { new AnyOf(patternAsString) };
+ return [new AnyOf(patternAsString)];
}
if (matcher.Pattern is IEnumerable patternAsStringArray)
@@ -241,7 +244,7 @@ private AnyOf[] ParseStringPatterns(MatcherModel matcher)
{
var patternAsFile = matcher.PatternAsFile!;
var pattern = _settings.FileSystemHandler.ReadFileAsString(patternAsFile);
- return new[] { new AnyOf(new StringPattern { Pattern = pattern, PatternAsFile = patternAsFile }) };
+ return [new AnyOf(new StringPattern { Pattern = pattern, PatternAsFile = patternAsFile })];
}
return EmptyArray>.Value;
diff --git a/test/WireMock.Net.Tests/MappingBuilderTests.GetMappings.verified.txt b/test/WireMock.Net.Tests/MappingBuilderTests.GetMappings.verified.txt
index 8812a2ff..5c5c98c8 100644
--- a/test/WireMock.Net.Tests/MappingBuilderTests.GetMappings.verified.txt
+++ b/test/WireMock.Net.Tests/MappingBuilderTests.GetMappings.verified.txt
@@ -1,6 +1,6 @@
[
{
- Guid: Guid_1,
+ Guid: 41372914-1838-4c67-916b-b9aacdd096ce,
UpdatedAt: 2023-01-14 15:16:17,
Request: {
Path: {
@@ -33,7 +33,7 @@
}
},
{
- Guid: Guid_2,
+ Guid: 98fae52e-76df-47d9-876f-2ee32e931002,
UpdatedAt: 2023-01-14 15:16:17,
Request: {
Path: {
@@ -61,5 +61,77 @@
}
},
Response: {}
+ },
+ {
+ Guid: 98fae52e-76df-47d9-876f-2ee32e931003,
+ UpdatedAt: 2023-01-14 15:16:17,
+ Request: {
+ Path: {
+ Matchers: [
+ {
+ Name: WildcardMatcher,
+ Pattern: /form-urlencoded,
+ IgnoreCase: false
+ }
+ ]
+ },
+ Methods: [
+ POST
+ ],
+ Headers: [
+ {
+ Name: Content-Type,
+ Matchers: [
+ {
+ Name: WildcardMatcher,
+ Pattern: application/x-www-form-urlencoded,
+ IgnoreCase: true
+ }
+ ],
+ IgnoreCase: true
+ }
+ ],
+ Body: {
+ Matcher: {
+ Name: FormUrlEncodedMatcher,
+ Patterns: [
+ name=John Doe,
+ email=johndoe@example.com
+ ],
+ IgnoreCase: false,
+ MatchOperator: Or
+ }
+ }
+ },
+ Response: {}
+ },
+ {
+ Guid: 98fae52e-76df-47d9-876f-2ee32e931001,
+ UpdatedAt: 2023-01-14 15:16:17,
+ Request: {
+ Path: {
+ Matchers: [
+ {
+ Name: WildcardMatcher,
+ Pattern: /users/post1,
+ IgnoreCase: false
+ }
+ ]
+ },
+ Methods: [
+ POST
+ ],
+ Body: {
+ Matcher: {
+ Name: JsonMatcher,
+ Pattern: {
+ Request: Hello?
+ },
+ IgnoreCase: false,
+ Regex: false
+ }
+ }
+ },
+ Response: {}
}
]
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/MappingBuilderTests.ToCSharpCode_Builder.verified.txt b/test/WireMock.Net.Tests/MappingBuilderTests.ToCSharpCode_Builder.verified.txt
index be439d3b..17ef2023 100644
--- a/test/WireMock.Net.Tests/MappingBuilderTests.ToCSharpCode_Builder.verified.txt
+++ b/test/WireMock.Net.Tests/MappingBuilderTests.ToCSharpCode_Builder.verified.txt
@@ -24,7 +24,35 @@ builder
regex: false
))
)
- .WithGuid("98fae52e-76df-47d9-876f-2ee32e931d9b")
+ .WithGuid("98fae52e-76df-47d9-876f-2ee32e931002")
+ .RespondWith(Response.Create()
+ );
+
+builder
+ .Given(Request.Create()
+ .UsingMethod("POST")
+ .WithPath("/form-urlencoded")
+ .WithHeader("Content-Type", "application/x-www-form-urlencoded", true)
+ .WithBody("name=John Doe")
+ )
+ .WithGuid("98fae52e-76df-47d9-876f-2ee32e931003")
+ .RespondWith(Response.Create()
+ );
+
+builder
+ .Given(Request.Create()
+ .UsingMethod("POST")
+ .WithPath("/users/post1")
+ .WithBody(new JsonMatcher(
+ value: new
+ {
+ Request = "Hello?"
+ },
+ ignoreCase: false,
+ regex: false
+ ))
+ )
+ .WithGuid("98fae52e-76df-47d9-876f-2ee32e931001")
.RespondWith(Response.Create()
);
diff --git a/test/WireMock.Net.Tests/MappingBuilderTests.ToCSharpCode_Server.verified.txt b/test/WireMock.Net.Tests/MappingBuilderTests.ToCSharpCode_Server.verified.txt
index 6091acbd..c9e10fa1 100644
--- a/test/WireMock.Net.Tests/MappingBuilderTests.ToCSharpCode_Server.verified.txt
+++ b/test/WireMock.Net.Tests/MappingBuilderTests.ToCSharpCode_Server.verified.txt
@@ -24,7 +24,35 @@ server
regex: false
))
)
- .WithGuid("98fae52e-76df-47d9-876f-2ee32e931d9b")
+ .WithGuid("98fae52e-76df-47d9-876f-2ee32e931002")
+ .RespondWith(Response.Create()
+ );
+
+server
+ .Given(Request.Create()
+ .UsingMethod("POST")
+ .WithPath("/form-urlencoded")
+ .WithHeader("Content-Type", "application/x-www-form-urlencoded", true)
+ .WithBody("name=John Doe")
+ )
+ .WithGuid("98fae52e-76df-47d9-876f-2ee32e931003")
+ .RespondWith(Response.Create()
+ );
+
+server
+ .Given(Request.Create()
+ .UsingMethod("POST")
+ .WithPath("/users/post1")
+ .WithBody(new JsonMatcher(
+ value: new
+ {
+ Request = "Hello?"
+ },
+ ignoreCase: false,
+ regex: false
+ ))
+ )
+ .WithGuid("98fae52e-76df-47d9-876f-2ee32e931001")
.RespondWith(Response.Create()
);
diff --git a/test/WireMock.Net.Tests/MappingBuilderTests.ToJson.verified.txt b/test/WireMock.Net.Tests/MappingBuilderTests.ToJson.verified.txt
index bf66f9df..20c08346 100644
--- a/test/WireMock.Net.Tests/MappingBuilderTests.ToJson.verified.txt
+++ b/test/WireMock.Net.Tests/MappingBuilderTests.ToJson.verified.txt
@@ -1,6 +1,6 @@
[
{
- Guid: Guid_1,
+ Guid: 41372914-1838-4c67-916b-b9aacdd096ce,
UpdatedAt: 2023-01-14T15:16:17,
Request: {
Path: {
@@ -33,7 +33,7 @@
}
},
{
- Guid: Guid_2,
+ Guid: 98fae52e-76df-47d9-876f-2ee32e931002,
UpdatedAt: 2023-01-14T15:16:17,
Request: {
Path: {
@@ -60,5 +60,75 @@
}
}
}
+ },
+ {
+ Guid: 98fae52e-76df-47d9-876f-2ee32e931003,
+ UpdatedAt: 2023-01-14T15:16:17,
+ Request: {
+ Path: {
+ Matchers: [
+ {
+ Name: WildcardMatcher,
+ Pattern: /form-urlencoded,
+ IgnoreCase: false
+ }
+ ]
+ },
+ Methods: [
+ POST
+ ],
+ Headers: [
+ {
+ Name: Content-Type,
+ Matchers: [
+ {
+ Name: WildcardMatcher,
+ Pattern: application/x-www-form-urlencoded,
+ IgnoreCase: true
+ }
+ ],
+ IgnoreCase: true
+ }
+ ],
+ Body: {
+ Matcher: {
+ Name: FormUrlEncodedMatcher,
+ Patterns: [
+ name=John Doe,
+ email=johndoe@example.com
+ ],
+ IgnoreCase: false,
+ MatchOperator: Or
+ }
+ }
+ }
+ },
+ {
+ Guid: 98fae52e-76df-47d9-876f-2ee32e931001,
+ UpdatedAt: 2023-01-14T15:16:17,
+ Request: {
+ Path: {
+ Matchers: [
+ {
+ Name: WildcardMatcher,
+ Pattern: /users/post1,
+ IgnoreCase: false
+ }
+ ]
+ },
+ Methods: [
+ POST
+ ],
+ Body: {
+ Matcher: {
+ Name: JsonMatcher,
+ Pattern: {
+ Request: Hello?
+ },
+ IgnoreCase: false,
+ Regex: false
+ }
+ }
+ }
}
]
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/MappingBuilderTests.cs b/test/WireMock.Net.Tests/MappingBuilderTests.cs
index 0b798c8e..6a62af2f 100644
--- a/test/WireMock.Net.Tests/MappingBuilderTests.cs
+++ b/test/WireMock.Net.Tests/MappingBuilderTests.cs
@@ -30,7 +30,6 @@ static MappingBuilderTests()
VerifySettings.Init();
}
- private static readonly Guid NewGuid = new("98fae52e-76df-47d9-876f-2ee32e931d9b");
private const string MappingGuid = "41372914-1838-4c67-916b-b9aacdd096ce";
private static readonly DateTime UtcNow = new(2023, 1, 14, 15, 16, 17);
@@ -43,7 +42,8 @@ public MappingBuilderTests()
_fileSystemHandlerMock = new Mock();
var guidUtilsMock = new Mock();
- guidUtilsMock.Setup(g => g.NewGuid()).Returns(NewGuid);
+ var startGuid = 1000;
+ guidUtilsMock.Setup(g => g.NewGuid()).Returns(() => new Guid($"98fae52e-76df-47d9-876f-2ee32e93{startGuid++}"));
var dateTimeUtilsMock = new Mock();
dateTimeUtilsMock.SetupGet(d => d.UtcNow).Returns(UtcNow);
@@ -95,6 +95,13 @@ public MappingBuilderTests()
country = "The Netherlands"
}))
).RespondWith(Response.Create());
+
+ _sut.Given(Request.Create()
+ .UsingPost()
+ .WithPath("/form-urlencoded")
+ .WithHeader("Content-Type", "application/x-www-form-urlencoded")
+ .WithBody(new FormUrlEncodedMatcher(["name=John Doe", "email=johndoe@example.com"]))
+ ).RespondWith(Response.Create());
}
[Fact]
@@ -104,7 +111,7 @@ public Task GetMappings()
var mappings = _sut.GetMappings();
// Verify
- return Verifier.Verify(mappings, VerifySettings);
+ return Verifier.Verify(mappings, VerifySettings).DontScrubGuids();
}
[Fact]
@@ -114,7 +121,7 @@ public Task ToJson()
var json = _sut.ToJson();
// Verify
- return Verifier.VerifyJson(json, VerifySettings);
+ return Verifier.VerifyJson(json, VerifySettings).DontScrubGuids();
}
[Fact]
@@ -124,7 +131,7 @@ public Task ToCSharpCode_Server()
var code = _sut.ToCSharpCode(MappingConverterType.Server);
// Verify
- return Verifier.Verify(code, VerifySettings);
+ return Verifier.Verify(code, VerifySettings).DontScrubGuids();
}
[Fact]
@@ -134,7 +141,7 @@ public Task ToCSharpCode_Builder()
var code = _sut.ToCSharpCode(MappingConverterType.Builder);
// Verify
- return Verifier.Verify(code, VerifySettings);
+ return Verifier.Verify(code, VerifySettings).DontScrubGuids();
}
[Fact]
@@ -183,9 +190,9 @@ public void SaveMappingsToFolder_FolderIsNull()
_sut.SaveMappingsToFolder(null);
// Verify
- _fileSystemHandlerMock.Verify(fs => fs.GetMappingFolder(), Times.Exactly(2));
- _fileSystemHandlerMock.Verify(fs => fs.FolderExists(mappingFolder), Times.Exactly(2));
- _fileSystemHandlerMock.Verify(fs => fs.WriteMappingFile(It.IsAny(), It.IsAny()), Times.Exactly(2));
+ _fileSystemHandlerMock.Verify(fs => fs.GetMappingFolder(), Times.Exactly(4));
+ _fileSystemHandlerMock.Verify(fs => fs.FolderExists(mappingFolder), Times.Exactly(4));
+ _fileSystemHandlerMock.Verify(fs => fs.WriteMappingFile(It.IsAny(), It.IsAny()), Times.Exactly(4));
_fileSystemHandlerMock.VerifyNoOtherCalls();
}
@@ -201,8 +208,8 @@ public void SaveMappingsToFolder_FolderExists_IsTrue()
// Verify
_fileSystemHandlerMock.Verify(fs => fs.GetMappingFolder(), Times.Never);
- _fileSystemHandlerMock.Verify(fs => fs.FolderExists(path), Times.Exactly(2));
- _fileSystemHandlerMock.Verify(fs => fs.WriteMappingFile(It.IsAny(), It.IsAny()), Times.Exactly(2));
+ _fileSystemHandlerMock.Verify(fs => fs.FolderExists(path), Times.Exactly(4));
+ _fileSystemHandlerMock.Verify(fs => fs.WriteMappingFile(It.IsAny(), It.IsAny()), Times.Exactly(4));
_fileSystemHandlerMock.VerifyNoOtherCalls();
}
}
diff --git a/test/WireMock.Net.Tests/Matchers/FormUrlEncodedMatcherTests.cs b/test/WireMock.Net.Tests/Matchers/FormUrlEncodedMatcherTests.cs
new file mode 100644
index 00000000..712b4ece
--- /dev/null
+++ b/test/WireMock.Net.Tests/Matchers/FormUrlEncodedMatcherTests.cs
@@ -0,0 +1,78 @@
+// Copyright © WireMock.Net
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Threading.Tasks;
+using AnyOfTypes;
+using FluentAssertions;
+using WireMock.Matchers;
+using WireMock.Models;
+using Xunit;
+
+namespace WireMock.Net.Tests.Matchers;
+
+public class FormUrlEncodedMatcherTest
+{
+ [Theory]
+ [InlineData("*=*")]
+ [InlineData("name=John Doe")]
+ [InlineData("name=*")]
+ [InlineData("*=John Doe")]
+ [InlineData("email=johndoe@example.com")]
+ [InlineData("email=*")]
+ [InlineData("*=johndoe@example.com")]
+ [InlineData("name=John Doe", "email=johndoe@example.com")]
+ [InlineData("name=John Doe", "email=*")]
+ [InlineData("name=*", "email=*")]
+ [InlineData("*=John Doe", "*=johndoe@example.com")]
+ public async Task FormUrlEncodedMatcher_IsMatch(params string[] patterns)
+ {
+ // Arrange
+ var content = new FormUrlEncodedContent(new[]
+ {
+ new KeyValuePair("name", "John Doe"),
+ new KeyValuePair("email", "johndoe@example.com")
+ });
+ var contentAsString = await content.ReadAsStringAsync();
+
+ var matcher = new FormUrlEncodedMatcher(patterns.Select(p => new AnyOf(p)).ToArray());
+
+ // Act
+ var score = matcher.IsMatch(contentAsString).IsPerfect();
+
+ // Assert
+ score.Should().BeTrue();
+ }
+
+ [Theory]
+ [InlineData(false, "name=John Doe")]
+ [InlineData(false, "name=*")]
+ [InlineData(false, "*=John Doe")]
+ [InlineData(false, "email=johndoe@example.com")]
+ [InlineData(false, "email=*")]
+ [InlineData(false, "*=johndoe@example.com")]
+ [InlineData(true, "name=John Doe", "email=johndoe@example.com")]
+ [InlineData(true, "name=John Doe", "email=*")]
+ [InlineData(true, "name=*", "email=*")]
+ [InlineData(true, "*=John Doe", "*=johndoe@example.com")]
+ [InlineData(true, "*=*")]
+ public async Task FormUrlEncodedMatcher_IsMatch_And(bool expected, params string[] patterns)
+ {
+ // Arrange
+ var content = new FormUrlEncodedContent(new[]
+ {
+ new KeyValuePair("name", "John Doe"),
+ new KeyValuePair("email", "johndoe@example.com")
+ });
+ var contentAsString = await content.ReadAsStringAsync();
+
+ var matcher = new FormUrlEncodedMatcher(patterns.Select(p => new AnyOf(p)).ToArray(), true, MatchOperator.And);
+
+ // Act
+ var score = matcher.IsMatch(contentAsString).IsPerfect();
+
+ // Assert
+ score.Should().Be(expected);
+ }
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/WireMockServerTests.WithBody.cs b/test/WireMock.Net.Tests/WireMockServerTests.WithBody.cs
index 525e7688..3bcfbaf6 100644
--- a/test/WireMock.Net.Tests/WireMockServerTests.WithBody.cs
+++ b/test/WireMock.Net.Tests/WireMockServerTests.WithBody.cs
@@ -225,5 +225,63 @@ public async Task WireMockServer_WithBodyAsFormUrlEncoded_Using_PostAsync_And_Wi
server.Stop();
}
+
+ [Fact]
+ public async Task WireMockServer_WithBodyAsFormUrlEncoded_Using_PostAsync_And_WithFormUrlEncodedMatcher()
+ {
+ // Arrange
+ var matcher = new FormUrlEncodedMatcher(["email=johndoe@example.com", "name=John Doe"]);
+ var server = WireMockServer.Start();
+ server.Given(
+ Request.Create()
+ .UsingPost()
+ .WithPath("/foo")
+ .WithHeader("Content-Type", "application/x-www-form-urlencoded")
+ .WithBody(matcher)
+ )
+ .RespondWith(
+ Response.Create()
+ );
+
+ server.Given(
+ Request.Create()
+ .UsingPost()
+ .WithPath("/bar")
+ .WithHeader("Content-Type", "application/x-www-form-urlencoded")
+ .WithBody(matcher)
+ )
+ .RespondWith(
+ Response.Create()
+ );
+
+ // Act 1
+ var contentOrdered = new FormUrlEncodedContent(new[]
+ {
+ new KeyValuePair("name", "John Doe"),
+ new KeyValuePair("email", "johndoe@example.com")
+ });
+ var responseOrdered = await new HttpClient()
+ .PostAsync($"{server.Url}/foo", contentOrdered)
+ .ConfigureAwait(false);
+
+ // Assert 1
+ responseOrdered.StatusCode.Should().Be(HttpStatusCode.OK);
+
+
+ // Act 2
+ var contentUnordered = new FormUrlEncodedContent(new[]
+ {
+ new KeyValuePair("email", "johndoe@example.com"),
+ new KeyValuePair("name", "John Doe"),
+ });
+ var responseUnordered = await new HttpClient()
+ .PostAsync($"{server.Url}/bar", contentUnordered)
+ .ConfigureAwait(false);
+
+ // Assert 2
+ responseUnordered.StatusCode.Should().Be(HttpStatusCode.OK);
+
+ server.Stop();
+ }
}
#endif
\ No newline at end of file