Compare commits

..

11 Commits

Author SHA1 Message Date
Stef Heyenrath
a25a8cabf8 1.5.37 2023-09-27 21:43:50 +02:00
Stef Heyenrath
9f9fc85a64 JmesPathMatcherTests (#998) 2023-09-27 21:42:34 +02:00
Stef Heyenrath
b63076a9ac Fix MappingModel to map IgnoreCase and RejectOnMatch for Headers, Cookies and Parameters (#1004) 2023-09-26 21:01:33 +02:00
Stef Heyenrath
05e2aa548b 1.5.36 2023-09-21 11:40:34 +02:00
Stef Heyenrath
b9a8ee4145 Fixed logic for SaveUnmatchedRequests (#1002)
* Fixed logic for SaveUnmatchedRequests

* fix
2023-09-21 11:38:01 +02:00
Stef Heyenrath
59aab9e1c3 Remove dependency on Microsoft.AspNet.WebApi.Client (#996)
* Using an alias for System.Net.Http.Formatting

* .

* fix

* space
2023-08-29 17:53:17 +02:00
Stef Heyenrath
93c87845c2 Write logging in case a Matcher throws an exception (#986)
* ThrowException

* ...

* .

* ...

* b

* fix test

* ...

* .

* sonar

* ft

* .

* fix tst
2023-08-21 20:07:46 +02:00
Stef Heyenrath
09a302baf2 1.5.35 2023-08-19 10:00:18 +02:00
Stef Heyenrath
f0139eb837 Update comment in JsonPathMatcher.cs 2023-08-17 17:22:05 +02:00
DayLightDancer
8adf34fb56 Update JSONPathMatcher.cs to cover the string path selection to a child (#993)
* Update JSONPathMatcher.cs to cover the string path selection to a child 

The .SelectToken method accept string path selection and JSONPath queries. The current code works only for the queries because the result is JObject. When the string path is selected the result is JValue and event with a valid result the code the code doesn't return valued result.
https://www.newtonsoft.com/json/help/html/SelectToken.htm

* Added unit tests

* Addressed the comments

* Addressed the comments

* Update JSONPathMatcher.cs
2023-08-17 17:18:32 +02:00
Stef Heyenrath
fd816f0952 Add extra unit test for WithParam multiple values comma (#992) 2023-08-12 21:05:13 +02:00
109 changed files with 1946 additions and 1316 deletions

View File

@@ -1,3 +1,19 @@
# 1.5.37 (27 September 2023)
- [#998](https://github.com/WireMock-Net/WireMock.Net/pull/998) - Add JmesPathMatcher UnitTests [test] contributed by [StefH](https://github.com/StefH)
- [#1004](https://github.com/WireMock-Net/WireMock.Net/pull/1004) - Fix MappingModel to map IgnoreCase and RejectOnMatch for Headers, Cookies and Parameters [bug] contributed by [StefH](https://github.com/StefH)
- [#1003](https://github.com/WireMock-Net/WireMock.Net/issues/1003) - Store Mapping per POST request ignores "IgnoreCase" property of HeaderModel [bug]
# 1.5.36 (21 September 2023)
- [#986](https://github.com/WireMock-Net/WireMock.Net/pull/986) - Write logging in case a Matcher throws an exception [feature] contributed by [StefH](https://github.com/StefH)
- [#996](https://github.com/WireMock-Net/WireMock.Net/pull/996) - Remove dependency on Microsoft.AspNet.WebApi.Client [feature] contributed by [StefH](https://github.com/StefH)
- [#1002](https://github.com/WireMock-Net/WireMock.Net/pull/1002) - Fixed logic for SaveUnmatchedRequests [bug] contributed by [StefH](https://github.com/StefH)
- [#974](https://github.com/WireMock-Net/WireMock.Net/issues/974) - HttpClient extension methods causes ambiguous invocations in .NET 7 [bug]
- [#1001](https://github.com/WireMock-Net/WireMock.Net/issues/1001) - SaveUnmatchedRequests stopped working [bug]
# 1.5.35 (19 August 2023)
- [#992](https://github.com/WireMock-Net/WireMock.Net/pull/992) - Add extra unit test for WithParam multiple values comma [test] contributed by [StefH](https://github.com/StefH)
- [#993](https://github.com/WireMock-Net/WireMock.Net/pull/993) - Update JSONPathMatcher.cs to cover the string path selection to a child contributed by [DayLightDancer](https://github.com/DayLightDancer)
# 1.5.34 (04 August 2023)
- [#989](https://github.com/WireMock-Net/WireMock.Net/pull/989) - Fix MimeKitLite NuGet include [bug] contributed by [StefH](https://github.com/StefH)
- [#988](https://github.com/WireMock-Net/WireMock.Net/issues/988) - v1.5.33 Returns always StatusCode 500 [bug]

View File

@@ -4,7 +4,7 @@
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>1.5.34</VersionPrefix>
<VersionPrefix>1.5.37</VersionPrefix>
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>

View File

@@ -1,6 +1,6 @@
rem https://github.com/StefH/GitHubReleaseNotes
SET version=1.5.34
SET version=1.5.37
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate --version %version% --token %GH_TOKEN%

View File

@@ -52,4 +52,4 @@ For more details see also [Docker](https://github.com/WireMock-Net/WireMock.Net-
More details on using HTTPS (SSL) can be found here [Wiki : HTTPS](https://github.com/WireMock-Net/WireMock.Net/wiki/Using-HTTPS-(SSL))
## :books: Documentation
For more info, see also this WIKI page: [What is WireMock.Net](https://github.com/WireMock-Net/WireMock.Net/wiki/What-Is-WireMock.Net).
For more info, see also this WIKI page: [What is WireMock.Net](https://github.com/WireMock-Net/WireMock.Net/wiki/What-Is-WireMock.Net).

View File

@@ -1,5 +1,6 @@
# 1.5.34 (04 August 2023)
- #989 Fix MimeKitLite NuGet include [bug]
- #988 v1.5.33 Returns always StatusCode 500 [bug]
# 1.5.37 (27 September 2023)
- #998 Add JmesPathMatcher UnitTests [test]
- #1004 Fix MappingModel to map IgnoreCase and RejectOnMatch for Headers, Cookies and Parameters [bug]
- #1003 Store Mapping per POST request ignores &quot;IgnoreCase&quot; property of HeaderModel [bug]
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md

View File

@@ -22,10 +22,6 @@
<Compile Remove="__admin\mappings\1.cs" />
</ItemGroup>
<ItemGroup>
<None Remove="__admin\mappings\array.json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
<PackageReference Include="log4net" Version="2.0.15" />
@@ -39,15 +35,6 @@
<None Update="nlog.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="__admin\mappings\791a3f31-6946-4ce7-8e6f-0237c7443275.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="__admin\mappings\791a3f31-6946-4ce7-8e6f-0237c7443275.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
<None Update="__admin\mappings\MyXmlResponse.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,48 @@
[
{
"Guid": "12343f31-6946-4ce7-8e6f-0237c7001000",
"Title": "1",
"Request": {
"Path": {
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "/mappings_static_1"
}
]
},
"Methods": [
"get"
]
},
"Response": {
"BodyAsJson": { "result": "mappings static_1" },
"Headers": {
"Content-Type": "application/json"
}
}
},
{
"Guid": "12343f31-6946-4ce7-8e6f-0237c7002000",
"Title": "2",
"Request": {
"Path": {
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "/mappings_static_2"
}
]
},
"Methods": [
"get"
]
},
"Response": {
"BodyAsJson": { "result": "mappings static_2" },
"Headers": {
"Content-Type": "application/json"
}
}
}
]

View File

@@ -137,6 +137,7 @@ namespace WireMock.Net.ConsoleApplication
Urls = new[] { url1, url2, url3 },
StartAdminInterface = true,
ReadStaticMappings = true,
SaveUnmatchedRequests = true,
WatchStaticMappings = true,
WatchStaticMappingsInSubdirectories = true,
//ProxyAndRecordSettings = new ProxyAndRecordSettings

View File

@@ -16,6 +16,11 @@ public class ParamModel
/// </summary>
public bool? IgnoreCase { get; set; }
/// <summary>
/// Gets or sets the Reject on match for the Param Name.
/// </summary>
public bool? RejectOnMatch { get; set; }
/// <summary>
/// Gets or sets the matchers.
/// </summary>

View File

@@ -1,21 +1,25 @@
using System;
using System;
namespace WireMock.Admin.Mappings
namespace WireMock.Admin.Mappings;
/// <summary>
/// Status
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class StatusModel
{
/// <summary>
/// Status
/// The optional guid.
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class StatusModel
{
/// <summary>
/// The optional guid.
/// </summary>
public Guid? Guid { get; set; }
public Guid? Guid { get; set; }
/// <summary>
/// The status (can also contain the error message).
/// </summary>
public string Status { get; set; }
}
/// <summary>
/// The status.
/// </summary>
public string? Status { get; set; }
/// <summary>
/// The error message.
/// </summary>
public string? Error { get; set; }
}

View File

@@ -62,11 +62,6 @@ public class SettingsModel
/// </summary>
public bool? HandleRequestsSynchronously { get; set; }
/// <summary>
/// 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 <c>true</c>).
/// </summary>

View File

@@ -1,121 +1,120 @@
using JetBrains.Annotations;
using System.Collections.Generic;
namespace WireMock.Handlers
namespace WireMock.Handlers;
/// <summary>
/// Handler to interact with the file system to handle folders and read and write (static mapping) files.
/// </summary>
public interface IFileSystemHandler
{
/// <summary>
/// Handler to interact with the file system to handle folders and read and write (static mapping) files.
/// Gets the folder where the static mappings are located. For local file system, this would be `{CurrentFolder}/__admin/mappings`.
/// </summary>
public interface IFileSystemHandler
{
/// <summary>
/// Gets the folder where the static mappings are located. For local file system, this would be `{CurrentFolder}/__admin/mappings`.
/// </summary>
/// <returns>The folder name.</returns>
string GetMappingFolder();
/// <returns>The folder name.</returns>
string GetMappingFolder();
/// <summary>
/// Determines whether the given path refers to an existing directory on disk.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>true if path refers to an existing directory; false if the directory does not exist or an error occurs when trying to determine if the specified directory exists.</returns>
bool FolderExists([NotNull] string path);
/// <summary>
/// Determines whether the given path refers to an existing directory on disk.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>true if path refers to an existing directory; false if the directory does not exist or an error occurs when trying to determine if the specified directory exists.</returns>
bool FolderExists([NotNull] string path);
/// <summary>
/// Creates all directories and subdirectories in the specified path unless they already exist.
/// </summary>
/// <param name="path">The path.</param>
void CreateFolder([NotNull] string path);
/// <summary>
/// Creates all directories and subdirectories in the specified path unless they already exist.
/// </summary>
/// <param name="path">The path.</param>
void CreateFolder([NotNull] string path);
/// <summary>
/// Returns an enumerable collection of file names in a specified path.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="includeSubdirectories">A value indicating whether subdirectories should also included when enumerating files.</param>
/// <returns>An enumerable collection of the full names (including paths) for the files in the directory (and optionally subdirectories) specified by path.</returns>
IEnumerable<string> EnumerateFiles([NotNull] string path, bool includeSubdirectories);
/// <summary>
/// Returns an enumerable collection of file names in a specified path.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="includeSubdirectories">A value indicating whether subdirectories should also included when enumerating files.</param>
/// <returns>An enumerable collection of the full names (including paths) for the files in the directory (and optionally subdirectories) specified by path.</returns>
IEnumerable<string> EnumerateFiles([NotNull] string path, bool includeSubdirectories);
/// <summary>
/// Read a static mapping file as text.
/// </summary>
/// <param name="path">The path (folder + filename with .json extension).</param>
/// <returns>The file content as text.</returns>
string ReadMappingFile([NotNull] string path);
/// <summary>
/// Read a static mapping file as text.
/// </summary>
/// <param name="path">The path (folder + filename with .json extension).</param>
/// <returns>The file content as text.</returns>
string ReadMappingFile([NotNull] string path);
/// <summary>
/// Write the static mapping file.
/// </summary>
/// <param name="path">The path (folder + filename with .json extension).</param>
/// <param name="text">The text.</param>
void WriteMappingFile([NotNull] string path, [NotNull] string text);
/// <summary>
/// Write the static mapping file.
/// </summary>
/// <param name="path">The path (folder + filename with .json extension).</param>
/// <param name="text">The text.</param>
void WriteMappingFile([NotNull] string path, [NotNull] string text);
/// <summary>
/// Read a response body file as byte[].
/// </summary>
/// <param name="path">The path or filename from the file to read.</param>
/// <returns>The file content as bytes.</returns>
byte[] ReadResponseBodyAsFile([NotNull] string path);
/// <summary>
/// Read a response body file as byte[].
/// </summary>
/// <param name="path">The path or filename from the file to read.</param>
/// <returns>The file content as bytes.</returns>
byte[] ReadResponseBodyAsFile([NotNull] string path);
/// <summary>
/// Read a response body file as text.
/// </summary>
/// <param name="path">The path or filename from the file to read.</param>
/// <returns>The file content as text.</returns>
string ReadResponseBodyAsString([NotNull] string path);
/// <summary>
/// Read a response body file as text.
/// </summary>
/// <param name="path">The path or filename from the file to read.</param>
/// <returns>The file content as text.</returns>
string ReadResponseBodyAsString([NotNull] string path);
/// <summary>
/// Delete a file.
/// </summary>
/// <param name="filename">The filename.</param>
void DeleteFile([NotNull] string filename);
/// <summary>
/// Delete a file.
/// </summary>
/// <param name="filename">The filename.</param>
void DeleteFile([NotNull] string filename);
/// <summary>
/// Determines whether the given path refers to an existing file on disk.
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns>true if path refers to an existing file; false if the file does not exist.</returns>
bool FileExists([NotNull] string filename);
/// <summary>
/// Determines whether the given path refers to an existing file on disk.
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns>true if path refers to an existing file; false if the file does not exist.</returns>
bool FileExists([NotNull] string filename);
/// <summary>
/// Write a file.
/// </summary>
/// <param name="filename">The filename.</param>
/// <param name="bytes">The bytes.</param>
void WriteFile([NotNull] string filename, [NotNull] byte[] bytes);
/// <summary>
/// Write a file.
/// </summary>
/// <param name="filename">The filename.</param>
/// <param name="bytes">The bytes.</param>
void WriteFile([NotNull] string filename, [NotNull] byte[] bytes);
/// <summary>
/// Write a file.
/// </summary>
/// <param name="folder">The folder.</param>
/// <param name="filename">The filename.</param>
/// <param name="bytes">The bytes.</param>
void WriteFile([NotNull] string folder, [NotNull] string filename, [NotNull] byte[] bytes);
/// <summary>
/// Write a file.
/// </summary>
/// <param name="folder">The folder.</param>
/// <param name="filename">The filename.</param>
/// <param name="bytes">The bytes.</param>
void WriteFile([NotNull] string folder, [NotNull] string filename, [NotNull] byte[] bytes);
/// <summary>
/// Read a file as bytes.
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns>The file content as bytes.</returns>
byte[] ReadFile([NotNull] string filename);
/// <summary>
/// Read a file as bytes.
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns>The file content as bytes.</returns>
byte[] ReadFile([NotNull] string filename);
/// <summary>
/// Read a file as string.
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns>The file content as a string.</returns>
string ReadFileAsString([NotNull] string filename);
/// <summary>
/// Read a file as string.
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns>The file content as a string.</returns>
string ReadFileAsString([NotNull] string filename);
/// <summary>
/// Gets the folder where the unmatched requests should be stored. For local file system, this would be `{CurrentFolder}/requests/unmatched`.
/// </summary>
/// <returns>The folder name.</returns>
string GetUnmatchedRequestsFolder();
/// <summary>
/// Gets the folder where the unmatched requests should be stored. For local file system, this would be `{CurrentFolder}/requests/unmatched`.
/// </summary>
/// <returns>The folder name.</returns>
string GetUnmatchedRequestsFolder();
/// <summary>
/// Write a unmatched request to the Unmatched RequestsFolder.
/// </summary>
/// <param name="filename">The filename.</param>
/// <param name="text">The text.</param>
void WriteUnmatchedRequest([NotNull] string filename, [NotNull] string text);
}
/// <summary>
/// Write a unmatched request to the Unmatched RequestsFolder.
/// </summary>
/// <param name="filename">The filename.</param>
/// <param name="text">The text.</param>
void WriteUnmatchedRequest([NotNull] string filename, [NotNull] string text);
}

View File

@@ -2,65 +2,63 @@ using System;
using JetBrains.Annotations;
using WireMock.Admin.Requests;
namespace WireMock.Logging
namespace WireMock.Logging;
/// <summary>
/// IWireMockLogger interface
/// </summary>
[PublicAPI]
public interface IWireMockLogger
{
/// <summary>
/// IWireMockLogger interface
/// Writes the message at the Debug level using the specified parameters.
/// </summary>
/// <param name="formatString">The format string.</param>
/// <param name="args">The arguments.</param>
[PublicAPI]
public interface IWireMockLogger
{
/// <summary>
/// Writes the message at the Debug level using the specified parameters.
/// </summary>
/// <param name="formatString">The format string.</param>
/// <param name="args">The arguments.</param>
[PublicAPI]
[StringFormatMethod("formatString")]
void Debug(string formatString, params object[] args);
[StringFormatMethod("formatString")]
void Debug(string formatString, params object[] args);
/// <summary>
/// Writes the message at the Info level using the specified parameters.
/// </summary>
/// <param name="formatString">The format string.</param>
/// <param name="args">The arguments.</param>
[PublicAPI]
[StringFormatMethod("formatString")]
void Info(string formatString, params object[] args);
/// <summary>
/// Writes the message at the Info level using the specified parameters.
/// </summary>
/// <param name="formatString">The format string.</param>
/// <param name="args">The arguments.</param>
[PublicAPI]
[StringFormatMethod("formatString")]
void Info(string formatString, params object[] args);
/// <summary>
/// Writes the message at the Warning level using the specified parameters.
/// </summary>
/// <param name="formatString">The format string.</param>
/// <param name="args">The arguments.</param>
[PublicAPI]
[StringFormatMethod("formatString")]
void Warn(string formatString, params object[] args);
/// <summary>
/// Writes the message at the Warning level using the specified parameters.
/// </summary>
/// <param name="formatString">The format string.</param>
/// <param name="args">The arguments.</param>
[PublicAPI]
[StringFormatMethod("formatString")]
void Warn(string formatString, params object[] args);
/// <summary>
/// Writes the message at the Error level using the specified parameters.
/// </summary>
/// <param name="formatString">The format string.</param>
/// <param name="args">The arguments.</param>
[PublicAPI]
[StringFormatMethod("formatString")]
void Error(string formatString, params object[] args);
/// <summary>
/// Writes the message at the Error level using the specified parameters.
/// </summary>
/// <param name="formatString">The format string.</param>
/// <param name="args">The arguments.</param>
[PublicAPI]
[StringFormatMethod("formatString")]
void Error(string formatString, params object[] args);
/// <summary>
/// Writes the message at the Error level using the specified exception.
/// </summary>
/// <param name="formatString">The format string.</param>
/// <param name="exception">The exception.</param>
[PublicAPI]
[StringFormatMethod("formatString")]
void Error(string formatString, Exception exception);
/// <summary>
/// Writes the message at the Error level using the specified exception.
/// </summary>
/// <param name="formatString">The format string.</param>
/// <param name="exception">The exception.</param>
[PublicAPI]
void Error(string formatString, Exception exception);
/// <summary>
/// Writes the LogEntryModel (LogRequestModel, LogResponseModel and more).
/// </summary>
/// <param name="logEntryModel">The Request Log Model.</param>
/// <param name="isAdminRequest">Defines if this request is an admin request.</param>
[PublicAPI]
void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest);
}
/// <summary>
/// Writes the LogEntryModel (LogRequestModel, LogResponseModel and more).
/// </summary>
/// <param name="logEntryModel">The Request Log Model.</param>
/// <param name="isAdminRequest">Defines if this request is an admin request.</param>
[PublicAPI]
void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest);
}

View File

@@ -1,56 +1,56 @@
using System;
using System.Collections.Generic;
namespace WireMock.Matchers.Request
namespace WireMock.Matchers.Request;
/// <summary>
/// IRequestMatchResult
/// </summary>
public interface IRequestMatchResult : IComparable
{
/// <summary>
/// IRequestMatchResult
/// Gets the match percentage.
/// </summary>
public interface IRequestMatchResult : IComparable
{
/// <summary>
/// Gets the match percentage.
/// </summary>
/// <value>
/// The match percentage.
/// </value>
double AverageTotalScore { get; }
/// <value>
/// The match percentage.
/// </value>
double AverageTotalScore { get; }
/// <summary>
/// Gets or sets a value indicating whether this instance is perfect match.
/// </summary>
/// <value>
/// <c>true</c> if this instance is perfect match; otherwise, <c>false</c>.
/// </value>
bool IsPerfectMatch { get; }
/// <summary>
/// Gets or sets a value indicating whether this instance is perfect match.
/// </summary>
/// <value>
/// <c>true</c> if this instance is perfect match; otherwise, <c>false</c>.
/// </value>
bool IsPerfectMatch { get; }
/// <summary>
/// Gets the match details.
/// </summary>
IList<MatchDetail> MatchDetails { get; }
/// <summary>
/// Gets the match details.
/// </summary>
IList<MatchDetail> MatchDetails { get; }
/// <summary>
/// Gets or sets the total number of matches.
/// </summary>
/// <value>
/// The total number of matches.
/// </value>
int TotalNumber { get; }
/// <summary>
/// Gets or sets the total number of matches.
/// </summary>
/// <value>
/// The total number of matches.
/// </value>
int TotalNumber { get; }
/// <summary>
/// Gets or sets the match-score.
/// </summary>
/// <value>
/// The match-score.
/// </value>
double TotalScore { get; }
/// <summary>
/// Gets or sets the match-score.
/// </summary>
/// <value>
/// The match-score.
/// </value>
double TotalScore { get; }
/// <summary>
/// Adds the score.
/// </summary>
/// <param name="matcherType">The matcher Type.</param>
/// <param name="score">The score.</param>
/// <returns>The score.</returns>
double AddScore(Type matcherType, double score);
}
/// <summary>
/// Adds the score.
/// </summary>
/// <param name="matcherType">The matcher Type.</param>
/// <param name="score">The score.</param>
/// <param name="exception">The exception [Optional].</param>
/// <returns>The score.</returns>
double AddScore(Type matcherType, double score, Exception? exception);
}

View File

@@ -1,20 +1,25 @@
using System;
using System;
namespace WireMock.Matchers.Request
namespace WireMock.Matchers.Request;
/// <summary>
/// MatchDetail
/// </summary>
public class MatchDetail
{
/// <summary>
/// MatchDetail
/// Gets or sets the type of the matcher.
/// </summary>
public class MatchDetail
{
/// <summary>
/// Gets or sets the type of the matcher.
/// </summary>
public Type MatcherType { get; set; }
public Type MatcherType { get; set; } = null!;
/// <summary>
/// Gets or sets the score between 0.0 and 1.0
/// </summary>
public double Score { get; set; }
}
/// <summary>
/// Gets or sets the score between 0.0 and 1.0
/// </summary>
public double Score { get; set; }
/// <summary>
/// The exception in case the Matcher throws exception.
/// [Optional]
/// </summary>
public Exception? Exception { get; set; }
}

View File

@@ -30,11 +30,9 @@ internal class CSharpCodeMatcher : ICSharpCodeMatcher
"Newtonsoft.Json.Linq"
};
/// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc cref="IMatcher.ThrowException"/>
public bool ThrowException { get; }
private readonly AnyOf<string, StringPattern>[] _patterns;
/// <summary>
@@ -55,37 +53,44 @@ internal class CSharpCodeMatcher : ICSharpCodeMatcher
{
_patterns = Guard.NotNull(patterns);
MatchBehaviour = matchBehaviour;
ThrowException = false;
MatchOperator = matchOperator;
}
public double IsMatch(string? input)
public MatchResult IsMatch(string? input)
{
return IsMatchInternal(input);
}
public double IsMatch(object? input)
public MatchResult IsMatch(object? input)
{
return IsMatchInternal(input);
}
public double IsMatchInternal(object? input)
public MatchResult IsMatchInternal(object? input)
{
double match = MatchScores.Mismatch;
var score = MatchScores.Mismatch;
Exception? exception = null;
if (input != null)
{
match = MatchScores.ToScore(_patterns.Select(pattern => IsMatch(input, pattern.GetPattern())).ToArray(), MatchOperator);
try
{
score = MatchScores.ToScore(_patterns.Select(pattern => IsMatch(input, pattern.GetPattern())).ToArray(), MatchOperator);
}
catch (Exception ex)
{
exception = ex;
}
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
}
private bool IsMatch(dynamic input, string pattern)
{
bool isMatchWithString = input is string;
var isMatchWithString = input is string;
var inputValue = isMatchWithString ? input : JObject.FromObject(input);
string source = GetSourceForIsMatchWithString(pattern, isMatchWithString);
var source = GetSourceForIsMatchWithString(pattern, isMatchWithString);
object? result;
@@ -155,7 +160,7 @@ internal class CSharpCodeMatcher : ICSharpCodeMatcher
}
#elif (NETSTANDARD2_0 || NETSTANDARD2_1 || NETCOREAPP3_1 || NET5_0 || NET6_0 || NET7_0)
Assembly assembly;
Assembly assembly;
try
{
assembly = CSScriptLib.CSScript.Evaluator.CompileCode(source);
@@ -198,10 +203,10 @@ internal class CSharpCodeMatcher : ICSharpCodeMatcher
private string GetSourceForIsMatchWithString(string pattern, bool isMatchWithString)
{
string template = isMatchWithString ? TemplateForIsMatchWithString : TemplateForIsMatchWithDynamic;
var template = isMatchWithString ? TemplateForIsMatchWithString : TemplateForIsMatchWithDynamic;
var stringBuilder = new StringBuilder();
foreach (string @using in _usings)
foreach (var @using in _usings)
{
stringBuilder.AppendLine($"using {@using};");
}
@@ -211,7 +216,7 @@ internal class CSharpCodeMatcher : ICSharpCodeMatcher
return stringBuilder.ToString();
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
/// <inheritdoc />
public AnyOf<string, StringPattern>[] GetPatterns()
{
return _patterns;
@@ -220,6 +225,6 @@ internal class CSharpCodeMatcher : ICSharpCodeMatcher
/// <inheritdoc />
public MatchOperator MatchOperator { get; }
/// <inheritdoc cref="IMatcher.Name"/>
public string Name => "CSharpCodeMatcher";
/// <inheritdoc />
public string Name => nameof(CSharpCodeMatcher);
}

View File

@@ -19,12 +19,15 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\WireMock.Net\Http\HttpClientFactory2.cs" Link="Http\HttpClientFactory2.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="IsExternalInit" Version="1.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" />
<PackageReference Include="Stef.Validation" Version="0.1.1" />
<PackageReference Include="Testcontainers" Version="3.2.0" />
<PackageReference Include="JetBrains.Annotations" VersionOverride="2022.3.1" PrivateAssets="All" Version="2022.3.1" />
@@ -33,4 +36,8 @@
<ItemGroup>
<ProjectReference Include="..\WireMock.Net.RestClient\WireMock.Net.RestClient.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Http\" />
</ItemGroup>
</Project>

View File

@@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging;
using RestEase;
using Stef.Validation;
using WireMock.Client;
using WireMock.Http;
namespace WireMock.Net.Testcontainers;
@@ -71,7 +72,7 @@ public sealed class WireMockContainer : DockerContainer
{
ValidateIfRunning();
var client = HttpClientFactory.Create(handlers);
var client = HttpClientFactory2.Create(handlers);
client.BaseAddress = GetPublicUri();
return client;
}
@@ -93,7 +94,7 @@ public sealed class WireMockContainer : DockerContainer
{
ValidateIfRunning();
var client = HttpClientFactory.Create(innerHandler, handlers);
var client = HttpClientFactory2.Create(innerHandler, handlers);
client.BaseAddress = GetPublicUri();
return client;
}

View File

@@ -34,8 +34,6 @@ internal class AzureADAuthenticationMatcher : IStringMatcher
public MatchBehaviour MatchBehaviour => MatchBehaviour.AcceptOnMatch;
public bool ThrowException => false;
public AnyOf<string, StringPattern>[] GetPatterns()
{
return EmptyArray<AnyOf<string, StringPattern>>.Value;
@@ -43,7 +41,7 @@ internal class AzureADAuthenticationMatcher : IStringMatcher
public MatchOperator MatchOperator { get; } = MatchOperator.Or;
public double IsMatch(string? input)
public MatchResult IsMatch(string? input)
{
if (string.IsNullOrEmpty(input))
{
@@ -70,9 +68,9 @@ internal class AzureADAuthenticationMatcher : IStringMatcher
return MatchScores.Perfect;
}
catch
catch (Exception ex)
{
return MatchScores.Mismatch;
return new MatchResult(MatchScores.Mismatch, ex);
}
}
}

View File

@@ -8,4 +8,6 @@ internal static class WireMockConstants
public const string ContentTypeJson = "application/json";
public const string ContentTypeTextPlain = "text/plain";
public const string NoMatchingFound = "No matching mapping found";
}

View File

@@ -0,0 +1,16 @@
using System;
namespace WireMock.Extensions;
internal static class ExceptionExtensions
{
public static Exception? ToException(this Exception[] exceptions)
{
return exceptions.Length switch
{
1 => exceptions[0],
> 1 => new AggregateException(exceptions),
_ => null
};
}
}

View File

@@ -82,7 +82,7 @@ public class LocalFileSystemHandler : IFileSystemHandler
public virtual byte[] ReadResponseBodyAsFile(string path)
{
Guard.NotNullOrEmpty(path);
path = PathUtils.CleanPath(path);
path = PathUtils.CleanPath(path)!;
// If the file exists at the given path relative to the MappingsFolder, then return that.
// Else the path will just be as-is.
return File.ReadAllBytes(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path);
@@ -92,7 +92,7 @@ public class LocalFileSystemHandler : IFileSystemHandler
public virtual string ReadResponseBodyAsString(string path)
{
Guard.NotNullOrEmpty(path);
path = PathUtils.CleanPath(path);
path = PathUtils.CleanPath(path)!;
// In case the path is a filename, the path will be adjusted to the MappingFolder.
// Else the path will just be as-is.
return File.ReadAllText(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path);

View File

@@ -1,3 +1,4 @@
using System.Linq;
using System.Net.Http;
namespace WireMock.Http;
@@ -6,19 +7,31 @@ internal static class HttpClientFactory2
{
public static HttpClient Create(params DelegatingHandler[] handlers)
{
#if NETSTANDARD1_3
return new HttpClient();
#else
return HttpClientFactory.Create(handlers);
#endif
var handler = CreateHandlerPipeline(new HttpClientHandler(), handlers);
return new HttpClient(handler);
}
public static HttpClient Create(HttpMessageHandler innerHandler, params DelegatingHandler[] handlers)
{
#if NETSTANDARD1_3
return new HttpClient(innerHandler);
#else
return HttpClientFactory.Create(innerHandler, handlers);
#endif
var handler = CreateHandlerPipeline(innerHandler, handlers);
return new HttpClient(handler);
}
private static HttpMessageHandler CreateHandlerPipeline(HttpMessageHandler handler, params DelegatingHandler[] delegatingHandlers)
{
if (delegatingHandlers.Length == 0)
{
return handler;
}
var next = handler;
foreach (var delegatingHandler in delegatingHandlers.Reverse())
{
delegatingHandler.InnerHandler = next;
next = delegatingHandler;
}
return next;
}
}

View File

@@ -139,13 +139,13 @@ public class Mapping : IMapping
Probability = probability;
}
/// <inheritdoc cref="IMapping.ProvideResponseAsync" />
/// <inheritdoc />
public Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage)
{
return Provider.ProvideResponseAsync(this, requestMessage, Settings);
}
/// <inheritdoc cref="IMapping.GetRequestMatchResult" />
/// <inheritdoc />
public IRequestMatchResult GetRequestMatchResult(IRequestMessage requestMessage, string? nextState)
{
var result = new RequestMatchResult();

View File

@@ -20,10 +20,8 @@ public abstract class AbstractJsonPartialMatcher : JsonMatcher
/// </summary>
/// <param name="value">The string value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="regex">Support Regex.</param>
protected AbstractJsonPartialMatcher(string value, bool ignoreCase = false, bool throwException = false, bool regex = false)
: base(value, ignoreCase, throwException)
protected AbstractJsonPartialMatcher(string value, bool ignoreCase = false, bool regex = false) : base(value, ignoreCase)
{
Regex = regex;
}
@@ -33,10 +31,8 @@ public abstract class AbstractJsonPartialMatcher : JsonMatcher
/// </summary>
/// <param name="value">The object value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="regex">Support Regex.</param>
protected AbstractJsonPartialMatcher(object value, bool ignoreCase = false, bool throwException = false, bool regex = false)
: base(value, ignoreCase, throwException)
protected AbstractJsonPartialMatcher(object value, bool ignoreCase = false, bool regex = false) : base(value, ignoreCase)
{
Regex = regex;
}
@@ -47,10 +43,8 @@ public abstract class AbstractJsonPartialMatcher : JsonMatcher
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="value">The value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="regex">Support Regex.</param>
protected AbstractJsonPartialMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool throwException = false, bool regex = false)
: base(matchBehaviour, value, ignoreCase, throwException)
protected AbstractJsonPartialMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false) : base(matchBehaviour, value, ignoreCase)
{
Regex = regex;
}
@@ -66,7 +60,7 @@ public abstract class AbstractJsonPartialMatcher : JsonMatcher
if (Regex && value.Type == JTokenType.String && input != null)
{
var valueAsString = value.ToString();
var (valid, result) = RegexUtils.MatchRegex(valueAsString, input.ToString());
if (valid)
{
@@ -75,10 +69,10 @@ public abstract class AbstractJsonPartialMatcher : JsonMatcher
}
if (input != null &&
((value.Type == JTokenType.Guid && input.Type == JTokenType.String) ||
(value.Type == JTokenType.String && input.Type == JTokenType.Guid)))
((value.Type == JTokenType.Guid && input.Type == JTokenType.String) ||
(value.Type == JTokenType.String && input.Type == JTokenType.Guid)))
{
return IsMatch(value.ToString(), input.ToString());
return IsMatch(value.ToString(), input.ToString());
}
if (input == null || value.Type != input.Type)

View File

@@ -46,15 +46,13 @@ public class ContentTypeMatcher : WildcardMatcher
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">IgnoreCase (default false)</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
public ContentTypeMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false, bool throwException = false) :
base(matchBehaviour, patterns, ignoreCase, throwException)
public ContentTypeMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false) : base(matchBehaviour, patterns, ignoreCase)
{
_patterns = patterns;
}
/// <inheritdoc cref="RegexMatcher.IsMatch"/>
public override double IsMatch(string? input)
/// <inheritdoc />
public override MatchResult IsMatch(string? input)
{
if (string.IsNullOrEmpty(input) || !MediaTypeHeaderValue.TryParse(input, out var contentType))
{
@@ -64,12 +62,12 @@ public class ContentTypeMatcher : WildcardMatcher
return base.IsMatch(contentType.MediaType);
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
/// <inheritdoc />
public override AnyOf<string, StringPattern>[] GetPatterns()
{
return _patterns;
}
/// <inheritdoc cref="IMatcher.Name"/>
public override string Name => "ContentTypeMatcher";
/// <inheritdoc />
public override string Name => nameof(ContentTypeMatcher);
}

View File

@@ -14,17 +14,14 @@ public class ExactMatcher : IStringMatcher, IIgnoreCaseMatcher
{
private readonly AnyOf<string, StringPattern>[] _values;
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
/// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc cref="IMatcher.ThrowException"/>
public bool ThrowException { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ExactMatcher"/> class.
/// </summary>
/// <param name="values">The values.</param>
public ExactMatcher(params AnyOf<string, StringPattern>[] values) : this(MatchBehaviour.AcceptOnMatch, false, false, MatchOperator.Or, values)
public ExactMatcher(params AnyOf<string, StringPattern>[] values) : this(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, values)
{
}
@@ -33,7 +30,7 @@ public class ExactMatcher : IStringMatcher, IIgnoreCaseMatcher
/// </summary>
/// <param name="ignoreCase">Ignore the case from the pattern(s).</param>
/// <param name="values">The values.</param>
public ExactMatcher(bool ignoreCase, params AnyOf<string, StringPattern>[] values) : this(MatchBehaviour.AcceptOnMatch, ignoreCase, false, MatchOperator.Or, values)
public ExactMatcher(bool ignoreCase, params AnyOf<string, StringPattern>[] values) : this(MatchBehaviour.AcceptOnMatch, ignoreCase, MatchOperator.Or, values)
{
}
@@ -42,36 +39,33 @@ public class ExactMatcher : IStringMatcher, IIgnoreCaseMatcher
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="ignoreCase">Ignore the case from the pattern(s).</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
/// <param name="values">The values.</param>
public ExactMatcher(
MatchBehaviour matchBehaviour,
bool ignoreCase = false,
bool throwException = false,
MatchOperator matchOperator = MatchOperator.Or,
params AnyOf<string, StringPattern>[] values)
{
_values = Guard.NotNull(values);
MatchBehaviour = matchBehaviour;
ThrowException = throwException;
IgnoreCase = ignoreCase;
MatchOperator = matchOperator;
}
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
public double IsMatch(string? input)
/// <inheritdoc />
public MatchResult IsMatch(string? input)
{
Func<string?, bool> equals = IgnoreCase
? pattern => string.Equals(pattern, input, StringComparison.OrdinalIgnoreCase)
: pattern => pattern == input;
double score = MatchScores.ToScore(_values.Select(v => equals(v)).ToArray(), MatchOperator);
return MatchBehaviourHelper.Convert(MatchBehaviour, score);
var score = MatchScores.ToScore(_values.Select(v => equals(v)).ToArray(), MatchOperator);
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score));
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
/// <inheritdoc />
public AnyOf<string, StringPattern>[] GetPatterns()
{
return _values;
@@ -80,7 +74,7 @@ public class ExactMatcher : IStringMatcher, IIgnoreCaseMatcher
/// <inheritdoc />
public MatchOperator MatchOperator { get; }
/// <inheritdoc cref="IMatcher.Name"/>
/// <inheritdoc />
public string Name => "ExactMatcher";
/// <inheritdoc />

View File

@@ -19,12 +19,9 @@ public class ExactObjectMatcher : IObjectMatcher
/// </summary>
public byte[]? ValueAsBytes { get; }
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
/// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc cref="IMatcher.ThrowException"/>
public bool ThrowException { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ExactObjectMatcher"/> class.
/// </summary>
@@ -56,17 +53,15 @@ public class ExactObjectMatcher : IObjectMatcher
/// Initializes a new instance of the <see cref="ExactObjectMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="value">The value.</param>
public ExactObjectMatcher(MatchBehaviour matchBehaviour, byte[] value, bool throwException = false)
public ExactObjectMatcher(MatchBehaviour matchBehaviour, byte[] value)
{
ValueAsBytes = Guard.NotNull(value);
MatchBehaviour = matchBehaviour;
ThrowException = throwException;
}
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
public double IsMatch(object? input)
/// <inheritdoc />
public MatchResult IsMatch(object? input)
{
bool equals = false;
if (ValueAsObject != null)
@@ -81,6 +76,6 @@ public class ExactObjectMatcher : IObjectMatcher
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(equals));
}
/// <inheritdoc cref="IMatcher.Name"/>
/// <inheritdoc />
public string Name => "ExactObjectMatcher";
}

View File

@@ -1,12 +1,14 @@
#if GRAPHQL
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using AnyOfTypes;
using GraphQL;
using GraphQL.Types;
using Newtonsoft.Json;
using Stef.Validation;
using WireMock.Extensions;
using WireMock.Models;
namespace WireMock.Matchers;
@@ -19,8 +21,10 @@ public class GraphQLMatcher : IStringMatcher
{
private sealed class GraphQLRequest
{
// ReSharper disable once UnusedAutoPropertyAccessor.Local
public string? Query { get; set; }
// ReSharper disable once UnusedAutoPropertyAccessor.Local
public Dictionary<string, object?>? Variables { get; set; }
}
@@ -31,21 +35,16 @@ public class GraphQLMatcher : IStringMatcher
/// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc />
public bool ThrowException { get; }
/// <summary>
/// Initializes a new instance of the <see cref="LinqMatcher"/> class.
/// </summary>
/// <param name="schema">The schema.</param>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
public GraphQLMatcher(AnyOf<string, StringPattern, ISchema> schema, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch, bool throwException = false, MatchOperator matchOperator = MatchOperator.Or)
public GraphQLMatcher(AnyOf<string, StringPattern, ISchema> schema, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch, MatchOperator matchOperator = MatchOperator.Or)
{
Guard.NotNull(schema);
MatchBehaviour = matchBehaviour;
ThrowException = throwException;
MatchOperator = matchOperator;
var patterns = new List<AnyOf<string, StringPattern>>();
@@ -72,51 +71,44 @@ public class GraphQLMatcher : IStringMatcher
}
/// <inheritdoc />
public double IsMatch(string? input)
public MatchResult IsMatch(string? input)
{
var match = MatchScores.Mismatch;
var score = MatchScores.Mismatch;
Exception? exception = null;
try
if (input != null && TryGetGraphQLRequest(input, out var graphQLRequest))
{
var graphQLRequest = JsonConvert.DeserializeObject<GraphQLRequest>(input!)!;
var executionResult = new DocumentExecuter().ExecuteAsync(_ =>
try
{
_.ThrowOnUnhandledException = true;
_.Schema = _schema;
_.Query = graphQLRequest.Query;
if (graphQLRequest.Variables != null)
var executionResult = new DocumentExecuter().ExecuteAsync(_ =>
{
_.Variables = new Inputs(graphQLRequest.Variables);
}
}).GetAwaiter().GetResult();
_.ThrowOnUnhandledException = true;
if (executionResult.Errors == null || executionResult.Errors.Count == 0)
{
match = MatchScores.Perfect;
}
else
{
var exceptions = executionResult.Errors.OfType<Exception>().ToArray();
if (exceptions.Length == 1)
_.Schema = _schema;
_.Query = graphQLRequest.Query;
if (graphQLRequest.Variables != null)
{
_.Variables = new Inputs(graphQLRequest.Variables);
}
}).GetAwaiter().GetResult();
if (executionResult.Errors == null || executionResult.Errors.Count == 0)
{
throw exceptions[0];
score = MatchScores.Perfect;
}
else
{
exception = executionResult.Errors.OfType<Exception>().ToArray().ToException();
}
throw new AggregateException(exceptions);
}
}
catch
{
if (ThrowException)
catch (Exception ex)
{
throw;
exception = ex;
}
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
}
/// <inheritdoc />
@@ -131,6 +123,20 @@ public class GraphQLMatcher : IStringMatcher
/// <inheritdoc />
public string Name => nameof(GraphQLMatcher);
private static bool TryGetGraphQLRequest(string input, [NotNullWhen(true)] out GraphQLRequest? graphQLRequest)
{
try
{
graphQLRequest = JsonConvert.DeserializeObject<GraphQLRequest>(input);
return graphQLRequest != null;
}
catch
{
graphQLRequest = default;
return false;
}
}
private static ISchema BuildSchema(string schema)
{
return Schema.For(schema);

View File

@@ -6,7 +6,7 @@ namespace WireMock.Matchers.Helpers;
internal static class BodyDataMatchScoreCalculator
{
public static double CalculateMatchScore(IBodyData? requestMessage, IMatcher matcher)
public static MatchResult CalculateMatchScore(IBodyData? requestMessage, IMatcher matcher)
{
Guard.NotNull(matcher);
@@ -23,7 +23,7 @@ internal static class BodyDataMatchScoreCalculator
return notNullOrEmptyMatcher.IsMatch(requestMessage.BodyAsBytes);
default:
return MatchScores.Mismatch;
return default;
}
}
@@ -63,16 +63,6 @@ internal static class BodyDataMatchScoreCalculator
}
}
#if MIMEKIT_XXX
if (matcher is MultiPartMatcher multiPartMatcher)
{
// If the body is a String or MultiPart, use the BodyAsString to match on.
if (requestMessage?.DetectedBodyType is BodyType.String or BodyType.MultiPart)
{
return multiPartMatcher.IsMatch(requestMessage.BodyAsString);
}
}
#endif
return MatchScores.Mismatch;
return default;
}
}

View File

@@ -14,9 +14,4 @@ public interface IMatcher
/// Gets the match behaviour.
/// </summary>
MatchBehaviour MatchBehaviour { get; }
/// <summary>
/// Should this matcher throw an exception?
/// </summary>
bool ThrowException { get; }
}

View File

@@ -9,6 +9,6 @@ public interface IObjectMatcher : IMatcher
/// Determines whether the specified input is match.
/// </summary>
/// <param name="input">The input.</param>
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
double IsMatch(object? input);
/// <returns>MatchResult</returns>
MatchResult IsMatch(object? input);
}

View File

@@ -13,8 +13,8 @@ public interface IStringMatcher : IMatcher
/// Determines whether the specified input is match.
/// </summary>
/// <param name="input">The input.</param>
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
double IsMatch(string? input);
/// <returns>MatchResult</returns>
MatchResult IsMatch(string? input);
/// <summary>
/// Gets the patterns.

View File

@@ -1,6 +1,6 @@
using System;
using System.Linq;
using AnyOfTypes;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Stef.Validation;
using WireMock.Extensions;
@@ -17,17 +17,15 @@ public class JsonPathMatcher : IStringMatcher, IObjectMatcher
{
private readonly AnyOf<string, StringPattern>[] _patterns;
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
/// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc cref="IMatcher.ThrowException"/>
public bool ThrowException { get; }
/// <summary>
/// Initializes a new instance of the <see cref="JsonPathMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public JsonPathMatcher(params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, patterns.ToAnyOfPatterns())
public JsonPathMatcher(params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or,
patterns.ToAnyOfPatterns())
{
}
@@ -35,7 +33,8 @@ public class JsonPathMatcher : IStringMatcher, IObjectMatcher
/// Initializes a new instance of the <see cref="JsonPathMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public JsonPathMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, patterns)
public JsonPathMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch,
MatchOperator.Or, patterns)
{
}
@@ -43,48 +42,45 @@ public class JsonPathMatcher : IStringMatcher, IObjectMatcher
/// Initializes a new instance of the <see cref="JsonPathMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
/// <param name="patterns">The patterns.</param>
public JsonPathMatcher(
MatchBehaviour matchBehaviour,
bool throwException = false,
MatchOperator matchOperator = MatchOperator.Or,
params AnyOf<string, StringPattern>[] patterns)
{
_patterns = Guard.NotNull(patterns);
MatchBehaviour = matchBehaviour;
ThrowException = throwException;
MatchOperator = matchOperator;
}
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
public double IsMatch(string? input)
/// <inheritdoc />
public MatchResult IsMatch(string? input)
{
double match = MatchScores.Mismatch;
var score = MatchScores.Mismatch;
Exception? exception = null;
if (input != null)
{
try
{
var jToken = JToken.Parse(input);
match = IsMatch(jToken);
score = IsMatch(jToken);
}
catch (JsonException)
catch (Exception ex)
{
if (ThrowException)
{
throw;
}
exception = ex;
}
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
}
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
public double IsMatch(object? input)
/// <inheritdoc />
public MatchResult IsMatch(object? input)
{
double match = MatchScores.Mismatch;
var score = MatchScores.Mismatch;
Exception? exception = null;
// When input is null or byte[], return Mismatch.
if (input != null && !(input is byte[]))
@@ -93,18 +89,15 @@ public class JsonPathMatcher : IStringMatcher, IObjectMatcher
{
// Check if JToken or object
JToken jToken = input as JToken ?? JObject.FromObject(input);
match = IsMatch(jToken);
score = IsMatch(jToken);
}
catch (JsonException)
catch (Exception ex)
{
if (ThrowException)
{
throw;
}
exception = ex;
}
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
}
/// <inheritdoc />
@@ -116,14 +109,18 @@ public class JsonPathMatcher : IStringMatcher, IObjectMatcher
/// <inheritdoc />
public MatchOperator MatchOperator { get; }
/// <inheritdoc cref="IMatcher.Name"/>
/// <inheritdoc />
public string Name => "JsonPathMatcher";
private double IsMatch(JToken jToken)
{
var array = ConvertJTokenToJArrayIfNeeded(jToken);
return MatchScores.ToScore(_patterns.Select(pattern => array.SelectToken(pattern.GetPattern())?.Any() == true).ToArray(), MatchOperator);
// The SelectToken method can accept a string path to a child token ( i.e. "Manufacturers[0].Products[0].Price").
// In that case it will return a JValue (some type) which does not implement the IEnumerable interface.
var values = _patterns.Select(pattern => array.SelectToken(pattern.GetPattern()) != null).ToArray();
return MatchScores.ToScore(values, MatchOperator);
}
// https://github.com/WireMock-Net/WireMock.Net/issues/965
@@ -138,9 +135,9 @@ public class JsonPathMatcher : IStringMatcher, IObjectMatcher
var item = property.First();
if (item is JArray)
{
return jToken;
return jToken;
}
return new JObject
{
[property.Path] = new JArray(item)

View File

@@ -1,3 +1,4 @@
using System;
using System.Linq;
using AnyOfTypes;
using DevLab.JmesPath;
@@ -15,17 +16,14 @@ public class JmesPathMatcher : IStringMatcher, IObjectMatcher
{
private readonly AnyOf<string, StringPattern>[] _patterns;
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
/// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc cref="IMatcher.ThrowException"/>
public bool ThrowException { get; }
/// <summary>
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public JmesPathMatcher(params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, patterns.ToAnyOfPatterns())
public JmesPathMatcher(params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, patterns.ToAnyOfPatterns())
{
}
@@ -33,18 +31,17 @@ public class JmesPathMatcher : IStringMatcher, IObjectMatcher
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public JmesPathMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, patterns)
public JmesPathMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, patterns)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use.</param>
/// <param name="patterns">The patterns.</param>
public JmesPathMatcher(bool throwException = false, MatchOperator matchOperator = MatchOperator.Or, params AnyOf<string, StringPattern>[] patterns) :
this(MatchBehaviour.AcceptOnMatch, throwException, matchOperator, patterns)
public JmesPathMatcher(MatchOperator matchOperator = MatchOperator.Or, params AnyOf<string, StringPattern>[] patterns) :
this(MatchBehaviour.AcceptOnMatch, matchOperator, patterns)
{
}
@@ -52,60 +49,56 @@ public class JmesPathMatcher : IStringMatcher, IObjectMatcher
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use.</param>
/// <param name="patterns">The patterns.</param>
public JmesPathMatcher(
MatchBehaviour matchBehaviour,
bool throwException = false,
MatchOperator matchOperator = MatchOperator.Or,
params AnyOf<string, StringPattern>[] patterns)
{
_patterns = Guard.NotNull(patterns);
MatchBehaviour = matchBehaviour;
ThrowException = throwException;
MatchOperator = matchOperator;
}
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
public double IsMatch(string? input)
/// <inheritdoc />
public MatchResult IsMatch(string? input)
{
double match = MatchScores.Mismatch;
var score = MatchScores.Mismatch;
Exception? exception = null;
if (input != null)
{
try
{
var results = _patterns.Select(pattern => bool.Parse(new JmesPath().Transform(input, pattern.GetPattern()))).ToArray();
match = MatchScores.ToScore(results, MatchOperator);
score = MatchScores.ToScore(results, MatchOperator);
}
catch (JsonException)
catch (Exception ex)
{
if (ThrowException)
{
throw;
}
exception = ex;
}
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
}
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
public double IsMatch(object? input)
/// <inheritdoc />
public MatchResult IsMatch(object? input)
{
double match = MatchScores.Mismatch;
var score = MatchScores.Mismatch;
// When input is null or byte[], return Mismatch.
if (input != null && !(input is byte[]))
{
string inputAsString = JsonConvert.SerializeObject(input);
var inputAsString = JsonConvert.SerializeObject(input);
return IsMatch(inputAsString);
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
return MatchBehaviourHelper.Convert(MatchBehaviour, score);
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
/// <inheritdoc />
public AnyOf<string, StringPattern>[] GetPatterns()
{
return _patterns;
@@ -114,6 +107,6 @@ public class JmesPathMatcher : IStringMatcher, IObjectMatcher
/// <inheritdoc />
public MatchOperator MatchOperator { get; }
/// <inheritdoc cref="IMatcher.Name"/>
public string Name => "JmesPathMatcher";
/// <inheritdoc />
public string Name => nameof(JmesPathMatcher);
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Stef.Validation;
using WireMock.Util;
@@ -13,21 +12,18 @@ namespace WireMock.Matchers;
/// </summary>
public class JsonMatcher : IValueMatcher, IIgnoreCaseMatcher
{
/// <inheritdoc cref="IMatcher.Name"/>
/// <inheritdoc />
public virtual string Name => "JsonMatcher";
/// <inheritdoc cref="IValueMatcher.Value"/>
public object Value { get; }
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
/// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc cref="IIgnoreCaseMatcher.IgnoreCase"/>
public bool IgnoreCase { get; }
/// <inheritdoc cref="IMatcher.ThrowException"/>
public bool ThrowException { get; }
private readonly JToken _valueAsJToken;
private readonly Func<JToken, JToken> _jTokenConverter;
@@ -36,8 +32,7 @@ public class JsonMatcher : IValueMatcher, IIgnoreCaseMatcher
/// </summary>
/// <param name="value">The string value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
public JsonMatcher(string value, bool ignoreCase = false, bool throwException = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, throwException)
public JsonMatcher(string value, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase)
{
}
@@ -46,8 +41,7 @@ public class JsonMatcher : IValueMatcher, IIgnoreCaseMatcher
/// </summary>
/// <param name="value">The object value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
public JsonMatcher(object value, bool ignoreCase = false, bool throwException = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, throwException)
public JsonMatcher(object value, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase)
{
}
@@ -57,24 +51,23 @@ public class JsonMatcher : IValueMatcher, IIgnoreCaseMatcher
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="value">The value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
public JsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool throwException = false)
public JsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false)
{
Guard.NotNull(value, nameof(value));
MatchBehaviour = matchBehaviour;
IgnoreCase = ignoreCase;
ThrowException = throwException;
Value = value;
_valueAsJToken = ConvertValueToJToken(value);
_jTokenConverter = ignoreCase ? Rename : jToken => jToken;
}
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
public double IsMatch(object? input)
/// <inheritdoc />
public MatchResult IsMatch(object? input)
{
bool match = false;
var score = MatchScores.Mismatch;
Exception? error = null;
// When input is null or byte[], return Mismatch.
if (input != null && input is not byte[])
@@ -83,20 +76,16 @@ public class JsonMatcher : IValueMatcher, IIgnoreCaseMatcher
{
var inputAsJToken = ConvertValueToJToken(input);
match = IsMatch(
_jTokenConverter(_valueAsJToken),
_jTokenConverter(inputAsJToken));
var match = IsMatch(_jTokenConverter(_valueAsJToken), _jTokenConverter(inputAsJToken));
score = MatchScores.ToScore(match);
}
catch (JsonException)
catch (Exception ex)
{
if (ThrowException)
{
throw;
}
error = ex;
}
}
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(match));
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), error);
}
/// <summary>

View File

@@ -9,27 +9,27 @@ public class JsonPartialMatcher : AbstractJsonPartialMatcher
public override string Name => nameof(JsonPartialMatcher);
/// <inheritdoc />
public JsonPartialMatcher(string value, bool ignoreCase = false, bool throwException = false, bool regex = false)
: base(value, ignoreCase, throwException, regex)
public JsonPartialMatcher(string value, bool ignoreCase = false, bool regex = false)
: base(value, ignoreCase, regex)
{
}
/// <inheritdoc />
public JsonPartialMatcher(object value, bool ignoreCase = false, bool throwException = false, bool regex = false)
: base(value, ignoreCase, throwException, regex)
public JsonPartialMatcher(object value, bool ignoreCase = false, bool regex = false)
: base(value, ignoreCase, regex)
{
}
/// <inheritdoc />
public JsonPartialMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool throwException = false, bool regex = false)
: base(matchBehaviour, value, ignoreCase, throwException, regex)
public JsonPartialMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false)
: base(matchBehaviour, value, ignoreCase, regex)
{
}
/// <inheritdoc />
protected override bool IsMatch(string value, string input)
{
var exactStringMatcher = new ExactMatcher(MatchBehaviour.AcceptOnMatch, IgnoreCase, ThrowException, MatchOperator.Or, value);
return MatchScores.IsPerfect(exactStringMatcher.IsMatch(input));
var exactStringMatcher = new ExactMatcher(MatchBehaviour.AcceptOnMatch, IgnoreCase, MatchOperator.Or, value);
return exactStringMatcher.IsMatch(input).IsPerfect();
}
}

View File

@@ -9,20 +9,20 @@ public class JsonPartialWildcardMatcher : AbstractJsonPartialMatcher
public override string Name => nameof(JsonPartialWildcardMatcher);
/// <inheritdoc />
public JsonPartialWildcardMatcher(string value, bool ignoreCase = false, bool throwException = false, bool regex = false)
: base(value, ignoreCase, throwException, regex)
public JsonPartialWildcardMatcher(string value, bool ignoreCase = false, bool regex = false)
: base(value, ignoreCase, regex)
{
}
/// <inheritdoc />
public JsonPartialWildcardMatcher(object value, bool ignoreCase = false, bool throwException = false, bool regex = false)
: base(value, ignoreCase, throwException, regex)
public JsonPartialWildcardMatcher(object value, bool ignoreCase = false, bool regex = false)
: base(value, ignoreCase, regex)
{
}
/// <inheritdoc />
public JsonPartialWildcardMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool throwException = false, bool regex = false)
: base(matchBehaviour, value, ignoreCase, throwException, regex)
public JsonPartialWildcardMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false)
: base(matchBehaviour, value, ignoreCase, regex)
{
}
@@ -30,6 +30,6 @@ public class JsonPartialWildcardMatcher : AbstractJsonPartialMatcher
protected override bool IsMatch(string value, string input)
{
var wildcardStringMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, value, IgnoreCase);
return MatchScores.IsPerfect(wildcardStringMatcher.IsMatch(input));
return wildcardStringMatcher.IsMatch(input).IsPerfect();
}
}

View File

@@ -1,3 +1,4 @@
using System;
using System.Linq;
using System.Linq.Dynamic.Core;
using AnyOfTypes;
@@ -18,12 +19,9 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
{
private readonly AnyOf<string, StringPattern>[] _patterns;
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
/// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc cref="IMatcher.ThrowException"/>
public bool ThrowException { get; }
/// <summary>
/// Initializes a new instance of the <see cref="LinqMatcher"/> class.
/// </summary>
@@ -36,7 +34,7 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
/// Initializes a new instance of the <see cref="LinqMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public LinqMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, patterns)
public LinqMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, patterns)
{
}
@@ -45,7 +43,7 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="pattern">The pattern.</param>
public LinqMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern> pattern) : this(matchBehaviour, false, MatchOperator.Or, pattern)
public LinqMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern> pattern) : this(matchBehaviour, MatchOperator.Or, pattern)
{
}
@@ -53,25 +51,23 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
/// Initializes a new instance of the <see cref="LinqMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
/// <param name="patterns">The patterns.</param>
public LinqMatcher(
MatchBehaviour matchBehaviour,
bool throwException = false,
MatchOperator matchOperator = MatchOperator.Or,
params AnyOf<string, StringPattern>[] patterns)
{
_patterns = Guard.NotNull(patterns);
MatchBehaviour = matchBehaviour;
ThrowException = throwException;
MatchOperator = matchOperator;
}
/// <inheritdoc />
public double IsMatch(string? input)
public MatchResult IsMatch(string? input)
{
double match = MatchScores.Mismatch;
var score = MatchScores.Mismatch;
Exception? error = null;
// Convert a single input string to a Queryable string-list with 1 entry.
IQueryable queryable = new[] { input }.AsQueryable();
@@ -79,25 +75,21 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
try
{
// Use the Any(...) method to check if the result matches
match = MatchScores.ToScore(_patterns.Select(pattern => queryable.Any(pattern.GetPattern())).ToArray(), MatchOperator);
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
score = MatchScores.ToScore(_patterns.Select(pattern => queryable.Any(pattern.GetPattern())).ToArray(), MatchOperator);
}
catch
catch (Exception e)
{
if (ThrowException)
{
throw;
}
error = e;
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), error);
}
/// <inheritdoc />
public double IsMatch(object? input)
public MatchResult IsMatch(object? input)
{
double match = MatchScores.Mismatch;
var score = MatchScores.Mismatch;
Exception? error = null;
JArray jArray;
try
@@ -106,7 +98,7 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
}
catch
{
jArray = new JArray { JToken.FromObject(input) };
jArray = input == null ? new JArray() : new JArray { JToken.FromObject(input) };
}
// Convert a single object to a Queryable JObject-list with 1 entry.
@@ -117,19 +109,14 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
var patternsAsStringArray = _patterns.Select(p => p.GetPattern()).ToArray();
var scores = patternsAsStringArray.Select(p => queryable.Any(p)).ToArray();
match = MatchScores.ToScore(scores, MatchOperator);
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
score = MatchScores.ToScore(scores, MatchOperator);
}
catch
catch (Exception e)
{
if (ThrowException)
{
throw;
}
error = e;
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), error);
}
/// <inheritdoc />
@@ -142,5 +129,5 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
public MatchOperator MatchOperator { get; }
/// <inheritdoc />
public string Name => "LinqMatcher";
public string Name => nameof(LinqMatcher);
}

