mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-01-19 00:27:04 +01:00
Add FormUrlEncodedMatcher (#1147)
* FormUrlEncodedMatcher * . * Fix * new * support wildcard
This commit is contained in:
153
src/WireMock.Net/Matchers/FormUrlEncodedMatcher.cs
Normal file
153
src/WireMock.Net/Matchers/FormUrlEncodedMatcher.cs
Normal file
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// FormUrl Encoded fields Matcher
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IStringMatcher"/>
|
||||
/// <inheritdoc cref="IIgnoreCaseMatcher"/>
|
||||
public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
|
||||
{
|
||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
private readonly List<(WildcardMatcher Key, WildcardMatcher? Value)> _pairs = [];
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
public FormUrlEncodedMatcher(
|
||||
AnyOf<string, StringPattern> pattern,
|
||||
bool ignoreCase = false,
|
||||
MatchOperator matchOperator = MatchOperator.Or) :
|
||||
this(MatchBehaviour.AcceptOnMatch, [pattern], ignoreCase, matchOperator)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
public FormUrlEncodedMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
AnyOf<string, StringPattern> pattern,
|
||||
bool ignoreCase = false,
|
||||
MatchOperator matchOperator = MatchOperator.Or) :
|
||||
this(matchBehaviour, [pattern], ignoreCase, matchOperator)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
public FormUrlEncodedMatcher(
|
||||
AnyOf<string, StringPattern>[] patterns,
|
||||
bool ignoreCase = false,
|
||||
MatchOperator matchOperator = MatchOperator.Or) :
|
||||
this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase, matchOperator)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
public FormUrlEncodedMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
AnyOf<string, StringPattern>[] 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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<bool>();
|
||||
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));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string Name => nameof(FormUrlEncodedMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IgnoreCase { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchOperator MatchOperator { get; }
|
||||
}
|
||||
@@ -147,19 +147,22 @@ internal class MappingConverter
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -111,6 +111,9 @@ internal class MatcherMapper
|
||||
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 @@ internal class MatcherMapper
|
||||
{
|
||||
if (matcher.Pattern is string patternAsString)
|
||||
{
|
||||
return new[] { new AnyOf<string, StringPattern>(patternAsString) };
|
||||
return [new AnyOf<string, StringPattern>(patternAsString)];
|
||||
}
|
||||
|
||||
if (matcher.Pattern is IEnumerable<string> patternAsStringArray)
|
||||
@@ -241,7 +244,7 @@ internal class MatcherMapper
|
||||
{
|
||||
var patternAsFile = matcher.PatternAsFile!;
|
||||
var pattern = _settings.FileSystemHandler.ReadFileAsString(patternAsFile);
|
||||
return new[] { new AnyOf<string, StringPattern>(new StringPattern { Pattern = pattern, PatternAsFile = patternAsFile }) };
|
||||
return [new AnyOf<string, StringPattern>(new StringPattern { Pattern = pattern, PatternAsFile = patternAsFile })];
|
||||
}
|
||||
|
||||
return EmptyArray<AnyOf<string, StringPattern>>.Value;
|
||||
|
||||
@@ -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: {}
|
||||
}
|
||||
]
|
||||
@@ -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()
|
||||
);
|
||||
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -30,7 +30,6 @@ public class 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 class MappingBuilderTests
|
||||
_fileSystemHandlerMock = new Mock<IFileSystemHandler>();
|
||||
|
||||
var guidUtilsMock = new Mock<IGuidUtils>();
|
||||
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<IDateTimeUtils>();
|
||||
dateTimeUtilsMock.SetupGet(d => d.UtcNow).Returns(UtcNow);
|
||||
@@ -95,6 +95,13 @@ public class 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 class MappingBuilderTests
|
||||
var mappings = _sut.GetMappings();
|
||||
|
||||
// Verify
|
||||
return Verifier.Verify(mappings, VerifySettings);
|
||||
return Verifier.Verify(mappings, VerifySettings).DontScrubGuids();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -114,7 +121,7 @@ public class MappingBuilderTests
|
||||
var json = _sut.ToJson();
|
||||
|
||||
// Verify
|
||||
return Verifier.VerifyJson(json, VerifySettings);
|
||||
return Verifier.VerifyJson(json, VerifySettings).DontScrubGuids();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -124,7 +131,7 @@ public class MappingBuilderTests
|
||||
var code = _sut.ToCSharpCode(MappingConverterType.Server);
|
||||
|
||||
// Verify
|
||||
return Verifier.Verify(code, VerifySettings);
|
||||
return Verifier.Verify(code, VerifySettings).DontScrubGuids();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -134,7 +141,7 @@ public class MappingBuilderTests
|
||||
var code = _sut.ToCSharpCode(MappingConverterType.Builder);
|
||||
|
||||
// Verify
|
||||
return Verifier.Verify(code, VerifySettings);
|
||||
return Verifier.Verify(code, VerifySettings).DontScrubGuids();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -183,9 +190,9 @@ public class MappingBuilderTests
|
||||
_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<string>(), It.IsAny<string>()), 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<string>(), It.IsAny<string>()), Times.Exactly(4));
|
||||
_fileSystemHandlerMock.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
@@ -201,8 +208,8 @@ public class MappingBuilderTests
|
||||
|
||||
// Verify
|
||||
_fileSystemHandlerMock.Verify(fs => fs.GetMappingFolder(), Times.Never);
|
||||
_fileSystemHandlerMock.Verify(fs => fs.FolderExists(path), Times.Exactly(2));
|
||||
_fileSystemHandlerMock.Verify(fs => fs.WriteMappingFile(It.IsAny<string>(), It.IsAny<string>()), Times.Exactly(2));
|
||||
_fileSystemHandlerMock.Verify(fs => fs.FolderExists(path), Times.Exactly(4));
|
||||
_fileSystemHandlerMock.Verify(fs => fs.WriteMappingFile(It.IsAny<string>(), It.IsAny<string>()), Times.Exactly(4));
|
||||
_fileSystemHandlerMock.VerifyNoOtherCalls();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<string, string>("name", "John Doe"),
|
||||
new KeyValuePair<string, string>("email", "johndoe@example.com")
|
||||
});
|
||||
var contentAsString = await content.ReadAsStringAsync();
|
||||
|
||||
var matcher = new FormUrlEncodedMatcher(patterns.Select(p => new AnyOf<string, StringPattern>(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<string, string>("name", "John Doe"),
|
||||
new KeyValuePair<string, string>("email", "johndoe@example.com")
|
||||
});
|
||||
var contentAsString = await content.ReadAsStringAsync();
|
||||
|
||||
var matcher = new FormUrlEncodedMatcher(patterns.Select(p => new AnyOf<string, StringPattern>(p)).ToArray(), true, MatchOperator.And);
|
||||
|
||||
// Act
|
||||
var score = matcher.IsMatch(contentAsString).IsPerfect();
|
||||
|
||||
// Assert
|
||||
score.Should().Be(expected);
|
||||
}
|
||||
}
|
||||
@@ -225,5 +225,63 @@ public partial class WireMockServerTests
|
||||
|
||||
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<string, string>("name", "John Doe"),
|
||||
new KeyValuePair<string, string>("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<string, string>("email", "johndoe@example.com"),
|
||||
new KeyValuePair<string, string>("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
|
||||
Reference in New Issue
Block a user