Fix MappingMatcher in case of an exception in LinqMatcher. (#322)

* Fix MappingMatcher in case of an exception in LinqMatcher.

* update unit-tests
This commit is contained in:
Stef Heyenrath
2019-08-17 16:24:14 +00:00
committed by GitHub
parent 0a5c9880bd
commit 94f179ba17
8 changed files with 118 additions and 52 deletions

View File

@@ -56,11 +56,23 @@ namespace WireMock.Matchers
/// <inheritdoc cref="IStringMatcher.IsMatch"/> /// <inheritdoc cref="IStringMatcher.IsMatch"/>
public double IsMatch(string input) public double IsMatch(string input)
{ {
double match = MatchScores.Mismatch;
// Convert a single input string to a Queryable string-list with 1 entry. // Convert a single input string to a Queryable string-list with 1 entry.
IQueryable queryable = new[] { input }.AsQueryable(); IQueryable queryable = new[] { input }.AsQueryable();
// Use the Any(...) method to check if the result matches try
double match = MatchScores.ToScore(_patterns.Select(pattern => queryable.Any(pattern))); {
// Use the Any(...) method to check if the result matches
match = MatchScores.ToScore(_patterns.Select(pattern => queryable.Any(pattern)));
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
}
catch
{
// just ignore exception
// TODO add logging?
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match); return MatchBehaviourHelper.Convert(MatchBehaviour, match);
} }
@@ -68,6 +80,8 @@ namespace WireMock.Matchers
/// <inheritdoc cref="IObjectMatcher.IsMatch"/> /// <inheritdoc cref="IObjectMatcher.IsMatch"/>
public double IsMatch(object input) public double IsMatch(object input)
{ {
double match = MatchScores.Mismatch;
JObject value; JObject value;
switch (input) switch (input)
{ {
@@ -83,16 +97,27 @@ namespace WireMock.Matchers
// Convert a single object to a Queryable JObject-list with 1 entry. // Convert a single object to a Queryable JObject-list with 1 entry.
var queryable1 = new[] { value }.AsQueryable(); var queryable1 = new[] { value }.AsQueryable();
// Generate the DynamicLinq select statement. try
string dynamicSelect = JsonUtils.GenerateDynamicLinqStatement(value); {
// Generate the DynamicLinq select statement.
string dynamicSelect = JsonUtils.GenerateDynamicLinqStatement(value);
// Execute DynamicLinq Select statement. // Execute DynamicLinq Select statement.
var queryable2 = queryable1.Select(dynamicSelect); var queryable2 = queryable1.Select(dynamicSelect);
// Use the Any(...) method to check if the result matches. // Use the Any(...) method to check if the result matches.
double match = MatchScores.ToScore(_patterns.Select(pattern => queryable2.Any(pattern))); match = MatchScores.ToScore(_patterns.Select(pattern => queryable2.Any(pattern)));
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
}
catch
{
// just ignore exception
// TODO add logging?
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match); return MatchBehaviourHelper.Convert(MatchBehaviour, match);
} }
/// <inheritdoc cref="IStringMatcher.GetPatterns"/> /// <inheritdoc cref="IStringMatcher.GetPatterns"/>

View File

@@ -59,6 +59,7 @@ namespace WireMock.Matchers
catch (Exception) catch (Exception)
{ {
// just ignore exception // just ignore exception
// TODO add logging?
} }
} }

View File

@@ -1,9 +1,7 @@
using WireMock.Matchers.Request; namespace WireMock.Owin
namespace WireMock.Owin
{ {
internal interface IMappingMatcher internal interface IMappingMatcher
{ {
(IMapping Mapping, RequestMatchResult RequestMatchResult) Match(RequestMessage request); MappingMatcherResult FindBestMatch(RequestMessage request);
} }
} }

View File

@@ -1,5 +1,6 @@
using System.Linq; using System;
using WireMock.Matchers.Request; using System.Collections.Generic;
using System.Linq;
using WireMock.Validation; using WireMock.Validation;
namespace WireMock.Owin namespace WireMock.Owin
@@ -15,34 +16,41 @@ namespace WireMock.Owin
_options = options; _options = options;
} }
public (IMapping Mapping, RequestMatchResult RequestMatchResult) Match(RequestMessage request) public MappingMatcherResult FindBestMatch(RequestMessage request)
{ {
var mappings = _options.Mappings.Values var mappings = new List<MappingMatcherResult>();
.Select(m => new foreach (var mapping in _options.Mappings.Values)
{
try
{ {
Mapping = m, string scenario = mapping.Scenario != null && _options.Scenarios.ContainsKey(mapping.Scenario) ? _options.Scenarios[mapping.Scenario].NextState : null;
MatchResult = m.GetRequestMatchResult(request, m.Scenario != null && _options.Scenarios.ContainsKey(m.Scenario) ? _options.Scenarios[m.Scenario].NextState : null)
}) mappings.Add(new MappingMatcherResult
.ToList(); {
Mapping = mapping,
RequestMatchResult = mapping.GetRequestMatchResult(request, scenario)
});
}
catch (Exception ex)
{
_options.Logger.Error($"Getting a Request MatchResult for Mapping '{mapping.Guid}' failed. This mapping will not be evaluated. Exception: {ex}");
}
}
if (_options.AllowPartialMapping) if (_options.AllowPartialMapping)
{ {
var partialMappings = mappings var partialMappings = mappings
.Where(pm => (pm.Mapping.IsAdminInterface && pm.MatchResult.IsPerfectMatch) || !pm.Mapping.IsAdminInterface) .Where(pm => (pm.Mapping.IsAdminInterface && pm.RequestMatchResult.IsPerfectMatch) || !pm.Mapping.IsAdminInterface)
.OrderBy(m => m.MatchResult) .OrderBy(m => m.RequestMatchResult)
.ThenBy(m => m.Mapping.Priority) .ThenBy(m => m.Mapping.Priority)
.ToList(); .ToList();
var bestPartialMatch = partialMappings.FirstOrDefault(pm => pm.MatchResult.AverageTotalScore > 0.0); return partialMappings.FirstOrDefault(pm => pm.RequestMatchResult.AverageTotalScore > 0.0);
return (bestPartialMatch?.Mapping, bestPartialMatch?.MatchResult);
} }
var perfectMatch = mappings return mappings
.OrderBy(m => m.Mapping.Priority) .OrderBy(m => m.Mapping.Priority)
.FirstOrDefault(m => m.MatchResult.IsPerfectMatch); .FirstOrDefault(m => m.RequestMatchResult.IsPerfectMatch);
return (perfectMatch?.Mapping, perfectMatch?.MatchResult);
} }
} }
} }