View File

@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Stef.Validation;
using WireMock.Extensions;
namespace WireMock.Matchers;
/// <summary>
/// The MatchResult which contains the score (value between 0.0 - 1.0 of the similarity) and an optional error message.
/// </summary>
public struct MatchResult
{
/// <summary>
/// A value between 0.0 - 1.0 of the similarity.
/// </summary>
public double Score { get; set; }
/// <summary>
/// The exception message) in case the matching fails.
/// [Optional]
/// </summary>
public Exception? Exception { get; set; }
/// <summary>
/// Create a MatchResult
/// </summary>
/// <param name="score">A value between 0.0 - 1.0 of the similarity.</param>
/// <param name="exception">The exception in case the matching fails. [Optional]</param>
public MatchResult(double score, Exception? exception = null)
{
Score = score;
Exception = exception;
}
/// <summary>
/// Create a MatchResult
/// </summary>
/// <param name="exception">The exception in case the matching fails.</param>
public MatchResult(Exception exception)
{
Exception = Guard.NotNull(exception);
}
/// <summary>
/// Implicitly converts a double to a MatchResult.
/// </summary>
/// <param name="score">The score</param>
public static implicit operator MatchResult(double score)
{
return new MatchResult(score);
}
/// <summary>
/// Is the value a perfect match?
/// </summary>
public bool IsPerfect() => MatchScores.IsPerfect(Score);
/// <summary>
/// Create a MatchResult from multiple MatchResults.
/// </summary>
/// <param name="matchResults">A list of MatchResults.</param>
/// <param name="matchOperator">The MatchOperator</param>
/// <returns>MatchResult</returns>
public static MatchResult From(IReadOnlyList<MatchResult> matchResults, MatchOperator matchOperator)
{
Guard.NotNullOrEmpty(matchResults);
if (matchResults.Count == 1)
{
return matchResults[0];
}
return new MatchResult
{
Score = MatchScores.ToScore(matchResults.Select(r => r.Score).ToArray(), matchOperator),
Exception = matchResults.Select(m => m.Exception).OfType<Exception>().ToArray().ToException()
};
}
/// <summary>
/// Expand to Tuple
/// </summary>
/// <returns>Tuple : Score and Exception</returns>
public (double Score, Exception? Exception) Expand()
{
return (Score, Exception);
}
}

