mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-03-29 05:42:14 +02:00
Fix MimePartMatcher and add more tests (#1389)
* mp * . * --return * Fixed * -- * ... * fix * ... * .
This commit is contained in:
@@ -152,6 +152,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.OpenTelemetry"
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.OpenTelemetryDemo", "examples\WireMock.Net.OpenTelemetryDemo\WireMock.Net.OpenTelemetryDemo.csproj", "{9957038D-F9C3-CA5D-E8AE-BE188E512635}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.OpenTelemetryDemo", "examples\WireMock.Net.OpenTelemetryDemo\WireMock.Net.OpenTelemetryDemo.csproj", "{9957038D-F9C3-CA5D-E8AE-BE188E512635}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Console.MimePart", "examples\WireMock.Net.Console.MimePart\WireMock.Net.Console.MimePart.csproj", "{4005E20C-D42B-138A-79BE-B3F5420C563F}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -810,6 +812,18 @@ Global
|
|||||||
{9957038D-F9C3-CA5D-E8AE-BE188E512635}.Release|x64.Build.0 = Release|Any CPU
|
{9957038D-F9C3-CA5D-E8AE-BE188E512635}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{9957038D-F9C3-CA5D-E8AE-BE188E512635}.Release|x86.ActiveCfg = Release|Any CPU
|
{9957038D-F9C3-CA5D-E8AE-BE188E512635}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{9957038D-F9C3-CA5D-E8AE-BE188E512635}.Release|x86.Build.0 = Release|Any CPU
|
{9957038D-F9C3-CA5D-E8AE-BE188E512635}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -871,6 +885,7 @@ Global
|
|||||||
{2DBBD70D-8051-441F-92BB-FF9B8B4B4982} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
{2DBBD70D-8051-441F-92BB-FF9B8B4B4982} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||||
{C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
{C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||||
{9957038D-F9C3-CA5D-E8AE-BE188E512635} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
{9957038D-F9C3-CA5D-E8AE-BE188E512635} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||||
|
{4005E20C-D42B-138A-79BE-B3F5420C563F} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
|
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
|
||||||
|
|||||||
82
examples/WireMock.Net.Console.MimePart/MainApp.cs
Normal file
82
examples/WireMock.Net.Console.MimePart/MainApp.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using WireMock.Logging;
|
||||||
|
using WireMock.Matchers;
|
||||||
|
using WireMock.RequestBuilders;
|
||||||
|
using WireMock.ResponseBuilders;
|
||||||
|
using WireMock.Server;
|
||||||
|
using WireMock.Settings;
|
||||||
|
|
||||||
|
namespace WireMock.Net.Console.MimePart;
|
||||||
|
|
||||||
|
// Test this CURL:
|
||||||
|
// curl -X POST http://localhost:9091/multipart -F "plainText=This is some plain text;type=text/plain" -F "jsonData={ `"Key`": `"Value`" };type=application/json" -F "image=@image.png;type=image/png"
|
||||||
|
//
|
||||||
|
// curl -X POST http://localhost:9091/multipart2 -F "plainText=This is some plain text;type=text/plain" -F "jsonData={ `"Key`": `"Value`" };type=application/json" -F "image=@image.png;type=image/png"
|
||||||
|
|
||||||
|
public static class MainApp
|
||||||
|
{
|
||||||
|
public static async Task RunAsync()
|
||||||
|
{
|
||||||
|
using var server = WireMockServer.Start(new WireMockServerSettings
|
||||||
|
{
|
||||||
|
Port = 9091,
|
||||||
|
StartAdminInterface = true,
|
||||||
|
|
||||||
|
ReadStaticMappings = true,
|
||||||
|
//WatchStaticMappings = true,
|
||||||
|
//WatchStaticMappingsInSubdirectories = true,
|
||||||
|
|
||||||
|
Logger = new WireMockConsoleLogger()
|
||||||
|
});
|
||||||
|
System.Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls));
|
||||||
|
|
||||||
|
var textPlainContentTypeMatcher = new ContentTypeMatcher("text/plain");
|
||||||
|
var textPlainContentMatcher = new ExactMatcher("This is some plain text");
|
||||||
|
var textPlainMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, textPlainContentTypeMatcher, null, null, textPlainContentMatcher);
|
||||||
|
|
||||||
|
var textJsonContentTypeMatcher = new ContentTypeMatcher("application/json");
|
||||||
|
var textJsonContentMatcher = new JsonMatcher(new { Key = "Value" }, true);
|
||||||
|
var textJsonMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, textJsonContentTypeMatcher, null, null, textJsonContentMatcher);
|
||||||
|
|
||||||
|
var imagePngContentTypeMatcher = new ContentTypeMatcher("image/png");
|
||||||
|
var imagePngContentDispositionMatcher = new ExactMatcher("form-data; name=\"image\"; filename=\"image.png\"");
|
||||||
|
var imagePngContentTransferEncodingMatcher = new ExactMatcher("default");
|
||||||
|
var imagePngContentMatcher = new ExactObjectMatcher(Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAIAAAACAgMAAAAP2OW3AAAADFBMVEX/tID/vpH/pWX/sHidUyjlAAAADElEQVR4XmMQYNgAAADkAMHebX3mAAAAAElFTkSuQmCC"));
|
||||||
|
var imagePngMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, imagePngContentTypeMatcher, imagePngContentDispositionMatcher, imagePngContentTransferEncodingMatcher, imagePngContentMatcher);
|
||||||
|
|
||||||
|
var matchers = new IMatcher[]
|
||||||
|
{
|
||||||
|
textPlainMatcher,
|
||||||
|
textJsonMatcher,
|
||||||
|
imagePngMatcher
|
||||||
|
};
|
||||||
|
|
||||||
|
server
|
||||||
|
.Given(Request.Create()
|
||||||
|
.WithPath("/multipart")
|
||||||
|
.UsingPost()
|
||||||
|
.WithMultiPart(matchers)
|
||||||
|
)
|
||||||
|
.WithGuid("b9c82182-e469-41da-bcaf-b6e3157fefdb")
|
||||||
|
.RespondWith(Response.Create()
|
||||||
|
.WithBody("MultiPart is ok")
|
||||||
|
);
|
||||||
|
|
||||||
|
// server.SaveStaticMappings();
|
||||||
|
|
||||||
|
System.Console.WriteLine(JsonConvert.SerializeObject(server.MappingModels, Formatting.Indented));
|
||||||
|
|
||||||
|
System.Console.WriteLine("Press any key to stop the server");
|
||||||
|
System.Console.ReadKey();
|
||||||
|
server.Stop();
|
||||||
|
|
||||||
|
System.Console.WriteLine("Displaying all requests");
|
||||||
|
var allRequests = server.LogEntries;
|
||||||
|
System.Console.WriteLine(JsonConvert.SerializeObject(allRequests, Formatting.Indented));
|
||||||
|
|
||||||
|
System.Console.WriteLine("Press any key to quit");
|
||||||
|
System.Console.ReadKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
23
examples/WireMock.Net.Console.MimePart/Program.cs
Normal file
23
examples/WireMock.Net.Console.MimePart/Program.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using System.Reflection;
|
||||||
|
using log4net;
|
||||||
|
using log4net.Config;
|
||||||
|
using log4net.Repository;
|
||||||
|
|
||||||
|
namespace WireMock.Net.Console.MimePart;
|
||||||
|
|
||||||
|
static class Program
|
||||||
|
{
|
||||||
|
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
|
||||||
|
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
|
||||||
|
|
||||||
|
static async Task Main(params string[] args)
|
||||||
|
{
|
||||||
|
Log.Info("Starting WireMock.Net.Console.MimePart...");
|
||||||
|
|
||||||
|
XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config"));
|
||||||
|
|
||||||
|
await MainApp.RunAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<DefineConstants>$(DefineConstants);GRAPHQL;MIMEKIT;PROTOBUF</DefineConstants>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="__admin\mappings\*.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
||||||
|
<PackageReference Include="log4net" Version="2.0.15" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="log4net.config">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"Guid": "b9c82182-e469-41da-bcaf-b6e3157fefdc",
|
||||||
|
"UpdatedAt": "2025-12-18T17:21:57.3879723Z",
|
||||||
|
"Request": {
|
||||||
|
"Path": {
|
||||||
|
"Matchers": [
|
||||||
|
{
|
||||||
|
"Name": "WildcardMatcher",
|
||||||
|
"Pattern": "/multipart2",
|
||||||
|
"IgnoreCase": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Methods": [
|
||||||
|
"POST"
|
||||||
|
],
|
||||||
|
"Body": {
|
||||||
|
"MatcherName": "MultiPartMatcher",
|
||||||
|
"Matchers": [
|
||||||
|
{
|
||||||
|
"Name": "MimePartMatcher",
|
||||||
|
"ContentTypeMatcher": {
|
||||||
|
"Name": "ContentTypeMatcher",
|
||||||
|
"Pattern": "text/plain",
|
||||||
|
"IgnoreCase": false
|
||||||
|
},
|
||||||
|
"ContentMatcher": {
|
||||||
|
"Name": "ExactMatcher",
|
||||||
|
"Pattern": "This is some plain text",
|
||||||
|
"IgnoreCase": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "MimePartMatcher",
|
||||||
|
"ContentTypeMatcher": {
|
||||||
|
"Name": "ContentTypeMatcher",
|
||||||
|
"Pattern": "application/json",
|
||||||
|
"IgnoreCase": false
|
||||||
|
},
|
||||||
|
"ContentMatcher": {
|
||||||
|
"Name": "JsonMatcher",
|
||||||
|
"Pattern": {
|
||||||
|
"Key": "Value"
|
||||||
|
},
|
||||||
|
"IgnoreCase": true,
|
||||||
|
"Regex": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "MimePartMatcher",
|
||||||
|
"ContentTypeMatcher": {
|
||||||
|
"Name": "ContentTypeMatcher",
|
||||||
|
"Pattern": "image/png",
|
||||||
|
"IgnoreCase": false
|
||||||
|
},
|
||||||
|
"ContentDispositionMatcher": {
|
||||||
|
"Name": "ExactMatcher",
|
||||||
|
"Pattern": "form-data; name=\"image\"; filename=\"image.png\"",
|
||||||
|
"IgnoreCase": false
|
||||||
|
},
|
||||||
|
"ContentTransferEncodingMatcher": {
|
||||||
|
"Name": "ExactMatcher",
|
||||||
|
"Pattern": "default",
|
||||||
|
"IgnoreCase": false
|
||||||
|
},
|
||||||
|
"ContentMatcher": {
|
||||||
|
"Name": "ExactObjectMatcher",
|
||||||
|
"Pattern": "iVBORw0KGgoAAAANSUhEUgAAAAIAAAACAgMAAAAP2OW3AAAADFBMVEX/tID/vpH/pWX/sHidUyjlAAAADElEQVR4XmMQYNgAAADkAMHebX3mAAAAAElFTkSuQmCC"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"MatchOperator": "Or"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Response": {
|
||||||
|
"BodyDestination": "SameAsSource",
|
||||||
|
"Body": "MultiPart2 is ok"
|
||||||
|
}
|
||||||
|
}
|
||||||
20
examples/WireMock.Net.Console.MimePart/log4net.config
Normal file
20
examples/WireMock.Net.Console.MimePart/log4net.config
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<configSections>
|
||||||
|
<section name="log4net" type="System.Configuration.IgnoreSectionHandler" />
|
||||||
|
</configSections>
|
||||||
|
<appSettings>
|
||||||
|
<add key="log4net.Internal.Debug" value="true"/>
|
||||||
|
</appSettings>
|
||||||
|
<log4net>
|
||||||
|
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
|
||||||
|
<layout type="log4net.Layout.PatternLayout">
|
||||||
|
<conversionPattern value="%date [%thread] %-5level %logger{1} - %message%newline" />
|
||||||
|
</layout>
|
||||||
|
</appender>
|
||||||
|
<root>
|
||||||
|
<level value="DEBUG" />
|
||||||
|
<appender-ref ref="ConsoleAppender" />
|
||||||
|
</root>
|
||||||
|
</log4net>
|
||||||
|
</configuration>
|
||||||
@@ -339,7 +339,7 @@ namespace WireMock.Net.ConsoleApplication
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.Console.WriteLine("X = {0} ; default = {1} ; pX = {2:0.00} ; valueX = {3:0.00}", xCount, defaultCount, pX, 1.0 * xCount / tot);
|
System.Console.WriteLine("X = {0} ; default = {1} ; pX = {2:0.00} ; valueX = {3:0.00}", xCount, defaultCount, pX, 1.0 * xCount / tot);
|
||||||
return;
|
|
||||||
using var httpAndHttpsWithPort = WireMockServer.Start(new WireMockServerSettings
|
using var httpAndHttpsWithPort = WireMockServer.Start(new WireMockServerSettings
|
||||||
{
|
{
|
||||||
HostingScheme = HostingScheme.HttpAndHttps,
|
HostingScheme = HostingScheme.HttpAndHttps,
|
||||||
@@ -556,39 +556,6 @@ namespace WireMock.Net.ConsoleApplication
|
|||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if MIMEKIT
|
|
||||||
var textPlainContentTypeMatcher = new ContentTypeMatcher("text/plain");
|
|
||||||
var textPlainContentMatcher = new ExactMatcher("This is some plain text");
|
|
||||||
var textPlainMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, textPlainContentTypeMatcher, null, null, textPlainContentMatcher);
|
|
||||||
|
|
||||||
var textJsonContentTypeMatcher = new ContentTypeMatcher("text/json");
|
|
||||||
var textJsonContentMatcher = new JsonMatcher(new { Key = "Value" }, true);
|
|
||||||
var textJsonMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, textJsonContentTypeMatcher, null, null, textJsonContentMatcher);
|
|
||||||
|
|
||||||
var imagePngContentTypeMatcher = new ContentTypeMatcher("image/png");
|
|
||||||
var imagePngContentDispositionMatcher = new ExactMatcher("attachment; filename=\"image.png\"");
|
|
||||||
var imagePngContentTransferEncodingMatcher = new ExactMatcher("base64");
|
|
||||||
var imagePngContentMatcher = new ExactObjectMatcher(Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAIAAAACAgMAAAAP2OW3AAAADFBMVEX/tID/vpH/pWX/sHidUyjlAAAADElEQVR4XmMQYNgAAADkAMHebX3mAAAAAElFTkSuQmCC"));
|
|
||||||
var imagePngMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, imagePngContentTypeMatcher, imagePngContentDispositionMatcher, imagePngContentTransferEncodingMatcher, imagePngContentMatcher);
|
|
||||||
|
|
||||||
var matchers = new IMatcher[]
|
|
||||||
{
|
|
||||||
textPlainMatcher,
|
|
||||||
textJsonMatcher,
|
|
||||||
imagePngMatcher
|
|
||||||
};
|
|
||||||
|
|
||||||
server
|
|
||||||
.Given(Request.Create()
|
|
||||||
.WithPath("/multipart")
|
|
||||||
.UsingPost()
|
|
||||||
.WithMultiPart(matchers)
|
|
||||||
)
|
|
||||||
.WithGuid("b9c82182-e469-41da-bcaf-b6e3157fefdb")
|
|
||||||
.RespondWith(Response.Create()
|
|
||||||
.WithBody("MultiPart is ok")
|
|
||||||
);
|
|
||||||
#endif
|
|
||||||
// 400 ms
|
// 400 ms
|
||||||
server
|
server
|
||||||
.Given(Request.Create()
|
.Given(Request.Create()
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ namespace WireMock.Admin.Mappings;
|
|||||||
[FluentBuilder.AutoGenerateBuilder]
|
[FluentBuilder.AutoGenerateBuilder]
|
||||||
public class BodyModel
|
public class BodyModel
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the body matcher.
|
||||||
|
/// Currently only "MultiPartMatcher" is supported.
|
||||||
|
/// </summary>
|
||||||
|
public string? MatcherName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the matcher.
|
/// Gets or sets the matcher.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using WireMock.Matchers.Request;
|
||||||
|
|
||||||
namespace WireMock.Admin.Requests;
|
namespace WireMock.Admin.Requests;
|
||||||
|
|
||||||
@@ -47,5 +48,5 @@ public class LogRequestMatchModel
|
|||||||
/// <value>
|
/// <value>
|
||||||
/// The match details.
|
/// The match details.
|
||||||
/// </value>
|
/// </value>
|
||||||
public IList<object> MatchDetails { get; set; }
|
public IList<MatchDetail> MatchDetails { get; set; } = [];
|
||||||
}
|
}
|
||||||
@@ -55,4 +55,11 @@ public interface IRequestMatchResult : IComparable
|
|||||||
/// <param name="exception">The exception [Optional].</param>
|
/// <param name="exception">The exception [Optional].</param>
|
||||||
/// <returns>The score.</returns>
|
/// <returns>The score.</returns>
|
||||||
double AddScore(Type matcherType, double score, Exception? exception);
|
double AddScore(Type matcherType, double score, Exception? exception);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the score.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="matchDetail">The matchDetail.</param>
|
||||||
|
/// <returns>The score.</returns>
|
||||||
|
double AddMatchDetail(MatchDetail matchDetail);
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,12 @@ public class MatchDetail
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the type of the matcher.
|
/// Gets or sets the type of the matcher.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Type MatcherType { get; set; } = null!;
|
public required Type MatcherType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the type of the matcher.
|
||||||
|
/// </summary>
|
||||||
|
public required string Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the score between 0.0 and 1.0
|
/// Gets or sets the score between 0.0 and 1.0
|
||||||
@@ -24,4 +29,9 @@ public class MatchDetail
|
|||||||
/// [Optional]
|
/// [Optional]
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Exception? Exception { get; set; }
|
public Exception? Exception { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The child MatchResults in case of multiple matchers.
|
||||||
|
/// </summary>
|
||||||
|
public MatchDetail[]? MatchDetails { get; set; }
|
||||||
}
|
}
|
||||||
@@ -140,7 +140,7 @@ public class GraphQLMatcher : IGraphQLMatcher
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ public class CSharpCodeMatcher : ICSharpCodeMatcher
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsMatch(dynamic input, string pattern)
|
private bool IsMatch(dynamic input, string pattern)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using WireMock.Matchers.Helpers;
|
using WireMock.Matchers.Helpers;
|
||||||
using WireMock.Models.Mime;
|
using WireMock.Models.Mime;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
@@ -12,7 +13,7 @@ namespace WireMock.Matchers;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class MimePartMatcher : IMimePartMatcher
|
public class MimePartMatcher : IMimePartMatcher
|
||||||
{
|
{
|
||||||
private readonly Func<IMimePartData, MatchResult>[] _funcs;
|
private readonly IList<(string Name, Func<IMimePartData, MatchResult> func)> _matcherFunctions;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => nameof(MimePartMatcher);
|
public string Name => nameof(MimePartMatcher);
|
||||||
@@ -49,34 +50,47 @@ public class MimePartMatcher : IMimePartMatcher
|
|||||||
ContentTransferEncodingMatcher = contentTransferEncodingMatcher;
|
ContentTransferEncodingMatcher = contentTransferEncodingMatcher;
|
||||||
ContentMatcher = contentMatcher;
|
ContentMatcher = contentMatcher;
|
||||||
|
|
||||||
_funcs =
|
_matcherFunctions = [];
|
||||||
[
|
if (ContentTypeMatcher != null)
|
||||||
mp => ContentTypeMatcher?.IsMatch(GetContentTypeAsString(mp.ContentType)) ?? MatchScores.Perfect,
|
{
|
||||||
mp => ContentDispositionMatcher?.IsMatch(mp.ContentDisposition?.ToString()?.Replace("Content-Disposition: ", string.Empty)) ?? MatchScores.Perfect,
|
_matcherFunctions.Add((nameof(ContentTypeMatcher), mp => ContentTypeMatcher.IsMatch(GetContentTypeAsString(mp.ContentType))));
|
||||||
mp => ContentTransferEncodingMatcher?.IsMatch(mp.ContentTransferEncoding.ToLowerInvariant()) ?? MatchScores.Perfect,
|
}
|
||||||
MatchOnContent
|
|
||||||
];
|
if (ContentDispositionMatcher != null)
|
||||||
|
{
|
||||||
|
_matcherFunctions.Add((nameof(ContentDispositionMatcher), mp => ContentDispositionMatcher.IsMatch(mp.ContentDisposition?.ToString()?.Replace("Content-Disposition: ", string.Empty))));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ContentTransferEncodingMatcher != null)
|
||||||
|
{
|
||||||
|
_matcherFunctions.Add((nameof(ContentTransferEncodingMatcher), mp => ContentTransferEncodingMatcher.IsMatch(mp.ContentTransferEncoding.ToLowerInvariant())));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ContentMatcher != null)
|
||||||
|
{
|
||||||
|
_matcherFunctions.Add((ContentMatcher.Name, MatchOnContent));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public MatchResult IsMatch(IMimePartData value)
|
public MatchResult IsMatch(IMimePartData value)
|
||||||
{
|
{
|
||||||
var score = MatchScores.Mismatch;
|
var results = new List<MatchResult>();
|
||||||
Exception? exception = null;
|
|
||||||
|
|
||||||
try
|
foreach (var matcherFunction in _matcherFunctions)
|
||||||
{
|
{
|
||||||
if (Array.TrueForAll(_funcs, func => func(value).IsPerfect()))
|
try
|
||||||
{
|
{
|
||||||
score = MatchScores.Perfect;
|
var matchResult = matcherFunction.func(value);
|
||||||
|
results.Add(MatchResult.From(matcherFunction.Name, matchResult.Score));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
results.Add(MatchResult.From(matcherFunction.Name, MatchScores.Mismatch, ex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
exception = ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
return MatchResult.From(nameof(MimePartMatcher), results, MatchOperator.And);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -87,11 +101,6 @@ public class MimePartMatcher : IMimePartMatcher
|
|||||||
|
|
||||||
private MatchResult MatchOnContent(IMimePartData mimePart)
|
private MatchResult MatchOnContent(IMimePartData mimePart)
|
||||||
{
|
{
|
||||||
if (ContentMatcher == null)
|
|
||||||
{
|
|
||||||
return MatchScores.Perfect;
|
|
||||||
}
|
|
||||||
|
|
||||||
var bodyParserSettings = new BodyParserSettings
|
var bodyParserSettings = new BodyParserSettings
|
||||||
{
|
{
|
||||||
Stream = mimePart.Open(),
|
Stream = mimePart.Open(),
|
||||||
@@ -102,7 +111,7 @@ public class MimePartMatcher : IMimePartMatcher
|
|||||||
};
|
};
|
||||||
|
|
||||||
var bodyData = BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false).GetAwaiter().GetResult();
|
var bodyData = BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||||
return BodyDataMatchScoreCalculator.CalculateMatchScore(bodyData, ContentMatcher);
|
return BodyDataMatchScoreCalculator.CalculateMatchScore(bodyData, ContentMatcher!);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string? GetContentTypeAsString(IContentTypeData? contentType)
|
private static string? GetContentTypeAsString(IContentTypeData? contentType)
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ internal class MimeKitUtils : IMimeKitUtils
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fixedBytes = FixBytes(bytes, contentTypeHeader[0]);
|
var fixedBytes = PrependContentTypeHeader(bytes, contentTypeHeader[0]);
|
||||||
|
|
||||||
mimeMessageData = LoadFromStream(new MemoryStream(fixedBytes));
|
mimeMessageData = LoadFromStream(new MemoryStream(fixedBytes));
|
||||||
return true;
|
return true;
|
||||||
@@ -68,7 +68,10 @@ internal class MimeKitUtils : IMimeKitUtils
|
|||||||
return contentTypeHeader.Any(ct => ct.TrimStart().StartsWith("multipart/", StringComparison.OrdinalIgnoreCase));
|
return contentTypeHeader.Any(ct => ct.TrimStart().StartsWith("multipart/", StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] FixBytes(byte[] bytes, WireMockList<string> contentType)
|
/// <summary>
|
||||||
|
/// Prepends the Content-Type header to the byte array to make it a valid MIME message for MimeKit.
|
||||||
|
/// </summary>
|
||||||
|
private static byte[] PrependContentTypeHeader(byte[] bytes, WireMockList<string> contentType)
|
||||||
{
|
{
|
||||||
var contentTypeBytes = Encoding.UTF8.GetBytes($"{HttpKnownHeaderNames.ContentType}: {contentType}\r\n\r\n");
|
var contentTypeBytes = Encoding.UTF8.GetBytes($"{HttpKnownHeaderNames.ContentType}: {contentType}\r\n\r\n");
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ internal class AzureADAuthenticationMatcher : IStringMatcher
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(input))
|
if (string.IsNullOrEmpty(input))
|
||||||
{
|
{
|
||||||
return MatchScores.Mismatch;
|
return MatchResult.From(Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
var token = Regex.Replace(input, BearerPrefix, string.Empty, RegexOptions.IgnoreCase, RegexConstants.DefaultTimeout);
|
var token = Regex.Replace(input, BearerPrefix, string.Empty, RegexOptions.IgnoreCase, RegexConstants.DefaultTimeout);
|
||||||
@@ -83,11 +83,11 @@ internal class AzureADAuthenticationMatcher : IStringMatcher
|
|||||||
// Throws an Exception as the token is invalid (expired, invalid-formatted, tenant mismatch, etc.)
|
// Throws an Exception as the token is invalid (expired, invalid-formatted, tenant mismatch, etc.)
|
||||||
_jwtSecurityTokenHandler.ValidateToken(token, validationParameters, out _);
|
_jwtSecurityTokenHandler.ValidateToken(token, validationParameters, out _);
|
||||||
|
|
||||||
return MatchScores.Perfect;
|
return MatchResult.From(Name, MatchScores.Perfect);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return new MatchResult(MatchScores.Mismatch, ex);
|
return MatchResult.From(Name, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public class WireMockConsoleLogger : IWireMockLogger
|
|||||||
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
|
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
|
||||||
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
|
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
|
||||||
{
|
{
|
||||||
string message = JsonConvert.SerializeObject(logEntryModel, Formatting.Indented);
|
string message = JsonConvert.SerializeObject(logEntryModel, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||||
Console.WriteLine(Format("DebugRequestResponse", "Admin[{0}] {1}", isAdminRequest, message));
|
Console.WriteLine(Format("DebugRequestResponse", "Admin[{0}] {1}", isAdminRequest, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
46
src/WireMock.Net.Minimal/Matchers/CompositeMatcher.cs
Normal file
46
src/WireMock.Net.Minimal/Matchers/CompositeMatcher.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace WireMock.Matchers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a matcher that combines multiple matching strategies into a single composite operation.
|
||||||
|
/// </summary>
|
||||||
|
public class CompositeMatcher : IMatcher
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Name => nameof(CompositeMatcher);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The logical operator used to combine the results of the matchers.
|
||||||
|
/// </summary>
|
||||||
|
public MatchOperator MatchOperator { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public MatchBehaviour MatchBehaviour { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All matchers.
|
||||||
|
/// </summary>
|
||||||
|
public IMatcher[] Matchers { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the CompositeMatcher class with the specified matchers, operator, and match behaviour.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="matchers">An array of matchers to be combined. Cannot be null or contain null elements.</param>
|
||||||
|
/// <param name="matchOperator">The logical operator used to combine the results of the matchers.</param>
|
||||||
|
/// <param name="matchBehaviour">The behaviour that determines how the composite matcher interprets the combined results.</param>
|
||||||
|
public CompositeMatcher(IMatcher[] matchers, MatchOperator matchOperator, MatchBehaviour matchBehaviour)
|
||||||
|
{
|
||||||
|
Matchers = matchers;
|
||||||
|
MatchOperator = matchOperator;
|
||||||
|
MatchBehaviour = matchBehaviour;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string GetCSharpCodeArguments()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,7 +62,7 @@ public class ContentTypeMatcher : WildcardMatcher
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(input) || !MediaTypeHeaderValue.TryParse(input, out var contentType))
|
if (string.IsNullOrEmpty(input) || !MediaTypeHeaderValue.TryParse(input, out var contentType))
|
||||||
{
|
{
|
||||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
|
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch));
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.IsMatch(contentType.MediaType);
|
return base.IsMatch(contentType.MediaType);
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ public class ExactMatcher : IStringMatcher, IIgnoreCaseMatcher
|
|||||||
: pattern => pattern == input;
|
: pattern => pattern == input;
|
||||||
|
|
||||||
var score = MatchScores.ToScore(_values.Select(v => equals(v)).ToArray(), MatchOperator);
|
var score = MatchScores.ToScore(_values.Select(v => equals(v)).ToArray(), MatchOperator);
|
||||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score));
|
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -106,18 +106,18 @@ public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
|
|||||||
// Input is null or empty and if no patterns defined, return Perfect match.
|
// Input is null or empty and if no patterns defined, return Perfect match.
|
||||||
if (string.IsNullOrEmpty(input) && _patterns.Length == 0)
|
if (string.IsNullOrEmpty(input) && _patterns.Length == 0)
|
||||||
{
|
{
|
||||||
return new MatchResult(MatchScores.Perfect);
|
return MatchResult.From(Name, MatchScores.Perfect);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!QueryStringParser.TryParse(input, IgnoreCase, out var inputNameValueCollection))
|
if (!QueryStringParser.TryParse(input, IgnoreCase, out var inputNameValueCollection))
|
||||||
{
|
{
|
||||||
return new MatchResult(MatchScores.Mismatch);
|
return MatchResult.From(Name, MatchScores.Mismatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
var matches = GetMatches(inputNameValueCollection);
|
var matches = GetMatches(inputNameValueCollection);
|
||||||
|
|
||||||
var score = MatchScores.ToScore(matches, MatchOperator);
|
var score = MatchScores.ToScore(matches, MatchOperator);
|
||||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score));
|
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score));
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool[] GetMatches(IDictionary<string, string> inputNameValueCollection)
|
private bool[] GetMatches(IDictionary<string, string> inputNameValueCollection)
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ public class JsonPathMatcher : IStringMatcher, IObjectMatcher
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -104,7 +104,7 @@ public class JsonPathMatcher : IStringMatcher, IObjectMatcher
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ public class JmesPathMatcher : IStringMatcher, IObjectMatcher
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -102,7 +102,7 @@ public class JmesPathMatcher : IStringMatcher, IObjectMatcher
|
|||||||
return IsMatch(inputAsString);
|
return IsMatch(inputAsString);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MatchBehaviourHelper.Convert(MatchBehaviour, score);
|
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ public class JsonMatcher : IJsonMatcher
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), error);
|
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -24,14 +24,26 @@ public class RequestMatchResult : IRequestMatchResult
|
|||||||
public double AverageTotalScore => TotalNumber == 0 ? MatchScores.Mismatch : TotalScore / TotalNumber;
|
public double AverageTotalScore => TotalNumber == 0 ? MatchScores.Mismatch : TotalScore / TotalNumber;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IList<MatchDetail> MatchDetails { get; } = new List<MatchDetail>();
|
public IList<MatchDetail> MatchDetails { get; } = [];
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public double AddScore(Type matcherType, double score, Exception? exception)
|
public double AddScore(Type matcherType, double score, Exception? exception)
|
||||||
{
|
{
|
||||||
MatchDetails.Add(new MatchDetail { MatcherType = matcherType, Score = score, Exception = exception });
|
return AddMatchDetail(new MatchDetail
|
||||||
|
{
|
||||||
|
Name = matcherType.Name.Replace("RequestMessage", string.Empty),
|
||||||
|
MatcherType = matcherType,
|
||||||
|
Score = score,
|
||||||
|
Exception = exception
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return score;
|
/// <inheritdoc />
|
||||||
|
public double AddMatchDetail(MatchDetail matchDetail)
|
||||||
|
{
|
||||||
|
MatchDetails.Add(matchDetail);
|
||||||
|
|
||||||
|
return matchDetail.Score;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -17,27 +17,27 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The body function
|
/// The body function
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<string?, bool>? Func { get; }
|
public Func<string?, bool>? MatchOnBodyAsStringFunc { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The body data function for byte[]
|
/// The body data function for byte[]
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<byte[]?, bool>? DataFunc { get; }
|
public Func<byte[]?, bool>? MatchOnBodyAsBytesFunc { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The body data function for json
|
/// The body data function for json
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<object?, bool>? JsonFunc { get; }
|
public Func<object?, bool>? MatchOnBodyAsJsonFunc { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The body data function for BodyData
|
/// The body data function for BodyData
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<IBodyData?, bool>? BodyDataFunc { get; }
|
public Func<IBodyData?, bool>? MatchOnBodyAsBodyDataFunc { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The body data function for FormUrlEncoded
|
/// The body data function for FormUrlEncoded
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<IDictionary<string, string>?, bool>? FormUrlEncodedFunc { get; }
|
public Func<IDictionary<string, string>?, bool>? MatchOnBodyAsFormUrlEncodedFunc { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The matchers.
|
/// The matchers.
|
||||||
@@ -85,7 +85,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
|||||||
/// <param name="func">The function.</param>
|
/// <param name="func">The function.</param>
|
||||||
public RequestMessageBodyMatcher(Func<string?, bool> func)
|
public RequestMessageBodyMatcher(Func<string?, bool> func)
|
||||||
{
|
{
|
||||||
Func = Guard.NotNull(func);
|
MatchOnBodyAsStringFunc = Guard.NotNull(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -94,7 +94,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
|||||||
/// <param name="func">The function.</param>
|
/// <param name="func">The function.</param>
|
||||||
public RequestMessageBodyMatcher(Func<byte[]?, bool> func)
|
public RequestMessageBodyMatcher(Func<byte[]?, bool> func)
|
||||||
{
|
{
|
||||||
DataFunc = Guard.NotNull(func);
|
MatchOnBodyAsBytesFunc = Guard.NotNull(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -103,7 +103,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
|||||||
/// <param name="func">The function.</param>
|
/// <param name="func">The function.</param>
|
||||||
public RequestMessageBodyMatcher(Func<object?, bool> func)
|
public RequestMessageBodyMatcher(Func<object?, bool> func)
|
||||||
{
|
{
|
||||||
JsonFunc = Guard.NotNull(func);
|
MatchOnBodyAsJsonFunc = Guard.NotNull(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -112,7 +112,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
|||||||
/// <param name="func">The function.</param>
|
/// <param name="func">The function.</param>
|
||||||
public RequestMessageBodyMatcher(Func<IBodyData?, bool> func)
|
public RequestMessageBodyMatcher(Func<IBodyData?, bool> func)
|
||||||
{
|
{
|
||||||
BodyDataFunc = Guard.NotNull(func);
|
MatchOnBodyAsBodyDataFunc = Guard.NotNull(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -121,7 +121,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
|||||||
/// <param name="func">The function.</param>
|
/// <param name="func">The function.</param>
|
||||||
public RequestMessageBodyMatcher(Func<IDictionary<string, string>?, bool> func)
|
public RequestMessageBodyMatcher(Func<IDictionary<string, string>?, bool> func)
|
||||||
{
|
{
|
||||||
FormUrlEncodedFunc = Guard.NotNull(func);
|
MatchOnBodyAsFormUrlEncodedFunc = Guard.NotNull(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -147,43 +147,43 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||||
{
|
{
|
||||||
var (score, exception) = CalculateMatchScore(requestMessage).Expand();
|
var (score, exception) = CalculateMatchResult(requestMessage).Expand();
|
||||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
return requestMatchResult.AddScore(GetType(), score, exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MatchResult CalculateMatchScore(IRequestMessage requestMessage)
|
private MatchResult CalculateMatchResult(IRequestMessage requestMessage)
|
||||||
{
|
{
|
||||||
if (Matchers != null && Matchers.Any())
|
if (Matchers != null && Matchers.Any())
|
||||||
{
|
{
|
||||||
var results = Matchers.Select(matcher => BodyDataMatchScoreCalculator.CalculateMatchScore(requestMessage.BodyData, matcher)).ToArray();
|
var results = Matchers.Select(matcher => BodyDataMatchScoreCalculator.CalculateMatchScore(requestMessage.BodyData, matcher)).ToArray();
|
||||||
return MatchResult.From(results, MatchOperator);
|
return MatchResult.From(nameof(RequestMessageBodyMatcher), results, MatchOperator);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Func != null)
|
if (MatchOnBodyAsStringFunc != null)
|
||||||
{
|
{
|
||||||
return MatchScores.ToScore(Func(requestMessage.BodyData?.BodyAsString));
|
return MatchResult.From($"{nameof(RequestMessageBodyMatcher)}:{nameof(MatchOnBodyAsStringFunc)}", MatchScores.ToScore(MatchOnBodyAsStringFunc(requestMessage.BodyData?.BodyAsString)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FormUrlEncodedFunc != null)
|
if (MatchOnBodyAsFormUrlEncodedFunc != null)
|
||||||
{
|
{
|
||||||
return MatchScores.ToScore(FormUrlEncodedFunc(requestMessage.BodyData?.BodyAsFormUrlEncoded));
|
return MatchResult.From($"{nameof(RequestMessageBodyMatcher)}:{nameof(MatchOnBodyAsFormUrlEncodedFunc)}", MatchScores.ToScore(MatchOnBodyAsFormUrlEncodedFunc(requestMessage.BodyData?.BodyAsFormUrlEncoded)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JsonFunc != null)
|
if (MatchOnBodyAsJsonFunc != null)
|
||||||
{
|
{
|
||||||
return MatchScores.ToScore(JsonFunc(requestMessage.BodyData?.BodyAsJson));
|
return MatchResult.From($"{nameof(RequestMessageBodyMatcher)}:{nameof(MatchOnBodyAsJsonFunc)}", MatchScores.ToScore(MatchOnBodyAsJsonFunc(requestMessage.BodyData?.BodyAsJson)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DataFunc != null)
|
if (MatchOnBodyAsBytesFunc != null)
|
||||||
{
|
{
|
||||||
return MatchScores.ToScore(DataFunc(requestMessage.BodyData?.BodyAsBytes));
|
return MatchResult.From($"{nameof(RequestMessageBodyMatcher)}:{nameof(MatchOnBodyAsBytesFunc)}", MatchScores.ToScore(MatchOnBodyAsBytesFunc(requestMessage.BodyData?.BodyAsBytes)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BodyDataFunc != null)
|
if (MatchOnBodyAsBodyDataFunc != null)
|
||||||
{
|
{
|
||||||
return MatchScores.ToScore(BodyDataFunc(requestMessage.BodyData));
|
return MatchResult.From($"{nameof(RequestMessageBodyMatcher)}:{nameof(MatchOnBodyAsBodyDataFunc)}", MatchScores.ToScore(MatchOnBodyAsBodyDataFunc(requestMessage.BodyData)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return default;
|
return MatchResult.From(nameof(RequestMessageBodyMatcher));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,8 @@ namespace WireMock.Matchers.Request;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class RequestMessageBodyMatcher<T> : IRequestMatcher
|
public class RequestMessageBodyMatcher<T> : IRequestMatcher
|
||||||
{
|
{
|
||||||
|
private const string _name = nameof(RequestMessageBodyMatcher<T>);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The body data function for type T
|
/// The body data function for type T
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -46,15 +48,15 @@ public class RequestMessageBodyMatcher<T> : IRequestMatcher
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var bodyAsT = jsonObject.ToObject<T>();
|
var bodyAsT = jsonObject.ToObject<T>();
|
||||||
return MatchScores.ToScore(Func(bodyAsT));
|
return MatchResult.From(_name, MatchScores.ToScore(Func(bodyAsT)));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return new MatchResult(ex);
|
return MatchResult.From(_name, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return default;
|
return MatchResult.From(_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,6 +14,8 @@ namespace WireMock.Matchers.Request;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class RequestMessageClientIPMatcher : IRequestMatcher
|
public class RequestMessageClientIPMatcher : IRequestMatcher
|
||||||
{
|
{
|
||||||
|
private const string _name = nameof(RequestMessageClientIPMatcher);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The matchers
|
/// The matchers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -77,8 +79,8 @@ public class RequestMessageClientIPMatcher : IRequestMatcher
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||||
{
|
{
|
||||||
var (score, exception) = GetMatchResult(requestMessage).Expand();
|
var matchDetail = GetMatchResult(requestMessage).ToMatchDetail();
|
||||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
return requestMatchResult.AddMatchDetail(matchDetail);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MatchResult GetMatchResult(IRequestMessage requestMessage)
|
private MatchResult GetMatchResult(IRequestMessage requestMessage)
|
||||||
@@ -86,15 +88,15 @@ public class RequestMessageClientIPMatcher : IRequestMatcher
|
|||||||
if (Matchers != null)
|
if (Matchers != null)
|
||||||
{
|
{
|
||||||
var results = Matchers.Select(m => m.IsMatch(requestMessage.ClientIP)).ToArray();
|
var results = Matchers.Select(m => m.IsMatch(requestMessage.ClientIP)).ToArray();
|
||||||
return MatchResult.From(results, MatchOperator);
|
return MatchResult.From(_name, results, MatchOperator);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Funcs != null)
|
if (Funcs != null)
|
||||||
{
|
{
|
||||||
var results = Funcs.Select(func => func(requestMessage.ClientIP)).ToArray();
|
var results = Funcs.Select(func => func(requestMessage.ClientIP)).ToArray();
|
||||||
return MatchScores.ToScore(results, MatchOperator);
|
return MatchResult.From(_name, MatchScores.ToScore(results, MatchOperator));
|
||||||
}
|
}
|
||||||
|
|
||||||
return default;
|
return MatchResult.From(_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,8 @@ namespace WireMock.Matchers.Request;
|
|||||||
/// <inheritdoc cref="IRequestMatcher"/>
|
/// <inheritdoc cref="IRequestMatcher"/>
|
||||||
public class RequestMessageCookieMatcher : IRequestMatcher
|
public class RequestMessageCookieMatcher : IRequestMatcher
|
||||||
{
|
{
|
||||||
|
private const string _name = nameof(RequestMessageCookieMatcher);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// MatchBehaviour
|
/// MatchBehaviour
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -104,7 +106,7 @@ public class RequestMessageCookieMatcher : IRequestMatcher
|
|||||||
{
|
{
|
||||||
if (requestMessage.Cookies == null)
|
if (requestMessage.Cookies == null)
|
||||||
{
|
{
|
||||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
|
return MatchResult.From(_name, MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we want to use IgnoreCase to compare the Cookie-Name and Cookie-Value
|
// Check if we want to use IgnoreCase to compare the Cookie-Name and Cookie-Value
|
||||||
@@ -112,19 +114,19 @@ public class RequestMessageCookieMatcher : IRequestMatcher
|
|||||||
|
|
||||||
if (Funcs != null)
|
if (Funcs != null)
|
||||||
{
|
{
|
||||||
return MatchScores.ToScore(Funcs.Any(f => f(cookies)));
|
return MatchResult.From(_name, MatchScores.ToScore(Funcs.Any(f => f(cookies))));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Matchers == null)
|
if (Matchers == null)
|
||||||
{
|
{
|
||||||
return default;
|
return MatchResult.From(_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cookies.ContainsKey(Name))
|
if (!cookies.ContainsKey(Name))
|
||||||
{
|
{
|
||||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
|
return MatchResult.From(_name, MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Matchers.Max(m => m.IsMatch(cookies[Name]));
|
return MatchResult.From(_name, Matchers.Max(m => m.IsMatch(cookies[Name]))?.Score ?? MatchScores.Mismatch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,6 +14,8 @@ namespace WireMock.Matchers.Request;
|
|||||||
/// <inheritdoc cref="IRequestMatcher"/>
|
/// <inheritdoc cref="IRequestMatcher"/>
|
||||||
public class RequestMessageHeaderMatcher : IRequestMatcher
|
public class RequestMessageHeaderMatcher : IRequestMatcher
|
||||||
{
|
{
|
||||||
|
private const string _name = nameof(RequestMessageCookieMatcher);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// MatchBehaviour
|
/// MatchBehaviour
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -117,7 +119,7 @@ public class RequestMessageHeaderMatcher : IRequestMatcher
|
|||||||
{
|
{
|
||||||
if (requestMessage.Headers == null)
|
if (requestMessage.Headers == null)
|
||||||
{
|
{
|
||||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
|
return MatchResult.From(_name, MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we want to use IgnoreCase to compare the Header-Name and Header-Value(s)
|
// Check if we want to use IgnoreCase to compare the Header-Name and Header-Value(s)
|
||||||
@@ -126,14 +128,14 @@ public class RequestMessageHeaderMatcher : IRequestMatcher
|
|||||||
if (Funcs != null)
|
if (Funcs != null)
|
||||||
{
|
{
|
||||||
var funcResults = Funcs.Select(f => f(headers.ToDictionary(entry => entry.Key, entry => entry.Value.ToArray()))).ToArray();
|
var funcResults = Funcs.Select(f => f(headers.ToDictionary(entry => entry.Key, entry => entry.Value.ToArray()))).ToArray();
|
||||||
return MatchScores.ToScore(funcResults, MatchOperator);
|
return MatchResult.From(_name, MatchScores.ToScore(funcResults, MatchOperator));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Matchers != null)
|
if (Matchers != null)
|
||||||
{
|
{
|
||||||
if (!headers.ContainsKey(Name))
|
if (!headers.ContainsKey(Name))
|
||||||
{
|
{
|
||||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
|
return MatchResult.From(_name, MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch));
|
||||||
}
|
}
|
||||||
|
|
||||||
var results = new List<MatchResult>();
|
var results = new List<MatchResult>();
|
||||||
@@ -141,12 +143,12 @@ public class RequestMessageHeaderMatcher : IRequestMatcher
|
|||||||
{
|
{
|
||||||
var resultsPerMatcher = headers[Name].Select(matcher.IsMatch).ToArray();
|
var resultsPerMatcher = headers[Name].Select(matcher.IsMatch).ToArray();
|
||||||
|
|
||||||
results.Add(MatchResult.From(resultsPerMatcher, MatchOperator.And));
|
results.Add(MatchResult.From(_name, resultsPerMatcher, MatchOperator.And));
|
||||||
}
|
}
|
||||||
|
|
||||||
return MatchResult.From(results, MatchOperator);
|
return MatchResult.From(_name, results, MatchOperator);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
|
return MatchResult.From(_name, MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,8 @@ namespace WireMock.Matchers.Request;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class RequestMessageHttpVersionMatcher : IRequestMatcher
|
public class RequestMessageHttpVersionMatcher : IRequestMatcher
|
||||||
{
|
{
|
||||||
|
private const string _name = nameof(RequestMessageHttpVersionMatcher);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The matcher.
|
/// The matcher.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -19,7 +21,7 @@ public class RequestMessageHttpVersionMatcher : IRequestMatcher
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The func.
|
/// The func.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<string, bool>? Func { get; }
|
public Func<string, bool>? MatcherOnStringFunc { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="MatchBehaviour"/>
|
/// The <see cref="MatchBehaviour"/>
|
||||||
@@ -61,7 +63,7 @@ public class RequestMessageHttpVersionMatcher : IRequestMatcher
|
|||||||
/// <param name="func">The function.</param>
|
/// <param name="func">The function.</param>
|
||||||
public RequestMessageHttpVersionMatcher(Func<string, bool> func)
|
public RequestMessageHttpVersionMatcher(Func<string, bool> func)
|
||||||
{
|
{
|
||||||
Func = Guard.NotNull(func);
|
MatcherOnStringFunc = Guard.NotNull(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -78,11 +80,11 @@ public class RequestMessageHttpVersionMatcher : IRequestMatcher
|
|||||||
return Matcher.IsMatch(requestMessage.HttpVersion);
|
return Matcher.IsMatch(requestMessage.HttpVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Func != null)
|
if (MatcherOnStringFunc != null)
|
||||||
{
|
{
|
||||||
return MatchScores.ToScore(Func(requestMessage.HttpVersion));
|
return MatchResult.From($"{_name}:{nameof(MatcherOnStringFunc)}", MatchScores.ToScore(MatcherOnStringFunc(requestMessage.HttpVersion)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return default;
|
return MatchResult.From(_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,11 @@ namespace WireMock.Matchers.Request;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class RequestMessageMultiPartMatcher : IRequestMatcher
|
public class RequestMessageMultiPartMatcher : IRequestMatcher
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The name of this matcher.
|
||||||
|
/// </summary>
|
||||||
|
public const string Name = "MultiPartMatcher";
|
||||||
|
|
||||||
private readonly IMimeKitUtils _mimeKitUtils = LoadMimeKitUtils();
|
private readonly IMimeKitUtils _mimeKitUtils = LoadMimeKitUtils();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -22,7 +27,7 @@ public class RequestMessageMultiPartMatcher : IRequestMatcher
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="MatchOperator"/>
|
/// The <see cref="MatchOperator"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MatchOperator MatchOperator { get; } = MatchOperator.Or;
|
public MatchOperator MatchOperator { get; } = MatchOperator.And;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="MatchBehaviour"/>
|
/// The <see cref="MatchBehaviour"/>
|
||||||
@@ -54,19 +59,20 @@ public class RequestMessageMultiPartMatcher : IRequestMatcher
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||||
{
|
{
|
||||||
var score = MatchScores.Mismatch;
|
var matchDetail = MatchResult.From(Name).ToMatchDetail();
|
||||||
Exception? exception = null;
|
Exception? exception = null;
|
||||||
|
|
||||||
if (Matchers?.Any() != true)
|
if (Matchers == null)
|
||||||
{
|
{
|
||||||
return requestMatchResult.AddScore(GetType(), score, null);
|
return requestMatchResult.AddMatchDetail(matchDetail);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_mimeKitUtils.TryGetMimeMessage(requestMessage, out var message))
|
if (!_mimeKitUtils.TryGetMimeMessage(requestMessage, out var message))
|
||||||
{
|
{
|
||||||
return requestMatchResult.AddScore(GetType(), score, null);
|
return requestMatchResult.AddMatchDetail(matchDetail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double score = MatchScores.Mismatch;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (var mimePartMatcher in Matchers.OfType<IMimePartMatcher>().ToArray())
|
foreach (var mimePartMatcher in Matchers.OfType<IMimePartMatcher>().ToArray())
|
||||||
@@ -94,7 +100,7 @@ public class RequestMessageMultiPartMatcher : IRequestMatcher
|
|||||||
exception = ex;
|
exception = ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
return requestMatchResult.AddMatchDetail(MatchResult.From(Name, score, exception).ToMatchDetail());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IMimeKitUtils LoadMimeKitUtils()
|
private static IMimeKitUtils LoadMimeKitUtils()
|
||||||
|
|||||||
@@ -77,8 +77,8 @@ public class RequestMessagePathMatcher : IRequestMatcher
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||||
{
|
{
|
||||||
var (score, exception) = GetMatchResult(requestMessage).Expand();
|
var matchDetail = GetMatchResult(requestMessage).ToMatchDetail();
|
||||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
return requestMatchResult.AddMatchDetail(matchDetail);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MatchResult GetMatchResult(IRequestMessage requestMessage)
|
private MatchResult GetMatchResult(IRequestMessage requestMessage)
|
||||||
@@ -86,15 +86,16 @@ public class RequestMessagePathMatcher : IRequestMatcher
|
|||||||
if (Matchers != null)
|
if (Matchers != null)
|
||||||
{
|
{
|
||||||
var results = Matchers.Select(m => m.IsMatch(requestMessage.Path)).ToArray();
|
var results = Matchers.Select(m => m.IsMatch(requestMessage.Path)).ToArray();
|
||||||
return MatchResult.From(results, MatchOperator);
|
return MatchResult.From(nameof(RequestMessagePathMatcher), results, MatchOperator);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Funcs != null)
|
if (Funcs != null)
|
||||||
{
|
{
|
||||||
var results = Funcs.Select(func => func(requestMessage.Path)).ToArray();
|
var results = Funcs.Select(func => func(requestMessage.Path)).ToArray();
|
||||||
return MatchScores.ToScore(results, MatchOperator);
|
var score = MatchScores.ToScore(results, MatchOperator);
|
||||||
|
return MatchResult.From(nameof(RequestMessagePathMatcher), score);
|
||||||
}
|
}
|
||||||
|
|
||||||
return default;
|
return MatchResult.From(nameof(RequestMessagePathMatcher));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,15 +86,16 @@ public class RequestMessageUrlMatcher : IRequestMatcher
|
|||||||
if (Matchers != null)
|
if (Matchers != null)
|
||||||
{
|
{
|
||||||
var results = Matchers.Select(m => m.IsMatch(requestMessage.Url)).ToArray();
|
var results = Matchers.Select(m => m.IsMatch(requestMessage.Url)).ToArray();
|
||||||
return MatchResult.From(results, MatchOperator);
|
return MatchResult.From(nameof(RequestMessageUrlMatcher), results, MatchOperator);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Funcs != null)
|
if (Funcs != null)
|
||||||
{
|
{
|
||||||
var results = Funcs.Select(func => func(requestMessage.Url)).ToArray();
|
var results = Funcs.Select(func => func(requestMessage.Url)).ToArray();
|
||||||
return MatchScores.ToScore(results, MatchOperator);
|
var score = MatchScores.ToScore(results, MatchOperator);
|
||||||
|
return MatchResult.From(nameof(RequestMessageUrlMatcher), score);
|
||||||
}
|
}
|
||||||
|
|
||||||
return default;
|
return MatchResult.From(nameof(RequestMessageUrlMatcher));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ public class SimMetricsMatcher : IStringMatcher
|
|||||||
IStringMetric stringMetricType = GetStringMetricType();
|
IStringMetric stringMetricType = GetStringMetricType();
|
||||||
|
|
||||||
var score = MatchScores.ToScore(_patterns.Select(p => stringMetricType.GetSimilarity(p.GetPattern(), input)).ToArray(), MatchOperator);
|
var score = MatchScores.ToScore(_patterns.Select(p => stringMetricType.GetSimilarity(p.GetPattern(), input)).ToArray(), MatchOperator);
|
||||||
return MatchBehaviourHelper.Convert(MatchBehaviour, score);
|
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ public class XPathMatcher : IStringMatcher
|
|||||||
|
|
||||||
private MatchResult CreateMatchResult(double score, Exception? exception = null)
|
private MatchResult CreateMatchResult(double score, Exception? exception = null)
|
||||||
{
|
{
|
||||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class XPathEvaluator
|
private sealed class XPathEvaluator
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public partial class Request
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IRequestBuilder WithMultiPart(IMatcher[] matchers, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch, MatchOperator matchOperator = MatchOperator.Or)
|
public IRequestBuilder WithMultiPart(IMatcher[] matchers, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch, MatchOperator matchOperator = MatchOperator.And)
|
||||||
{
|
{
|
||||||
_requestMatchers.Add(new RequestMessageMultiPartMatcher(matchBehaviour, matchOperator, matchers));
|
_requestMatchers.Add(new RequestMessageMultiPartMatcher(matchBehaviour, matchOperator, matchers));
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -166,11 +166,12 @@ internal class LogEntryMapper
|
|||||||
TotalScore = matchResult.TotalScore,
|
TotalScore = matchResult.TotalScore,
|
||||||
TotalNumber = matchResult.TotalNumber,
|
TotalNumber = matchResult.TotalNumber,
|
||||||
AverageTotalScore = matchResult.AverageTotalScore,
|
AverageTotalScore = matchResult.AverageTotalScore,
|
||||||
MatchDetails = matchResult.MatchDetails.Select(md => new
|
MatchDetails = matchResult.MatchDetails
|
||||||
{
|
//MatchDetails = matchResult.MatchDetails.Select(md => new
|
||||||
Name = md.MatcherType.Name.Replace("RequestMessage", string.Empty),
|
//{
|
||||||
md.Score
|
// Name = md.MatcherType.Name.Replace("RequestMessage", string.Empty),
|
||||||
} as object).ToList()
|
// md.Score
|
||||||
|
//} as object).ToList()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,9 +25,9 @@ internal class MatcherMapper
|
|||||||
_settings = Guard.NotNull(settings);
|
_settings = Guard.NotNull(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IMatcher[]? Map(IEnumerable<MatcherModel>? matchers)
|
public IMatcher[] Map(IEnumerable<MatcherModel>? matchers)
|
||||||
{
|
{
|
||||||
return matchers?.Select(Map).OfType<IMatcher>().ToArray();
|
return matchers?.Select(Map).OfType<IMatcher>().ToArray() ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public IMatcher? Map(MatcherModel? matcherModel)
|
public IMatcher? Map(MatcherModel? matcherModel)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.Linq;
|
|||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
|
using WireMock.Matchers.Request;
|
||||||
using WireMock.RequestBuilders;
|
using WireMock.RequestBuilders;
|
||||||
using WireMock.ResponseBuilders;
|
using WireMock.ResponseBuilders;
|
||||||
using WireMock.Serialization;
|
using WireMock.Serialization;
|
||||||
@@ -253,7 +254,15 @@ public partial class WireMockServer
|
|||||||
else if (requestModel.Body?.Matchers != null)
|
else if (requestModel.Body?.Matchers != null)
|
||||||
{
|
{
|
||||||
var matchOperator = StringUtils.ParseMatchOperator(requestModel.Body.MatchOperator);
|
var matchOperator = StringUtils.ParseMatchOperator(requestModel.Body.MatchOperator);
|
||||||
requestBuilder = requestBuilder.WithBody(_matcherMapper.Map(requestModel.Body.Matchers)!, matchOperator);
|
|
||||||
|
if (requestModel.Body.MatcherName == RequestMessageMultiPartMatcher.Name)
|
||||||
|
{
|
||||||
|
requestBuilder = requestBuilder.WithMultiPart(_matcherMapper.Map(requestModel.Body.Matchers), matchOperator: matchOperator);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
requestBuilder = requestBuilder.WithBody(_matcherMapper.Map(requestModel.Body.Matchers), matchOperator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return requestBuilder;
|
return requestBuilder;
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class ProtoBufMatcher : IProtoBufMatcher
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<MatchResult> IsMatchAsync(byte[]? input, CancellationToken cancellationToken = default)
|
public async Task<MatchResult> IsMatchAsync(byte[]? input, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var result = new MatchResult();
|
var result = MatchResult.From(Name);
|
||||||
|
|
||||||
if (input != null)
|
if (input != null)
|
||||||
{
|
{
|
||||||
@@ -65,11 +65,11 @@ public class ProtoBufMatcher : IProtoBufMatcher
|
|||||||
{
|
{
|
||||||
var instance = await DecodeAsync(input, true, cancellationToken).ConfigureAwait(false);
|
var instance = await DecodeAsync(input, true, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
result = Matcher?.IsMatch(instance) ?? new MatchResult(MatchScores.Perfect);
|
result = Matcher?.IsMatch(instance) ?? MatchResult.From(Name, MatchScores.Perfect);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
result = new MatchResult(MatchScores.Mismatch, e);
|
result = MatchResult.From(Name, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public class ExactObjectMatcher : IObjectMatcher
|
|||||||
equals = Equals(Value, input);
|
equals = Equals(Value, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(equals));
|
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(equals)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -8,66 +8,68 @@ namespace WireMock.Matchers.Helpers;
|
|||||||
|
|
||||||
internal static class BodyDataMatchScoreCalculator
|
internal static class BodyDataMatchScoreCalculator
|
||||||
{
|
{
|
||||||
internal static MatchResult CalculateMatchScore(IBodyData? requestMessage, IMatcher matcher)
|
private static string _name = nameof(BodyDataMatchScoreCalculator);
|
||||||
|
|
||||||
|
internal static MatchResult CalculateMatchScore(IBodyData? bodyData, IMatcher matcher)
|
||||||
{
|
{
|
||||||
Guard.NotNull(matcher);
|
Guard.NotNull(matcher);
|
||||||
|
|
||||||
if (requestMessage == null)
|
if (bodyData == null)
|
||||||
{
|
{
|
||||||
return default;
|
return MatchResult.From(_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matcher is NotNullOrEmptyMatcher notNullOrEmptyMatcher)
|
if (matcher is NotNullOrEmptyMatcher notNullOrEmptyMatcher)
|
||||||
{
|
{
|
||||||
switch (requestMessage.DetectedBodyType)
|
switch (bodyData.DetectedBodyType)
|
||||||
{
|
{
|
||||||
case BodyType.Json:
|
case BodyType.Json:
|
||||||
case BodyType.String:
|
case BodyType.String:
|
||||||
case BodyType.FormUrlEncoded:
|
case BodyType.FormUrlEncoded:
|
||||||
return notNullOrEmptyMatcher.IsMatch(requestMessage.BodyAsString);
|
return notNullOrEmptyMatcher.IsMatch(bodyData.BodyAsString);
|
||||||
|
|
||||||
case BodyType.Bytes:
|
case BodyType.Bytes:
|
||||||
return notNullOrEmptyMatcher.IsMatch(requestMessage.BodyAsBytes);
|
return notNullOrEmptyMatcher.IsMatch(bodyData.BodyAsBytes);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return default;
|
return MatchResult.From(_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matcher is ExactObjectMatcher { Value: byte[] } exactObjectMatcher)
|
if (matcher is ExactObjectMatcher { Value: byte[] } exactObjectMatcher)
|
||||||
{
|
{
|
||||||
// If the body is a byte array, try to match.
|
// If the body is a byte array, try to match.
|
||||||
return exactObjectMatcher.IsMatch(requestMessage.BodyAsBytes);
|
return exactObjectMatcher.IsMatch(bodyData.BodyAsBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the matcher is a IObjectMatcher
|
// Check if the matcher is a IObjectMatcher
|
||||||
if (matcher is IObjectMatcher objectMatcher)
|
if (matcher is IObjectMatcher objectMatcher)
|
||||||
{
|
{
|
||||||
// If the body is a JSON object, try to match.
|
// If the body is a JSON object, try to match.
|
||||||
if (requestMessage.DetectedBodyType == BodyType.Json)
|
if (bodyData.DetectedBodyType == BodyType.Json)
|
||||||
{
|
{
|
||||||
return objectMatcher.IsMatch(requestMessage.BodyAsJson);
|
return objectMatcher.IsMatch(bodyData.BodyAsJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the body is a byte array, try to match.
|
// If the body is a byte array, try to match.
|
||||||
if (requestMessage.DetectedBodyType == BodyType.Bytes)
|
if (bodyData.DetectedBodyType == BodyType.Bytes)
|
||||||
{
|
{
|
||||||
return objectMatcher.IsMatch(requestMessage.BodyAsBytes);
|
return objectMatcher.IsMatch(bodyData.BodyAsBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case the matcher is a IStringMatcher and If body is a Json or a String, use the BodyAsString to match on.
|
// In case the matcher is a IStringMatcher and if body is a Json or a String, use the BodyAsString to match on.
|
||||||
if (matcher is IStringMatcher stringMatcher && requestMessage.DetectedBodyType is BodyType.Json or BodyType.String or BodyType.FormUrlEncoded)
|
if (matcher is IStringMatcher stringMatcher && bodyData.DetectedBodyType is BodyType.Json or BodyType.String or BodyType.FormUrlEncoded)
|
||||||
{
|
{
|
||||||
return stringMatcher.IsMatch(requestMessage.BodyAsString);
|
return stringMatcher.IsMatch(bodyData.BodyAsString);
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case the matcher is a IProtoBufMatcher, use the BodyAsBytes to match on.
|
// In case the matcher is a IProtoBufMatcher, use the BodyAsBytes to match on.
|
||||||
if (matcher is IProtoBufMatcher protoBufMatcher)
|
if (matcher is IProtoBufMatcher protoBufMatcher)
|
||||||
{
|
{
|
||||||
return protoBufMatcher.IsMatchAsync(requestMessage.BodyAsBytes).GetAwaiter().GetResult();
|
return protoBufMatcher.IsMatchAsync(bodyData.BodyAsBytes).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
return default;
|
return MatchResult.From(_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,6 +36,6 @@ internal static class MatchBehaviourHelper
|
|||||||
/// <returns>match result</returns>
|
/// <returns>match result</returns>
|
||||||
internal static MatchResult Convert(MatchBehaviour matchBehaviour, MatchResult result)
|
internal static MatchResult Convert(MatchBehaviour matchBehaviour, MatchResult result)
|
||||||
{
|
{
|
||||||
return matchBehaviour == MatchBehaviour.AcceptOnMatch ? result : new MatchResult(Convert(matchBehaviour, result.Score), result.Exception);
|
return matchBehaviour == MatchBehaviour.AcceptOnMatch ? result : MatchResult.From(result.Name, Convert(matchBehaviour, result.Score), result.Exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,13 +5,14 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Extensions;
|
using WireMock.Extensions;
|
||||||
|
using WireMock.Matchers.Request;
|
||||||
|
|
||||||
namespace WireMock.Matchers;
|
namespace WireMock.Matchers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The MatchResult which contains the score (value between 0.0 - 1.0 of the similarity) and an optional error message.
|
/// The MatchResult which contains the score (value between 0.0 - 1.0 of the similarity) and an optional error message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct MatchResult
|
public class MatchResult
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A value between 0.0 - 1.0 of the similarity.
|
/// A value between 0.0 - 1.0 of the similarity.
|
||||||
@@ -25,46 +26,55 @@ public struct MatchResult
|
|||||||
public Exception? Exception { get; set; }
|
public Exception? Exception { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a MatchResult
|
/// The name or description of the matcher.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="score">A value between 0.0 - 1.0 of the similarity.</param>
|
public required string Name { get; set; }
|
||||||
/// <param name="exception">The exception in case the matching fails. [Optional]</param>
|
|
||||||
public MatchResult(double score, Exception? exception = null)
|
|
||||||
{
|
|
||||||
Score = score;
|
|
||||||
Exception = exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a MatchResult
|
/// The child MatchResults in case of multiple matchers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="exception">The exception in case the matching fails.</param>
|
public MatchResult[]? MatchResults { get; set; }
|
||||||
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>
|
/// <summary>
|
||||||
/// Is the value a perfect match?
|
/// Is the value a perfect match?
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsPerfect() => MatchScores.IsPerfect(Score);
|
public bool IsPerfect() => MatchScores.IsPerfect(Score);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a MatchResult.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name or description of the matcher.</param>
|
||||||
|
/// <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 static MatchResult From(string name, double score = 0, Exception? exception = null)
|
||||||
|
{
|
||||||
|
return new MatchResult
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
Score = score,
|
||||||
|
Exception = exception
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a MatchResult from exception.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name or description of the matcher.</param>
|
||||||
|
/// <param name="exception">The exception in case the matching fails.</param>
|
||||||
|
/// <returns>MatchResult</returns>
|
||||||
|
public static MatchResult From(string name, Exception exception)
|
||||||
|
{
|
||||||
|
return From(name, MatchScores.Mismatch, exception);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a MatchResult from multiple MatchResults.
|
/// Create a MatchResult from multiple MatchResults.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="name">The name or description of the matcher.</param>
|
||||||
/// <param name="matchResults">A list of MatchResults.</param>
|
/// <param name="matchResults">A list of MatchResults.</param>
|
||||||
/// <param name="matchOperator">The MatchOperator</param>
|
/// <param name="matchOperator">The MatchOperator</param>
|
||||||
/// <returns>MatchResult</returns>
|
/// <returns>MatchResult</returns>
|
||||||
public static MatchResult From(IReadOnlyList<MatchResult> matchResults, MatchOperator matchOperator)
|
public static MatchResult From(string name, IReadOnlyList<MatchResult> matchResults, MatchOperator matchOperator)
|
||||||
{
|
{
|
||||||
Guard.NotNullOrEmpty(matchResults);
|
Guard.NotNullOrEmpty(matchResults);
|
||||||
|
|
||||||
@@ -75,6 +85,8 @@ public struct MatchResult
|
|||||||
|
|
||||||
return new MatchResult
|
return new MatchResult
|
||||||
{
|
{
|
||||||
|
Name = name,
|
||||||
|
MatchResults = matchResults.ToArray(),
|
||||||
Score = MatchScores.ToScore(matchResults.Select(r => r.Score).ToArray(), matchOperator),
|
Score = MatchScores.ToScore(matchResults.Select(r => r.Score).ToArray(), matchOperator),
|
||||||
Exception = matchResults.Select(m => m.Exception).OfType<Exception>().ToArray().ToException()
|
Exception = matchResults.Select(m => m.Exception).OfType<Exception>().ToArray().ToException()
|
||||||
};
|
};
|
||||||
@@ -88,4 +100,19 @@ public struct MatchResult
|
|||||||
{
|
{
|
||||||
return (Score, Exception);
|
return (Score, Exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert to <see cref="MatchResult"/>.
|
||||||
|
/// </summary>
|
||||||
|
public MatchDetail ToMatchDetail()
|
||||||
|
{
|
||||||
|
return new MatchDetail
|
||||||
|
{
|
||||||
|
Name = Name,
|
||||||
|
MatcherType = typeof(MatchResult),
|
||||||
|
Score = Score,
|
||||||
|
Exception = Exception,
|
||||||
|
MatchDetails = MatchResults?.Select(mr => mr.ToMatchDetail()).ToArray()
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using AnyOfTypes;
|
using AnyOfTypes;
|
||||||
using WireMock.Extensions;
|
using WireMock.Extensions;
|
||||||
@@ -53,7 +52,7 @@ public class NotNullOrEmptyMatcher : IObjectMatcher, IStringMatcher
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(match));
|
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(match)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -61,7 +60,7 @@ public class NotNullOrEmptyMatcher : IObjectMatcher, IStringMatcher
|
|||||||
{
|
{
|
||||||
var match = !string.IsNullOrEmpty(input);
|
var match = !string.IsNullOrEmpty(input);
|
||||||
|
|
||||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(match));
|
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(match)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public class RequestMessageGraphQLMatcher : IRequestMatcher
|
|||||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||||
{
|
{
|
||||||
var results = CalculateMatchResults(requestMessage);
|
var results = CalculateMatchResults(requestMessage);
|
||||||
var (score, exception) = MatchResult.From(results, MatchOperator).Expand();
|
var (score, exception) = MatchResult.From(nameof(RequestMessageGraphQLMatcher), results, MatchOperator).Expand();
|
||||||
|
|
||||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
return requestMatchResult.AddScore(GetType(), score, exception);
|
||||||
}
|
}
|
||||||
@@ -86,12 +86,12 @@ public class RequestMessageGraphQLMatcher : IRequestMatcher
|
|||||||
return stringMatcher.IsMatch(requestMessage.BodyData.BodyAsString);
|
return stringMatcher.IsMatch(requestMessage.BodyData.BodyAsString);
|
||||||
}
|
}
|
||||||
|
|
||||||
return default;
|
return MatchResult.From(nameof(RequestMessageGraphQLMatcher));
|
||||||
}
|
}
|
||||||
|
|
||||||
private IReadOnlyList<MatchResult> CalculateMatchResults(IRequestMessage requestMessage)
|
private IReadOnlyList<MatchResult> CalculateMatchResults(IRequestMessage requestMessage)
|
||||||
{
|
{
|
||||||
return Matchers == null ? [new MatchResult()] : Matchers.Select(matcher => CalculateMatchResult(requestMessage, matcher)).ToArray();
|
return Matchers == null ? [MatchResult.From(nameof(RequestMessageGraphQLMatcher))] : Matchers.Select(matcher => CalculateMatchResult(requestMessage, matcher)).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IMatcher[] CreateMatcherArray(
|
private static IMatcher[] CreateMatcherArray(
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public interface IMultiPartRequestBuilder : IHttpVersionBuilder
|
|||||||
/// <param name="matchBehaviour">The <see cref="MatchBehaviour"/> to use.</param>
|
/// <param name="matchBehaviour">The <see cref="MatchBehaviour"/> to use.</param>
|
||||||
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use.</param>
|
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use.</param>
|
||||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||||
IRequestBuilder WithMultiPart(IMatcher[] matchers, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch, MatchOperator matchOperator = MatchOperator.Or);
|
IRequestBuilder WithMultiPart(IMatcher[] matchers, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch, MatchOperator matchOperator = MatchOperator.And);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// WithMultiPart: MatchBehaviour and IMatcher[]
|
/// WithMultiPart: MatchBehaviour and IMatcher[]
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||||
<PackageReference Include="WireMock.Net" Version="1.8.11" />
|
<PackageReference Include="WireMock.Net" Version="1.23.0" />
|
||||||
<PackageReference Include="xunit" Version="2.9.3" />
|
<PackageReference Include="xunit" Version="2.9.3" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.1">
|
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.1">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public partial class WireMockServerTests
|
|||||||
var textPlainMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, textPlainContentTypeMatcher, null, null, textPlainContentMatcher);
|
var textPlainMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, textPlainContentTypeMatcher, null, null, textPlainContentMatcher);
|
||||||
|
|
||||||
var textJson = "{ \"Key\" : \"Value\" }";
|
var textJson = "{ \"Key\" : \"Value\" }";
|
||||||
var textJsonContentType = "text/json";
|
var textJsonContentType = "applicatiom/json";
|
||||||
var textJsonContentTypeMatcher = new ContentTypeMatcher(textJsonContentType);
|
var textJsonContentTypeMatcher = new ContentTypeMatcher(textJsonContentType);
|
||||||
var textJsonContentMatcher = new JsonMatcher(new { Key = "Value" }, true);
|
var textJsonContentMatcher = new JsonMatcher(new { Key = "Value" }, true);
|
||||||
var jsonMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, textJsonContentTypeMatcher, null, null, textJsonContentMatcher);
|
var jsonMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, textJsonContentTypeMatcher, null, null, textJsonContentMatcher);
|
||||||
|
|||||||
@@ -15,32 +15,31 @@ public class MimePartMatcherTests
|
|||||||
|
|
||||||
private const string TestMultiPart =
|
private const string TestMultiPart =
|
||||||
"""
|
"""
|
||||||
From:
|
Content-Type: multipart/mixed; boundary=----MyBoundary123
|
||||||
Date: Sun, 23 Jul 2023 16:13:13 +0200
|
|
||||||
Subject:
|
|
||||||
Message-Id: <HZ3K1HEAJKU4.IO57XCVO4BWV@desktop-6dd5qi2>
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: multipart/mixed; boundary="=-5XgmpXt0XOfzdtcgNJc2ZQ=="
|
|
||||||
|
|
||||||
--=-5XgmpXt0XOfzdtcgNJc2ZQ==
|
------MyBoundary123
|
||||||
Content-Type: text/plain; charset=utf-8
|
Content-Type: text/plain
|
||||||
|
Content-Disposition: form-data; name="textPart"
|
||||||
|
|
||||||
This is some plain text
|
This is some plain text.
|
||||||
--=-5XgmpXt0XOfzdtcgNJc2ZQ==
|
------MyBoundary123
|
||||||
Content-Type: text/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
Content-Disposition: form-data; name="jsonPart"
|
||||||
|
|
||||||
{
|
{
|
||||||
"Key": "Value"
|
"id": 42,
|
||||||
|
"message": "Hello from JSON"
|
||||||
}
|
}
|
||||||
--=-5XgmpXt0XOfzdtcgNJc2ZQ==
|
|
||||||
Content-Type: image/png; name=image.png
|
------MyBoundary123
|
||||||
Content-Disposition: attachment; filename=image.png
|
Content-Type: image/png
|
||||||
|
Content-Disposition: form-data; name="imagePart"; filename="example.png"
|
||||||
Content-Transfer-Encoding: base64
|
Content-Transfer-Encoding: base64
|
||||||
|
|
||||||
iVBORw0KGgoAAAANSUhEUgAAAAIAAAACAgMAAAAP2OW3AAAADFBMVEX/tID/vpH/pWX/sHidUyjl
|
iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4
|
||||||
AAAADElEQVR4XmMQYNgAAADkAMHebX3mAAAAAElFTkSuQmCC
|
//8wEzIABCMDgAEMwEAAAwAA//8DAKkCBf4AAAAASUVORK5CYII=
|
||||||
|
|
||||||
--=-5XgmpXt0XOfzdtcgNJc2ZQ==--
|
------MyBoundary123--
|
||||||
""";
|
""";
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -52,7 +51,7 @@ public class MimePartMatcherTests
|
|||||||
|
|
||||||
// Act
|
// Act
|
||||||
var contentTypeMatcher = new ContentTypeMatcher("text/plain");
|
var contentTypeMatcher = new ContentTypeMatcher("text/plain");
|
||||||
var contentMatcher = new ExactMatcher("This is some plain text");
|
var contentMatcher = new ExactMatcher("This is some plain text.");
|
||||||
|
|
||||||
var matcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, contentTypeMatcher, null, null, contentMatcher);
|
var matcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, contentTypeMatcher, null, null, contentMatcher);
|
||||||
var result = matcher.IsMatch(part);
|
var result = matcher.IsMatch(part);
|
||||||
@@ -70,8 +69,8 @@ public class MimePartMatcherTests
|
|||||||
var part = message.BodyParts[1];
|
var part = message.BodyParts[1];
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var contentTypeMatcher = new ContentTypeMatcher("text/json");
|
var contentTypeMatcher = new ContentTypeMatcher("application/json");
|
||||||
var contentMatcher = new JsonMatcher(new { Key = "Value" }, true);
|
var contentMatcher = new JsonPartialMatcher(new { id = 42 }, true);
|
||||||
|
|
||||||
var matcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, contentTypeMatcher, null, null, contentMatcher);
|
var matcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, contentTypeMatcher, null, null, contentMatcher);
|
||||||
var result = matcher.IsMatch(part);
|
var result = matcher.IsMatch(part);
|
||||||
@@ -89,9 +88,9 @@ public class MimePartMatcherTests
|
|||||||
|
|
||||||
// Act
|
// Act
|
||||||
var contentTypeMatcher = new ContentTypeMatcher("image/png");
|
var contentTypeMatcher = new ContentTypeMatcher("image/png");
|
||||||
var contentDispositionMatcher = new ExactMatcher("attachment; filename=\"image.png\"");
|
var contentDispositionMatcher = new WildcardMatcher("*filename=\"example.png\"");
|
||||||
var contentTransferEncodingMatcher = new ExactMatcher("base64");
|
var contentTransferEncodingMatcher = new ExactMatcher("base64");
|
||||||
var contentMatcher = new ExactObjectMatcher(Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAIAAAACAgMAAAAP2OW3AAAADFBMVEX/tID/vpH/pWX/sHidUyjlAAAADElEQVR4XmMQYNgAAADkAMHebX3mAAAAAElFTkSuQmCC"));
|
var contentMatcher = new ExactObjectMatcher(Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4\r\n//8wEzIABCMDgAEMwEAAAwAA//8DAKkCBf4AAAAASUVORK5CYII="));
|
||||||
|
|
||||||
var matcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, contentTypeMatcher, contentDispositionMatcher, contentTransferEncodingMatcher, contentMatcher);
|
var matcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, contentTypeMatcher, contentDispositionMatcher, contentTransferEncodingMatcher, contentMatcher);
|
||||||
var result = matcher.IsMatch(part);
|
var result = matcher.IsMatch(part);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class RequestMessageBodyMatcherTests
|
|||||||
DetectedBodyType = BodyType.String
|
DetectedBodyType = BodyType.String
|
||||||
};
|
};
|
||||||
var stringMatcherMock = new Mock<IStringMatcher>();
|
var stringMatcherMock = new Mock<IStringMatcher>();
|
||||||
stringMatcherMock.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(1d);
|
stringMatcherMock.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(MatchResult.From(nameof(IStringMatcher), 1d));
|
||||||
|
|
||||||
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body);
|
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body);
|
||||||
|
|
||||||
@@ -61,10 +61,10 @@ public class RequestMessageBodyMatcherTests
|
|||||||
DetectedBodyType = BodyType.String
|
DetectedBodyType = BodyType.String
|
||||||
};
|
};
|
||||||
var stringMatcherMock1 = new Mock<IStringMatcher>();
|
var stringMatcherMock1 = new Mock<IStringMatcher>();
|
||||||
stringMatcherMock1.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(one);
|
stringMatcherMock1.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(MatchResult.From(nameof(IStringMatcher), one));
|
||||||
|
|
||||||
var stringMatcherMock2 = new Mock<IStringMatcher>();
|
var stringMatcherMock2 = new Mock<IStringMatcher>();
|
||||||
stringMatcherMock2.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(two);
|
stringMatcherMock2.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(MatchResult.From(nameof(IStringMatcher), two));
|
||||||
|
|
||||||
var matchers = new[] { stringMatcherMock1.Object, stringMatcherMock2.Object };
|
var matchers = new[] { stringMatcherMock1.Object, stringMatcherMock2.Object };
|
||||||
|
|
||||||
@@ -102,10 +102,10 @@ public class RequestMessageBodyMatcherTests
|
|||||||
DetectedBodyType = BodyType.String
|
DetectedBodyType = BodyType.String
|
||||||
};
|
};
|
||||||
var stringMatcherMock1 = new Mock<IStringMatcher>();
|
var stringMatcherMock1 = new Mock<IStringMatcher>();
|
||||||
stringMatcherMock1.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(one);
|
stringMatcherMock1.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(MatchResult.From(nameof(IStringMatcher), one));
|
||||||
|
|
||||||
var stringMatcherMock2 = new Mock<IStringMatcher>();
|
var stringMatcherMock2 = new Mock<IStringMatcher>();
|
||||||
stringMatcherMock2.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(two);
|
stringMatcherMock2.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(MatchResult.From(nameof(IStringMatcher), two));
|
||||||
|
|
||||||
var matchers = new[] { stringMatcherMock1.Object, stringMatcherMock2.Object };
|
var matchers = new[] { stringMatcherMock1.Object, stringMatcherMock2.Object };
|
||||||
|
|
||||||
@@ -143,10 +143,10 @@ public class RequestMessageBodyMatcherTests
|
|||||||
DetectedBodyType = BodyType.String
|
DetectedBodyType = BodyType.String
|
||||||
};
|
};
|
||||||
var stringMatcherMock1 = new Mock<IStringMatcher>();
|
var stringMatcherMock1 = new Mock<IStringMatcher>();
|
||||||
stringMatcherMock1.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(one);
|
stringMatcherMock1.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(MatchResult.From(nameof(IStringMatcher), one));
|
||||||
|
|
||||||
var stringMatcherMock2 = new Mock<IStringMatcher>();
|
var stringMatcherMock2 = new Mock<IStringMatcher>();
|
||||||
stringMatcherMock2.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(two);
|
stringMatcherMock2.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(MatchResult.From(nameof(IStringMatcher), two));
|
||||||
|
|
||||||
var matchers = new[] { stringMatcherMock1.Object, stringMatcherMock2.Object };
|
var matchers = new[] { stringMatcherMock1.Object, stringMatcherMock2.Object };
|
||||||
|
|
||||||
@@ -175,11 +175,11 @@ public class RequestMessageBodyMatcherTests
|
|||||||
// Assign
|
// Assign
|
||||||
var body = new BodyData
|
var body = new BodyData
|
||||||
{
|
{
|
||||||
BodyAsBytes = new byte[] { 1 },
|
BodyAsBytes = [1],
|
||||||
DetectedBodyType = BodyType.Bytes
|
DetectedBodyType = BodyType.Bytes
|
||||||
};
|
};
|
||||||
var stringMatcherMock = new Mock<IStringMatcher>();
|
var stringMatcherMock = new Mock<IStringMatcher>();
|
||||||
stringMatcherMock.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(0.5d);
|
stringMatcherMock.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(MatchResult.From(nameof(IStringMatcher), 0.5d));
|
||||||
|
|
||||||
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body);
|
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body);
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ public class RequestMessageBodyMatcherTests
|
|||||||
DetectedBodyType = BodyType.Json
|
DetectedBodyType = BodyType.Json
|
||||||
};
|
};
|
||||||
var stringMatcherMock = new Mock<IStringMatcher>();
|
var stringMatcherMock = new Mock<IStringMatcher>();
|
||||||
stringMatcherMock.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(1.0d);
|
stringMatcherMock.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(MatchResult.From(nameof(IStringMatcher), 1.0d));
|
||||||
|
|
||||||
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body);
|
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body);
|
||||||
|
|
||||||
@@ -235,7 +235,7 @@ public class RequestMessageBodyMatcherTests
|
|||||||
DetectedBodyType = BodyType.Json
|
DetectedBodyType = BodyType.Json
|
||||||
};
|
};
|
||||||
var stringMatcherMock = new Mock<IStringMatcher>();
|
var stringMatcherMock = new Mock<IStringMatcher>();
|
||||||
stringMatcherMock.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(1d);
|
stringMatcherMock.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(MatchResult.From(nameof(IStringMatcher), 1d));
|
||||||
stringMatcherMock.SetupGet(m => m.MatchOperator).Returns(MatchOperator.Or);
|
stringMatcherMock.SetupGet(m => m.MatchOperator).Returns(MatchOperator.Or);
|
||||||
|
|
||||||
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body);
|
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body);
|
||||||
@@ -263,7 +263,7 @@ public class RequestMessageBodyMatcherTests
|
|||||||
DetectedBodyType = BodyType.Json
|
DetectedBodyType = BodyType.Json
|
||||||
};
|
};
|
||||||
var objectMatcherMock = new Mock<IObjectMatcher>();
|
var objectMatcherMock = new Mock<IObjectMatcher>();
|
||||||
objectMatcherMock.Setup(m => m.IsMatch(It.IsAny<object>())).Returns(1d);
|
objectMatcherMock.Setup(m => m.IsMatch(It.IsAny<object>())).Returns(MatchResult.From(nameof(IStringMatcher), 1d));
|
||||||
|
|
||||||
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body);
|
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body);
|
||||||
|
|
||||||
@@ -387,7 +387,7 @@ public class RequestMessageBodyMatcherTests
|
|||||||
DetectedBodyType = BodyType.Bytes
|
DetectedBodyType = BodyType.Bytes
|
||||||
};
|
};
|
||||||
var objectMatcherMock = new Mock<IObjectMatcher>();
|
var objectMatcherMock = new Mock<IObjectMatcher>();
|
||||||
objectMatcherMock.Setup(m => m.IsMatch(It.IsAny<object>())).Returns(1.0d);
|
objectMatcherMock.Setup(m => m.IsMatch(It.IsAny<object>())).Returns(MatchResult.From(nameof(IStringMatcher), 1.0d));
|
||||||
|
|
||||||
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body);
|
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body);
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public class RequestMessageGraphQLMatcherTests
|
|||||||
DetectedBodyType = BodyType.String
|
DetectedBodyType = BodyType.String
|
||||||
};
|
};
|
||||||
var stringMatcherMock = new Mock<IStringMatcher>();
|
var stringMatcherMock = new Mock<IStringMatcher>();
|
||||||
stringMatcherMock.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(1d);
|
stringMatcherMock.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(MatchResult.From(nameof(IStringMatcher), 1d));
|
||||||
|
|
||||||
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body);
|
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body);
|
||||||
|
|
||||||
@@ -57,10 +57,10 @@ public class RequestMessageGraphQLMatcherTests
|
|||||||
DetectedBodyType = BodyType.String
|
DetectedBodyType = BodyType.String
|
||||||
};
|
};
|
||||||
var stringMatcherMock1 = new Mock<IStringMatcher>();
|
var stringMatcherMock1 = new Mock<IStringMatcher>();
|
||||||
stringMatcherMock1.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(one);
|
stringMatcherMock1.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(MatchResult.From(nameof(IStringMatcher), one));
|
||||||
|
|
||||||
var stringMatcherMock2 = new Mock<IStringMatcher>();
|
var stringMatcherMock2 = new Mock<IStringMatcher>();
|
||||||
stringMatcherMock2.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(two);
|
stringMatcherMock2.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(MatchResult.From(nameof(IStringMatcher), two));
|
||||||
|
|
||||||
var matchers = new[] { stringMatcherMock1.Object, stringMatcherMock2.Object };
|
var matchers = new[] { stringMatcherMock1.Object, stringMatcherMock2.Object };
|
||||||
|
|
||||||
@@ -98,10 +98,10 @@ public class RequestMessageGraphQLMatcherTests
|
|||||||
DetectedBodyType = BodyType.String
|
DetectedBodyType = BodyType.String
|
||||||
};
|
};
|
||||||
var stringMatcherMock1 = new Mock<IStringMatcher>();
|
var stringMatcherMock1 = new Mock<IStringMatcher>();
|
||||||
stringMatcherMock1.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(one);
|
stringMatcherMock1.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(MatchResult.From(nameof(IStringMatcher), one));
|
||||||
|
|
||||||
var stringMatcherMock2 = new Mock<IStringMatcher>();
|
var stringMatcherMock2 = new Mock<IStringMatcher>();
|
||||||
stringMatcherMock2.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(two);
|
stringMatcherMock2.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(MatchResult.From(nameof(IStringMatcher), two));
|
||||||
|
|
||||||
var matchers = new[] { stringMatcherMock1.Object, stringMatcherMock2.Object };
|
var matchers = new[] { stringMatcherMock1.Object, stringMatcherMock2.Object };
|
||||||
|
|
||||||
@@ -139,10 +139,10 @@ public class RequestMessageGraphQLMatcherTests
|
|||||||
DetectedBodyType = BodyType.String
|
DetectedBodyType = BodyType.String
|
||||||
};
|
};
|
||||||
var stringMatcherMock1 = new Mock<IStringMatcher>();
|
var stringMatcherMock1 = new Mock<IStringMatcher>();
|
||||||
stringMatcherMock1.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(one);
|
stringMatcherMock1.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(MatchResult.From(nameof(IStringMatcher), one));
|
||||||
|
|
||||||
var stringMatcherMock2 = new Mock<IStringMatcher>();
|
var stringMatcherMock2 = new Mock<IStringMatcher>();
|
||||||
stringMatcherMock2.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(two);
|
stringMatcherMock2.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(MatchResult.From(nameof(IStringMatcher), two));
|
||||||
|
|
||||||
var matchers = new[] { stringMatcherMock1.Object, stringMatcherMock2.Object };
|
var matchers = new[] { stringMatcherMock1.Object, stringMatcherMock2.Object };
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ public class RequestMessageGraphQLMatcherTests
|
|||||||
DetectedBodyType = BodyType.Bytes
|
DetectedBodyType = BodyType.Bytes
|
||||||
};
|
};
|
||||||
var stringMatcherMock = new Mock<IStringMatcher>();
|
var stringMatcherMock = new Mock<IStringMatcher>();
|
||||||
stringMatcherMock.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(0.5d);
|
stringMatcherMock.Setup(m => m.IsMatch(It.IsAny<string>())).Returns(MatchResult.From(nameof(IStringMatcher), 0.5d));
|
||||||
|
|
||||||
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body);
|
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body);
|
||||||
|
|
||||||
|
|||||||
@@ -153,6 +153,44 @@ AAAADElEQVR4XmMQYNgAAADkAMHebX3mAAAAAElFTkSuQmCC
|
|||||||
score.Should().Be(MatchScores.Perfect);
|
score.Should().Be(MatchScores.Perfect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RequestMessageBodyMatcher_GetMatchingScore_BodyAsMultiPart_Issue1371()
|
||||||
|
{
|
||||||
|
var body = new BodyData
|
||||||
|
{
|
||||||
|
BodyAsString =
|
||||||
|
"--------------------------woli8b80pw4vBJtNpAMOKS\r\nContent-Disposition: form-data; name=\"metadata\"\r\nContent-Type: application/json\r\n\r\n{\"ID\": \"9858013b-e020-4ef9-b8a8-0bebc740e6a7\", \"DATE\": \"2025-08-15T13:45:30.0000000Z\", \"NAME\": \"32c9a8dd-e214-4afb-9611-9cde81f827c6\", \"NUMBER\": 10}\r\n--------------------------woli8b80pw4vBJtNpAMOKS--\r\n",
|
||||||
|
DetectedBodyType = BodyType.MultiPart
|
||||||
|
};
|
||||||
|
|
||||||
|
var headers = new Dictionary<string, string[]>
|
||||||
|
{
|
||||||
|
{ "Content-Type", [@"multipart/form-data; boundary=------------------------woli8b80pw4vBJtNpAMOKS"] }
|
||||||
|
};
|
||||||
|
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body, headers);
|
||||||
|
|
||||||
|
var bodyMatcher = new MimePartMatcher(
|
||||||
|
MatchBehaviour.AcceptOnMatch,
|
||||||
|
new ContentTypeMatcher("application/json"),
|
||||||
|
null, // Content-Disposition
|
||||||
|
null, // Content-Transfer-Encoding
|
||||||
|
new JsonPartialMatcher(new { id = "9858013b-e020-4ef9-b8a8-0bebc740e6a7" }, true)
|
||||||
|
);
|
||||||
|
|
||||||
|
var matchers = new[] { bodyMatcher }
|
||||||
|
.OfType<IMatcher>()
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
var matcher = new RequestMessageMultiPartMatcher(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, matchers!);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = new RequestMatchResult();
|
||||||
|
var score = matcher.GetMatchingScore(requestMessage, result);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
score.Should().Be(MatchScores.Perfect);
|
||||||
|
}
|
||||||
|
|
||||||
private static double GetScore(IMatcher? matcher1, IMatcher? matcher2, IMatcher? matcher3, MatchOperator matchOperator = MatchOperator.And)
|
private static double GetScore(IMatcher? matcher1, IMatcher? matcher2, IMatcher? matcher3, MatchOperator matchOperator = MatchOperator.And)
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public class CustomPathParamMatcher : IStringMatcher
|
|||||||
var inputParts = GetPathParts(input);
|
var inputParts = GetPathParts(input);
|
||||||
if (inputParts.Length != _pathParts.Length)
|
if (inputParts.Length != _pathParts.Length)
|
||||||
{
|
{
|
||||||
return MatchScores.Mismatch;
|
return MatchResult.From(Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -60,29 +60,29 @@ public class CustomPathParamMatcher : IStringMatcher
|
|||||||
var pathParamName = pathPart.Trim('{').Trim('}');
|
var pathParamName = pathPart.Trim('{').Trim('}');
|
||||||
if (!_pathParams.ContainsKey(pathParamName))
|
if (!_pathParams.ContainsKey(pathParamName))
|
||||||
{
|
{
|
||||||
return MatchScores.Mismatch;
|
return MatchResult.From(Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Regex.IsMatch(inputPart, _pathParams[pathParamName], RegexOptions.IgnoreCase))
|
if (!Regex.IsMatch(inputPart, _pathParams[pathParamName], RegexOptions.IgnoreCase))
|
||||||
{
|
{
|
||||||
return MatchScores.Mismatch;
|
return MatchResult.From(Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!inputPart.Equals(pathPart, StringComparison.InvariantCultureIgnoreCase))
|
if (!inputPart.Equals(pathPart, StringComparison.InvariantCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
return MatchScores.Mismatch;
|
return MatchResult.From(Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
return MatchScores.Mismatch;
|
return MatchResult.From(Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MatchScores.Perfect;
|
return MatchResult.From(Name, MatchScores.Perfect);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AnyOf<string, StringPattern>[] GetPatterns()
|
public AnyOf<string, StringPattern>[] GetPatterns()
|
||||||
|
|||||||
Reference in New Issue
Block a user