Skip to content

Interaction

Interaction module.

This module defines the classes that are used to define individual interactions within a Pact between a consumer and a provider. These interactions can be of different types, such as HTTP requests, synchronous messages, or asynchronous messages.

An interaction is a specific request that the consumer makes to the provider, and the response that the provider should return. On the consumer side, the interaction clearly defines the request that the consumer will make to the provider and the response that the consumer expects to receive. On the provider side, the interaction is replayed to the provider to ensure that the provider is able to handle the request and return the expected response.

Best Practices

When defining an interaction, it is important to ensure that the interaction is as minimal as possible (which is in contrast to the way specifications like OpenAPI are often written). This is because the interaction is used to verify that the consumer and provider can communicate correctly, not to define the entire API.

For example, consider a simple user API that has a GET /user/{id} endpoint which returns an object of the form:

{
    "id": 123,
    "username": "Alice"
    "email": "alice@example.com",
    "registered": "2021-02-26T10:17:51+11:00",
    "last_login": "2024-07-04T13:25:45+10:00"
}

The user client might have two functionalities:

  1. To check if the user exists, and
  2. To retrieve the user's username.

The implementation of these two would be:

from pact.v3 import Pact


pact = Pact(consumer="UserClient", provider="UserService")

# Checking if a user exists
(
    pact.upon_receiving("A request to check if a user exists")
    .given("A user with ID 123 exists")
    .with_request("GET", "/user/123")
    .will_respond_with(200)
)

# Getting a user's username
(
    pact.upon_receiving("A request to get a user's username")
    .given("A user with ID 123 exists")
    .with_request("GET", "/user/123")
    .will_respond_with(200)
    .with_body({"username": "Alice"})
)

Importantly, even if the server returns more information than just the username, since the client does not care about this information, it should not be included in the interaction.

Classes

AsyncMessageInteraction(pact_handle: pact.v3.ffi.PactHandle, description: str)

Bases: Interaction

An asynchronous message interaction.

This class defines an asynchronous message interaction between a consumer and a provider. It defines the kind of messages a consumer can accept, and the is agnostic of the underlying protocol, be it a message queue, Apache Kafka, or some other asynchronous protocol.

Warning

This class is not yet fully implemented and is not yet usable.

This function should not be called directly. Instead, an AsyncMessageInteraction should be created using the upon_receiving(...) method of a Pact instance using the "Async" interaction type.

PARAMETER DESCRIPTION
pact_handle

The Pact instance this interaction belongs to.

TYPE: PactHandle

description

Description of the interaction. This must be unique within the Pact.

TYPE: str

Source code in src/pact/v3/interaction/_async_message_interaction.py
def __init__(self, pact_handle: pact.v3.ffi.PactHandle, description: str) -> None:
    """
    Initialise a new Asynchronous Message Interaction.

    This function should not be called directly. Instead, an
    AsyncMessageInteraction should be created using the
    [`upon_receiving(...)`][pact.v3.Pact.upon_receiving] method of a
    [`Pact`][pact.v3.Pact] instance using the `"Async"` interaction type.

    Args:
        pact_handle:
            The Pact instance this interaction belongs to.

        description:
            Description of the interaction. This must be unique within the
            Pact.
    """
    super().__init__(description)
    self.__handle = pact.v3.ffi.new_message_interaction(pact_handle, description)

HttpInteraction(pact_handle: pact.v3.ffi.PactHandle, description: str)

Bases: Interaction

A synchronous HTTP interaction.

This class defines a synchronous HTTP interaction between a consumer and a provider. It defines a specific request that the consumer makes to the provider, and the response that the provider should return.

This class provides a simple way to define the request and response for an HTTP interaction. As many elements are shared between the request and response, this class provides a common interface for both. The functions intelligently determine whether the element should be added to the request or the response based on whether will_respond_with(...) has been called.

For example, the following two interactions are equivalent:

(
    pact.upon_receiving("a request")
    .with_request("GET", "/")
    .with_header("X-Foo", "bar")
    .will_respond_with(200)
    .with_header("X-Hello", "world")
)
(
    pact.upon_receiving("a request")
    .with_request("GET", "/")
    .will_respond_with(200)
    .with_header("X-Foo", "bar", part="Request")
    .with_header("X-Hello", "world", part="Response")
)

This class should not be instantiated directly. Instead, an HttpInteraction should be created using the upon_receiving(...) method of a Pact instance.

Source code in src/pact/v3/interaction/_http_interaction.py
def __init__(self, pact_handle: pact.v3.ffi.PactHandle, description: str) -> None:
    """
    Initialise a new HTTP Interaction.

    This class should not be instantiated directly. Instead, an
    `HttpInteraction` should be created using the
    [`upon_receiving(...)`][pact.v3.Pact.upon_receiving] method of a
    [`Pact`][pact.v3.Pact] instance.
    """
    super().__init__(description)
    self.__handle = pact.v3.ffi.new_interaction(pact_handle, description)
    self.__interaction_part = pact.v3.ffi.InteractionPart.REQUEST
    self._request_indices: dict[
        tuple[pact.v3.ffi.InteractionPart, str],
        int,
    ] = defaultdict(int)
    self._parameter_indices: dict[str, int] = defaultdict(int)

Functions

set_header(name: str, value: str, part: Literal['Request', 'Response'] | None = None) -> Self

Add a header to the request.

Unlike with_header(...), this function does no additional processing of the header value. This is useful for headers that contain a JSON object.

PARAMETER DESCRIPTION
name

Name of the header.

TYPE: str

value

