mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-02-11 04:47:43 +01:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9dea577da1 | ||
|
|
7ca4294de6 | ||
|
|
66245409f9 | ||
|
|
da6cb9fe0a | ||
|
|
b30e4faab6 | ||
|
|
1221d52c69 | ||
|
|
52d2109c7e | ||
|
|
30064b922b | ||
|
|
78b94d2ebc | ||
|
|
19701f5260 | ||
|
|
1269fb178f | ||
|
|
7426bf76ee | ||
|
|
36c9d95abb | ||
|
|
674fa89c3e | ||
|
|
61cdc13fae | ||
|
|
c344b73f45 |
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,3 +1,24 @@
|
||||
# 1.5.21 (22 March 2023)
|
||||
- [#908](https://github.com/WireMock-Net/WireMock.Net/pull/908) - RequestBuilder : add WithBodyAsJson and WithBody (with IJsonConverter) [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#911](https://github.com/WireMock-Net/WireMock.Net/pull/911) - Fixed QueryStringParser for UrlEncoded values [bug] contributed by [StefH](https://github.com/StefH)
|
||||
- [#901](https://github.com/WireMock-Net/WireMock.Net/issues/901) - Matching one form-urlencoded value [feature]
|
||||
|
||||
# 1.5.20 (19 March 2023)
|
||||
- [#905](https://github.com/WireMock-Net/WireMock.Net/pull/905) - Add DeserializeFormUrl Encoded to the settings [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#907](https://github.com/WireMock-Net/WireMock.Net/pull/907) - Fix issue with application/x-www-form-urlencoded and ExactMatcher [bug] contributed by [StefH](https://github.com/StefH)
|
||||
- [#906](https://github.com/WireMock-Net/WireMock.Net/issues/906) - Upgrade to 1.5.19 breaks a form data test [bug]
|
||||
|
||||
# 1.5.19 (17 March 2023)
|
||||
- [#903](https://github.com/WireMock-Net/WireMock.Net/pull/903) - Add WithBody with IDictionary (form-urlencoded values) [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#904](https://github.com/WireMock-Net/WireMock.Net/pull/904) - Update Handlebars.Net.Helpers to 2.3.15 [feature] contributed by [StefH](https://github.com/StefH)
|
||||
|
||||
# 1.5.18 (09 March 2023)
|
||||
- [#893](https://github.com/WireMock-Net/WireMock.Net/pull/893) - Add 'Data' to response which can be used during transforming the response [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#896](https://github.com/WireMock-Net/WireMock.Net/pull/896) - Bump Microsoft.Owin from 2.0.2 to 4.2.2 in /examples/WireMock.Net.Service [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#900](https://github.com/WireMock-Net/WireMock.Net/pull/900) - ProxySettings : Add logic to not save some requests depending on HttpMethods [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#897](https://github.com/WireMock-Net/WireMock.Net/issues/897) - WebHostBuilder.ConfigureServices method not found when using nunit3testadapter 4.4.0 [bug]
|
||||
- [#899](https://github.com/WireMock-Net/WireMock.Net/issues/899) - Ignore OPTIONS request when using proxyandrecord [feature]
|
||||
|
||||
# 1.5.17 (25 February 2023)
|
||||
- [#881](https://github.com/WireMock-Net/WireMock.Net/pull/881) - Add WithBodyAsJson builder method with accepts a Func [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#882](https://github.com/WireMock-Net/WireMock.Net/pull/882) - Add example code to test HTTP Status 400 and 500 [test] contributed by [StefH](https://github.com/StefH)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<VersionPrefix>1.5.17</VersionPrefix>
|
||||
<VersionPrefix>1.5.21</VersionPrefix>
|
||||
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
||||
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
|
||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
rem https://github.com/StefH/GitHubReleaseNotes
|
||||
|
||||
SET version=1.5.17
|
||||
SET version=1.5.21
|
||||
|
||||
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate --version %version% --token %GH_TOKEN%
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 1.5.17 (25 February 2023)
|
||||
- #881 Add WithBodyAsJson builder method with accepts a Func [feature]
|
||||
- #882 Add example code to test HTTP Status 400 and 500 [test]
|
||||
- #890 AdminApiMappingBuilder [feature]
|
||||
# 1.5.21 (22 March 2023)
|
||||
- #908 RequestBuilder : add WithBodyAsJson and WithBody (with IJsonConverter) [feature]
|
||||
- #911 Fixed QueryStringParser for UrlEncoded values [bug]
|
||||
- #901 Matching one form-urlencoded value [feature]
|
||||
|
||||
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers" Version="2.*" />
|
||||
<!--<PackageReference Include="Handlebars.Net.Helpers" Version="2.*" />-->
|
||||
<PackageReference Include="log4net" Version="2.0.15" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
|
||||
@@ -643,6 +643,7 @@ namespace WireMock.Net.ConsoleApplication
|
||||
.WithStatusCode(200)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBodyAsJson(new { Id = "5bdf076c-5654-4b3e-842c-7caf1fabf8c9" }));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/random200or505").UsingGet())
|
||||
.RespondWith(Response.Create().WithCallback(request =>
|
||||
@@ -650,7 +651,11 @@ namespace WireMock.Net.ConsoleApplication
|
||||
int code = new Random().Next(1, 2) == 1 ? 505 : 200;
|
||||
return new ResponseMessage
|
||||
{
|
||||
BodyData = new BodyData { BodyAsString = "random200or505:" + code, DetectedBodyType = Types.BodyType.String },
|
||||
BodyData = new BodyData
|
||||
{
|
||||
BodyAsString = "random200or505:" + code + ", HeadersFromRequest = " + string.Join(",", request.Headers),
|
||||
DetectedBodyType = Types.BodyType.String,
|
||||
},
|
||||
StatusCode = code
|
||||
};
|
||||
}));
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNet.WebApi.Owin" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNet.WebApi.OwinSelfHost" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.Owin" version="2.0.2" targetFramework="net452" />
|
||||
<package id="Microsoft.Owin" version="4.2.2" targetFramework="net452" />
|
||||
<package id="Microsoft.Owin.Host.HttpListener" version="2.0.2" targetFramework="net452" />
|
||||
<package id="Microsoft.Owin.Hosting" version="2.0.2" targetFramework="net452" />
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net452" />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using WireMock.Models;
|
||||
|
||||
namespace WireMock.Admin.Mappings;
|
||||
@@ -84,4 +85,13 @@ public class MappingModel
|
||||
/// Fire and forget for webhooks.
|
||||
/// </summary>
|
||||
public bool? UseWebhooksFireAndForget { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Data Object which can be used when WithTransformer is used.
|
||||
/// e.g. lookup an path in this object using
|
||||
/// <example>
|
||||
/// lookup data "1"
|
||||
/// </example>
|
||||
/// </summary>
|
||||
public object? Data { get; set; }
|
||||
}
|
||||
@@ -1,121 +1,120 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WireMock.Admin.Mappings
|
||||
namespace WireMock.Admin.Mappings;
|
||||
|
||||
/// <summary>
|
||||
/// ResponseModel
|
||||
/// </summary>
|
||||
[FluentBuilder.AutoGenerateBuilder]
|
||||
public class ResponseModel
|
||||
{
|
||||
/// <summary>
|
||||
/// ResponseModel
|
||||
/// Gets or sets the HTTP status.
|
||||
/// </summary>
|
||||
[FluentBuilder.AutoGenerateBuilder]
|
||||
public class ResponseModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the HTTP status.
|
||||
/// </summary>
|
||||
public object? StatusCode { get; set; }
|
||||
public object? StatusCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body destination (SameAsSource, String or Bytes).
|
||||
/// </summary>
|
||||
public string? BodyDestination { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the body destination (SameAsSource, String or Bytes).
|
||||
/// </summary>
|
||||
public string? BodyDestination { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
public string? Body { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
public string? Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body (as JSON object).
|
||||
/// </summary>
|
||||
public object? BodyAsJson { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the body (as JSON object).
|
||||
/// </summary>
|
||||
public object? BodyAsJson { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether child objects to be indented according to the Newtonsoft.Json.JsonTextWriter.Indentation and Newtonsoft.Json.JsonTextWriter.IndentChar settings.
|
||||
/// </summary>
|
||||
public bool? BodyAsJsonIndented { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether child objects to be indented according to the Newtonsoft.Json.JsonTextWriter.Indentation and Newtonsoft.Json.JsonTextWriter.IndentChar settings.
|
||||
/// </summary>
|
||||
public bool? BodyAsJsonIndented { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body (as bytearray).
|
||||
/// </summary>
|
||||
public byte[]? BodyAsBytes { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the body (as bytearray).
|
||||
/// </summary>
|
||||
public byte[]? BodyAsBytes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body as a file.
|
||||
/// </summary>
|
||||
public string? BodyAsFile { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the body as a file.
|
||||
/// </summary>
|
||||
public string? BodyAsFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the body as file cached?
|
||||
/// </summary>
|
||||
public bool? BodyAsFileIsCached { get; set; }
|
||||
/// <summary>
|
||||
/// Is the body as file cached?
|
||||
/// </summary>
|
||||
public bool? BodyAsFileIsCached { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body encoding.
|
||||
/// </summary>
|
||||
public EncodingModel? BodyEncoding { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the body encoding.
|
||||
/// </summary>
|
||||
public EncodingModel? BodyEncoding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Use ResponseMessage Transformer.
|
||||
/// </summary>
|
||||
public bool? UseTransformer { get; set; }
|
||||
/// <summary>
|
||||
/// Use ResponseMessage Transformer.
|
||||
/// </summary>
|
||||
public bool? UseTransformer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the transformer.
|
||||
/// </summary>
|
||||
public string? TransformerType { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the type of the transformer.
|
||||
/// </summary>
|
||||
public string? TransformerType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Use the Handlebars transformer for the content from the referenced BodyAsFile.
|
||||
/// </summary>
|
||||
public bool? UseTransformerForBodyAsFile { get; set; }
|
||||
/// <summary>
|
||||
/// Use the Handlebars transformer for the content from the referenced BodyAsFile.
|
||||
/// </summary>
|
||||
public bool? UseTransformerForBodyAsFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ReplaceNodeOptions to use when transforming a JSON node.
|
||||
/// </summary>
|
||||
public string? TransformerReplaceNodeOptions { get; set; }
|
||||
/// <summary>
|
||||
/// The ReplaceNodeOptions to use when transforming a JSON node.
|
||||
/// </summary>
|
||||
public string? TransformerReplaceNodeOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the headers.
|
||||
/// </summary>
|
||||
public IDictionary<string, object>? Headers { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the headers.
|
||||
/// </summary>
|
||||
public IDictionary<string, object>? Headers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Headers (Raw).
|
||||
/// </summary>
|
||||
public string? HeadersRaw { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the Headers (Raw).
|
||||
/// </summary>
|
||||
public string? HeadersRaw { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? Delay { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? Delay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum random delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? MinimumRandomDelay { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum random delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? MinimumRandomDelay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum random delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? MaximumRandomDelay { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum random delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? MaximumRandomDelay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Proxy URL.
|
||||
/// </summary>
|
||||
public string? ProxyUrl { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the Proxy URL.
|
||||
/// </summary>
|
||||
public string? ProxyUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The client X509Certificate2 Thumbprint or SubjectName to use.
|
||||
/// </summary>
|
||||
public string? X509Certificate2ThumbprintOrSubjectName { get; set; }
|
||||
/// <summary>
|
||||
/// The client X509Certificate2 Thumbprint or SubjectName to use.
|
||||
/// </summary>
|
||||
public string? X509Certificate2ThumbprintOrSubjectName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the fault.
|
||||
/// </summary>
|
||||
public FaultModel? Fault { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the fault.
|
||||
/// </summary>
|
||||
public FaultModel? Fault { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WebProxy settings.
|
||||
/// </summary>
|
||||
public WebProxyModel? WebProxy { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the WebProxy settings.
|
||||
/// </summary>
|
||||
public WebProxyModel? WebProxy { get; set; }
|
||||
}
|
||||
@@ -31,27 +31,49 @@ public class SettingsModel
|
||||
public int? MaxRequestLogCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allow a Body for all HTTP Methods. (default set to false).
|
||||
/// Allow a Body for all HTTP Methods. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
public bool? AllowBodyForAllHttpMethods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Handle all requests synchronously. (default set to false).
|
||||
/// Allow only a HttpStatus Code in the response which is defined. (default set to <c>false</c>).
|
||||
/// - false : also null, 0, empty or invalid HttpStatus codes are allowed.
|
||||
/// - true : only codes defined in <see cref="System.Net.HttpStatusCode"/> are allowed.
|
||||
/// </summary>
|
||||
public bool? AllowOnlyDefinedHttpStatusCodeInResponse { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to true to disable Json deserialization when processing requests. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
public bool? DisableJsonBodyParsing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Disable support for GZip and Deflate request body decompression. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
public bool? DisableRequestBodyDecompressing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to true to disable FormUrlEncoded deserializing when processing requests. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
public bool? DisableDeserializeFormUrlEncoded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Handle all requests synchronously. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
public bool? HandleRequestsSynchronously { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Throw an exception when the Matcher fails because of invalid input. (default set to false).
|
||||
/// Throw an exception when the Matcher fails because of invalid input. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
public bool? ThrowExceptionWhenMatcherFails { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Use the RegexExtended instead of the default <see cref="Regex"/>. (default set to true).
|
||||
/// Use the RegexExtended instead of the default <see cref="Regex"/>. (default set to <c>true</c>).
|
||||
/// </summary>
|
||||
public bool? UseRegexExtended { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Save unmatched requests to a file using the <see cref="IFileSystemHandler"/>. (default set to false).
|
||||
/// Save unmatched requests to a file using the <see cref="IFileSystemHandler"/>. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
public bool? SaveUnmatchedRequests { get; set; }
|
||||
|
||||
@@ -86,7 +108,7 @@ public class SettingsModel
|
||||
public HostingScheme? HostingScheme { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Don't save the response-string in the LogEntry when WithBody(Func{IRequestMessage, string}) or WithBody(Func{IRequestMessage, Task{string}}) is used. (default set to false).
|
||||
/// Don't save the response-string in the LogEntry when WithBody(Func{IRequestMessage, string}) or WithBody(Func{IRequestMessage, Task{string}}) is used. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ public interface IResponseMessage
|
||||
/// Gets or sets the status code.
|
||||
/// </summary>
|
||||
object? StatusCode { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds the header.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using WireMock.Types;
|
||||
|
||||
@@ -38,6 +39,11 @@ public interface IBodyData
|
||||
/// </summary>
|
||||
string? BodyAsString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The body as Form UrlEncoded dictionary.
|
||||
/// </summary>
|
||||
IDictionary<string, string>? BodyAsFormUrlEncoded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The detected body type (detection based on body content).
|
||||
/// </summary>
|
||||
|
||||
@@ -1,38 +1,42 @@
|
||||
namespace WireMock.Types
|
||||
namespace WireMock.Types;
|
||||
|
||||
/// <summary>
|
||||
/// The BodyType
|
||||
/// </summary>
|
||||
public enum BodyType
|
||||
{
|
||||
/// <summary>
|
||||
/// The BodyType
|
||||
/// No body present
|
||||
/// </summary>
|
||||
public enum BodyType
|
||||
{
|
||||
/// <summary>
|
||||
/// No body present
|
||||
/// </summary>
|
||||
None,
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a String
|
||||
/// </summary>
|
||||
String,
|
||||
/// <summary>
|
||||
/// Body is a String
|
||||
/// </summary>
|
||||
String,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a Json object
|
||||
/// </summary>
|
||||
Json,
|
||||
/// <summary>
|
||||
/// Body is a Json object
|
||||
/// </summary>
|
||||
Json,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a Byte array
|
||||
/// </summary>
|
||||
Bytes,
|
||||
/// <summary>
|
||||
/// Body is a Byte array
|
||||
/// </summary>
|
||||
Bytes,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a File
|
||||
/// </summary>
|
||||
File,
|
||||
/// <summary>
|
||||
/// Body is a File
|
||||
/// </summary>
|
||||
File,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a MultiPart
|
||||
/// </summary>
|
||||
MultiPart
|
||||
}
|
||||
/// <summary>
|
||||
/// Body is a MultiPart
|
||||
/// </summary>
|
||||
MultiPart,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a String which is x-www-form-urlencoded.
|
||||
/// </summary>
|
||||
FormUrlEncoded
|
||||
}
|
||||
@@ -28,7 +28,7 @@ internal static class HttpRequestMessageHelper
|
||||
switch (requestMessage.BodyData?.DetectedBodyType)
|
||||
{
|
||||
case BodyType.Bytes:
|
||||
httpRequestMessage.Content = ByteArrayContentHelper.Create(requestMessage.BodyData.BodyAsBytes, contentType);
|
||||
httpRequestMessage.Content = ByteArrayContentHelper.Create(requestMessage.BodyData.BodyAsBytes!, contentType);
|
||||
break;
|
||||
|
||||
case BodyType.Json:
|
||||
@@ -36,7 +36,8 @@ internal static class HttpRequestMessageHelper
|
||||
break;
|
||||
|
||||
case BodyType.String:
|
||||
httpRequestMessage.Content = StringContentHelper.Create(requestMessage.BodyData.BodyAsString, contentType);
|
||||
case BodyType.FormUrlEncoded:
|
||||
httpRequestMessage.Content = StringContentHelper.Create(requestMessage.BodyData.BodyAsString!, contentType);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,8 @@ internal static class HttpResponseMessageHelper
|
||||
Uri requiredUri,
|
||||
Uri originalUri,
|
||||
bool deserializeJson,
|
||||
bool decompressGzipAndDeflate)
|
||||
bool decompressGzipAndDeflate,
|
||||
bool deserializeFormUrlEncoded)
|
||||
{
|
||||
var responseMessage = new ResponseMessage { StatusCode = (int)httpResponseMessage.StatusCode };
|
||||
|
||||
@@ -44,7 +45,8 @@ internal static class HttpResponseMessageHelper
|
||||
ContentType = contentTypeHeader?.FirstOrDefault(),
|
||||
DeserializeJson = deserializeJson,
|
||||
ContentEncoding = contentEncodingHeader?.FirstOrDefault(),
|
||||
DecompressGZipAndDeflate = decompressGzipAndDeflate
|
||||
DecompressGZipAndDeflate = decompressGzipAndDeflate,
|
||||
DeserializeFormUrlEncoded = deserializeFormUrlEncoded
|
||||
};
|
||||
responseMessage.BodyData = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
|
||||
}
|
||||
@@ -55,7 +57,7 @@ internal static class HttpResponseMessageHelper
|
||||
// If Location header contains absolute redirect URL, and base URL is one that we proxy to,
|
||||
// we need to replace it to original one.
|
||||
if (string.Equals(header.Key, HttpKnownHeaderNames.Location, StringComparison.OrdinalIgnoreCase)
|
||||
&& Uri.TryCreate(header.Value.First(), UriKind.Absolute, out Uri absoluteLocationUri)
|
||||
&& Uri.TryCreate(header.Value.First(), UriKind.Absolute, out var absoluteLocationUri)
|
||||
&& string.Equals(absoluteLocationUri.Host, requiredUri.Host, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var replacedLocationUri = new Uri(originalUri, absoluteLocationUri.PathAndQuery);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Models;
|
||||
@@ -120,7 +121,16 @@ public interface IMapping
|
||||
/// <summary>
|
||||
/// Use Fire and Forget for the defined webhook(s). [Optional]
|
||||
/// </summary>
|
||||
public bool? UseWebhooksFireAndForget { get; set; }
|
||||
bool? UseWebhooksFireAndForget { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Data Object which can be used when WithTransformer is used.
|
||||
/// e.g. lookup an path in this object using
|
||||
/// <example>
|
||||
/// lookup data "1"
|
||||
/// </example>
|
||||
/// </summary>
|
||||
object? Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ProvideResponseAsync
|
||||
|
||||
10
src/WireMock.Net/Json/DynamicJsonClassOptions.cs
Normal file
10
src/WireMock.Net/Json/DynamicJsonClassOptions.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
|
||||
|
||||
namespace WireMock.Json;
|
||||
|
||||
internal class DynamicJsonClassOptions
|
||||
{
|
||||
public IntegerBehavior IntegerConvertBehavior { get; set; } = IntegerBehavior.UseLong;
|
||||
|
||||
public FloatBehavior FloatConvertBehavior { get; set; } = FloatBehavior.UseDouble;
|
||||
}
|
||||
15
src/WireMock.Net/Json/DynamicPropertyWithValue.cs
Normal file
15
src/WireMock.Net/Json/DynamicPropertyWithValue.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
|
||||
|
||||
using System.Linq.Dynamic.Core;
|
||||
|
||||
namespace WireMock.Json;
|
||||
|
||||
public class DynamicPropertyWithValue : DynamicProperty
|
||||
{
|
||||
public object? Value { get; }
|
||||
|
||||
public DynamicPropertyWithValue(string name, object? value) : base(name, value?.GetType() ?? typeof(object))
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
24
src/WireMock.Net/Json/FloatBehavior.cs
Normal file
24
src/WireMock.Net/Json/FloatBehavior.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
|
||||
|
||||
namespace WireMock.Json;
|
||||
|
||||
/// <summary>
|
||||
/// Enum to define how to convert an Float in the Json Object.
|
||||
/// </summary>
|
||||
internal enum FloatBehavior
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert all Float types in the Json Object to a double. (default)
|
||||
/// </summary>
|
||||
UseDouble = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Convert all Float types in the Json Object to a float (unless overflow).
|
||||
/// </summary>
|
||||
UseFloat = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Convert all Float types in the Json Object to a decimal (unless overflow).
|
||||
/// </summary>
|
||||
UseDecimal = 2
|
||||
}
|
||||
20
src/WireMock.Net/Json/IntegerBehavior.cs
Normal file
20
src/WireMock.Net/Json/IntegerBehavior.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
|
||||
|
||||
namespace WireMock.Json;
|
||||
|
||||
/// <summary>
|
||||
/// Enum to define how to convert an Integer in the Json Object.
|
||||
/// </summary>
|
||||
internal enum IntegerBehavior
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert all Integer types in the Json Object to a int (unless overflow).
|
||||
/// (default)
|
||||
/// </summary>
|
||||
UseInt = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Convert all Integer types in the Json Object to a long.
|
||||
/// </summary>
|
||||
UseLong = 1
|
||||
}
|
||||
202
src/WireMock.Net/Json/JObjectExtensions.cs
Normal file
202
src/WireMock.Net/Json/JObjectExtensions.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace WireMock.Json;
|
||||
|
||||
internal static class JObjectExtensions
|
||||
{
|
||||
private class JTokenResolvers : Dictionary<JTokenType, Func<JToken, DynamicJsonClassOptions?, object?>>
|
||||
{
|
||||
}
|
||||
|
||||
private static readonly JTokenResolvers Resolvers = new()
|
||||
{
|
||||
{ JTokenType.Array, ConvertJTokenArray },
|
||||
{ JTokenType.Boolean, (jToken, _) => jToken.Value<bool>() },
|
||||
{ JTokenType.Bytes, (jToken, _) => jToken.Value<byte[]>() },
|
||||
{ JTokenType.Date, (jToken, _) => jToken.Value<DateTime>() },
|
||||
{ JTokenType.Float, ConvertJTokenFloat },
|
||||
{ JTokenType.Guid, (jToken, _) => jToken.Value<Guid>() },
|
||||
{ JTokenType.Integer, ConvertJTokenInteger },
|
||||
{ JTokenType.None, (_, _) => null },
|
||||
{ JTokenType.Null, (_, _) => null },
|
||||
{ JTokenType.Object, ConvertJObject },
|
||||
{ JTokenType.Property, ConvertJTokenProperty },
|
||||
{ JTokenType.String, (jToken, _) => jToken.Value<string>() },
|
||||
{ JTokenType.TimeSpan, (jToken, _) => jToken.Value<TimeSpan>() },
|
||||
{ JTokenType.Undefined, (_, _) => null },
|
||||
{ JTokenType.Uri, (o, _) => o.Value<Uri>() },
|
||||
};
|
||||
|
||||
internal static DynamicClass? ToDynamicJsonClass(this JObject? src, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
if (src == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var dynamicPropertyWithValues = new List<DynamicPropertyWithValue>();
|
||||
|
||||
foreach (var prop in src.Properties())
|
||||
{
|
||||
var value = Resolvers[prop.Type](prop.Value, options);
|
||||
if (value != null)
|
||||
{
|
||||
dynamicPropertyWithValues.Add(new DynamicPropertyWithValue(prop.Name, value));
|
||||
}
|
||||
}
|
||||
|
||||
return CreateInstance(dynamicPropertyWithValues);
|
||||
}
|
||||
|
||||
internal static IEnumerable ToDynamicClassArray(this JArray? src, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
if (src == null)
|
||||
{
|
||||
return EmptyArray<object?>.Value;
|
||||
}
|
||||
|
||||
return ConvertJTokenArray(src, options);
|
||||
}
|
||||
|
||||
private static object? ConvertJObject(JToken arg, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
if (arg is JObject asJObject)
|
||||
{
|
||||
return asJObject.ToDynamicJsonClass(options);
|
||||
}
|
||||
|
||||
return GetResolverFor(arg)(arg, options);
|
||||
}
|
||||
|
||||
private static object PassThrough(JToken arg, DynamicJsonClassOptions? options)
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
|
||||
private static Func<JToken, DynamicJsonClassOptions?, object?> GetResolverFor(JToken arg)
|
||||
{
|
||||
return Resolvers.TryGetValue(arg.Type, out var result) ? result : PassThrough;
|
||||
}
|
||||
|
||||
private static object ConvertJTokenFloat(JToken arg, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
if (arg.Type != JTokenType.Float)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to double or float.");
|
||||
}
|
||||
|
||||
if (options?.FloatConvertBehavior == FloatBehavior.UseFloat)
|
||||
{
|
||||
try
|
||||
{
|
||||
return arg.Value<float>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return arg.Value<double>();
|
||||
}
|
||||
}
|
||||
|
||||
if (options?.FloatConvertBehavior == FloatBehavior.UseDecimal)
|
||||
{
|
||||
try
|
||||
{
|
||||
return arg.Value<decimal>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return arg.Value<double>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return arg.Value<double>();
|
||||
}
|
||||
|
||||
private static object ConvertJTokenInteger(JToken arg, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
if (arg.Type != JTokenType.Integer)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to long or int.");
|
||||
}
|
||||
|
||||
var longValue = arg.Value<long>();
|
||||
|
||||
if (options is null || options.IntegerConvertBehavior == IntegerBehavior.UseInt)
|
||||
{
|
||||
if (longValue is >= int.MinValue and <= int.MaxValue)
|
||||
{
|
||||
return Convert.ToInt32(longValue);
|
||||
}
|
||||
}
|
||||
|
||||
return longValue;
|
||||
}
|
||||
|
||||
private static object? ConvertJTokenProperty(JToken arg, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
var resolver = GetResolverFor(arg);
|
||||
if (resolver is null)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to handle {nameof(JToken)} of type: {arg.Type}.");
|
||||
}
|
||||
|
||||
return resolver(arg, options);
|
||||
}
|
||||
|
||||
private static IEnumerable ConvertJTokenArray(JToken arg, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
if (arg is not JArray array)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to {nameof(JArray)}.");
|
||||
}
|
||||
|
||||
var result = new List<object?>();
|
||||
foreach (var item in array)
|
||||
{
|
||||
result.Add(ConvertJObject(item));
|
||||
}
|
||||
|
||||
var distinctType = FindSameTypeOf(result);
|
||||
return distinctType == null ? result.ToArray() : ConvertToTypedArray(result, distinctType);
|
||||
}
|
||||
|
||||
private static Type? FindSameTypeOf(IEnumerable<object?> src)
|
||||
{
|
||||
var types = src.Select(o => o?.GetType()).Distinct().OfType<Type>().ToArray();
|
||||
return types.Length == 1 ? types[0] : null;
|
||||
}
|
||||
|
||||
private static IEnumerable ConvertToTypedArray(IEnumerable<object?> src, Type newType)
|
||||
{
|
||||
var method = ConvertToTypedArrayGenericMethod.MakeGenericMethod(newType);
|
||||
return (IEnumerable)method.Invoke(null, new object[] { src })!;
|
||||
}
|
||||
|
||||
private static readonly MethodInfo ConvertToTypedArrayGenericMethod = typeof(JObjectExtensions).GetMethod(nameof(ConvertToTypedArrayGeneric), BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
|
||||
private static T[] ConvertToTypedArrayGeneric<T>(IEnumerable<object> src)
|
||||
{
|
||||
return src.Cast<T>().ToArray();
|
||||
}
|
||||
|
||||
public static DynamicClass CreateInstance(IList<DynamicPropertyWithValue> dynamicPropertiesWithValue, bool createParameterCtor = true)
|
||||
{
|
||||
var type = DynamicClassFactory.CreateType(dynamicPropertiesWithValue.Cast<DynamicProperty>().ToArray(), createParameterCtor);
|
||||
var dynamicClass = (DynamicClass)Activator.CreateInstance(type);
|
||||
foreach (var dynamicPropertyWithValue in dynamicPropertiesWithValue.Where(p => p.Value != null))
|
||||
{
|
||||
dynamicClass.SetDynamicPropertyValue(dynamicPropertyWithValue.Name, dynamicPropertyWithValue.Value!);
|
||||
}
|
||||
|
||||
return dynamicClass;
|
||||
}
|
||||
}
|
||||
@@ -72,6 +72,9 @@ public class Mapping : IMapping
|
||||
/// <inheritdoc />
|
||||
public ITimeSettings? TimeSettings { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public object? Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Mapping"/> class.
|
||||
/// </summary>
|
||||
@@ -91,6 +94,7 @@ public class Mapping : IMapping
|
||||
/// <param name="webhooks">The Webhooks. [Optional]</param>
|
||||
/// <param name="useWebhooksFireAndForget">Use Fire and Forget for the defined webhook(s). [Optional]</param>
|
||||
/// <param name="timeSettings">The TimeSettings. [Optional]</param>
|
||||
/// <param name="data">The data object. [Optional]</param>
|
||||
public Mapping(
|
||||
Guid guid,
|
||||
DateTime updatedAt,
|
||||
@@ -107,7 +111,8 @@ public class Mapping : IMapping
|
||||
int? stateTimes,
|
||||
IWebhook[]? webhooks,
|
||||
bool? useWebhooksFireAndForget,
|
||||
ITimeSettings? timeSettings)
|
||||
ITimeSettings? timeSettings,
|
||||
object? data)
|
||||
{
|
||||
Guid = guid;
|
||||
UpdatedAt = updatedAt;
|
||||
@@ -125,6 +130,7 @@ public class Mapping : IMapping
|
||||
Webhooks = webhooks;
|
||||
UseWebhooksFireAndForget = useWebhooksFireAndForget;
|
||||
TimeSettings = timeSettings;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IMapping.ProvideResponseAsync" />
|
||||
|
||||
@@ -5,8 +5,8 @@ using AnyOfTypes;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Json;
|
||||
using WireMock.Models;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
@@ -100,38 +100,55 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
|
||||
{
|
||||
double match = MatchScores.Mismatch;
|
||||
|
||||
JObject value;
|
||||
switch (input)
|
||||
JArray jArray;
|
||||
try
|
||||
{
|
||||
case JObject valueAsJObject:
|
||||
value = valueAsJObject;
|
||||
break;
|
||||
|
||||
case { } valueAsObject:
|
||||
value = JObject.FromObject(valueAsObject);
|
||||
break;
|
||||
|
||||
default:
|
||||
return MatchScores.Mismatch;
|
||||
jArray = new JArray { input };
|
||||
}
|
||||
catch
|
||||
{
|
||||
jArray = new JArray { JToken.FromObject(input) };
|
||||
}
|
||||
|
||||
//enumerable = jArray.ToDynamicClassArray();
|
||||
|
||||
//JObject value;
|
||||
//switch (input)
|
||||
//{
|
||||
// case JObject valueAsJObject:
|
||||
// value = valueAsJObject;
|
||||
// break;
|
||||
|
||||
// case { } valueAsObject:
|
||||
// value = JObject.FromObject(valueAsObject);
|
||||
// break;
|
||||
|
||||
// default:
|
||||
// return MatchScores.Mismatch;
|
||||
//}
|
||||
|
||||
// Convert a single object to a Queryable JObject-list with 1 entry.
|
||||
var queryable1 = new[] { value }.AsQueryable();
|
||||
//var queryable1 = new[] { value }.AsQueryable();
|
||||
var queryable = jArray.ToDynamicClassArray().AsQueryable();
|
||||
|
||||
try
|
||||
{
|
||||
// Generate the DynamicLinq select statement.
|
||||
string dynamicSelect = JsonUtils.GenerateDynamicLinqStatement(value);
|
||||
//string dynamicSelect = JsonUtils.GenerateDynamicLinqStatement(value);
|
||||
|
||||
// Execute DynamicLinq Select statement.
|
||||
var queryable2 = queryable1.Select(dynamicSelect);
|
||||
//var queryable2 = queryable1.Select(dynamicSelect);
|
||||
|
||||
// Use the Any(...) method to check if the result matches.
|
||||
match = MatchScores.ToScore(_patterns.Select(pattern => queryable2.Any(pattern)).ToArray(), MatchOperator);
|
||||
|
||||
var patternsAsStringArray = _patterns.Select(p => p.GetPattern()).ToArray();
|
||||
var scores = patternsAsStringArray.Select(p => queryable.Any(p)).ToArray();
|
||||
|
||||
match = MatchScores.ToScore(_patterns.Select(pattern => queryable.Any(pattern.GetPattern())).ToArray(), MatchOperator);
|
||||
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
|
||||
}
|
||||
catch
|
||||
catch (Exception e)
|
||||
{
|
||||
if (ThrowException)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using WireMock.Models;
|
||||
@@ -62,7 +63,7 @@ public class NotNullOrEmptyMatcher : IObjectMatcher, IStringMatcher
|
||||
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
|
||||
public AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return new AnyOf<string, StringPattern>[0];
|
||||
return EmptyArray<AnyOf<string, StringPattern>>.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using Stef.Validation;
|
||||
using WireMock.Models;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
@@ -33,6 +32,11 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
/// </summary>
|
||||
public Func<IBodyData?, bool>? BodyDataFunc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The body data function for FormUrlEncoded
|
||||
/// </summary>
|
||||
public Func<IDictionary<string, string>?, bool>? FormUrlEncodedFunc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The matchers.
|
||||
/// </summary>
|
||||
@@ -109,6 +113,15 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
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>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
@@ -144,6 +157,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
{
|
||||
case BodyType.Json:
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
return notNullOrEmptyMatcher.IsMatch(requestMessage.BodyData.BodyAsString);
|
||||
|
||||
case BodyType.Bytes:
|
||||
@@ -158,7 +172,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
{
|
||||
// If the body is a byte array, try to match.
|
||||
var detectedBodyType = requestMessage.BodyData?.DetectedBodyType;
|
||||
if (detectedBodyType is BodyType.Bytes or BodyType.String)
|
||||
if (detectedBodyType is BodyType.Bytes or BodyType.String or BodyType.FormUrlEncoded)
|
||||
{
|
||||
return exactObjectMatcher.IsMatch(requestMessage.BodyData?.BodyAsBytes);
|
||||
}
|
||||
@@ -184,7 +198,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
if (matcher is IStringMatcher stringMatcher)
|
||||
{
|
||||
// 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 or BodyType.FormUrlEncoded)
|
||||
{
|
||||
return stringMatcher.IsMatch(requestMessage.BodyData.BodyAsString);
|
||||
}
|
||||
@@ -206,6 +220,11 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
return MatchScores.ToScore(Func(requestMessage.BodyData?.BodyAsString));
|
||||
}
|
||||
|
||||
if (FormUrlEncodedFunc != null)
|
||||
{
|
||||
return MatchScores.ToScore(FormUrlEncodedFunc(requestMessage.BodyData?.BodyAsFormUrlEncoded));
|
||||
}
|
||||
|
||||
if (JsonFunc != null)
|
||||
{
|
||||
return MatchScores.ToScore(JsonFunc(requestMessage.BodyData?.BodyAsJson));
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using WireMock.Types;
|
||||
|
||||
@@ -14,6 +15,9 @@ public class BodyData : IBodyData
|
||||
/// <inheritdoc />
|
||||
public string? BodyAsString { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDictionary<string, string>? BodyAsFormUrlEncoded { get; set; }
|
||||
|
||||
/// <inheritdoc cref="IBodyData.BodyAsJson" />
|
||||
public object? BodyAsJson { get; set; }
|
||||
|
||||
|
||||
@@ -130,6 +130,7 @@ namespace WireMock.Owin.Mappers
|
||||
switch (responseMessage.BodyData?.DetectedBodyType)
|
||||
{
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
return (responseMessage.BodyData.Encoding ?? _utf8NoBom).GetBytes(responseMessage.BodyData.BodyAsString!);
|
||||
|
||||
case BodyType.Json:
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Stef.Validation;
|
||||
using WireMock.Http;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Serialization;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Util;
|
||||
@@ -43,16 +45,45 @@ internal class ProxyHelper
|
||||
// Create ResponseMessage
|
||||
bool deserializeJson = !_settings.DisableJsonBodyParsing.GetValueOrDefault(false);
|
||||
bool decompressGzipAndDeflate = !_settings.DisableRequestBodyDecompressing.GetValueOrDefault(false);
|
||||
bool deserializeFormUrlEncoded = !_settings.DisableDeserializeFormUrlEncoded.GetValueOrDefault(false);
|
||||
|
||||
var responseMessage = await HttpResponseMessageHelper.CreateAsync(httpResponseMessage, requiredUri, originalUri, deserializeJson, decompressGzipAndDeflate).ConfigureAwait(false);
|
||||
var responseMessage = await HttpResponseMessageHelper.CreateAsync(
|
||||
httpResponseMessage,
|
||||
requiredUri,
|
||||
originalUri,
|
||||
deserializeJson,
|
||||
decompressGzipAndDeflate,
|
||||
deserializeFormUrlEncoded
|
||||
).ConfigureAwait(false);
|
||||
|
||||
IMapping? newMapping = null;
|
||||
if (HttpStatusRangeParser.IsMatch(proxyAndRecordSettings.SaveMappingForStatusCodePattern, responseMessage.StatusCode) &&
|
||||
(proxyAndRecordSettings.SaveMapping || proxyAndRecordSettings.SaveMappingToFile))
|
||||
|
||||
var saveMappingSettings = proxyAndRecordSettings.SaveMappingSettings;
|
||||
|
||||
bool save = true;
|
||||
if (saveMappingSettings != null)
|
||||
{
|
||||
save &= Check(saveMappingSettings.StatusCodePattern,
|
||||
() => saveMappingSettings.StatusCodePattern != null && HttpStatusRangeParser.IsMatch(saveMappingSettings.StatusCodePattern, responseMessage.StatusCode)
|
||||
);
|
||||
|
||||
save &= Check(saveMappingSettings.HttpMethods,
|
||||
() => saveMappingSettings.HttpMethods != null && saveMappingSettings.HttpMethods.Value.Contains(requestMessage.Method, StringComparer.OrdinalIgnoreCase)
|
||||
);
|
||||
}
|
||||
|
||||
if (save && (proxyAndRecordSettings.SaveMapping || proxyAndRecordSettings.SaveMappingToFile))
|
||||
{
|
||||
newMapping = _proxyMappingConverter.ToMapping(mapping, proxyAndRecordSettings, requestMessage, responseMessage);
|
||||
}
|
||||
|
||||
return (responseMessage, newMapping);
|
||||
}
|
||||
|
||||
private static bool Check<T>(ProxySaveMappingSetting<T>? saveMappingSetting, Func<bool> action)
|
||||
{
|
||||
var isMatch = saveMappingSetting is null || action();
|
||||
var matchBehaviour = saveMappingSetting?.MatchBehaviour ?? MatchBehaviour.AcceptOnMatch;
|
||||
return isMatch == (matchBehaviour == MatchBehaviour.AcceptOnMatch);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JsonConverter.Abstractions;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Util;
|
||||
@@ -45,35 +47,60 @@ public interface IBodyRequestBuilder : IRequestMatcher
|
||||
/// WithBody: Body as object
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour [default is AcceptOnMatch].</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody : Body as a string response based on a object (which will be converted to a JSON string using NewtonSoft.Json).
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour [default is AcceptOnMatch].</param>
|
||||
/// <returns>A <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBodyAsJson(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody : Body as a string response based on a object (which will be converted to a JSON string using the <see cref="IJsonConverter"/>).
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="converter">The JsonConverter.</param>
|
||||
/// <param name="options">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <param name="matchBehaviour">The match behaviour [default is AcceptOnMatch].</param>
|
||||
/// <returns>A <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBodyAsJson(object body, IJsonConverter converter, JsonConverterOptions? options = null, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody: func (string)
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody(Func<string, bool> func);
|
||||
IRequestBuilder WithBody(Func<string?, bool> func);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody: func (byte[])
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody(Func<byte[], bool> func);
|
||||
IRequestBuilder WithBody(Func<byte[]?, bool> func);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody: func (json object)
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody(Func<object, bool> func);
|
||||
IRequestBuilder WithBody(Func<object?, bool> func);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody: func (BodyData object)
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
/// <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);
|
||||
}
|
||||
@@ -1,36 +1,57 @@
|
||||
// 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.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JsonConverter.Abstractions;
|
||||
using Newtonsoft.Json;
|
||||
using Stef.Validation;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Util;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.RequestBuilders;
|
||||
|
||||
public partial class Request
|
||||
{
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(string, MatchBehaviour)"/>
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(string body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(byte[], MatchBehaviour)"/>
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(byte[] body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(object, MatchBehaviour)"/>
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBodyAsJson(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
var bodyAsJsonString = JsonConvert.SerializeObject(body);
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, bodyAsJsonString));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBodyAsJson(object body, IJsonConverter converter, JsonConverterOptions? options = null, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
Guard.NotNull(converter);
|
||||
|
||||
var bodyAsJsonString = converter.Serialize(body, options);
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, bodyAsJsonString));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(IMatcher matcher)
|
||||
{
|
||||
@@ -46,39 +67,46 @@ public partial class Request
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{string, bool})"/>
|
||||
public IRequestBuilder WithBody(Func<string, bool> func)
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(Func<string?, bool> func)
|
||||
{
|
||||
Guard.NotNull(func, nameof(func));
|
||||
Guard.NotNull(func);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{byte[], bool})"/>
|
||||
public IRequestBuilder WithBody(Func<byte[], bool> func)
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(Func<byte[]?, bool> func)
|
||||
{
|
||||
Guard.NotNull(func, nameof(func));
|
||||
Guard.NotNull(func);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{object, bool})"/>
|
||||
public IRequestBuilder WithBody(Func<object, bool> func)
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(Func<object?, bool> func)
|
||||
{
|
||||
Guard.NotNull(func, nameof(func));
|
||||
Guard.NotNull(func);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{IBodyData, bool})"/>
|
||||
public IRequestBuilder WithBody(Func<IBodyData, bool> func)
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(Func<IBodyData?, bool> func)
|
||||
{
|
||||
Guard.NotNull(func, nameof(func));
|
||||
Guard.NotNull(func);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(Func<IDictionary<string, string>?, bool> func)
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(Guard.NotNull(func)));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,7 @@ public interface IBodyResponseBuilder : IFaultResponseBuilder
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="converter">The JsonConverter.</param>
|
||||
/// <param name="options">The IJsonConverterOption [optional].</param>
|
||||
/// <param name="options">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBody(object body, IJsonConverter converter, JsonConverterOptions? options = null);
|
||||
|
||||
@@ -102,7 +102,7 @@ public interface IBodyResponseBuilder : IFaultResponseBuilder
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="encoding">The body encoding, can be <c>null</c>.</param>
|
||||
/// <param name="converter">The JsonConverter.</param>
|
||||
/// <param name="options">The IJsonConverterOption [optional].</param>
|
||||
/// <param name="options">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBody(object body, Encoding? encoding, IJsonConverter converter, JsonConverterOptions? options = null);
|
||||
}
|
||||
@@ -107,14 +107,12 @@ public partial class Response : IResponseBuilder
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Response"/> class.
|
||||
/// </summary>
|
||||
/// <param name="responseMessage">
|
||||
/// The response.
|
||||
/// </param>
|
||||
/// <param name="responseMessage">The response.</param>
|
||||
private Response(ResponseMessage responseMessage)
|
||||
{
|
||||
ResponseMessage = responseMessage;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc cref="IStatusCodeResponseBuilder.WithStatusCode(int)"/>
|
||||
[PublicAPI]
|
||||
public IResponseBuilder WithStatusCode(int code)
|
||||
|
||||
@@ -44,6 +44,7 @@ internal class LogEntryMapper
|
||||
switch (logEntry.RequestMessage.BodyData.DetectedBodyType)
|
||||
{
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
logRequestModel.Body = logEntry.RequestMessage.BodyData.BodyAsString;
|
||||
break;
|
||||
|
||||
@@ -120,6 +121,7 @@ internal class LogEntryMapper
|
||||
switch (logEntry.ResponseMessage.BodyData!.DetectedBodyType)
|
||||
{
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
if (!string.IsNullOrEmpty(logEntry.ResponseMessage.BodyData.IsFuncUsed) && _options.DoNotSaveDynamicResponseInLogEntry == true)
|
||||
{
|
||||
logResponseModel.Body = logEntry.ResponseMessage.BodyData.IsFuncUsed;
|
||||
@@ -142,6 +144,9 @@ internal class LogEntryMapper
|
||||
logResponseModel.BodyAsFile = logEntry.ResponseMessage.BodyData.BodyAsFile;
|
||||
logResponseModel.BodyAsFileIsCached = logEntry.ResponseMessage.BodyData.BodyAsFileIsCached;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -131,6 +131,7 @@ internal class MappingConverter
|
||||
switch (response.ResponseMessage.BodyData.DetectedBodyType)
|
||||
{
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
sb.AppendLine($" .WithBody(\"{response.ResponseMessage.BodyData.BodyAsString}\")");
|
||||
break;
|
||||
}
|
||||
@@ -140,7 +141,7 @@ internal class MappingConverter
|
||||
{
|
||||
sb.AppendLine($" .WithDelay({response.Delay.Value.TotalMilliseconds})");
|
||||
}
|
||||
else if (response.MinimumDelayMilliseconds > 0 && response.MaximumDelayMilliseconds > 0)
|
||||
else if (response is { MinimumDelayMilliseconds: > 0, MaximumDelayMilliseconds: > 0 })
|
||||
{
|
||||
sb.AppendLine($" .WithRandomDelay({response.MinimumDelayMilliseconds}, {response.MaximumDelayMilliseconds})");
|
||||
}
|
||||
@@ -181,6 +182,7 @@ internal class MappingConverter
|
||||
Scenario = mapping.Scenario,
|
||||
WhenStateIs = mapping.ExecutionConditionState,
|
||||
SetStateTo = mapping.NextState,
|
||||
Data = mapping.Data,
|
||||
Request = new RequestModel
|
||||
{
|
||||
Headers = headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel
|
||||
@@ -324,6 +326,7 @@ internal class MappingConverter
|
||||
switch (response.ResponseMessage.BodyData?.DetectedBodyType)
|
||||
{
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
mappingModel.Response.Body = response.ResponseMessage.BodyData.BodyAsString;
|
||||
break;
|
||||
|
||||
|
||||
@@ -26,8 +26,12 @@ internal class ProxyMappingConverter
|
||||
_dateTimeUtils = Guard.NotNull(dateTimeUtils);
|
||||
}
|
||||
|
||||
public IMapping ToMapping(IMapping? mapping, ProxyAndRecordSettings proxyAndRecordSettings, IRequestMessage requestMessage, ResponseMessage responseMessage)
|
||||
public IMapping? ToMapping(IMapping? mapping, ProxyAndRecordSettings proxyAndRecordSettings, IRequestMessage requestMessage, ResponseMessage responseMessage)
|
||||
{
|
||||
var useDefinedRequestMatchers = proxyAndRecordSettings.UseDefinedRequestMatchers;
|
||||
var excludedHeaders = new List<string>(proxyAndRecordSettings.ExcludedHeaders ?? new string[] { }) { "Cookie" };
|
||||
var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[0];
|
||||
|
||||
var request = (Request?)mapping?.RequestMatcher;
|
||||
var clientIPMatcher = request?.GetRequestMessageMatcher<RequestMessageClientIPMatcher>();
|
||||
var pathMatcher = request?.GetRequestMessageMatcher<RequestMessagePathMatcher>();
|
||||
@@ -37,11 +41,6 @@ internal class ProxyMappingConverter
|
||||
var methodMatcher = request?.GetRequestMessageMatcher<RequestMessageMethodMatcher>();
|
||||
var bodyMatcher = request?.GetRequestMessageMatcher<RequestMessageBodyMatcher>();
|
||||
|
||||
var useDefinedRequestMatchers = proxyAndRecordSettings.UseDefinedRequestMatchers;
|
||||
|
||||
var excludedHeaders = new List<string>(proxyAndRecordSettings.ExcludedHeaders ?? new string[] { }) { "Cookie" };
|
||||
var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[] { };
|
||||
|
||||
var newRequest = Request.Create();
|
||||
|
||||
// ClientIP
|
||||
@@ -142,6 +141,7 @@ internal class ProxyMappingConverter
|
||||
break;
|
||||
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
newRequest.WithBody(new ExactMatcher(MatchBehaviour.AcceptOnMatch, true, throwExceptionWhenMatcherFails, MatchOperator.Or, requestMessage.BodyData.BodyAsString!));
|
||||
break;
|
||||
|
||||
@@ -178,7 +178,8 @@ internal class ProxyMappingConverter
|
||||
stateTimes: null,
|
||||
webhooks: null,
|
||||
useWebhooksFireAndForget: null,
|
||||
timeSettings: null
|
||||
timeSettings: null,
|
||||
data: mapping?.Data
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -97,6 +97,7 @@ internal static class WebhookMapper
|
||||
switch (webhook.Request.BodyData.DetectedBodyType)
|
||||
{
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
model.Request.Body = webhook.Request.BodyData.BodyAsString;
|
||||
break;
|
||||
|
||||
|
||||
@@ -166,4 +166,14 @@ public interface IRespondWithAProvider
|
||||
bool useTransformer = true,
|
||||
TransformerType transformerType = TransformerType.Handlebars
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Data Object which can be used when WithTransformer is used.
|
||||
/// e.g. lookup an path in this object using
|
||||
/// <param name="data">The data dictionary object.</param>
|
||||
/// <example>
|
||||
/// lookup data "1"
|
||||
/// </example>
|
||||
/// </summary>
|
||||
IRespondWithAProvider WithData(object data);
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Stef.Validation;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Models;
|
||||
@@ -39,6 +40,8 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
|
||||
public ITimeSettings? TimeSettings { get; private set; }
|
||||
|
||||
public object? Data { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RespondWithAProvider"/> class.
|
||||
/// </summary>
|
||||
@@ -88,10 +91,20 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
_timesInSameState,
|
||||
Webhooks,
|
||||
_useWebhookFireAndForget,
|
||||
TimeSettings);
|
||||
TimeSettings,
|
||||
Data);
|
||||
|
||||
_registrationCallback(mapping, _saveToFile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[PublicAPI]
|
||||
public IRespondWithAProvider WithData(object data)
|
||||
{
|
||||
Data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRespondWithAProvider WithGuid(string guid)
|
||||
{
|
||||
|
||||
@@ -217,10 +217,17 @@ public partial class WireMockServer
|
||||
var model = new SettingsModel
|
||||
{
|
||||
AllowBodyForAllHttpMethods = _settings.AllowBodyForAllHttpMethods,
|
||||
AllowOnlyDefinedHttpStatusCodeInResponse = _settings.AllowOnlyDefinedHttpStatusCodeInResponse,
|
||||
AllowPartialMapping = _settings.AllowPartialMapping,
|
||||
DisableDeserializeFormUrlEncoded = _settings.DisableDeserializeFormUrlEncoded,
|
||||
DisableJsonBodyParsing = _settings.DisableJsonBodyParsing,
|
||||
DisableRequestBodyDecompressing = _settings.DisableRequestBodyDecompressing,
|
||||
DoNotSaveDynamicResponseInLogEntry = _settings.DoNotSaveDynamicResponseInLogEntry,
|
||||
GlobalProcessingDelay = (int?)_options.RequestProcessingDelay?.TotalMilliseconds,
|
||||
HandleRequestsSynchronously = _settings.HandleRequestsSynchronously,
|
||||
HostingScheme = _settings.HostingScheme,
|
||||
MaxRequestLogCount = _settings.MaxRequestLogCount,
|
||||
QueryParameterMultipleValueSupport = _settings.QueryParameterMultipleValueSupport,
|
||||
ReadStaticMappings = _settings.ReadStaticMappings,
|
||||
RequestLogExpirationDuration = _settings.RequestLogExpirationDuration,
|
||||
SaveUnmatchedRequests = _settings.SaveUnmatchedRequests,
|
||||
@@ -228,14 +235,11 @@ public partial class WireMockServer
|
||||
UseRegexExtended = _settings.UseRegexExtended,
|
||||
WatchStaticMappings = _settings.WatchStaticMappings,
|
||||
WatchStaticMappingsInSubdirectories = _settings.WatchStaticMappingsInSubdirectories,
|
||||
HostingScheme = _settings.HostingScheme,
|
||||
DoNotSaveDynamicResponseInLogEntry = _settings.DoNotSaveDynamicResponseInLogEntry,
|
||||
QueryParameterMultipleValueSupport = _settings.QueryParameterMultipleValueSupport,
|
||||
|
||||
#if USE_ASPNETCORE
|
||||
CorsPolicyOptions = _settings.CorsPolicyOptions?.ToString(),
|
||||
AcceptAnyClientCertificate = _settings.AcceptAnyClientCertificate,
|
||||
ClientCertificateMode = _settings.ClientCertificateMode,
|
||||
AcceptAnyClientCertificate = _settings.AcceptAnyClientCertificate
|
||||
CorsPolicyOptions = _settings.CorsPolicyOptions?.ToString()
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -250,10 +254,16 @@ public partial class WireMockServer
|
||||
|
||||
// _settings
|
||||
_settings.AllowBodyForAllHttpMethods = settings.AllowBodyForAllHttpMethods;
|
||||
_settings.AllowOnlyDefinedHttpStatusCodeInResponse = settings.AllowOnlyDefinedHttpStatusCodeInResponse;
|
||||
_settings.AllowPartialMapping = settings.AllowPartialMapping;
|
||||
_settings.DisableDeserializeFormUrlEncoded = settings.DisableDeserializeFormUrlEncoded;
|
||||
_settings.DisableJsonBodyParsing = settings.DisableJsonBodyParsing;
|
||||
_settings.DisableRequestBodyDecompressing = settings.DisableRequestBodyDecompressing;
|
||||
_settings.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry;
|
||||
_settings.HandleRequestsSynchronously = settings.HandleRequestsSynchronously;
|
||||
_settings.MaxRequestLogCount = settings.MaxRequestLogCount;
|
||||
_settings.ProxyAndRecordSettings = TinyMapperUtils.Instance.Map(settings.ProxyAndRecordSettings);
|
||||
_settings.QueryParameterMultipleValueSupport = settings.QueryParameterMultipleValueSupport;
|
||||
_settings.ReadStaticMappings = settings.ReadStaticMappings;
|
||||
_settings.RequestLogExpirationDuration = settings.RequestLogExpirationDuration;
|
||||
_settings.SaveUnmatchedRequests = settings.SaveUnmatchedRequests;
|
||||
@@ -261,8 +271,6 @@ public partial class WireMockServer
|
||||
_settings.UseRegexExtended = settings.UseRegexExtended;
|
||||
_settings.WatchStaticMappings = settings.WatchStaticMappings;
|
||||
_settings.WatchStaticMappingsInSubdirectories = settings.WatchStaticMappingsInSubdirectories;
|
||||
_settings.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry;
|
||||
_settings.QueryParameterMultipleValueSupport = settings.QueryParameterMultipleValueSupport;
|
||||
|
||||
InitSettings(_settings);
|
||||
|
||||
@@ -759,14 +767,18 @@ public partial class WireMockServer
|
||||
|
||||
private static T DeserializeObject<T>(IRequestMessage requestMessage) where T : new()
|
||||
{
|
||||
return requestMessage.BodyData?.DetectedBodyType switch
|
||||
switch (requestMessage.BodyData?.DetectedBodyType)
|
||||
{
|
||||
BodyType.String => JsonUtils.DeserializeObject<T>(requestMessage.BodyData.BodyAsString!),
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
return JsonUtils.DeserializeObject<T>(requestMessage.BodyData.BodyAsString!);
|
||||
|
||||
BodyType.Json when requestMessage.BodyData?.BodyAsJson != null => ((JObject)requestMessage.BodyData.BodyAsJson).ToObject<T>()!,
|
||||
case BodyType.Json when requestMessage.BodyData?.BodyAsJson != null:
|
||||
return ((JObject)requestMessage.BodyData.BodyAsJson).ToObject<T>()!;
|
||||
|
||||
_ => throw new NotSupportedException()
|
||||
};
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
private static T[] DeserializeRequestMessageToArray<T>(IRequestMessage requestMessage)
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace WireMock.Server
|
||||
}
|
||||
};
|
||||
|
||||
if (BytesEncodingUtils.TryGetEncoding(bytes, out Encoding encoding) && FileBodyIsString.Select(x => x.Equals(encoding)).Any())
|
||||
if (BytesEncodingUtils.TryGetEncoding(bytes, out var encoding) && FileBodyIsString.Select(x => x.Equals(encoding)).Any())
|
||||
{
|
||||
response.BodyData.DetectedBodyType = BodyType.String;
|
||||
response.BodyData.BodyAsString = encoding.GetString(bytes);
|
||||
|
||||
@@ -46,7 +46,7 @@ public partial class WireMockServer
|
||||
}
|
||||
|
||||
var respondProvider = Given(requestBuilder, mappingModel.SaveToFile == true);
|
||||
|
||||
|
||||
if (guid != null)
|
||||
{
|
||||
respondProvider = respondProvider.WithGuid(guid.Value);
|
||||
@@ -56,6 +56,11 @@ public partial class WireMockServer
|
||||
respondProvider = respondProvider.WithGuid(mappingModel.Guid.Value);
|
||||
}
|
||||
|
||||
if (mappingModel.Data != null)
|
||||
{
|
||||
respondProvider = respondProvider.WithData(mappingModel.Data);
|
||||
}
|
||||
|
||||
var timeSettings = TimeSettingsMapper.Map(mappingModel.TimeSettings);
|
||||
if (timeSettings != null)
|
||||
{
|
||||
|
||||
@@ -28,9 +28,28 @@ public class ProxyAndRecordSettings : HttpClientSettings
|
||||
/// <summary>
|
||||
/// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.)
|
||||
/// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
|
||||
///
|
||||
/// Deprecated : use SaveMappingSettings.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string SaveMappingForStatusCodePattern { get; set; } = "*";
|
||||
public string SaveMappingForStatusCodePattern
|
||||
{
|
||||
set
|
||||
{
|
||||
if (SaveMappingSettings is null)
|
||||
{
|
||||
SaveMappingSettings = new ProxySaveMappingSettings();
|
||||
}
|
||||
|
||||
SaveMappingSettings.StatusCodePattern = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Additional SaveMappingSettings.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public ProxySaveMappingSettings? SaveMappingSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines a list from headers which will be excluded from the saved mappings.
|
||||
|
||||
20
src/WireMock.Net/Settings/ProxySaveMappingSetting.cs
Normal file
20
src/WireMock.Net/Settings/ProxySaveMappingSetting.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using WireMock.Matchers;
|
||||
|
||||
namespace WireMock.Settings;
|
||||
|
||||
public class ProxySaveMappingSetting<T>
|
||||
{
|
||||
public MatchBehaviour MatchBehaviour { get; } = MatchBehaviour.AcceptOnMatch;
|
||||
|
||||
public T Value { get; }
|
||||
|
||||
public ProxySaveMappingSetting(T value, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
Value = value;
|
||||
MatchBehaviour = matchBehaviour;
|
||||
}
|
||||
|
||||
public static implicit operator ProxySaveMappingSetting<T>(T value) => new(value);
|
||||
|
||||
public static implicit operator T(ProxySaveMappingSetting<T> @this) => @this.Value;
|
||||
}
|
||||
22
src/WireMock.Net/Settings/ProxySaveMappingSettings.cs
Normal file
22
src/WireMock.Net/Settings/ProxySaveMappingSettings.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace WireMock.Settings;
|
||||
|
||||
/// <summary>
|
||||
/// ProxySaveMappingSettings
|
||||
/// </summary>
|
||||
public class ProxySaveMappingSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.)
|
||||
/// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public ProxySaveMappingSetting<string>? StatusCodePattern { get; set; } = "*";
|
||||
|
||||
/// <summary>
|
||||
/// Only save these Http Methods. (Note that SaveMapping must also be set to true.)
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public ProxySaveMappingSetting<string[]>? HttpMethods { get; set; }
|
||||
}
|
||||
@@ -32,7 +32,7 @@ internal class SimpleCommandLineParser
|
||||
}
|
||||
else if (string.IsNullOrEmpty(currentName))
|
||||
{
|
||||
Arguments[arg] = new string[0];
|
||||
Arguments[arg] = EmptyArray<string>.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -182,13 +182,13 @@ public class WireMockServerSettings
|
||||
public bool? AllowCSharpCodeMatcher { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allow a Body for all HTTP Methods. (default set to false).
|
||||
/// Allow a Body for all HTTP Methods. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? AllowBodyForAllHttpMethods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allow only a HttpStatus Code in the response which is defined. (default set to false).
|
||||
/// Allow only a HttpStatus Code in the response which is defined. (default set to <c>false</c>).
|
||||
/// - false : also null, 0, empty or invalid HttpStatus codes are allowed.
|
||||
/// - true : only codes defined in <see cref="System.Net.HttpStatusCode"/> are allowed.
|
||||
/// </summary>
|
||||
@@ -196,25 +196,31 @@ public class WireMockServerSettings
|
||||
public bool? AllowOnlyDefinedHttpStatusCodeInResponse { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to true to disable Json deserialization when processing requests. (default set to false).
|
||||
/// Set to true to disable Json deserialization when processing requests. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? DisableJsonBodyParsing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Disable support for GZip and Deflate request body decompression. (default set to false).
|
||||
/// Disable support for GZip and Deflate request body decompression. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? DisableRequestBodyDecompressing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Handle all requests synchronously. (default set to false).
|
||||
/// Set to true to disable FormUrlEncoded deserializing when processing requests. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? DisableDeserializeFormUrlEncoded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Handle all requests synchronously. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? HandleRequestsSynchronously { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Throw an exception when the <see cref="IMatcher"/> fails because of invalid input. (default set to false).
|
||||
/// Throw an exception when the <see cref="IMatcher"/> fails because of invalid input. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? ThrowExceptionWhenMatcherFails { get; set; }
|
||||
@@ -255,19 +261,19 @@ public class WireMockServerSettings
|
||||
public WebhookSettings? WebhookSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Use the <see cref="RegexExtended"/> instead of the default <see cref="Regex"/> (default set to true).
|
||||
/// Use the <see cref="RegexExtended"/> instead of the default <see cref="Regex"/> (default set to <c>true</c>).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? UseRegexExtended { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Save unmatched requests to a file using the <see cref="IFileSystemHandler"/> (default set to false).
|
||||
/// Save unmatched requests to a file using the <see cref="IFileSystemHandler"/> (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? SaveUnmatchedRequests { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Don't save the response-string in the LogEntry when WithBody(Func{IRequestMessage, string}) or WithBody(Func{IRequestMessage, Task{string}}) is used. (default set to false).
|
||||
/// Don't save the response-string in the LogEntry when WithBody(Func{IRequestMessage, string}) or WithBody(Func{IRequestMessage, Task{string}}) is used. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
|
||||
|
||||
@@ -46,6 +46,8 @@ public static class WireMockServerSettingsParser
|
||||
AllowOnlyDefinedHttpStatusCodeInResponse = parser.GetBoolValue("AllowOnlyDefinedHttpStatusCodeInResponse"),
|
||||
AllowPartialMapping = parser.GetBoolValue("AllowPartialMapping"),
|
||||
DisableJsonBodyParsing = parser.GetBoolValue("DisableJsonBodyParsing"),
|
||||
DisableRequestBodyDecompressing = parser.GetBoolValue(nameof(WireMockServerSettings.DisableRequestBodyDecompressing)),
|
||||
DisableDeserializeFormUrlEncoded = parser.GetBoolValue(nameof(WireMockServerSettings.DisableDeserializeFormUrlEncoded)),
|
||||
HandleRequestsSynchronously = parser.GetBoolValue("HandleRequestsSynchronously"),
|
||||
MaxRequestLogCount = parser.GetIntValue("MaxRequestLogCount"),
|
||||
ReadStaticMappings = parser.GetBoolValue("ReadStaticMappings"),
|
||||
@@ -111,7 +113,12 @@ public static class WireMockServerSettingsParser
|
||||
SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"),
|
||||
UseDefinedRequestMatchers = parser.GetBoolValue(nameof(ProxyAndRecordSettings.UseDefinedRequestMatchers)),
|
||||
AppendGuidToSavedMappingFile = parser.GetBoolValue(nameof(ProxyAndRecordSettings.AppendGuidToSavedMappingFile)),
|
||||
Url = proxyUrl!
|
||||
Url = proxyUrl!,
|
||||
SaveMappingSettings = new ProxySaveMappingSettings
|
||||
{
|
||||
StatusCodePattern = parser.GetStringValue("SaveMappingForStatusCodePattern", "*"),
|
||||
// HttpMethods = new ProxySaveMappingSetting<string[]>(parser.GetValues("DoNotSaveMappingForHttpMethods", new string[0]), MatchBehaviour.RejectOnMatch)
|
||||
}
|
||||
};
|
||||
|
||||
string? proxyAddress = parser.GetStringValue("WebProxyAddress");
|
||||
|
||||
@@ -10,4 +10,6 @@ internal struct TransformModel
|
||||
public IRequestMessage request { get; set; }
|
||||
|
||||
public IResponseMessage? response { get; set; }
|
||||
|
||||
public object data { get; set; }
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
@@ -84,7 +85,7 @@ internal class Transformer : ITransformer
|
||||
{
|
||||
responseMessage.BodyData = TransformBodyData(transformerContext, options, model, original.BodyData, useTransformerForBodyAsFile);
|
||||
|
||||
if (original.BodyData.DetectedBodyType == BodyType.String)
|
||||
if (original.BodyData.DetectedBodyType is BodyType.String or BodyType.FormUrlEncoded)
|
||||
{
|
||||
responseMessage.BodyOriginal = original.BodyData.BodyAsString;
|
||||
}
|
||||
@@ -115,19 +116,28 @@ internal class Transformer : ITransformer
|
||||
{
|
||||
mapping = mapping,
|
||||
request = request,
|
||||
response = response
|
||||
response = response,
|
||||
data = mapping.Data ?? new { }
|
||||
});
|
||||
}
|
||||
|
||||
private IBodyData? TransformBodyData(ITransformerContext transformerContext, ReplaceNodeOptions options, TransformModel model, IBodyData original, bool useTransformerForBodyAsFile)
|
||||
{
|
||||
return original.DetectedBodyType switch
|
||||
switch (original.DetectedBodyType)
|
||||
{
|
||||
BodyType.Json => TransformBodyAsJson(transformerContext, options, model, original),
|
||||
BodyType.File => TransformBodyAsFile(transformerContext, model, original, useTransformerForBodyAsFile),
|
||||
BodyType.String => TransformBodyAsString(transformerContext, model, original),
|
||||
_ => null
|
||||
};
|
||||
case BodyType.Json:
|
||||
return TransformBodyAsJson(transformerContext, options, model, original);
|
||||
|
||||
case BodyType.File:
|
||||
return TransformBodyAsFile(transformerContext, model, original, useTransformerForBodyAsFile);
|
||||
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
return TransformBodyAsString(transformerContext, model, original);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static IDictionary<string, WireMockList<string>> TransformHeaders(ITransformerContext transformerContext, TransformModel model, IDictionary<string, WireMockList<string>>? original)
|
||||
|
||||
@@ -49,12 +49,14 @@ internal static class BodyParser
|
||||
new WildcardMatcher("application/vnd.*+json", true)
|
||||
};
|
||||
|
||||
private static readonly IStringMatcher FormUrlEncodedMatcher = new WildcardMatcher("application/x-www-form-urlencoded", true);
|
||||
|
||||
private static readonly IStringMatcher[] TextContentTypeMatchers =
|
||||
{
|
||||
new WildcardMatcher("text/*", true),
|
||||
new RegexMatcher("^application\\/(java|type)script$", true),
|
||||
new WildcardMatcher("application/*xml", true),
|
||||
new WildcardMatcher("application/x-www-form-urlencoded", true)
|
||||
FormUrlEncodedMatcher
|
||||
};
|
||||
|
||||
public static bool ShouldParseBody(string? httpMethod, bool allowBodyForAllHttpMethods)
|
||||
@@ -69,7 +71,7 @@ internal static class BodyParser
|
||||
return true;
|
||||
}
|
||||
|
||||
if (BodyAllowedForMethods.TryGetValue(httpMethod!.ToUpper(), out bool allowed))
|
||||
if (BodyAllowedForMethods.TryGetValue(httpMethod!.ToUpper(), out var allowed))
|
||||
{
|
||||
return allowed;
|
||||
}
|
||||
@@ -88,6 +90,11 @@ internal static class BodyParser
|
||||
return BodyType.Bytes;
|
||||
}
|
||||
|
||||
if (MatchScores.IsPerfect(FormUrlEncodedMatcher.IsMatch(contentType.MediaType)))
|
||||
{
|
||||
return BodyType.FormUrlEncoded;
|
||||
}
|
||||
|
||||
if (TextContentTypeMatchers.Any(matcher => MatchScores.IsPerfect(matcher.IsMatch(contentType.MediaType))))
|
||||
{
|
||||
return BodyType.String;
|
||||
@@ -133,13 +140,30 @@ internal static class BodyParser
|
||||
return data;
|
||||
}
|
||||
|
||||
// Try to get the body as String or Json
|
||||
// Try to get the body as String, FormUrlEncoded or Json
|
||||
try
|
||||
{
|
||||
data.BodyAsString = DefaultEncoding.GetString(data.BodyAsBytes);
|
||||
data.Encoding = DefaultEncoding;
|
||||
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 (settings.DeserializeJson && !string.IsNullOrEmpty(data.BodyAsString))
|
||||
{
|
||||
|
||||
@@ -13,4 +13,6 @@ internal class BodyParserSettings
|
||||
public bool DecompressGZipAndDeflate { get; set; } = true;
|
||||
|
||||
public bool DeserializeJson { get; set; } = true;
|
||||
|
||||
public bool DeserializeFormUrlEncoded { get; set; } = true;
|
||||
}
|
||||
@@ -16,18 +16,14 @@ internal static class HttpStatusRangeParser
|
||||
/// <param name="pattern">The pattern. (Can be null, in that case it's allowed.)</param>
|
||||
/// <param name="httpStatusCode">The value.</param>
|
||||
/// <exception cref="ArgumentException"><paramref name="pattern"/> is invalid.</exception>
|
||||
public static bool IsMatch(string pattern, object? httpStatusCode)
|
||||
public static bool IsMatch(string? pattern, object? httpStatusCode)
|
||||
{
|
||||
switch (httpStatusCode)
|
||||
return httpStatusCode switch
|
||||
{
|
||||
case int statusCodeAsInteger:
|
||||
return IsMatch(pattern, statusCodeAsInteger);
|
||||
|
||||
case string statusCodeAsString:
|
||||
return IsMatch(pattern, int.Parse(statusCodeAsString));
|
||||
}
|
||||
|
||||
return false;
|
||||
int statusCodeAsInteger => IsMatch(pattern, statusCodeAsInteger),
|
||||
string statusCodeAsString => IsMatch(pattern, int.Parse(statusCodeAsString)),
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@@ -11,38 +9,6 @@ namespace WireMock.Util;
|
||||
|
||||
internal static class JsonUtils
|
||||
{
|
||||
public static Type CreateTypeFromJObject(JObject instance, string? fullName = null)
|
||||
{
|
||||
static Type ConvertType(JToken value, string? propertyName = null)
|
||||
{
|
||||
var type = value.Type;
|
||||
return type switch
|
||||
{
|
||||
JTokenType.Array => value.HasValues ? ConvertType(value.First!, propertyName).MakeArrayType() : typeof(object).MakeArrayType(),
|
||||
JTokenType.Boolean => typeof(bool),
|
||||
JTokenType.Bytes => typeof(byte[]),
|
||||
JTokenType.Date => typeof(DateTime),
|
||||
JTokenType.Guid => typeof(Guid),
|
||||
JTokenType.Float => typeof(float),
|
||||
JTokenType.Integer => typeof(long),
|
||||
JTokenType.Null => typeof(object),
|
||||
JTokenType.Object => CreateTypeFromJObject((JObject)value, propertyName),
|
||||
JTokenType.String => typeof(string),
|
||||
JTokenType.TimeSpan => typeof(TimeSpan),
|
||||
JTokenType.Uri => typeof(string),
|
||||
_ => typeof(object)
|
||||
};
|
||||
}
|
||||
|
||||
var properties = new Dictionary<string, Type>();
|
||||
foreach (var item in instance.Properties())
|
||||
{
|
||||
properties.Add(item.Name, ConvertType(item.Value, item.Name));
|
||||
}
|
||||
|
||||
return TypeBuilderUtils.BuildType(properties, fullName) ?? throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public static bool TryParseAsJObject(string? strInput, [NotNullWhen(true)] out JObject? value)
|
||||
{
|
||||
value = null;
|
||||
@@ -140,100 +106,4 @@ internal static class JsonUtils
|
||||
_ => throw new NotSupportedException($"Unable to convert value to {typeof(T)}.")
|
||||
};
|
||||
}
|
||||
|
||||
public static string GenerateDynamicLinqStatement(JToken jsonObject)
|
||||
{
|
||||
var lines = new List<string>();
|
||||
WalkNode(jsonObject, null, null, lines);
|
||||
|
||||
return lines.First();
|
||||
}
|
||||
|
||||
private static void WalkNode(JToken node, string? path, string? propertyName, List<string> lines)
|
||||
{
|
||||
switch (node.Type)
|
||||
{
|
||||
case JTokenType.Object:
|
||||
ProcessObject(node, propertyName, lines);
|
||||
break;
|
||||
|
||||
case JTokenType.Array:
|
||||
ProcessArray(node, propertyName, lines);
|
||||
break;
|
||||
|
||||
default:
|
||||
ProcessItem(node, path ?? "it", propertyName, lines);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ProcessObject(JToken node, string? propertyName, List<string> lines)
|
||||
{
|
||||
var items = new List<string>();
|
||||
var text = new StringBuilder("new (");
|
||||
|
||||
// In case of Object, loop all children. Do a ToArray() to avoid `Collection was modified` exceptions.
|
||||
foreach (var child in node.Children<JProperty>().ToArray())
|
||||
{
|
||||
WalkNode(child.Value, child.Path, child.Name, items);
|
||||
}
|
||||
|
||||
text.Append(string.Join(", ", items));
|
||||
text.Append(")");
|
||||
|
||||
if (!string.IsNullOrEmpty(propertyName))
|
||||
{
|
||||
text.AppendFormat(" as {0}", propertyName);
|
||||
}
|
||||
|
||||
lines.Add(text.ToString());
|
||||
}
|
||||
|
||||
private static void ProcessArray(JToken node, string? propertyName, List<string> lines)
|
||||
{
|
||||
var items = new List<string>();
|
||||
var text = new StringBuilder("(new [] { ");
|
||||
|
||||
// In case of Array, loop all items. Do a ToArray() to avoid `Collection was modified` exceptions.
|
||||
var idx = 0;
|
||||
foreach (var child in node.Children().ToArray())
|
||||
{
|
||||
WalkNode(child, $"{node.Path}[{idx}]", null, items);
|
||||
idx++;
|
||||
}
|
||||
|
||||
text.Append(string.Join(", ", items));
|
||||
text.Append("})");
|
||||
|
||||
if (!string.IsNullOrEmpty(propertyName))
|
||||
{
|
||||
text.AppendFormat(" as {0}", propertyName);
|
||||
}
|
||||
|
||||
lines.Add(text.ToString());
|
||||
}
|
||||
|
||||
private static void ProcessItem(JToken node, string path, string? propertyName, List<string> lines)
|
||||
{
|
||||
var castText = node.Type switch
|
||||
{
|
||||
JTokenType.Boolean => $"bool({path})",
|
||||
JTokenType.Date => $"DateTime({path})",
|
||||
JTokenType.Float => $"double({path})",
|
||||
JTokenType.Guid => $"Guid({path})",
|
||||
JTokenType.Integer => $"long({path})",
|
||||
JTokenType.Null => "null",
|
||||
JTokenType.String => $"string({path})",
|
||||
JTokenType.TimeSpan => $"TimeSpan({path})",
|
||||
JTokenType.Uri => $"Uri({path})",
|
||||
_ => throw new NotSupportedException($"JTokenType '{node.Type}' cannot be converted to a Dynamic Linq cast operator.")
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(propertyName))
|
||||
{
|
||||
castText += $" as {propertyName}";
|
||||
}
|
||||
|
||||
lines.Add(castText);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock.Util;
|
||||
@@ -13,6 +14,31 @@ internal static class QueryStringParser
|
||||
{
|
||||
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], WebUtility.UrlDecode(part[1]));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static IDictionary<string, WireMockList<string>> Parse(string? queryString, QueryParameterMultipleValueSupport? support = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(queryString))
|
||||
|
||||
@@ -36,14 +36,12 @@ internal static class RegexUtils
|
||||
{
|
||||
if (useRegexExtended)
|
||||
{
|
||||
var r = new RegexExtended(pattern, RegexOptions.None, RegexTimeOut);
|
||||
return (true, r.IsMatch(input));
|
||||
}
|
||||
else
|
||||
{
|
||||
var r = new Regex(pattern, RegexOptions.None, RegexTimeOut);
|
||||
return (true, r.IsMatch(input));
|
||||
var regexExtended = new RegexExtended(pattern, RegexOptions.None, RegexTimeOut);
|
||||
return (true, regexExtended.IsMatch(input));
|
||||
}
|
||||
|
||||
var regex = new Regex(pattern, RegexOptions.None, RegexTimeOut);
|
||||
return (true, regex.IsMatch(input));
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace WireMock.Util;
|
||||
|
||||
/// <summary>
|
||||
/// Code based on https://stackoverflow.com/questions/40507909/convert-jobject-to-anonymous-object
|
||||
/// </summary>
|
||||
internal static class TypeBuilderUtils
|
||||
{
|
||||
private static readonly ConcurrentDictionary<IDictionary<string, Type>, Type> Types = new();
|
||||
|
||||
private static readonly ModuleBuilder ModuleBuilder = AssemblyBuilder
|
||||
.DefineDynamicAssembly(new AssemblyName("WireMock.Net.Reflection"), AssemblyBuilderAccess.Run)
|
||||
.DefineDynamicModule("WireMock.Net.Reflection.Module");
|
||||
|
||||
public static Type BuildType(IDictionary<string, Type> properties, string? name = null)
|
||||
{
|
||||
var keyExists = Types.Keys.FirstOrDefault(k => Compare(k, properties));
|
||||
if (keyExists != null)
|
||||
{
|
||||
return Types[keyExists];
|
||||
}
|
||||
|
||||
var typeBuilder = GetTypeBuilder(name ?? Guid.NewGuid().ToString());
|
||||
foreach (var property in properties)
|
||||
{
|
||||
CreateGetSetMethods(typeBuilder, property.Key, property.Value);
|
||||
}
|
||||
|
||||
var type = typeBuilder.CreateTypeInfo()!.AsType();
|
||||
|
||||
Types.TryAdd(properties, type);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// https://stackoverflow.com/questions/3804367/testing-for-equality-between-dictionaries-in-c-sharp
|
||||
/// </summary>
|
||||
private static bool Compare<TKey, TValue>(IDictionary<TKey, TValue> dict1, IDictionary<TKey, TValue> dict2)
|
||||
where TKey : notnull
|
||||
{
|
||||
if (dict1 == dict2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dict1.Count != dict2.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var valueComparer = EqualityComparer<TValue>.Default;
|
||||
|
||||
foreach (var kvp in dict1)
|
||||
{
|
||||
if (!dict2.TryGetValue(kvp.Key, out var value2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!valueComparer.Equals(kvp.Value, value2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static TypeBuilder GetTypeBuilder(string name)
|
||||
{
|
||||
return ModuleBuilder.DefineType(name,
|
||||
TypeAttributes.Public |
|
||||
TypeAttributes.Class |
|
||||
TypeAttributes.AutoClass |
|
||||
TypeAttributes.AnsiClass |
|
||||
TypeAttributes.BeforeFieldInit |
|
||||
TypeAttributes.AutoLayout,
|
||||
null);
|
||||
}
|
||||
|
||||
private static void CreateGetSetMethods(TypeBuilder typeBuilder, string propertyName, Type propertyType)
|
||||
{
|
||||
var fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
|
||||
|
||||
var propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
|
||||
|
||||
var getPropertyMethodBuilder = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
|
||||
var getIl = getPropertyMethodBuilder.GetILGenerator();
|
||||
|
||||
getIl.Emit(OpCodes.Ldarg_0);
|
||||
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
|
||||
getIl.Emit(OpCodes.Ret);
|
||||
|
||||
var setPropertyMethodBuilder = typeBuilder.DefineMethod("set_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, null, new[] { propertyType });
|
||||
var setIl = setPropertyMethodBuilder.GetILGenerator();
|
||||
var modifyProperty = setIl.DefineLabel();
|
||||
|
||||
var exitSet = setIl.DefineLabel();
|
||||
|
||||
setIl.MarkLabel(modifyProperty);
|
||||
setIl.Emit(OpCodes.Ldarg_0);
|
||||
setIl.Emit(OpCodes.Ldarg_1);
|
||||
setIl.Emit(OpCodes.Stfld, fieldBuilder);
|
||||
|
||||
setIl.Emit(OpCodes.Nop);
|
||||
setIl.MarkLabel(exitSet);
|
||||
setIl.Emit(OpCodes.Ret);
|
||||
|
||||
propertyBuilder.SetGetMethod(getPropertyMethodBuilder);
|
||||
propertyBuilder.SetSetMethod(setPropertyMethodBuilder);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace WireMock.Validation
|
||||
{
|
||||
/// <summary>
|
||||
/// To fix 'xxx' is null on at least one execution path. See also https://rules.sonarsource.com/csharp/RSPEC-3900.
|
||||
/// </summary>
|
||||
internal class ValidatedNotNullAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -53,13 +53,12 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2022.3.1" PrivateAssets="All" />
|
||||
<PackageReference Include="JsonConverter.Abstractions" Version="0.3.0" />
|
||||
<PackageReference Include="JsonConverter.Abstractions" Version="0.4.0" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="NJsonSchema.Extensions" Version="0.1.0" />
|
||||
<PackageReference Include="NSwag.Core" Version="13.16.1" />
|
||||
<PackageReference Include="SimMetrics.Net" Version="1.0.5" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.23" />
|
||||
<PackageReference Include="JmesPath.Net" Version="1.0.125" />
|
||||
<PackageReference Include="AnyOf" Version="0.3.0" />
|
||||
<PackageReference Include="TinyMapper" Version="3.0.3" />
|
||||
@@ -177,13 +176,13 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Handlebars.Net.Helpers" Version="2.3.12" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.DynamicLinq" Version="2.3.12" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Humanizer" Version="2.3.12" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.3.12" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.3.12" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.XPath" Version="2.3.12" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.3.12" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers" Version="2.3.15" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.DynamicLinq" Version="2.3.15" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Humanizer" Version="2.3.15" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.3.15" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.3.15" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.XPath" Version="2.3.15" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.3.15" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NFluent;
|
||||
using WireMock.Matchers;
|
||||
@@ -37,7 +38,7 @@ public class JmesPathMatcherTests
|
||||
public void JmesPathMatcher_IsMatch_ByteArray()
|
||||
{
|
||||
// Assign
|
||||
var bytes = new byte[0];
|
||||
var bytes = EmptyArray<byte>.Value;
|
||||
var matcher = new JmesPathMatcher("");
|
||||
|
||||
// Act
|
||||
|
||||
@@ -111,7 +111,7 @@ public class JsonMatcherTests
|
||||
public void JsonMatcher_IsMatch_ByteArray()
|
||||
{
|
||||
// Assign
|
||||
var bytes = new byte[0];
|
||||
var bytes = EmptyArray<byte>.Value;
|
||||
var matcher = new JsonMatcher("");
|
||||
|
||||
// Act
|
||||
|
||||
@@ -89,7 +89,7 @@ public class JsonPartialMatcherTests
|
||||
public void JsonPartialMatcher_IsMatch_ByteArray()
|
||||
{
|
||||
// Assign
|
||||
var bytes = new byte[0];
|
||||
var bytes = EmptyArray<byte>.Value;
|
||||
var matcher = new JsonPartialMatcher("");
|
||||
|
||||
// Act
|
||||
|
||||
@@ -89,7 +89,7 @@ public class JsonPartialWildcardMatcherTests
|
||||
public void JsonPartialWildcardMatcher_IsMatch_ByteArray()
|
||||
{
|
||||
// Assign
|
||||
var bytes = new byte[0];
|
||||
var bytes = EmptyArray<byte>.Value;
|
||||
var matcher = new JsonPartialWildcardMatcher("");
|
||||
|
||||
// Act
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NFluent;
|
||||
using WireMock.Matchers;
|
||||
@@ -37,7 +38,7 @@ public class JsonPathMatcherTests
|
||||
public void JsonPathMatcher_IsMatch_ByteArray()
|
||||
{
|
||||
// Assign
|
||||
var bytes = new byte[0];
|
||||
var bytes = EmptyArray<byte>.Value;
|
||||
var matcher = new JsonPathMatcher("");
|
||||
|
||||
// Act
|
||||
|
||||
@@ -177,7 +177,7 @@ public class WireMockMiddlewareTests
|
||||
_mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder);
|
||||
_mappingMock.SetupGet(m => m.Settings).Returns(settings);
|
||||
|
||||
var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null);
|
||||
var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null, null);
|
||||
_mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy));
|
||||
|
||||
var requestBuilder = Request.Create().UsingAnyMethod();
|
||||
@@ -231,7 +231,7 @@ public class WireMockMiddlewareTests
|
||||
_mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder);
|
||||
_mappingMock.SetupGet(m => m.Settings).Returns(settings);
|
||||
|
||||
var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, "my-title", "my-description", null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null);
|
||||
var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, "my-title", "my-description", null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null, data: null);
|
||||
_mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy));
|
||||
|
||||
var requestBuilder = Request.Create().UsingAnyMethod();
|
||||
|
||||
@@ -431,7 +431,7 @@ public class RequestMessageBodyMatcherTests
|
||||
// JSON match +++
|
||||
{json, new RequestMessageBodyMatcher((object? o) => ((dynamic) o!).a == "b"), 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, new RequestMessageBodyMatcher((object? o) => false), false},
|
||||
@@ -442,7 +442,7 @@ public class RequestMessageBodyMatcherTests
|
||||
// string match +++
|
||||
{str, new RequestMessageBodyMatcher((object? o) => o == null), 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 ---
|
||||
{str, new RequestMessageBodyMatcher((object? o) => false), false},
|
||||
@@ -453,13 +453,13 @@ public class RequestMessageBodyMatcherTests
|
||||
// binary match +++
|
||||
{bytes, new RequestMessageBodyMatcher((object? o) => o == 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 ---
|
||||
{bytes, new RequestMessageBodyMatcher((object? o) => false), false},
|
||||
{bytes, new RequestMessageBodyMatcher((string? s) => false), false},
|
||||
{bytes, new RequestMessageBodyMatcher((byte[]? b) => false), false},
|
||||
{bytes, new RequestMessageBodyMatcher(), false },
|
||||
{bytes, new RequestMessageBodyMatcher(), false }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using FluentAssertions;
|
||||
using JsonConverter.Abstractions;
|
||||
using Moq;
|
||||
using Newtonsoft.Json;
|
||||
using NFluent;
|
||||
using WireMock.Matchers;
|
||||
@@ -55,11 +58,31 @@ namespace WireMock.Net.Tests
|
||||
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]
|
||||
public void Request_WithBody_FuncBodyData()
|
||||
{
|
||||
// Assign
|
||||
var requestBuilder = Request.Create().UsingAnyMethod().WithBody((IBodyData b) => b != null);
|
||||
var requestBuilder = Request.Create().UsingAnyMethod().WithBody((IBodyData? b) => b != null);
|
||||
|
||||
// Act
|
||||
var body = new BodyData
|
||||
@@ -78,7 +101,7 @@ namespace WireMock.Net.Tests
|
||||
public void Request_WithBody_FuncByteArray()
|
||||
{
|
||||
// Assign
|
||||
var requestBuilder = Request.Create().UsingAnyMethod().WithBody((byte[] b) => b != null);
|
||||
var requestBuilder = Request.Create().UsingAnyMethod().WithBody((byte[]? b) => b != null);
|
||||
|
||||
// Act
|
||||
var body = new BodyData
|
||||
@@ -113,7 +136,7 @@ namespace WireMock.Net.Tests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Request_BodyAsString_Using_WildcardMatcher()
|
||||
public void Request_WithBody_BodyDataAsString_Using_WildcardMatcher()
|
||||
{
|
||||
// Arrange
|
||||
var spec = Request.Create().WithPath("/foo").UsingAnyMethod().WithBody(new WildcardMatcher("H*o*"));
|
||||
@@ -132,7 +155,7 @@ namespace WireMock.Net.Tests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Request_BodyAsJson_Using_WildcardMatcher()
|
||||
public void Request_WithBody_BodyDataAsJson_Using_WildcardMatcher()
|
||||
{
|
||||
// Arrange
|
||||
var spec = Request.Create().WithPath("/foo").UsingAnyMethod().WithBody(new WildcardMatcher("*Hello*"));
|
||||
@@ -328,6 +351,56 @@ namespace WireMock.Net.Tests
|
||||
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Request_WithBodyAsJson_UsingObject()
|
||||
{
|
||||
// Assign
|
||||
object body = new
|
||||
{
|
||||
Test = "abc"
|
||||
};
|
||||
var requestBuilder = Request.Create().UsingAnyMethod().WithBodyAsJson(body);
|
||||
|
||||
var bodyData = new BodyData
|
||||
{
|
||||
BodyAsString = JsonConvert.SerializeObject(body),
|
||||
DetectedBodyType = BodyType.String
|
||||
};
|
||||
|
||||
// Act
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, bodyData);
|
||||
|
||||
// Assert
|
||||
var requestMatchResult = new RequestMatchResult();
|
||||
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Request_WithBodyAsJson_WithIJsonConverter_UsingObject()
|
||||
{
|
||||
// Assign
|
||||
var jsonConverterMock = new Mock<IJsonConverter>();
|
||||
jsonConverterMock.Setup(j => j.Serialize(It.IsAny<object>(), It.IsAny<JsonConverterOptions>())).Returns("test");
|
||||
object body = new
|
||||
{
|
||||
Any = "key"
|
||||
};
|
||||
var requestBuilder = Request.Create().UsingAnyMethod().WithBodyAsJson(body, jsonConverterMock.Object);
|
||||
|
||||
var bodyData = new BodyData
|
||||
{
|
||||
BodyAsString = "test",
|
||||
DetectedBodyType = BodyType.String
|
||||
};
|
||||
|
||||
// Act
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, bodyData);
|
||||
|
||||
// Assert
|
||||
var requestMatchResult = new RequestMatchResult();
|
||||
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(new byte[] { 1 }, BodyType.Bytes)]
|
||||
[InlineData(new byte[] { 48, 49, 50 }, BodyType.Bytes)]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -279,4 +280,38 @@ public class ResponseWithCallbackTests
|
||||
response.Message.BodyData.BodyAsString.Should().Be("/fooBar");
|
||||
response.Message.StatusCode.Should().Be(302);
|
||||
}
|
||||
|
||||
// https://github.com/WireMock-Net/WireMock.Net/issues/898
|
||||
[Fact]
|
||||
public async Task Response_WithCallback_WithRequestHeaders_Should_BeAccessibleInCallback()
|
||||
{
|
||||
// Assign
|
||||
var headerKey = "headerKey";
|
||||
var headerValues = new[] { "abc" };
|
||||
var requestHeaders = new Dictionary<string, string[]>
|
||||
{
|
||||
{ headerKey, headerValues }
|
||||
};
|
||||
var requestMessage = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1", null, requestHeaders);
|
||||
var responseBuilder = Response.Create()
|
||||
.WithCallback(request =>
|
||||
{
|
||||
var headersFromRequest = request!.Headers![headerKey].ToList();
|
||||
headersFromRequest.Add("extra");
|
||||
|
||||
return new ResponseMessage
|
||||
{
|
||||
Headers = new Dictionary<string, WireMockList<string>>
|
||||
{
|
||||
{ headerKey, new WireMockList<string>(headersFromRequest) }
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Act
|
||||
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, requestMessage, _settings).ConfigureAwait(false);
|
||||
|
||||
// Assert
|
||||
response.Message.Headers![headerKey].Should().Contain("extra");
|
||||
}
|
||||
}
|
||||
@@ -163,22 +163,6 @@ public class ResponseWithHandlebarsLinqTests
|
||||
Check.ThatAsyncCode(() => responseBuilder.ProvideResponseAsync(mappingMock.Object, request, _settings)).Throws<ArgumentException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Response_ProvideResponse_Handlebars_Linq1_Throws_ArgumentNullException()
|
||||
{
|
||||
// Assign
|
||||
var body = new BodyData();
|
||||
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost:1234"), "POST", "::1", body);
|
||||
|
||||
var responseBuilder = Response.Create()
|
||||
.WithBodyAsJson(new { x = "{{Linq request.body 'Name'}}" })
|
||||
.WithTransformer();
|
||||
|
||||
// Act
|
||||
Check.ThatAsyncCode(() => responseBuilder.ProvideResponseAsync(mappingMock.Object, request, _settings)).Throws<ArgumentNullException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Response_ProvideResponse_Handlebars_Linq1_Throws_HandlebarsException()
|
||||
{
|
||||
|
||||
@@ -782,4 +782,31 @@ public class ResponseWithTransformerTests
|
||||
response.Message.BodyData!.BodyAsString.Should().Be(text);
|
||||
response.Message.BodyData.Encoding.Should().Be(enc);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/wiremock-data/1", "one")]
|
||||
[InlineData("/wiremock-data/2", "two")]
|
||||
[InlineData("/wiremock-data/3", "N/A")]
|
||||
public async Task Response_ProvideResponse_Handlebars_DataObject(string path, string expected)
|
||||
{
|
||||
// Arrange
|
||||
var request = new RequestMessage(new UrlDetails("https://localhost" + path), "POST", ClientIp);
|
||||
var data = new Dictionary<string, object?>
|
||||
{
|
||||
{ "1", "one" },
|
||||
{ "2", "two" }
|
||||
};
|
||||
|
||||
var responseBuilder = Response.Create()
|
||||
.WithBody("{{lookup data request.PathSegments.[1] 'N/A'}}")
|
||||
.WithTransformer();
|
||||
|
||||
_mappingMock.SetupGet(m => m.Data).Returns(data);
|
||||
|
||||
// Act
|
||||
var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, request, _settings).ConfigureAwait(false);
|
||||
|
||||
// Assert
|
||||
response.Message.BodyData!.BodyAsString.Should().Be(expected);
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,25 @@ public partial class MappingConverterTests
|
||||
.WithDelay(12345)
|
||||
.WithTransformer();
|
||||
|
||||
return new Mapping(guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null);
|
||||
return new Mapping(
|
||||
guid,
|
||||
_updatedAt,
|
||||
string.Empty,
|
||||
string.Empty,
|
||||
null,
|
||||
_settings,
|
||||
request,
|
||||
response,
|
||||
42,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
null,
|
||||
data: null
|
||||
);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -57,7 +57,7 @@ public partial class MappingConverterTests
|
||||
}
|
||||
}
|
||||
};
|
||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, false, null);
|
||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, false, null, data: null);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
@@ -130,7 +130,7 @@ public partial class MappingConverterTests
|
||||
}
|
||||
}
|
||||
};
|
||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, true, null);
|
||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, true, null, data: null);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
@@ -168,7 +168,7 @@ public partial class MappingConverterTests
|
||||
var description = "my-description";
|
||||
var request = Request.Create();
|
||||
var response = Response.Create();
|
||||
var mapping = new Mapping(_guid, _updatedAt, title, description, null, _settings, request, response, 0, null, null, null, null, null, false, null);
|
||||
var mapping = new Mapping(_guid, _updatedAt, title, description, null, _settings, request, response, 0, null, null, null, null, null, false, null, data: null);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
@@ -188,7 +188,7 @@ public partial class MappingConverterTests
|
||||
// Assign
|
||||
var request = Request.Create();
|
||||
var response = Response.Create().WithBodyAsJson(new { x = "x" }).WithTransformer();
|
||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null);
|
||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
@@ -217,7 +217,7 @@ public partial class MappingConverterTests
|
||||
End = end,
|
||||
TTL = ttl
|
||||
};
|
||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, timeSettings);
|
||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, timeSettings, data: null);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
@@ -248,7 +248,7 @@ public partial class MappingConverterTests
|
||||
{
|
||||
var request = Request.Create();
|
||||
var response = Response.Create().WithDelay(test.Delay);
|
||||
var mapping = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, string.Empty, _settings, request, response, 42, null, null, null, null, null, false, null);
|
||||
var mapping = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, string.Empty, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
@@ -266,7 +266,7 @@ public partial class MappingConverterTests
|
||||
var delay = 1000;
|
||||
var request = Request.Create();
|
||||
var response = Response.Create().WithDelay(delay);
|
||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null);
|
||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
@@ -286,7 +286,7 @@ public partial class MappingConverterTests
|
||||
int minimumDelay = 1000;
|
||||
var request = Request.Create();
|
||||
var response = Response.Create().WithRandomDelay(minimumDelay);
|
||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null);
|
||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
@@ -309,7 +309,7 @@ public partial class MappingConverterTests
|
||||
int maximumDelay = 2000;
|
||||
var request = Request.Create();
|
||||
var response = Response.Create().WithRandomDelay(minimumDelay, maximumDelay);
|
||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null);
|
||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#if !(NET452 || NET461 || NETCOREAPP3_1)
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using VerifyTests;
|
||||
using VerifyXunit;
|
||||
@@ -70,7 +71,7 @@ public class ProxyMappingConverterTests
|
||||
var responseMessage = new ResponseMessage();
|
||||
|
||||
// Act
|
||||
var proxyMapping = _sut.ToMapping(mappingMock.Object, proxyAndRecordSettings, requestMessageMock.Object, responseMessage);
|
||||
var proxyMapping = _sut.ToMapping(mappingMock.Object, proxyAndRecordSettings, requestMessageMock.Object, responseMessage)!;
|
||||
|
||||
// Assert
|
||||
var model = _mappingConverter.ToMappingModel(proxyMapping);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using System.Collections.Generic;
|
||||
using FluentAssertions;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
using Xunit;
|
||||
@@ -8,6 +8,51 @@ namespace WireMock.Net.Tests.Util;
|
||||
|
||||
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]
|
||||
public void TryParse_Should_Parse_QueryStringWithUrlEncodedValues()
|
||||
{
|
||||
// Arrange
|
||||
var key = "x";
|
||||
var value = "rNaCP7hv8UOmS%2FJcujdvLw%3D%3D";
|
||||
|
||||
// Act
|
||||
var result = QueryStringParser.TryParse($"{key}={value}", true, out var actual);
|
||||
|
||||
// Assert
|
||||
result.Should().BeTrue();
|
||||
actual.Should().BeEquivalentTo(new Dictionary<string, string> { { "x", "rNaCP7hv8UOmS/JcujdvLw==" } });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_WithNullString()
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>../../src/WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
|
||||
<!--<DelaySign>true</DelaySign>-->
|
||||
|
||||
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
||||
|
||||
<!--https://developercommunity.visualstudio.com/content/problem/26347/unit-tests-fail-with-fileloadexception-newtonsoftj-1.html-->
|
||||
@@ -24,6 +24,10 @@
|
||||
<DefineConstants>NETFRAMEWORK</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Util\JsonUtilsTests.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- https://stackoverflow.com/questions/59406201/filenesting-not-working-for-class-or-shared-library-projects -->
|
||||
<ProjectCapability Include="ConfigurableFileNesting" />
|
||||
@@ -59,12 +63,13 @@
|
||||
</PackageReference>
|
||||
|
||||
<PackageReference Include="Moq" Version="4.17.2" />
|
||||
|
||||
<!--<PackageReference Include="System.Linq.Dynamic.Core" Version="1.3.1" />-->
|
||||
<PackageReference Include="System.Threading" Version="4.3.0" />
|
||||
<PackageReference Include="RestEase" Version="1.5.7" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
||||
<PackageReference Include="NFluent" Version="2.8.0" />
|
||||
<PackageReference Include="SimMetrics.Net" Version="1.0.5" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.23" />
|
||||
<PackageReference Include="AnyOf" Version="0.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -93,7 +98,8 @@
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' != 'net452'">
|
||||
<PackageReference Include="System.Net.Http.Json" Version="3.2.1" />
|
||||
<PackageReference Include="JsonConverter.System.Text.Json" Version="0.3.0" />
|
||||
<PackageReference Include="JsonConverter.System.Text.Json" Version="0.4.0" />
|
||||
<!--<PackageReference Include="JsonConverter.NewtonSoft.Json" Version="0.4.0" />-->
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -113,8 +119,8 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="client_cert.pfx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<DependentUpon>WireMockServerTests.ClientCertificate.cs</DependentUpon>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<DependentUpon>WireMockServerTests.ClientCertificate.cs</DependentUpon>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
@@ -123,7 +129,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Pact\files\" />
|
||||
<Folder Include="Pact\files\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -248,6 +248,45 @@ public class WireMockServerProxyTests
|
||||
fileSystemHandlerMock.Verify(f => f.WriteMappingFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WireMockServer_Proxy_With_DoNotSaveMappingForHttpMethod_Should_Not_SaveMapping()
|
||||
{
|
||||
// Assign
|
||||
var fileSystemHandlerMock = new Mock<IFileSystemHandler>();
|
||||
fileSystemHandlerMock.Setup(f => f.GetMappingFolder()).Returns("m");
|
||||
|
||||
var settings = new WireMockServerSettings
|
||||
{
|
||||
ProxyAndRecordSettings = new ProxyAndRecordSettings
|
||||
{
|
||||
Url = "http://www.google.com",
|
||||
SaveMapping = true,
|
||||
SaveMappingToFile = true,
|
||||
SaveMappingSettings = new ProxySaveMappingSettings
|
||||
{
|
||||
HttpMethods = new ProxySaveMappingSetting<string[]>(new string[] { "GET" }, MatchBehaviour.RejectOnMatch) // To make sure that we don't want this mapping
|
||||
}
|
||||
},
|
||||
FileSystemHandler = fileSystemHandlerMock.Object
|
||||
};
|
||||
var server = WireMockServer.Start(settings);
|
||||
|
||||
// Act
|
||||
var requestMessage = new HttpRequestMessage
|
||||
{
|
||||
Method = HttpMethod.Get,
|
||||
RequestUri = new Uri(server.Urls[0])
|
||||
};
|
||||
var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false };
|
||||
await new HttpClient(httpClientHandler).SendAsync(requestMessage).ConfigureAwait(false);
|
||||
|
||||
// Assert
|
||||
server.Mappings.Should().HaveCount(1);
|
||||
|
||||
// Verify
|
||||
fileSystemHandlerMock.Verify(f => f.WriteMappingFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WireMockServer_Proxy_Should_log_proxied_requests()
|
||||
{
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#if !NET452
|
||||
//using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
//using System.Net.Http.Json;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using WireMock.Matchers;
|
||||
@@ -10,61 +12,120 @@ using WireMock.ResponseBuilders;
|
||||
using WireMock.Server;
|
||||
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; }
|
||||
}
|
||||
public string? Hi { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WireMockServer_WithBodyAsJson_Using_PostAsJsonAsync_And_WildcardMatcher_ShouldMatch()
|
||||
[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
|
||||
{
|
||||
// Arrange
|
||||
var server = WireMockServer.Start();
|
||||
server.Given(
|
||||
Request.Create().UsingPost().WithPath("/foo").WithBody(new WildcardMatcher("*Hello*"))
|
||||
Hi = "Hello World!"
|
||||
};
|
||||
|
||||
// Act
|
||||
var response = await new HttpClient().PostAsJsonAsync("http://localhost:" + server.Ports[0] + "/foo", jsonObject).ConfigureAwait(false);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
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)
|
||||
);
|
||||
|
||||
// Act
|
||||
var response = await new HttpClient().PostAsync("http://localhost:" + server.Ports[0] + "/foo", new StringContent("{ Hi = \"Hello World\" }")).ConfigureAwait(false);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
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()
|
||||
);
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WireMockServer_WithBodyAsFormUrlEncoded_Using_PostAsync_And_WithExactMatcher()
|
||||
{
|
||||
// Arrange
|
||||
var server = WireMockServer.Start();
|
||||
server.Given(
|
||||
Request.Create()
|
||||
.UsingPost()
|
||||
.WithPath("/foo")
|
||||
.WithHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||
.WithBody(new ExactMatcher("name=John+Doe&email=johndoe%40example.com")
|
||||
)
|
||||
.RespondWith(
|
||||
Response.Create().WithStatusCode(200)
|
||||
);
|
||||
)
|
||||
.RespondWith(
|
||||
Response.Create()
|
||||
);
|
||||
|
||||
var jsonObject = new DummyClass
|
||||
{
|
||||
Hi = "Hello World!"
|
||||
};
|
||||
|
||||
// Act
|
||||
var response = await new HttpClient().PostAsJsonAsync("http://localhost:" + server.Ports[0] + "/foo", jsonObject).ConfigureAwait(false);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WireMockServer_WithBodyAsJson_Using_PostAsync_And_WildcardMatcher_ShouldMatch()
|
||||
// Act
|
||||
var content = new FormUrlEncodedContent(new[]
|
||||
{
|
||||
// Arrange
|
||||
var server = WireMockServer.Start();
|
||||
server.Given(
|
||||
Request.Create().UsingPost().WithPath("/foo").WithBody(new WildcardMatcher("*Hello*"))
|
||||
)
|
||||
.RespondWith(
|
||||
Response.Create().WithStatusCode(200)
|
||||
);
|
||||
new KeyValuePair<string, string>("name", "John Doe"),
|
||||
new KeyValuePair<string, string>("email", "johndoe@example.com")
|
||||
});
|
||||
var response = await new HttpClient()
|
||||
.PostAsync($"{server.Url}/foo", content)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// Act
|
||||
var response = await new HttpClient().PostAsync("http://localhost:" + server.Ports[0] + "/foo", new StringContent("{ Hi = \"Hello World\" }")).ConfigureAwait(false);
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
server.Stop();
|
||||
}
|
||||
server.Stop();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user