View File

@@ -1,7 +1,9 @@
#if MIMEKIT
using System;
using MimeKit;
using WireMock.Matchers;
using WireMock.Matchers.Helpers;
using WireMock.Models;
using WireMock.Util;
namespace WireMock.Matchers;
@@ -11,7 +13,7 @@ namespace WireMock.Matchers;
/// </summary>
public class MimePartMatcher : IMatcher
{
private readonly Func<MimePart, double>[] _funcs;
private readonly Func<MimePart, MatchResult>[] _funcs;
/// <inheritdoc />
public string Name => nameof(MimePartMatcher);
@@ -39,9 +41,6 @@ public class MimePartMatcher : IMatcher
/// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc />
public bool ThrowException { get; }
/// <summary>
/// Initializes a new instance of the <see cref="MimePartMatcher"/> class.
/// </summary>
@@ -50,8 +49,7 @@ public class MimePartMatcher : IMatcher
IStringMatcher? contentTypeMatcher,
IStringMatcher? contentDispositionMatcher,
IStringMatcher? contentTransferEncodingMatcher,
IMatcher? contentMatcher,
bool throwException = false
IMatcher? contentMatcher
)
{
MatchBehaviour = matchBehaviour;
@@ -59,7 +57,6 @@ public class MimePartMatcher : IMatcher
ContentDispositionMatcher = contentDispositionMatcher;
ContentTransferEncodingMatcher = contentTransferEncodingMatcher;
ContentMatcher = contentMatcher;
ThrowException = throwException;
_funcs = new[]
{
@@ -75,29 +72,27 @@ public class MimePartMatcher : IMatcher
/// </summary>
/// <param name="mimePart">The MimePart.</param>
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
public double IsMatch(MimePart mimePart)
public MatchResult IsMatch(MimePart mimePart)
{
var match = MatchScores.Mismatch;
var score = MatchScores.Mismatch;
Exception? exception = null;
try
{
if (Array.TrueForAll(_funcs, func => MatchScores.IsPerfect(func(mimePart))))
if (Array.TrueForAll(_funcs, func => func(mimePart).IsPerfect()))
{
match = MatchScores.Perfect;
score = MatchScores.Perfect;
}
}
catch
catch (Exception ex)
{
if (ThrowException)
{
throw;
}
exception = ex;
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
}
private double MatchOnContent(MimePart mimePart)
private MatchResult MatchOnContent(MimePart mimePart)
{
if (ContentMatcher == null)
{

View File

@@ -11,15 +11,12 @@ namespace WireMock.Matchers;
/// <seealso cref="IObjectMatcher" />
public class NotNullOrEmptyMatcher : IObjectMatcher, IStringMatcher
{
/// <inheritdoc cref="IMatcher.Name"/>
public string Name => "NotNullOrEmptyMatcher";
/// <inheritdoc />
public string Name => nameof(NotNullOrEmptyMatcher);
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
/// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc cref="IMatcher.ThrowException"/>
public bool ThrowException { get; }
/// <summary>
/// Initializes a new instance of the <see cref="NotNullOrEmptyMatcher"/> class.
/// </summary>
@@ -29,8 +26,8 @@ public class NotNullOrEmptyMatcher : IObjectMatcher, IStringMatcher
MatchBehaviour = matchBehaviour;
}
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
public double IsMatch(object? input)
/// <inheritdoc />
public MatchResult IsMatch(object? input)
{
bool match;
@@ -52,15 +49,15 @@ public class NotNullOrEmptyMatcher : IObjectMatcher, IStringMatcher
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(match));
}
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
public double IsMatch(string? input)
/// <inheritdoc />
public MatchResult IsMatch(string? input)
{
var match = !string.IsNullOrEmpty(input);
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(match));
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
/// <inheritdoc />
public AnyOf<string, StringPattern>[] GetPatterns()
{
return EmptyArray<AnyOf<string, StringPattern>>.Value;

View File

@@ -20,27 +20,22 @@ public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher
private readonly AnyOf<string, StringPattern>[] _patterns;
private readonly Regex[] _expressions;
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
/// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc cref="IMatcher.ThrowException"/>
public bool ThrowException { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RegexMatcher"/> class.
/// </summary>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="useRegexExtended">Use RegexExtended (default = true).</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
public RegexMatcher(
[RegexPattern] AnyOf<string, StringPattern> pattern,
bool ignoreCase = false,
bool throwException = false,
bool useRegexExtended = true,
MatchOperator matchOperator = MatchOperator.Or) :
this(MatchBehaviour.AcceptOnMatch, new[] { pattern }, ignoreCase, throwException, useRegexExtended, matchOperator)
this(MatchBehaviour.AcceptOnMatch, new[] { pattern }, ignoreCase, useRegexExtended, matchOperator)
{
}
@@ -50,17 +45,15 @@ public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="useRegexExtended">Use RegexExtended (default = true).</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
public RegexMatcher(
MatchBehaviour matchBehaviour,
[RegexPattern] AnyOf<string, StringPattern> pattern,
bool ignoreCase = false,
bool throwException = false,
bool useRegexExtended = true,
MatchOperator matchOperator = MatchOperator.Or) :
this(matchBehaviour, new[] { pattern }, ignoreCase, throwException, useRegexExtended, matchOperator)
this(matchBehaviour, new[] { pattern }, ignoreCase, useRegexExtended, matchOperator)
{
}
@@ -70,21 +63,18 @@ public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="useRegexExtended">Use RegexExtended (default = true).</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
public RegexMatcher(
MatchBehaviour matchBehaviour,
[RegexPattern] AnyOf<string, StringPattern>[] patterns,
bool ignoreCase = false,
bool throwException = false,
bool useRegexExtended = true,
MatchOperator matchOperator = MatchOperator.Or)
{
_patterns = Guard.NotNull(patterns);
IgnoreCase = ignoreCase;
MatchBehaviour = matchBehaviour;
ThrowException = throwException;
MatchOperator = matchOperator;
RegexOptions options = RegexOptions.Compiled | RegexOptions.Multiline;
@@ -97,26 +87,25 @@ public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher
_expressions = patterns.Select(p => useRegexExtended ? new RegexExtended(p.GetPattern(), options) : new Regex(p.GetPattern(), options)).ToArray();
}
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
public virtual double IsMatch(string? input)
/// <inheritdoc />
public virtual MatchResult IsMatch(string? input)
{
double match = MatchScores.Mismatch;
var score = MatchScores.Mismatch;
Exception? exception = null;
if (input != null)
{
try
{
match = MatchScores.ToScore(_expressions.Select(e => e.IsMatch(input)).ToArray(), MatchOperator);
score = MatchScores.ToScore(_expressions.Select(e => e.IsMatch(input)).ToArray(), MatchOperator);
}
catch (Exception)
catch (Exception ex)
{
if (ThrowException)
{
throw;
}
exception = ex;
}
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
}
/// <inheritdoc />

View File

@@ -9,30 +9,25 @@ namespace WireMock.Matchers.Request;
/// </summary>
public class RequestMatchResult : IRequestMatchResult
{
/// <inheritdoc cref="IRequestMatchResult.TotalScore" />
/// <inheritdoc />
public double TotalScore => MatchDetails.Sum(md => md.Score);
/// <inheritdoc cref="IRequestMatchResult.TotalNumber" />
/// <inheritdoc />
public int TotalNumber => MatchDetails.Count;
/// <inheritdoc cref="IRequestMatchResult.IsPerfectMatch" />
/// <inheritdoc />
public bool IsPerfectMatch => Math.Abs(TotalScore - TotalNumber) < MatchScores.Tolerance;
/// <inheritdoc cref="IRequestMatchResult.AverageTotalScore" />
public double AverageTotalScore => TotalNumber == 0 ? 0.0 : TotalScore / TotalNumber;
/// <inheritdoc />
public double AverageTotalScore => TotalNumber == 0 ? MatchScores.Mismatch : TotalScore / TotalNumber;
/// <inheritdoc cref="IRequestMatchResult.MatchDetails" />
/// <inheritdoc />
public IList<MatchDetail> MatchDetails { get; } = new List<MatchDetail>();
/// <summary>
/// Adds the score.
/// </summary>
/// <param name="matcherType">The matcher Type.</param>
/// <param name="score">The score.</param>
/// <returns>The score.</returns>
public double AddScore(Type matcherType, double score)
/// <inheritdoc />
public double AddScore(Type matcherType, double score, Exception? exception)
{
MatchDetails.Add(new MatchDetail { MatcherType = matcherType, Score = score });
MatchDetails.Add(new MatchDetail { MatcherType = matcherType, Score = score, Exception = exception });
return score;
}
@@ -44,11 +39,16 @@ public class RequestMatchResult : IRequestMatchResult
/// <returns>
/// A value that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less than zero This instance precedes <paramref name="obj" /> in the sort order. Zero This instance occurs in the same position in the sort order as <paramref name="obj" />. Greater than zero This instance follows <paramref name="obj" /> in the sort order.
/// </returns>
public int CompareTo(object obj)
public int CompareTo(object? obj)
{
if (obj == null)
{
return -1;
}
var compareObj = (RequestMatchResult)obj;
int averageTotalScoreResult = compareObj.AverageTotalScore.CompareTo(AverageTotalScore);
var averageTotalScoreResult = compareObj.AverageTotalScore.CompareTo(AverageTotalScore);
// In case the score is equal, prefer the one with the most matchers.
return averageTotalScoreResult == 0 ? compareObj.TotalNumber.CompareTo(TotalNumber) : averageTotalScoreResult;

View File

@@ -145,16 +145,16 @@ public class RequestMessageBodyMatcher : IRequestMatcher
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
double score = CalculateMatchScore(requestMessage);
return requestMatchResult.AddScore(GetType(), score);
var (score, exception) = CalculateMatchScore(requestMessage).Expand();
return requestMatchResult.AddScore(GetType(), score, exception);
}
private double CalculateMatchScore(IRequestMessage requestMessage)
private MatchResult CalculateMatchScore(IRequestMessage requestMessage)
{
if (Matchers != null)
if (Matchers != null && Matchers.Any())
{
var matchersResult = Matchers.Select(matcher => BodyDataMatchScoreCalculator.CalculateMatchScore(requestMessage.BodyData, matcher)).ToArray();
return MatchScores.ToScore(matchersResult, MatchOperator);
var results = Matchers.Select(matcher => BodyDataMatchScoreCalculator.CalculateMatchScore(requestMessage.BodyData, matcher)).ToArray();
return MatchResult.From(results, MatchOperator);
}
if (Func != null)
@@ -182,6 +182,6 @@ public class RequestMessageBodyMatcher : IRequestMatcher
return MatchScores.ToScore(BodyDataFunc(requestMessage.BodyData));
}
return MatchScores.Mismatch;
return default;
}
}

View File

@@ -43,7 +43,7 @@ public class RequestMessageClientIPMatcher : IRequestMatcher
MatchOperator matchOperator,
params string[] clientIPs) :
this(matchBehaviour, matchOperator, clientIPs
.Select(clientIP => new WildcardMatcher(matchBehaviour, new AnyOf<string, StringPattern>[] { clientIP }, false, false, matchOperator))
.Select(clientIP => new WildcardMatcher(matchBehaviour, new AnyOf<string, StringPattern>[] { clientIP }, false, matchOperator))
.Cast<IStringMatcher>().ToArray())
{
Behaviour = matchBehaviour;
@@ -75,16 +75,16 @@ public class RequestMessageClientIPMatcher : IRequestMatcher
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);
return requestMatchResult.AddScore(GetType(), score);
var (score, exception) = GetMatchResult(requestMessage).Expand();
return requestMatchResult.AddScore(GetType(), score, exception);
}
private double IsMatch(IRequestMessage requestMessage)
private MatchResult GetMatchResult(IRequestMessage requestMessage)
{
if (Matchers != null)
{
var results = Matchers.Select(m => m.IsMatch(requestMessage.ClientIP)).ToArray();
return MatchScores.ToScore(results, MatchOperator);
return MatchResult.From(results, MatchOperator);
}
if (Funcs != null)
@@ -93,6 +93,6 @@ public class RequestMessageClientIPMatcher : IRequestMatcher
return MatchScores.ToScore(results, MatchOperator);
}
return MatchScores.Mismatch;
return default;
}
}

View File

@@ -11,9 +11,15 @@ namespace WireMock.Matchers.Request;
/// <inheritdoc cref="IRequestMatcher"/>
public class RequestMessageCookieMatcher : IRequestMatcher
{
private readonly MatchBehaviour _matchBehaviour;
/// <summary>
/// MatchBehaviour
/// </summary>
public MatchBehaviour MatchBehaviour { get; }
private readonly bool _ignoreCase;
/// <summary>
/// IgnoreCase
/// </summary>
public bool IgnoreCase { get; }
/// <summary>
/// The functions
@@ -39,8 +45,8 @@ public class RequestMessageCookieMatcher : IRequestMatcher
/// <param name="matchBehaviour">The match behaviour.</param>
public RequestMessageCookieMatcher(MatchBehaviour matchBehaviour, string name, string pattern, bool ignoreCase)
{
_matchBehaviour = matchBehaviour;
_ignoreCase = ignoreCase;
MatchBehaviour = matchBehaviour;
IgnoreCase = ignoreCase;
Name = Guard.NotNull(name);
Matchers = new IStringMatcher[] { new WildcardMatcher(matchBehaviour, Guard.NotNull(pattern), ignoreCase) };
}
@@ -67,10 +73,10 @@ public class RequestMessageCookieMatcher : IRequestMatcher
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
public RequestMessageCookieMatcher(MatchBehaviour matchBehaviour, string name, bool ignoreCase, params IStringMatcher[] matchers)
{
_matchBehaviour = matchBehaviour;
MatchBehaviour = matchBehaviour;
Name = Guard.NotNull(name);
Matchers = Guard.NotNull(matchers);
_ignoreCase = ignoreCase;
IgnoreCase = ignoreCase;
}
/// <summary>
@@ -88,19 +94,19 @@ public class RequestMessageCookieMatcher : IRequestMatcher
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);
return requestMatchResult.AddScore(GetType(), score);
var (score, exception) = GetMatchResult(requestMessage).Expand();
return requestMatchResult.AddScore(GetType(), score, exception);
}
private double IsMatch(IRequestMessage requestMessage)
private MatchResult GetMatchResult(IRequestMessage requestMessage)
{
if (requestMessage.Cookies == null)
{
return MatchBehaviourHelper.Convert(_matchBehaviour, MatchScores.Mismatch);
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
}
// Check if we want to use IgnoreCase to compare the Cookie-Name and Cookie-Value
var cookies = !_ignoreCase ? requestMessage.Cookies : new Dictionary<string, string>(requestMessage.Cookies, StringComparer.OrdinalIgnoreCase);
var cookies = !IgnoreCase ? requestMessage.Cookies : new Dictionary<string, string>(requestMessage.Cookies, StringComparer.OrdinalIgnoreCase);
if (Funcs != null)
{
@@ -109,15 +115,14 @@ public class RequestMessageCookieMatcher : IRequestMatcher
if (Matchers == null)
{
return MatchScores.Mismatch;
return default;
}
if (!cookies.ContainsKey(Name))
{
return MatchBehaviourHelper.Convert(_matchBehaviour, MatchScores.Mismatch);
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
}
string value = cookies[Name];
return Matchers.Max(m => m.IsMatch(value));
return Matchers.Max(m => m.IsMatch(cookies[Name]));
}
}

View File

@@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using Stef.Validation;
using WireMock.Types;
@@ -64,11 +65,13 @@ public class RequestMessageGraphQLMatcher : IRequestMatcher
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
var score = CalculateMatchScore(requestMessage);
return requestMatchResult.AddScore(GetType(), score);
var results = CalculateMatchResults(requestMessage);
var (score, exception) = MatchResult.From(results, MatchOperator).Expand();
return requestMatchResult.AddScore(GetType(), score, exception);
}
private static double CalculateMatchScore(IRequestMessage requestMessage, IMatcher matcher)
private static MatchResult CalculateMatchResult(IRequestMessage requestMessage, IMatcher matcher)
{
// Check if the matcher is a IStringMatcher
// If the body is a Json or a String, use the BodyAsString to match on.
@@ -77,18 +80,12 @@ public class RequestMessageGraphQLMatcher : IRequestMatcher
return stringMatcher.IsMatch(requestMessage.BodyData.BodyAsString);
}
return MatchScores.Mismatch;
return default;
}
private double CalculateMatchScore(IRequestMessage requestMessage)
private IReadOnlyList<MatchResult> CalculateMatchResults(IRequestMessage requestMessage)
{
if (Matchers == null)
{
return MatchScores.Mismatch;
}
var matchersResult = Matchers.Select(matcher => CalculateMatchScore(requestMessage, matcher)).ToArray();
return MatchScores.ToScore(matchersResult, MatchOperator);
return Matchers == null ? new[] { new MatchResult() } : Matchers.Select(matcher => CalculateMatchResult(requestMessage, matcher)).ToArray();
}
#if GRAPHQL