View File

@@ -0,0 +1,11 @@
using WireMock.Matchers.Request;
namespace WireMock.Owin
{
internal class MappingMatcherResult
{
public IMapping Mapping { get; set; }
public RequestMatchResult RequestMatchResult { get; set; }
}
}

View File

@@ -1,7 +1,6 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using WireMock.Logging; using WireMock.Logging;
using WireMock.Matchers.Request;
using System.Linq; using System.Linq;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Util; using WireMock.Util;
@@ -74,7 +73,7 @@ namespace WireMock.Owin
bool logRequest = false; bool logRequest = false;
ResponseMessage response = null; ResponseMessage response = null;
(IMapping TargetMapping, RequestMatchResult RequestMatchResult) result = (null, null); MappingMatcherResult result = null;
try try
{ {
foreach (var mapping in _options.Mappings.Values.Where(m => m?.Scenario != null)) foreach (var mapping in _options.Mappings.Values.Where(m => m?.Scenario != null))
@@ -89,9 +88,9 @@ namespace WireMock.Owin
} }
} }
result = _mappingMatcher.Match(request); result = _mappingMatcher.FindBestMatch(request);
var targetMapping = result.TargetMapping;
var targetMapping = result?.Mapping;
if (targetMapping == null) if (targetMapping == null)
{ {
logRequest = true; logRequest = true;
@@ -129,7 +128,7 @@ namespace WireMock.Owin
} }
catch (Exception ex) catch (Exception ex)
{ {
_options.Logger.Error($"Providing a Response for Mapping '{result.TargetMapping.Guid}' failed. HttpStatusCode set to 500. Exception: {ex}"); _options.Logger.Error($"Providing a Response for Mapping '{result?.Mapping?.Guid}' failed. HttpStatusCode set to 500. Exception: {ex}");
response = ResponseMessageBuilder.Create(JsonConvert.SerializeObject(ex), 500); response = ResponseMessageBuilder.Create(JsonConvert.SerializeObject(ex), 500);
} }
finally finally
@@ -139,9 +138,9 @@ namespace WireMock.Owin
Guid = Guid.NewGuid(), Guid = Guid.NewGuid(),
RequestMessage = request, RequestMessage = request,
ResponseMessage = response, ResponseMessage = response,
MappingGuid = result.TargetMapping?.Guid, MappingGuid = result?.Mapping?.Guid,
MappingTitle = result.TargetMapping?.Title, MappingTitle = result?.Mapping?.Title,
RequestMatchResult = result.RequestMatchResult RequestMatchResult = result?.RequestMatchResult
}; };
LogRequest(log, logRequest); LogRequest(log, logRequest);

