https://pact.io logo
Join Slack
Powered by
# pact-python
  • m

    Mike Geeves

    06/16/2022, 8:28 PM
    [disclaimer: I have limited usage within teams so relatively light on useful suggestions] re the
    /_pact/provider_states"
    endpoint, I quite liked that pattern, iirc I did have a conversation about why you have to have this other way of starting it along the lines of "well, alternatively we could have the /_pact/provider_states_ endpoint exposed alongside the other routes, where people could then wipe the database from a single unauthenticated call" -> "ohhhhh! I get it now, this way is better" 🙂 but I agree having to explain it one person at a time is tricky to scale
    👍 1
  • m

    Mike Geeves

    06/16/2022, 8:33 PM
    there is quite a lot to add in and get your head around, I'm not sure it's much harder than "everything else", once you've got that first example in there and a pattern to follow 🤔
  • m

    Mike Geeves

    06/18/2022, 10:50 AM
    Looking at the message examples, but had some blockers of getting a dev environment going again 🙂 I had this previously but worked around by just putting the .tar.gz file where it expected to be - the logic had download_bin_path always being set so the route to go get could never happen
  • m

    Mike Geeves

    06/18/2022, 10:58 AM
    eugh. which is doing the...build failed first time on my repo, but worked with a retry 😞
    e
    m
    • 3
    • 13
  • m

    Mike Geeves

    06/18/2022, 10:58 AM
    [when running some of the e2e tests, no perms to hit retry]
  • j

    John Williams

    06/22/2022, 10:08 AM
    I wrote a consumer test in Python, but when i run it in pytest nothing happens? Do i need to have the broker running locally? Actually Im gettting this error trying to get it to run:
    Copy code
    Hint: make sure your test modules/packages have valid Python names.
    Traceback:
    /opt/homebrew/Cellar/python@3.9/3.9.13_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/importlib/__init__.py:127: in import_module
        return _bootstrap._gcd_import(name[level:], package, level)
    consumer.py:9: in <module>
        from web.api.api import cancel_order
    E   ModuleNotFoundError: No module named 'web'
    Im not too familiar with python
  • j

    John Williams

    06/22/2022, 12:40 PM
    If someone can help me out my test is looking like this:
    Copy code
    import atexit
    import logging
    import os
    import requests
    import pytest
    import subprocess
    
    from pact import Consumer, Like, Provider, Term, Format
    
    from orders_service.orders import Order
    
    log = logging.getLogger(__name__)
    logging.basicConfig(level=<http://logging.INFO|logging.INFO>)
    
    # If publishing the Pact(s), they will be submitted to the Pact Broker here.
    # For the purposes of this example, the broker is started up as a fixture defined
    # in conftest.py. For normal usage this would be self-hosted or using Pactflow.
    PACT_BROKER_URL = "<https://johnwilliams.pactflow.io/>"
    PACT_BROKER_USERNAME = "johnwilliams"
    PACT_BROKER_PASSWORD = "secure123"
    
    # Define where to run the mock server, for the consumer to connect to. These
    # are the defaults so may be omitted
    PACT_MOCK_HOST = "localhost"
    PACT_MOCK_PORT = 1234
    
    # Where to output the JSON Pact files created by any tests
    PACT_DIR = os.path.dirname(os.path.realpath(__file__))
    
    
    @pytest.fixture
    def consumer() -> Order.cancel:
        return Order.cancel("http://{host}:{port}".format(host=PACT_MOCK_HOST, port=PACT_MOCK_PORT))
    
    
    @pytest.fixture(scope="session")
    def pact(request):
        """Setup a Pact Consumer, which provides the Provider mock service. This
        will generate and optionally publish Pacts to the Pact Broker"""
        # When publishing a Pact to the Pact Broker, a version number of the Consumer
        # is required, to be able to construct the compatability matrix between the
        # Consumer versions and Provider versions
        # version = request.config.getoption("--publish-pact")
        # publish = True if version else False
    
        pact = Consumer("UserServiceClient", version=1).has_pact_with(
            Provider("UserService"),
            host_name=PACT_MOCK_HOST,
            port=PACT_MOCK_PORT,
            pact_dir=PACT_DIR,
            publish_to_broker=True,
            broker_base_url=PACT_BROKER_URL,
            broker_username=PACT_BROKER_USERNAME,
            broker_password=PACT_BROKER_PASSWORD,
        )
    
        pact.start_service()
    
        # Make sure the Pact mocked provider is stopped when we finish, otherwise
        # port 1234 may become blocked
        atexit.register(pact.stop_service)
    
        yield pact
    
        # This will stop the Pact mock server, and if publish is True, submit Pacts
        # to the Pact Broker
        pact.stop_service()
    
        # Given we have cleanly stopped the service, we do not want to re-submit the
        # Pacts to the Pact Broker again atexit, since the Broker may no longer be
        # available if it has been started using the --run-broker option, as it will
        # have been torn down at that point
        pact.publish_to_broker = False
    
    
    def test_cancel_scheduled_order(pact, consumer):
            expected = \
           {
              "id": "1e54e244-d0ab-46ed-a88a-b9e6037655ef",
              "order": [
                {
                  "product": "coffee",
                  "quantity": 1,
                  "size": "small"
                }
              ],
              "scheduled": "Wed, 22 Jun 2022 09:21:26 GMT",
              "status": "cancelled"
            }
    
    
            (pact
            .given('A scheduled order exists and it is not cancelled already')
            .upon_receiving('a request for cancellation')
            .with_request('get', f'<http://localhost:3001/kitchen/schedule/{Like(12343)}/cancel>')
            .will_respond_with(200, body=Like(expected)))
    
            with pact:
                response = consumer.cancel()
                assert response['status'] == "cancelled"
            
            pact.verify()
  • j

    John Williams

    06/22/2022, 12:41 PM
    Im not sure about the fixture part and the following:
    Copy code
    with pact:
                response = consumer.cancel()
                assert response['status'] == "cancelled"
  • j

    John Williams

    06/22/2022, 12:41 PM
    Im trying to write a test for the cancel function :
    Copy code
    class Order:
        def __init__(self, id, created, items, status, schedule_id=None,
                     delivery_id=None, order_=None):
            self._order = order_
            self._id = id
            self._created = created
            self.items = [OrderItem(**item) for item in items]
            self._status = status
            self.schedule_id = schedule_id
            self.delivery_id = delivery_id
    
        @property
        def id(self):
            return self._id or self._order.id
    
        @property
        def created(self):
            return self._created or self._order.created
    
        @property
        def status(self):
            return self._status or self._order.status
    
        def cancel(self):
            if self.status == 'progress':
                response = requests.get(
                    f'<http://localhost:3001/kitchen/schedule/{self.schedule_id}/cancel>',
                    data={'order': self.items}
                )
                if response.status_code == 200:
                    return
                raise APIIntegrationError(
                    f'Could not cancel order with id {self.id}'
                )
            if self.status == 'delivery':
                raise InvalidActionError(f'Cannot cancel order with id {self.id}')
  • j

    John Williams

    06/22/2022, 12:42 PM
    and the error i get is:
    Copy code
    self = '<http://localhost:1234>'
    
        def cancel(self):
    >       if self.status == 'progress':
    E       AttributeError: 'str' object has no attribute 'status'
    
    ../orders_service/orders.py:48: AttributeError
    🙌 1
    m
    • 2
    • 12
  • j

    John Williams

    06/23/2022, 6:15 AM
    any idea what the issue is with this:
    Copy code
    I, [2022-06-23T16:14:14.635384 #84978]  INFO -- : Cleared interactions
    I, [2022-06-23T16:14:14.663371 #84978]  INFO -- : Registered expected interaction GET <http://127.0.0.1:5000/kitchen/schedule/><pact.matchers.Like object at 0x104e7b220>/cancel
    D, [2022-06-23T16:14:14.663741 #84978] DEBUG -- : {
      "description": "a request for cancellation",
      "providerState": "A scheduled order exists and it is not cancelled already",
      "request": {
        "method": "get",
        "path": "<http://127.0.0.1:5000/kitchen/schedule/><pact.matchers.Like object at 0x104e7b220>/cancel"
      },
      "response": {
        "status": 200,
        "headers": {
        },
        "body": {
          "json_class": "Pact::SomethingLike",
          "contents": {
            "id": "1e54e244-d0ab-46ed-a88a-b9e6037655ef",
            "order": [
              {
                "product": "coffee",
                "quantity": 1,
                "size": "small"
              }
            ],
            "scheduled": "Wed, 22 Jun 2022 09:21:26 GMT",
            "status": "cancelled"
          }
        }
      },
      "metadata": null
    }
    W, [2022-06-23T16:14:14.721886 #84978]  WARN -- : Verifying - actual interactions do not match expected interactions. 
    Missing requests:
    	GET <http://127.0.0.1:5000/kitchen/schedule/><pact.matchers.Like object at 0x104e7b220>/cancel
    
    
    
    W, [2022-06-23T16:14:14.721961 #84978]  WARN -- : Missing requests:
    	GET <http://127.0.0.1:5000/kitchen/schedule/><pact.matchers.Like object at 0x104e7b220>/cancel
    m
    m
    • 3
    • 43
  • j

    John Williams

    06/25/2022, 1:59 PM
    Hey all just a quick question. I have the following code:
    Copy code
    """pact test for OrderService provider"""
    
    import logging
    
    import pytest
    import os
    
    from pact import Verifier
    from dotenv import load_dotenv
    load_dotenv()
    
    log = logging.getLogger(__name__)
    logging.basicConfig(level=<http://logging.INFO|logging.INFO>)
    
    # For the purposes of this example, the broker is started up as a fixture defined
    # in conftest.py. For normal usage this would be self-hosted or using Pactflow.
    PACT_BROKER_BASE_URL=os.getenv('PACT_USER')
    PACT_BROKER_TOKEN=os.getenv('PACT_BROKER_TOKEN')
    
    # For the purposes of this example, the Flask provider will be started up as part
    # of run_pytest.sh when running the tests. Alternatives could be, for example
    # running a Docker container with a database of test data configured.
    # This is the "real" provider to verify against.
    PROVIDER_HOST = "localhost"
    PROVIDER_PORT = 5000
    PROVIDER_URL = f"http://{PROVIDER_HOST}:{PROVIDER_PORT}"
    
    
    @pytest.fixture
    def broker_opts():
        return {
            # "broker_username": PACT_BROKER_USERNAME,
            # "broker_password": PACT_BROKER_PASSWORD,
            "broker_url": PACT_BROKER_BASE_URL,
            "publish_version": "1",
            "publish_verification_results": True,
            "broker_base_url":PACT_BROKER_BASE_URL,
            "broker_token":PACT_BROKER_TOKEN
        }
    
    
    def test_user_service_provider_against_broker(broker_opts):
        verifier = Verifier(provider="KitchenService", provider_base_url=PROVIDER_URL)
    
        # Request all Pact(s) from the Pact Broker to verify this Provider against.
        # In the Pact Broker logs, this corresponds to the following entry:
        # PactBroker::Api::Resources::ProviderPactsForVerification -- Fetching pacts for verification by UserService -- {:provider_name=>"UserService", :params=>{}}
        success, logs = verifier.verify_with_broker(
            **broker_opts,
            verbose=True,
            provider_states_setup_url=f"{PROVIDER_URL}/_pact/provider_states",
            enable_pending=False,
        )
        # If publish_verification_results is set to True, the results will be
        # published to the Pact Broker.
        # In the Pact Broker logs, this corresponds to the following entry:
        #   PactBroker::Verifications::Service -- Creating verification 200 for \
        #   pact_version_sha=c8568cbb30d2e3933b2df4d6e1248b3d37f3be34 -- \
        #   {"success"=>true, "providerApplicationVersion"=>"3", "wip"=>false, \
        #   "pending"=>"true"}
    
        # Note:
        #  If "successful", then the return code here will be 0
        #  This can still be 0 and so PASS if a Pact verification FAILS, as long as
        #  it has not resulted in a REGRESSION of an already verified interaction.
        #  See <https://docs.pact.io/pact_broker/advanced_topics/pending_pacts/> for
        #  more details.
        assert success == 0
    
    
    def test_KitchenService_provider_against_pact():
        verifier = Verifier(provider="KitchenService", provider_base_url=PROVIDER_URL)
    
        # Rather than requesting the Pact interactions from the Pact Broker, this
        # will perform the verification based on the Pact file locally.
        #
        # Because there is no way of knowing the previous state of an interaction,
        # if it has been successful in the past (since this is what the Pact Broker
        # is for), if the verification of an interaction fails then the success
        # result will be != 0, and so the test will FAIL.
        output, _ = verifier.verify_pacts(
            "../pacts/OrderService-KitchenService.json",
            verbose=False,
            provider_states_setup_url="{}/_pact/provider_states".format(PROVIDER_URL),
        )
    
        assert output == 0
    but I get the error: I did create a new endpoint that posts the data to the KitchenService on the endpoint: So im not quite sure what the error means?
    m
    m
    • 3
    • 40
  • j

    John Williams

    07/01/2022, 2:53 AM
    Is provider-state-injected something which is available with Python?
    👎 1
  • t

    tmegha

    07/06/2022, 7:18 AM
    Hi All, I am completely new to pact so decided to start with the examples given in pact-python repo.I cloned the repo and am running the pact broker via docker compose as mentioned in the readme. I intermittently get the error
    container for service "broker_app" is unhealthy
    What can be the issue with the container? Also, on running
    ./run_pytest.sh
    , with a healthy broker, I get the following error:-
    Copy code
    docker.errors.DockerException: Error while fetching server API version: ('Connection aborted.', ConnectionRefusedError(61, 'Connection refused'))
    What can be causing the issue? Any insights will be greatly appreciated. TIA
  • e

    Elliott Murray

    07/06/2022, 7:23 AM
    This is a docker issue by the looks of it. Are there some log messages around the broker app start up?
    t
    • 2
    • 6
  • a

    Adam Anderson

    07/08/2022, 1:19 AM
    Hello, is there any support right now for BDCT in Pactflow using pact-python?
    ✅ 1
    m
    t
    • 3
    • 8
  • g

    GitHub

    07/18/2022, 3:07 PM
    #298 pact-message return code ignored New issue created by mikegeeves In message_pact.py we have:
    Copy code
    def write_to_pact_file(self):
            """
            Create a pact file based on provided attributes in DSL.
    
            Return 0 if success, 1 otherwise.
    
            :rtype: int
            """
            command = [
                MESSAGE_PATH,
                "update",
                json.dumps(self._messages[0]),
                "--pact-dir", self.pact_dir,
                f"--pact-specification-version={self.version}",
                "--consumer", f"{self.consumer.name}",
                "--provider", f"{self.provider.name}",
            ]
    
            self._message_process = Popen(command)
            self._message_process.wait()
    It claims to return 0 or 1 but does not! Need to look at other implementations, how the STDERR/STDOUT is handled, this should maybe do something like Popen(command, stdout=PIPE, stderr=PIPE) and communicate instead of wait? May simply be best to just "return self._message_process.wait()" -> but then how to handle later. For reference, picked up while doing silly things with
    Term("5|15|60", 60)
    which is obviously invalid, causes ruby to fail, but it carries on regardless. The end result being if a previous pact file exists, it won't then fail. If one does not exist it fails because "No value provided for required pact_files" which isn't a helpful message. pact-foundation/pact-python
    m
    • 2
    • 1
  • j

    json

    07/20/2022, 2:14 PM
    Is there a good way to mock a pact provider service's dependencies during the verification run? I want to seed my fake db with test data when I run my setup functions, but because of the way our service is designed, it is reaching out to other services to get auth data and other such things tangentially related to the contract I'm trying to verify, so even with the fake data the contract is expecting, I'm not accessing that data when the pact verifier runs because the user that it's using can't be retrieved because the pact provider service can't reach the auth service. The traditional methods of mocking that I'm familiar with are all for running unit tests though (like the pytest_mock's MockerFixture.patch, for example) and while I could overwrite the config that contains the base url of the auth service and run another mock server to return the expected user data whenever it's called, that seems like it'll wind up being a lot of extra work and it seems like there's got to be another better solution.
    m
    • 2
    • 37
  • t

    Timbo

    07/21/2022, 3:03 AM
    Hey guys, I am new to PACT/Pactflow, I am currently building a bi-directional contract testing POC. Our consumer is in typescript and the provider is a python based service. So far, I was able to publish my consumer contract to pactflow and I also used the pact standalone cli to publish the openapi spec to pactflow from the provider:
    Copy code
    ./pact/bin/pactflow publish-provider-contract \
      "openapi/v0/openapi.yaml" \
      --broker-token=<token> \
      --broker-base-url=<https://adae.pactflow.io> \
      --specification=oas \
      --provider "messaging-api" \
      --provider-app-version 12345\
      --branch cdc \
    After I ran the above, it published the openapi spec and also validated the consumer contract but I also noticed the provider contract (self verification) was not done. That's the piece I am a bit confused about. Our openapi spec is manually generated and we use pytest to validate it. How can I get the provider contract (openapi) to be validated in pactflow as well? I saw the below args for the CLI but I am not quite sure how to use them. --verification-exit-code=EXIT_CODE \ --verification-results REPORT_PATH \ --verification-results-content-type REPORT_CONTENT_TYPE \ --verifier VERIFIER Is it even necessary to do the provider contract self validation in pactflow if we already have tests that validate the openapi spec? I am also contemplating using dredd maybe to validate our openapi spec. I am looking for a bit of guidance, best practice etc. Any help is greatly appreciated.
  • m

    Matt (pactflow.io / pact-js / pact-go)

    07/21/2022, 8:36 AM
    Is it even necessary to do the provider contract self validation in pactflow if we already have tests that validate the openapi spec?
    Pactflow doesn’t do the verification, you just need to tell Pactflow that it was / was not verified successfully.
  • m

    Matt (pactflow.io / pact-js / pact-go)

    07/21/2022, 8:36 AM
    We allow the situation for you to publish the work in progress OAS, even if the implementation is not yet done.
  • m

    Matt (pactflow.io / pact-js / pact-go)

    07/21/2022, 8:38 AM
    In the case of pytest, if you have at output file you can upload as the report path, that should suffice. Otherwise an empty string I think will work for that value
  • m

    Matt (pactflow.io / pact-js / pact-go)

    07/21/2022, 8:38 AM
    An exit code of
    0
    would indicate success
    t
    • 2
    • 1
  • t

    tmegha

    07/21/2022, 10:14 AM
    Hi I am new to pact and was checking out the examples folder for pact-python to test out publishing pacts. I was able to generate a pact for the example consumer earlier but now I am seeing the following error:-
    Copy code
    docker.errors.ContainerError: Command 'publish /pacts --consumer-app-version 1' in image 'pactfoundation/pact-cli:latest' returned non-zero exit status 1: b"bundler: failed to load command: /pact/bin/pact (/pact/bin/pact)\n/usr/lib/ruby/2.7.0/net/http.rb:960:in `initialize': Failed to open TCP connection to broker_app:9292 (getaddrinfo: Name does not resolve)
    I did not change anything in the sharedfixtures.py file. The pact-broker app is up and running on 9292 but I am still getting this error. Any help would be greatly appreciated!
    e
    • 2
    • 1
  • a

    Adam Anderson

    08/03/2022, 11:46 PM
    Hi, is there a pattern with the Python pact Verifier to route the requests to a python function instead of
    localhost:<port>
    ?
    m
    • 2
    • 2
  • a

    Andre Rodrigues

    08/17/2022, 2:24 PM
    Hello there! In python is possible to test the contract between fields as multipart/form-data? I searched for some examples, but Im found nothing 😕
  • m

    M K

    08/30/2022, 11:10 AM
    Hey Guys ! started pact-python ,so curious where i can start from scratch with good understanding .
    m
    s
    d
    • 4
    • 10
  • d

    davT

    09/08/2022, 12:00 PM
    Good afternoon, I have just started using Pact and have a problem, our API response returns an array of objects which can have child objects , if the parent object has children they are in an array , if the parent has no children the array is empty. From looking at the Docs, the EachLike matcher dictates the array has at least one child object , so that is not useable in my case. Is there any other way to have a matcher for this situation. This is a bit of a deal breaker for us and I'm not sure why you cannot have a situation for, if array contains objects then objects should have this shape , else object is empty ? Something like Either {} or [{...}, {...} , ...]
    👋 1
    m
    • 2
    • 3
  • m

    Matt (pactflow.io / pact-js / pact-go)

    09/11/2022, 10:52 AM
    🎉
  • m

    Matt (pactflow.io / pact-js / pact-go)

    09/11/2022, 10:53 AM
    Thanks @Elliott Murray @Yousaf Nabi (pactflow.io) and most of all @Shuying Lin for have a crack at this 👏
    🎉 2
1234567Latest