View File

@@ -12,8 +12,15 @@ namespace WireMock.Matchers.Request;
/// <inheritdoc cref="IRequestMatcher"/>
public class RequestMessageHeaderMatcher : IRequestMatcher
{
private readonly MatchBehaviour _matchBehaviour;
private readonly bool _ignoreCase;
/// <summary>
/// MatchBehaviour
/// </summary>
public MatchBehaviour MatchBehaviour { get; }
/// <summary>
/// IgnoreCase
/// </summary>
public bool IgnoreCase { get; }
/// <summary>
/// The functions
@@ -47,8 +54,8 @@ public class RequestMessageHeaderMatcher : IRequestMatcher
Guard.NotNull(name);
Guard.NotNull(pattern);
_matchBehaviour = matchBehaviour;
_ignoreCase = ignoreCase;
MatchBehaviour = matchBehaviour;
IgnoreCase = ignoreCase;
Name = name;
Matchers = new IStringMatcher[] { new WildcardMatcher(matchBehaviour, pattern, ignoreCase) };
}
@@ -80,11 +87,11 @@ public class RequestMessageHeaderMatcher : IRequestMatcher
Guard.NotNull(name);
Guard.NotNull(matchers);
_matchBehaviour = matchBehaviour;
MatchBehaviour = matchBehaviour;
MatchOperator = matchOperator;
Name = name;
Matchers = matchers;
_ignoreCase = ignoreCase;
IgnoreCase = ignoreCase;
}
/// <summary>
@@ -100,19 +107,19 @@ public class RequestMessageHeaderMatcher : IRequestMatcher
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);
return requestMatchResult.AddScore(GetType(), score);
var (score, exception) = GetMatchResult(requestMessage).Expand();
return requestMatchResult.AddScore(GetType(), score, exception);
}
private double IsMatch(IRequestMessage requestMessage)
private MatchResult GetMatchResult(IRequestMessage requestMessage)
{
if (requestMessage.Headers == null)
{
return MatchBehaviourHelper.Convert(_matchBehaviour, MatchScores.Mismatch);
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
}
// Check if we want to use IgnoreCase to compare the Header-Name and Header-Value(s)
var headers = !_ignoreCase ? requestMessage.Headers : new Dictionary<string, WireMockList<string>>(requestMessage.Headers, StringComparer.OrdinalIgnoreCase);
var headers = !IgnoreCase ? requestMessage.Headers : new Dictionary<string, WireMockList<string>>(requestMessage.Headers, StringComparer.OrdinalIgnoreCase);
if (Funcs != null)
{
@@ -124,20 +131,20 @@ public class RequestMessageHeaderMatcher : IRequestMatcher
{
if (!headers.ContainsKey(Name))
{
return MatchBehaviourHelper.Convert(_matchBehaviour, MatchScores.Mismatch);
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
}
var results = new List<double>();
var results = new List<MatchResult>();
foreach (var matcher in Matchers)
{
var resultsPerMatcher = headers[Name].Select(v => matcher.IsMatch(v)).ToArray();
var resultsPerMatcher = headers[Name].Select(matcher.IsMatch).ToArray();
results.Add(MatchScores.ToScore(resultsPerMatcher, MatchOperator.And));
results.Add(MatchResult.From(resultsPerMatcher, MatchOperator.And));
}
return MatchScores.ToScore(results, MatchOperator);
return MatchResult.From(results, MatchOperator);
}
return MatchBehaviourHelper.Convert(_matchBehaviour, MatchScores.Mismatch);
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
}
}

View File

@@ -39,14 +39,9 @@ internal class RequestMessageMethodMatcher : IRequestMatcher
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
double score = MatchBehaviourHelper.Convert(MatchBehaviour, IsMatch(requestMessage));
return requestMatchResult.AddScore(GetType(), score);
}
private double IsMatch(IRequestMessage requestMessage)
{
var scores = Methods.Select(m => string.Equals(m, requestMessage.Method, StringComparison.OrdinalIgnoreCase)).ToArray();
return MatchScores.ToScore(scores, MatchOperator);
var score = MatchScores.ToScore(scores, MatchOperator);
return requestMatchResult.AddScore(GetType(), score, null);
}
}

View File

@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Stef.Validation;
using WireMock.Http;
using WireMock.Util;
namespace WireMock.Matchers.Request;
@@ -54,27 +54,31 @@ public class RequestMessageMultiPartMatcher : IRequestMatcher
#if !MIMEKIT
throw new System.NotSupportedException("The MultiPartMatcher can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower.");
#else
var match = MatchScores.Mismatch;
var score = MatchScores.Mismatch;
Exception? exception = null;
if (Matchers?.Any() != true)
{
return requestMatchResult.AddScore(GetType(), match);
return requestMatchResult.AddScore(GetType(), score, null);
}
if (!MimeKitUtils.TryGetMimeMessage(requestMessage, out var message))
{
return requestMatchResult.AddScore(GetType(), score, null);
}
try
{
var message = MimeKitUtils.GetMimeMessage(requestMessage.BodyData!, requestMessage.Headers![HttpKnownHeaderNames.ContentType].ToString());
var mimePartMatchers = Matchers.OfType<MimePartMatcher>().ToArray();
foreach (var mimePart in message.BodyParts.OfType<MimeKit.MimePart>())
{
var matchesForMimePart = new List<double> { MatchScores.Mismatch };
var matchesForMimePart = new List<MatchResult> { default };
matchesForMimePart.AddRange(mimePartMatchers.Select(matcher => matcher.IsMatch(mimePart)));
match = matchesForMimePart.Max();
score = matchesForMimePart.Select(m => m.Score).Max();
if (MatchScores.IsPerfect(match))
if (MatchScores.IsPerfect(score))
{
if (MatchOperator == MatchOperator.Or)
{
@@ -83,17 +87,17 @@ public class RequestMessageMultiPartMatcher : IRequestMatcher
}
else
{
match = MatchScores.Mismatch;
score = MatchScores.Mismatch;
break;
}
}
}
catch
catch (Exception ex)
{
// Empty
exception = ex;
}
return requestMatchResult.AddScore(GetType(), match);
return requestMatchResult.AddScore(GetType(), score, exception);
#endif
}
}

View File

@@ -29,7 +29,7 @@ public class RequestMessageParamMatcher : IRequestMatcher
/// <summary>
/// Defines if the key should be matched using case-ignore.
/// </summary>
public bool? IgnoreCase { get; }
public bool IgnoreCase { get; }
/// <summary>
/// The matchers.
@@ -54,7 +54,7 @@ public class RequestMessageParamMatcher : IRequestMatcher
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
/// <param name="values">The values.</param>
public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, string key, bool ignoreCase, params string[]? values) :
this(matchBehaviour, key, ignoreCase, values?.Select(value => new ExactMatcher(matchBehaviour, ignoreCase, false, MatchOperator.And, value)).Cast<IStringMatcher>().ToArray())
this(matchBehaviour, key, ignoreCase, values?.Select(value => new ExactMatcher(matchBehaviour, ignoreCase, MatchOperator.And, value)).Cast<IStringMatcher>().ToArray())
{
}
@@ -85,22 +85,22 @@ public class RequestMessageParamMatcher : IRequestMatcher
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
double score = MatchBehaviourHelper.Convert(MatchBehaviour, IsMatch(requestMessage));
return requestMatchResult.AddScore(GetType(), score);
var (score, exception) = GetMatchResult(requestMessage).Expand();
return requestMatchResult.AddScore(GetType(), score, exception);
}
private double IsMatch(IRequestMessage requestMessage)
private MatchResult GetMatchResult(IRequestMessage requestMessage)
{
if (Funcs != null)
{
return MatchScores.ToScore(requestMessage.Query != null && Funcs.Any(f => f(requestMessage.Query)));
}
var valuesPresentInRequestMessage = ((RequestMessage)requestMessage).GetParameter(Key, IgnoreCase ?? false);
var valuesPresentInRequestMessage = ((RequestMessage)requestMessage).GetParameter(Key, IgnoreCase);
if (valuesPresentInRequestMessage == null)
{
// Key is not present at all, just return Mismatch
return MatchScores.Mismatch;
return default;
}
if (Matchers != null && Matchers.Any())
@@ -115,10 +115,10 @@ public class RequestMessageParamMatcher : IRequestMatcher
return MatchScores.Perfect;
}
return MatchScores.Mismatch;
return default;
}
private double CalculateScore(IReadOnlyList<IStringMatcher> matchers, WireMockList<string> valuesPresentInRequestMessage)
private static MatchResult CalculateScore(IReadOnlyList<IStringMatcher> matchers, WireMockList<string> valuesPresentInRequestMessage)
{
var total = new List<double>();
@@ -130,7 +130,7 @@ public class RequestMessageParamMatcher : IRequestMatcher
double score = 0d;
foreach (string valuePresentInRequestMessage in valuesPresentInRequestMessage)
{
score += matcher.IsMatch(valuePresentInRequestMessage) / matcher.GetPatterns().Length;
score += matcher.IsMatch(valuePresentInRequestMessage).Score / matcher.GetPatterns().Length;
}
total.Add(score);
@@ -140,11 +140,11 @@ public class RequestMessageParamMatcher : IRequestMatcher
{
foreach (string valuePresentInRequestMessage in valuesPresentInRequestMessage)
{
double score = matchers.Max(m => m.IsMatch(valuePresentInRequestMessage));
var score = matchers.Max(m => m.IsMatch(valuePresentInRequestMessage).Score);
total.Add(score);
}
}
return total.Any() ? MatchScores.ToScore(total, MatchOperator.Average) : MatchScores.Mismatch;
return total.Any() ? MatchScores.ToScore(total, MatchOperator.Average) : default;
}
}

View File

@@ -43,7 +43,7 @@ public class RequestMessagePathMatcher : IRequestMatcher
MatchOperator matchOperator,
params string[] paths) :
this(matchBehaviour, matchOperator, paths
.Select(path => new WildcardMatcher(matchBehaviour, new AnyOf<string, StringPattern>[] { path }, false, false, matchOperator))
.Select(path => new WildcardMatcher(matchBehaviour, new AnyOf<string, StringPattern>[] { path }, false, matchOperator))
.Cast<IStringMatcher>().ToArray())
{
Behaviour = matchBehaviour;
@@ -75,16 +75,16 @@ public class RequestMessagePathMatcher : IRequestMatcher
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);
return requestMatchResult.AddScore(GetType(), score);
var (score, exception) = GetMatchResult(requestMessage).Expand();
return requestMatchResult.AddScore(GetType(), score, exception);
}
private double IsMatch(IRequestMessage requestMessage)
private MatchResult GetMatchResult(IRequestMessage requestMessage)
{
if (Matchers != null)
{
var results = Matchers.Select(m => m.IsMatch(requestMessage.Path)).ToArray();
return MatchScores.ToScore(results, MatchOperator);
return MatchResult.From(results, MatchOperator);
}
if (Funcs != null)
@@ -93,6 +93,6 @@ public class RequestMessagePathMatcher : IRequestMatcher
return MatchScores.ToScore(results, MatchOperator);
}
return MatchScores.Mismatch;
return default;
}
}

View File

@@ -30,11 +30,10 @@ internal class RequestMessageScenarioAndStateMatcher : IRequestMatcher
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
double score = IsMatch();
return requestMatchResult.AddScore(GetType(), score);
return requestMatchResult.AddScore(GetType(), GetScore(), null);
}
private double IsMatch()
private double GetScore()
{
return Equals(_executionConditionState, _nextState) ? MatchScores.Perfect : MatchScores.Mismatch;
}

View File

@@ -43,7 +43,7 @@ public class RequestMessageUrlMatcher : IRequestMatcher
MatchOperator matchOperator,
params string[] urls) :
this(matchBehaviour, matchOperator, urls
.Select(url => new WildcardMatcher(matchBehaviour, new AnyOf<string, StringPattern>[] { url }, false, false, matchOperator))
.Select(url => new WildcardMatcher(matchBehaviour, new AnyOf<string, StringPattern>[] { url }, false, matchOperator))
.Cast<IStringMatcher>().ToArray())
{
Behaviour = matchBehaviour;
@@ -75,16 +75,16 @@ public class RequestMessageUrlMatcher : IRequestMatcher
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);
return requestMatchResult.AddScore(GetType(), score);
var (score, exception) = GetMatchResult(requestMessage).Expand();
return requestMatchResult.AddScore(GetType(), score, exception);
}
private double IsMatch(IRequestMessage requestMessage)
private MatchResult GetMatchResult(IRequestMessage requestMessage)
{
if (Matchers != null)
{
var results = Matchers.Select(m => m.IsMatch(requestMessage.Url)).ToArray();
return MatchScores.ToScore(results, MatchOperator);
return MatchResult.From(results, MatchOperator);
}
if (Funcs != null)
@@ -93,6 +93,6 @@ public class RequestMessageUrlMatcher : IRequestMatcher
return MatchScores.ToScore(results, MatchOperator);
}
return MatchScores.Mismatch;
return default;
}
}

View File

@@ -18,12 +18,9 @@ public class SimMetricsMatcher : IStringMatcher
private readonly AnyOf<string, StringPattern>[] _patterns;
private readonly SimMetricType _simMetricType;
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
/// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc cref="IMatcher.ThrowException"/>
public bool ThrowException { get; }
/// <summary>
/// Initializes a new instance of the <see cref="SimMetricsMatcher"/> class.
/// </summary>
@@ -67,24 +64,21 @@ public class SimMetricsMatcher : IStringMatcher
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="patterns">The patterns.</param>
/// <param name="simMetricType">The SimMetric Type</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
public SimMetricsMatcher(
MatchBehaviour matchBehaviour,
AnyOf<string, StringPattern>[] patterns,
SimMetricType simMetricType = SimMetricType.Levenstein,
bool throwException = false,
MatchOperator matchOperator = MatchOperator.Average)
{
_patterns = Guard.NotNull(patterns);
_simMetricType = simMetricType;
MatchBehaviour = matchBehaviour;
ThrowException = throwException;
MatchOperator = matchOperator;
}
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
public double IsMatch(string input)
/// <inheritdoc />
public MatchResult IsMatch(string? input)
{
IStringMetric stringMetricType = GetStringMetricType();
@@ -94,48 +88,30 @@ public class SimMetricsMatcher : IStringMatcher
private IStringMetric GetStringMetricType()
{
switch (_simMetricType)
return _simMetricType switch
{
case SimMetricType.BlockDistance:
return new BlockDistance();
case SimMetricType.ChapmanLengthDeviation:
return new ChapmanLengthDeviation();
case SimMetricType.CosineSimilarity:
return new CosineSimilarity();
case SimMetricType.DiceSimilarity:
return new DiceSimilarity();
case SimMetricType.EuclideanDistance:
return new EuclideanDistance();
case SimMetricType.JaccardSimilarity:
return new JaccardSimilarity();
case SimMetricType.Jaro:
return new Jaro();
case SimMetricType.JaroWinkler:
return new JaroWinkler();
case SimMetricType.MatchingCoefficient:
return new MatchingCoefficient();
case SimMetricType.MongeElkan:
return new MongeElkan();
case SimMetricType.NeedlemanWunch:
return new NeedlemanWunch();
case SimMetricType.OverlapCoefficient:
return new OverlapCoefficient();
case SimMetricType.QGramsDistance:
return new QGramsDistance();
case SimMetricType.SmithWaterman:
return new SmithWaterman();
case SimMetricType.SmithWatermanGotoh:
return new SmithWatermanGotoh();
case SimMetricType.SmithWatermanGotohWindowedAffine:
return new SmithWatermanGotohWindowedAffine();
case SimMetricType.ChapmanMeanLength:
return new ChapmanMeanLength();
default:
return new Levenstein();
}
SimMetricType.BlockDistance => new BlockDistance(),
SimMetricType.ChapmanLengthDeviation => new ChapmanLengthDeviation(),
SimMetricType.CosineSimilarity => new CosineSimilarity(),
SimMetricType.DiceSimilarity => new DiceSimilarity(),
SimMetricType.EuclideanDistance => new EuclideanDistance(),
SimMetricType.JaccardSimilarity => new JaccardSimilarity(),
SimMetricType.Jaro => new Jaro(),
SimMetricType.JaroWinkler => new JaroWinkler(),
SimMetricType.MatchingCoefficient => new MatchingCoefficient(),
SimMetricType.MongeElkan => new MongeElkan(),
SimMetricType.NeedlemanWunch => new NeedlemanWunch(),
SimMetricType.OverlapCoefficient => new OverlapCoefficient(),
SimMetricType.QGramsDistance => new QGramsDistance(),
SimMetricType.SmithWaterman => new SmithWaterman(),
SimMetricType.SmithWatermanGotoh => new SmithWatermanGotoh(),
SimMetricType.SmithWatermanGotohWindowedAffine => new SmithWatermanGotohWindowedAffine(),
SimMetricType.ChapmanMeanLength => new ChapmanMeanLength(),
_ => new Levenstein()
};
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
/// <inheritdoc />
public AnyOf<string, StringPattern>[] GetPatterns()
{
return _patterns;
@@ -144,6 +120,6 @@ public class SimMetricsMatcher : IStringMatcher
/// <inheritdoc />
public MatchOperator MatchOperator { get; } = MatchOperator.Average;
/// <inheritdoc cref="IMatcher.Name"/>
/// <inheritdoc />
public string Name => $"SimMetricsMatcher.{_simMetricType}";
}

View File

@@ -49,15 +49,12 @@ public class WildcardMatcher : RegexMatcher
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">IgnoreCase</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use. (default = "Or")</param>
public WildcardMatcher(
MatchBehaviour matchBehaviour,
AnyOf<string, StringPattern>[] patterns,
bool ignoreCase = false,
bool throwException = false,
MatchOperator matchOperator = MatchOperator.Or) :
base(matchBehaviour, CreateArray(patterns), ignoreCase, throwException, true, matchOperator)
MatchOperator matchOperator = MatchOperator.Or) : base(matchBehaviour, CreateArray(patterns), ignoreCase, true, matchOperator)
{
_patterns = Guard.NotNull(patterns);
}

View File

@@ -1,6 +1,8 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Xml;
using System.Xml.XPath;
using AnyOfTypes;
using WireMock.Extensions;
using WireMock.Models;
@@ -9,86 +11,91 @@ using Stef.Validation;
using Wmhelp.XPath2;
#endif
namespace WireMock.Matchers
namespace WireMock.Matchers;
/// <summary>
/// XPath2Matcher
/// </summary>
/// <seealso cref="IStringMatcher" />
public class XPathMatcher : IStringMatcher
{
private readonly AnyOf<string, StringPattern>[] _patterns;
/// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; }
/// <summary>
/// XPath2Matcher
/// Initializes a new instance of the <see cref="XPathMatcher"/> class.
/// </summary>
/// <seealso cref="IStringMatcher" />
public class XPathMatcher : IStringMatcher
/// <param name="patterns">The patterns.</param>
public XPathMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, patterns)
{
private readonly AnyOf<string, StringPattern>[] _patterns;
}
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
public MatchBehaviour MatchBehaviour { get; }
/// <summary>
/// Initializes a new instance of the <see cref="XPathMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
/// <param name="patterns">The patterns.</param>
public XPathMatcher(
MatchBehaviour matchBehaviour,
MatchOperator matchOperator = MatchOperator.Or,
params AnyOf<string, StringPattern>[] patterns)
{
_patterns = Guard.NotNull(patterns);
MatchBehaviour = matchBehaviour;
MatchOperator = matchOperator;
}
/// <inheritdoc cref="IMatcher.ThrowException"/>
public bool ThrowException { get; }
/// <inheritdoc />
public MatchResult IsMatch(string? input)
{
var score = MatchScores.Mismatch;
Exception? exception = null;
/// <summary>
/// Initializes a new instance of the <see cref="XPathMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public XPathMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, patterns)
if (input != null && TryGetXPathNavigator(input, out var nav))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="XPathMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
/// <param name="patterns">The patterns.</param>
public XPathMatcher(
MatchBehaviour matchBehaviour,
bool throwException = false,
MatchOperator matchOperator = MatchOperator.Or,
params AnyOf<string, StringPattern>[] patterns)
{
_patterns = Guard.NotNull(patterns);
MatchBehaviour = matchBehaviour;
ThrowException = throwException;
MatchOperator = matchOperator;
}
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
public double IsMatch(string? input)
{
double match = MatchScores.Mismatch;
if (input != null)
try
{
try
{
var nav = new XmlDocument { InnerXml = input }.CreateNavigator();
#if NETSTANDARD1_3
match = MatchScores.ToScore(_patterns.Select(p => true.Equals(nav.Evaluate($"boolean({p.GetPattern()})"))).ToArray(), MatchOperator);
score = MatchScores.ToScore(_patterns.Select(p => true.Equals(nav.Evaluate($"boolean({p.GetPattern()})"))).ToArray(), MatchOperator);
#else
match = MatchScores.ToScore(_patterns.Select(p => true.Equals(nav.XPath2Evaluate($"boolean({p.GetPattern()})"))).ToArray(), MatchOperator);
score = MatchScores.ToScore(_patterns.Select(p => true.Equals(nav.XPath2Evaluate($"boolean({p.GetPattern()})"))).ToArray(), MatchOperator);
#endif
}
catch (Exception)
{
if (ThrowException)
{
throw;
}
}
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
catch (Exception ex)
{
exception = ex;
}
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public AnyOf<string, StringPattern>[] GetPatterns()
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
}
/// <inheritdoc />
public AnyOf<string, StringPattern>[] GetPatterns()
{
return _patterns;
}
/// <inheritdoc />
public MatchOperator MatchOperator { get; }
/// <inheritdoc />
public string Name => nameof(XPathMatcher);
private static bool TryGetXPathNavigator(string input, [NotNullWhen(true)] out XPathNavigator? nav)
{
try
{
return _patterns;
nav = new XmlDocument { InnerXml = input }.CreateNavigator()!;
return true;
}
catch
{
nav = default;
return false;
}
/// <inheritdoc />
public MatchOperator MatchOperator { get; }
/// <inheritdoc cref="IMatcher.Name"/>
public string Name => "XPathMatcher";
}
}

View File

@@ -70,6 +70,7 @@ namespace WireMock.Owin
services.AddSingleton<IRandomizerDoubleBetween0And1, RandomizerDoubleBetween0And1>();
services.AddSingleton<IOwinRequestMapper, OwinRequestMapper>();
services.AddSingleton<IOwinResponseMapper, OwinResponseMapper>();
services.AddSingleton<IGuidUtils, GuidUtils>();
#if NETCOREAPP3_1 || NET5_0 || NET6_0 || NET7_0
AddCors(services);

View File

@@ -67,7 +67,7 @@ namespace WireMock.Owin
catch (Exception ex)
{
_options.Logger.Error("HttpStatusCode set to 500 {0}", ex);
await _responseMapper.MapAsync(ResponseMessageBuilder.Create(JsonConvert.SerializeObject(ex), 500), ctx.Response).ConfigureAwait(false);
await _responseMapper.MapAsync(ResponseMessageBuilder.Create(500, JsonConvert.SerializeObject(ex)), ctx.Response).ConfigureAwait(false);
}
}
}

