# Start the Pact Broker
git clone https://github.com/pact-foundation/pact-broker-docker
Run to start the pact broker running at localhost:9292
Pre-requisite:
brew install postgres
brew service start postgresql
psql postgres
CREATE DATABASE pact_broker; CREATE ROLE pact_broker WITH LOGIN PASSWORD 'password'; GRANT ALL PRIVILEGES ON DATABASE pact_broker TO pact_broker;
reference:
https://github.com/pact-foundation/pact-broker-docker
Run to start the pact broker running at localhost:9292
docker-compose up -d
Go to localhost:9292 to see the pact broker running in the browser
docker-compose up
To run in local environment with dependencies setup
npm i
To run the consumer pact test which will generate the pact file to ./pacts/iconsumer-iprovider.json
npm run test:pact
To publish the pact file to the pact broker
npm run publish:pact
To run the provider pact test to verify the pact file
npm run verify:pact
User API
The APIs are described below, including a bunch of cURL statements to invoke them.
curl --location --request POST 'https://xxxx/api/admin/users?username=mike&firstName=mike&lastName=tan&password=CukeStudio)123&[email protected]&organizations=e290e5c2-bd43-11ea-882a-cd26553a22fa&role=ROLE_KCP_DUMMY'
--header 'Content-Type: application/json'
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.xxxxx'
--data-raw ''
Structure:
```bash
├── package.json
├── README.md
├── pactSetup.js
├── pactTestWrapper.js
├── publishPacts.js
├── verifyPacts.js
└── pacts
├── iConsumer-iProvider.json (generated by pact)
└── __pact-tests__
├── user_api.test.pact.js
├── ...
The nodejs test framework used is jest
The testsuites are found in the pact-tests folder
The Pact server is defined at pactSetup.js: 1. The mock server is 127.0.0.1:8991
2. While we are writing a consumer test, do not be mis-led by the provider variable.
This is where we define the pact server which mocks our provider and will respond to API requests we make to it.
3. Pact will start a service listening on port 8891 writing logs to a logs/ directory
where the test are executed from and will create the actual pact contract file in the pacts/ directory.
4. Pact will use the latest specification version (spec: 2)
Test Setup:
provider.setup())
Before our tests can actually run, we need to start the Pact service and provide it with
our expected interactions.
Example: authentication_api.test.pact.js
```const interaction = {
state: 'Authenticate with valid clientId',
uponReceiving: 'access_token, token_type and refresh_token',
headers: {
'Content-Type': 'application/json',
'X-Active-Organization': Matchers.like('42cd1c1f-112d-11ea-8153-0242ac120002'),
'Authorization': Matchers.like('Basic cHVibGljQ2xpZW50SWQ6'),
},
withRequest: {
method: 'POST',
path: urlpath,
param: {
"grant-type": Matchers.like("password"),
"password": Matchers.like("pass"),
"username": Matchers.like("admin-xxxx"),
},
},
willRespondWith: {
status: 200,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
},
body: like(EXPECTED_BODY),
},
};
return provider.addInteraction(interaction);
```
1. This is where we define our expectations. Any mis-match between expected interactions will cause the test to throw an error when being asserted
2. withRequest and willRespondWith define the expected interaction between API consumer and provider
3. withRequest part is defining what the consumer API is expected to send and we use the capability from the pact library known as Matchers to allow some flexibility on the provider implementation of the contract
Consumer Test:
```
// add expectations
it('returns a successfully body',() => {
return axios.request({
method: 'POST',
baseURL: getApiEndpoint(),
url: urlpath,
headers: {
Accept: '*/*',
'content-type': 'application/json',
},
data: BODY,
})
.then((response) => {
expect(response.headers['content-type']).toEqual('application/json;charset=UTF-8');
expect(response.data).toEqual(EXPECTED_BODY);
expect(response.status).toEqual(200);
})
.then(() => provider.verify())
})
```
1. This is our actual consumer test where we use the axios.request to make HTTP requests to the mocked API service that the pact library created for us.
This is the actual expected usage in real world where fire a API call to the provider
curl --location --request POST 'https://xxxx.io/api/admin/users?username=mike&firstName=mike&lastName=tan&password=CukeStudio)123&[email protected]&organizations=e290e5c2-bd43-11ea-882a-cd26553a22fa&role=ROLE_KCP_DUMMY' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.xxxxxxx' \
--data-raw ''
2. We assert with provider.verify()) that all expected interactions have been fulfilled by making sure it doesn't throw an error and conclude the test.
Test Teardown:
provider.finalize())
After running the test, you have a pact file in the pacts/ directory that you can collaborate with your provider.
reference: https://github.com/DiUS/pact-workshop-js https://github.com/pact-foundation/pact-js/tree/master/examples/jest
Deploy pact broker to Azure DevOps CI pipeline:
I tested this Application for a sample multi-container App Service on my end and everything seems to work fine. Although there are a few changes I needed to do first to successfully run this App:
Removed the Nginx part in the docker-compose.yml file.
Added the following Application Setting to the Web App: WEBSITES_WEB_CONTAINER_NAME=pact-broker. You can add these application settings by browsing to the Azure Portal of your Web App and under Configurations > Application Settings, add the key-value pairs mentioned-above.
I also had a chance to deploy a sample Web App using a single container and used a dedicated MySQL server for the test. This single container application also worked as expected. I created a custom container Web App for this sample and added the following application settings to make it work:
PACT_BROKER_DATABASE_URL: The format of this setting should be similar to “postgres://user1:pass1@myhost/mydb”, as mentioned in the documentation.
WEBSITES_PORT: 9292, since the Pact-Broker container listens to this Port by default.
I understand that you want to use an Azure DevOps pipeline to deploy a Web App for containers and would like to understand how to configure the various release pipeline parameters.
In case we want to deploy a Web App for Container App Service, we should use the “Azure App Service Deploy” or the “Azure Web App on container Deploy” task, which gives us an option of selecting the App Service Type, for ex., “Web App for Containers (Linux)” and based on the App Type selection it would populate the App Service name dropdown showing only the Apps of the selected type.
Also, we can specify other parameters in the pipeline, like the Application Settings through the text area under “Application and Configuration Settings”.
You can find more information and examples regarding these steps in the following links:
Deploying a Docker based web application to Azure App Service
Azure App Service Deploy task
Issue:
let opts = {
providerBaseUrl: "http://localhost:8082",
pactFilesOrDirs: [path.resolve(process.cwd(), "pacts")],
pactBroker: process.env.PACTBROKERURL,
pactBrokerUrl: process.env.PACTBROKERTOKEN,
check_for_potential_duplicate_pacticipant_names: "false",
consumerVersion: "2.0.0",
//consumerVersion: gitSha,
tags: [branch],
}
Could not publish pact:
No value provided for option '--tag'