pact-foundation / pact-python

Python version of Pact. Enables consumer driven contract testing, providing a mock service and DSL for the consumer project, and interaction playback and verification for the service provider project.
http://pact.io
MIT License
567 stars 137 forks source link

Callbacks in python #277

Closed NicolasAgra closed 1 year ago

NicolasAgra commented 2 years ago

I need to handle variables inside the consumer file but I am not finding any documentation on how to do it. I found this reference but it seems it is only available for java. Is this feature not present for python?

elliottmurray commented 2 years ago

I was actually unaware of this feature in JVM. It isn't supported right now.

NicolasAgra commented 2 years ago

Thank you for the quick response Elliot. I really need this to handle a token authentication in the contracts headers, do you know any workaround?

elliottmurray commented 2 years ago

So the workaround normally is to make the system more testable normally by disabling or being able to switch out the auth strategy. This doesn't fit every use case but remember Pact is not here to test your auth strategy but your contract.

mefellows commented 2 years ago

Your use case doesn't really match what that functionality in JVM was created for.

Have you read: https://docs.pact.io/provider/handling_auth/?

Provider states is probably your best bet here.

NicolasAgra commented 2 years ago

Yes, the token is only a header, so as long as this value is well written in the contract and it is valid, it should work. The problem is that the token expires over time, so without any dynamically way to resolve this (in python-pact), it is a blocker. However, @elliottmurray is correct with his comment, I should have a way in the Provider to bypass the authentication.

mefellows commented 2 years ago

Yes, the token is only a header, so as long as this value is well written in the contract and it is valid, it should work. The problem is that the token expires over time, so without any dynamically way to resolve this (in python-pact), it is a blocker. However, @elliottmurray is correct with his comment, I should have a way in the Provider to bypass the authentication.

That's only one of the strategies you can employ here.

The simplest is to use provider states and test the various scenarios:

  1. "a valid authentication token" - should result in a 200 OK
  2. "an invalid authentication token" - should result in a 401 Bad Request

Your provider code should be modular enough to stub any authentication code to allow those scenarios to be tested independently, rather than relying on a valid token to exist in a contract (personally, I think that's the worst of all of the strategies).

NicolasAgra commented 2 years ago

Yes, the token is only a header, so as long as this value is well written in the contract and it is valid, it should work. The problem is that the token expires over time, so without any dynamically way to resolve this (in python-pact), it is a blocker. However, @elliottmurray is correct with his comment, I should have a way in the Provider to bypass the authentication.

That's only one of the strategies you can employ here.

The simplest is to use provider states and test the various scenarios:

  1. "a valid authentication token" - should result in a 200 OK
  2. "an invalid authentication token" - should result in a 401 Bad Request

Your provider code should be modular enough to stub any authentication code to allow those scenarios to be tested independently, rather than relying on a valid token to exist in a contract (personally, I think that's the worst of all of the strategies).

Yes I agree. Convincing the development team to implement a mechanism to bypass authorization is another story :laughing: . However, I am in the same dilemma with another data residing in the payloads that requires a value from a previous call. Let's suppose I create an object with method POST@/foo and the response returns {"id": "123"}, then I have another service GET@/{id} which requires a valid {id} in the system. Without callbacks I am in the same situation.

mefellows commented 2 years ago

I understand! In case it's not clear, Pact testing is really aimed at the development teams and should be authored when you write the code (think TDD). But I understand you might be retro fitting this.

However, I am in the same dilemma with another data residing in the payloads that requires a value from a previous call. Let's suppose I create an object with method POST@/foo and the response returns {"id": "123"}, then I have another service GET@/{id} which requires a valid {id} in the system. Without callbacks I am in the same situation.

The solution to this problem is the same as above - provider states.

You should not have Pact tests that depend on the outcome/output of a previous test. Each test should be completely isolated from one another. You should have two tests, and use a provider state for the scenario that "id 123 exists".

I would suggest reading up on provider states on our docs, and also consider running one of the tutorials/workshops.

YOU54F commented 1 year ago

As stated provider states and the ability to add custom_headers on the provider side exists in pact-python today.

There is an ask for requestFilters as part of #107 which is the no 4 opt on our strategies for provider auth.

https://docs.pact.io/provider/handling_auth

Will close as a dupe of that filter request.

Provider states with parameters with become available in the next iteration of pact-python following https://github.com/pact-foundation/pact-python/pull/367