Adam Cox
06/02/2023, 9:51 AM"request": { "id": 1 },
The configure interaction request receives a payload like this:
"request": Value { kind: Some(StructValue(Struct { fields: {"id": Value { kind: Some(NumberValue(1.0)) } } })) }
Now in the contract the the value to match against is 1.0
and not 1
. In the consumer test a message is sent to the mock server with the payload:
{
"id": 1
}
When the mock server receives this request we try to match against all interactions in the pact but we get a mismatch for this field as:
1.1) [$.id] Expected '0.0' to be equal to '0'
I have been trying to debug and fix this issue and it looks to me like the following is happening:
1. The pact framework is taking the consumer contract and converting it to a protobuf payload to send to the plugin
2. This process converted the id field to a Value:Kind:Number(&f64)
3. In the plugin we decode the protobuf payload using proto_value_to_json
(I think I took this directly from your protobuf plugin) and for a number Kind it runs: serde_json::json!(n)
4. serde is seeing the f64 and creating a Value::Number(Number::Float))
5. So now we have lost the information that the original number was an int and not a float and our comparison fails when we receive an int
It seems to me that serde has a good way of handling the different number types but the prost_types definition of Kind::NumberValue(f64),
is reducing the amount of information that can be conveyed to the plugin about a numeric field during the configure interaction request.
I had a search online around protobuf number types and it looked like different types of numbers are supported so I'm not understanding why prost_types treats all numbers as floats. I am not very familiar with protobuf at all though so I probably am just missing something.
In the mean time we are encouraging the users to define the contract as { "id": "matching(integer, 1)" }
but I think it should be possible for them to just specify the contract as { "id": 1 }
and have it work. This did work when we were using the core application/json matcher but I am assuming that is because the type information is not lost when passing the values around in rust.uglyog
// `Value` represents a dynamically typed value which can be either
// null, a number, a string, a boolean, a recursive struct value, or a
// list of values. A producer of value is expected to set one of these
// variants. Absence of any variant indicates an error.
//
// The JSON representation for `Value` is JSON value.
message Value {
// The kind of value.
oneof kind {
// Represents a null value.
NullValue null_value = 1;
// Represents a double value.
double number_value = 2;
// Represents a string value.
string string_value = 3;
// Represents a boolean value.
bool bool_value = 4;
// Represents a structured value.
Struct struct_value = 5;
// Represents a repeated `Value`.
ListValue list_value = 6;
}
}
uglyog
uglyog
Matt (pactflow.io / pact-js / pact-go)
Matt (pactflow.io / pact-js / pact-go)