WireMock-Net / WireMock.Net

WireMock.Net is a flexible product for stubbing and mocking web HTTP responses using advanced request matching and response templating. Based on the functionality from http://WireMock.org, but extended with more functionality.
Apache License 2.0
1.35k stars 197 forks source link

Multipart matcher #1119

Open rmeshksar opened 2 weeks ago

rmeshksar commented 2 weeks ago

Hi, I would like to match multipart requests. In the request there can be several parts and I only need to match some of them and not all. It seems when passing an array of matchers to .WithMultipart it has to be one to one associated with the request but in my case there can be different number of parts in the request and I always want to only match the first two parts based on some conditions.

In the example below request has 3 parts but how can I check if the part that has text/json content-type has specific content regardless of other parts or regardless of position of the json part.

I commented out some elements of matchers array and it fails.

    static void Main(string[] args)
    {
        // Arrange
        var server = WireMockServer.Start();

        var textPlainContent = "This is some plain text";
        var textPlainContentType = "text/plain";
        var textPlainContentTypeMatcher = new ContentTypeMatcher(textPlainContentType);
        var textPlainContentMatcher = new ExactMatcher(textPlainContent);
        var textPlainMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, textPlainContentTypeMatcher, null, null, textPlainContentMatcher);

        var textJson = "{ \"Key\" : \"Value\" }";
        var textJsonContentType = "text/json";
        var textJsonContentTypeMatcher = new ContentTypeMatcher(textJsonContentType);
        var textJsonContentMatcher = new JsonMatcher(new { Key = "Value" }, true);
        var jsonMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, textJsonContentTypeMatcher, null, null, textJsonContentMatcher);

        var imagePngBytes = Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAIAAAACAgMAAAAP2OW3AAAADFBMVEX/tID/vpH/pWX/sHidUyjlAAAADElEQVR4XmMQYNgAAADkAMHebX3mAAAAAElFTkSuQmCC");
        var imagePngContentMatcher = new ExactObjectMatcher(imagePngBytes);
        var imagePngMatcher = new MimePartMatcher(MatchBehaviour.AcceptOnMatch, null, null, null, imagePngContentMatcher);

        var matchers = new IMatcher[]
        {
            //textPlainMatcher,
            jsonMatcher,
            //imagePngMatcher
        };

        server
            .Given(
                Request.Create()
                    .UsingPost()
                    .WithPath("/multipart")
                    .WithMultiPart(matchers))
            .RespondWith(Response.Create());

        // Act
        var formDataContent = new MultipartFormDataContent();
        formDataContent.Add(new StringContent(textPlainContent, Encoding.UTF8, textPlainContentType), "text");
        formDataContent.Add(new StringContent(textJson, Encoding.UTF8, textJsonContentType), "json");

        var fileContent = new ByteArrayContent(imagePngBytes);
        fileContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
        formDataContent.Add(fileContent, "somefile", "image.png");

        var client = server.CreateClient();

        var response = client.PostAsync("/multipart", formDataContent).GetAwaiter().GetResult();

        // Assert
        Console.WriteLine(response.StatusCode);
        Console.ReadLine();

        server.Stop();
    }
rmeshksar commented 2 weeks ago

I think it is a bug. in the file ~\Matchers\Request\RequestMessageMultiPartMatcher.cs, the logic of GetMatchingScore method should be changed to have two loops with the outer loop to be matchers and inner loop each part.

I tried the following and it worked but there might be better ways to implement it:

    public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
    {
#if !MIMEKIT
        throw new System.NotSupportedException("The MultiPartMatcher can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower.");
#else
        var score = MatchScores.Mismatch;
        Exception? exception = null;

        if (Matchers?.Any() != true)
        {
            return requestMatchResult.AddScore(GetType(), score, null);
        }

        if (!MimeKitUtils.TryGetMimeMessage(requestMessage, out var message))
        {
            return requestMatchResult.AddScore(GetType(), score, null);
        }

        try
        {
            foreach (var mimePartMatcher in Matchers.OfType<MimePartMatcher>().ToArray())
            {
                score = MatchScores.Mismatch;
                foreach (var mimeBodyPart in message.BodyParts.OfType<MimeKit.MimePart>())
                {
                    var matchResult = mimePartMatcher.IsMatch(mimeBodyPart);
                    if (matchResult.IsPerfect())
                    {
                        score = MatchScores.Perfect;
                        break;
                    }
                }
                if ((MatchOperator == MatchOperator.Or && MatchScores.IsPerfect(score))
                    || (MatchOperator == MatchOperator.And && !MatchScores.IsPerfect(score)))
                {
                    break;
                }
            }
        }
        catch (Exception ex)
        {
            exception = ex;
        }

        return requestMatchResult.AddScore(GetType(), score, exception);
#endif
    }
StefH commented 2 days ago

@rmeshksar Can you please create a fix PR which also includes a unit-test?