Skip to content

Commit a57645f

Browse files
committed
chore(ffi): add multipart test
Signed-off-by: JP-Ellis <josh@jpellis.me>
1 parent 5d7754e commit a57645f

1 file changed

Lines changed: 110 additions & 0 deletions

File tree

rust/pact_ffi/tests/tests.rs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ use pact_ffi::mock_server::handles::{
6161
pactffi_upon_receiving,
6262
pactffi_with_binary_file,
6363
pactffi_with_body,
64+
pactffi_with_binary_body,
6465
pactffi_with_header,
6566
pactffi_with_header_v2,
6667
pactffi_with_multipart_file,
@@ -2309,3 +2310,112 @@ fn date_matcher_in_response_body_without_generator_type() {
23092310

23102311
expect!(mismatches).to(be_equal_to("[]"));
23112312
}
2313+
2314+
// Test multipart request with JSON metadata and JPEG image using matching rules
2315+
#[test_log::test]
2316+
fn mime_multipart_with_json_and_image() {
2317+
let pact_handle = pactffi_new_pact(
2318+
c"multipart-json-image-consumer".as_ptr(),
2319+
c"multipart-json-image-provider".as_ptr()
2320+
);
2321+
let interaction = pactffi_new_interaction(
2322+
pact_handle,
2323+
c"multipart_with_json_metadata_and_jpeg_image".as_ptr()
2324+
);
2325+
pactffi_with_request(interaction, c"POST".as_ptr(), c"/upload".as_ptr());
2326+
2327+
// Create the expected multipart body with JSON metadata and JPEG image
2328+
let jpeg_bytes: [u8; 48] = [
2329+
2330+
// Magic bytes
2331+
0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,
2332+
// Other data
2333+
0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xff, 0xdb, 0x00, 0x43,
2334+
0x00, 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, 0x0d, 0x0e, 0x12,
2335+
0x11, 0x10, 0x13, 0x18, 0x28, 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23,
2336+
];
2337+
2338+
let boundary = "test-boundary-12345";
2339+
let expected_body = format!(
2340+
"--{boundary}\r\n\
2341+
Content-Type: application/json\r\n\
2342+
Content-Disposition: form-data; name=\"metadata\"\r\n\
2343+
\r\n\
2344+
{{\"name\":\"test\",\"size\":100}}\r\n\
2345+
--{boundary}\r\n\
2346+
Content-Type: image/jpeg\r\n\
2347+
Content-Disposition: form-data; name=\"image\"; filename=\"test.jpg\"\r\n\
2348+
\r\n",
2349+
);
2350+
2351+
let mut body = expected_body.as_bytes().to_vec();
2352+
body.extend_from_slice(&jpeg_bytes);
2353+
body.extend_from_slice(format!("\r\n--{}--\r\n", boundary).as_bytes());
2354+
2355+
let content_type = CString::new(format!("multipart/form-data; boundary={}", boundary)).unwrap();
2356+
pactffi_with_binary_body(
2357+
interaction,
2358+
InteractionPart::Request,
2359+
content_type.as_ptr(),
2360+
body.as_ptr(),
2361+
body.len()
2362+
);
2363+
2364+
// Add matching rules for the JSON metadata and image
2365+
let matching_rules = matchingrules!{
2366+
"body" => {
2367+
"$.metadata" => [ MatchingRule::Type ],
2368+
"$.metadata.name" => [ MatchingRule::Regex("^[a-zA-Z]+$".to_string()) ],
2369+
"$.metadata.size" => [ MatchingRule::Integer ],
2370+
"$.image" => [ MatchingRule::ContentType("image/jpeg".to_string()) ]
2371+
},
2372+
"header" => {
2373+
"Content-Type" => [
2374+
MatchingRule::Regex("multipart/form-data;(\\s*charset=[^;]*;)?\\s*boundary=.*".to_string())
2375+
]
2376+
}
2377+
};
2378+
let matching_rules_json = matchers_to_json(&matching_rules, &PactSpecification::V4);
2379+
println!("Matching rules JSON: {}", matching_rules_json);
2380+
let matching_rules_str = CString::new(matching_rules_json.to_string()).unwrap();
2381+
pactffi_with_matching_rules(interaction, InteractionPart::Request, matching_rules_str.as_ptr());
2382+
2383+
pactffi_response_status(interaction, 201);
2384+
2385+
let port = pactffi_create_mock_server_for_transport(pact_handle, c"127.0.0.1".as_ptr(), 0, null(), null());
2386+
expect!(port).to(be_greater_than(0));
2387+
2388+
// Make actual request with different JSON values and the same JPEG structure
2389+
let client = Client::default();
2390+
let form = reqwest::blocking::multipart::Form::new()
2391+
.text("metadata", r#"{"name":"different","size":200}"#)
2392+
.part(
2393+
"image",
2394+
reqwest::blocking::multipart::Part::bytes(jpeg_bytes.to_vec())
2395+
.file_name("actual.jpg")
2396+
.mime_str("image/jpeg").unwrap()
2397+
);
2398+
2399+
let result = client.post(format!("http://127.0.0.1:{}/upload", port).as_str())
2400+
.multipart(form)
2401+
.send();
2402+
2403+
thread::sleep(Duration::from_millis(100)); // Give mock server some time to update events
2404+
let mismatches = unsafe {
2405+
CStr::from_ptr(pactffi_mock_server_mismatches(port)).to_string_lossy().into_owned()
2406+
};
2407+
2408+
match result {
2409+
Ok(res) => {
2410+
let status = res.status();
2411+
expect!(status).to(be_eq(201));
2412+
},
2413+
Err(err) => {
2414+
panic!("expected 201 response but request failed - {}", err);
2415+
}
2416+
};
2417+
2418+
pactffi_cleanup_mock_server(port);
2419+
2420+
expect!(mismatches).to(be_equal_to("[]"));
2421+
}

0 commit comments

Comments
 (0)