Add WithBody with IDictionary (form-urlencoded values) (#903)

* .

* x

* fx

* fix

* f

* tests

* fix tests

* add tst
This commit is contained in:
Stef Heyenrath
2023-03-17 17:08:45 +01:00
committed by GitHub
parent 19701f5260
commit 78b94d2ebc
13 changed files with 286 additions and 106 deletions

View File

@@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Text; using System.Text;
using WireMock.Types; using WireMock.Types;
@@ -38,6 +39,11 @@ public interface IBodyData
/// </summary> /// </summary>
string? BodyAsString { get; set; } string? BodyAsString { get; set; }
/// <summary>
/// The body as Form UrlEncoded dictionary.
/// </summary>
IDictionary<string, string>? BodyAsFormUrlEncoded { get; set; }
/// <summary> /// <summary>
/// The detected body type (detection based on body content). /// The detected body type (detection based on body content).
/// </summary> /// </summary>

View File

@@ -1,38 +1,42 @@
namespace WireMock.Types namespace WireMock.Types;
/// <summary>
/// The BodyType
/// </summary>
public enum BodyType
{ {
/// <summary> /// <summary>
/// The BodyType /// No body present
/// </summary> /// </summary>
public enum BodyType None,
{
/// <summary>
/// No body present
/// </summary>
None,
/// <summary> /// <summary>
/// Body is a String /// Body is a String
/// </summary> /// </summary>
String, String,
/// <summary> /// <summary>
/// Body is a Json object /// Body is a Json object
/// </summary> /// </summary>
Json, Json,
/// <summary> /// <summary>
/// Body is a Byte array /// Body is a Byte array
/// </summary> /// </summary>
Bytes, Bytes,
/// <summary> /// <summary>
/// Body is a File /// Body is a File
/// </summary> /// </summary>
File, File,
/// <summary> /// <summary>
/// Body is a MultiPart /// Body is a MultiPart
/// </summary> /// </summary>
MultiPart MultiPart,
}
/// <summary>
/// Body is a String which is x-www-form-urlencoded.
/// </summary>
FormUrlEncoded
} }

View File

@@ -1,8 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using AnyOfTypes;
using Stef.Validation; using Stef.Validation;
using WireMock.Models;
using WireMock.Types; using WireMock.Types;
using WireMock.Util; using WireMock.Util;
@@ -33,6 +32,11 @@ public class RequestMessageBodyMatcher : IRequestMatcher
/// </summary> /// </summary>
public Func<IBodyData?, bool>? BodyDataFunc { get; } public Func<IBodyData?, bool>? BodyDataFunc { get; }
/// <summary>
/// The body data function for FormUrlEncoded
/// </summary>
public Func<IDictionary<string, string>?, bool>? FormUrlEncodedFunc { get; }
/// <summary> /// <summary>
/// The matchers. /// The matchers.
/// </summary> /// </summary>
@@ -109,6 +113,15 @@ public class RequestMessageBodyMatcher : IRequestMatcher
BodyDataFunc = Guard.NotNull(func); BodyDataFunc = Guard.NotNull(func);
} }
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary>
/// <param name="func">The function.</param>
public RequestMessageBodyMatcher(Func<IDictionary<string, string>?, bool> func)
{
FormUrlEncodedFunc = Guard.NotNull(func);
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
@@ -184,7 +197,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
if (matcher is IStringMatcher stringMatcher) if (matcher is IStringMatcher stringMatcher)
{ {
// If the body is a Json or a String, use the BodyAsString to match on. // If the body is a Json or a String, use the BodyAsString to match on.
if (requestMessage?.BodyData?.DetectedBodyType == BodyType.Json || requestMessage?.BodyData?.DetectedBodyType == BodyType.String) if (requestMessage?.BodyData?.DetectedBodyType is BodyType.Json or BodyType.String)
{ {
return stringMatcher.IsMatch(requestMessage.BodyData.BodyAsString); return stringMatcher.IsMatch(requestMessage.BodyData.BodyAsString);
} }
@@ -206,6 +219,11 @@ public class RequestMessageBodyMatcher : IRequestMatcher
return MatchScores.ToScore(Func(requestMessage.BodyData?.BodyAsString)); return MatchScores.ToScore(Func(requestMessage.BodyData?.BodyAsString));
} }
if (FormUrlEncodedFunc != null)
{
return MatchScores.ToScore(FormUrlEncodedFunc(requestMessage.BodyData?.BodyAsFormUrlEncoded));
}
if (JsonFunc != null) if (JsonFunc != null)
{ {
return MatchScores.ToScore(JsonFunc(requestMessage.BodyData?.BodyAsJson)); return MatchScores.ToScore(JsonFunc(requestMessage.BodyData?.BodyAsJson));

View File

@@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Text; using System.Text;
using WireMock.Types; using WireMock.Types;
@@ -14,6 +15,9 @@ public class BodyData : IBodyData
/// <inheritdoc /> /// <inheritdoc />
public string? BodyAsString { get; set; } public string? BodyAsString { get; set; }
/// <inheritdoc />
public IDictionary<string, string>? BodyAsFormUrlEncoded { get; set; }
/// <inheritdoc cref="IBodyData.BodyAsJson" /> /// <inheritdoc cref="IBodyData.BodyAsJson" />
public object? BodyAsJson { get; set; } public object? BodyAsJson { get; set; }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using WireMock.Util; using WireMock.Util;
@@ -54,26 +55,33 @@ public interface IBodyRequestBuilder : IRequestMatcher
/// </summary> /// </summary>
/// <param name="func">The function.</param> /// <param name="func">The function.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithBody(Func<string, bool> func); IRequestBuilder WithBody(Func<string?, bool> func);
/// <summary> /// <summary>
/// WithBody: func (byte[]) /// WithBody: func (byte[])
/// </summary> /// </summary>
/// <param name="func">The function.</param> /// <param name="func">The function.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithBody(Func<byte[], bool> func); IRequestBuilder WithBody(Func<byte[]?, bool> func);
/// <summary> /// <summary>
/// WithBody: func (json object) /// WithBody: func (json object)
/// </summary> /// </summary>
/// <param name="func">The function.</param> /// <param name="func">The function.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithBody(Func<object, bool> func); IRequestBuilder WithBody(Func<object?, bool> func);
/// <summary> /// <summary>
/// WithBody: func (BodyData object) /// WithBody: func (BodyData object)
/// </summary> /// </summary>
/// <param name="func">The function.</param> /// <param name="func">The function.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithBody(Func<IBodyData, bool> func); IRequestBuilder WithBody(Func<IBodyData?, bool> func);
/// <summary>
/// WithBody: Body as form-urlencoded values.
/// </summary>
/// <param name="func">The form-urlencoded values.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithBody(Func<IDictionary<string, string>?, bool> func);
} }

View File

@@ -1,6 +1,7 @@
// This source file is based on mock4net by Alexandre Victoor which is licensed under the Apache 2.0 License. // This source file is based on mock4net by Alexandre Victoor which is licensed under the Apache 2.0 License.
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root. // For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
using System; using System;
using System.Collections.Generic;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using WireMock.Util; using WireMock.Util;
@@ -10,21 +11,21 @@ namespace WireMock.RequestBuilders;
public partial class Request public partial class Request
{ {
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(string, MatchBehaviour)"/> /// <inheritdoc />
public IRequestBuilder WithBody(string body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) public IRequestBuilder WithBody(string body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{ {
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body)); _requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body));
return this; return this;
} }
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(byte[], MatchBehaviour)"/> /// <inheritdoc />
public IRequestBuilder WithBody(byte[] body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) public IRequestBuilder WithBody(byte[] body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{ {
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body)); _requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body));
return this; return this;
} }
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(object, MatchBehaviour)"/> /// <inheritdoc />
public IRequestBuilder WithBody(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) public IRequestBuilder WithBody(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{ {
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body)); _requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body));
@@ -46,39 +47,46 @@ public partial class Request
return this; return this;
} }
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{string, bool})"/> /// <inheritdoc />
public IRequestBuilder WithBody(Func<string, bool> func) public IRequestBuilder WithBody(Func<string?, bool> func)
{ {
Guard.NotNull(func, nameof(func)); Guard.NotNull(func);
_requestMatchers.Add(new RequestMessageBodyMatcher(func)); _requestMatchers.Add(new RequestMessageBodyMatcher(func));
return this; return this;
} }
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{byte[], bool})"/> /// <inheritdoc />
public IRequestBuilder WithBody(Func<byte[], bool> func) public IRequestBuilder WithBody(Func<byte[]?, bool> func)
{ {
Guard.NotNull(func, nameof(func)); Guard.NotNull(func);
_requestMatchers.Add(new RequestMessageBodyMatcher(func)); _requestMatchers.Add(new RequestMessageBodyMatcher(func));
return this; return this;
} }
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{object, bool})"/> /// <inheritdoc />
public IRequestBuilder WithBody(Func<object, bool> func) public IRequestBuilder WithBody(Func<object?, bool> func)
{ {
Guard.NotNull(func, nameof(func)); Guard.NotNull(func);
_requestMatchers.Add(new RequestMessageBodyMatcher(func)); _requestMatchers.Add(new RequestMessageBodyMatcher(func));
return this; return this;
} }
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{IBodyData, bool})"/> /// <inheritdoc />
public IRequestBuilder WithBody(Func<IBodyData, bool> func) public IRequestBuilder WithBody(Func<IBodyData?, bool> func)
{ {
Guard.NotNull(func, nameof(func)); Guard.NotNull(func);
_requestMatchers.Add(new RequestMessageBodyMatcher(func)); _requestMatchers.Add(new RequestMessageBodyMatcher(func));
return this; return this;
} }
/// <inheritdoc />
public IRequestBuilder WithBody(Func<IDictionary<string, string>?, bool> func)
{
_requestMatchers.Add(new RequestMessageBodyMatcher(Guard.NotNull(func)));
return this;
}
} }

