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 #3.5: Support branch #299

Closed
Closed
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
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
103 changes: 49 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
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 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)
- [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 @@ -191,6 +178,14 @@ $builder
->willRespondWith($response); // This has to be last. This is what makes an API request to the Mock Server to set the interaction.
```

### Start the Mock Server

Mock server need to be started manually

```php
$builder->createMockServer();
```

### Make the Request

```php
Expand All @@ -204,7 +199,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
25 changes: 25 additions & 0 deletions UPGRADE-9.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
UPGRADE FROM 8.x to 9.0
=======================

* Interaction Builder
* It's now required to call `PhpPact\Consumer\InteractionBuilder::createMockServer` manually

Example Usage:
```php
$builder = new InteractionBuilder($config);
$builder
->given('a person exists')
->uponReceiving('a get request to /hello/{name}')
->with($request)
->willRespondWith($response);
$builder->createMockServer();

$apiClient->sendRequest();

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

* These environment variables can be removed:
* PACT_CORS
* PACT_MOCK_SERVER_HEALTH_CHECK_TIMEOUT
* PACT_MOCK_SERVER_HEALTH_CHECK_RETRY_SEC
18 changes: 17 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
},
"scripts": {
"start-provider": "php -S localhost:58000 -t example/src/Provider/public/",
"static-code-analysis": "phpstan analyse src/ --level=5",
"static-code-analysis": "phpstan analyse src/ --level=5 -c phpstan.neon",
"lint": "php-cs-fixer fix --config .php-cs-fixer.php --dry-run",
"fix": "php-cs-fixer fix --config .php-cs-fixer.php",
"test": "phpunit --debug -c example/phpunit.all.xml"
Expand All @@ -87,6 +87,22 @@
},
"url": "https://github.com/pact-foundation/pact-ruby-standalone/releases/download/v{$version}/pact-{$version}-{$os}{$architecture}.{$extension}",
"path": "bin/pact-ruby-standalone"
},
"pact-ffi-headers": {
"version": "0.4.1",
"url": "https://github.com/pact-foundation/pact-reference/releases/download/libpact_ffi-v{$version}/pact.h",
"path": "bin/pact-ffi-headers/pact.h"
},
"pact-ffi-lib": {
"version": "0.4.1",
"variables": {
"{$prefix}": "PHP_OS_FAMILY === 'Windows' ? 'pact_ffi' : 'libpact_ffi'",
"{$os}": "PHP_OS === 'Darwin' ? 'osx' : strtolower(PHP_OS_FAMILY)",
"{$architecture}": "in_array(php_uname('m'), ['arm64', 'aarch64']) ? (PHP_OS === 'Darwin' ? 'aarch64-apple-darwin' : 'aarch64') : 'x86_64'",
"{$extension}": "PHP_OS_FAMILY === 'Windows' ? 'dll' : (PHP_OS === 'Darwin' ? 'dylib' : 'so')"
},
"url": "https://github.com/pact-foundation/pact-reference/releases/download/libpact_ffi-v{$version}/{$prefix}-{$os}-{$architecture}.{$extension}.gz",
"path": "bin/pact-ffi-lib/pact.{$extension}"
}
}
},
Expand Down
2 changes: 0 additions & 2 deletions example/phpunit.all.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,5 @@
<env name="PACT_CONSUMER_TAG" value="master"/>
<env name="PACT_PROVIDER_NAME" value="someProvider"/>
<env name="PACT_OUTPUT_DIR" value=".\example\output\\"/>
<env name="PACT_CORS" value="true"/>
<env name="PACT_MOCK_SERVER_HEALTH_CHECK_RETRY_SEC" value="2"/>
</php>
</phpunit>
1 change: 0 additions & 1 deletion example/phpunit.consumer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
<env name="PACT_CONSUMER_TAG" value="master"/>
<env name="PACT_PROVIDER_NAME" value="someProvider"/>
<env name="PACT_OUTPUT_DIR" value=".\example\output"/>
<env name="PACT_MOCK_SERVER_HEALTH_CHECK_TIMEOUT" value="10"/>
<!-- <env name="PACT_BROKER_URI" value="http://localhost"/> -->
</php>
</phpunit>
2 changes: 0 additions & 2 deletions example/phpunit.core.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,5 @@
<env name="PACT_CONSUMER_TAG" value="master"/>
<env name="PACT_PROVIDER_NAME" value="someProvider"/>
<env name="PACT_OUTPUT_DIR" value=".\example\output\\"/>
<env name="PACT_CORS" value="true"/>
<env name="PACT_MOCK_SERVER_HEALTH_CHECK_RETRY_SEC" value="2"/>
</php>
</phpunit>
4 changes: 2 additions & 2 deletions example/src/Consumer/Service/HttpClientService.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
class HttpClientService
{
/** @var Client */
private $httpClient;
private Client $httpClient;

/** @var string */
private $baseUri;
private string $baseUri;

public function __construct(string $baseUri)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ public function testGetGoodbyeString()
->uponReceiving('A get request to /goodbye/{name}')
->with($request)
->willRespondWith($response);
$builder->createMockServer();

$service = new HttpClientService($config->getBaseUri());
$result = $service->getGoodbyeString('Bob');

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

$this->assertEquals('Goodbye, Bob', $result);
}
Expand Down
7 changes: 4 additions & 3 deletions example/tests/Consumer/Service/ConsumerServiceHelloTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public function testGetHelloString()
->setStatus(200)
->addHeader('Content-Type', 'application/json')
->setBody([
'message' => $matcher->term('Hello, Bob', '(Hello, )[A-Za-z]')
'message' => $matcher->term('Hello, Bob', '(Hello, )[A-Za-z]+')
]);

// Create a configuration that reflects the server that was started. You can create a custom MockServerConfigInterface if needed.
Expand All @@ -42,12 +42,13 @@ public function testGetHelloString()
$builder
->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 a FFI call to the Mock Server to set the interaction.
$builder->createMockServer();

$service = new HttpClientService($config->getBaseUri()); // Pass in the URL to the Mock Server.
$result = $service->getHelloString('Bob'); // Make the real API request against the Mock Server.

$builder->verify(); // This will verify that the interactions took place.
$this->assertTrue($builder->verify()); // This will verify that the interactions took place.

$this->assertEquals('Hello, Bob', $result); // Make your assertions.
}
Expand Down
3 changes: 3 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
parameters:
excludePaths:
- src/PhpPact/Consumer/Model/Pact.php
1 change: 0 additions & 1 deletion phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,5 @@
<env name="PACT_BROKER_URI" value="http://localhost"/>
<env name="PACT_LOGLEVEL" value="DEBUG"/>
<!-- <env name="PACT_BROKER_BEARER_TOKEN" value="someToken"/> -->
<env name="PACT_CORS" value="true"/>
</php>
</phpunit>
7 changes: 0 additions & 7 deletions src/PhpPact/Consumer/BuilderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,4 @@ interface BuilderInterface
* Verify that the interactions are valid.
*/
public function verify(): bool;

/**
* Write the Pact without deleting the interactions.
*
* @return bool
*/
public function writePact(): bool;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace PhpPact\Consumer\Exception;

use Exception;

/**
* Class InteractionRequestBodyNotAddedException.
*/
class InteractionRequestBodyNotAddedException extends Exception
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace PhpPact\Consumer\Exception;

use Exception;

/**
* Class InteractionResponseBodyNotAddedException.
*/
class InteractionResponseBodyNotAddedException extends Exception
{
}
12 changes: 12 additions & 0 deletions src/PhpPact/Consumer/Exception/MockServerNotStartedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace PhpPact\Consumer\Exception;

use Exception;

/**
* Class MockServerNotStartedException.
*/
class MockServerNotStartedException extends Exception
{
}
12 changes: 12 additions & 0 deletions src/PhpPact/Consumer/Exception/PactFileNotWroteException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace PhpPact\Consumer\Exception;

use Exception;

/**
* Class PactFileNotWroteException.
*/
class PactFileNotWroteException extends Exception
{
}
Loading