:thinking_face: I'm looking at Pact message (in py...
# general
m
šŸ¤” I'm looking at Pact message (in python specifically) and would like to get rid of the "...as there is no --provider-states-setup-url specified" warning and possibly add usage of states in somewhere. From a quick grep of "provider.states.setup.url" across pact-foundation and pactflow repos I can't find any examples (thought it was worth a try!) - it's used in "normal" http style Pact where the provider is spun up and the additional states endpoint can be spliced in somewhere, but I'm not sure how a nice pattern for message would look? Given the handler mapping approach for each of the provider states to implement, maybe it isn't actually necessary as this can be done via the defined handler itself? TLDR: does anyone have examples using provider states for message?
[for python, could do something lightweight with https://docs.python.org/3/library/http.server.html as per Beth's example https://stackoverflow.com/questions/47539344/pact-how-to-set-up-provider-states ...but I'm not sure that's how message is intended šŸ¤” ]
m
so the idea of provider states is definitely still applicable to messaging, albeit because in the case of messages the description is the unique key, you can often just use the description that also includes the state. I prefer not to do that though personally. But really, it’s exactly the same concept as HTTP. maintainer detail………. The way we handle this in several of the ā€œclientā€ languages (not the canonical Rust, Java or Ruby) is that each language spins up an HTTP server transparently to the user, and registers user provided ā€œstate handlerā€ functions with that server, and then gives that server URL to the verifier. When the state request comes in, the framework discovers the function to call, and delegates to the user provided function to ā€œsetupā€ the state. This way, it looks just like a regular function to the test author, but actually they are effectively implementing an HTTP handler.
m
šŸ¤”
I may be asking the wrong question
So, I'm aware of the sneaky http implementation approach, and have gone through chunks of the pact-python implementation, it doesn't seem to provide a way to hook in with the http server to give those states, hence I was assuming maybe the expectation is to spin up something outside
šŸ‘ 1
I should probably checkout some of the other implementations, could be something isn't complete on the python side
Looking at example-provider-js-sns to give you some js rather than my python, you have:
Copy code
const opts = {
    ...baseOpts,
    ...(process.env.PACT_URL ? pactChangedOpts : fetchPactsDynamicallyOpts),
    messageProviders: {
      "a product event update": () =>
        createEvent(new Product("42", "food", "pizza"), "UPDATED"),
    },
  };

  const p = new MessageProviderPact(opts);
So in this case, would you say if some actual state was required, instead of createEvent you would map to e.g. createEventWithState which 'did some stuff' to setup the state, and then made the call to createEvent, returning the payload (I think I am confusing myself wrt the hexagonal architecture approach - in this case the 'state' has already happened and as a result of the 'state' existing, the attributes for Product are provided) the handler is responsible for 'setting' any state based on the description, and also returning the appropriate payload for the message? -> in which case the provider-states-setup-url is no longer relevant and python shouldn't be WARN'ing that it's missing -> so possibly it would make sense to add an additional endpoint which does nothing, and send that to the verifier as a default provider-sates-setup-url as the warning isn't relevant?
make test
is failing for me on that example so I can't actually see how that one is expected to behave šŸ¤”
"you can often just use the description that also includes the state. I prefer not to do that though personally." could you elaborate on that a little please? šŸ™‚ as in do you mean having e.g. "A product exists with id 3" and then in your handler scraping out the 3 to create a Product? [and you prefer to not do that] ..or something else?
m
Let’s say you are testing an
Order
updated use case, which has these scenarios: 1. Order has new shipping information 2. Order has a new delivery note 3. … You could write the consumer tests for these in two ways: 1. The use case + scenario is all in a single description: ā€œAn order is updated with new shipping informationā€ ā€œAn order is updated with a new delivery noteā€ ā€œAn order is updated with ā€¦ā€ 2. The use case + scenario is separated by different provider states use case: ā€œAn order update eventā€ scenario 1: ā€œwith new shipping informationā€ scenario 2: ā€œwith a new delivery noteā€ scenario 3: ā€œā€¦ā€ I think 2 is more readable, but does rely on the state handlers instead of a single function that handles the whole thing
-> in which case the provider-states-setup-url is no longer relevant and python shouldn’t be WARN’ing that it’s missing
I agree that we should not have this warning though if there are no states to be setup. It’s not a useful warning - that could be fixed in the Ruby core I think (i.e. don’t warn if there are no states and that URL is not set)
cc @Beth (pactflow.io/Pact Broker/pact-ruby)
b
sure, thanks.
is it python that is warning or ruby?
m
It's coming from ruby I believe,
set_up_provider_state.rb
/
set_up_provider_state_spec.rb
I'll see if I can find other language examples to see what they do, in case it's a non-issue down to the way the python implementation sets things up
The use case + scenario is separated by different provider states
Do you know of any other examples demonstrating that approach for message? Don't worry about looking if not, (I'll have a look through in the morning) I haven't been able to see so far how that's intended to hook in šŸ¤” Only approach 1 with the single description as it's basically a map of description to handler
m
Not any public ones I don’t think, but conceptually I’m not sure why it’s different than HTTP? Aside from the fact that message implementations tend to be at the function level, rather than at an HTTP API level (spanning a whole live system)
m
possibly I've just completely missed something here šŸ˜„
m
I think you’re right, in that most examples I’ve seen don’t actually make use of states for messages, but the infrastructure is there to support it.
and if it helps, it doesn’t look like a thing unique to python
m
consumer side has something like
Copy code
(
    pact.given("A document deleted successfully")
    .expects_to_receive("Description with broker")
    .with_content(expected_event)
    .with_metadata({"Content-Type": "application/json"})
)
provider side has something like
Copy code
def test_verify_success():
    provider = MessageProvider(
        message_providers={
            "A document created successfully": document_created_handler,
            "A document deleted successfully": document_deleted_handler,
        },
        provider="ContentProvider",
        consumer="DetectContentLambda",
        pact_dir="pacts",
    )
    with provider:
        provider.verify()
