I’m having some trouble figuring out how to use th...
# pact-plugins
f
I’m having some trouble figuring out how to use the
pact_verifier_cli
to verify the pact I have now managed to produce using pactffi with the plugin I wrote to add a custom transport as I described before. I don’t know if I’m just expecting the wrong thing here, but what I hope is possible is to do the following: 1. Run
pact_verifier_cli
pointing it to the pact file (I’m just using a local file for now) 2.
pact_verifier_cli
loads the plugin and registers a custom transport 3.
pact_verifier_cli
verifies the interactions by sending requests over the custom transport, I imagine this would be implemented by the various interaction related rpcs in the plugin service So far I am seeing in the trace logs of the cli that it tries to send requests directly, ignoring the plugin. I’ve tried several arguments but haven’t gotten anywhere really. It’s loading the plugin fine, which is getting picked up from the pact file, and I see it in the printed catalogue entries:
Copy code
2025-01-31T11:35:33.117935Z DEBUG                 main verify_provider{provider_name="provider"}: pact_plugin_driver::catalogue_manager: Updated catalogue entries:
core/content-generator/binary
...
core/matcher/v4-semver
plugin/pact-messagebroker/transport/application/adsk_MessageBroker
Is what I’m trying to do supported? Or do I need to use pactffi to implement the provider tests?
y
what does your command look like? are you passing in a transport with the type of your plugin? and port it is running on?
f
I’ve tried several things, but this is what it looks like more or less:
Copy code
pact_verifier_cli --transport adsk_MessageBroker --file pacts/PactMessageBrokerConsumerDSL.tst-LibrariesCore.json -l debug -h 127.0.0.1 -p 50575
The thing that doesn’t make complete sense to me yet are the address / port arguments, my plugin will need to connect to a grpc relay service, but from the logs it looks like pact_verifier_cli is trying to connect directly somehow to that host/port
This is what the pact file interaction looks like (stripped the contents because IP)
Copy code
"interactions": [
    {
      "contents": {
        "contentType": "application/json",
        "encoded": false
      },
      "description": "libraries/getLibraries/request",
      "pending": false,
      "providerStates": [
        {
          "name": "I have a list of libraries"
        }
      ],
      "transport": "adsk_MessageBroker",
      "type": "Asynchronous/Messages"
    }
  ],
