Hello! Can anyone please tell me if anybody has e...
# general
m
Hello! Can anyone please tell me if anybody has experience sending requests with Content-Type
"application/octet-stream"
(pact-js library) and verification on the provider side with C# language (pact-net library)? Because of many of my attempts, the response is 200 on the provider side, but when I try to test HTTP 400 Bad Request, I cannot find a suitable
withRequest
body for that test.
Copy code
test("...", async () => {
                const badRequestData = {
                    error: {
                        code: "validationError",
                        message: "Validation error(s) has occurred",
                        details: [],
                    },
                };
                const badRequestResponse = somethingLike({
                    error: {
                        code: like(badRequestData.error.code),
                        message: like(badRequestData.error.message),
                        details: like(badRequestData.error.details),
                    },
                });
                const status = 400;

                await provider.addInteraction({
                    state: "...",
                    uponReceiving: "...",
                    withRequest: { method, headers, path, body: "\u0000" },
                    willRespondWith: { status, body: badRequestResponse },
                });
                expect.assertions(3);
                try {
                    await testService.sendContent(mailboxId, content);
                } catch (e) {
                    checkIfHttpError(e, status);
                    expect((e as AxiosError).response?.data).toStrictEqual(badRequestData);
                }
            });
Copy code
[ApiController]
[Route("test")]
public class TestController : ControllerBase
{
    private readonly ITestService _testService;

    public TestController(ITestService testService)
    {
        _testService = testService;
    }

    [HttpPost("...")]
    [Authorize(...)]
    [Produces("application/json")]
    [Consumes("application/octet-stream")]
    [ProducesResponseType(type: typeof(TestResponse), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
    public async Task<TestResponse> HandleAsync(string id, [FromQuery(Name = "fileName")] string? fileName)
    {
        if (Request.ContentLength == 0)
        {
            throw new InvalidArgumentException("Request body should not be empty");
        }

        var messageId = await _testService.HandleAsync(id: id.ToLower().Trim(), fileName, Request.Body);

        return new TestResponse(messageId);
    }
}
m
What’s the issue with the
400
use case?
m
Sorry for the long time to answer. The issue is more related to the Axios library. It would be great if you, Matt, could help me deal with that. Because for now, it is a real blocking issue for our team. The main problem is sending an empty object with the type ArrayBuffer in the body and "application/octet-stream" as a content type request in the contract test - to get a validation error from the provider. To get such a response from the .NET part - the request content length should be zero. But all of my attempts to send, such as ArrayBuffer(0), don't represent Request with zero content type on the provider side, and as a result - API is trying to do some domain logic with this data returns 200 as well
m
No problems. I’m not sure I can give you an answer without detailed exploration on ArrayBuffers. Could you just omit the binary content altogether?
Personally, I don’t think this is a useful test case for Pact though - it feels more of a functional test of the provider. You’re trying to force a
400
on the provider by given them garbage and by knowledge of why it’s causing a
400
. The main thing is that the consumer can handle a
400
. The consumer test should not encode detailed knowledge of how the provider works like this, as it might lead to brittle tests if the provider changes its validation.
m
Maybe, but it is a part of our actual business logic case, which calls for sending empty files from consumers. Omit from binary content we cannot do because it must be in the protocol of our system. Most possible that I should manually find which is request body sending in UI during that case and pass them in our contract test
Yeah, it seems we send
ArrayBuffer(0)
to test empty file sending on our UI. But, Pact convert
withRequest
body
to null if that body is
{}
or
ArrayBuffer(0)
Copy code
[2022-09-28 01:54:13.419 +0000] ERROR (26056 on ...): pact@9.18.1: error making http request: Request failed with status code 500
 FAIL  .../__tests__/TestingService.contract.test.ts (5.606 s)                               
  ● Console

    console.error
      

      at node_modules/@pact-foundation/src/httpPact.ts:151:17

    console.error
      Pact verification failed!

      at node_modules/@pact-foundation/src/httpPact.ts:152:17

    console.error
      Actual interactions do not match expected interactions for mock MockService.
      
      Incorrect requests:
        POST /.../004b90d2 (request body did not match)
      
      Diff with interaction: "a bad post content request" given "..."
      Diff
      --------------------------------------
      Key: - is expected 
           + is actual 
      Matching keys and values are not shown
      
       {
      -  "body": {
      -  }
      +  "body": null
       }
      
      Description of differences
      --------------------------------------
      * Expected a Hash but got nil at $.body
      
      See C:/.../.../.../pact/logs/...-mockserver-interaction-port-9001.log for details.
      

      at node_modules/@pact-foundation/src/httpPact.ts:153:17
Copy code
await provider.addInteraction({
                    state: "...",
                    uponReceiving: "a bad post content request",
                    withRequest: { method, headers, path, body: /* new ArrayBuffer(0) or {} */ },
                    willRespondWith: { status, body: badRequestResponse },
                });
                expect.assertions(3);
                try {
                    await keyService.sendContent(mailboxId, new ArrayBuffer(0));
m
Well, what is an empty array buffer? It’s 0 bytes of data
JSON has no concept of an array buffer, so it needs to be converted into something expressible in JSON. A 0 byte object is nothing
m
You're right; I didn't even think about that
👍 1
So, can we conclude that it is a colossal corner case that cannot be tested with contract tests due to JSON limitation for working with binaries, only if our team agrees to do that with functional tests on the provider (API) side?
m
Yeah, I think the main confidence you want to get here, is that your consumer can handle a
400
. You could do that with provider states, and be agnostic to how the
400
happened (but it’s a bit clunky for the provider). You should definitely functionally test the provider for its validation use cases, but these belong in the provider code base
m
I understand. Thank you very much, Matt! 🙂
🙌 1
m
You’re welcome!