so you can match the "A document deleted successfully" but all the happens is the document_deleted_handler method is called
the setting up of states is then just doing
Copy code
def _setup_states(self):
    message_handlers = {}
    for key, handler in self.message_providers.items():
        message_handlers[f'{key}'] = handler()

    resp = <http://requests.post|requests.post>(f'{self._proxy_url()}/setup',
                         verify=False,
                         json={"messageHandlers": message_handlers})
    assert resp.status_code == 201, resp.text
    return message_handlers
so calling that handler, and passing whatever it generates as a key -> payload
m
wait, that’s the exact opposite that I would expect:
Copy code
(
    pact.given("A document deleted successfully")  <--------- this is the state
    .expects_to_receive("Description with broker") <--------- this is the actual scenario, that should map to a handler
    .with_content(expected_event)
    .with_metadata({"Content-Type": "application/json"})
)
Copy code
def test_verify_success():
    provider = MessageProvider(
        message_providers={
            "A document created successfully": document_created_handler, <--- scenario handler, not a state
            "A document deleted successfully": document_deleted_handler,
        },
        state_handlers={
            "A document deleted successfully": some_func,                <---- in an ideal world, we want something like this
        },
        provider="ContentProvider",
        consumer="DetectContentLambda",
        pact_dir="pacts",
    )
    with provider:
        provider.verify()