View File

@@ -50,7 +50,7 @@ namespace WireMock.Owin.Mappers
_options = Guard.NotNull(options);
}
/// <inheritdoc cref="IOwinResponseMapper.MapAsync"/>
/// <inheritdoc />
public async Task MapAsync(IResponseMessage? responseMessage, IResponse response)
{
if (responseMessage == null)
@@ -134,10 +134,8 @@ namespace WireMock.Owin.Mappers
return (responseMessage.BodyData.Encoding ?? _utf8NoBom).GetBytes(responseMessage.BodyData.BodyAsString!);
case BodyType.Json:
var formatting = responseMessage.BodyData.BodyAsJsonIndented == true
? Formatting.Indented
: Formatting.None;
string jsonBody = JsonConvert.SerializeObject(responseMessage.BodyData.BodyAsJson, new JsonSerializerSettings { Formatting = formatting, NullValueHandling = NullValueHandling.Ignore });
var formatting = responseMessage.BodyData.BodyAsJsonIndented == true ? Formatting.Indented : Formatting.None;
var jsonBody = JsonConvert.SerializeObject(responseMessage.BodyData.BodyAsJson, new JsonSerializerSettings { Formatting = formatting, NullValueHandling = NullValueHandling.Ignore });
return (responseMessage.BodyData.Encoding ?? _utf8NoBom).GetBytes(jsonBody);
case BodyType.Bytes:

View File

@@ -35,15 +35,32 @@ internal class MappingMatcher : IMappingMatcher
{
var nextState = GetNextState(mapping);
possibleMappings.Add(new MappingMatcherResult
var mappingMatcherResult = new MappingMatcherResult
{
Mapping = mapping,
RequestMatchResult = mapping.GetRequestMatchResult(request, nextState)
});
};
var exceptions = mappingMatcherResult.RequestMatchResult.MatchDetails
.Where(md => md.Exception != null)
.Select(md => md.Exception)
.ToArray();
if (!exceptions.Any())
{
possibleMappings.Add(mappingMatcherResult);
}
else if (!request.AbsolutePath.StartsWith("/__admin", StringComparison.OrdinalIgnoreCase))
{
foreach (var ex in exceptions)
{
LogException(mapping, ex!);
}
}
}
catch (Exception ex)
{
_options.Logger.Error($"Getting a Request MatchResult for Mapping '{mapping.Guid}' failed. This mapping will not be evaluated. Exception: {ex}");
LogException(mapping, ex);
}
}
@@ -66,6 +83,11 @@ internal class MappingMatcher : IMappingMatcher
return (match, partialMatch);
}
private void LogException(IMapping mapping, Exception ex)
{
_options.Logger.Error($"Getting a Request MatchResult for Mapping '{mapping.Guid}' failed. This mapping will not be evaluated. Exception: {ex}");
}
private string? GetNextState(IMapping mapping)
{
// If the mapping does not have a scenario or _options.Scenarios does not contain this scenario from the mapping,

View File

@@ -9,9 +9,8 @@ using JetBrains.Annotations;
using WireMock.Logging;
using WireMock.Owin.Mappers;
using Stef.Validation;
using RandomDataGenerator.FieldOptions;
using RandomDataGenerator.Randomizers;
using WireMock.Services;
using WireMock.Util;
namespace WireMock.Owin;
@@ -63,7 +62,7 @@ internal class OwinSelfHost : IOwinSelfHost
private void StartServers()
{
#if NET46
_logger.Info("Server using .net 4.6.1 or higher");
_logger.Info("Server using .net 4.6");
#else
_logger.Info("Server using .net 4.5.x");
#endif
@@ -74,12 +73,13 @@ internal class OwinSelfHost : IOwinSelfHost
var requestMapper = new OwinRequestMapper();
var responseMapper = new OwinResponseMapper(_options);
var matcher = new MappingMatcher(_options, new RandomizerDoubleBetween0And1());
var guidUtils = new GuidUtils();
Action<IAppBuilder> startup = app =>
{
app.Use<GlobalExceptionMiddleware>(_options, responseMapper);
_options.PreWireMockMiddlewareInit?.Invoke(app);
app.Use<WireMockMiddleware>(_options, requestMapper, responseMapper, matcher);
app.Use<WireMockMiddleware>(_options, requestMapper, responseMapper, matcher, guidUtils);
_options.PostWireMockMiddlewareInit?.Invoke(app);
};

View File

@@ -12,6 +12,8 @@ using WireMock.Types;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using System.Collections.Generic;
using WireMock.Constants;
using WireMock.Util;
#if !USE_ASPNETCORE
using IContext = Microsoft.Owin.IOwinContext;
using OwinMiddleware = Microsoft.Owin.OwinMiddleware;
@@ -28,29 +30,47 @@ namespace WireMock.Owin
{
private readonly object _lock = new();
private static readonly Task CompletedTask = Task.FromResult(false);
private readonly IWireMockMiddlewareOptions _options;
private readonly IOwinRequestMapper _requestMapper;
private readonly IOwinResponseMapper _responseMapper;
private readonly IMappingMatcher _mappingMatcher;
private readonly LogEntryMapper _logEntryMapper;
private readonly IGuidUtils _guidUtils;
#if !USE_ASPNETCORE
public WireMockMiddleware(Next next, IWireMockMiddlewareOptions options, IOwinRequestMapper requestMapper, IOwinResponseMapper responseMapper, IMappingMatcher mappingMatcher) : base(next)
public WireMockMiddleware(
Next next,
IWireMockMiddlewareOptions options,
IOwinRequestMapper requestMapper,
IOwinResponseMapper responseMapper,
IMappingMatcher mappingMatcher,
IGuidUtils guidUtils
) : base(next)
{
_options = Guard.NotNull(options);
_requestMapper = Guard.NotNull(requestMapper);
_responseMapper = Guard.NotNull(responseMapper);
_mappingMatcher = Guard.NotNull(mappingMatcher);
_logEntryMapper = new LogEntryMapper(options);
_guidUtils = Guard.NotNull(guidUtils);
}
#else
public WireMockMiddleware(Next next, IWireMockMiddlewareOptions options, IOwinRequestMapper requestMapper, IOwinResponseMapper responseMapper, IMappingMatcher mappingMatcher)
public WireMockMiddleware(
Next next,
IWireMockMiddlewareOptions options,
IOwinRequestMapper requestMapper,
IOwinResponseMapper responseMapper,
IMappingMatcher mappingMatcher,
IGuidUtils guidUtils
)
{
_options = Guard.NotNull(options);
_requestMapper = Guard.NotNull(requestMapper);
_responseMapper = Guard.NotNull(responseMapper);
_mappingMatcher = Guard.NotNull(mappingMatcher);
_logEntryMapper = new LogEntryMapper(options);
_guidUtils = Guard.NotNull(guidUtils);
}
#endif
@@ -105,7 +125,7 @@ namespace WireMock.Owin
{
logRequest = true;
_options.Logger.Warn("HttpStatusCode set to 404 : No matching mapping found");
response = ResponseMessageBuilder.Create("No matching mapping found", 404);
response = ResponseMessageBuilder.Create(HttpStatusCode.NotFound, WireMockConstants.NoMatchingFound);
return;
}
@@ -113,11 +133,11 @@ namespace WireMock.Owin
if (targetMapping.IsAdminInterface && _options.AuthenticationMatcher != null && request.Headers != null)
{
bool present = request.Headers.TryGetValue(HttpKnownHeaderNames.Authorization, out WireMockList<string> authorization);
if (!present || _options.AuthenticationMatcher.IsMatch(authorization!.ToString()) < MatchScores.Perfect)
bool present = request.Headers.TryGetValue(HttpKnownHeaderNames.Authorization, out WireMockList<string>? authorization);
if (!present || _options.AuthenticationMatcher.IsMatch(authorization!.ToString()).Score < MatchScores.Perfect)
{
_options.Logger.Error("HttpStatusCode set to 401");
response = ResponseMessageBuilder.Create(null, HttpStatusCode.Unauthorized);
response = ResponseMessageBuilder.Create(HttpStatusCode.Unauthorized, null);
return;
}
}
@@ -161,14 +181,14 @@ namespace WireMock.Owin
}
catch (Exception ex)
{
_options.Logger.Error($"Providing a Response for Mapping '{result.Match?.Mapping?.Guid}' failed. HttpStatusCode set to 500. Exception: {ex}");
response = ResponseMessageBuilder.Create(ex.Message, 500);
_options.Logger.Error($"Providing a Response for Mapping '{result.Match?.Mapping.Guid}' failed. HttpStatusCode set to 500. Exception: {ex}");
response = ResponseMessageBuilder.Create(500, ex.Message);
}
finally
{
var log = new LogEntry
{
Guid = Guid.NewGuid(),
Guid = _guidUtils.NewGuid(),
RequestMessage = request,
ResponseMessage = response,
@@ -185,10 +205,10 @@ namespace WireMock.Owin
try
{
if (_options.SaveUnmatchedRequests == true && result.Match?.RequestMatchResult.IsPerfectMatch != true)
if (_options.SaveUnmatchedRequests == true && result.Match?.RequestMatchResult is not { IsPerfectMatch: true })
{
var filename = $"{log.Guid}.LogEntry.json";
_options.FileSystemHandler?.WriteUnmatchedRequest(filename, Util.JsonUtils.Serialize(log));
_options.FileSystemHandler?.WriteUnmatchedRequest(filename, JsonUtils.Serialize(log));
}
}
catch
@@ -196,7 +216,17 @@ namespace WireMock.Owin
// Empty catch
}
await _responseMapper.MapAsync(response, ctx.Response).ConfigureAwait(false);
try
{
await _responseMapper.MapAsync(response, ctx.Response).ConfigureAwait(false);
}
catch (Exception ex)
{
_options.Logger.Error("HttpStatusCode set to 404 : No matching mapping found", ex);
var notFoundResponse = ResponseMessageBuilder.Create(HttpStatusCode.NotFound, WireMockConstants.NoMatchingFound);
await _responseMapper.MapAsync(notFoundResponse, ctx.Response).ConfigureAwait(false);
}
}
await CompletedTask.ConfigureAwait(false);

View File

@@ -4,11 +4,11 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Newtonsoft.Json;
#if USE_ASPNETCORE
using System.Security.Cryptography.X509Certificates;
#endif
using Stef.Validation;
using WireMock.Http;
using WireMock.Models;
using WireMock.Owin;
using WireMock.Types;
@@ -80,6 +80,7 @@ public class RequestMessage : IRequestMessage
#if MIMEKIT
/// <inheritdoc />
[JsonIgnore] // Issue 1001
public object? BodyAsMimeMessage { get; }
#endif
@@ -112,7 +113,7 @@ public class RequestMessage : IRequestMessage
/// <summary>
/// Used for Unit Testing
/// </summary>
public RequestMessage(
internal RequestMessage(
UrlDetails urlDetails,
string method,
string clientIP,
@@ -122,19 +123,6 @@ public class RequestMessage : IRequestMessage
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessage"/> class.
/// </summary>
/// <param name="options">The<seealso cref="IWireMockMiddlewareOptions"/>.</param>
/// <param name="urlDetails">The original url details.</param>
/// <param name="method">The HTTP method.</param>
/// <param name="clientIP">The client IP Address.</param>
/// <param name="bodyData">The BodyData.</param>
/// <param name="headers">The headers.</param>
/// <param name="cookies">The cookies.</param>
#if USE_ASPNETCORE
/// <param name="clientCertificate">The client certificate</param>
#endif
internal RequestMessage(
IWireMockMiddlewareOptions? options,
UrlDetails urlDetails, string method,
@@ -177,25 +165,29 @@ public class RequestMessage : IRequestMessage
DetectedBodyTypeFromContentType = BodyData?.DetectedBodyTypeFromContentType.ToString();
DetectedCompression = BodyData?.DetectedCompression;
#if MIMEKIT
try
{
BodyAsMimeMessage = MimeKitUtils.GetMimeMessage(BodyData, headers![HttpKnownHeaderNames.ContentType].First());
}
catch
{
// Ignore exception from MimeMessage.Load
}
#endif
Headers = headers?.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
Cookies = cookies;
RawQuery = urlDetails.Url.Query;
Query = QueryStringParser.Parse(RawQuery, options?.QueryParameterMultipleValueSupport);
QueryIgnoreCase = new Dictionary<string, WireMockList<string>>(Query, StringComparer.OrdinalIgnoreCase);
#if USE_ASPNETCORE
ClientCertificate = clientCertificate;
#endif
#if MIMEKIT
try
{
if (MimeKitUtils.TryGetMimeMessage(this, out var mimeMessage))
{
BodyAsMimeMessage = mimeMessage;
}
}
catch
{
// Ignore exception from MimeMessage.Load
}
#endif
}
/// <inheritdoc />

View File

@@ -16,12 +16,17 @@ internal static class ResponseMessageBuilder
{ HttpKnownHeaderNames.ContentType, new WireMockList<string> { WireMockConstants.ContentTypeJson } }
};
internal static ResponseMessage Create(string? message, HttpStatusCode statusCode, Guid? guid = null)
internal static ResponseMessage Create(HttpStatusCode statusCode, string? status, Guid? guid = null)
{
return Create(message, (int)statusCode, guid);
return Create((int)statusCode, status, guid);
}
internal static ResponseMessage Create(string? message, int statusCode = 200, Guid? guid = null)
internal static ResponseMessage Create(int statusCode, string? status, Guid? guid = null)
{
return Create(statusCode, status, null, guid);
}
internal static ResponseMessage Create(int statusCode, string? status, string? error, Guid? guid = null)
{
var response = new ResponseMessage
{
@@ -29,7 +34,7 @@ internal static class ResponseMessageBuilder
Headers = ContentTypeJsonHeaders
};
if (message != null)
if (status != null || error != null)
{
response.BodyData = new BodyData
{
@@ -37,7 +42,8 @@ internal static class ResponseMessageBuilder
BodyAsJson = new StatusModel
{
Guid = guid,
Status = message
Status = status,
Error = error
}
};
}
@@ -45,7 +51,7 @@ internal static class ResponseMessageBuilder
return response;
}
internal static ResponseMessage Create(int statusCode)
internal static ResponseMessage Create(HttpStatusCode statusCode)
{
return new ResponseMessage
{

View File

@@ -139,7 +139,6 @@ internal class MappingConverter
sb.AppendLine(@$" .WithBody(new JsonPartialMatcher(
value: {ToCSharpStringLiteral(jsonPartialMatcher.Value.ToString())},
ignoreCase: {ToCSharpBooleanLiteral(jsonPartialMatcher.IgnoreCase)},
throwException: {ToCSharpBooleanLiteral(jsonPartialMatcher.ThrowException)},
regex: {ToCSharpBooleanLiteral(jsonPartialMatcher.Regex)}
))");
}
@@ -148,7 +147,6 @@ internal class MappingConverter
sb.AppendLine(@$" .WithBody(new JsonPartialWildcardMatcher(
value: {ToCSharpStringLiteral(jsonPartialWildcardMatcher.Value.ToString())},
ignoreCase: {ToCSharpBooleanLiteral(jsonPartialWildcardMatcher.IgnoreCase)},
throwException: {ToCSharpBooleanLiteral(jsonPartialWildcardMatcher.ThrowException)},
regex: {ToCSharpBooleanLiteral(jsonPartialWildcardMatcher.Regex)}
))");
}
@@ -261,19 +259,24 @@ internal class MappingConverter
Headers = headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel
{
Name = hm.Name,
Matchers = _mapper.Map(hm.Matchers)
IgnoreCase = hm.IgnoreCase ? true : null,
RejectOnMatch = hm.MatchBehaviour == MatchBehaviour.RejectOnMatch ? true : null,
Matchers = _mapper.Map(hm.Matchers),
}).ToList() : null,
Cookies = cookieMatchers.Any() ? cookieMatchers.Select(cm => new CookieModel
{
Name = cm.Name,
IgnoreCase = cm.IgnoreCase ? true : null,
RejectOnMatch = cm.MatchBehaviour == MatchBehaviour.RejectOnMatch ? true : null,
Matchers = _mapper.Map(cm.Matchers)
}).ToList() : null,
Params = paramsMatchers.Any() ? paramsMatchers.Select(pm => new ParamModel
{
Name = pm.Key,
IgnoreCase = pm.IgnoreCase == true ? true : null,
IgnoreCase = pm.IgnoreCase ? true : null,
RejectOnMatch = pm.MatchBehaviour == MatchBehaviour.RejectOnMatch ? true : null,
Matchers = _mapper.Map(pm.Matchers)
}).ToList() : null
},

View File

@@ -46,7 +46,6 @@ internal class MatcherMapper
var matchBehaviour = matcher.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch;
var matchOperator = StringUtils.ParseMatchOperator(matcher.MatchOperator);
bool ignoreCase = matcher.IgnoreCase == true;
bool throwExceptionWhenMatcherFails = _settings.ThrowExceptionWhenMatcherFails == true;
bool useRegexExtended = _settings.UseRegexExtended == true;
bool useRegex = matcher.Regex == true;
@@ -64,51 +63,51 @@ internal class MatcherMapper
throw new NotSupportedException("It's not allowed to use the 'CSharpCodeMatcher' because WireMockServerSettings.AllowCSharpCodeMatcher is not set to 'true'.");
case nameof(LinqMatcher):
return new LinqMatcher(matchBehaviour, throwExceptionWhenMatcherFails, matchOperator, stringPatterns);
return new LinqMatcher(matchBehaviour, matchOperator, stringPatterns);
case nameof(ExactMatcher):
return new ExactMatcher(matchBehaviour, ignoreCase, throwExceptionWhenMatcherFails, matchOperator, stringPatterns);
return new ExactMatcher(matchBehaviour, ignoreCase, matchOperator, stringPatterns);
case nameof(ExactObjectMatcher):
return CreateExactObjectMatcher(matchBehaviour, stringPatterns[0], throwExceptionWhenMatcherFails);
return CreateExactObjectMatcher(matchBehaviour, stringPatterns[0]);
#if GRAPHQL
case nameof(GraphQLMatcher):
return new GraphQLMatcher(stringPatterns[0].GetPattern(), matchBehaviour, throwExceptionWhenMatcherFails, matchOperator);
return new GraphQLMatcher(stringPatterns[0].GetPattern(), matchBehaviour, matchOperator);
#endif
#if MIMEKIT
case nameof(MimePartMatcher):
return CreateMimePartMatcher(matchBehaviour, matcher, throwExceptionWhenMatcherFails);
return CreateMimePartMatcher(matchBehaviour, matcher);
#endif
case nameof(RegexMatcher):
return new RegexMatcher(matchBehaviour, stringPatterns, ignoreCase, throwExceptionWhenMatcherFails, useRegexExtended, matchOperator);
return new RegexMatcher(matchBehaviour, stringPatterns, ignoreCase, useRegexExtended, matchOperator);
case nameof(JsonMatcher):
var valueForJsonMatcher = matcher.Pattern ?? matcher.Patterns;
return new JsonMatcher(matchBehaviour, valueForJsonMatcher!, ignoreCase, throwExceptionWhenMatcherFails);
return new JsonMatcher(matchBehaviour, valueForJsonMatcher!, ignoreCase);
case nameof(JsonPartialMatcher):
var valueForJsonPartialMatcher = matcher.Pattern ?? matcher.Patterns;
return new JsonPartialMatcher(matchBehaviour, valueForJsonPartialMatcher!, ignoreCase, throwExceptionWhenMatcherFails, useRegex);
return new JsonPartialMatcher(matchBehaviour, valueForJsonPartialMatcher!, ignoreCase, useRegex);
case nameof(JsonPartialWildcardMatcher):
var valueForJsonPartialWildcardMatcher = matcher.Pattern ?? matcher.Patterns;
return new JsonPartialWildcardMatcher(matchBehaviour, valueForJsonPartialWildcardMatcher!, ignoreCase, throwExceptionWhenMatcherFails, useRegex);
return new JsonPartialWildcardMatcher(matchBehaviour, valueForJsonPartialWildcardMatcher!, ignoreCase, useRegex);
case nameof(JsonPathMatcher):
return new JsonPathMatcher(matchBehaviour, throwExceptionWhenMatcherFails, matchOperator, stringPatterns);
return new JsonPathMatcher(matchBehaviour, matchOperator, stringPatterns);
case nameof(JmesPathMatcher):
return new JmesPathMatcher(matchBehaviour, throwExceptionWhenMatcherFails, matchOperator, stringPatterns);
return new JmesPathMatcher(matchBehaviour, matchOperator, stringPatterns);
case nameof(XPathMatcher):
return new XPathMatcher(matchBehaviour, throwExceptionWhenMatcherFails, matchOperator, stringPatterns);
return new XPathMatcher(matchBehaviour, matchOperator, stringPatterns);
case nameof(WildcardMatcher):
return new WildcardMatcher(matchBehaviour, stringPatterns, ignoreCase, throwExceptionWhenMatcherFails, matchOperator);
return new WildcardMatcher(matchBehaviour, stringPatterns, ignoreCase, matchOperator);
case nameof(ContentTypeMatcher):
return new ContentTypeMatcher(matchBehaviour, stringPatterns, ignoreCase, throwExceptionWhenMatcherFails);
return new ContentTypeMatcher(matchBehaviour, stringPatterns, ignoreCase);
case nameof(SimMetricsMatcher):
SimMetricType type = SimMetricType.Levenstein;
@@ -117,7 +116,7 @@ internal class MatcherMapper
throw new NotSupportedException($"Matcher '{matcherName}' with Type '{matcherType}' is not supported.");
}
return new SimMetricsMatcher(matchBehaviour, stringPatterns, type, throwExceptionWhenMatcherFails);
return new SimMetricsMatcher(matchBehaviour, stringPatterns, type);
default:
if (_settings.CustomMatcherMappings != null && _settings.CustomMatcherMappings.ContainsKey(matcherName))
@@ -236,7 +235,7 @@ internal class MatcherMapper
return EmptyArray<AnyOf<string, StringPattern>>.Value;
}
private static ExactObjectMatcher CreateExactObjectMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern> stringPattern, bool throwException)
private static ExactObjectMatcher CreateExactObjectMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern> stringPattern)
{
byte[] bytePattern;
try
@@ -248,18 +247,18 @@ internal class MatcherMapper
throw new ArgumentException($"Matcher 'ExactObjectMatcher' has invalid pattern. The pattern value '{stringPattern}' is not a Base64String.", nameof(stringPattern));
}
return new ExactObjectMatcher(matchBehaviour, bytePattern, throwException);
return new ExactObjectMatcher(matchBehaviour, bytePattern);
}
#if MIMEKIT
private MimePartMatcher CreateMimePartMatcher(MatchBehaviour matchBehaviour, MatcherModel? matcher, bool throwExceptionWhenMatcherFails)
private MimePartMatcher CreateMimePartMatcher(MatchBehaviour matchBehaviour, MatcherModel? matcher)
{
var contentTypeMatcher = Map(matcher?.ContentTypeMatcher) as IStringMatcher;
var contentDispositionMatcher = Map(matcher?.ContentDispositionMatcher) as IStringMatcher;
var contentTransferEncodingMatcher = Map(matcher?.ContentTransferEncodingMatcher) as IStringMatcher;
var contentMatcher = Map(matcher?.ContentMatcher);
return new MimePartMatcher(matchBehaviour, contentTypeMatcher, contentDispositionMatcher, contentTransferEncodingMatcher, contentMatcher, throwExceptionWhenMatcherFails);
return new MimePartMatcher(matchBehaviour, contentTypeMatcher, contentDispositionMatcher, contentTransferEncodingMatcher, contentMatcher);
}
#endif
}

View File

