Hi All! I am trying to understand how pact states ...
# pact-python
t
Hi All! I am trying to understand how pact states work and how it can be implemented. Input data: • My provider uses Python 3.10 and FastAPI • I have two consumer tests, one to get one specific event, another one to get all events. And pact is generated. I have to ensure that at least one event exists in my local database, so I need to use pact states. I would like to insert just one event to my DB. So, I am doing the following: I’m trying to implement post endpoint as it’s shown here. I am mapping just one state for my getting a specific endpoint (exactly for this endpoint I need a specific event existed in the DB). My code looks like:
Copy code
from fastapi import APIRouter
from pydantic import BaseModel

from eventhub.core.events.types import EventLifecycle, EventStatus
from tests.e2e.conftest import team_factory, event_factory

pact_router = APIRouter()

STARTED_EVENT_ID = "ba5ec002-6fac-4285-bbbb-46c8801fb283"
TEAM_ID = "dummy_tid"


class ProviderState(BaseModel):
    state: str  # noqa: E999


@pact_router.post("/_pact/provider_states")
async def provider_states(provider_state: ProviderState):
    mapping = {
        "I can get a specific event": setup_started_event
    }
    mapping[provider_state.state]()

    return {"result": mapping[provider_state.state]}


async def setup_started_event(db_session):
    await team_factory(db_session, team_id=TEAM_ID)
    await event_factory(
        db_session, STARTED_EVENT_ID, team_id=TEAM_ID, life_cycle=EventLifecycle.STARTED, status=EventStatus.PUBLISHED
    )
The application is running and I am trying to verify my pact by running:
Copy code
pact-verifier --provider-base-url=<http://localhost:8000> --pact-url=./pacts/mousedeer-events-consumer-eventhub-provider.json --provider-states-setup-url=<http://localhost:8000/_pact/provider_states>
But I am getting the errors:
Copy code
Failures:

  1) Verifying a pact between mousedeer-events-consumer and eventhub-provider Given I can get a specific event a request to get a specific event with GET /events/ba5ec002-6fac-4285-bbbb-46c8801fb283 returns a response which has status code 200
     Failure/Error: set_up_provider_states interaction.provider_states, options[:consumer]

     Pact::ProviderVerifier::SetUpProviderStateError:
       Error setting up provider state 'I can get a specific event' for consumer 'mousedeer-events-consumer' at <http://localhost:8000/_pact/provider_states>. response status=404 response body={"detail":"Not Found"}

...
...
...
 6) Verifying a pact between mousedeer-events-consumer and eventhub-provider Given I can get all events a request to get all events with GET /events?limit=10&name=&offset=0 returns a response which includes headers "Content-Type" which equals "application/json"
     Failure/Error: set_up_provider_states interaction.provider_states, options[:consumer]

     Pact::ProviderVerifier::SetUpProviderStateError:
       Error setting up provider state 'I can get all events' for consumer 'mousedeer-events-consumer' at <http://localhost:8000/_pact/provider_states>. response status=404 response body={"detail":"Not Found"}
First, I am wondering why it tries to setup a state for my all two endpoints if I provided state just for one? Second, does this setup looks fine or something is wrong? Unfortunately, I don’t fully understand how post request should be implemented for pact states (as I can see it should be done according to this), so I just tried to use your example. Third, do I understand correctly that in my case I should seed my local DB (the app and DB is running against localhost in my case)? or I can use fakedb as in your example?
m
The states are defined in the pact file. It will always try to setup the states, even if you haven’t defined them in your test.
Your state mapping function should be able to deal with unknown states, because consumers may add new ones without the provider knowing about them
Third, do I understand correctly that in my case I should seed my local DB (the app and DB is running against localhost in my case)? or I can use fakedb as in your example? (edited)
Pact is agnostic to how you setup the state - stubs, mocks, database seeding etc. The provider should get to choose the method that is most convenient for it, rather than have the consumer specify how the provider does that (like in e2e tests).
I tend to stub/mock the repository layer, personally.
👀 1
j
@Matt (pactflow.io / pact-js / pact-go) Does this mean we don’t need to create
@pact_router.post("/_pact/provider_states")
in our provider side of tests?
The states are defined in the pact file. It will always try to setup the states, even if you haven’t defined them in your test.
m
In the case of Python, you probably still will need these routes for your tests, to be able to receive the state setup messages and respond accordingly
j
I am running my provider service in a docker container where my provider verification tests are, not sure from the examples what do I need to return?
m
a
200
ok is all you need. The point of the provider states endpoint is to setup mocks/data to be able to respond to a scenario-
e.g. if the state is
User A exists
when the state handler is executed, you should ensure that User A exists. This could be injecting data into a database, setting up a mock etc.
j
taking this conversation to the message thread I posted
👍 1