View File

@@ -49,12 +49,14 @@ internal static class BodyParser
new WildcardMatcher("application/vnd.*+json", true) new WildcardMatcher("application/vnd.*+json", true)
}; };
private static readonly IStringMatcher FormUrlEncodedMatcher = new WildcardMatcher("application/x-www-form-urlencoded", true);
private static readonly IStringMatcher[] TextContentTypeMatchers = private static readonly IStringMatcher[] TextContentTypeMatchers =
{ {
new WildcardMatcher("text/*", true), new WildcardMatcher("text/*", true),
new RegexMatcher("^application\\/(java|type)script$", true), new RegexMatcher("^application\\/(java|type)script$", true),
new WildcardMatcher("application/*xml", true), new WildcardMatcher("application/*xml", true),
new WildcardMatcher("application/x-www-form-urlencoded", true) FormUrlEncodedMatcher
}; };
public static bool ShouldParseBody(string? httpMethod, bool allowBodyForAllHttpMethods) public static bool ShouldParseBody(string? httpMethod, bool allowBodyForAllHttpMethods)
@@ -69,7 +71,7 @@ internal static class BodyParser
return true; return true;
} }
if (BodyAllowedForMethods.TryGetValue(httpMethod!.ToUpper(), out bool allowed)) if (BodyAllowedForMethods.TryGetValue(httpMethod!.ToUpper(), out var allowed))
{ {
return allowed; return allowed;
} }
@@ -88,6 +90,11 @@ internal static class BodyParser
return BodyType.Bytes; return BodyType.Bytes;
} }
if (MatchScores.IsPerfect(FormUrlEncodedMatcher.IsMatch(contentType.MediaType)))
{
return BodyType.FormUrlEncoded;
}
if (TextContentTypeMatchers.Any(matcher => MatchScores.IsPerfect(matcher.IsMatch(contentType.MediaType)))) if (TextContentTypeMatchers.Any(matcher => MatchScores.IsPerfect(matcher.IsMatch(contentType.MediaType))))
{ {
return BodyType.String; return BodyType.String;
@@ -133,13 +140,30 @@ internal static class BodyParser
return data; return data;
} }
// Try to get the body as String or Json // Try to get the body as String, FormUrlEncoded or Json
try try
{ {
data.BodyAsString = DefaultEncoding.GetString(data.BodyAsBytes); data.BodyAsString = DefaultEncoding.GetString(data.BodyAsBytes);
data.Encoding = DefaultEncoding; data.Encoding = DefaultEncoding;
data.DetectedBodyType = BodyType.String; data.DetectedBodyType = BodyType.String;
// If string is not null or empty, try to deserialize the string to a IDictionary<string, string>
if (settings.DeserializeFormUrlEncoded &&
data.DetectedBodyTypeFromContentType == BodyType.FormUrlEncoded &&
QueryStringParser.TryParse(data.BodyAsString, false, out var nameValueCollection)
)
{
try
{
data.BodyAsFormUrlEncoded = nameValueCollection;
data.DetectedBodyType = BodyType.FormUrlEncoded;
}
catch
{
// Deserialize FormUrlEncoded failed, just ignore.
}
}
// If string is not null or empty, try to deserialize the string to a JObject // If string is not null or empty, try to deserialize the string to a JObject
if (settings.DeserializeJson && !string.IsNullOrEmpty(data.BodyAsString)) if (settings.DeserializeJson && !string.IsNullOrEmpty(data.BodyAsString))
{ {

View File

@@ -13,4 +13,6 @@ internal class BodyParserSettings
public bool DecompressGZipAndDeflate { get; set; } = true; public bool DecompressGZipAndDeflate { get; set; } = true;
public bool DeserializeJson { get; set; } = true; public bool DeserializeJson { get; set; } = true;
public bool DeserializeFormUrlEncoded { get; set; } = true;
} }

View File

@@ -1,7 +1,8 @@
using System; using System;
using System.Net;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Net;
using WireMock.Types; using WireMock.Types;
namespace WireMock.Util; namespace WireMock.Util;
@@ -13,6 +14,31 @@ internal static class QueryStringParser
{ {
private static readonly Dictionary<string, WireMockList<string>> Empty = new(); private static readonly Dictionary<string, WireMockList<string>> Empty = new();
public static bool TryParse(string? queryString, bool caseIgnore, [NotNullWhen(true)] out IDictionary<string, string>? nameValueCollection)
{
if (queryString is null)
{
nameValueCollection = default;
return false;
}
var parts = queryString!
.Split(new[] { "&" }, StringSplitOptions.RemoveEmptyEntries)
.Select(parameter => parameter.Split('='))
.Distinct();
nameValueCollection = caseIgnore ? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) : new Dictionary<string, string>();
foreach (var part in parts)
{
if (part.Length == 2)
{
nameValueCollection.Add(part[0], part[1]);
}
}
return true;
}
public static IDictionary<string, WireMockList<string>> Parse(string? queryString, QueryParameterMultipleValueSupport? support = null) public static IDictionary<string, WireMockList<string>> Parse(string? queryString, QueryParameterMultipleValueSupport? support = null)
{ {
if (string.IsNullOrEmpty(queryString)) if (string.IsNullOrEmpty(queryString))

View File

@@ -431,7 +431,7 @@ public class RequestMessageBodyMatcherTests
// JSON match +++ // JSON match +++
{json, new RequestMessageBodyMatcher((object? o) => ((dynamic) o!).a == "b"), true}, {json, new RequestMessageBodyMatcher((object? o) => ((dynamic) o!).a == "b"), true},
{json, new RequestMessageBodyMatcher((string? s) => s == json), true}, {json, new RequestMessageBodyMatcher((string? s) => s == json), true},
{json, new RequestMessageBodyMatcher((byte[]? b) => b.SequenceEqual(Encoding.UTF8.GetBytes(json))), true}, {json, new RequestMessageBodyMatcher((byte[]? b) => b?.SequenceEqual(Encoding.UTF8.GetBytes(json)) == true), true},
// JSON no match --- // JSON no match ---
{json, new RequestMessageBodyMatcher((object? o) => false), false}, {json, new RequestMessageBodyMatcher((object? o) => false), false},
@@ -442,7 +442,7 @@ public class RequestMessageBodyMatcherTests
// string match +++ // string match +++
{str, new RequestMessageBodyMatcher((object? o) => o == null), true}, {str, new RequestMessageBodyMatcher((object? o) => o == null), true},
{str, new RequestMessageBodyMatcher((string? s) => s == str), true}, {str, new RequestMessageBodyMatcher((string? s) => s == str), true},
{str, new RequestMessageBodyMatcher((byte[]? b) => b.SequenceEqual(Encoding.UTF8.GetBytes(str))), true}, {str, new RequestMessageBodyMatcher((byte[]? b) => b?.SequenceEqual(Encoding.UTF8.GetBytes(str)) == true), true},
// string no match --- // string no match ---
{str, new RequestMessageBodyMatcher((object? o) => false), false}, {str, new RequestMessageBodyMatcher((object? o) => false), false},
@@ -453,13 +453,13 @@ public class RequestMessageBodyMatcherTests
// binary match +++ // binary match +++
{bytes, new RequestMessageBodyMatcher((object? o) => o == null), true}, {bytes, new RequestMessageBodyMatcher((object? o) => o == null), true},
{bytes, new RequestMessageBodyMatcher((string? s) => s == null), true}, {bytes, new RequestMessageBodyMatcher((string? s) => s == null), true},
{bytes, new RequestMessageBodyMatcher((byte[]? b) => b.SequenceEqual(bytes)), true}, {bytes, new RequestMessageBodyMatcher((byte[]? b) => b?.SequenceEqual(bytes) == true), true},
// binary no match --- // binary no match ---
{bytes, new RequestMessageBodyMatcher((object? o) => false), false}, {bytes, new RequestMessageBodyMatcher((object? o) => false), false},
{bytes, new RequestMessageBodyMatcher((string? s) => false), false}, {bytes, new RequestMessageBodyMatcher((string? s) => false), false},
{bytes, new RequestMessageBodyMatcher((byte[]? b) => false), false}, {bytes, new RequestMessageBodyMatcher((byte[]? b) => false), false},
{bytes, new RequestMessageBodyMatcher(), false }, {bytes, new RequestMessageBodyMatcher(), false }
}; };
} }
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Text; using System.Text;
using FluentAssertions; using FluentAssertions;
using Newtonsoft.Json; using Newtonsoft.Json;
@@ -55,11 +56,31 @@ namespace WireMock.Net.Tests
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
[Fact]
public void Request_WithBody_FuncFormUrlEncoded()
{
// Assign
var requestBuilder = Request.Create().UsingAnyMethod().WithBody((IDictionary<string, string>? values) => values != null);
// Act
var body = new BodyData
{
BodyAsFormUrlEncoded = new Dictionary<string, string>(),
DetectedBodyTypeFromContentType = BodyType.FormUrlEncoded,
DetectedBodyType = BodyType.FormUrlEncoded
};
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body);
// Assert
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact] [Fact]
public void Request_WithBody_FuncBodyData() public void Request_WithBody_FuncBodyData()
{ {
// Assign // Assign
var requestBuilder = Request.Create().UsingAnyMethod().WithBody((IBodyData b) => b != null); var requestBuilder = Request.Create().UsingAnyMethod().WithBody((IBodyData? b) => b != null);
// Act // Act
var body = new BodyData var body = new BodyData
@@ -78,7 +99,7 @@ namespace WireMock.Net.Tests
public void Request_WithBody_FuncByteArray() public void Request_WithBody_FuncByteArray()
{ {
// Assign // Assign
var requestBuilder = Request.Create().UsingAnyMethod().WithBody((byte[] b) => b != null); var requestBuilder = Request.Create().UsingAnyMethod().WithBody((byte[]? b) => b != null);
// Act // Act
var body = new BodyData var body = new BodyData

View File

@@ -1,5 +1,5 @@
using FluentAssertions;
using System.Collections.Generic; using System.Collections.Generic;
using FluentAssertions;
using WireMock.Types; using WireMock.Types;
using WireMock.Util; using WireMock.Util;
using Xunit; using Xunit;
@@ -8,6 +8,36 @@ namespace WireMock.Net.Tests.Util;
public class QueryStringParserTests public class QueryStringParserTests
{ {
public static IEnumerable<object?[]> QueryStringTestData => new List<object?[]>
{
new object?[] { null, false, false, null },
new object?[] { string.Empty, false, true, new Dictionary<string, string>() },
new object?[] { "test", false, true, new Dictionary<string, string>() },
new object?[] { "&", false, true, new Dictionary<string, string>() },
new object?[] { "&&", false, true, new Dictionary<string, string>() },
new object?[] { "a=", false, true, new Dictionary<string, string> { { "a", "" } } },
new object?[] { "&a", false, true, new Dictionary<string, string>() },
new object?[] { "&a=", false, true, new Dictionary<string, string> { { "a", "" } } },
new object?[] { "&key1=value1", false, true, new Dictionary<string, string> { { "key1", "value1" } } },
new object?[] { "key1=value1", false, true, new Dictionary<string, string> { { "key1", "value1" } } },
new object?[] { "key1=value1&key2=value2", false, true, new Dictionary<string, string> { { "key1", "value1" }, { "key2", "value2" } } },
new object?[] { "key1=value1&key2=value2&", false, true, new Dictionary<string, string> { { "key1", "value1" }, { "key2", "value2" } } },
new object?[] { "key1=value1&&key2=value2", false, true, new Dictionary<string, string> { { "key1", "value1" }, { "key2", "value2" } } },
new object?[] { "&key1=value1&key2=value2&&", false, true, new Dictionary<string, string> { { "key1", "value1" }, { "key2", "value2" } } },
};
[Theory]
[MemberData(nameof(QueryStringTestData))]
public void TryParse_Should_Parse_QueryString(string queryString, bool caseIgnore, bool expectedResult, IDictionary<string, string> expectedOutput)
{
// Act
var result = QueryStringParser.TryParse(queryString, caseIgnore, out var actual);
// Assert
result.Should().Be(expectedResult);
actual.Should().BeEquivalentTo(expectedOutput);
}
[Fact] [Fact]
public void Parse_WithNullString() public void Parse_WithNullString()
{ {

View File

@@ -1,7 +1,9 @@
#if !NET452 #if !NET452
//using System;
using System.Collections.Generic;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Json; //using System.Net.Http.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using WireMock.Matchers; using WireMock.Matchers;
@@ -10,61 +12,88 @@ using WireMock.ResponseBuilders;
using WireMock.Server; using WireMock.Server;
using Xunit; using Xunit;
namespace WireMock.Net.Tests namespace WireMock.Net.Tests;
public partial class WireMockServerTests
{ {
public partial class WireMockServerTests public class DummyClass
{ {
public class DummyClass public string? Hi { get; set; }
}
[Fact]
public async Task WireMockServer_WithBodyAsJson_Using_PostAsJsonAsync_And_WildcardMatcher_ShouldMatch()
{
// Arrange
var server = WireMockServer.Start();
server.Given(
Request.Create().UsingPost().WithPath("/foo").WithBody(new WildcardMatcher("*Hello*"))
)
.RespondWith(
Response.Create().WithStatusCode(200)
);
var jsonObject = new DummyClass
{ {
public string Hi { get; set; } Hi = "Hello World!"
} };
[Fact] // Act
public async Task WireMockServer_WithBodyAsJson_Using_PostAsJsonAsync_And_WildcardMatcher_ShouldMatch() var response = await new HttpClient().PostAsJsonAsync("http://localhost:" + server.Ports[0] + "/foo", jsonObject).ConfigureAwait(false);
{
// Arrange
var server = WireMockServer.Start();
server.Given(
Request.Create().UsingPost().WithPath("/foo").WithBody(new WildcardMatcher("*Hello*"))
)
.RespondWith(
Response.Create().WithStatusCode(200)
);
var jsonObject = new DummyClass // Assert
{ response.StatusCode.Should().Be(HttpStatusCode.OK);
Hi = "Hello World!"
};
// Act server.Stop();
var response = await new HttpClient().PostAsJsonAsync("http://localhost:" + server.Ports[0] + "/foo", jsonObject).ConfigureAwait(false); }
// Assert [Fact]
response.StatusCode.Should().Be(HttpStatusCode.OK); public async Task WireMockServer_WithBodyAsJson_Using_PostAsync_And_WildcardMatcher_ShouldMatch()
{
// Arrange
var server = WireMockServer.Start();
server.Given(
Request.Create().UsingPost().WithPath("/foo").WithBody(new WildcardMatcher("*Hello*"))
)
.RespondWith(
Response.Create().WithStatusCode(200)
);
server.Stop(); // Act
} var response = await new HttpClient().PostAsync("http://localhost:" + server.Ports[0] + "/foo", new StringContent("{ Hi = \"Hello World\" }")).ConfigureAwait(false);
[Fact] // Assert
public async Task WireMockServer_WithBodyAsJson_Using_PostAsync_And_WildcardMatcher_ShouldMatch() response.StatusCode.Should().Be(HttpStatusCode.OK);
{
// Arrange
var server = WireMockServer.Start();
server.Given(
Request.Create().UsingPost().WithPath("/foo").WithBody(new WildcardMatcher("*Hello*"))
)
.RespondWith(
Response.Create().WithStatusCode(200)
);
// Act server.Stop();
var response = await new HttpClient().PostAsync("http://localhost:" + server.Ports[0] + "/foo", new StringContent("{ Hi = \"Hello World\" }")).ConfigureAwait(false); }
// Assert [Fact]
response.StatusCode.Should().Be(HttpStatusCode.OK); public async Task WireMockServer_WithBodyAsFormUrlEncoded_Using_PostAsync_And_WithFunc()
{
// Arrange
var server = WireMockServer.Start();
server.Given(
Request.Create()
.UsingPost()
.WithPath("/foo")
.WithBody(values => values != null && values["key1"] == "value1")
)
.RespondWith(
Response.Create()
);
server.Stop(); // Act
} var content = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("key1", "value1") });
var response = await new HttpClient()
.PostAsync($"{server.Url}/foo", content)
.ConfigureAwait(false);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
server.Stop();
} }
} }
#endif #endif