@@ -30,8 +30,8 @@ internal class ProxyMappingConverter
{
var useDefinedRequestMatchers = proxyAndRecordSettings.UseDefinedRequestMatchers;
var excludedHeaders = new List<string>(proxyAndRecordSettings.ExcludedHeaders ?? new string[] { }) { "Cookie" };
var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[0];
var excludedParams = proxyAndRecordSettings.ExcludedParams ?? new string[0];
var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? EmptyArray<string>.Value;
var excludedParams = proxyAndRecordSettings.ExcludedParams ?? EmptyArray<string>.Value;
var request = (Request?)mapping?.RequestMatcher;
var clientIPMatcher = request?.GetRequestMessageMatcher<RequestMessageClientIPMatcher>();
@@ -137,7 +137,6 @@ internal class ProxyMappingConverter
}
// Body
bool throwExceptionWhenMatcherFails = _settings.ThrowExceptionWhenMatcherFails == true;
if (useDefinedRequestMatchers && bodyMatcher?.Matchers is not null)
{
newRequest.WithBody(bodyMatcher.Matchers);
@@ -147,16 +146,16 @@ internal class ProxyMappingConverter
switch (requestMessage.BodyData?.DetectedBodyType)
{
case BodyType.Json:
newRequest.WithBody(new JsonMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsJson!, true, throwExceptionWhenMatcherFails));
newRequest.WithBody(new JsonMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsJson!, true));
break;
case BodyType.String:
case BodyType.FormUrlEncoded:
newRequest.WithBody(new ExactMatcher(MatchBehaviour.AcceptOnMatch, true, throwExceptionWhenMatcherFails, MatchOperator.Or, requestMessage.BodyData.BodyAsString!));
newRequest.WithBody(new ExactMatcher(MatchBehaviour.AcceptOnMatch, true, MatchOperator.Or, requestMessage.BodyData.BodyAsString!));
break;
case BodyType.Bytes:
newRequest.WithBody(new ExactObjectMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsBytes!, throwExceptionWhenMatcherFails));
newRequest.WithBody(new ExactObjectMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsBytes!));
break;
}
}

View File

@@ -237,7 +237,6 @@ public partial class WireMockServer
ReadStaticMappings = _settings.ReadStaticMappings,
RequestLogExpirationDuration = _settings.RequestLogExpirationDuration,
SaveUnmatchedRequests = _settings.SaveUnmatchedRequests,
ThrowExceptionWhenMatcherFails = _settings.ThrowExceptionWhenMatcherFails,
UseRegexExtended = _settings.UseRegexExtended,
WatchStaticMappings = _settings.WatchStaticMappings,
WatchStaticMappingsInSubdirectories = _settings.WatchStaticMappingsInSubdirectories,
@@ -273,7 +272,6 @@ public partial class WireMockServer
_settings.ReadStaticMappings = settings.ReadStaticMappings;
_settings.RequestLogExpirationDuration = settings.RequestLogExpirationDuration;
_settings.SaveUnmatchedRequests = settings.SaveUnmatchedRequests;
_settings.ThrowExceptionWhenMatcherFails = settings.ThrowExceptionWhenMatcherFails;
_settings.UseRegexExtended = settings.UseRegexExtended;
_settings.WatchStaticMappings = settings.WatchStaticMappings;
_settings.WatchStaticMappingsInSubdirectories = settings.WatchStaticMappingsInSubdirectories;
@@ -303,7 +301,7 @@ public partial class WireMockServer
_options.AcceptAnyClientCertificate = _settings.AcceptAnyClientCertificate;
#endif
return ResponseMessageBuilder.Create("Settings updated");
return ResponseMessageBuilder.Create(200, "Settings updated");
}
#endregion Settings
@@ -314,7 +312,7 @@ public partial class WireMockServer
if (mapping == null)
{
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
return ResponseMessageBuilder.Create("Mapping not found", HttpStatusCode.NotFound);
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
}
var model = _mappingConverter.ToMappingModel(mapping);
@@ -330,14 +328,14 @@ public partial class WireMockServer
if (code is null)
{
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
return ResponseMessageBuilder.Create("Mapping not found", HttpStatusCode.NotFound);
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
}
return ToResponseMessage(code);
}
_settings.Logger.Warn("HttpStatusCode set to 400");
return ResponseMessageBuilder.Create("GUID is missing", HttpStatusCode.BadRequest);
return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, "GUID is missing");
}
private static MappingConverterType GetMappingConverterType(IRequestMessage requestMessage)
@@ -365,22 +363,22 @@ public partial class WireMockServer
var mappingModel = DeserializeObject<MappingModel>(requestMessage);
var guidFromPut = ConvertMappingAndRegisterAsRespondProvider(mappingModel, guid);
return ResponseMessageBuilder.Create("Mapping added or updated", HttpStatusCode.OK, guidFromPut);
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "Mapping added or updated", guidFromPut);
}
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
return ResponseMessageBuilder.Create("Mapping not found", HttpStatusCode.NotFound);
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
}
private IResponseMessage MappingDelete(IRequestMessage requestMessage)
{
if (TryParseGuidFromRequestMessage(requestMessage, out var guid) && DeleteMapping(guid))
{
return ResponseMessageBuilder.Create("Mapping removed", HttpStatusCode.OK, guid);
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "Mapping removed", guid);
}
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
return ResponseMessageBuilder.Create("Mapping not found", HttpStatusCode.NotFound);
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
}
private static bool TryParseGuidFromRequestMessage(IRequestMessage requestMessage, out Guid guid)
@@ -409,7 +407,7 @@ public partial class WireMockServer
{
SaveStaticMappings();
return ResponseMessageBuilder.Create("Mappings saved to disk");
return ResponseMessageBuilder.Create(200, "Mappings saved to disk");
}
private MappingModel[] ToMappingModels()
@@ -439,22 +437,22 @@ public partial class WireMockServer
if (mappingModels.Length == 1)
{
Guid? guid = ConvertMappingAndRegisterAsRespondProvider(mappingModels[0]);
return ResponseMessageBuilder.Create("Mapping added", 201, guid);
return ResponseMessageBuilder.Create(201, "Mapping added", guid);
}
ConvertMappingsAndRegisterAsRespondProvider(mappingModels);
return ResponseMessageBuilder.Create("Mappings added", 201);
return ResponseMessageBuilder.Create(201, "Mappings added");
}
catch (ArgumentException a)
{
_settings.Logger.Error("HttpStatusCode set to 400 {0}", a);
return ResponseMessageBuilder.Create(a.Message, 400);
return ResponseMessageBuilder.Create(400, a.Message);
}
catch (Exception e)
{
_settings.Logger.Error("HttpStatusCode set to 500 {0}", e);
return ResponseMessageBuilder.Create(e.ToString(), 500);
return ResponseMessageBuilder.Create(500, e.ToString());
}
}
@@ -465,18 +463,18 @@ public partial class WireMockServer
var deletedGuids = MappingsDeleteMappingFromBody(requestMessage);
if (deletedGuids != null)
{
return ResponseMessageBuilder.Create($"Mappings deleted. Affected GUIDs: [{string.Join(", ", deletedGuids.ToArray())}]");
return ResponseMessageBuilder.Create(200, $"Mappings deleted. Affected GUIDs: [{string.Join(", ", deletedGuids.ToArray())}]");
}
// return bad request
return ResponseMessageBuilder.Create("Poorly formed mapping JSON.", 400);
return ResponseMessageBuilder.Create(400, "Poorly formed mapping JSON.");
}
ResetMappings();
ResetScenarios();
return ResponseMessageBuilder.Create("Mappings deleted");
return ResponseMessageBuilder.Create(200, "Mappings deleted");
}
private IEnumerable<Guid>? MappingsDeleteMappingFromBody(IRequestMessage requestMessage)
@@ -528,7 +526,7 @@ public partial class WireMockServer
message = $"{message} and static mappings reloaded";
}
return ResponseMessageBuilder.Create(message);
return ResponseMessageBuilder.Create(200, message);
}
#endregion Mappings
@@ -546,18 +544,18 @@ public partial class WireMockServer
}
_settings.Logger.Warn("HttpStatusCode set to 404 : Request not found");
return ResponseMessageBuilder.Create("Request not found", HttpStatusCode.NotFound);
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Request not found");
}
private IResponseMessage RequestDelete(IRequestMessage requestMessage)
{
if (TryParseGuidFromRequestMessage(requestMessage, out var guid) && DeleteLogEntry(guid))
{
return ResponseMessageBuilder.Create("Request removed");
return ResponseMessageBuilder.Create(200, "Request removed");
}
_settings.Logger.Warn("HttpStatusCode set to 404 : Request not found");
return ResponseMessageBuilder.Create("Request not found", HttpStatusCode.NotFound);
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Request not found");
}
#endregion Request/{guid}
@@ -576,7 +574,7 @@ public partial class WireMockServer
{
ResetLogEntries();
return ResponseMessageBuilder.Create("Requests deleted");
return ResponseMessageBuilder.Create(200, "Requests deleted");
}
#endregion Requests
@@ -623,7 +621,7 @@ public partial class WireMockServer
{
ResetScenarios();
return ResponseMessageBuilder.Create("Scenarios reset");
return ResponseMessageBuilder.Create(200, "Scenarios reset");
}
private IResponseMessage ScenarioReset(IRequestMessage requestMessage)
@@ -633,8 +631,8 @@ public partial class WireMockServer
requestMessage.Path.Split('/').Reverse().Skip(1).First();
return ResetScenario(name) ?
ResponseMessageBuilder.Create("Scenario reset") :
ResponseMessageBuilder.Create($"No scenario found by name '{name}'.", HttpStatusCode.NotFound);
ResponseMessageBuilder.Create(200, "Scenario reset") :
ResponseMessageBuilder.Create(HttpStatusCode.NotFound, $"No scenario found by name '{name}'.");
}
#endregion

View File

@@ -1,5 +1,6 @@
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using WireMock.Matchers;
using WireMock.Types;
@@ -25,7 +26,7 @@ namespace WireMock.Server
_settings.FileSystemHandler.WriteFile(filename, requestMessage.BodyAsBytes);
return ResponseMessageBuilder.Create("File created");
return ResponseMessageBuilder.Create(200, "File created");
}
private IResponseMessage FilePut(IRequestMessage requestMessage)
@@ -35,12 +36,12 @@ namespace WireMock.Server
if (!_settings.FileSystemHandler.FileExists(filename))
{
_settings.Logger.Info("The file '{0}' does not exist, updating file will be skipped.", filename);
return ResponseMessageBuilder.Create("File is not found", 404);
return ResponseMessageBuilder.Create(404, "File is not found");
}
_settings.FileSystemHandler.WriteFile(filename, requestMessage.BodyAsBytes);
return ResponseMessageBuilder.Create("File updated");
return ResponseMessageBuilder.Create(200, "File updated");
}
private IResponseMessage FileGet(IRequestMessage requestMessage)
@@ -50,7 +51,7 @@ namespace WireMock.Server
if (!_settings.FileSystemHandler.FileExists(filename))
{
_settings.Logger.Info("The file '{0}' does not exist.", filename);
return ResponseMessageBuilder.Create("File is not found", 404);
return ResponseMessageBuilder.Create(404, "File is not found");
}
byte[] bytes = _settings.FileSystemHandler.ReadFile(filename);
@@ -86,10 +87,10 @@ namespace WireMock.Server
if (!_settings.FileSystemHandler.FileExists(filename))
{
_settings.Logger.Info("The file '{0}' does not exist.", filename);
return ResponseMessageBuilder.Create(404);
return ResponseMessageBuilder.Create(HttpStatusCode.NotFound);
}
return ResponseMessageBuilder.Create(204);
return ResponseMessageBuilder.Create(HttpStatusCode.NoContent);
}
private IResponseMessage FileDelete(IRequestMessage requestMessage)
@@ -99,11 +100,11 @@ namespace WireMock.Server
if (!_settings.FileSystemHandler.FileExists(filename))
{
_settings.Logger.Info("The file '{0}' does not exist.", filename);
return ResponseMessageBuilder.Create("File is not deleted", 404);
return ResponseMessageBuilder.Create(404, "File is not deleted");
}
_settings.FileSystemHandler.DeleteFile(filename);
return ResponseMessageBuilder.Create("File deleted.");
return ResponseMessageBuilder.Create(200, "File deleted.");
}
private string GetFileNameFromRequestMessage(IRequestMessage requestMessage)

View File

@@ -52,7 +52,7 @@ public partial class WireMockServer
if (mappingModels.Length == 1)
{
Guid? guid = ConvertWireMockOrgMappingAndRegisterAsRespondProvider(mappingModels[0]);
return ResponseMessageBuilder.Create("Mapping added", 201, guid);
return ResponseMessageBuilder.Create(201, "Mapping added", guid);
}
foreach (var mappingModel in mappingModels)
@@ -60,17 +60,17 @@ public partial class WireMockServer
ConvertWireMockOrgMappingAndRegisterAsRespondProvider(mappingModel);
}
return ResponseMessageBuilder.Create("Mappings added", 201);
return ResponseMessageBuilder.Create(201, "Mappings added");
}
catch (ArgumentException a)
{
_settings.Logger.Error("HttpStatusCode set to 400 {0}", a);
return ResponseMessageBuilder.Create(a.Message, 400);
return ResponseMessageBuilder.Create(400, a.Message);
}
catch (Exception e)
{
_settings.Logger.Error("HttpStatusCode set to 500 {0}", e);
return ResponseMessageBuilder.Create(e.ToString(), 500);
return ResponseMessageBuilder.Create(500, e.ToString());
}
}

View File

@@ -1,7 +1,7 @@
using System.Net;
#if OPENAPIPARSER
using System;
using System.Linq;
using System.Net;
using WireMock.Net.OpenApiParser;
#endif
@@ -14,16 +14,16 @@ public partial class WireMockServer
#if OPENAPIPARSER
try
{
var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body, out var diagnostic);
var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body!, out var diagnostic);
return diagnostic.Errors.Any() ? ToJson(diagnostic, false, HttpStatusCode.BadRequest) : ToJson(mappingModels);
}
catch (Exception e)
{
_settings.Logger.Error("HttpStatusCode set to {0} {1}", HttpStatusCode.BadRequest, e);
return ResponseMessageBuilder.Create(e.Message, HttpStatusCode.BadRequest);
return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, e.Message);
}
#else
return ResponseMessageBuilder.Create("Not supported for .NETStandard 1.3 and .NET 4.5.2 or lower.", 400);
return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, "Not supported for .NETStandard 1.3 and .NET 4.5.2 or lower.");
#endif
}
@@ -32,7 +32,7 @@ public partial class WireMockServer
#if OPENAPIPARSER
try
{
var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body, out var diagnostic);
var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body!, out var diagnostic);
if (diagnostic.Errors.Any())
{
return ToJson(diagnostic, false, HttpStatusCode.BadRequest);
@@ -40,15 +40,15 @@ public partial class WireMockServer
ConvertMappingsAndRegisterAsRespondProvider(mappingModels);
return ResponseMessageBuilder.Create("OpenApi document converted to Mappings", HttpStatusCode.Created);
return ResponseMessageBuilder.Create(HttpStatusCode.Created, "OpenApi document converted to Mappings");
}
catch (Exception e)
{
_settings.Logger.Error("HttpStatusCode set to {0} {1}", HttpStatusCode.BadRequest, e);
return ResponseMessageBuilder.Create(e.Message, HttpStatusCode.BadRequest);
return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, e.Message);
}
#else
return ResponseMessageBuilder.Create("Not supported for .NETStandard 1.3 and .NET 4.5.2 or lower.", 400);
return ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, "Not supported for .NETStandard 1.3 and .NET 4.5.2 or lower.");
#endif
}
}

View File

@@ -4,6 +4,7 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using JetBrains.Annotations;
@@ -11,6 +12,7 @@ using Newtonsoft.Json;
using Stef.Validation;
using WireMock.Admin.Mappings;
using WireMock.Authentication;
using WireMock.Constants;
using WireMock.Exceptions;
using WireMock.Handlers;
using WireMock.Http;
@@ -391,7 +393,7 @@ public partial class WireMockServer : IWireMockServer
Given(Request.Create().WithPath("/*").UsingAnyMethod())
.WithGuid(Guid.Parse("90008000-0000-4444-a17e-669cd84f1f05"))
.AtPriority(1000)
.RespondWith(new DynamicResponseProvider(_ => ResponseMessageBuilder.Create("No matching mapping found", 404)));
.RespondWith(new DynamicResponseProvider(_ => ResponseMessageBuilder.Create(HttpStatusCode.NotFound, WireMockConstants.NoMatchingFound)));
}
/// <inheritdoc cref="IWireMockServer.Reset" />

View File

@@ -221,12 +221,6 @@ public class WireMockServerSettings
[PublicAPI]
public bool? HandleRequestsSynchronously { get; set; }
/// <summary>
/// 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; }
/// <summary>
/// If https is used, these settings can be used to configure the CertificateSettings in case a custom certificate instead the default .NET certificate should be used.
///

View File

@@ -57,7 +57,6 @@ public static class WireMockServerSettingsParser
SaveUnmatchedRequests = parser.GetBoolValue(nameof(WireMockServerSettings.SaveUnmatchedRequests)),
StartAdminInterface = parser.GetBoolValue("StartAdminInterface", true),
StartTimeout = parser.GetIntValue(nameof(WireMockServerSettings.StartTimeout), WireMockServerSettings.DefaultStartTimeout),
ThrowExceptionWhenMatcherFails = parser.GetBoolValue("ThrowExceptionWhenMatcherFails"),
UseRegexExtended = parser.GetBoolValue(nameof(WireMockServerSettings.UseRegexExtended), true),
WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"),
WatchStaticMappingsInSubdirectories = parser.GetBoolValue("WatchStaticMappingsInSubdirectories"),

View File

@@ -85,27 +85,27 @@ internal static class BodyParser
public static BodyType DetectBodyTypeFromContentType(string? contentTypeValue)
{
if (string.IsNullOrEmpty(contentTypeValue) || !MediaTypeHeaderValue.TryParse(contentTypeValue, out MediaTypeHeaderValue contentType))
if (string.IsNullOrEmpty(contentTypeValue) || !MediaTypeHeaderValue.TryParse(contentTypeValue, out MediaTypeHeaderValue? contentType))
{
return BodyType.Bytes;
}
if (MatchScores.IsPerfect(FormUrlEncodedMatcher.IsMatch(contentType.MediaType)))
if (FormUrlEncodedMatcher.IsMatch(contentType.MediaType).IsPerfect())
{
return BodyType.FormUrlEncoded;
}
if (TextContentTypeMatchers.Any(matcher => MatchScores.IsPerfect(matcher.IsMatch(contentType.MediaType))))
if (TextContentTypeMatchers.Any(matcher => matcher.IsMatch(contentType.MediaType).IsPerfect()))
{
return BodyType.String;
}
if (JsonContentTypesMatchers.Any(matcher => MatchScores.IsPerfect(matcher.IsMatch(contentType.MediaType))))
if (JsonContentTypesMatchers.Any(matcher => matcher.IsMatch(contentType.MediaType).IsPerfect()))
{
return BodyType.Json;
}
if (MultipartContentTypesMatchers.Any(matcher => MatchScores.IsPerfect(matcher.IsMatch(contentType.MediaType))))
if (MultipartContentTypesMatchers.Any(matcher => matcher.IsMatch(contentType.MediaType).IsPerfect()))
{
return BodyType.MultiPart;
}

View File

@@ -1,8 +1,11 @@
#if MIMEKIT
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text;
using MimeKit;
using Stef.Validation;
using WireMock.Http;
using WireMock.Types;
@@ -10,21 +13,39 @@ namespace WireMock.Util;
internal static class MimeKitUtils
{
public static MimeMessage GetMimeMessage(IBodyData? bodyData, string contentTypeHeaderValue)
public static bool TryGetMimeMessage(IRequestMessage requestMessage, [NotNullWhen(true)] out MimeMessage? mimeMessage)
{
var bytes = bodyData?.DetectedBodyType switch
Guard.NotNull(requestMessage);
if (requestMessage.BodyData != null &&
requestMessage.Headers?.TryGetValue(HttpKnownHeaderNames.ContentType, out var contentTypeHeader) == true &&
StartsWithMultiPart(contentTypeHeader) // Only parse when "multipart/mixed"
)
{
// If the body is bytes, use the BodyAsBytes to match on.
BodyType.Bytes => bodyData.BodyAsBytes!,
var bytes = requestMessage.BodyData?.DetectedBodyType switch
{
// If the body is bytes, use the BodyAsBytes to match on.
BodyType.Bytes => requestMessage.BodyData.BodyAsBytes!,
// If the body is a String or MultiPart, use the BodyAsString to match on.
BodyType.String or BodyType.MultiPart => Encoding.UTF8.GetBytes(bodyData.BodyAsString!),
// If the body is a String or MultiPart, use the BodyAsString to match on.
BodyType.String or BodyType.MultiPart => Encoding.UTF8.GetBytes(requestMessage.BodyData.BodyAsString!),
_ => throw new NotSupportedException()
};
_ => throw new NotSupportedException()
};
var fixedBytes = FixBytes(bytes, contentTypeHeaderValue);
return MimeMessage.Load(new MemoryStream(fixedBytes));
var fixedBytes = FixBytes(bytes, contentTypeHeader[0]);
mimeMessage = MimeMessage.Load(new MemoryStream(fixedBytes));
return true;
}
mimeMessage = null;
return false;
}
private static bool StartsWithMultiPart(WireMockList<string> contentTypeHeader)
{
return contentTypeHeader.Any(ct => ct.TrimStart().StartsWith("multipart/", StringComparison.OrdinalIgnoreCase));
}
private static byte[] FixBytes(byte[] bytes, WireMockList<string> contentType)

View File

@@ -82,7 +82,6 @@
<ItemGroup Condition=" '$(TargetFramework)' != 'netstandard1.3' ">
<PackageReference Include="XPath2.Extensions" Version="1.1.3" />
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="6.12.2" />
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.8" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' or '$(TargetFramework)' == 'net452' ">

View File

@@ -1,3 +1,4 @@
using FluentAssertions;
using NFluent;
using WireMock.Matchers;
using Xunit;
@@ -11,12 +12,13 @@ public class CSharpCodeMatcherTests
{
// Assign
string input = "x";
// Act
var matcher = new CSharpCodeMatcher("return it == \"x\";");
// Act
var score = matcher.IsMatch(input).Score;
// Assert
Check.That(matcher.IsMatch(input)).IsEqualTo(1.0d);
score.Should().Be(MatchScores.Perfect);
}
[Fact]
@@ -24,12 +26,13 @@ public class CSharpCodeMatcherTests
{
// Assign
string input = "y";
// Act
var matcher = new CSharpCodeMatcher("return it == \"x\";");
// Act
var score = matcher.IsMatch(input).Score;
// Assert
Check.That(matcher.IsMatch(input)).IsEqualTo(0.0d);
score.Should().Be(MatchScores.Mismatch);
}
[Fact]
@@ -37,12 +40,13 @@ public class CSharpCodeMatcherTests
{
// Assign
string input = "x";
// Act
var matcher = new CSharpCodeMatcher(MatchBehaviour.RejectOnMatch, MatchOperator.Or, "return it == \"x\";");
// Act
var score = matcher.IsMatch(input).Score;
// Assert
Check.That(matcher.IsMatch(input)).IsEqualTo(0.0d);
score.Should().Be(MatchScores.Mismatch);
}
[Fact]
@@ -55,12 +59,13 @@ public class CSharpCodeMatcherTests
Name = "Test"
};
// Act
var matcher = new CSharpCodeMatcher("return it.Id > 1 && it.Name == \"Test\";");
double match = matcher.IsMatch(input);
// Act
var score = matcher.IsMatch(input).Score;
// Assert
Assert.Equal(1.0, match);
score.Should().Be(MatchScores.Perfect);
}
[Fact]

View File

@@ -1,3 +1,4 @@
using FluentAssertions;
using NFluent;
using WireMock.Matchers;
using Xunit;
@@ -14,7 +15,12 @@ public class ContentTypeMatcherTests
public void ContentTypeMatcher_IsMatchWithIgnoreCaseFalse_Positive(string contentType)
{
var matcher = new ContentTypeMatcher("application/json");
Check.That(matcher.IsMatch(contentType)).IsEqualTo(1.0d);
// Act
var score = matcher.IsMatch(contentType).Score;
// Assert
score.Should().Be(MatchScores.Perfect);
}
[Theory]
@@ -26,7 +32,12 @@ public class ContentTypeMatcherTests
public void ContentTypeMatcher_IsMatchWithIgnoreCaseTrue_Positive(string contentType)
{
var matcher = new ContentTypeMatcher("application/json", true);
Check.That(matcher.IsMatch(contentType)).IsEqualTo(1.0d);
// Act
var score = matcher.IsMatch(contentType).Score;
// Assert
score.Should().Be(MatchScores.Perfect);
}
[Fact]

View File