View File

@@ -14,7 +14,7 @@ namespace WireMock.Net.Tests.Owin
public class MappingMatcherTests public class MappingMatcherTests
{ {
private readonly Mock<IWireMockMiddlewareOptions> _optionsMock; private readonly Mock<IWireMockMiddlewareOptions> _optionsMock;
private readonly IMappingMatcher _sut; private readonly MappingMatcher _sut;
public MappingMatcherTests() public MappingMatcherTests()
{ {
@@ -24,25 +24,50 @@ namespace WireMock.Net.Tests.Owin
_optionsMock.Setup(o => o.LogEntries).Returns(new ConcurrentObservableCollection<LogEntry>()); _optionsMock.Setup(o => o.LogEntries).Returns(new ConcurrentObservableCollection<LogEntry>());
_optionsMock.Setup(o => o.Scenarios).Returns(new ConcurrentDictionary<string, ScenarioState>()); _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); _sut = new MappingMatcher(_optionsMock.Object);
} }
[Fact] [Fact]
public void MappingMatcher_Match_NoMappingsDefined() public void MappingMatcher_FindBestMatch_WhenNoMappingsDefined_ShouldReturnNull()
{ {
// Assign // Assign
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1"); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
// Act // Act
var result = _sut.Match(request); var result = _sut.FindBestMatch(request);
// Assert and Verify // Assert and Verify
Check.That(result.Mapping).IsNull(); Check.That(result).IsNull();
Check.That(result.RequestMatchResult).IsNull();
} }
[Fact] [Fact]
public void MappingMatcher_Match_GetBestMapping_Exact() 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 // Assign
var mappings = InitMappings(new[] { (Guid.Parse("00000000-0000-0000-0000-000000000001"), 0.1), (Guid.Parse("00000000-0000-0000-0000-000000000002"), 1.0) }); var mappings = InitMappings(new[] { (Guid.Parse("00000000-0000-0000-0000-000000000001"), 0.1), (Guid.Parse("00000000-0000-0000-0000-000000000002"), 1.0) });
@@ -51,7 +76,7 @@ namespace WireMock.Net.Tests.Owin
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1"); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
// Act // Act
var result = _sut.Match(request); var result = _sut.FindBestMatch(request);
// Assert and Verify // Assert and Verify
Check.That(result.Mapping.Guid).IsEqualTo(Guid.Parse("00000000-0000-0000-0000-000000000002")); Check.That(result.Mapping.Guid).IsEqualTo(Guid.Parse("00000000-0000-0000-0000-000000000002"));
@@ -59,7 +84,7 @@ namespace WireMock.Net.Tests.Owin
} }
[Fact] [Fact]
public void MappingMatcher_Match_GetBestMapping_AllowPartialMapping() public void MappingMatcher_FindBestMatch_WhenAllowPartialMappingIsTrue_ShouldReturnAnyMatch()
{ {
// Assign // Assign
_optionsMock.SetupGet(o => o.AllowPartialMapping).Returns(true); _optionsMock.SetupGet(o => o.AllowPartialMapping).Returns(true);
@@ -69,7 +94,7 @@ namespace WireMock.Net.Tests.Owin
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1"); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
// Act // Act
var result = _sut.Match(request); var result = _sut.FindBestMatch(request);
// Assert and Verify // Assert and Verify
Check.That(result.Mapping.Guid).IsEqualTo(Guid.Parse("00000000-0000-0000-0000-000000000002")); Check.That(result.Mapping.Guid).IsEqualTo(Guid.Parse("00000000-0000-0000-0000-000000000002"));

View File

@@ -11,7 +11,6 @@ using WireMock.Owin.Mappers;
using WireMock.Util; using WireMock.Util;
using WireMock.Admin.Requests; using WireMock.Admin.Requests;
using WireMock.Logging; using WireMock.Logging;
using WireMock.Matchers.Request;
using WireMock.Matchers; using WireMock.Matchers;
using System.Collections.Generic; using System.Collections.Generic;
#if NET452 #if NET452
@@ -61,7 +60,7 @@ namespace WireMock.Net.Tests.Owin
_matcherMock = new Mock<IMappingMatcher>(); _matcherMock = new Mock<IMappingMatcher>();
_matcherMock.SetupAllProperties(); _matcherMock.SetupAllProperties();
_matcherMock.Setup(m => m.Match(It.IsAny<RequestMessage>())).Returns(((IMapping)null, (RequestMatchResult)null)); _matcherMock.Setup(m => m.FindBestMatch(It.IsAny<RequestMessage>())).Returns(new MappingMatcherResult());
_contextMock = new Mock<IContext>(); _contextMock = new Mock<IContext>();
@@ -92,7 +91,7 @@ namespace WireMock.Net.Tests.Owin
_optionsMock.SetupGet(o => o.AuthorizationMatcher).Returns(new ExactMatcher()); _optionsMock.SetupGet(o => o.AuthorizationMatcher).Returns(new ExactMatcher());
_mappingMock.SetupGet(m => m.IsAdminInterface).Returns(true); _mappingMock.SetupGet(m => m.IsAdminInterface).Returns(true);
_matcherMock.Setup(m => m.Match(It.IsAny<RequestMessage>())).Returns((_mappingMock.Object, (RequestMatchResult)null)); _matcherMock.Setup(m => m.FindBestMatch(It.IsAny<RequestMessage>())).Returns(new MappingMatcherResult { Mapping = _mappingMock.Object });
// Act // Act
await _sut.Invoke(_contextMock.Object); await _sut.Invoke(_contextMock.Object);
@@ -113,7 +112,7 @@ namespace WireMock.Net.Tests.Owin
_optionsMock.SetupGet(o => o.AuthorizationMatcher).Returns(new ExactMatcher()); _optionsMock.SetupGet(o => o.AuthorizationMatcher).Returns(new ExactMatcher());
_mappingMock.SetupGet(m => m.IsAdminInterface).Returns(true); _mappingMock.SetupGet(m => m.IsAdminInterface).Returns(true);
_matcherMock.Setup(m => m.Match(It.IsAny<RequestMessage>())).Returns((_mappingMock.Object, (RequestMatchResult)null)); _matcherMock.Setup(m => m.FindBestMatch(It.IsAny<RequestMessage>())).Returns(new MappingMatcherResult { Mapping = _mappingMock.Object });
// Act // Act
await _sut.Invoke(_contextMock.Object); await _sut.Invoke(_contextMock.Object);