m
only message_providers on the python side as far as I can see
m
so does it provide an option to provide a states setup URL?
m
not visibly, but it gets passed through to the verifier
šŸ¤”
I can see stateHandlers and messageProviders in pact-js, but then examples e.g. example-provider-js-sns only make use of messageProviders
m
I’m sure there are plenty of options we don’t use in all of the examples
we often get the feedback ā€œdo you have an example with feature xā€ and ā€œyour examples are too complicatedā€ (because they use all things)
can’t win that one!
m
šŸ˜„
for sure
m
I’m sure there is a lesson in there somewhere
also, are you pairing with Yousaf or something? Why are you both awake!
šŸ˜… 1
m
this isn't causing me a particular problem atm btw, so maybe something to come back to slightly more structured šŸ¤”
šŸ‘ 1
lol
I've been doing the usual going in circles a bit looking at trying to improve some of the examples and hitting the...but...different examples...things not working...head hurt šŸ˜„
šŸ˜… 1
😬 1
it is getting a bit early yes
šŸ“ 1
m
I’ve been doing the usual going in circles a bit looking at trying to improve some of the examples and hitting the...but...different examples...things not working...head hurt
Python or beyond?
m
yes
šŸ˜„
started adding one of Tim's message examples which was a bit more useful over to python but then tangenting into a bottomless rabbit hole of thinking how an uber example repo could work šŸ¤”
šŸ˜† 1
as per the, "do you have an example with feature xā€ and ā€œyour examples are too complicatedā€ šŸ™‚
I think for now I'll head for sleep and maybe the implications of "wait, that’s the exact opposite that I would expect" will make more sense in the morning šŸ˜‚
nod hmm yes 2
y
You are amazing Mike and we really appreciate you! Thanks for this, I knew it was going to be a rollercoaster, just wasn't sure how much šŸ˜…
m
ā˜ŗļø
There is still also the possibility I've completely misunderstood and gone off on one, but if that's the case it is still a good place to try and help with docs to understand!
1000000 1
Hadn't even heard of provider state injected šŸ¤” which may or may not have any relevance here
y
Aye I was about to say exactly that, whatever comes out of this though, will benefit others and help clear some of the confusion! As if you and me are confused, then I would imagine others are as well.
m
I think one thing we could do to save everyone’s confusion, is to have a clear set of features that are used as a ā€œthis is what a full implementation looks likeā€. This is a start, but it’s mostly only useful for maintainers that know what each of those things means, and means almost nothing to a brand new user.
It also of course can and does drift from reality (which I think you proposed some solutions to, Mike)
m
Yeah having a few chats with Yousaf about that, I have a cunning plan šŸ˜€
interesting 1
I wouldn't agree about meaning nothing to a user though, if it's presented right
Oh wait specific link rather than the concept nvm šŸ˜€
šŸ’Æ 1
I'd like to have something like that auto generated and testable with a whole load of dark magic šŸ˜€
m
yes! I think it could mean a lot, but right now unless you know what
Injecting values from provider state callbacks
it’s only useful for the basic/obvious features.
There’s no one place that says ā€œhere are all of our features and how to use themā€, with code snippets etc. and also blank spaces so it’s obvious ā€œoh, Python doesn’t support that. Maybe I can add it?ā€
m
That's what I'm working on šŸ™‚
šŸ‘ 1
chefkiss 1
I think it would be really useful but need to see what the barrier to entry could look like etc
Going a bit ot here but definitely worth carrying on the discussion!
😬 1
hm, I'm still not quite getting my head around message_provider and state_handler would work in a nice demonstratable flow šŸ¤” it makes sense to me for normal HTTP where the consumer is pulling from the provider, but where the provider is pushing to the consumer I'm finding it harder to "explain" maybe if the given/state is effectively adding to a queue/buffer object [i.e. add a payload onto a stack or something [that you can setup nicely in a test, similar to let's pretend there's a user database by setting up a dict in other examples] and then the handler is responsible for handling whatever is in the queue and pushing it out but that does seem like extra complexity without an understandable value šŸ¤” vs the slightly muddy just doing in one hit where the description/given is doing everything going to try and go through some of this with @Yousaf Nabi (pactflow.io) next week, not sure if it makes sense to look at progressing the feature list examples idea and do this as one of them [later], or try and make sense of message by itself first anyway šŸ¤”