@@ -39,7 +39,7 @@ public class ExactMatcherTests
var matcher = new ExactMatcher(true, "x");
// Act
double result = matcher.IsMatch("X");
double result = matcher.IsMatch("X").Score;
// Assert
Check.That(result).IsEqualTo(1.0);
@@ -52,7 +52,7 @@ public class ExactMatcherTests
var matcher = new ExactMatcher("x");
// Act
double result = matcher.IsMatch("x");
double result = matcher.IsMatch("x").Score;
// Assert
Check.That(result).IsEqualTo(1.0);
@@ -65,7 +65,7 @@ public class ExactMatcherTests
var matcher = new ExactMatcher("x");
// Act
double result = matcher.IsMatch("y");
double result = matcher.IsMatch("y").Score;
// Assert
Check.That(result).IsEqualTo(0.0);
@@ -78,7 +78,7 @@ public class ExactMatcherTests
var matcher = new ExactMatcher("x", "y");
// Act
double result = matcher.IsMatch("x");
double result = matcher.IsMatch("x").Score;
// Assert
Check.That(result).IsEqualTo(1.0);
@@ -91,7 +91,7 @@ public class ExactMatcherTests
var matcher = new ExactMatcher("x", "y");
// Act
double result = matcher.IsMatch("x");
double result = matcher.IsMatch("x").Score;
// Assert
Check.That(result).IsEqualTo(1.0);
@@ -104,10 +104,10 @@ public class ExactMatcherTests
public void ExactMatcher_IsMatch_WithMultiplePatterns_Average_ReturnsMatch(MatchOperator matchOperator, double score)
{
// Assign
var matcher = new ExactMatcher(MatchBehaviour.AcceptOnMatch, false, false, matchOperator, "x", "y");
var matcher = new ExactMatcher(MatchBehaviour.AcceptOnMatch, false, matchOperator, "x", "y");
// Act
double result = matcher.IsMatch("x");
double result = matcher.IsMatch("x").Score;
// Assert
Check.That(result).IsEqualTo(score);
@@ -120,7 +120,7 @@ public class ExactMatcherTests
var matcher = new ExactMatcher("cat");
// Act
double result = matcher.IsMatch("caR");
double result = matcher.IsMatch("caR").Score;
// Assert
Check.That(result).IsEqualTo(0.0);
@@ -130,10 +130,10 @@ public class ExactMatcherTests
public void ExactMatcher_IsMatch_SinglePattern_AcceptOnMatch()
{
// Assign
var matcher = new ExactMatcher(MatchBehaviour.AcceptOnMatch, false, false, MatchOperator.Or, "cat");
var matcher = new ExactMatcher(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, "cat");
// Act
double result = matcher.IsMatch("cat");
double result = matcher.IsMatch("cat").Score;
// Assert
Check.That(result).IsEqualTo(1.0);
@@ -143,10 +143,10 @@ public class ExactMatcherTests
public void ExactMatcher_IsMatch_SinglePattern_RejectOnMatch()
{
// Assign
var matcher = new ExactMatcher(MatchBehaviour.RejectOnMatch, false, false, MatchOperator.Or, "cat");
var matcher = new ExactMatcher(MatchBehaviour.RejectOnMatch, false, MatchOperator.Or, "cat");
// Act
double result = matcher.IsMatch("cat");
double result = matcher.IsMatch("cat").Score;
// Assert
Check.That(result).IsEqualTo(0.0);

View File

@@ -14,7 +14,7 @@ public class ExactObjectMatcherTests
// Act
var matcher = new ExactObjectMatcher(obj);
string name = matcher.Name;
var name = matcher.Name;
// Assert
Check.That(name).Equals("ExactObjectMatcher");
@@ -28,10 +28,10 @@ public class ExactObjectMatcherTests
// Act
var matcher = new ExactObjectMatcher(new byte[] { 1, 2 });
double result = matcher.IsMatch(checkValue);
var score = matcher.IsMatch(checkValue).Score;
// Assert
Check.That(result).IsEqualTo(1.0);
Check.That(score).IsEqualTo(1.0);
}
[Fact]
@@ -42,10 +42,10 @@ public class ExactObjectMatcherTests
// Act
var matcher = new ExactObjectMatcher(obj);
double result = matcher.IsMatch(new { x = 500, s = "s" });
var score = matcher.IsMatch(new { x = 500, s = "s" }).Score;
// Assert
Check.That(result).IsEqualTo(1.0);
Check.That(score).IsEqualTo(1.0);
}
[Fact]
@@ -56,9 +56,9 @@ public class ExactObjectMatcherTests
// Act
var matcher = new ExactObjectMatcher(MatchBehaviour.RejectOnMatch, obj);
double result = matcher.IsMatch(new { x = 500, s = "s" });
var score = matcher.IsMatch(new { x = 500, s = "s" }).Score;
// Assert
Check.That(result).IsEqualTo(0.0);
Check.That(score).IsEqualTo(0.0);
}
}

View File

@@ -51,7 +51,7 @@ public class GraphQLMatcherTests
var result = matcher.IsMatch(input);
// Assert
result.Should().Be(MatchScores.Perfect);
result.Score.Should().Be(MatchScores.Perfect);
matcher.GetPatterns().Should().Contain(TestSchema);
}
@@ -71,7 +71,7 @@ public class GraphQLMatcherTests
var result = matcher.IsMatch(input);
// Assert
result.Should().Be(MatchScores.Perfect);
result.Score.Should().Be(MatchScores.Perfect);
matcher.GetPatterns().Should().Contain(TestSchema);
}
@@ -93,7 +93,7 @@ public class GraphQLMatcherTests
var result = matcher.IsMatch(input);
// Assert
result.Should().Be(MatchScores.Mismatch);
result.Score.Should().Be(MatchScores.Mismatch);
}
[Fact]
@@ -111,21 +111,22 @@ public class GraphQLMatcherTests
var result = matcher.IsMatch(input);
// Assert
result.Should().Be(MatchScores.Perfect);
result.Score.Should().Be(MatchScores.Perfect);
}
[Fact]
public void GraphQLMatcher_For_ValidSchema_And_IncorrectQueryWithError_WithThrowExceptionTrue_ThrowsException()
public void GraphQLMatcher_For_ValidSchema_And_IncorrectQueryWithError_WithThrowExceptionTrue_ReturnsError()
{
// Arrange
var input = "{\"query\":\"{\\r\\n studentsX {\\r\\n fullName\\r\\n X\\r\\n }\\r\\n}\"}";
// Act
var matcher = new GraphQLMatcher(TestSchema, MatchBehaviour.AcceptOnMatch, true);
Action action = () => matcher.IsMatch(input);
var matcher = new GraphQLMatcher(TestSchema);
var result = matcher.IsMatch(input);
// Assert
action.Should().Throw<Exception>();
result.Score.Should().Be(MatchScores.Mismatch);
result.Exception!.Message.Should().StartWith("Cannot query field 'studentsX' on type 'Query'");
}
[Fact]

View File

@@ -1,4 +1,5 @@
using System;
using FluentAssertions;
using Newtonsoft.Json.Linq;
using NFluent;
using WireMock.Matchers;
@@ -42,7 +43,7 @@ public class JmesPathMatcherTests
var matcher = new JmesPathMatcher("");
// Act
double match = matcher.IsMatch(bytes);
double match = matcher.IsMatch(bytes).Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -56,7 +57,7 @@ public class JmesPathMatcherTests
var matcher = new JmesPathMatcher("");
// Act
double match = matcher.IsMatch(s);
double match = matcher.IsMatch(s).Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -70,7 +71,7 @@ public class JmesPathMatcherTests
var matcher = new JmesPathMatcher("");
// Act
double match = matcher.IsMatch(o);
double match = matcher.IsMatch(o).Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -83,7 +84,7 @@ public class JmesPathMatcherTests
var matcher = new JmesPathMatcher("xxx");
// Act
double match = matcher.IsMatch("");
double match = matcher.IsMatch("").Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -96,7 +97,7 @@ public class JmesPathMatcherTests
var matcher = new JmesPathMatcher("");
// Act
double match = matcher.IsMatch("x");
double match = matcher.IsMatch("x").Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -109,7 +110,7 @@ public class JmesPathMatcherTests
var matcher = new JmesPathMatcher("things.name == 'RequiredThing'");
// Act
double match = matcher.IsMatch(new { things = new { name = "RequiredThing" } });
double match = matcher.IsMatch(new { things = new { name = "RequiredThing" } }).Score;
// Assert
Check.That(match).IsEqualTo(1);
@@ -132,7 +133,7 @@ public class JmesPathMatcherTests
{ "Id", new JValue(1) },
{ "things", sub }
};
double match = matcher.IsMatch(jobject);
double match = matcher.IsMatch(jobject).Score;
// Assert
Check.That(match).IsEqualTo(1);
@@ -145,7 +146,7 @@ public class JmesPathMatcherTests
var matcher = new JmesPathMatcher("things.x == 'RequiredThing'");
// Act
double match = matcher.IsMatch(JObject.Parse("{ \"things\": { \"x\": \"RequiredThing\" } }"));
double match = matcher.IsMatch(JObject.Parse("{ \"things\": { \"x\": \"RequiredThing\" } }")).Score;
// Assert
Check.That(match).IsEqualTo(1);
@@ -155,12 +156,38 @@ public class JmesPathMatcherTests
public void JmesPathMatcher_IsMatch_RejectOnMatch()
{
// Assign
var matcher = new JmesPathMatcher(MatchBehaviour.RejectOnMatch, false, MatchOperator.Or, "things.x == 'RequiredThing'");
var matcher = new JmesPathMatcher(MatchBehaviour.RejectOnMatch, MatchOperator.Or, "things.x == 'RequiredThing'");
// Act
double match = matcher.IsMatch(JObject.Parse("{ \"things\": { \"x\": \"RequiredThing\" } }"));
double match = matcher.IsMatch(JObject.Parse("{ \"things\": { \"x\": \"RequiredThing\" } }")).Score;
// Assert
Check.That(match).IsEqualTo(0.0);
}
[Fact]
public void JmesPathMatcher_IsMatch_MultiplePatternsUsingMatchOperatorAnd()
{
// Assign
var matcher = new JmesPathMatcher(MatchOperator.And, "things.x == 'RequiredThing'", "things.x == 'abc'");
// Act
double score = matcher.IsMatch(JObject.Parse("{ \"things\": { \"x\": \"RequiredThing\" } }")).Score;
// Assert
score.Should().Be(0);
}
[Fact]
public void JmesPathMatcher_IsMatch_MultiplePatternsUsingMatchOperatorOr()
{
// Assign
var matcher = new JmesPathMatcher(MatchOperator.Or, "things.x == 'RequiredThing'", "things.x == 'abc'");
// Act
double score = matcher.IsMatch(JObject.Parse("{ \"things\": { \"x\": \"RequiredThing\" } }")).Score;
// Assert
score.Should().Be(1);
}
}

View File

