Matt (pactflow.io / pact-js / pact-go)
Timothy Jones
12/05/2022, 11:35 PMSide bar: I think I like this way of doing it, because this way there isn’t a long lived branch off the side. Opinions thoughts welcome 🙂).YES
Timothy Jones
12/05/2022, 11:35 PMTimothy Jones
12/05/2022, 11:37 PM.usingPlugin({
plugin: 'matt',
version: '0.0.5',
})
I’m not a fan of this. Can’t it be done at the Pact level, when you initialise the contract?Timothy Jones
12/05/2022, 11:37 PMSlackbot
12/05/2022, 11:37 PMTimothy Jones
12/05/2022, 11:38 PMTimothy Jones
12/05/2022, 11:39 PMTimothy Jones
12/05/2022, 11:40 PMTimothy Jones
12/05/2022, 11:40 PMSlackbot
12/05/2022, 11:40 PMTimothy Jones
12/05/2022, 11:40 PMTimothy Jones
12/05/2022, 11:43 PMTimothy Jones
12/05/2022, 11:46 PMstartTransport
begin the listener? I think you can avoid the need for this callTimothy Jones
12/05/2022, 11:47 PMaddSynchronousInteraction
- Just make PactJS think they’re both asynchronousTimothy Jones
12/05/2022, 11:48 PMaddInteraction
implies startTransport
Timothy Jones
12/05/2022, 11:48 PMTimothy Jones
12/05/2022, 11:50 PMwithPluginContents
is the interaction description? I would put the content in thereTimothy Jones
12/05/2022, 11:51 PMpact
.addInteraction('a MATT message')
.interaction(mattMessage, 'application/matt')
.config({ host: HOST })
.executeTest(async (tc) => {
const message = await sendMattMessageTCP('hellotcp', HOST, tc.port);
expect(message).to.eq('tcpworld');
});
Timothy Jones
12/05/2022, 11:53 PMsetup(
willSendHttpInteraction({
request: {
method: 'GET',
path: '/health',
},
response: { status: httpStatus(['4XX', '5XX']) },
}),
{ port } // config is optional and inherited from root contract
)
Timothy Jones
12/05/2022, 11:55 PMsetup(
willSendMattMessage({
message: mattMessage,
}),
{ host: HOST }
)
Timothy Jones
12/05/2022, 11:56 PMstartContract({
consumerName: 'matt response consumer',
providerName: 'matt response provider',
}).withPlugin(matt)
Timothy Jones
12/05/2022, 11:56 PMTimothy Jones
12/06/2022, 12:00 AMsetup(
getMatcher('willSendMattMessage')({
.....
)
Matt (pactflow.io / pact-js / pact-go)
Matt (pactflow.io / pact-js / pact-go)
DoesI’d like to. I think we could put it on thebegin the listener? I think you can avoid the need for this callstartTransport
PactV4
constructor. One challenge is that not all plugins provide transports and they aren’t always going to be used in all interactions.Matt (pactflow.io / pact-js / pact-go)
Similarly, I don’t think you need to make a distinction here:That would be very hard to retrofit into the model now I think- Just make PactJS think they’re both asynchronousaddSynchronousInteraction
Matt (pactflow.io / pact-js / pact-go)
No one asked you, slackbot.I’m going to delete that entry. Not sure who did it on such a wide string match (hopefully not me!)
Matt (pactflow.io / pact-js / pact-go)
No, that accepts the plugin configuration (which is the contents + whatever config the plugin itself takes)is the interaction description? I would put the content in therewithPluginContents
Matt (pactflow.io / pact-js / pact-go)
Matt (pactflow.io / pact-js / pact-go)
return pact
.addSynchronousInteraction('a MATT message') // <- I'm testing a req/response message with this description/scenario name
.usingPlugin({ // <- I need a plugin for this interaction
plugin: 'matt',
version: '0.0.5',
})
.withPluginContents(mattMessage, 'application/matt') // <- interaction contents
.startTransport('matt', HOST) // <- use a mock server for this test please
.executeTest(async (tc) => { // <- transport specific test execution. Will validate the mock server was used, and delegate to the plugin mock server
const message = await sendMattMessageTCP('hellotcp', HOST, tc.port);
expect(message).to.eq('tcpworld');
});
Matt (pactflow.io / pact-js / pact-go)
Matt (pactflow.io / pact-js / pact-go)
executeTest
function and also ensuring the right version of a plugin is used? Plugins unfortunately introduce another versioning problem, and we made the decision to be explicit about versioning, rather than implicit (i.e. discovering). We could change that, but we have already seen issues on the provider side where the driver loaded the most recent version of a plugin to verify and it was incompatible with what was used on the consumer side. We fixed that to ensure the same version is used, because you can imagine the classes of bugs we’d need to address if we didn’t.Timothy Jones
12/06/2022, 2:30 AMThat would be very hard to retrofit into the model now I thinkYes. I’m afraid this is my general position on the plugins
Timothy Jones
12/06/2022, 2:30 AMTimothy Jones
12/06/2022, 2:30 AMTimothy Jones
12/06/2022, 2:30 AMTimothy Jones
12/06/2022, 2:31 AMTimothy Jones
12/06/2022, 2:32 AMPlugins unfortunately introduce another versioning problem, and we made the decision to be explicit about versioning, rather than implicit (i.e. discovering).I don’t think they do. I think this problem already exists inside Pact, with matchers.
Timothy Jones
12/06/2022, 2:34 AMTimothy Jones
12/06/2022, 2:34 AMTimothy Jones
12/06/2022, 2:34 AMTimothy Jones
12/06/2022, 2:35 AMTimothy Jones
12/06/2022, 2:35 AMTimothy Jones
12/06/2022, 2:35 AMTimothy Jones
12/06/2022, 2:36 AMTimothy Jones
12/06/2022, 2:37 AMTimothy Jones
12/06/2022, 2:37 AMTimothy Jones
12/06/2022, 2:43 AMAny ideas on how we could do that, whilst preserving the dynamic nature of the executeTest functionSo, what I’m doing, is there’s a test context that knows all the configuration. This is passed recursively down the matcher tree, and some matchers might write to it. This means that configuration at the matcher (ie, the interaction) level is the same as at the contract level.
also ensuring the right version of a plugin is used?I think this is the responsibility of the package manager. When you register a plugin (ie, connect it to Pact) you can maybe set a semver string for the versions that you will accept.
Plugins unfortunately introduce another versioning problem, and we made the decision to be explicit about versioning, rather than implicit (i.e. discovering).You need to record the version in the contract, of course, and you need to be able to interpret it at the other end to make sure that your version of the plugin is acceptable.
We could change that, but we have already seen issues on the provider side where the driver loaded the most recent version of a plugin to verify and it was incompatible with what was used on the consumer side.This is the fault of the plugin author, yes? You’d need to say what “breaking change” means, but with semver you could just reject the plugin if you expected it to be incompatible. Yes, it’s a bug if a plugin author violates that, but I mean, this is a good reason not to.
We fixed that to ensure the same version is used, because you can imagine the classes of bugs we’d need to address if we didn’t.This feels like it’s unnecessarily strict. I think it’s also not a good look for a contract testing framework to say “we couldn’t figure out how this contract compatibility should work, so we enforce that you use the same version”
Timothy Jones
12/06/2022, 2:47 AMTimothy Jones
12/06/2022, 2:52 AMmatch
functions for testing whether their data matches, and strip
functions for removing the matchers and returning the example. This means that even fromProviderState
is a matcher - when asked to match it does an exact match against the data returned from provider state, and when asked to strip, it returns that data.Timothy Jones
12/06/2022, 2:53 AMMatt (pactflow.io / pact-js / pact-go)
This feels like it’s unnecessarily strict. I think it’s also not a good look for a contract testing framework to say “we couldn’t figure out how this contract compatibility should work, so we enforce that you use the same version” (edited)I think it did/does support something like that, so we could probably improve that configuration option.
Matt (pactflow.io / pact-js / pact-go)
This idea is of everything being a matcher is so powerful. For example, Matchers haveit definitely sounds elegant, but that ship has sailed (at least for this version of Pact). I’d be interested to see how we might be able to adopt/migrate to something like that and any consequences of doing so, but it’s definitely beyond my technical know how to suggest it. Also, it is a bit of a paradigm shift from Pact as it is today and (possibly) it is best to have this not be in Pact, and let the marketplace of ideas choose the winner. That is to say, it may well be the better paradigm and have it run alongside tools like Pact might be the best way to validate the idea cc @uglyog who might be keen to learn more about this line of thinking.functions for testing whether their data matches, andmatch
functions for removing the matchers and returning the example. This means that evenstrip
is a matcher - when asked to match it does an exact match against the data returned from provider state, and when asked to strip, it returns that data. (edited)fromProviderState
Matt (pactflow.io / pact-js / pact-go)
I think it’s important for the plugin framework to reduce complexity, rather than increase itI’m not sure I agree with this statement, albeit of course in general, reducing complexity usually improves DX and that is a good thing. What I’m saying is that we don’t actually have a tabula rasa here - we are working with an existing toolchain, ecosystem and user base. These conditions mean we are necessarily constrained in certain choices
Matt (pactflow.io / pact-js / pact-go)
I’m not a fan of this. Can’t it be done at the Pact level, when you initialise the contract?Coming back to this. I think we probably could and then just behind the scenes add them to the interaction, however as I said, it’s possible different interactions will need different transports (because you might have an HTTP and gRPC or whatever endpoint) so I’m not sure we can work around that
startTransport
fn.Timothy Jones
12/06/2022, 11:19 PMTimothy Jones
12/06/2022, 11:27 PMan existing toolchain, ecosystem and user base.That’s true, but the pact file is opaque, so you have a lot of flexibility - unless concepts get surfaced unnecessarily to the user
Matt (pactflow.io / pact-js / pact-go)
Matt (pactflow.io / pact-js / pact-go)
That’s true, but the pact file is opaque, so you have a lot of flexibility - unless concepts get surfaced unnecessarily to the user
These concepts are already exposed to the user, that's exactly the point. A case could be made (see what I did there?) That Pact != Pact Specification. In fact that's really the case, we already know there are many behaviours of Pact that aren't modelled in the spec at all. Perhaps we should be taking a wider lens of the pact spec to support wider use cases (such as different contract testing solutions?)
Timothy Jones
12/07/2022, 12:56 AMTimothy Jones
12/07/2022, 12:56 AMMatt (pactflow.io / pact-js / pact-go)
Timothy Jones
12/07/2022, 12:58 AMMatt (pactflow.io / pact-js / pact-go)
Timothy Jones
12/07/2022, 12:58 AMTimothy Jones
12/07/2022, 12:58 AMTimothy Jones
12/07/2022, 12:59 AMMatt (pactflow.io / pact-js / pact-go)
Matt (pactflow.io / pact-js / pact-go)
Timothy Jones
12/07/2022, 1:00 AMMatt (pactflow.io / pact-js / pact-go)
Timothy Jones
12/07/2022, 1:00 AMMatt (pactflow.io / pact-js / pact-go)