fix(pact_models): V4 body with no content attribute is not a missing body - #547
Merged
rholshausen merged 1 commit intoAug 5, 2026
Conversation
Endika
force-pushed
the
fix/v4-body-without-content-attribute
branch
from
August 5, 2026 07:25
25f45c4 to
f701ee6
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes pact-foundation/pact-python#1103.
A V4 message whose
contentsis a JSON object without acontentattribute wasread as a missing body, so the interaction verified successfully whatever the
provider produced. From the issue, these two pacts differ only in how
contentsis written:
Verified against a provider returning
{"Payload": "does NOT exist"}, the secondfails as it should and the first reports
has a matching body (OK).Cause
body_from_jsoninpact_models/src/v4/http_parts.rstook theValue::Objectbranch, found no
contentattribute, and returnedOptionalBody::Missingsilently. Nothing downstream then had a body to compare.
Note the asymmetry this sat next to: when the body attribute is not a JSON
object at all, the same function already warns and loads the value as plain text.
Only the object-without-
contentcase was silent.Fix
Missingis kept when the object holds nothing but body attributes —{}, orcontentType/encodedwith no content — since that is a genuinely absent body.Any other key means the payload has been written where the body attributes
belong, so it now warns and loads the whole value as the body, which is the
treatment the function already gives a non-object body.
What made me comfortable doing that rather than only adding a warning is that
this shape is never produced by this library. On the write side,
OptionalBody::to_v4_jsonemitscontentforPresent,{"content": ""}forEmpty, andValue::NullforMissing/Null— andMessageContents::to_jsononly inserts
contentswhen that is an object, so a missing body omits theattribute entirely. An object without
contentcan only come from a hand-writtenor incorrectly generated pact, so there is no legitimate producer to break.
Tests
Three tests, all failing before the change:
body_from_json_returns_missing_if_the_body_only_has_body_attributes—{}and an object with only
contentType/encodedstayMissing.body_from_json_loads_the_whole_value_if_the_body_has_no_content_attribute—the payload is loaded rather than dropped.
message_contents_without_a_content_attribute_are_not_droppedinv4/async_message.rs— the same at the interaction level, using the JSON fromthe issue.
cargo test --package pact_models616 passed,--package pact_matching419 and823 passed.
cargo clippy --package pact_modelsreports the same 274 warnings asmaster, so none are added.
End-to-end
Since the report is against pact-python, I checked the symptom actually goes away
there rather than only the parsing. I built
libpact_ffifrom this branch andrebuilt pact-python's CFFI extension against it —
pactffi_version()reports0.5.6andlddconfirms it resolves to the local build — then ran thereporter's scenario:
has a matching body (FAILED)has a matching body (FAILED)contentattributehas a matching body (OK)has a matching body (FAILED)The malformed pact now fails with the same mismatch as the correct one:
Notes
body_from_json, which is shared with V4 HTTP request andresponse bodies, so it applies there too. That seemed right rather than
message-specific: an HTTP body written the same way was being dropped just as
silently.
existing "salvage what we can" behaviour of this function, and turning
malformed bodies into hard failures felt like a separate decision for you to
make. Happy to change it if you would rather it failed loudly.
LLM disclosure
This PR text was written with LLM assistance.