@@ -82,29 +82,17 @@ public class JsonMatcherTests
}
[Fact]
public void JsonMatcher_IsMatch_WithInvalidValue_And_ThrowExceptionIsFalse_Should_ReturnMismatch()
public void JsonMatcher_IsMatch_WithInvalidValue_Should_ReturnMismatch_And_Exception_ShouldBeSet()
{
// Assign
var matcher = new JsonMatcher("");
// Act
double match = matcher.IsMatch(new MemoryStream());
var result = matcher.IsMatch(new MemoryStream());
// Assert
Check.That(match).IsEqualTo(0);
}
[Fact]
public void JsonMatcher_IsMatch_WithInvalidValue_And_ThrowExceptionIsTrue_Should_ReturnMismatch()
{
// Assign
var matcher = new JsonMatcher("", false, true);
// Act
Action action = () => matcher.IsMatch(new MemoryStream());
// Assert
action.Should().Throw<JsonException>();
result.Score.Should().Be(MatchScores.Mismatch);
result.Exception.Should().BeAssignableTo<JsonException>();
}
[Fact]
@@ -115,7 +103,7 @@ public class JsonMatcherTests
var matcher = new JsonMatcher("");
// Act
double match = matcher.IsMatch(bytes);
double match = matcher.IsMatch(bytes).Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -129,7 +117,7 @@ public class JsonMatcherTests
var matcher = new JsonMatcher("");
// Act
double match = matcher.IsMatch(s);
double match = matcher.IsMatch(s).Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -143,7 +131,7 @@ public class JsonMatcherTests
var matcher = new JsonMatcher("");
// Act
double match = matcher.IsMatch(o);
double match = matcher.IsMatch(o).Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -161,7 +149,7 @@ public class JsonMatcherTests
"x",
"y"
};
double match = matcher.IsMatch(jArray);
double match = matcher.IsMatch(jArray).Score;
// Assert
Assert.Equal(1.0, match);
@@ -179,7 +167,7 @@ public class JsonMatcherTests
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -197,7 +185,7 @@ public class JsonMatcherTests
{ "Id", new JValue(1) },
{ "NaMe", new JValue("Test") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -211,7 +199,7 @@ public class JsonMatcherTests
// Act
var jObject = JObject.Parse("{ \"Id\" : 1, \"Name\" : \"Test\" }");
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -225,7 +213,7 @@ public class JsonMatcherTests
// Act
var jObject = JObject.Parse("{ \"Id\" : 1, \"Name\" : \"Test\" }");
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -243,7 +231,7 @@ public class JsonMatcherTests
"x",
"y"
};
double match = matcher.IsMatch(jArray);
double match = matcher.IsMatch(jArray).Score;
// Assert
Assert.Equal(1.0, match);
@@ -261,7 +249,7 @@ public class JsonMatcherTests
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -279,7 +267,7 @@ public class JsonMatcherTests
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -297,7 +285,7 @@ public class JsonMatcherTests
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(0.0, match);
@@ -314,7 +302,7 @@ public class JsonMatcherTests
{
{ "preferredAt", new JValue("2019-11-21T10:32:53.2210009+00:00") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -331,7 +319,7 @@ public class JsonMatcherTests
{
{ "NormalEnum", new JValue(0) }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
match.Should().Be(1.0);
@@ -348,7 +336,7 @@ public class JsonMatcherTests
{
{ "EnumWithJsonConverter", new JValue("Type1") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
match.Should().Be(1.0);

View File

@@ -60,29 +60,17 @@ public class JsonPartialMatcherTests
}
[Fact]
public void JsonPartialMatcher_IsMatch_WithInvalidValue_And_ThrowExceptionIsFalse_Should_ReturnMismatch()
public void JsonPartialMatcher_IsMatch_WithInvalidValue_Should_ReturnMismatch_And_Exception_ShouldBeSet()
{
// Assign
var matcher = new JsonPartialMatcher("");
// Act
double match = matcher.IsMatch(new MemoryStream());
var result = matcher.IsMatch(new MemoryStream());
// Assert
Check.That(match).IsEqualTo(0);
}
[Fact]
public void JsonPartialMatcher_IsMatch_WithInvalidValue_And_ThrowExceptionIsTrue_Should_ReturnMismatch()
{
// Assign
var matcher = new JsonPartialMatcher("", false, true);
// Act
Action action = () => matcher.IsMatch(new MemoryStream());
// Assert
action.Should().Throw<JsonException>();
result.Score.Should().Be(MatchScores.Mismatch);
result.Exception.Should().BeAssignableTo<JsonException>();
}
[Fact]
@@ -93,7 +81,7 @@ public class JsonPartialMatcherTests
var matcher = new JsonPartialMatcher("");
// Act
double match = matcher.IsMatch(bytes);
double match = matcher.IsMatch(bytes).Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -107,7 +95,7 @@ public class JsonPartialMatcherTests
var matcher = new JsonPartialMatcher("");
// Act
double match = matcher.IsMatch(s);
double match = matcher.IsMatch(s).Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -121,7 +109,7 @@ public class JsonPartialMatcherTests
var matcher = new JsonPartialMatcher("");
// Act
double match = matcher.IsMatch(o);
double match = matcher.IsMatch(o).Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -139,7 +127,7 @@ public class JsonPartialMatcherTests
"x",
"y"
};
double match = matcher.IsMatch(jArray);
double match = matcher.IsMatch(jArray).Score;
// Assert
Assert.Equal(1.0, match);
@@ -157,7 +145,7 @@ public class JsonPartialMatcherTests
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -167,7 +155,7 @@ public class JsonPartialMatcherTests
public void JsonPartialMatcher_IsMatch_WithRegexTrue()
{
// Assign
var matcher = new JsonPartialMatcher(new { Id = "^\\d+$", Name = "Test" }, false, false, true);
var matcher = new JsonPartialMatcher(new { Id = "^\\d+$", Name = "Test" }, false, true);
// Act
var jObject = new JObject
@@ -175,7 +163,7 @@ public class JsonPartialMatcherTests
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -193,7 +181,7 @@ public class JsonPartialMatcherTests
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(0.0, match);
@@ -203,10 +191,11 @@ public class JsonPartialMatcherTests
public void JsonPartialMatcher_IsMatch_GuidAsString_UsingRegex()
{
var guid = new Guid("1111238e-b775-44a9-a263-95e570135c94");
var matcher = new JsonPartialMatcher(new {
var matcher = new JsonPartialMatcher(new
{
Id = 1,
Name = "^1111[a-fA-F0-9]{4}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"
}, false, false, true);
}, false, true);
// Act
var jObject = new JObject
@@ -214,7 +203,7 @@ public class JsonPartialMatcherTests
{ "Id", new JValue(1) },
{ "Name", new JValue(guid) }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -232,7 +221,7 @@ public class JsonPartialMatcherTests
{ "Id", new JValue(1) },
{ "NaMe", new JValue("Test") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -246,7 +235,7 @@ public class JsonPartialMatcherTests
// Act
var jObject = JObject.Parse("{ \"Id\" : 1, \"Name\" : \"Test\" }");
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -260,7 +249,7 @@ public class JsonPartialMatcherTests
// Act
var jObject = JObject.Parse("{ \"Id\" : 1, \"Name\" : \"Test\" }");
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -278,7 +267,7 @@ public class JsonPartialMatcherTests
"x",
"y"
};
double match = matcher.IsMatch(jArray);
double match = matcher.IsMatch(jArray).Score;
// Assert
Assert.Equal(1.0, match);
@@ -296,7 +285,7 @@ public class JsonPartialMatcherTests
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -305,20 +294,20 @@ public class JsonPartialMatcherTests
[Fact]
public void JsonPartialMatcher_IsMatch_GuidAsString()
{
// Assign
var guid = Guid.NewGuid();
var matcher = new JsonPartialMatcher(new { Id = 1, Name = guid });
// Assign
var guid = Guid.NewGuid();
var matcher = new JsonPartialMatcher(new { Id = 1, Name = guid });
// Act
var jObject = new JObject
{
{ "Id", new JValue(1) },
{ "Name", new JValue(guid.ToString()) }
};
double match = matcher.IsMatch(jObject);
// Act
var jObject = new JObject
{
{ "Id", new JValue(1) },
{ "Name", new JValue(guid.ToString()) }
};
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
// Assert
Assert.Equal(1.0, match);
}
[Fact]
@@ -333,7 +322,7 @@ public class JsonPartialMatcherTests
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -351,7 +340,7 @@ public class JsonPartialMatcherTests
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(0.0, match);
@@ -368,7 +357,7 @@ public class JsonPartialMatcherTests
{
{ "preferredAt", new JValue("2019-11-21T10:32:53.2210009+00:00") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -391,7 +380,7 @@ public class JsonPartialMatcherTests
var matcher = new JsonPartialMatcher(value);
// Act
double match = matcher.IsMatch(input);
double match = matcher.IsMatch(input).Score;
// Assert
Assert.Equal(1.0, match);
@@ -415,7 +404,7 @@ public class JsonPartialMatcherTests
var matcher = new JsonPartialMatcher(value);
// Act
double match = matcher.IsMatch(input);
double match = matcher.IsMatch(input).Score;
// Assert
Assert.Equal(0.0, match);
@@ -434,7 +423,7 @@ public class JsonPartialMatcherTests
var matcher = new JsonPartialMatcher(value);
// Act
double match = matcher.IsMatch(input);
double match = matcher.IsMatch(input).Score;
// Assert
Assert.Equal(1.0, match);
@@ -453,7 +442,7 @@ public class JsonPartialMatcherTests
var matcher = new JsonPartialMatcher(value);
// Act
double match = matcher.IsMatch(input);
double match = matcher.IsMatch(input).Score;
// Assert
Assert.Equal(0.0, match);

View File

@@ -60,29 +60,17 @@ public class JsonPartialWildcardMatcherTests
}
[Fact]
public void JsonPartialWildcardMatcher_IsMatch_WithInvalidValue_And_ThrowExceptionIsFalse_Should_ReturnMismatch()
public void JsonPartialWildcardMatcher_IsMatch_WithInvalidValue_Should_ReturnMismatch_And_Exception_ShouldBeSet()
{
// Assign
var matcher = new JsonPartialWildcardMatcher("");
// Act
double match = matcher.IsMatch(new MemoryStream());
var result = matcher.IsMatch(new MemoryStream());
// Assert
Check.That(match).IsEqualTo(0);
}
[Fact]
public void JsonPartialWildcardMatcher_IsMatch_WithInvalidValue_And_ThrowExceptionIsTrue_Should_ReturnMismatch()
{
// Assign
var matcher = new JsonPartialWildcardMatcher("", false, true);
// Act
Action action = () => matcher.IsMatch(new MemoryStream());
// Assert
action.Should().Throw<JsonException>();
result.Score.Should().Be(MatchScores.Mismatch);
result.Exception.Should().BeAssignableTo<JsonException>();
}
[Fact]
@@ -93,7 +81,7 @@ public class JsonPartialWildcardMatcherTests
var matcher = new JsonPartialWildcardMatcher("");
// Act
double match = matcher.IsMatch(bytes);
double match = matcher.IsMatch(bytes).Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -107,7 +95,7 @@ public class JsonPartialWildcardMatcherTests
var matcher = new JsonPartialWildcardMatcher("");
// Act
double match = matcher.IsMatch(s);
double match = matcher.IsMatch(s).Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -121,7 +109,7 @@ public class JsonPartialWildcardMatcherTests
var matcher = new JsonPartialWildcardMatcher("");
// Act
double match = matcher.IsMatch(o);
double match = matcher.IsMatch(o).Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -139,7 +127,7 @@ public class JsonPartialWildcardMatcherTests
"x",
"y"
};
double match = matcher.IsMatch(jArray);
double match = matcher.IsMatch(jArray).Score;
// Assert
Assert.Equal(1.0, match);
@@ -157,7 +145,7 @@ public class JsonPartialWildcardMatcherTests
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -175,7 +163,7 @@ public class JsonPartialWildcardMatcherTests
{ "Id", new JValue(1) },
{ "NaMe", new JValue("Test") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -189,7 +177,7 @@ public class JsonPartialWildcardMatcherTests
// Act
var jObject = JObject.Parse("{ \"Id\" : 1, \"Name\" : \"Test\" }");
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -203,7 +191,7 @@ public class JsonPartialWildcardMatcherTests
// Act
var jObject = JObject.Parse("{ \"Id\" : 1, \"Name\" : \"Test\" }");
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -221,7 +209,7 @@ public class JsonPartialWildcardMatcherTests
"x",
"y"
};
double match = matcher.IsMatch(jArray);
double match = matcher.IsMatch(jArray).Score;
// Assert
Assert.Equal(1.0, match);
@@ -239,7 +227,7 @@ public class JsonPartialWildcardMatcherTests
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -257,7 +245,7 @@ public class JsonPartialWildcardMatcherTests
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -275,7 +263,7 @@ public class JsonPartialWildcardMatcherTests
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(0.0, match);
@@ -292,7 +280,7 @@ public class JsonPartialWildcardMatcherTests
{
{ "preferredAt", new JValue("2019-11-21T10:32:53.2210009+00:00") }
};
double match = matcher.IsMatch(jObject);
double match = matcher.IsMatch(jObject).Score;
// Assert
Assert.Equal(1.0, match);
@@ -315,7 +303,7 @@ public class JsonPartialWildcardMatcherTests
var matcher = new JsonPartialWildcardMatcher(value);
// Act
double match = matcher.IsMatch(input);
double match = matcher.IsMatch(input).Score;
// Assert
Assert.Equal(1.0, match);
@@ -330,7 +318,7 @@ public class JsonPartialWildcardMatcherTests
var matcher = new JsonPartialWildcardMatcher(value);
// Act
double match = matcher.IsMatch(input);
double match = matcher.IsMatch(input).Score;
// Assert
match.Should().Be(1.0);
@@ -354,7 +342,7 @@ public class JsonPartialWildcardMatcherTests
var matcher = new JsonPartialWildcardMatcher(value);
// Act
double match = matcher.IsMatch(input);
double match = matcher.IsMatch(input).Score;
// Assert
Assert.Equal(0.0, match);
@@ -373,7 +361,7 @@ public class JsonPartialWildcardMatcherTests
var matcher = new JsonPartialWildcardMatcher(value);
// Act
double match = matcher.IsMatch(input);
double match = matcher.IsMatch(input).Score;
// Assert
Assert.Equal(1.0, match);
@@ -392,7 +380,7 @@ public class JsonPartialWildcardMatcherTests
var matcher = new JsonPartialWildcardMatcher(value);
// Act
double match = matcher.IsMatch(input);
double match = matcher.IsMatch(input).Score;
// Assert
Assert.Equal(0.0, match);

View File

@@ -1,4 +1,5 @@
using System;
using FluentAssertions;
using Newtonsoft.Json.Linq;
using NFluent;
using WireMock.Matchers;
@@ -42,7 +43,7 @@ public class JsonPathMatcherTests
var matcher = new JsonPathMatcher("");
// Act
double match = matcher.IsMatch(bytes);
double match = matcher.IsMatch(bytes).Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -56,7 +57,7 @@ public class JsonPathMatcherTests
var matcher = new JsonPathMatcher("");
// Act
double match = matcher.IsMatch(s);
double match = matcher.IsMatch(s).Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -70,7 +71,7 @@ public class JsonPathMatcherTests
var matcher = new JsonPathMatcher("");
// Act
double match = matcher.IsMatch(o);
double match = matcher.IsMatch(o).Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -83,7 +84,7 @@ public class JsonPathMatcherTests
var matcher = new JsonPathMatcher("xxx");
// Act
double match = matcher.IsMatch("");
double match = matcher.IsMatch("").Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -96,7 +97,7 @@ public class JsonPathMatcherTests
var matcher = new JsonPathMatcher("");
// Act
double match = matcher.IsMatch("x");
double match = matcher.IsMatch("x").Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -109,7 +110,7 @@ public class JsonPathMatcherTests
var matcher = new JsonPathMatcher("$..[?(@.Id == 1)]");
// Act
double match = matcher.IsMatch(new { Id = 1, Name = "Test" });
double match = matcher.IsMatch(new { Id = 1, Name = "Test" }).Score;
// Assert
Check.That(match).IsEqualTo(1);
@@ -122,7 +123,7 @@ public class JsonPathMatcherTests
var matcher = new JsonPathMatcher("$.things[?(@.name == 'x')]");
// Act
double match = matcher.IsMatch(new { things = new { name = "x" } });
double match = matcher.IsMatch(new { things = new { name = "x" } }).Score;
// Assert
Check.That(match).IsEqualTo(1);
@@ -136,7 +137,7 @@ public class JsonPathMatcherTests
var matcher = new JsonPathMatcher("$.things[?(@.name == 'x')]");
// Act
double match = matcher.IsMatch(json);
double match = matcher.IsMatch(json).Score;
// Assert
Check.That(match).IsEqualTo(1);
@@ -150,7 +151,7 @@ public class JsonPathMatcherTests
var matcher = new JsonPathMatcher("$.things[?(@.name == 'x')]");
// Act
double match = matcher.IsMatch(json);
double match = matcher.IsMatch(json).Score;
// Assert
Check.That(match).IsEqualTo(0);
@@ -169,7 +170,7 @@ public class JsonPathMatcherTests
{ "Id", new JValue(1) },
{ "Name", new JValue("Test") }
};
double match = matcher.IsMatch(jobject);
double match = matcher.IsMatch(jobject).Score;
// Assert
Check.That(match).IsEqualTo(1);
@@ -182,7 +183,7 @@ public class JsonPathMatcherTests
var matcher = new JsonPathMatcher("$..[?(@.Id == 1)]");
// Act
double match = matcher.IsMatch(JObject.Parse("{\"Id\":1,\"Name\":\"Test\"}"));
double match = matcher.IsMatch(JObject.Parse("{\"Id\":1,\"Name\":\"Test\"}")).Score;
// Assert
Check.That(match).IsEqualTo(1);
@@ -192,12 +193,184 @@ public class JsonPathMatcherTests
public void JsonPathMatcher_IsMatch_RejectOnMatch()
{
// Arrange
var matcher = new JsonPathMatcher(MatchBehaviour.RejectOnMatch, false, MatchOperator.Or, "$..[?(@.Id == 1)]");
var matcher = new JsonPathMatcher(MatchBehaviour.RejectOnMatch, MatchOperator.Or, "$..[?(@.Id == 1)]");
// Act
double match = matcher.IsMatch(JObject.Parse("{\"Id\":1,\"Name\":\"Test\"}"));
double match = matcher.IsMatch(JObject.Parse("{\"Id\":1,\"Name\":\"Test\"}")).Score;
// Assert
Check.That(match).IsEqualTo(0.0);
}
[Fact]
public void JsonPathMatcher_IsMatch_ArrayOneLevel()
{
// Arrange
var matcher = new JsonPathMatcher("$.arr[0].line1");
// Act
double match = matcher.IsMatch(JObject.Parse(@"{
""name"": ""PathSelectorTest"",
""test"": ""test"",
""test2"": ""test2"",
""arr"": [{
""line1"": ""line1"",
}]
}")).Score;
// Assert
Check.That(match).IsEqualTo(1.0);
}
[Fact]
public void JsonPathMatcher_IsMatch_ObjectMatch()
{
// Arrange
var matcher = new JsonPathMatcher("$.test");
// Act
double match = matcher.IsMatch(JObject.Parse(@"{
""name"": ""PathSelectorTest"",
""test"": ""test"",
""test2"": ""test2"",
""arr"": [
{
""line1"": ""line1"",
}
]
}")).Score;
// Assert
Check.That(match).IsEqualTo(1.0);
}
[Fact]
public void JsonPathMatcher_IsMatch_DoesntMatch()
{
// Arrange
var matcher = new JsonPathMatcher("$.test3");
// Act
double match = matcher.IsMatch(JObject.Parse(@"{
""name"": ""PathSelectorTest"",
""test"": ""test"",
""test2"": ""test2"",
""arr"": [
{
""line1"": ""line1"",
}
]
}")).Score;
// Assert
Check.That(match).IsEqualTo(0.0);
}
[Fact]
public void JsonPathMatcher_IsMatch_DoesntMatchInArray()
{
// Arrange
var matcher = new JsonPathMatcher("$arr[0].line1");
// Act
double match = matcher.IsMatch(JObject.Parse(@"{
""name"": ""PathSelectorTest"",
""test"": ""test"",
""test2"": ""test2"",
""arr"": []
}")).Score;
// Assert
Check.That(match).IsEqualTo(0.0);
}
[Fact]
public void JsonPathMatcher_IsMatch_DoesntMatchNoObjectsInArray()
{
// Arrange
var matcher = new JsonPathMatcher("$arr[2].line1");
// Act
double match = matcher.IsMatch(JObject.Parse(@"{
""name"": ""PathSelectorTest"",
""test"": ""test"",
""test2"": ""test2"",
""arr"": []
}")).Score;
// Assert
Check.That(match).IsEqualTo(0.0);
}
[Fact]
public void JsonPathMatcher_IsMatch_NestedArrays()
{
// Arrange
var matcher = new JsonPathMatcher("$.arr[0].sub[0].subline1");
// Act
double match = matcher.IsMatch(JObject.Parse(@"{
""name"": ""PathSelectorTest"",
""test"": ""test"",
""test2"": ""test2"",
""arr"": [{
""line1"": ""line1"",
""sub"":[
{
""subline1"":""subline1""
}]
}]
}")).Score;
// Assert
Check.That(match).IsEqualTo(1.0);
}
[Fact]
public void JsonPathMatcher_IsMatch_MultiplePatternsUsingMatchOperatorAnd()
{
// Assign
var matcher = new JsonPathMatcher(MatchBehaviour.AcceptOnMatch, MatchOperator.And, "$.arr[0].sub[0].subline1", "$.arr[0].line2");
// Act
double match = matcher.IsMatch(JObject.Parse(@"{
""name"": ""PathSelectorTest"",
""test"": ""test"",
""test2"": ""test2"",
""arr"": [{
""line1"": ""line1"",
""sub"":[
{
""subline1"":""subline1""
}]
}]
}")).Score;
// Assert
match.Should().Be(0);
}
[Fact]
public void JsonPathMatcher_IsMatch_MultiplePatternsUsingMatchOperatorOr()
{
// Assign
var matcher = new JsonPathMatcher(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, "$.arr[0].sub[0].subline2", "$.arr[0].line1");
// Act
double match = matcher.IsMatch(JObject.Parse(@"{
""name"": ""PathSelectorTest"",
""test"": ""test"",
""test2"": ""test2"",
""arr"": [{
""line1"": ""line1"",
""sub"":[
{
""subline1"":""subline1""
}]
}]
}")).Score;
// Assert
match.Should().Be(1);
}
}

View File

@@ -1,3 +1,4 @@
using FluentAssertions;
using Newtonsoft.Json.Linq;
using NFluent;
using WireMock.Matchers;
@@ -17,7 +18,8 @@ public class LinqMatcherTests
var matcher = new LinqMatcher("DateTime.Parse(it) > \"2018-08-01 13:50:00\"");
// Assert
Check.That(matcher.IsMatch(input)).IsEqualTo(1.0d);
var score = matcher.IsMatch(input).Score;
score.Should().Be(MatchScores.Perfect);
}
[Fact]
@@ -30,7 +32,8 @@ public class LinqMatcherTests
var matcher = new LinqMatcher("DateTime.Parse(it) > \"2019-01-01 00:00:00\"");
// Assert
Check.That(matcher.IsMatch(input)).IsEqualTo(0.0d);
var score = matcher.IsMatch(input).Score;
score.Should().Be(MatchScores.Mismatch);
}
[Fact]
@@ -43,7 +46,8 @@ public class LinqMatcherTests
var matcher = new LinqMatcher(MatchBehaviour.RejectOnMatch, "DateTime.Parse(it) > \"2018-08-01 13:50:00\"");
// Assert
Check.That(matcher.IsMatch(input)).IsEqualTo(0.0d);
var score = matcher.IsMatch(input).Score;
score.Should().Be(MatchScores.Mismatch);
}
[Fact]
@@ -58,7 +62,7 @@ public class LinqMatcherTests
// Act
var matcher = new LinqMatcher("Id > 1 AND Name == \"Test\"");
double match = matcher.IsMatch(input);
double match = matcher.IsMatch(input).Score;
// Assert
Assert.Equal(1.0, match);
@@ -77,7 +81,7 @@ public class LinqMatcherTests
// Act
var matcher = new LinqMatcher("IntegerId > 1 AND LongId > 1 && Name == \"Test\"");
double match = matcher.IsMatch(input);
double match = matcher.IsMatch(input).Score;
// Assert
Assert.Equal(1.0, match);

View File

@@ -55,7 +55,7 @@ AAAADElEQVR4XmMQYNgAAADkAMHebX3mAAAAAElFTkSuQmCC
// Assert
matcher.Name.Should().Be("MimePartMatcher");
result.Should().Be(MatchScores.Perfect);
result.Score.Should().Be(MatchScores.Perfect);
}
[Fact]
@@ -73,7 +73,7 @@ AAAADElEQVR4XmMQYNgAAADkAMHebX3mAAAAAElFTkSuQmCC
var result = matcher.IsMatch(part);
// Assert
result.Should().Be(MatchScores.Perfect);
result.Score.Should().Be(MatchScores.Perfect);
}
[Fact]
@@ -93,7 +93,7 @@ AAAADElEQVR4XmMQYNgAAADkAMHebX3mAAAAAElFTkSuQmCC
var result = matcher.IsMatch(part);
// Assert
result.Should().Be(MatchScores.Perfect);
result.Score.Should().Be(MatchScores.Perfect);
}
}
#endif

View File

@@ -12,7 +12,7 @@ public class NotNullOrEmptyMatcherTests
{
// Act
var matcher = new NotNullOrEmptyMatcher();
string name = matcher.Name;
var name = matcher.Name;
// Assert
Check.That(name).Equals("NotNullOrEmptyMatcher");
@@ -26,7 +26,7 @@ public class NotNullOrEmptyMatcherTests
{
// Act
var matcher = new NotNullOrEmptyMatcher();
double result = matcher.IsMatch(data);
var result = matcher.IsMatch(data).Score;
// Assert
result.Should().Be(expected);
@@ -40,7 +40,7 @@ public class NotNullOrEmptyMatcherTests
{
// Act
var matcher = new NotNullOrEmptyMatcher();
double result = matcher.IsMatch(@string);
var result = matcher.IsMatch(@string).Score;
// Assert
result.Should().Be(expected);
@@ -54,7 +54,7 @@ public class NotNullOrEmptyMatcherTests
{
// Act
var matcher = new NotNullOrEmptyMatcher();
double result = matcher.IsMatch((object)@string);
var result = matcher.IsMatch((object)@string).Score;
// Assert
result.Should().Be(expected);
@@ -65,7 +65,7 @@ public class NotNullOrEmptyMatcherTests
{
// Act
var matcher = new NotNullOrEmptyMatcher();
double result = matcher.IsMatch(new { x = "x" });
var result = matcher.IsMatch(new { x = "x" }).Score;
// Assert
result.Should().Be(1.0);

View File

@@ -53,7 +53,7 @@ public class RegexMatcherTests
var matcher = new RegexMatcher("H.*o");
// Act
double result = matcher.IsMatch("Hello world!");
double result = matcher.IsMatch("Hello world!").Score;
// Assert
Check.That(result).IsEqualTo(1.0d);
@@ -66,7 +66,7 @@ public class RegexMatcherTests
var matcher = new RegexMatcher("H.*o");
// Act
double result = matcher.IsMatch(null);
double result = matcher.IsMatch(null).Score;
// Assert
Check.That(result).IsEqualTo(0.0d);
@@ -79,7 +79,7 @@ public class RegexMatcherTests
var matcher = new RegexMatcher(@"\GUIDB", true);
// Act
double result = matcher.IsMatch(Guid.NewGuid().ToString("B"));
double result = matcher.IsMatch(Guid.NewGuid().ToString("B")).Score;
// Assert
result.Should().Be(1.0);
@@ -89,10 +89,10 @@ public class RegexMatcherTests
public void RegexMatcher_IsMatch_Regex_Guid()
{
// Assign
var matcher = new RegexMatcher(@"\GUIDB", true, false, false);
var matcher = new RegexMatcher(@"\GUIDB", true, false);
// Act
double result = matcher.IsMatch(Guid.NewGuid().ToString("B"));
double result = matcher.IsMatch(Guid.NewGuid().ToString("B")).Score;
// Assert
result.Should().Be(0);
@@ -105,7 +105,7 @@ public class RegexMatcherTests
var matcher = new RegexMatcher("H.*o", true);
// Act
double result = matcher.IsMatch("hello");
double result = matcher.IsMatch("hello").Score;
// Assert
Check.That(result).IsEqualTo(1.0d);
@@ -118,7 +118,7 @@ public class RegexMatcherTests
var matcher = new RegexMatcher(MatchBehaviour.RejectOnMatch, "h.*o");
// Act
double result = matcher.IsMatch("hello");
double result = matcher.IsMatch("hello").Score;
// Assert
Check.That(result).IsEqualTo(0.0);

View File

@@ -13,12 +13,12 @@ public class RequestMatchResultTests
{
// Arrange
var result1 = new RequestMatchResult();
result1.AddScore(typeof(WildcardMatcher), 1);
result1.AddScore(typeof(WildcardMatcher), 0.9);
result1.AddScore(typeof(WildcardMatcher), 1, null);
result1.AddScore(typeof(WildcardMatcher), 0.9, null);
var result2 = new RequestMatchResult();
result2.AddScore(typeof(LinqMatcher), 1);
result2.AddScore(typeof(LinqMatcher), 1);
result2.AddScore(typeof(LinqMatcher), 1, null);
result2.AddScore(typeof(LinqMatcher), 1, null);
var results = new[] { result1, result2 };
@@ -34,13 +34,13 @@ public class RequestMatchResultTests
{
// Arrange
var result1 = new RequestMatchResult();
result1.AddScore(typeof(WildcardMatcher), 1);
result1.AddScore(typeof(WildcardMatcher), 1);
result1.AddScore(typeof(WildcardMatcher), 1, null);
result1.AddScore(typeof(WildcardMatcher), 1, null);
var result2 = new RequestMatchResult();
result2.AddScore(typeof(LinqMatcher), 1);
result2.AddScore(typeof(LinqMatcher), 1);
result2.AddScore(typeof(LinqMatcher), 1);
result2.AddScore(typeof(LinqMatcher), 1, null);
result2.AddScore(typeof(LinqMatcher), 1, null);
result2.AddScore(typeof(LinqMatcher), 1, null);
var results = new[] { result1, result2 };

View File

@@ -39,7 +39,7 @@ public class SimMetricsMatcherTests
var matcher = new SimMetricsMatcher("The cat walks in the street.");
// Act
double result = matcher.IsMatch("The car drives in the street.");
double result = matcher.IsMatch("The car drives in the street.").Score;
// Assert
Check.That(result).IsStrictlyLessThan(1.0).And.IsStrictlyGreaterThan(0.5);
@@ -52,7 +52,7 @@ public class SimMetricsMatcherTests
var matcher = new SimMetricsMatcher("The cat walks in the street.");
// Act
double result = matcher.IsMatch("Hello");
double result = matcher.IsMatch("Hello").Score;
// Assert
Check.That(result).IsStrictlyLessThan(0.1).And.IsStrictlyGreaterThan(0.05);
@@ -65,7 +65,7 @@ public class SimMetricsMatcherTests
var matcher = new SimMetricsMatcher("test");
// Act
double result = matcher.IsMatch("test");
double result = matcher.IsMatch("test").Score;
// Assert
Check.That(result).IsEqualTo(1.0);
@@ -78,7 +78,7 @@ public class SimMetricsMatcherTests
var matcher = new SimMetricsMatcher(MatchBehaviour.RejectOnMatch, "test");
// Act
double result = matcher.IsMatch("test");
double result = matcher.IsMatch("test").Score;
// Assert
Check.That(result).IsEqualTo(0.0);

View File

@@ -19,11 +19,13 @@ public class WildcardMatcherTest
PatternAsFile = "pf"
};
// Act
var matcher = new WildcardMatcher(pattern);
// Act
var score = matcher.IsMatch("a").Score;
// Assert
matcher.IsMatch("a").Should().Be(1.0d);
score.Should().Be(1.0d);
}
[Fact]
@@ -39,11 +41,13 @@ public class WildcardMatcherTest
Pattern = "b"
};
// Act
var matcher = new WildcardMatcher(new [] { pattern1, pattern2 });
// Act
var score = matcher.IsMatch("a").Score;
// Assert
matcher.IsMatch("a").Should().Be(1.0d);
score.Should().Be(1.0d);
}
[Fact]
@@ -69,7 +73,12 @@ public class WildcardMatcherTest
foreach (var test in tests)
{
var matcher = new WildcardMatcher(test.p);
matcher.IsMatch(test.i).Should().Be(1.0d, $"Pattern '{test.p}' with value '{test.i}' should be 1.0");
// Act
var score = matcher.IsMatch(test.i).Score;
// Assert
score.Should().Be(MatchScores.Perfect, $"Pattern '{test.p}' with value '{test.i}' should be 1.0");
}
}
@@ -94,7 +103,12 @@ public class WildcardMatcherTest
foreach (var test in tests)
{
var matcher = new WildcardMatcher(test.p);
Check.That(matcher.IsMatch(test.i)).IsEqualTo(0.0);
// Act
var score = matcher.IsMatch(test.i).Score;
// Assert
score.Should().Be(MatchScores.Mismatch);
}
}
@@ -105,7 +119,7 @@ public class WildcardMatcherTest
var matcher = new WildcardMatcher("x");
// Act
string name = matcher.Name;
var name = matcher.Name;
// Assert
Check.That(name).Equals("WildcardMatcher");
@@ -131,7 +145,7 @@ public class WildcardMatcherTest
var matcher = new WildcardMatcher(MatchBehaviour.RejectOnMatch, "m");
// Act
double result = matcher.IsMatch("m");
var result = matcher.IsMatch("m").Score;
Check.That(result).IsEqualTo(0.0);
}

View File

@@ -43,7 +43,7 @@ public class XPathMatcherTests
var matcher = new XPathMatcher("/todo-list[count(todo-item) = 1]");
// Act
double result = matcher.IsMatch(xml);
double result = matcher.IsMatch(xml).Score;
// Assert
Check.That(result).IsEqualTo(1.0);
@@ -57,10 +57,10 @@ public class XPathMatcherTests
<todo-list>
<todo-item id='a1'>abc</todo-item>
</todo-list>";
var matcher = new XPathMatcher(MatchBehaviour.RejectOnMatch, false, MatchOperator.Or, "/todo-list[count(todo-item) = 1]");
var matcher = new XPathMatcher(MatchBehaviour.RejectOnMatch, MatchOperator.Or, "/todo-list[count(todo-item) = 1]");
// Act
double result = matcher.IsMatch(xml);
double result = matcher.IsMatch(xml).Score;
// Assert
Check.That(result).IsEqualTo(0.0);

View File

@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using Xunit;
using Moq;
using System.Threading.Tasks;
using System.Threading;
using FluentAssertions;
using WireMock.Handlers;
using WireMock.Owin.Mappers;
using WireMock.ResponseBuilders;
@@ -235,6 +237,24 @@ namespace WireMock.Net.Tests.Owin.Mappers
#endif
}
[Fact]
public void OwinResponseMapper_MapAsync_BodyAsFile_ThrowsException()
{
// Arrange
var responseMessage = new ResponseMessage
{
Headers = new Dictionary<string, WireMockList<string>>(),
BodyData = new BodyData { DetectedBodyType = BodyType.File, BodyAsFile = string.Empty }
};
_fileSystemHandlerMock.Setup(f => f.ReadResponseBodyAsFile(It.IsAny<string>())).Throws<FileNotFoundException>();
// Act
Func<Task> action = () => _sut.MapAsync(responseMessage, _responseMock.Object);
// Assert
action.Should().ThrowAsync<FileNotFoundException>();
}
[Fact]
public async Task OwinResponseMapper_MapAsync_WithFault_EMPTY_RESPONSE()
{
@@ -251,7 +271,7 @@ namespace WireMock.Net.Tests.Owin.Mappers
await _sut.MapAsync(responseMessage, _responseMock.Object).ConfigureAwait(false);
// Assert
_stream.Verify(s => s.WriteAsync(new byte[0], 0, 0, It.IsAny<CancellationToken>()), Times.Once);
_stream.Verify(s => s.WriteAsync(EmptyArray<byte>.Value, 0, 0, It.IsAny<CancellationToken>()), Times.Once);
}
[Theory]

View File

@@ -220,7 +220,7 @@ public class MappingMatcherTests
var requestMatchResult = new RequestMatchResult();
foreach (var score in match.scores)
{
requestMatchResult.AddScore(typeof(object), score);
requestMatchResult.AddScore(typeof(object), score, null);
}
mappingMock.SetupGet(m => m.Probability).Returns(match.probability);

View File

@@ -34,9 +34,10 @@ namespace WireMock.Net.Tests.Owin;
public class WireMockMiddlewareTests
{
private readonly DateTime _updatedAt = new(2022, 12, 4);
private readonly ConcurrentDictionary<Guid, IMapping> _mappings = new();
private static readonly Guid NewGuid = new("98fae52e-76df-47d9-876f-2ee32e931d9b");
private static readonly DateTime UpdatedAt = new(2022, 12, 4);
private readonly ConcurrentDictionary<Guid, IMapping> _mappings = new();
private readonly Mock<IWireMockMiddlewareOptions> _optionsMock;
private readonly Mock<IOwinRequestMapper> _requestMapperMock;
private readonly Mock<IOwinResponseMapper> _responseMapperMock;
@@ -48,6 +49,9 @@ public class WireMockMiddlewareTests
public WireMockMiddlewareTests()
{
var guidUtilsMock = new Mock<IGuidUtils>();
guidUtilsMock.Setup(g => g.NewGuid()).Returns(NewGuid);
_optionsMock = new Mock<IWireMockMiddlewareOptions>();
_optionsMock.SetupAllProperties();
_optionsMock.Setup(o => o.Mappings).Returns(_mappings);
@@ -74,7 +78,14 @@ public class WireMockMiddlewareTests
_mappingMock = new Mock<IMapping>();
_sut = new WireMockMiddleware(null, _optionsMock.Object, _requestMapperMock.Object, _responseMapperMock.Object, _matcherMock.Object);
_sut = new WireMockMiddleware(
null,
_optionsMock.Object,
_requestMapperMock.Object,
_responseMapperMock.Object,
_matcherMock.Object,
guidUtilsMock.Object
);
}
[Fact]
@@ -86,10 +97,32 @@ public class WireMockMiddlewareTests
// Assert and Verify
_optionsMock.Verify(o => o.Logger.Warn(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once);
Expression<Func<ResponseMessage, bool>> match = r => (int)r.StatusCode == 404 && ((StatusModel)r.BodyData.BodyAsJson).Status == "No matching mapping found";
Expression<Func<ResponseMessage, bool>> match = r => (int)r.StatusCode! == 404 && ((StatusModel)r.BodyData!.BodyAsJson!).Status == "No matching mapping found";
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<IResponse>()), Times.Once);
}
[Fact]
public async Task WireMockMiddleware_Invoke_NoMatch_When_SaveUnmatchedRequestsIsTrue_Should_Call_LocalFileSystemHandler_WriteUnmatchedRequest()
{
// Arrange
var fileSystemHandlerMock = new Mock<IFileSystemHandler>();
_optionsMock.Setup(o => o.FileSystemHandler).Returns(fileSystemHandlerMock.Object);
_optionsMock.Setup(o => o.SaveUnmatchedRequests).Returns(true);
// Act
await _sut.Invoke(_contextMock.Object).ConfigureAwait(false);
// Assert
_optionsMock.Verify(o => o.Logger.Warn(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once);
Expression<Func<ResponseMessage, bool>> match = r => (int)r.StatusCode! == 404 && ((StatusModel)r.BodyData!.BodyAsJson!).Status == "No matching mapping found";
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<IResponse>()), Times.Once);
// Verify
fileSystemHandlerMock.Verify(f => f.WriteUnmatchedRequest("98fae52e-76df-47d9-876f-2ee32e931d9b.LogEntry.json", It.IsAny<string>()));
fileSystemHandlerMock.VerifyNoOtherCalls();
}
[Fact]
public async Task WireMockMiddleware_Invoke_IsAdminInterface_EmptyHeaders_401()
{
@@ -109,7 +142,7 @@ public class WireMockMiddlewareTests
// Assert and Verify
_optionsMock.Verify(o => o.Logger.Error(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once);
Expression<Func<ResponseMessage, bool>> match = r => (int)r.StatusCode == 401;
Expression<Func<ResponseMessage, bool>> match = r => (int?)r.StatusCode == 401;
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<IResponse>()), Times.Once);
}
@@ -132,7 +165,7 @@ public class WireMockMiddlewareTests
// Assert and Verify
_optionsMock.Verify(o => o.Logger.Error(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once);
Expression<Func<ResponseMessage, bool>> match = r => (int)r.StatusCode == 401;
Expression<Func<ResponseMessage, bool>> match = r => (int?)r.StatusCode == 401;
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<IResponse>()), Times.Once);
}
@@ -177,7 +210,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, null, null);
var newMappingFromProxy = new Mapping(NewGuid, UpdatedAt, string.Empty, string.Empty, null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null, null, null);
_mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy));
var requestBuilder = Request.Create().UsingAnyMethod();
@@ -231,7 +264,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, data: null, probability: null);
var newMappingFromProxy = new Mapping(NewGuid, UpdatedAt, "my-title", "my-description", null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null, data: null, probability: null);
_mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy));
var requestBuilder = Request.Create().UsingAnyMethod();
@@ -246,6 +279,6 @@ public class WireMockMiddlewareTests
// Assert and Verify
fileSystemHandlerMock.Verify(f => f.WriteMappingFile(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
_mappings.Count.Should().Be(1);
_mappings.Should().HaveCount(1);
}
}
}

Some files were not shown because too many files have changed in this diff Show More