Value of the header.

TYPE: str

part

Whether the header should be added to the request or the response. If None, then the function intelligently determines whether the header should be added to the request or the response, based on whether the will_respond_with(...) method has been called.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

Source code in src/pact/v3/interaction/_http_interaction.py
def set_header(
    self,
    name: str,
    value: str,
    part: Literal["Request", "Response"] | None = None,
) -> Self:
    r"""
    Add a header to the request.

    Unlike
    [`with_header(...)`][pact.v3.interaction.HttpInteraction.with_header],
    this function does no additional processing of the header value. This is
    useful for headers that contain a JSON object.

    Args:
        name:
            Name of the header.

        value:
            Value of the header.

        part:
            Whether the header should be added to the request or the
            response. If `None`, then the function intelligently determines
            whether the header should be added to the request or the
            response, based on whether the
            [`will_respond_with(...)`][pact.v3.interaction.HttpInteraction.will_respond_with]
            method has been called.
    """
    pact.v3.ffi.set_header(
        self._handle,
        self._parse_interaction_part(part),
        name,
        value,
    )
    return self
set_headers(headers: dict[str, str] | Iterable[tuple[str, str]], part: Literal['Request', 'Response'] | None = None) -> Self

Add multiple headers to the request.

This function intelligently determines whether the header should be added to the request or the response, based on whether the will_respond_with(...) method has been called.

See set_header(...) for more information.

PARAMETER DESCRIPTION
headers

Headers to add to the request.

TYPE: dict[str, str] | Iterable[tuple[str, str]]

part

Whether the headers should be added to the request or the response. If None, then the function intelligently determines whether the header should be added to the request or the response, based on whether the will_respond_with(...) method has been called.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

Source code in src/pact/v3/interaction/_http_interaction.py
def set_headers(
    self,
    headers: dict[str, str] | Iterable[tuple[str, str]],
    part: Literal["Request", "Response"] | None = None,
) -> Self:
    """
    Add multiple headers to the request.

    This function intelligently determines whether the header should be
    added to the request or the response, based on whether the
    [`will_respond_with(...)`][pact.v3.interaction.HttpInteraction.will_respond_with]
    method has been called.

    See [`set_header(...)`][pact.v3.interaction.HttpInteraction.set_header] for
    more information.

    Args:
        headers:
            Headers to add to the request.

        part:
            Whether the headers should be added to the request or the
            response. If `None`, then the function intelligently determines
            whether the header should be added to the request or the
            response, based on whether the
            [`will_respond_with(...)`][pact.v3.interaction.HttpInteraction.will_respond_with]
            method has been called.
    """
    if isinstance(headers, dict):
        headers = headers.items()
    for name, value in headers:
        self.set_header(name, value, part)
    return self
will_respond_with(status: int) -> Self

Set the response status.

Ideally, this function is called once all of the request information has been set. This allows functions such as with_header(...) to intelligently determine whether this is a request or response header.

Alternatively, the part argument can be used to explicitly specify whether the header should be added to the request or the response.

PARAMETER DESCRIPTION
status

Status for the response.

TYPE: int

Source code in src/pact/v3/interaction/_http_interaction.py
def will_respond_with(self, status: int) -> Self:
    """
    Set the response status.

    Ideally, this function is called once all of the request information has
    been set. This allows functions such as
    [`with_header(...)`][pact.v3.interaction.HttpInteraction.with_header]
    to intelligently determine whether this is a request or response header.

    Alternatively, the `part` argument can be used to explicitly specify
    whether the header should be added to the request or the response.

    Args:
        status:
            Status for the response.
    """
    pact.v3.ffi.response_status(self._handle, status)
    self.__interaction_part = pact.v3.ffi.InteractionPart.RESPONSE
    return self
with_header(name: str, value: str | dict[str, str] | Matcher[Any], part: Literal['Request', 'Response'] | None = None) -> Self

Add a header to the request.

Repeated Headers

If the same header has multiple values (see RFC9110 §5.2), then the same header must be specified multiple times with order being preserved. For example

(
    pact.upon_receiving("a request")
    .with_header("X-Foo", "bar")
    .with_header("X-Foo", "baz")
)

will expect a request with the following headers:

X-Foo: bar
X-Foo: baz
# Or, equivalently:
X-Foo: bar, baz

Note that repeated headers are case insensitive in accordance with RFC 9110 §5.1.

JSON Matching

Pact's matching rules are defined in the upstream documentation and support a wide range of matching rules. These can be specified using a JSON object as a strong using json.dumps(...). For example, the above rule whereby the X-Foo header has multiple values can be specified as:

(
    pact.upon_receiving("a request")
    .with_header(
        "X-Foo",
        json.dumps({
            "value": ["bar", "baz"],
        }),
    )
)

It is also possible to have a more complicated Regex pattern for the header. For example, a pattern for an Accept-Version header might be specified as:

(
    pact.upon_receiving("a request").with_header(
        "Accept-Version",
        json.dumps({
            "value": "1.2.3",
            "pact:matcher:type": "regex",
            "regex": r"\d+\.\d+\.\d+",
        }),
    )
)

If the value of the header is expected to be a JSON object and clashes with the above syntax, then it is recommended to make use of the set_header(...) method instead.

PARAMETER DESCRIPTION
name

Name of the header.

TYPE: str

value

Value of the header.

TYPE: str | dict[str, str] | Matcher[Any]

part

Whether the header should be added to the request or the response. If None, then the function intelligently determines whether the header should be added to the request or the response, based on whether the will_respond_with(...) method has been called.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

