Rohit Krishnan
11/07/2023, 3:20 PMAnna Nava
12/04/2023, 4:51 PM<http://au.com|au.com>.dius.pact.consumer.dsl.DslPart
for protobuf.... so I wonder if Protobuf requires a different approach to what I am attempting here questionmario
@Pact(consumer = "my-service")
fun createPact(builder: PactBuilder): V4Pact {
val messageFile = loadFileFromJarResource("/events/pub/a/v1/a_event.proto")
return builder
.usingPlugin("protobuf")
.expectsToReceive("an AEvent ", "core/interaction/message")
.with(
mapOf(
"message.contents" to mapOf(
"pact:proto" to messageFile.path,
"pact:message-type" to "AlertEvent",
"pact:content-type" to "application/protobuf",
"pact:protobuf-config" to mapOf("additionalIncludes" to listOf("/Users/anna.nava/dev/protobuf/proto")),
"this_amazing_property" to mapOf(
//this compiles, but passes empty string: "id" to regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$")
//this doesn't compile:"id" to matchRegex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$")
//this compiles, but ends up passing in an empty string: "id" to regex("[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}")
//"id" to mapOf("matchers" to mapOf("match" to "regex", "regex" to "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\$" ))
"id" to regexp("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$", "123e4567-e89b-12d3-a456-426614174000")
),
(apologies if this is not the right channel for, very appreciated if anyone can redirect me where is best for me to ask this, thanks!)Vinicius Gabriel
12/05/2023, 6:11 PMpact:proto
? For example, on my application <http://github.com/x|github.com/x>
, I set a path to <http://github.com/y/protos|github.com/y/protos>
?Vinicius Gabriel
12/07/2023, 2:55 PMprotoMessage := `{
"pact:proto": "` + path + `",
"pact:message-type": "CreateAccountMessage",
"pact:content-type": "application/protobuf",
"pact:protobuf-config": {
"additionalIncludes": [
"/Users/viniciusgabriel/Projects/common/proto"
]
},
"is_migration": "matching(boolean, true)",
"account_type": "matching(type, 'SelfDirected')",
"account_info": {
"account_id": "eachValue(matching(type, 10))",
"message_id": "eachValue(matching(type, 10))",
"reg_form": {
"reg_form": "matching(boolean, false)",
"reg_form_sign": "notEmpty('sign')",
"signed_at": {
"seconds": "matching(integer, 100)",
"nanos": "matching(integer, 100)"
}
}
}
}`
I removed the is_migration
field from Protobuff used on Provider side:
Test:
functionMappings := message.Handlers{
"CreateAccountMessage": func([]models.ProviderState) (message.Body, message.Metadata, error) {
msg, _ := proto.Marshal(&proto.CreateAccountMessage{
// IsMigration: true,
AccountType: sfproto.AccountType_SelfDirected,
AccountInfo: &proto.AccountMessage{
RegForm: &sfproto.RegForm{
RegFormSign: "sign",
},
},
})
return msg, message.Metadata{
"contentType": "application/protobuf;message=CreateAccountMessage",
}, nil
},
}
Protobuff code update (used on Provider side):
type CreateAccountMessage struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AccountInfo *AccountMessage `protobuf:"bytes,1,opt,name=account_info,json=accountInfo,proto3" json:"account_info,omitempty"`
AccountType sfproto.ApexAccountType `protobuf:"varint,3,opt,name=account_type,json=accountType,proto3,enum=bridges.AccountType" json:"account_type,omitempty"`
}
When I run the provider test, is not expected the test break, because a field that one Consumer expect do not exist anymore?Rohit Krishnan
01/18/2024, 9:44 PMprotocVersion
via the pact FFI when invoking a test? Right now it defaults to 3.19.1
which doesn't have support for M1 macs, so I need some mechanism to force it to download a later version.Rohit Krishnan
01/19/2024, 3:37 PMStanislav Vodetskyi
01/25/2024, 1:29 AMeachValue()
but that seems to work only for simple value fields.
I'm currently doing smth like this, but this defines one element, and I'm not sure how to specify counts. @rholshausen you've added atLeast
and atMost
but I'm not sure where to put it, should it be just the element in the list? Thanks!
{
"availability_zones": [
{
"cloud_provider": "notEmpty('AWS')",
"zone_name": "notEmpty('us-east-1a')"
}
]
}
Stanislav Vodetskyi
01/26/2024, 2:09 AMfalse
in that field (i can adjust the state in the test), I'm getting this error from the protobuf plugin:
1) Verifying a pact between <consumer> and <provider> - <message>
2024-01-25 17:53:27.1661 ┃ 1.1) has a matching body
2024-01-25 17:53:27.1661 ┃ $ -> Expected message '<message>' but was missing or empty
This happens regardless of whether consumer expects true or false. The server seems to respond with a correct message, seeing how changing the state to return true
makes the plugin work correctly, passing if the consumer requested true and failing if the consumer requested false.
This could be because boolean false is default, so the plugin cannot distinguish empty response from the explicit false, or am I misreading this?Christopher Tonog
01/30/2024, 5:09 AM2024-01-30T05:05:04.269707Z ERROR ThreadId(01) pact_ffi::plugins: Failed to call out to plugin - Request to configure interaction failed: Failed to process protobuf: Failed to invoke protoc binary: exit code exit status: 1
2024-01-30T05:05:04.270658Z ERROR ThreadId(01) pact_ffi::mock_server: Failed to start mock server - Mock server failed to start: Failed to start gRPC mock server: Pact file does not contain any Protobuf descriptors
I'm assuming the failure to start the mock server is because of the failed protoc invocation. Has anyone run into this? I added debug logging, but nothing really struck at me. Thanks so much!Stanislav Vodetskyi
01/30/2024, 5:25 AMEdd Schauman-Haigh
01/30/2024, 10:19 AM"pact:proto", filepath("proto1.proto)
but inside proto1.proto
I have import './proto2.proto'
which also has its own dependency import './proto3.proto'
. Can I pass multiple proto files in to the protobuf plugin so I can handle multiple imports like this?Edd Schauman-Haigh
01/30/2024, 10:20 AMpact:proto
the first file and have all of them in the same directoryEdd Schauman-Haigh
01/30/2024, 10:23 AMpact:proto
, pact:message-type
, and pact:content-type
, I only have 7 keys remaining for message.contents
values. If I add an 8th key, Java explodes. Is there a nice way around this? How are people dealing with messages including more than 7 values?Edd Schauman-Haigh
01/30/2024, 10:24 AMStanislav Vodetskyi
02/01/2024, 1:26 AMatLeast(x)
condition here - does this mean that empty lists/maps are ok unless I restrict them using atLeast
? Thanks!Stanislav Vodetskyi
02/01/2024, 7:08 AMmap<string, string> labels = 4
in the .proto file).
I've tried:
"labels": {"pact:match": "eachKey(matching(type, 'some-key')), eachValue(matching(type, 'some-value'))"}
and got rpc error: code = FailedPrecondition desc = Failed to match the request message - BodyMismatches({"$.destination": [BodyMismatch { path: "$.destination.labels", expected: None, actual: None, mismatch: "Expected repeated field 'labels' to be empty but received 1 values" }]})
"labels": {"pact:match": "atLeast(0), eachKey(matching(type, 'some-key')), eachValue(matching(type, 'some-value'))"},
and it gave me an error 2024-01-31 22:44:50.5126 ┃ 2024-02-01T06:44:50.498712Z ERROR ThreadId(01) pact_ffi::plugins: Failed to call out to plugin - min-type is not a valid matching rule type
(or at least that's the error I found in the debug logs, in info it was the usual 'pact file contains no protobuf descriptors')Edd Schauman-Haigh
02/06/2024, 12:49 PMio.grpc.StatusRuntimeException: UNAVAILABLE: io exception
and ... Connection refused/[0:0:0:0:0:0:1]:63856
The tests work but the logs are full of these. Any advice on why?Stanislav Vodetskyi
02/07/2024, 12:12 AM[]byte
fields in grpc requests/responses? I'm currently converting it to string, smth like:
"bytes": "matching(type, '" + string(bytes) + "')",
is this the right way to go?Stanislav Vodetskyi
02/07/2024, 2:28 AMEdd Schauman-Haigh
02/07/2024, 9:56 AMbody: $ Messages with StartGroup wire type fields are not supported
. Anyone know what that means? I can't find anything obvious onlineEdd Schauman-Haigh
02/07/2024, 9:57 AMverifyInteraction()
function, on the first contract testChristopher Tonog
02/07/2024, 8:41 PMERROR ThreadId(01) pact_ffi::plugins: Failed to call out to plugin - Request to configure interaction failed: Failed to process protobuf: Failed to invoke protoc binary: exit code exit status: 1
ERROR ThreadId(01) pact_ffi::mock_server: Failed to start mock server - Mock server failed to start: Failed to start gRPC mock server: Pact file does not contain any Protobuf descriptors
I can imagine a number of things can be the cause of this (and I unfortunately can't post much more in the public channel), but I was wondering if there was a way to get more information on what specifically caused the protoc binary to exit? I set log levels to debug and trace but nothing really stood out to me. Thanks!Stanislav Vodetskyi
02/07/2024, 11:39 PM"response": [
{
"contents": {
"content": ""
},
"metadata": {
"contentType": "application/protobuf;message=Feature",
"grpc-message": "no feature was found at latitude:-1 longitude:-1",
"grpc-status": "NOT_FOUND"
}
}
],
Stanislav Vodetskyi
02/26/2024, 9:44 PM// provider state: given obj exists
{
"objs": {
"pact:match": "eachValue(matching($'obj')),atLeast(1)",
"obj": {
"field": "matching(type, 'sample')"
}
}
}
which is cool and exactly what I want to express.
However, if, theoretically speaking, my client code had branching like if len(objs) == 1 {} else {}
we would want our client code to hit both branches. Would we want a separate consumer test for that? What should we specify? I guess we could do provider state define multiple objects, but what would I put in the matcher then?Matt (pactflow.io / pact-js / pact-go)
Eric Deandrea
03/18/2024, 7:55 PM#pact-jvm
channel before I realized this one was here. Its rather lengthy, so rather than re-type it I’m just going to link to it if thats ok…
https://pact-foundation.slack.com/archives/C9UN99H24/p1710791089530159James DeMaine
03/27/2024, 4:38 PM2925Z WARN tokio-runtime-worker request{method=POST uri=<http://127.0.0.1:43899/io.pact.plugin.PactPlugin/VerifyInteraction> version=HTTP/2.0 headers={"te": "trailers", "content-type": "application/grpc", "authorization": Sensitive, "user-agent": "tonic/0.10.2"}}:decode_message: pact_protobuf_plugin::message_decoder: Was not able to decode field: Did not find a field with number 7 in the descriptor
2024-03-27T16:22:07.932940Z DEBUG tokio-runtime-worker pact_plugin_driver::child_process: Plugin(protobuf, 247654, STDOUT) || 2024-03-27T16:22:07.932935Z WARN tokio-runtime-worker request{method=POST uri=<http://127.0.0.1:43899/io.pact.plugin.PactPlugin/VerifyInteraction> version=HTTP/2.0 headers={"te": "trailers", "content-type": "application/grpc", "authorization": Sensitive, "user-agent": "tonic/0.10.2"}}:decode_message: pact_protobuf_plugin::message_decoder: Was not able to decode field: Did not find a field with number 8 in the descriptor
2024-03-27T16:22:07.932948Z DEBUG tokio-runtime-worker pact_plugin_driver::child_process: Plugin(protobuf, 247654, STDOUT) || 2024-03-27T16:22:07.932944Z WARN tokio-runtime-worker request{method=POST uri=<http://127.0.0.1:43899/io.pact.plugin.PactPlugin/VerifyInteraction> version=HTTP/2.0 headers={"te": "trailers", "content-type": "application/grpc", "authorization": Sensitive, "user-agent": "tonic/0.10.2"}}:decode_message: pact_protobuf_plugin::message_decoder: Was not able to decode field: Did not find a field with number 9 in the descriptor
I'm confused as to how we'd ever be able to deploy a change to the provider with new fields if the consumer is expecting an exact response rather than a minimum number of fields. I'd appreciate any help if I've either misunderstood or there is some kind of known issue with the protobuf pluginRinka Yoshida
03/30/2024, 12:58 AMEric Muller
04/08/2024, 3:59 PMRinka Yoshida
04/16/2024, 12:00 AMGetString()
method to Matcher type, so that instead of doing this:
grpcInteraction := struct {
PactProtoService string `json:"pact:proto-service"`
Request any `json:"request"`
Response any `json:"response,omitempty"`
}{
PactProtoService: "MyService",
Request: map[string]interface{}{
"user_id": "matching(integer, 100)",
},
Response: map[string]interface{}{
"first_name": "matching(type, 'random')",
"last_name": "matching(type, 'random')",
},
}
We can do below instead:
grpcInteraction := struct {
PactProtoService string `json:"pact:proto-service"`
Request string `json:"request"`
Response string `json:"response,omitempty"`
}{
PactProtoService: "MyService",
Request: StructMatcher{
"user_id": Integer(100),
}.ToString(),
Response: StructMatcher{
"first_name": Like("random"),
"last_name": Like("random"),
}.ToString(),
}
For example matchers.Integer(100).ToString()
would be {"specification":"3.0.0","pact:matcher:type":"integer","value":100}
.
I tried it on like which Integer users. However, I'm running into the issue of
Request to configure interaction failed: Failed to process protobuf: Field values must be configured with a string value, got Object {"pact:matcher:type": String("integer"), "specification": String("3.0.0"), "value": Number(100.0)}
I learnt that matching evaluation is not done recursively https://pact-foundation.slack.com/archives/C9UTHTFFB/p1712026514061369?thread_ts=1711759161.767629&cid=C9UTHTFFB, so I was wondering if you know there's another way we can get the string representation of match expressions? Or is there a way to bypass the recursion issue?