Contract file format
This page describes the contract file format.
Note that you don't need to know the format of the contract file, as it is intended to be considered opaque.
However, if you're writing a plugin and want to understand what's happening under the hood, this description is for you.
Please do not rely on the details of the contract file format described here -
instead, if you are building tooling on top of ContractCase, we recommend you
use the @contract-case/case-plugin-base package to access and reason about the
contract. You can find the API documentation for it here.
If you need other functionality not covered by that package, please open an issue and we can discuss. You shouldn't need to directly know the details of the contract file format to build anything.
That said, we know people are going to look, and so, here are the details.
Overview
The top level of a contract file looks like this:
{
// Identifies this file as a ContractCase contract
"contractType": "case::contract",
// The consumer / provider pair this contract is for
"description": {
"consumerName": "Example-Client",
"providerName": "Example-Server",
},
// Metadata about the run that wrote this contract,
// including the ContractCase version
// Arbitrary other metadata might be included
"metadata": { "_case": { "version": "..." } },
// A lookup table of named matchers and state variables,
// keyed by their unique names. Interactions reference
// these by name, so that repeated structures are only
// written once
"matcherLookup": {
"matcher:an http \"GET\" request to \"/health\"...": {},
"variable:default:userId::test[0]": {},
},
// The interactions, as described below
"examples": [],
}
Anatomy of an interaction
Each interaction (called an example in the file format) has three parts:
-
states: An array of the state definitions this interaction needs. Each state has a_case:state:typeof either_case:NamedStateor_case:StateWithVariables, astateName, and (for states with variables) avariablesobject whose values are matchers. -
mock: The description of the mock for this interaction. It contains the matcher tree(s) for the data being exchanged (for examplerequestandresponsefor HTTP mocks), a_case:mock:typenaming the mock executor to use, and a_case:run:context:setupobject that tells ContractCase how to run the interaction from each side:write: How to run the interaction on the side that defines the contractread: How to run the interaction on the side that verifies the contract
Each of these describes which mock type to use (eg an HTTP client interaction is run with a mock HTTP server during definition, and a mock HTTP client during verification), whether state variables come from state handlers (
'state') or their default values ('default'), and whether triggers are'provided'by the user or'generated'by ContractCase. See writing mock types for a full description. -
result: The result of the interaction when the contract was defined (successful interactions are recorded asVERIFIED).
Metadata namespacing
Within the matcher trees, all ContractCase metadata keys are namespaced with a
_case: prefix (_case:matcher:type, _case:mock:type, _case:state:type
and so on), so they can't collide with user data. Everything without a
_case: prefix is literal data or the parameters of the enclosing matcher.
Matcher type constants provided by ContractCase itself are also prefixed with
_case: (for example _case:MatchInteger) - plugins use their own namespace
prefixes instead, as described in
the plugin documentation.
Stability
The format may change between versions. Do not rely on the structure described here. If you need to rely on details of the contract format, please open an issue and we can discuss your use case.