Source code in src/pact/v3/interaction/_http_interaction.py
def with_header(
    self,
    name: str,
    value: str | dict[str, str] | Matcher[Any],
    part: Literal["Request", "Response"] | None = None,
) -> Self:
    r"""
    Add a header to the request.

    # Repeated Headers

    If the same header has multiple values ([see RFC9110
    §5.2](https://www.rfc-editor.org/rfc/rfc9110.html#section-5.2)), then
    the same header must be specified multiple times with _order being
    preserved_. For example

    ```python
    (
        pact.upon_receiving("a request")
        .with_header("X-Foo", "bar")
        .with_header("X-Foo", "baz")
    )
    ```

    will expect a request with the following headers:

    ```http
    X-Foo: bar
    X-Foo: baz
    # Or, equivalently:
    X-Foo: bar, baz
    ```

    Note that repeated headers are _case insensitive_ in accordance with
    [RFC 9110
    §5.1](https://www.rfc-editor.org/rfc/rfc9110.html#section-5.1).

    # JSON Matching

    Pact's matching rules are defined in the [upstream
    documentation](https://github.com/pact-foundation/pact-reference/blob/libpact_ffi-v0.4.22/rust/pact_ffi/IntegrationJson.md)
    and support a wide range of matching rules. These can be specified
    using a JSON object as a strong using `json.dumps(...)`. For example,
    the above rule whereby the `X-Foo` header has multiple values can be
    specified as:

    ```python
    (
        pact.upon_receiving("a request")
        .with_header(
            "X-Foo",
            json.dumps({
                "value": ["bar", "baz"],
            }),
        )
    )
    ```

    It is also possible to have a more complicated Regex pattern for the
    header. For example, a pattern for an `Accept-Version` header might be
    specified as:

    ```python
    (
        pact.upon_receiving("a request").with_header(
            "Accept-Version",
            json.dumps({
                "value": "1.2.3",
                "pact:matcher:type": "regex",
                "regex": r"\d+\.\d+\.\d+",
            }),
        )
    )
    ```

    If the value of the header is expected to be a JSON object and clashes
    with the above syntax, then it is recommended to make use of the
    [`set_header(...)`][pact.v3.interaction.HttpInteraction.set_header]
    method instead.

    Args:
        name:
            Name of the header.

        value:
            Value of the header.

        part:
            Whether the header should be added to the request or the
            response. If `None`, then the function intelligently determines
            whether the header should be added to the request or the
            response, based on whether the
            [`will_respond_with(...)`][pact.v3.interaction.HttpInteraction.will_respond_with]
            method has been called.
    """
    interaction_part = self._parse_interaction_part(part)
    name_lower = name.lower()
    index = self._request_indices[(interaction_part, name_lower)]
    self._request_indices[(interaction_part, name_lower)] += 1
    if not isinstance(value, str):
        value_str: str = json.dumps(value, cls=IntegrationJSONEncoder)
    else:
        value_str = value
    pact.v3.ffi.with_header_v2(
        self._handle,
        interaction_part,
        name,
        index,
        value_str,
    )
    return self
with_headers(headers: dict[str, str] | Iterable[tuple[str, str]], part: Literal['Request', 'Response'] | None = None) -> Self

Add multiple headers to the request.

