Open
Description
🌎 Environment
- Platform:
pact_ffi
- Version/Release:
0.4.25
- Pact spec:
v3
,v4
- FFI used:
bool pactffi_with_body(
InteractionHandle interaction,
enum InteractionPart part,
const char *content_type,
const char *body
);
💬 Description
Setting up unit tests for PactSwift
verifying matchers in request body, specifically Numeric
matcher. When executing the API request and Pact verification, verification succeeds with false positive:
2025-01-28T03:42:33.886570Z INFO tokio-runtime-worker pact_mock_server::hyper_server: Request matched, sending response
2025-01-28T03:42:33.886572Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server:
----------------------------------------------------------------------------------------
status: 200
headers: None
body: Missing
----------------------------------------------------------------------------------------
2025-01-28T03:42:33.886586Z TRACE tokio-runtime-worker encode_headers: hyper::proto::h1::role: Server::encode status=200, body=None, req_method=Some(POST)
🦶 Reproduction Steps
Steps to reproduce the behaviour:
- Setup a pact interaction that expects request body with Numeric value, ie
"{\"value\”:{
\"key2\":{\"value\":321,\"pact:matcher:type\":\"number\”},
\"key1\”:{\"pact:matcher:type\":\"number\",\"value\":123.1}},
\"pact:matcher:type\":\"type\”
}"
- Prepare an encodable type
Body(key1: String, key2: String)
that takes strings for property’s values, - Send a POST request with the
Body
type and string values, - Verify pact interaction,
🤔 Expected Results
Pact test should fail due to invalid type being passed for the body keys’ values.
😲 Actual Results
Pact test succeeds with Request matched, sending response
----------------------------------------------------------------------------------------
status: 200
headers: None
body: Missing
----------------------------------------------------------------------------------------
🌳 Logs
...
2025-01-28T04:09:13.515623Z TRACE ThreadId(02) pact_ffi::mock_server::handles: with_interaction - inner = PactHandleInner { pact: V4Pact { consumer: Consumer { name: "Consumer" }, provider: Provider { name: "Provider" }, interactions: [SynchronousHttp { id: None, key: None, description: "a request for a known path with a body matching number", provider_states: [], request: HttpRequest { method: "POST", path: "/jsonbody", query: None, headers: Some({"Content-Type": ["application/json"]}), body: Present(b"{\"key1\":123.1,\"key2\":321}", Some(ContentType { main_type: "application", sub_type: "json", attributes: {}, suffix: None }), None), matching_rules: MatchingRules { rules: {PATH: MatchingRuleCategory { name: PATH, rules: {} }, BODY: MatchingRuleCategory { name: BODY, rules: {DocPath { path_tokens: [Root, Field("key2")], expr: "$.key2" }: RuleList { rules: [Number], rule_logic: And, cascaded: false }, DocPath { path_tokens: [Root, Field("key1")], expr: "$.key1" }: RuleList { rules: [Number], rule_logic: And, cascaded: false }, DocPath { path_tokens: [Root], expr: "$" }: RuleList { rules: [Type], rule_logic: And, cascaded: false }} }} }, generators: Generators { categories: {} } }, response: HttpResponse { status: 200, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } }, comments: {}, pending: false, plugin_config: {}, interaction_markup: InteractionMarkup { markup: "", markup_type: "" }, transport: None }], metadata: {"namespace1": Object {"name1": String("value1")}, "namespace2": Object {"name2": String("value2")}, "pactRust": Object {"ffi": String("0.4.25")}}, plugin_data: [] }, mock_server_started: false, specification_version: V4 }
…
2025-01-28T04:09:54.935771Z INFO tokio-runtime-worker pact_mock_server::hyper_server: Received request POST /jsonbody
2025-01-28T04:09:54.935775Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server:
----------------------------------------------------------------------------------------
method: POST
path: /jsonbody
query: None
headers: Some({"content-length": ["29"], "accept-encoding": ["gzip", "deflate"], "accept": ["*/*"], "accept-language": ["en-AU", "en;q=0.9"], "user-agent": ["xctest/23600 CFNetwork/1568.300.101 Darwin/24.2.0"], "host": ["127.0.0.1:4516"], "content-type": ["application/json"], "connection": ["keep-alive"]})
body: Present(29 bytes, application/json) '{"key2":"456","key1":"321.1"}'
----------------------------------------------------------------------------------------
…
2025-01-28T04:09:54.935896Z DEBUG tokio-runtime-worker pact_matching: comparing to expected HTTP Request ( method: POST, path: /jsonbody, query: None, headers: Some({"Content-Type": ["application/json"]}), body: Present(25 bytes, application/json) )
2025-01-28T04:09:54.935904Z DEBUG tokio-runtime-worker pact_matching: body: '{"key1":123.1,"key2":321}'
2025-01-28T04:09:54.935907Z DEBUG tokio-runtime-worker pact_matching: matching_rules: MatchingRules { rules: {PATH: MatchingRuleCategory { name: PATH, rules: {} }, BODY: MatchingRuleCategory { name: BODY, rules: {DocPath { path_tokens: [Root, Field("key2")], expr: "$.key2" }: RuleList { rules: [Number], rule_logic: And, cascaded: false }, DocPath { path_tokens: [Root, Field("key1")], expr: "$.key1" }: RuleList { rules: [Number], rule_logic: And, cascaded: false }, DocPath { path_tokens: [Root], expr: "$" }: RuleList { rules: [Type], rule_logic: And, cascaded: false }} }} }
…
2025-01-28T04:09:54.962229Z DEBUG tokio-runtime-worker pact_matching: --> Mismatches: []
2025-01-28T04:09:54.962254Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server: Test context = {"mockServer": Object {"port": Number(4516), "url": String("http://127.0.0.1:4516")}}
2025-01-28T04:09:54.962257Z TRACE tokio-runtime-worker pact_matching: generate_response response=HttpResponse { status: 200, headers: None, body: Missing, matching_rules: MatchingRules { rules: {} }, generators: Generators { categories: {} } } mode=Consumer context={"mockServer": Object {"port": Number(4516), "url": String("http://127.0.0.1:4516")}}
2025-01-28T04:09:54.962262Z INFO tokio-runtime-worker pact_mock_server::hyper_server: Request matched, sending response
2025-01-28T04:09:54.962521Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server:
----------------------------------------------------------------------------------------
status: 200
headers: None
body: Missing
----------------------------------------------------------------------------------------
2025-01-28T04:09:54.962539Z TRACE tokio-runtime-worker encode_headers: hyper::proto::h1::role: Server::encode status=200, body=None, req_method=Some(POST)
2025-01-28T04:09:54.962570Z DEBUG tokio-runtime-worker hyper::proto::h1::io: flushed 319 bytes
….
📄 Stack Traces
See attached false-positive-numeric-body-value
for full standardOut: .trace
log:
false-positive-numeric-body-value.log
🤝 Relationships
- Related PRs or Issues: