Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Rust FFI #8: Remove methods #285

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
b45e9c0
Use Rust FFI: Consumer
tienvx Dec 23, 2022
849efd5
Merge branch 'master' into use-rust-ffi-consumer
tienvx Jan 11, 2023
e1d49cc
Remove method BuilderInterface::writePact
tienvx Jan 11, 2023
0d7fe0a
Remove CHANGELOG.md. Add '@internal' to classes are not supposed to b…
tienvx Jan 15, 2023
0812b31
Add missing return type to method Matcher::regex
tienvx Jan 15, 2023
cf5144b
Add more details to UPGRADE-9.0.md
tienvx Jan 15, 2023
d20f1f5
Revert multiple values support in header and query parameter
tienvx Jan 16, 2023
7d57b25
Merge branch 'master' into use-rust-ffi-consumer
Feb 14, 2023
bb256ef
Support multiple values in header and query parameter
tienvx Mar 1, 2023
d6a9d0d
Update pact ffi library
tienvx Mar 1, 2023
ca46297
Rename method getCode to getHeader
tienvx Mar 2, 2023
39718b9
Revert breaking change that require to call createMockServer manually
tienvx Mar 12, 2023
ddc46e5
Remove composer/semver
tienvx Mar 2, 2023
5dae03a
Make sure every value of header and query parameter is string
tienvx Mar 14, 2023
52e9a99
Move exception message to exception
tienvx Mar 14, 2023
bff0f9b
Extract method getSpecification() to check for supporting plugin
tienvx Mar 14, 2023
312c45c
Extract, change visibility, and move methods to abstract class to sup…
tienvx Mar 14, 2023
6ac293c
Use Rust FFI: Message Consumer
tienvx Dec 28, 2022
f2f7ba9
Use Rust FFI: Provider
tienvx Jan 2, 2023
8427cb2
Fix wrong return type
tienvx Mar 7, 2023
60a1463
Fix wrong argument type
tienvx Mar 7, 2023
1b4c881
Fix log level not working
tienvx Mar 7, 2023
69ca8dd
Rename stateChangeAsQuery to stateChangeAsBody
tienvx Mar 9, 2023
e7b1637
Use Rust FFI: Stub Server
tienvx Jan 3, 2023
4d09fe9
Use Rust FFI: Offload process
tienvx Jan 3, 2023
8a757af
Fix false positive about successfully uploaded pacts to broker
tienvx Mar 5, 2023
d1068dc
Check port is open instead of output
tienvx Mar 3, 2023
814db59
Remove jsonSerialize methods
tienvx Jan 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Expand Up @@ -63,7 +63,7 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
extensions: openssl, sockets, curl, zip
extensions: openssl, sockets, curl, zip, ffi
php-version: ${{ matrix.php }}

- name: Composer install
Expand Down
248 changes: 99 additions & 149 deletions README.md
Expand Up @@ -11,31 +11,39 @@ PHP version of [Pact](https://pact.io). Enables consumer driven contract testing
Table of contents
=================

* [Versions](#versions)
* [Specifications](#specifications)
* [Installation](#installation)
* [Basic Consumer Usage](#basic-consumer-usage)
* [Start and Stop the Mock Server](#start-and-stop-the-mock-server)
* [Create Consumer Unit Test](#create-consumer-unit-test)
* [Create Mock Request](#create-mock-request)
* [Create Mock Response](#create-mock-response)
* [Build the Interaction](#build-the-interaction)
* [Make the Request](#make-the-request)
* [Make Assertions](#make-assertions)
* [Basic Provider Usage](#basic-provider-usage)
* [Create Unit Tests](#create-unit-test)
* [Start API](#start-api)
* [Provider Verification](#provider-verification)
* [Verify From Pact Broker](#verify-from-pact-broker)
* [Verify All from Pact Broker](#verify-all-from-pact-broker)
* [Verify Files by Path](#verify-files-by-path)
* [Tips](#tips)
* [Starting API Asynchronously](#starting-api-asynchronously)
* [Set Up Provider State](#set-up-provider-state)
* [Examples](#additional-examples)
- [Pact PHP](#pact-php)
- [Table of contents](#table-of-contents)
- [Versions](#versions)
- [Specifications](#specifications)
- [Installation](#installation)
- [Basic Consumer Usage](#basic-consumer-usage)
- [Publish Contracts To Pact Broker](#publish-contracts-to-pact-broker)
- [Create Consumer Unit Test](#create-consumer-unit-test)
- [Create Mock Request](#create-mock-request)
- [Create Mock Response](#create-mock-response)
- [Build the Interaction](#build-the-interaction)
- [Start the Mock Server](#start-the-mock-server)
- [Make the Request](#make-the-request)
- [Verify Interactions](#verify-interactions)
- [Make Assertions](#make-assertions)
- [Basic Provider Usage](#basic-provider-usage)
- [Create Unit Test](#create-unit-test)
- [Start API](#start-api)
- [Provider Verification](#provider-verification)
- [Verify From Pact Broker](#verify-from-pact-broker)
- [Verify Files in Directory](#verify-files-in-directory)
- [Verify Files by Path](#verify-files-by-path)
- [Tips](#tips)
- [Starting API Asynchronously](#starting-api-asynchronously)
- [Set Up Provider State](#set-up-provider-state)
- [Additional Examples](#additional-examples)
- [Message support](#message-support)
- [Consumer Side Message Processing](#consumer-side-message-processing)
- [Provider Side Message Validation](#provider-side-message-validation)
- [Usage for the optional `pact-stub-service`](#usage-for-the-optional-pact-stub-service)

## Versions
9.X updates internal dependencies and libraries. This results in dropping PHP 7.4
9.X adds support for pact specification 3.X & 4.X via Pact FFI. This results in dropping PHP 7.4

8.X updates internal dependencies and libraries. This results in dropping PHP 7.3

Expand All @@ -52,7 +60,13 @@ If you wish to stick with the 2.X implementation, you can continue to pull from

## Specifications

The 3.X version is the version of Pact-PHP, not the pact specification version that it supports. Pact-Php 3.X supports [Pact-Specification 2.X](https://github.com/pact-foundation/pact-specification/tree/version-2).
The 3.X version is the version of Pact-PHP, not the pact specification version that it supports.

Pact-Php 3.X -> 8.X supports [Pact-Specification 2.X](https://github.com/pact-foundation/pact-specification/tree/version-2).
Pact-Php 9.X supports:
* [Pact-Specification 2.X](https://github.com/pact-foundation/pact-specification/tree/version-2)
* [Pact-Specification 3.X](https://github.com/pact-foundation/pact-specification/tree/version-3).
* [Pact-Specification 4.X](https://github.com/pact-foundation/pact-specification/tree/version-4).

## Installation

Expand All @@ -68,39 +82,12 @@ Composer hosts older versions under `mattersight/phppact`, which is abandoned. P

All of the following code will be used exclusively for the Consumer.

### Start and Stop the Mock Server
### Publish Contracts To Pact Broker

This library contains a wrapper for the [Ruby Standalone Mock Service](https://github.com/pact-foundation/pact-mock_service).
When all tests in test suite are passed, you may want to publish generated contract files to pact broker automatically.

The easiest way to configure this is to use a [PHPUnit Listener](https://phpunit.de/manual/current/en/appendixes.configuration.html#appendixes.configuration.test-listeners). A default listener is included in this project, see [PactTestListener.php](/src/PhpPact/Consumer/Listener/PactTestListener.php). This utilizes environmental variables for configurations. These env variables can either be added to the system or to the phpunit.xml configuration file. Here is an example [phpunit.xml](/example/phpunit.consumer.xml) file configured to use the default. Keep in mind that both the test suite and the arguments array must be the same value.

Alternatively, you can start and stop as in whatever means you would like by following this example:

```php
<?php
use PhpPact\Standalone\MockService\MockServer;
use PhpPact\Standalone\MockService\MockServerConfig;

// Create your basic configuration. The host and port will need to match
// whatever your Http Service will be using to access the providers data.
$config = new MockServerConfig();
$config->setHost('localhost');
$config->setPort(7200);
$config->setConsumer('someConsumer');
$config->setProvider('someProvider');
$config->setCors(true);

// Instantiate the mock server object with the config. This can be any
// instance of MockServerConfigInterface.
$server = new MockServer($config);

// Create the process.
$server->start();

// Stop the process.
$server->stop();
```

### Create Consumer Unit Test

Create a standard PHPUnit test case class and function.
Expand Down Expand Up @@ -188,7 +175,7 @@ $builder
->given('a person exists')
->uponReceiving('a get request to /hello/{name}')
->with($request)
->willRespondWith($response); // This has to be last. This is what makes an API request to the Mock Server to set the interaction.
->willRespondWith($response); // This has to be last. This is what makes FFI calls to register the interaction and start the mock server.
```

### Make the Request
Expand All @@ -204,7 +191,7 @@ Verify that all interactions took place that were registered.
This typically should be in each test, that way the test that failed to verify is marked correctly.

```php
$builder->verify();
$this->assertTrue($builder->verify());
```

### Make Assertions
Expand Down Expand Up @@ -242,51 +229,57 @@ $config = new VerifierConfig();
$config
->setProviderName('someProvider') // Providers name to fetch.
->setProviderVersion('1.0.0') // Providers version.
->setProviderBranch('main') // Providers git branch name.
->setProviderBaseUrl(new Uri('http://localhost:58000')) // URL of the Provider.
->setBrokerUri(new Uri('http://localhost')) // URL of the Pact Broker to publish results.
->setPublishResults(true) // Flag the verifier service to publish the results to the Pact Broker.
->setProcessTimeout(60) // Set process timeout (optional) - default 60
->setProcessIdleTimeout(10) // Set process idle timeout (optional) - default 10
->setEnablePending(true) // Flag to enable pending pacts feature (check pact docs for further info)
->setIncludeWipPactSince('2020-01-30') //Start date of WIP Pacts (check pact docs for further info)
->setRequestFilter(
function (RequestInterface $r) {
return $r->withHeader('MY_SPECIAL_HEADER', 'my special value');
}
);
// Verify that the Consumer 'someConsumer' that is tagged with 'master' is valid.
->setProviderTags('prod' ,'dev')
->setProviderBranch('main')
->setScheme('http')
->setHost('localhost')
->setPort(58000)
->setBasePath('/')
->setStateChangeUrl(new Uri('http://localhost:58000/change-state'))
->setBuildUrl(new Uri('http://build.domain.com'))
->setFilterConsumerNames('someConsumer', 'otherConsumer')
->setFilterDescription('Send POST to create')
->setFilterNoState(true)
->setFilterState('state')
->setPublishResults(true)
->setDisableSslVerification(true)
->setStateChangeAsBody(false)
->setStateChangeTeardown(true)
->setRequestTimeout(500);

$verifier = new Verifier($config);
$verifier->verify('someConsumer', 'master'); // The tag is option. If no tag is set it will just grab the latest.

// This will not be reached if the PACT verifier throws an error, otherwise it was successful.
$this->assertTrue(true, 'Pact Verification has failed.');
$selectors = (new ConsumerVersionSelectors())
->addSelector('{"tag":"foo","latest":true}')
->addSelector('{"tag":"bar","latest":true}');

$broker = new Broker();
$broker
->setUrl(new Uri('http://localhost'))
->setUsername('user')
->setPassword('pass')
->setToken('token')
->setEnablePending(true)
->setIncludeWipPactSince('2020-01-30')
->setProviderTags(['prod'])
->setProviderBranch('main')
->setConsumerVersionSelectors($selectors)
->setConsumerVersionTags(['dev']);

$verifier->addBroker($broker);

$this->assertTrue($verifier->verify());
```

##### Verify All from Pact Broker
##### Verify Files in Directory

This will grab every Pact file associated with the given provider.
This allows local Pact file testing.

```php
public function testPactVerifyAll()
public function testPactVerifyFilesInDirectory()
{
$config = new VerifierConfig();
$config
->setProviderName('someProvider') // Providers name to fetch.
->setProviderVersion('1.0.0') // Providers version.
->setProviderBranch('main') // Providers git branch name.
->setProviderBaseUrl(new Uri('http://localhost:58000')) // URL of the Provider.
->setBrokerUri(new Uri('http://localhost')) // URL of the Pact Broker to publish results.
->setPublishResults(true) // Flag the verifier service to publish the results to the Pact Broker.
->setEnablePending(true) // Flag to enable pending pacts feature (check pact docs for further info)
->setIncludeWipPactSince('2020-01-30') //Start date of WIP Pacts (check pact docs for further info)

// Verify that all consumers of 'someProvider' are valid.
$verifier = new Verifier($config);
$verifier->verifyAll();

// This will not be reached if the PACT verifier throws an error, otherwise it was successful.
$this->assertTrue(true, 'Pact Verification has failed.');
$verifier->addDirectory('C:\SomePath');
$this->assertTrue($verifier->verify());
}
```

Expand All @@ -295,25 +288,10 @@ public function testPactVerifyAll()
This allows local Pact file testing.

```php
public function testPactVerifyAll()
public function testPactVerifyFiles()
{
$config = new VerifierConfig();
$config
->setProviderName('someProvider') // Providers name to fetch.
->setProviderVersion('1.0.0') // Providers version.
->setProviderBranch('main') // Providers git branch name.
->setProviderBaseUrl(new Uri('http://localhost:58000')) // URL of the Provider.
->setBrokerUri(new Uri('http://localhost')) // URL of the Pact Broker to publish results.
->setPublishResults(true); // Flag the verifier service to publish the results to the Pact Broker.
->setEnablePending(true) // Flag to enable pending pacts feature (check pact docs for further info)
->setIncludeWipPactSince('2020-01-30') //Start date of WIP Pacts (check pact docs for further info)

// Verify that the files in the array are valid.
$verifier = new Verifier($config);
$verifier->verifyFiles(['C:\SomePath\consumer-provider.json']);

// This will not be reached if the PACT verifier throws an error, otherwise it was successful.
$this->assertTrue(true, 'Pact Verification has failed.');
$verifier->addFile('C:\SomePath\consumer-provider.json');
$this->assertTrue($verifier->verify());
}
```

Expand Down Expand Up @@ -345,7 +323,6 @@ There is a separate repository with an end to end example for both the 2.X and 3
- [2.2.1 tag](https://github.com/mattermack/pact-php-example/tree/2.2.1) for 2.X examples

## Message support
This feature is preliminary as the Pact community as a whole is flushing this out.
The goal is not to test the transmission of an object over a bus but instead vet the contents of the message.
While examples included focus on a Rabbit MQ, the exact message queue is irrelevant. Initial comparisons require a certain
object type to be created by the Publisher/Producer and the Consumer of the message. This includes a metadata set where you
Expand Down Expand Up @@ -385,61 +362,34 @@ $consumerMessage = new ExampleMessageConsumer();
$callback = [$consumerMessage, 'ProcessSong'];
$builder->setCallback($callback);

$builder->verify();
$this->assertTrue($builder->verify());
```


### Provider Side Message Validation
This may evolve as we work through this implementation. The provider relies heavily on callbacks.
Some of the complexity lies in a consumer and provider having many messages and states between the each other in a single pact.
Handle these requests on provider's proxy:

For each message, one needs to provide a single provider state. The name of this provider state must be the key to run
a particular message callback on the provider side. See example\tests\MessageProvider
1. POST /change-state
1. Set up your database to meet the expectations of the request
2. Reset the database to its original state.
2. POST /
1. Return message's content in body
2. Return message's metadata in header `PACT-MESSAGE-METADATA`
3. Proxy to provider app on other requests

1. Create your callbacks and states wrapped in a callable object
1. The array key is a provider state / given() on the consumer side
1. It is helpful to wrap the whole thing in a lambda if you need to customize paramaters to be passed in
1. Choose your verification method
1. If nothing explodes, #winning

```php

$callbacks = array();

// a hello message is a provider state / given() on the consumer side
$callbacks["a hello message"] = function() {
$content = new \stdClass();
$content->text ="Hello Mary";

$metadata = array();
$metadata['queue'] = "myKey";

$provider = (new ExampleMessageProvider())
->setContents($content)
->setMetadata($metadata);

return $provider->Build();
};

$verifier = (new MessageVerifier($config))
->setCallbacks($callbacks)
->verifyFiles([__DIR__ . '/../../output/test_consumer-test_provider.json']);

```
[Click here](/example/src/Provider/public/proxy.php) to see the full sample file.

## Usage for the optional `pact-stub-service`

If you would like to test with fixtures, you can use the `pact-stub-service` like this:

```php
$pactLocation = __DIR__ . '/someconsumer-someprovider.json';
$host = 'localhost';
$port = 7201;
$endpoint = 'test';

$config = (new StubServerConfig())
->setPactLocation($pactLocation)
->setHost($host)
->setFiles($pactLocation)
->setPort($port)
->setEndpoint($endpoint);

Expand Down