Note that due to the requirement of Python dictionaries to have unique keys, it is not possible to specify a header multiple times to create a multi-valued header. Instead, you may:

  • Use an alternative data structure. Any iterable of key-value pairs is accepted, including a list of tuples, a list of lists, or a dictionary view.

  • Make multiple calls to with_header(...) or with_headers(...).

  • Specify the multiple values in a JSON object of the form:

    python ( pact.upon_receiving("a request") .with_headers({ "X-Foo": json.dumps({ "value": ["bar", "baz"], }), ) )

See with_header(...) for more information.

PARAMETER DESCRIPTION
headers

Headers to add to the request.

TYPE: dict[str, str] | Iterable[tuple[str, str]]

part

Whether the header should be added to the request or the response. If None, then the function intelligently determines whether the header should be added to the request or the response, based on whether the will_respond_with(...) method has been called.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

Source code in src/pact/v3/interaction/_http_interaction.py
def with_headers(
    self,
    headers: dict[str, str] | Iterable[tuple[str, str]],
    part: Literal["Request", "Response"] | None = None,
) -> Self:
    """
    Add multiple headers to the request.

    Note that due to the requirement of Python dictionaries to have unique
    keys, it is _not_ possible to specify a header multiple times to create
    a multi-valued header. Instead, you may:

    -   Use an alternative data structure. Any iterable of key-value pairs
        is accepted, including a list of tuples, a list of lists, or a
        dictionary view.

    -   Make multiple calls to
        [`with_header(...)`][pact.v3.interaction.HttpInteraction.with_header]
        or
        [`with_headers(...)`][pact.v3.interaction.HttpInteraction.with_headers].

    -   Specify the multiple values in a JSON object of the form:

        ```python (
            pact.upon_receiving("a request") .with_headers({
                "X-Foo": json.dumps({
                    "value": ["bar", "baz"],
                }),
            )
        )
        ```

    See
    [`with_header(...)`][pact.v3.interaction.HttpInteraction.with_header]
    for more information.

    Args:
        headers:
            Headers to add to the request.

        part:
            Whether the header should be added to the request or the
            response. If `None`, then the function intelligently determines
            whether the header should be added to the request or the
            response, based on whether the
            [`will_respond_with(...)`][pact.v3.interaction.HttpInteraction.will_respond_with]
            method has been called.
    """
    if isinstance(headers, dict):
        headers = headers.items()
    for name, value in headers:
        self.with_header(name, value, part)
    return self
with_query_parameter(name: str, value: str | dict[str, str] | Matcher[Any]) -> Self

Add a query to the request.

This is the query parameter(s) that the consumer will send to the provider.

If the same parameter can support multiple values, then the same parameter can be specified multiple times:

(
    pact.upon_receiving("a request")
    .with_query_parameter("name", "John")
    .with_query_parameter("name", "Mary")
)

The above can equivalently be specified as:

(
    pact.upon_receiving("a request").with_query_parameter(
        "name",
        json.dumps({
            "value": ["John", "Mary"],
        }),
    )
)

It is also possible to have a more complicated Regex pattern for the parameter. For example, a pattern for an version parameter might be specified as:

(
    pact.upon_receiving("a request").with_query_parameter(
        "version",
        json.dumps({
            "value": "1.2.3",
            "pact:matcher:type": "regex",
            "regex": r"\d+\.\d+\.\d+",
        }),
    )
)

For more information on the format of the JSON object, see the upstream documentation.

PARAMETER DESCRIPTION
name

Name of the query parameter.

TYPE: str

value

Value of the query parameter.

TYPE: str | dict[str, str] | Matcher[Any]

Source code in src/pact/v3/interaction/_http_interaction.py
def with_query_parameter(
    self,
    name: str,
    value: str | dict[str, str] | Matcher[Any],
) -> Self:
    r"""
    Add a query to the request.

    This is the query parameter(s) that the consumer will send to the
    provider.

    If the same parameter can support multiple values, then the same
    parameter can be specified multiple times:

    ```python
    (
        pact.upon_receiving("a request")
        .with_query_parameter("name", "John")
        .with_query_parameter("name", "Mary")
    )
    ```

    The above can equivalently be specified as:

    ```python
    (
        pact.upon_receiving("a request").with_query_parameter(
            "name",
            json.dumps({
                "value": ["John", "Mary"],
            }),
        )
    )
    ```

    It is also possible to have a more complicated Regex pattern for the
    parameter. For example, a pattern for an `version` parameter might be
    specified as:

    ```python
    (
        pact.upon_receiving("a request").with_query_parameter(
            "version",
            json.dumps({
                "value": "1.2.3",
                "pact:matcher:type": "regex",
                "regex": r"\d+\.\d+\.\d+",
            }),
        )
    )
    ```

    For more information on the format of the JSON object, see the [upstream
    documentation](https://github.com/pact-foundation/pact-reference/blob/libpact_ffi-v0.4.22/rust/pact_ffi/IntegrationJson.md).

    Args:
        name:
            Name of the query parameter.

        value:
            Value of the query parameter.
    """
    index = self._parameter_indices[name]
    self._parameter_indices[name] += 1
    if not isinstance(value, str):
        value_str: str = json.dumps(value, cls=IntegrationJSONEncoder)
    else:
        value_str = value
    pact.v3.ffi.with_query_parameter_v2(
        self._handle,
        name,
        index,
        value_str,
    )
    return self
with_query_parameters(parameters: dict[str, Any] | Iterable[tuple[str, Any]]) -> Self

Add multiple query parameters to the request.

See with_query_parameter(...) for more information.

PARAMETER DESCRIPTION
parameters

Query parameters to add to the request.

TYPE: dict[str, Any] | Iterable[tuple[str, Any]]

Source code in src/pact/v3/interaction/_http_interaction.py
def with_query_parameters(
    self,
    parameters: dict[str, Any] | Iterable[tuple[str, Any]],
) -> Self:
    """
    Add multiple query parameters to the request.

    See
    [`with_query_parameter(...)`][pact.v3.interaction.HttpInteraction.with_query_parameter]
    for more information.

    Args:
        parameters:
            Query parameters to add to the request.
    """
    if isinstance(parameters, dict):
        parameters = parameters.items()
    for name, value in parameters:
        self.with_query_parameter(name, value)
    return self
with_request(method: str, path: str | Matcher[Any]) -> Self

Set the request.

This is the request that the consumer will make to the provider.

PARAMETER DESCRIPTION
method

HTTP method for the request.

TYPE: str

path

Path for the request.

TYPE: str | Matcher[Any]

Source code in src/pact/v3/interaction/_http_interaction.py
def with_request(self, method: str, path: str | Matcher[Any]) -> Self:
    """
    Set the request.

    This is the request that the consumer will make to the provider.

    Args:
        method:
            HTTP method for the request.
        path:
            Path for the request.
    """
    if isinstance(path, Matcher):
        path_str = json.dumps(path, cls=IntegrationJSONEncoder)
    else:
        path_str = path
    pact.v3.ffi.with_request(self._handle, method, path_str)
    return self

Interaction(description: str)

Bases: ABC

Interaction between a consumer and a provider.

This abstract class defines an interaction between a consumer and a provider. The concrete subclasses define the type of interaction, and include:

Interaction Part

For HTTP and synchronous message interactions, the interaction is split into two parts: the request and the response. The interaction part is used to specify which part of the interaction is being set. This is specified using the part argument of various methods (which defaults to an intelligent choice based on the order of the methods called).

The asynchronous message interaction does not have parts, as the interaction contains a single message from the provider (a.ka. the producer of the message) to the consumer. An attempt to set a response part will raise an error.

As this class is abstract, this function should not be called directly but should instead be called through one of the concrete subclasses.

PARAMETER DESCRIPTION
description

Description of the interaction. This must be unique within the Pact.

TYPE: str

Source code in src/pact/v3/interaction/_base.py
def __init__(self, description: str) -> None:
    """
    Create a new Interaction.

    As this class is abstract, this function should not be called directly
    but should instead be called through one of the concrete subclasses.

    Args:
        description:
            Description of the interaction. This must be unique within the
            Pact.
    """
    self._description = description

Functions

add_text_comment(comment: str) -> Self

Add a text comment for the interaction.

This is used by V4 interactions to set arbitrary text comments for the interaction.

PARAMETER DESCRIPTION
comment

Text of the comment.

TYPE: str

Warning

Internally, the comments are appended to an array under the text comment key. Care should be taken to ensure that conflicts are not introduced by set_comment.

Source code in src/pact/v3/interaction/_base.py
def add_text_comment(self, comment: str) -> Self:
    """
    Add a text comment for the interaction.

    This is used by V4 interactions to set arbitrary text comments for the
    interaction.

    Args:
        comment:
            Text of the comment.

    # Warning

    Internally, the comments are appended to an array under the `text`
    comment key. Care should be taken to ensure that conflicts are not
    introduced by
    [`set_comment`][pact.v3.interaction.Interaction.set_comment].
    """
    pact.v3.ffi.add_text_comment(self._handle, comment)
    return self
given(state: str, *, name: str | None = None, value: str | None = None, parameters: dict[str, Any] | str | None = None) -> Self
given(state: str) -> Self
given(state: str, *, name: str, value: str) -> Self
given(state: str, *, parameters: dict[str, Any] | str) -> Self

Set the provider state.

This is the state that the provider should be in when the Interaction is executed. When the provider is being verified, the provider state is passed to the provider so that its internal state can be set to match the provider state.

In its simplest form, the provider state is a string. For example, to match a provider state of a user exists, you would use:

pact.upon_receiving("a request").given("a user exists")

It is also possible to specify a parameter that will be used to match the provider state. For example, to match a provider state of a user exists with a parameter id that has the value 123, you would use:

(
    pact.upon_receiving("a request").given(
        "a user exists",
        name="id",
        value="123",
    )
)

Lastly, it is possible to specify multiple parameters that will be used to match the provider state. For example, to match a provider state of a user exists with a parameter id that has the value 123 and a parameter name that has the value John, you would use:

(
    pact.upon_receiving("a request").given(
        "a user exists",
        parameters={
            "id": "123",
            "name": "John",
        },
    )
)

This function can be called repeatedly to specify multiple provider states for the same Interaction. If the same state is specified with different parameters, then the parameters are merged together. The above example with multiple parameters can equivalently be specified as:

(
    pact.upon_receiving("a request")
    .given("a user exists", name="id", value="123")
    .given("a user exists", name="name", value="John")
)
PARAMETER DESCRIPTION
state

Provider state for the Interaction.

TYPE: str

name

Name of the parameter. This must be specified in conjunction with value.

TYPE: str | None DEFAULT: None

value

Value of the parameter. This must be specified in conjunction with name.

TYPE: str | None DEFAULT: None

parameters

Key-value pairs of parameters to use for the provider state. These must be encodable using json.dumps(...). Alternatively, a string contained the JSON object can be passed directly.

If the string does not contain a valid JSON object, then the string is passed directly as follows:

(
    pact.upon_receiving("a request").given(
        "a user exists",
        name="value",
        value=parameters,
    )
)

TYPE: dict[str, Any] | str | None DEFAULT: None

RAISES DESCRIPTION
ValueError

If the combination of arguments is invalid or inconsistent.

Source code in src/pact/v3/interaction/_base.py
def given(
    self,
    state: str,
    *,
    name: str | None = None,
    value: str | None = None,
    parameters: dict[str, Any] | str | None = None,
) -> Self:
    """
    Set the provider state.

    This is the state that the provider should be in when the Interaction is
    executed. When the provider is being verified, the provider state is
    passed to the provider so that its internal state can be set to match
    the provider state.

    In its simplest form, the provider state is a string. For example, to
    match a provider state of `a user exists`, you would use:

    ```python
    pact.upon_receiving("a request").given("a user exists")
    ```

    It is also possible to specify a parameter that will be used to match
    the provider state. For example, to match a provider state of `a user
    exists` with a parameter `id` that has the value `123`, you would use:

    ```python
    (
        pact.upon_receiving("a request").given(
            "a user exists",
            name="id",
            value="123",
        )
    )
    ```

    Lastly, it is possible to specify multiple parameters that will be used
    to match the provider state. For example, to match a provider state of
    `a user exists` with a parameter `id` that has the value `123` and a
    parameter `name` that has the value `John`, you would use:

    ```python
    (
        pact.upon_receiving("a request").given(
            "a user exists",
            parameters={
                "id": "123",
                "name": "John",
            },
        )
    )
    ```

    This function can be called repeatedly to specify multiple provider
    states for the same Interaction. If the same `state` is specified with
    different parameters, then the parameters are merged together. The above
    example with multiple parameters can equivalently be specified as:

    ```python
    (
        pact.upon_receiving("a request")
        .given("a user exists", name="id", value="123")
        .given("a user exists", name="name", value="John")
    )
    ```

    Args:
        state:
            Provider state for the Interaction.

        name:
            Name of the parameter. This must be specified in conjunction
            with `value`.

        value:
            Value of the parameter. This must be specified in conjunction
            with `name`.

        parameters:
            Key-value pairs of parameters to use for the provider state.
            These must be encodable using [`json.dumps(...)`][json.dumps].
            Alternatively, a string contained the JSON object can be passed
            directly.

            If the string does not contain a valid JSON object, then the
            string is passed directly as follows:

            ```python
            (
                pact.upon_receiving("a request").given(
                    "a user exists",
                    name="value",
                    value=parameters,
                )
            )
            ```

    Raises:
        ValueError:
            If the combination of arguments is invalid or inconsistent.
    """
    if name is not None and value is not None and parameters is None:
        pact.v3.ffi.given_with_param(self._handle, state, name, value)
    elif name is None and value is None and parameters is not None:
        if isinstance(parameters, dict):
            pact.v3.ffi.given_with_params(
                self._handle,
                state,
                json.dumps(parameters),
            )
        else:
            pact.v3.ffi.given_with_params(self._handle, state, parameters)
    elif name is None and value is None and parameters is None:
        pact.v3.ffi.given(self._handle, state)
    else:
        msg = "Invalid combination of arguments."
        raise ValueError(msg)
    return self
set_comment(key: str, value: Any | None) -> Self

Set a comment for the interaction.

This is used by V4 interactions to set a comment for the interaction. A comment consists of a key-value pair, where the key is a string and the value is anything that can be encoded as JSON.

PARAMETER DESCRIPTION
key

Key for the comment.

TYPE: str

value

Value for the comment. This must be encodable using json.dumps(...), or an existing JSON string. The value of None will remove the comment with the given key.

TYPE: Any | None

Warning

This function will overwrite any existing comment with the same key. In particular, the text key is used by add_text_comment.

Source code in src/pact/v3/interaction/_base.py
def set_comment(self, key: str, value: Any | None) -> Self:  # noqa: ANN401
    """
    Set a comment for the interaction.

    This is used by V4 interactions to set a comment for the interaction. A
    comment consists of a key-value pair, where the key is a string and the
    value is anything that can be encoded as JSON.

    Args:
        key:
            Key for the comment.

        value:
            Value for the comment. This must be encodable using
            [`json.dumps(...)`][json.dumps], or an existing JSON string. The
            value of `None` will remove the comment with the given key.

    # Warning

    This function will overwrite any existing comment with the same key. In
    particular, the `text` key is used by `add_text_comment`.
    """
    if isinstance(value, str) or value is None:
        pact.v3.ffi.set_comment(self._handle, key, value)
    else:
        pact.v3.ffi.set_comment(self._handle, key, json.dumps(value))
    return self
set_key(key: str | None) -> Self

Sets the key for the interaction.

This is used by V4 interactions to set the key of the interaction, which can subsequently used to reference the interaction.

Source code in src/pact/v3/interaction/_base.py
def set_key(self, key: str | None) -> Self:
    """
    Sets the key for the interaction.

    This is used by V4 interactions to set the key of the interaction, which
    can subsequently used to reference the interaction.
    """
    pact.v3.ffi.set_key(self._handle, key)
    return self
set_pending(*, pending: bool) -> Self

Mark the interaction as pending.

This is used by V4 interactions to mark the interaction as pending, in which case the provider is not expected to honour the interaction.

Source code in src/pact/v3/interaction/_base.py
def set_pending(self, *, pending: bool) -> Self:
    """
    Mark the interaction as pending.

    This is used by V4 interactions to mark the interaction as pending, in
    which case the provider is not expected to honour the interaction.
    """
    pact.v3.ffi.set_pending(self._handle, pending=pending)
    return self
test_name(name: str) -> Self

Set the test name annotation for the interaction.

This is used by V4 interactions to set the name of the test.

PARAMETER DESCRIPTION
name

Name of the test.

TYPE: str

Source code in src/pact/v3/interaction/_base.py
def test_name(
    self,
    name: str,
) -> Self:
    """
    Set the test name annotation for the interaction.

    This is used by V4 interactions to set the name of the test.

    Args:
        name:
            Name of the test.
    """
    pact.v3.ffi.interaction_test_name(self._handle, name)
    return self
with_binary_body(body: bytes | None, content_type: str | None = None, part: Literal['Request', 'Response'] | None = None) -> Self

Adds a binary body to the request or response.

Note that for HTTP interactions, this function will overwrite the body if it has been set using with_body(...).

PARAMETER DESCRIPTION
part

Whether the body should be added to the request or the response. If None, then the function intelligently determines whether the body should be added to the request or the response.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

content_type

Content type of the body. This is ignored if the Content-Type header has already been set.

TYPE: str | None DEFAULT: None

body

Body of the request.

TYPE: bytes | None

Source code in src/pact/v3/interaction/_base.py
def with_binary_body(
    self,
    body: bytes | None,
    content_type: str | None = None,
    part: Literal["Request", "Response"] | None = None,
) -> Self:
    """
    Adds a binary body to the request or response.

    Note that for HTTP interactions, this function will overwrite the body
    if it has been set using
    [`with_body(...)`][pact.v3.interaction.Interaction.with_body].

    Args:
        part:
            Whether the body should be added to the request or the response.
            If `None`, then the function intelligently determines whether
            the body should be added to the request or the response.

        content_type:
            Content type of the body. This is ignored if the `Content-Type`
            header has already been set.

        body:
            Body of the request.
    """
    pact.v3.ffi.with_binary_body(
        self._handle,
        self._parse_interaction_part(part),
        content_type,
        body,
    )
    return self
with_body(body: str | dict[str, Any] | Matcher[Any] | None = None, content_type: str | None = None, part: Literal['Request', 'Response'] | None = None) -> Self

Set the body of the request or response.

PARAMETER DESCRIPTION
body

Body of the request. If this is None, then the body is empty.

TYPE: str | dict[str, Any] | Matcher[Any] | None DEFAULT: None

content_type

Content type of the body. This is ignored if the Content-Type header has already been set.

TYPE: str | None DEFAULT: None

part

Whether the body should be added to the request or the response. If None, then the function intelligently determines whether the body should be added to the request or the response.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

Source code in src/pact/v3/interaction/_base.py
def with_body(
    self,
    body: str | dict[str, Any] | Matcher[Any] | None = None,
    content_type: str | None = None,
    part: Literal["Request", "Response"] | None = None,
) -> Self:
    """
    Set the body of the request or response.

    Args:
        body:
            Body of the request. If this is `None`, then the body is
            empty.

        content_type:
            Content type of the body. This is ignored if the `Content-Type`
            header has already been set.

        part:
            Whether the body should be added to the request or the response.
            If `None`, then the function intelligently determines whether
            the body should be added to the request or the response.
    """
    if body and isinstance(body, str):
        body_str = body
    else:
        body_str = json.dumps(body, cls=IntegrationJSONEncoder)

    pact.v3.ffi.with_body(
        self._handle,
        self._parse_interaction_part(part),
        content_type,
        body_str,
    )
    return self
with_generators(generators: dict[str, Any] | str, part: Literal['Request', 'Response'] | None = None) -> Self

Add generators to the interaction.

Generators are used to adjust how parts of the request or response are generated when the Pact is being tested. This can be useful for fields that vary each time the request is made, such as a timestamp.

PARAMETER DESCRIPTION
generators

Generators to add to the interaction. This must be encodable using json.dumps(...), or a string.

TYPE: dict[str, Any] | str

part

Whether the generators should be added to the request or the response. If None, then the function intelligently determines whether the generators should be added to the request or the response.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

Source code in src/pact/v3/interaction/_base.py
def with_generators(
    self,
    generators: dict[str, Any] | str,
    part: Literal["Request", "Response"] | None = None,
) -> Self:
    """
    Add generators to the interaction.

    Generators are used to adjust how parts of the request or response are
    generated when the Pact is being tested. This can be useful for fields
    that vary each time the request is made, such as a timestamp.

    Args:
        generators:
            Generators to add to the interaction. This must be encodable using
            [`json.dumps(...)`][json.dumps], or a string.

        part:
            Whether the generators should be added to the request or the
            response. If `None`, then the function intelligently determines
            whether the generators should be added to the request or the
            response.
    """
    if isinstance(generators, dict):
        generators = json.dumps(generators)

    pact.v3.ffi.with_generators(
        self._handle,
        self._parse_interaction_part(part),
        generators,
    )
    return self
with_matching_rules(rules: dict[str, Any] | str, part: Literal['Request', 'Response'] | None = None) -> Self

Add matching rules to the interaction.

Matching rules are used to specify how the request or response should be matched. This is useful for specifying that certain parts of the request or response are flexible, such as the date or time.

PARAMETER DESCRIPTION
rules

Matching rules to add to the interaction. This must be encodable using json.dumps(...), or a string.

TYPE: dict[str, Any] | str

part

Whether the matching rules should be added to the request or the response. If None, then the function intelligently determines whether the matching rules should be added to the request or the response.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

Source code in src/pact/v3/interaction/_base.py
def with_matching_rules(
    self,
    rules: dict[str, Any] | str,
    part: Literal["Request", "Response"] | None = None,
) -> Self:
    """
    Add matching rules to the interaction.

    Matching rules are used to specify how the request or response should be
    matched. This is useful for specifying that certain parts of the request
    or response are flexible, such as the date or time.

    Args:
        rules:
            Matching rules to add to the interaction. This must be
            encodable using [`json.dumps(...)`][json.dumps], or a string.

        part:
            Whether the matching rules should be added to the request or the
            response. If `None`, then the function intelligently determines
            whether the matching rules should be added to the request or the
            response.
    """
    if isinstance(rules, dict):
        rules = json.dumps(rules)

    pact.v3.ffi.with_matching_rules(
        self._handle,
        self._parse_interaction_part(part),
        rules,
    )
    return self
with_metadata(__metadata: dict[str, str] | None = None, __part: Literal['Request', 'Response'] | None = None, /, **kwargs: str) -> Self

Set metadata for the interaction.

This function may either be called with a single dictionary of metadata, or with keyword arguments that are the key-value pairs of the metadata (or a combination therefore):

interaction.with_metadata({"key": "value", "key two": "value two"})
interaction.with_metadata(foo="bar", baz="qux")

The value of None will remove the metadata key from the interaction. This is distinct from using an empty string or a string containing the JSON null value, which will set the metadata key to an empty string or the JSON null value, respectively.

Note

There are two special keys which cannot be used as keyword arguments: __metadata and __part. Should there ever be a need to set metadata with one of these keys, they must be passed through as a dictionary:

interaction.with_metadata({"__metadata": "value", "__part": 1})
PARAMETER DESCRIPTION
___metadata

Dictionary of metadata keys and associated values.

__part

Whether the metadata should be added to the request or the response. If None, then the function intelligently determines whether the body should be added to the request or the response.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

**kwargs

Additional metadata key-value pairs.

TYPE: str DEFAULT: {}

RETURNS DESCRIPTION
Self

The current instance of the interaction.

Source code in src/pact/v3/interaction/_base.py
def with_metadata(
    self,
    __metadata: dict[str, str] | None = None,
    __part: Literal["Request", "Response"] | None = None,
    /,
    **kwargs: str,
) -> Self:
    """
    Set metadata for the interaction.

    This function may either be called with a single dictionary of metadata,
    or with keyword arguments that are the key-value pairs of the metadata
    (or a combination therefore):

    ```python
    interaction.with_metadata({"key": "value", "key two": "value two"})
    interaction.with_metadata(foo="bar", baz="qux")
    ```

    The value of `None` will remove the metadata key from the interaction.
    This is distinct from using an empty string or a string containing the
    JSON `null` value, which will set the metadata key to an empty string or
    the JSON `null` value, respectively.

    !!! note

        There are two special keys which cannot be used as keyword
        arguments: `__metadata` and `__part`. Should there ever be a need
        to set metadata with one of these keys, they must be passed through
        as a dictionary:

        ```python
        interaction.with_metadata({"__metadata": "value", "__part": 1})
        ```

    Args:
        ___metadata:
            Dictionary of metadata keys and associated values.

        __part:
            Whether the metadata should be added to the request or the
            response. If `None`, then the function intelligently determines
            whether the body should be added to the request or the response.

        **kwargs:
            Additional metadata key-value pairs.

    Returns:
        The current instance of the interaction.
    """
    part = self._parse_interaction_part(__part)
    for k, v in (__metadata or {}).items():
        pact.v3.ffi.with_metadata(
            self._handle,
            k,
            v,
            part,
        )
    for k, v in kwargs.items():
        pact.v3.ffi.with_metadata(
            self._handle,
            k,
            v,
            part,
        )
    return self
with_multipart_file(part_name: str, path: Path | None, content_type: str | None = None, part: Literal['Request', 'Response'] | None = None, boundary: str | None = None) -> Self

Adds a binary file as the body of a multipart request or response.

The content type of the body will be set to a MIME multipart message.

Source code in src/pact/v3/interaction/_base.py
def with_multipart_file(
    self,
    part_name: str,
    path: Path | None,
    content_type: str | None = None,
    part: Literal["Request", "Response"] | None = None,
    boundary: str | None = None,
) -> Self:
    """
    Adds a binary file as the body of a multipart request or response.

    The content type of the body will be set to a MIME multipart message.
    """
    pact.v3.ffi.with_multipart_file_v2(
        self._handle,
        self._parse_interaction_part(part),
        content_type,
        path,
        part_name,
        boundary,
    )
    return self
with_plugin_contents(contents: dict[str, Any] | str, content_type: str, part: Literal['Request', 'Response'] | None = None) -> Self

Set the interaction content using a plugin.

The value of contents is passed directly to the plugin as a JSON string. The plugin will document the format of the JSON content.

PARAMETER DESCRIPTION
contents

Body of the request. If this is None, then the body is empty.

TYPE: dict[str, Any] | str

content_type

Content type of the body. This is ignored if the Content-Type header has already been set.

TYPE: str

part

Whether the body should be added to the request or the response. If None, then the function intelligently determines whether the body should be added to the request or the response.

TYPE: Literal['Request', 'Response'] | None DEFAULT: None

Source code in src/pact/v3/interaction/_base.py
def with_plugin_contents(
    self,
    contents: dict[str, Any] | str,
    content_type: str,
    part: Literal["Request", "Response"] | None = None,
) -> Self:
    """
    Set the interaction content using a plugin.

    The value of `contents` is passed directly to the plugin as a JSON
    string. The plugin will document the format of the JSON content.

    Args:
        contents:
            Body of the request. If this is `None`, then the body is empty.

        content_type:
            Content type of the body. This is ignored if the `Content-Type`
            header has already been set.

        part:
            Whether the body should be added to the request or the response.
            If `None`, then the function intelligently determines whether
            the body should be added to the request or the response.
    """
    if isinstance(contents, dict):
        contents = json.dumps(contents)

    pact.v3.ffi.interaction_contents(
        self._handle,
        self._parse_interaction_part(part),
        content_type,
        contents,
    )
    return self

SyncMessageInteraction(pact_handle: pact.v3.ffi.PactHandle, description: str)

Bases: Interaction

A synchronous message interaction.

This class defines a synchronous message interaction between a consumer and a provider. As with HttpInteraction, it defines a specific request that the consumer makes to the provider, and the response that the provider should return.

Warning

This class is not yet fully implemented and is not yet usable.

This function should not be called directly. Instead, an AsyncMessageInteraction should be created using the upon_receiving(...) method of a Pact instance using the "Sync" interaction type.

PARAMETER DESCRIPTION
pact_handle

Handle for the Pact.

TYPE: PactHandle

description

Description of the interaction. This must be unique within the Pact.

TYPE: str

Source code in src/pact/v3/interaction/_sync_message_interaction.py
def __init__(self, pact_handle: pact.v3.ffi.PactHandle, description: str) -> None:
    """
    Initialise a new Synchronous Message Interaction.

    This function should not be called directly. Instead, an
    AsyncMessageInteraction should be created using the
    [`upon_receiving(...)`][pact.v3.Pact.upon_receiving] method of a
    [`Pact`][pact.v3.Pact] instance using the `"Sync"` interaction type.

    Args:
        pact_handle:
            Handle for the Pact.

        description:
            Description of the interaction. This must be unique within the
            Pact.
    """
    super().__init__(description)
    self.__handle = pact.v3.ffi.new_sync_message_interaction(
        pact_handle,
        description,
    )
    self.__interaction_part = pact.v3.ffi.InteractionPart.REQUEST