diff --git a/src/WireMock.Net/Matchers/Request/RequestMessageMultiPartMatcher.cs b/src/WireMock.Net/Matchers/Request/RequestMessageMultiPartMatcher.cs index 4695cb71..572e419b 100644 --- a/src/WireMock.Net/Matchers/Request/RequestMessageMultiPartMatcher.cs +++ b/src/WireMock.Net/Matchers/Request/RequestMessageMultiPartMatcher.cs @@ -71,23 +71,21 @@ public class RequestMessageMultiPartMatcher : IRequestMatcher { var mimePartMatchers = Matchers.OfType().ToArray(); - foreach (var mimePart in message.BodyParts.OfType()) + foreach (var mimePartMatcher in Matchers.OfType().ToArray()) { - var matchesForMimePart = new List { default }; - matchesForMimePart.AddRange(mimePartMatchers.Select(matcher => matcher.IsMatch(mimePart))); - - score = matchesForMimePart.Select(m => m.Score).Max(); - - if (MatchScores.IsPerfect(score)) + score = MatchScores.Mismatch; + foreach (var mimeBodyPart in message.BodyParts.OfType()) { - if (MatchOperator == MatchOperator.Or) + var matchResult = mimePartMatcher.IsMatch(mimeBodyPart); + if (matchResult.IsPerfect()) { + score = MatchScores.Perfect; break; } } - else + if ((MatchOperator == MatchOperator.Or && MatchScores.IsPerfect(score)) + || (MatchOperator == MatchOperator.And && !MatchScores.IsPerfect(score))) { - score = MatchScores.Mismatch; break; } } diff --git a/test/WireMock.Net.Tests/RequestMatchers/RequestMessageMultiPartMatcher.cs b/test/WireMock.Net.Tests/RequestMatchers/RequestMessageMultiPartMatcher.cs index 7f1dff0b..dd7f1933 100644 --- a/test/WireMock.Net.Tests/RequestMatchers/RequestMessageMultiPartMatcher.cs +++ b/test/WireMock.Net.Tests/RequestMatchers/RequestMessageMultiPartMatcher.cs @@ -1,6 +1,7 @@ #if MIMEKIT using System; using System.Collections.Generic; +using System.Linq; using FluentAssertions; using WireMock.Matchers; using WireMock.Matchers.Request; @@ -34,8 +35,123 @@ AAAADElEQVR4XmMQYNgAAADkAMHebX3mAAAAAElFTkSuQmCC --=-5XgmpXt0XOfzdtcgNJc2ZQ==-- "; - [Fact] - public void RequestMessageBodyMatcher_GetMatchingScore_BodyAsMultiPart() + private static readonly MimePartMatcher PerfectTextPlainMatcher = new( + MatchBehaviour.AcceptOnMatch, + new ContentTypeMatcher("text/plain"), + null, + null, + new ExactMatcher("This is some plain text") + ); + + private static readonly MimePartMatcher PerfectPartTextMatcher = new( + MatchBehaviour.AcceptOnMatch, + new ContentTypeMatcher("text/json"), + null, + null, + new JsonMatcher(new { Key = "Value" }, true) + ); + + private static readonly MimePartMatcher PerfectImagePngMatcher = new( + MatchBehaviour.AcceptOnMatch, + new ContentTypeMatcher("image/png"), + new ExactMatcher("attachment; filename=\"image.png\""), + new ExactMatcher("base64"), + new ExactObjectMatcher(Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAIAAAACAgMAAAAP2OW3AAAADFBMVEX/tID/vpH/pWX/sHidUyjlAAAADElEQVR4XmMQYNgAAADkAMHebX3mAAAAAElFTkSuQmCC")) + ); + + private static readonly MimePartMatcher MismatchTextPlainMatcher = new( + MatchBehaviour.AcceptOnMatch, + new ContentTypeMatcher("text/plain"), + null, + null, + new ExactMatcher("--INVALID TEXT--") + ); + + private static readonly MimePartMatcher MismatchPartTextMatcher = new( + MatchBehaviour.AcceptOnMatch, + new ContentTypeMatcher("text/json"), + null, + null, + new JsonMatcher(new { Key = "InvalidValue" }, true) + ); + + private static readonly MimePartMatcher MismatchImagePngMatcher = new( + MatchBehaviour.AcceptOnMatch, + new ContentTypeMatcher("image/png"), + new ExactMatcher("attachment; filename=\"invalid.png\""), + new ExactMatcher("base64"), + null + ); + + public static TheoryData PerfectMatchersData => new() + { + { PerfectTextPlainMatcher, null, null }, + { PerfectPartTextMatcher, null, null }, + { PerfectImagePngMatcher, null, null }, + { PerfectPartTextMatcher, PerfectTextPlainMatcher, null }, + { PerfectPartTextMatcher, PerfectImagePngMatcher, PerfectTextPlainMatcher }, + }; + + public static TheoryData MismatchMatchersData => new() + { + { MismatchTextPlainMatcher, null, null }, + { MismatchPartTextMatcher, null, null }, + { MismatchImagePngMatcher, null, null }, + { MismatchPartTextMatcher, MismatchTextPlainMatcher, null }, + { MismatchPartTextMatcher, MismatchImagePngMatcher, MismatchTextPlainMatcher }, + }; + + public static TheoryData MixedMatchersData => new() + { + { MismatchPartTextMatcher, PerfectTextPlainMatcher, null }, + { PerfectPartTextMatcher, MismatchImagePngMatcher, null }, + }; + + [Theory] + [MemberData(nameof(PerfectMatchersData))] + public void RequestMessageBodyMatcher_GetMatchingScore_BodyAsMultiPart_Perfect(IMatcher? matcher1, IMatcher? matcher2, IMatcher? matcher3) + { + // Assign + var score = GetScore(matcher1, matcher2, matcher3); + + // Assert + score.Should().Be(MatchScores.Perfect); + } + + [Theory] + [MemberData(nameof(MismatchMatchersData))] + public void RequestMessageBodyMatcher_GetMatchingScore_BodyAsMultiPart_Mismatch(IMatcher? matcher1, IMatcher? matcher2, IMatcher? matcher3) + { + // Assign + var score = GetScore(matcher1, matcher2, matcher3); + + // Assert + score.Should().Be(MatchScores.Mismatch); + } + + [Theory] + [MemberData(nameof(MixedMatchersData))] + public void RequestMessageBodyMatcher_GetMatchingScore_BodyAsMultiPart_Mixed_With_And(IMatcher? matcher1, IMatcher? matcher2, IMatcher? matcher3) + { + // Assign + var score = GetScore(matcher1, matcher2, matcher3); + + // Assert + score.Should().Be(MatchScores.Mismatch); + } + + [Theory] + [MemberData(nameof(MixedMatchersData))] + public void RequestMessageBodyMatcher_GetMatchingScore_BodyAsMultiPart_Mixed_With_Or(IMatcher? matcher1, IMatcher? matcher2, IMatcher? matcher3) + { + // Assign + var score = GetScore(matcher1, matcher2, matcher3, MatchOperator.Or); + + // Assert + score.Should().Be(MatchScores.Perfect); + } + + private static double GetScore(IMatcher? matcher1, IMatcher? matcher2, IMatcher? matcher3, MatchOperator matchOperator = MatchOperator.And) { // Assign var body = new BodyData @@ -44,41 +160,20 @@ AAAADElEQVR4XmMQYNgAAADkAMHebX3mAAAAAElFTkSuQmCC DetectedBodyType = BodyType.MultiPart }; - 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 partTextJsonContentTypeMatcher = new ContentTypeMatcher("text/json"); - var partTextJsonContentMatcher = new JsonMatcher(new { Key = "Value" }, true); - var partTextMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, partTextJsonContentTypeMatcher, null, null, partTextJsonContentMatcher); - - 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, - partTextMatcher, - imagePngMatcher - }; - var headers = new Dictionary { { "Content-Type", new[] { @"multipart/mixed; boundary=""=-5XgmpXt0XOfzdtcgNJc2ZQ==""" } } }; var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", body, headers); + var matchers = new IMatcher?[] { matcher1, matcher2, matcher3 } + .Where(m => m is not null) + .ToArray(); - var matcher = new RequestMessageMultiPartMatcher(matchers); + var matcher = new RequestMessageMultiPartMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, matchers!); // Act var result = new RequestMatchResult(); - var score = matcher.GetMatchingScore(requestMessage, result); - - // Assert - score.Should().Be(MatchScores.Perfect); + return matcher.GetMatchingScore(requestMessage, result); } } #endif \ No newline at end of file