mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-01-18 16:17:10 +01:00
Add PartialMatch to logging / logentries (#482)
* . * FluentAssertions * . * .
This commit is contained in:
@@ -69,6 +69,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.OpenApiParser"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.OpenApiParser.ConsoleApp", "examples\WireMock.Net.OpenApiParser.ConsoleApp\WireMock.Net.OpenApiParser.ConsoleApp.csproj", "{5C09FB93-1535-4F92-AF26-21E8A061EE4A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.FluentAssertions", "src\WireMock.Net.FluentAssertions\WireMock.Net.FluentAssertions.csproj", "{2C837E73-5EDD-43AD-B65A-194E4A3AD9FE}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -167,6 +169,10 @@ Global
|
||||
{5C09FB93-1535-4F92-AF26-21E8A061EE4A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5C09FB93-1535-4F92-AF26-21E8A061EE4A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5C09FB93-1535-4F92-AF26-21E8A061EE4A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2C837E73-5EDD-43AD-B65A-194E4A3AD9FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2C837E73-5EDD-43AD-B65A-194E4A3AD9FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2C837E73-5EDD-43AD-B65A-194E4A3AD9FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2C837E73-5EDD-43AD-B65A-194E4A3AD9FE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -195,6 +201,7 @@ Global
|
||||
{02082E34-DEF2-47D0-AF0B-3326FAA908CE} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{D3804228-91F4-4502-9595-39584E5AADAD} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||
{5C09FB93-1535-4F92-AF26-21E8A061EE4A} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{2C837E73-5EDD-43AD-B65A-194E4A3AD9FE} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
|
||||
|
||||
@@ -36,5 +36,20 @@ namespace WireMock.Admin.Requests
|
||||
/// The request match result.
|
||||
/// </summary>
|
||||
public LogRequestMatchModel RequestMatchResult { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The partial mapping unique identifier.
|
||||
/// </summary>
|
||||
public Guid? PartialMappingGuid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The partial mapping unique title.
|
||||
/// </summary>
|
||||
public string PartialMappingTitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The partial request match result.
|
||||
/// </summary>
|
||||
public LogRequestMatchModel PartialRequestMatchResult { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using WireMock.Server;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.FluentAssertions
|
||||
{
|
||||
public class WireMockANumberOfCallsAssertions
|
||||
{
|
||||
private readonly WireMockServer _server;
|
||||
private readonly int _callsCount;
|
||||
|
||||
public WireMockANumberOfCallsAssertions(WireMockServer server, int callsCount)
|
||||
{
|
||||
_server = server;
|
||||
_callsCount = callsCount;
|
||||
}
|
||||
|
||||
public WireMockAssertions Calls()
|
||||
{
|
||||
return new WireMockAssertions(_server, _callsCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using FluentAssertions.Execution;
|
||||
using WireMock.Server;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.FluentAssertions
|
||||
{
|
||||
public class WireMockAssertions
|
||||
{
|
||||
private readonly WireMockServer _instance;
|
||||
|
||||
public WireMockAssertions(WireMockServer instance, int? callsCount)
|
||||
{
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
[CustomAssertion]
|
||||
public AndConstraint<WireMockAssertions> AtAbsoluteUrl(string absoluteUrl, string because = "", params object[] becauseArgs)
|
||||
{
|
||||
Execute.Assertion
|
||||
.BecauseOf(because, becauseArgs)
|
||||
.Given(() => _instance.LogEntries.Select(x => x.RequestMessage).ToList())
|
||||
.ForCondition(requests => requests.Any())
|
||||
.FailWith(
|
||||
"Expected {context:wiremockserver} to have been called at address matching the absolute url {0}{reason}, but no calls were made.",
|
||||
absoluteUrl)
|
||||
.Then
|
||||
.ForCondition(x => x.Any(y => y.AbsoluteUrl == absoluteUrl))
|
||||
.FailWith(
|
||||
"Expected {context:wiremockserver} to have been called at address matching the absolute url {0}{reason}, but didn't find it among the calls to {1}.",
|
||||
_ => absoluteUrl, requests => requests.Select(request => request.AbsoluteUrl));
|
||||
|
||||
return new AndConstraint<WireMockAssertions>(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using FluentAssertions.Primitives;
|
||||
using WireMock.Server;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.FluentAssertions
|
||||
{
|
||||
public class WireMockReceivedAssertions : ReferenceTypeAssertions<WireMockServer, WireMockReceivedAssertions>
|
||||
{
|
||||
public WireMockReceivedAssertions(WireMockServer server)
|
||||
{
|
||||
Subject = server;
|
||||
}
|
||||
|
||||
public WireMockAssertions HaveReceivedACall()
|
||||
{
|
||||
return new WireMockAssertions(Subject, null);
|
||||
}
|
||||
|
||||
public WireMockANumberOfCallsAssertions HaveReceived(int callsCount)
|
||||
{
|
||||
return new WireMockANumberOfCallsAssertions(Subject, callsCount);
|
||||
}
|
||||
|
||||
protected override string Identifier => "wiremockserver";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using WireMock.Server;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.FluentAssertions
|
||||
{
|
||||
public static class WireMockExtensions
|
||||
{
|
||||
public static WireMockReceivedAssertions Should(this WireMockServer instance)
|
||||
{
|
||||
return new WireMockReceivedAssertions(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>1.2.13-preview-01</Version>
|
||||
<Description>FluentAssertions extensions for WireMock.Net</Description>
|
||||
<AssemblyTitle>WireMock.Net.FluentAssertions</AssemblyTitle>
|
||||
<Authors>Mahmoud Ali;Stef Heyenrath</Authors>
|
||||
<TargetFrameworks>netstandard1.3;netstandard2.0;netstandard2.1;net45</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>WireMock.Net.FluentAssertions</AssemblyName>
|
||||
<PackageId>WireMock.Net.FluentAssertions</PackageId>
|
||||
<PackageTags>wiremock;FluentAssertions;UnitTest;Assert;Assertions</PackageTags>
|
||||
<RootNamespace>WireMock.FluentAssertions</RootNamespace>
|
||||
<ProjectGuid>{B6269AAC-170A-4346-8B9A-579DED3D9A95}</ProjectGuid>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
|
||||
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
|
||||
<!--<DelaySign>true</DelaySign>-->
|
||||
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WireMock.Net\WireMock.Net.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,59 +1,83 @@
|
||||
using System;
|
||||
using WireMock.Matchers.Request;
|
||||
|
||||
namespace WireMock.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// LogEntry
|
||||
/// </summary>
|
||||
public class LogEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the unique identifier.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The unique identifier.
|
||||
/// </value>
|
||||
public Guid Guid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the request message.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The request message.
|
||||
/// </value>
|
||||
public RequestMessage RequestMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the response message.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The response message.
|
||||
/// </value>
|
||||
public ResponseMessage ResponseMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the request match result.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The request match result.
|
||||
/// </value>
|
||||
public RequestMatchResult RequestMatchResult { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mapping unique identifier.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The mapping unique identifier.
|
||||
/// </value>
|
||||
public Guid? MappingGuid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mapping unique title.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The mapping unique title.
|
||||
/// </value>
|
||||
public string MappingTitle { get; set; }
|
||||
}
|
||||
using System;
|
||||
using WireMock.Matchers.Request;
|
||||
|
||||
namespace WireMock.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// LogEntry
|
||||
/// </summary>
|
||||
public class LogEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the unique identifier.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The unique identifier.
|
||||
/// </value>
|
||||
public Guid Guid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the request message.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The request message.
|
||||
/// </value>
|
||||
public RequestMessage RequestMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the response message.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The response message.
|
||||
/// </value>
|
||||
public ResponseMessage ResponseMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the request match result.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The request match result.
|
||||
/// </value>
|
||||
public RequestMatchResult RequestMatchResult { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mapping unique identifier.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The mapping unique identifier.
|
||||
/// </value>
|
||||
public Guid? MappingGuid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mapping unique title.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The mapping unique title.
|
||||
/// </value>
|
||||
public string MappingTitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the partial mapping unique identifier.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The mapping unique identifier.
|
||||
/// </value>
|
||||
public Guid? PartialMappingGuid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the partial mapping unique title.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The mapping unique title.
|
||||
/// </value>
|
||||
public string PartialMappingTitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the partial match result.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The request match result.
|
||||
/// </value>
|
||||
public RequestMatchResult PartialMatchResult { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace WireMock.Owin
|
||||
{
|
||||
internal interface IMappingMatcher
|
||||
{
|
||||
MappingMatcherResult FindBestMatch(RequestMessage request);
|
||||
}
|
||||
namespace WireMock.Owin
|
||||
{
|
||||
internal interface IMappingMatcher
|
||||
{
|
||||
(MappingMatcherResult Match, MappingMatcherResult Partial) FindBestMatch(RequestMessage request);
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ namespace WireMock.Owin
|
||||
_options = options;
|
||||
}
|
||||
|
||||
public MappingMatcherResult FindBestMatch(RequestMessage request)
|
||||
public (MappingMatcherResult Match, MappingMatcherResult Partial) FindBestMatch(RequestMessage request)
|
||||
{
|
||||
var mappings = new List<MappingMatcherResult>();
|
||||
foreach (var mapping in _options.Mappings.Values)
|
||||
@@ -37,21 +37,24 @@ namespace WireMock.Owin
|
||||
}
|
||||
}
|
||||
|
||||
var partialMappings = mappings
|
||||
.Where(pm => (pm.Mapping.IsAdminInterface && pm.RequestMatchResult.IsPerfectMatch) || !pm.Mapping.IsAdminInterface)
|
||||
.OrderBy(m => m.RequestMatchResult)
|
||||
.ThenBy(m => m.Mapping.Priority)
|
||||
.ToList();
|
||||
var partialMatch = partialMappings.FirstOrDefault(pm => pm.RequestMatchResult.AverageTotalScore > 0.0);
|
||||
|
||||
if (_options.AllowPartialMapping == true)
|
||||
{
|
||||
var partialMappings = mappings
|
||||
.Where(pm => (pm.Mapping.IsAdminInterface && pm.RequestMatchResult.IsPerfectMatch) || !pm.Mapping.IsAdminInterface)
|
||||
.OrderBy(m => m.RequestMatchResult)
|
||||
.ThenBy(m => m.Mapping.Priority)
|
||||
.ToList();
|
||||
|
||||
return partialMappings.FirstOrDefault(pm => pm.RequestMatchResult.AverageTotalScore > 0.0);
|
||||
return (partialMatch, partialMatch);
|
||||
}
|
||||
|
||||
return mappings
|
||||
var match = mappings
|
||||
.Where(m => m.RequestMatchResult.IsPerfectMatch)
|
||||
.OrderBy(m => m.Mapping.Priority).ThenBy(m => m.RequestMatchResult)
|
||||
.FirstOrDefault();
|
||||
|
||||
return (match, partialMatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Threading.Tasks;
|
||||
using WireMock.Logging;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using WireMock.Matchers;
|
||||
using Newtonsoft.Json;
|
||||
using WireMock.Http;
|
||||
@@ -73,7 +74,7 @@ namespace WireMock.Owin
|
||||
|
||||
bool logRequest = false;
|
||||
ResponseMessage response = null;
|
||||
MappingMatcherResult result = null;
|
||||
(MappingMatcherResult Match, MappingMatcherResult Partial) result = (null, null);
|
||||
try
|
||||
{
|
||||
foreach (var mapping in _options.Mappings.Values.Where(m => m?.Scenario != null))
|
||||
@@ -90,7 +91,7 @@ namespace WireMock.Owin
|
||||
|
||||
result = _mappingMatcher.FindBestMatch(request);
|
||||
|
||||
var targetMapping = result?.Mapping;
|
||||
var targetMapping = result.Match?.Mapping;
|
||||
if (targetMapping == null)
|
||||
{
|
||||
logRequest = true;
|
||||
@@ -128,7 +129,7 @@ namespace WireMock.Owin
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_options.Logger.Error($"Providing a Response for Mapping '{result?.Mapping?.Guid}' failed. HttpStatusCode set to 500. Exception: {ex}");
|
||||
_options.Logger.Error($"Providing a Response for Mapping '{result.Match?.Mapping?.Guid}' failed. HttpStatusCode set to 500. Exception: {ex}");
|
||||
response = ResponseMessageBuilder.Create(ex.Message, 500);
|
||||
}
|
||||
finally
|
||||
@@ -138,9 +139,14 @@ namespace WireMock.Owin
|
||||
Guid = Guid.NewGuid(),
|
||||
RequestMessage = request,
|
||||
ResponseMessage = response,
|
||||
MappingGuid = result?.Mapping?.Guid,
|
||||
MappingTitle = result?.Mapping?.Title,
|
||||
RequestMatchResult = result?.RequestMatchResult
|
||||
|
||||
MappingGuid = result.Match?.Mapping?.Guid,
|
||||
MappingTitle = result.Match?.Mapping?.Title,
|
||||
RequestMatchResult = result.Match?.RequestMatchResult,
|
||||
|
||||
PartialMappingGuid = result.Partial?.Mapping?.Guid,
|
||||
PartialMappingTitle = result.Partial?.Mapping?.Title,
|
||||
PartialMatchResult = result.Partial?.RequestMatchResult
|
||||
};
|
||||
|
||||
LogRequest(log, logRequest);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Admin.Requests;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Types;
|
||||
|
||||
@@ -110,22 +111,37 @@ namespace WireMock.Serialization
|
||||
return new LogEntryModel
|
||||
{
|
||||
Guid = logEntry.Guid,
|
||||
MappingGuid = logEntry.MappingGuid,
|
||||
MappingTitle = logEntry.MappingTitle,
|
||||
Request = logRequestModel,
|
||||
Response = logResponseModel,
|
||||
RequestMatchResult = logEntry.RequestMatchResult != null ? new LogRequestMatchModel
|
||||
|
||||
MappingGuid = logEntry.MappingGuid,
|
||||
MappingTitle = logEntry.MappingTitle,
|
||||
RequestMatchResult = Map(logEntry.RequestMatchResult),
|
||||
|
||||
PartialMappingGuid = logEntry.PartialMappingGuid,
|
||||
PartialMappingTitle = logEntry.PartialMappingTitle,
|
||||
PartialRequestMatchResult = Map(logEntry.PartialMatchResult)
|
||||
};
|
||||
}
|
||||
|
||||
private static LogRequestMatchModel Map(RequestMatchResult matchResult)
|
||||
{
|
||||
if (matchResult == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new LogRequestMatchModel
|
||||
{
|
||||
IsPerfectMatch = matchResult.IsPerfectMatch,
|
||||
TotalScore = matchResult.TotalScore,
|
||||
TotalNumber = matchResult.TotalNumber,
|
||||
AverageTotalScore = matchResult.AverageTotalScore,
|
||||
MatchDetails = matchResult.MatchDetails.Select(md => new
|
||||
{
|
||||
IsPerfectMatch = logEntry.RequestMatchResult.IsPerfectMatch,
|
||||
TotalScore = logEntry.RequestMatchResult.TotalScore,
|
||||
TotalNumber = logEntry.RequestMatchResult.TotalNumber,
|
||||
AverageTotalScore = logEntry.RequestMatchResult.AverageTotalScore,
|
||||
MatchDetails = logEntry.RequestMatchResult.MatchDetails.Select(md => new
|
||||
{
|
||||
Name = md.MatcherType.Name.Replace("RequestMessage", string.Empty),
|
||||
Score = md.Score
|
||||
} as object).ToList()
|
||||
} : null
|
||||
Name = md.MatcherType.Name.Replace("RequestMessage", string.Empty),
|
||||
Score = md.Score
|
||||
} as object).ToList()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,153 +1,191 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using Moq;
|
||||
using NFluent;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Models;
|
||||
using WireMock.Owin;
|
||||
using WireMock.Util;
|
||||
using Xunit;
|
||||
|
||||
namespace WireMock.Net.Tests.Owin
|
||||
{
|
||||
public class MappingMatcherTests
|
||||
{
|
||||
private readonly Mock<IWireMockMiddlewareOptions> _optionsMock;
|
||||
private readonly MappingMatcher _sut;
|
||||
|
||||
public MappingMatcherTests()
|
||||
{
|
||||
_optionsMock = new Mock<IWireMockMiddlewareOptions>();
|
||||
_optionsMock.SetupAllProperties();
|
||||
_optionsMock.Setup(o => o.Mappings).Returns(new ConcurrentDictionary<Guid, IMapping>());
|
||||
_optionsMock.Setup(o => o.LogEntries).Returns(new ConcurrentObservableCollection<LogEntry>());
|
||||
_optionsMock.Setup(o => o.Scenarios).Returns(new ConcurrentDictionary<string, ScenarioState>());
|
||||
|
||||
var loggerMock = new Mock<IWireMockLogger>();
|
||||
loggerMock.SetupAllProperties();
|
||||
loggerMock.Setup(l => l.Error(It.IsAny<string>()));
|
||||
_optionsMock.Setup(o => o.Logger).Returns(loggerMock.Object);
|
||||
|
||||
_sut = new MappingMatcher(_optionsMock.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MappingMatcher_FindBestMatch_WhenNoMappingsDefined_ShouldReturnNull()
|
||||
{
|
||||
// Assign
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||
|
||||
// Act
|
||||
var result = _sut.FindBestMatch(request);
|
||||
|
||||
// Assert and Verify
|
||||
Check.That(result).IsNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MappingMatcher_FindBestMatch_WhenMappingThrowsException_ShouldReturnNull()
|
||||
{
|
||||
// Assign
|
||||
var mappingMock = new Mock<IMapping>();
|
||||
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Throws<Exception>();
|
||||
|
||||
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
||||
mappings.TryAdd(Guid.NewGuid(), mappingMock.Object);
|
||||
|
||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||
|
||||
// Act
|
||||
var result = _sut.FindBestMatch(request);
|
||||
|
||||
// Assert and Verify
|
||||
Check.That(result).IsNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MappingMatcher_FindBestMatch_WhenAllowPartialMappingIsFalse_ShouldReturnExactMatch()
|
||||
{
|
||||
// Assign
|
||||
var mappings = InitMappings(
|
||||
(Guid.Parse("00000000-0000-0000-0000-000000000001"), new[] { 0.1 }),
|
||||
(Guid.Parse("00000000-0000-0000-0000-000000000002"), new[] { 1.0 })
|
||||
);
|
||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||
|
||||
// Act
|
||||
var result = _sut.FindBestMatch(request);
|
||||
|
||||
// Assert and Verify
|
||||
Check.That(result.Mapping.Guid).IsEqualTo(Guid.Parse("00000000-0000-0000-0000-000000000002"));
|
||||
Check.That(result.RequestMatchResult.AverageTotalScore).IsEqualTo(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MappingMatcher_FindBestMatch_WhenAllowPartialMappingIsTrue_ShouldReturnAnyMatch()
|
||||
{
|
||||
// Assign
|
||||
_optionsMock.SetupGet(o => o.AllowPartialMapping).Returns(true);
|
||||
var mappings = InitMappings(
|
||||
(Guid.Parse("00000000-0000-0000-0000-000000000001"), new[] { 0.1 }),
|
||||
(Guid.Parse("00000000-0000-0000-0000-000000000002"), new[] { 0.9 })
|
||||
);
|
||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||
|
||||
// Act
|
||||
var result = _sut.FindBestMatch(request);
|
||||
|
||||
// Assert and Verify
|
||||
Check.That(result.Mapping.Guid).IsEqualTo(Guid.Parse("00000000-0000-0000-0000-000000000002"));
|
||||
Check.That(result.RequestMatchResult.AverageTotalScore).IsEqualTo(0.9);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MappingMatcher_FindBestMatch_WhenAllowPartialMappingIsFalse_And_WithSameAverageScoreButMoreMatchers_ReturnsMatchWithMoreMatchers()
|
||||
{
|
||||
// Assign
|
||||
var mappings = InitMappings(
|
||||
(Guid.Parse("00000000-0000-0000-0000-000000000001"), new[] { 1.0 }),
|
||||
(Guid.Parse("00000000-0000-0000-0000-000000000002"), new[] { 1.0, 1.0 })
|
||||
);
|
||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||
|
||||
// Act
|
||||
var result = _sut.FindBestMatch(request);
|
||||
|
||||
// Assert and Verify
|
||||
Check.That(result.Mapping.Guid).IsEqualTo(Guid.Parse("00000000-0000-0000-0000-000000000002"));
|
||||
Check.That(result.RequestMatchResult.AverageTotalScore).IsEqualTo(1.0);
|
||||
}
|
||||
|
||||
private ConcurrentDictionary<Guid, IMapping> InitMappings(params (Guid guid, double[] scores)[] matches)
|
||||
{
|
||||
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
||||
|
||||
foreach (var match in matches)
|
||||
{
|
||||
var mappingMock = new Mock<IMapping>();
|
||||
mappingMock.SetupGet(m => m.Guid).Returns(match.guid);
|
||||
|
||||
var requestMatchResult = new RequestMatchResult();
|
||||
foreach (var score in match.scores)
|
||||
{
|
||||
requestMatchResult.AddScore(typeof(object), score);
|
||||
}
|
||||
|
||||
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Returns(requestMatchResult);
|
||||
|
||||
mappings.TryAdd(match.guid, mappingMock.Object);
|
||||
}
|
||||
|
||||
return mappings;
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Models;
|
||||
using WireMock.Owin;
|
||||
using WireMock.Util;
|
||||
using Xunit;
|
||||
|
||||
namespace WireMock.Net.Tests.Owin
|
||||
{
|
||||
public class MappingMatcherTests
|
||||
{
|
||||
private readonly Mock<IWireMockMiddlewareOptions> _optionsMock;
|
||||
private readonly MappingMatcher _sut;
|
||||
|
||||
public MappingMatcherTests()
|
||||
{
|
||||
_optionsMock = new Mock<IWireMockMiddlewareOptions>();
|
||||
_optionsMock.SetupAllProperties();
|
||||
_optionsMock.Setup(o => o.Mappings).Returns(new ConcurrentDictionary<Guid, IMapping>());
|
||||
_optionsMock.Setup(o => o.LogEntries).Returns(new ConcurrentObservableCollection<LogEntry>());
|
||||
_optionsMock.Setup(o => o.Scenarios).Returns(new ConcurrentDictionary<string, ScenarioState>());
|
||||
|
||||
var loggerMock = new Mock<IWireMockLogger>();
|
||||
loggerMock.SetupAllProperties();
|
||||
loggerMock.Setup(l => l.Error(It.IsAny<string>()));
|
||||
_optionsMock.Setup(o => o.Logger).Returns(loggerMock.Object);
|
||||
|
||||
_sut = new MappingMatcher(_optionsMock.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MappingMatcher_FindBestMatch_WhenNoMappingsDefined_ShouldReturnNull()
|
||||
{
|
||||
// Assign
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||
|
||||
// Act
|
||||
var result = _sut.FindBestMatch(request);
|
||||
|
||||
// Assert
|
||||
result.Match.Should().BeNull();
|
||||
result.Partial.Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MappingMatcher_FindBestMatch_WhenMappingThrowsException_ShouldReturnNull()
|
||||
{
|
||||
// Assign
|
||||
var mappingMock = new Mock<IMapping>();
|
||||
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Throws<Exception>();
|
||||
|
||||
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
||||
mappings.TryAdd(Guid.NewGuid(), mappingMock.Object);
|
||||
|
||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||
|
||||
// Act
|
||||
var result = _sut.FindBestMatch(request);
|
||||
|
||||
// Assert
|
||||
result.Match.Should().BeNull();
|
||||
result.Partial.Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MappingMatcher_FindBestMatch_WhenAllowPartialMappingIsFalse_ShouldReturnExactMatch()
|
||||
{
|
||||
// Assign
|
||||
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||
var mappings = InitMappings(
|
||||
(guid1, new[] { 0.1 }),
|
||||
(guid2, new[] { 1.0 })
|
||||
);
|
||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||
|
||||
// Act
|
||||
var result = _sut.FindBestMatch(request);
|
||||
|
||||
// Assert
|
||||
result.Match.Mapping.Guid.Should().Be(guid2);
|
||||
result.Match.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
||||
result.Partial.Mapping.Guid.Should().Be(guid2);
|
||||
result.Partial.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MappingMatcher_FindBestMatch_WhenAllowPartialMappingIsFalse_AndNoExactmatch_ShouldReturnNullExactMatch_And_PartialMatch()
|
||||
{
|
||||
// Assign
|
||||
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||
var mappings = InitMappings(
|
||||
(guid1, new[] { 0.1 }),
|
||||
(guid2, new[] { 0.9 })
|
||||
);
|
||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||
|
||||
// Act
|
||||
var result = _sut.FindBestMatch(request);
|
||||
|
||||
// Assert
|
||||
result.Match.Should().BeNull();
|
||||
result.Partial.Mapping.Guid.Should().Be(guid2);
|
||||
result.Partial.RequestMatchResult.AverageTotalScore.Should().Be(0.9);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MappingMatcher_FindBestMatch_WhenAllowPartialMappingIsTrue_ShouldReturnAnyMatch()
|
||||
{
|
||||
// Assign
|
||||
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||
|
||||
_optionsMock.SetupGet(o => o.AllowPartialMapping).Returns(true);
|
||||
var mappings = InitMappings(
|
||||
(guid1, new[] { 0.1 }),
|
||||
(guid2, new[] { 0.9 })
|
||||
);
|
||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||
|
||||
// Act
|
||||
var result = _sut.FindBestMatch(request);
|
||||
|
||||
// Assert
|
||||
result.Match.Mapping.Guid.Should().Be(guid2);
|
||||
result.Match.RequestMatchResult.AverageTotalScore.Should().Be(0.9);
|
||||
result.Partial.Mapping.Guid.Should().Be(guid2);
|
||||
result.Partial.RequestMatchResult.AverageTotalScore.Should().Be(0.9);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MappingMatcher_FindBestMatch_WhenAllowPartialMappingIsFalse_And_WithSameAverageScoreButMoreMatchers_ReturnsMatchWithMoreMatchers()
|
||||
{
|
||||
// Assign
|
||||
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||
var mappings = InitMappings(
|
||||
(guid1, new[] { 1.0 }),
|
||||
(guid2, new[] { 1.0, 1.0 })
|
||||
);
|
||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||
|
||||
// Act
|
||||
var result = _sut.FindBestMatch(request);
|
||||
|
||||
// Assert and Verify
|
||||
result.Match.Mapping.Guid.Should().Be(guid2);
|
||||
result.Match.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
||||
result.Partial.Mapping.Guid.Should().Be(guid2);
|
||||
result.Partial.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
||||
}
|
||||
|
||||
private ConcurrentDictionary<Guid, IMapping> InitMappings(params (Guid guid, double[] scores)[] matches)
|
||||
{
|
||||
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
||||
|
||||
foreach (var match in matches)
|
||||
{
|
||||
var mappingMock = new Mock<IMapping>();
|
||||
mappingMock.SetupGet(m => m.Guid).Returns(match.guid);
|
||||
|
||||
var requestMatchResult = new RequestMatchResult();
|
||||
foreach (var score in match.scores)
|
||||
{
|
||||
requestMatchResult.AddScore(typeof(object), score);
|
||||
}
|
||||
|
||||
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Returns(requestMatchResult);
|
||||
|
||||
mappings.TryAdd(match.guid, mappingMock.Object);
|
||||
}
|
||||
|
||||
return mappings;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ namespace WireMock.Net.Tests.Owin
|
||||
|
||||
_matcherMock = new Mock<IMappingMatcher>();
|
||||
_matcherMock.SetupAllProperties();
|
||||
_matcherMock.Setup(m => m.FindBestMatch(It.IsAny<RequestMessage>())).Returns(new MappingMatcherResult());
|
||||
_matcherMock.Setup(m => m.FindBestMatch(It.IsAny<RequestMessage>())).Returns((new MappingMatcherResult(), new MappingMatcherResult()));
|
||||
|
||||
_contextMock = new Mock<IContext>();
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace WireMock.Net.Tests.Owin
|
||||
// Assert and Verify
|
||||
_optionsMock.Verify(o => o.Logger.Warn(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once);
|
||||
|
||||
Expression<Func<ResponseMessage, bool>> match = r => (int) r.StatusCode == 404 && ((StatusModel)r.BodyData.BodyAsJson).Status == "No matching mapping found";
|
||||
Expression<Func<ResponseMessage, bool>> match = r => (int)r.StatusCode == 404 && ((StatusModel)r.BodyData.BodyAsJson).Status == "No matching mapping found";
|
||||
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<IResponse>()), Times.Once);
|
||||
}
|
||||
|
||||
@@ -91,7 +91,9 @@ namespace WireMock.Net.Tests.Owin
|
||||
|
||||
_optionsMock.SetupGet(o => o.AuthorizationMatcher).Returns(new ExactMatcher());
|
||||
_mappingMock.SetupGet(m => m.IsAdminInterface).Returns(true);
|
||||
_matcherMock.Setup(m => m.FindBestMatch(It.IsAny<RequestMessage>())).Returns(new MappingMatcherResult { Mapping = _mappingMock.Object });
|
||||
|
||||
var result = new MappingMatcherResult { Mapping = _mappingMock.Object };
|
||||
_matcherMock.Setup(m => m.FindBestMatch(It.IsAny<RequestMessage>())).Returns((result, result));
|
||||
|
||||
// Act
|
||||
await _sut.Invoke(_contextMock.Object);
|
||||
@@ -99,7 +101,7 @@ namespace WireMock.Net.Tests.Owin
|
||||
// Assert and Verify
|
||||
_optionsMock.Verify(o => o.Logger.Error(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once);
|
||||
|
||||
Expression<Func<ResponseMessage, bool>> match = r => (int) r.StatusCode == 401;
|
||||
Expression<Func<ResponseMessage, bool>> match = r => (int)r.StatusCode == 401;
|
||||
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<IResponse>()), Times.Once);
|
||||
}
|
||||
|
||||
@@ -112,7 +114,9 @@ namespace WireMock.Net.Tests.Owin
|
||||
|
||||
_optionsMock.SetupGet(o => o.AuthorizationMatcher).Returns(new ExactMatcher());
|
||||
_mappingMock.SetupGet(m => m.IsAdminInterface).Returns(true);
|
||||
_matcherMock.Setup(m => m.FindBestMatch(It.IsAny<RequestMessage>())).Returns(new MappingMatcherResult { Mapping = _mappingMock.Object });
|
||||
|
||||
var result = new MappingMatcherResult { Mapping = _mappingMock.Object };
|
||||
_matcherMock.Setup(m => m.FindBestMatch(It.IsAny<RequestMessage>())).Returns((result, result));
|
||||
|
||||
// Act
|
||||
await _sut.Invoke(_contextMock.Object);
|
||||
@@ -120,7 +124,7 @@ namespace WireMock.Net.Tests.Owin
|
||||
// Assert and Verify
|
||||
_optionsMock.Verify(o => o.Logger.Error(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once);
|
||||
|
||||
Expression<Func<ResponseMessage, bool>> match = r => (int) r.StatusCode == 401;
|
||||
Expression<Func<ResponseMessage, bool>> match = r => (int)r.StatusCode == 401;
|
||||
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<IResponse>()), Times.Once);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user