GitHub
06/21/2024, 5:49 AMwithRequest I get body content type mismatch because my test uses random boundary for multipart body. Then I tried withCompleteRequest so I can just use regex matcher for the body. PactJS gives me Error: V4InteractionWithCompleteRequest is unimplemented.
Software versions
• OS: MacOS 14.5
• Consumer Pact library: Pact JS 12.5.2
• Provider Pact library: Pact JS 12.5.2
• Node Version: v20.11.1
Issue Checklist
Please confirm the following:
☑︎ I have upgraded to the latest
☑︎ I have the read the FAQs in the Readme
☑︎ I have triple checked, that there are no unhandled promises in my code and have read the section on intermittent test failures
☑︎ I have set my log level to debug and attached a log file showing the complete request/response cycle
☐ For bonus points and virtual high fives, I have created a reproduceable git repository (see below) to illustrate the problem
Expected behaviour
There should be a way to relax request matching while still providing a specific example for provider verification.
Actual behaviour
Error: V4InteractionWithCompleteRequest is unimplemented
Steps to reproduce
await new PactV4({
provider: "P",
consumer: "C",
logLevel: "debug",
})
.addInteraction()
.uponReceiving("...")
.withCompleteRequest({
method: "POST",
path: "/upload-file",
headers: {
accept: M.regex(/application\/json/, "application/json"),
"content-type": M.regex(
/^multipart\/form-data.+$/,
`multipart/form-data; boundary=${boundary}`,
),
},
body: M.regex(/.*/, bodyBuffer.toString("utf-8")),
})
.withCompleteResponse({
status: 200,
contentType: "application/json; charset=utf-8",
body: {
success: true,
},
})
.executeTest(async (server) => {
const form = new FormData();
form.append("uploadPath", "/blog");
form.append(
"files",
await openAsBlob(testImagePath),
"image.png",
);
await fetch(new URL("/upload-file", server.url), {
method: "POST",
headers: {
accept: "application/json",
},
body: form,
});
});
Relevant log files
2024-06-21T05:18:39.925518Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server:
----------------------------------------------------------------------------------------
method: POST
path: /upload-file
query: None
headers: Some({"connection": ["keep-alive"], "host": ["127.0.0.1:58624"], "accept-language": ["*"], "accept-encoding": ["gzip", "deflate"], "content-length": ["363"], "accept": ["application/json"], "user-agent": ["node"], "sec-fetch-mode": ["cors"], "content-type": ["multipart/form-data; boundary=----formdata-undici-074263581352"]})
body: Present(363 bytes, multipart/form-data;boundary=----formdata-undici-074263581352)
----------------------------------------------------------------------------------------
2024-06-21T05:18:39.925555Z INFO tokio-runtime-worker pact_matching: comparing to expected HTTP Request ( method: POST, path: /upload-file, query: None, headers: Some({"accept": ["application/json"], "content-type": ["multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW"]}), body: Present(362 bytes) )
2024-06-21T05:18:39.925564Z DEBUG tokio-runtime-worker pact_matching: body: '2D2D2D2D5765624B6974466F726D426F756E64617279374D41345957786B5472... (362 bytes)'
2024-06-21T05:18:39.925565Z DEBUG tokio-runtime-worker pact_matching: matching_rules: MatchingRules { rules: {BODY: MatchingRuleCategory { name: BODY, rules: {DocPath { path_tokens: [Root], expr: "$" }: RuleList { rules: [ContentType("multipart/form-data")], rule_logic: And, cascaded: false }} }, PATH: MatchingRuleCategory { name: PATH, rules: {} }, HEADER: MatchingRuleCategory { name: HEADER, rules: {DocPath { path_tokens: [Root, Field("content-type")], expr: "$['content-type']" }: RuleList { rules: [Regex("^multipart\\/form-data.+$")], rule_logic: And, cascaded: false }} }} }
2024-06-21T05:18:39.925573Z DEBUG tokio-runtime-worker pact_matching: generators: Generators { categories: {} }
2024-06-21T05:18:39.925594Z DEBUG tokio-runtime-worker pact_matching::matchers: String -> String: comparing '/upload-file' to '/upload-file' ==> true cascaded=false matcher=Equality
2024-06-21T05:18:39.925602Z DEBUG tokio-runtime-worker pact_matching: expected content type = 'multipart/form-data;boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW', actual content type = 'multipart/form-data;boundary=----formdata-undici-074263581352'
2024-06-21T05:18:39.925619Z DEBUG tokio-runtime-worker pact_matching: content type header matcher = 'RuleList { rules: [], rule_logic: And, cascaded: false }'
2024-06-21T05:18:39.925670Z DEBUG tokio-runtime-worker pact_matching::matchers: String -> String: comparing 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' to 'multipart/form-data; boundary=----formdata-undici-074263581352' ==> true cascaded=false matcher=Regex("^multipart\\/form-data.+$")
2024-06-21T05:18:39.925677Z DEBUG tokio-runtime-worker pact_matching: --> Mismatches: [BodyTypeMismatch { expected: "multipart/form-data;boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW", actual: "multipart/form-data;boundary=----formdata-undici-074263581352", mismatch: "Expected a body of 'multipart/form-data;boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' but the actual content type was 'multipart/form-data;boundary=----formdata-undici-074263581352'", expected_body: Some(b"----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"files\"; filename=\"white.png\"\nContent-Type: application/octet-stream\n\n\x89PNG\r\n\x1a\n\0\0\0\rIHDR\0\0\0\x01\0\0\0\x01\x08\x06\0\0\0\x1f\x15\xc4\x89\0\0\0\x01sRGB\0\xae\xce\x1c\xe9\0\0\0\rIDAT\x18Wc\xf8\xff\xef\xdf\x7f\0\t\xf6\x03\xfbW\xfe{\x1b\0\0\0\0IEND\xaeB`\x82\n----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"uploadPath\"\n\n/blog\n----WebKitFormBoundary7MA4YWxkTrZu0gW\n"), actual_body: Some(b"------formdata-undici-074263581352\r\nContent-Disposition: form-data; name=\"uploadPath\"\r\n\r\n/blog\r\n------formdata-undici-074263581352\r\nContent-Disposition: form-data; name=\"files\"; filename=\"image.png\"\r\nContent-Type: application/octet-stream\r\n\r\n\x89PNG\r\n\x1a\n\0\0\0\rIHDR\0\0\0\x01\0\0\0\x01\x08\x06\0\0\0\x1f\x15\xc4\x89\0\0\0\x01sRGB\0\xae\xce\x1c\xe9\0\0\0\rIDAT\x18Wc\xf8\xff\xef\xdf\x7f\0\t\xf6\x03\xfbW\xfe{\x1b\0\0\0\0IEND\xaeB`\x82\r\n------formdata-undici-074263581352--") }]
2024-06-21T05:18:39.925738Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server: Request did not match: Request did not match - HTTP Request ( method: POST, path: /upload-file, query: None, headers: Some({"accept": ["application/json"], "content-type": ["multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW"]}), body: Present(362 bytes) ) 0) Expected a body of 'multipart/form-data;boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' but the actual content type was 'multipart/form-data;boundary=----formdata-undici-074263581352'
pact-foundation/pact-js