y
-p, --port <port> Provider port (defaults to protocol default 80/443) [env: PACT_PROVIDER_PORT=] --transport <transport> Provider protocol transport to use (http, https, grpc, etc.) [env: PACT_PROVIDER_TRANSPORT=] [default: http] --transports <transports> Allows multiple protocol transports to be configured (http, https, grpc, etc.) with their associated port numbers separated by a colon. For example, use --transports http:8080 grpc:5555 to configure both is the transport type in your plugin called asdk_xyz? running on localhost:50575? what does the pact entry look like for the plugin? can you try transports and pass the port with seperate by a colon https://github.com/YOU54F/pact-ruby-ffi/blob/8a41f7fcbb525e97baf27a5aaaf173be754381a7/Makefile#L79 example with grpc plugin. I note I’ve specified the provider name aswell. in your screenshot it is using the default name provider
f
well so the thing that’s running on localhost:50575 is a grpc relay service I wrote myself, the plugin’s start mock server rpc starts a client to connect to that relay and listen for requests to respond with mock responses
the actual transport is a C++ messagebroker that doesn’t use sockets or anything, it’s just an interface with subscribe and publish methods
so the messages described in the pact file are really one level deeper than the grpc relay
they’re payloads for the messages sent over grpc
I’m digging through the code looking for the debug statements and see something interesting…
y
ohh okay, i think this is similar to message pact mode where pact expects a http proxy to act as a broker for the messages. pact clients languages spin up this http proxy and usually map the contents from the payload to a consuming or providing function. pact verifier cli doesn’t provide a http proxy for messages. you would normally start this by setting a transport of type message:<port> https://github.com/pact-foundation/pact-reference/pull/211 maybe that is why you are getting calls to your localhost ( as its expecting that host to serve messages) happy if you want to share any logs in private.
f
Copy code
2025-01-31T12:05:27.891555Z DEBUG                 main verify_provider{provider_name="LibrariesCore"}:verify_interaction{interaction="libraries/getLibraries/request"}: pact_verifier: Executing provider states
2025-01-31T12:05:27.891568Z  INFO                 main verify_provider{provider_name="LibrariesCore"}:verify_interaction{interaction="libraries/getLibraries/request"}: pact_verifier: Running setup provider state change handler 'I have a list of libraries' for 'libraries/getLibraries/request'
2025-01-31T12:05:27.891574Z  WARN                 main verify_provider{provider_name="LibrariesCore"}:verify_interaction{interaction="libraries/getLibraries/request"}: pact_verifier::callback_executors: State Change ignored as there is no state change URL provided for interaction 
2025-01-31T12:05:27.891579Z DEBUG                 main verify_provider{provider_name="LibrariesCore"}:verify_interaction{interaction="libraries/getLibraries/request"}: pact_verifier: State Change: "ProviderState { name: "I have a list of libraries", params: {} }" -> Ok({})
2025-01-31T12:05:27.891588Z  INFO                 main verify_provider{provider_name="LibrariesCore"}:verify_interaction{interaction="libraries/getLibraries/request"}: pact_verifier: Running provider verification for 'libraries/getLibraries/request'
2025-01-31T12:05:27.891599Z DEBUG                 main verify_provider{provider_name="LibrariesCore"}:verify_interaction{interaction="libraries/getLibraries/request"}: pact_verifier: Verifying an asynchronous message (single shot)
2025-01-31T12:05:27.891627Z  INFO                 main verify_provider{provider_name="LibrariesCore"}:verify_interaction{interaction="libraries/getLibraries/request"}: pact_verifier::provider_client: Sending request to provider at <http://localhost:50575>
2025-01-31T12:05:27.891630Z DEBUG                 main verify_provider{provider_name="LibrariesCore"}:verify_interaction{interaction="libraries/getLibraries/request"}: pact_verifier::provider_client: Provider details = ProviderInfo { name: "LibrariesCore", protocol: "adsk_MessageBroker", host: "localhost", port: Some(50575), path: "", transports: [ProviderTransport { transport: "adsk_MessageBroker", port: Some(50575), path: None, scheme: None }] }
2025-01-31T12:05:27.891635Z  INFO                 main verify_provider{provider_name="LibrariesCore"}:verify_interaction{interaction="libraries/getLibraries/request"}: pact_verifier::provider_client: Sending request HTTP Request ( method: POST, path: /, query: None, headers: Some({"Content-Type": ["application/json"]}), body: Present(105 bytes, application/json) )
2025-01-31T12:05:27.891641Z DEBUG                 main verify_provider{provider_name="LibrariesCore"}:verify_interaction{interaction="libraries/getLibraries/request"}: pact_verifier::provider_client: body:
{"description":"libraries/getLibraries/request","providerStates":[{"name":"I have a list of libraries"}]}
2025-01-31T12:05:27.891693Z DEBUG                 main verify_provider{provider_name="LibrariesCore"}:verify_interaction{interaction="libraries/getLibraries/request"}: log: starting new connection: <http://localhost:50575/>    
2025-01-31T12:05:27.891743Z DEBUG tokio-runtime-worker hyper_util::client::legacy::connect::dns: resolving host="localhost"
2025-01-31T12:05:27.892523Z DEBUG                 main verify_provider{provider_name="LibrariesCore"}:verify_interaction{interaction="libraries/getLibraries/request"}: hyper_util::client::legacy::connect::http: connecting to 127.0.0.1:50575
2025-01-31T12:05:27.892866Z DEBUG                 main verify_provider{provider_name="LibrariesCore"}:verify_interaction{interaction="libraries/getLibraries/request"}: hyper_util::client::legacy::connect::http: connected to 127.0.0.1:50575
it’s printing “Verifying an asynchronous message (single shot)”
y
that is correct, your interaction is async message so there is no response ( aka single shot / one way )
so that log is showing that its sending a request to a state handler, on your sut, so you can do any setup / teardown
also contains the description of the message to map and return response
f
Thanks, yeah I think there’s something wrong then with my consumer test which I’ll have to figure out, because it’s supposed to be a sync interaction…
I’m focusing on the provider side now though so I’ll just manually edit the pact file for the moment and figure that issue out later 😕
So suppose I change the pact file so it’s a proper sync interaction, does that change the HTTP proxy part you explained?
I assume it doesn’t
y
I would be cautious in modifying the pact file, as sync message has a diff layout (req/res, and resp is an array) fyi I tried testing out the sync message func in v4 pact, and it will only deal with the first resp in an array of messages (not currently coded to support more on the verifier side) my working branch is here. https://github.com/pact-foundation/pact-workshop-message/compare/main...feat/sync_message here is an sync/message plugin pact https://github.com/YOU54F/pact-reference/blob/master/php/pacts/grpc-consumer-php-area-calculator-provider.json which uses the pact-protobuf-plugin - https://github.com/pactflow/pact-protobuf-plugin There is an example of it using https://github.com/pact-foundation/pact-plugins/tree/main/examples/gRPC/area_calculator/provider-go I believe you will still require a http proxy for your provider states. You can turn the providerStates entry to an empty array and it won’t make those calls. And I assume the client libraries would still utilise their proxy handlers to deal with provider states. Whether they are also expected to map the mes I haven’t looked too much into detail as to how provider states work in the plugin ecosystem but i’ll do some digging today.
f
I just rewrote the pact file, restructuring the request response indeed
and it looks like that too expects a HTTP proxy, yes
I’ll share the pact file and log via DM
y
ahh okay, so your provider will need to create the message based on the desc and return it in an encoded header. I believe the pact framework will then send that message to your plugin (which is configured for the transport type).
r
Bit late to this party, but this looks wrong
Copy code
pact_verifier_cli --transport adsk_MessageBroker
and
Copy code
plugin/pact-messagebroker/transport/application/adsk_MessageBroker
It looks like the plugin is registering the transport as
application/adsk_MessageBroker
and not
adsk_MessageBroker
f
I remember trying
application/adsk_MessageBroker
initially but for some reason I ended up removing the
application/
part and saw it somehow seemed better? I’ll revisit this now I have a better understanding of things
Getting there I think! I’ve edited the transport name to be
adsk_MessageBroker
everywhere since the cli doesn’t parse a transport name containing a
/
in it, and added a simple HTTP server to accept the POST requests for state changes and now it continues to hit the plugin’s
PrepareInteractionForVerification
rpc cool dog
I’m trying to figure out what would be the “best practice” at this point, I’m getting the pact JSON with an interaction key. For writing the mock server I relied on the pactffi functions to parse the pact from json, but I don’t quite see how to find the interaction by key looking at the C interface…
The pact json in the rpc’s request object looks like this:
Copy code
{
  consumer: { name: 'PactMessageBrokerConsumerDSL.tst' },
  interactions: [
    {
      description: 'libraries/getLibraries/request',
      key: '75dee697cdd774dd',
      pending: false,
      request: [Object],
      response: [Array],
      transport: 'adsk_MessageBroker',
      type: 'Synchronous/Messages'
    }
  ],
  metadata: {
    pactRust: { ffi: '0.4.26', mockserver: '1.2.11', models: '1.2.5' },
    pactSpecification: { version: '4.0' },
    plugins: [ [Object] ]
  },
  provider: { name: 'LibrariesCore' }
}
I see there’s a function to set the interaction key in pactffi:
Copy code
bool pactffi_set_key(InteractionHandle interaction, const char *value);
but I can’t find any getter for it
r
Can you raise a GitHub issue for that?
f
Sure