diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7697056b..b6cd146b 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -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
diff --git a/README.md b/README.md
index accc88dd..9db9fc45 100644
--- a/README.md
+++ b/README.md
@@ -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
@@ -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
@@ -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
-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.
@@ -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
@@ -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
@@ -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());
}
```
@@ -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());
}
```
@@ -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
@@ -385,47 +362,22 @@ $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`
@@ -433,13 +385,11 @@ If you would like to test with fixtures, you can use the `pact-stub-service` lik
```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);
diff --git a/UPGRADE-9.0.md b/UPGRADE-9.0.md
new file mode 100644
index 00000000..5c11ae09
--- /dev/null
+++ b/UPGRADE-9.0.md
@@ -0,0 +1,50 @@
+UPGRADE FROM 8.x to 9.0
+=======================
+
+* Environment Variables
+ * These environment variables can be removed:
+ * PACT_CORS
+ * PACT_MOCK_SERVER_HEALTH_CHECK_TIMEOUT
+ * PACT_MOCK_SERVER_HEALTH_CHECK_RETRY_SEC
+
+* Verifier
+ * Different pacts sources can be configured via `addXxx` methods
+
+ Example Usage:
+ ```php
+ $config = new VerifierConfig();
+ $config
+ ->setPort(8000)
+ ->setProviderName('someProvider')
+ ->setProviderVersion('1.0.0');
+
+ $url = new Url();
+ $url
+ ->setUrl(new Uri('http://localhost'))
+ ->setProviderName('someProvider')
+ ->setUsername('user')
+ ->setPassword('pass')
+ ->setToken('token');
+
+ $selectors = (new ConsumerVersionSelectors())
+ ->addSelector('{"tag":"foo","latest":true}')
+ ->addSelector('{"tag":"bar","latest":true}');
+
+ $broker = new Broker();
+ $broker
+ ->setUrl(new Uri('http://localhost'))
+ ->setProviderName('someProvider')
+ ->setUsername('user')
+ ->setPassword('pass')
+ ->setToken('token')
+ ->setConsumerVersionSelectors($selectors);
+
+ $verifier = new Verifier($config);
+ $verifier
+ ->addFile('C:\SomePath\consumer-provider.json');
+ ->addDirectory('C:\OtherPath');
+ ->addUrl($url);
+ ->addBroker($broker);
+
+ $this->assertTrue($verifier->verify());
+ ```
diff --git a/composer.json b/composer.json
index 6f3aeae7..c55c721d 100644
--- a/composer.json
+++ b/composer.json
@@ -19,28 +19,14 @@
],
"require": {
"php": "^8.0",
- "ext-openssl": "*",
"ext-json": "*",
- "composer/semver": "^1.4.0|^3.2.0",
- "amphp/amp": "^2.5.1",
- "amphp/byte-stream": "^1.8",
- "amphp/dns": "^1.2.3",
- "amphp/hpack": "^3.1.0",
- "amphp/http-server": "^2.1",
- "amphp/log": "^1.1",
- "amphp/process": "^1.1.1",
- "amphp/serialization": "^1.0",
- "amphp/socket": "^1.1.3",
- "amphp/sync": "^1.4.0",
- "amphp/cache": "^1.4.0",
- "amphp/windows-registry": "v0.3.3",
+ "symfony/process": "^4.4|^5.4|^6.0",
"guzzlehttp/guzzle": "^6.5.8|^7.4.5",
"phpunit/phpunit": ">=8.5.23 <10",
- "tienvx/composer-downloads-plugin": "^1.1.0"
+ "tienvx/composer-downloads-plugin": "^1.2.0"
},
"require-dev": {
"roave/security-advisories": "dev-latest",
- "mockery/mockery": "^1.4.2",
"slim/slim": "^4.6",
"slim/psr7": "^1.2.0",
"friendsofphp/php-cs-fixer": "^3.0",
@@ -66,12 +52,15 @@
"MessageProvider\\": [
"example/src/MessageProvider",
"example/tests/MessageProvider"
+ ],
+ "Provider\\": [
+ "example/src/Provider"
]
}
},
"scripts": {
- "start-provider": "php -S localhost:58000 -t example/src/Provider/public/",
- "static-code-analysis": "phpstan analyse src/ --level=5",
+ "start-provider": "php -S localhost:58000 -t example/src/Provider/public/ example/src/Provider/public/proxy.php",
+ "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"
@@ -83,10 +72,41 @@
"variables": {
"{$os}": "PHP_OS_FAMILY === 'Windows' ? 'win32' : (PHP_OS === 'Darwin' ? 'osx' : 'linux')",
"{$architecture}": "PHP_OS === 'Linux' ? '-x86_64' : ''",
- "{$extension}": "PHP_OS_FAMILY === 'Windows' ? 'zip' : 'tar.gz'"
+ "{$extension}": "PHP_OS_FAMILY === 'Windows' ? 'zip' : 'tar.gz'",
+ "{$keep}": "PHP_OS_FAMILY === 'Windows' ? 'pact-broker.bat' : 'pact-broker'"
},
"url": "https://github.com/pact-foundation/pact-ruby-standalone/releases/download/v{$version}/pact-{$version}-{$os}{$architecture}.{$extension}",
- "path": "bin/pact-ruby-standalone"
+ "path": "bin/pact-ruby-standalone",
+ "ignore": [
+ "bin/*",
+ "!bin/{$keep}"
+ ]
+ },
+ "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}"
+ },
+ "pact-stub-server": {
+ "version": "0.5.2",
+ "variables": {
+ "{$os}": "PHP_OS === 'Darwin' ? 'osx' : strtolower(PHP_OS_FAMILY)",
+ "{$extension}": "PHP_OS_FAMILY === 'Windows' ? '.exe' : ''"
+ },
+ "url": "https://github.com/pact-foundation/pact-stub-server/releases/download/v{$version}/pact-stub-server-{$os}-x86_64{$extension}.gz",
+ "path": "bin/pact-stub-server/pact-stub-server{$extension}",
+ "executable": true
}
}
},
diff --git a/example/README.md b/example/README.md
index bb79b88e..ab66a43b 100644
--- a/example/README.md
+++ b/example/README.md
@@ -16,10 +16,6 @@ All examples could be run within tests.
## Consumer Tests for Message Processing
vendor/bin/phpunit -c example/phpunit.message.consumer.xml
-
-## Provider Verification Tests for Message Processing
-
- vendor/bin/phpunit -c example/phpunit.message.provider.xml
## All tests together
diff --git a/example/pacts/someconsumer-someprovider.json b/example/pacts/someconsumer-someprovider.json
index c502e5ba..f9a78bce 100644
--- a/example/pacts/someconsumer-someprovider.json
+++ b/example/pacts/someconsumer-someprovider.json
@@ -47,7 +47,7 @@
"matchingRules": {
"$.body.message": {
"match": "regex",
- "regex": "(Hello, )[A-Za-z]"
+ "regex": "(Hello, )[A-Za-z]+"
}
}
},
diff --git a/example/pacts/test_consumer-test_provider.json b/example/pacts/test_consumer-test_provider.json
index a015e9ae..ae5e2dab 100644
--- a/example/pacts/test_consumer-test_provider.json
+++ b/example/pacts/test_consumer-test_provider.json
@@ -47,7 +47,7 @@
],
"metadata": {
"pactSpecification": {
- "version": "2.0.0"
+ "version": "3.0.0"
}
}
}
diff --git a/example/phpunit.all.xml b/example/phpunit.all.xml
index 554c8a46..3cc67cbf 100644
--- a/example/phpunit.all.xml
+++ b/example/phpunit.all.xml
@@ -13,9 +13,6 @@
./tests/MessageConsumer
-
- ./tests/MessageProvider
-
@@ -36,7 +33,5 @@
-
-
diff --git a/example/phpunit.consumer.xml b/example/phpunit.consumer.xml
index 6e88b251..0e521db3 100644
--- a/example/phpunit.consumer.xml
+++ b/example/phpunit.consumer.xml
@@ -24,7 +24,6 @@
-
diff --git a/example/phpunit.core.xml b/example/phpunit.core.xml
index aefb2d26..57e6e2e4 100644
--- a/example/phpunit.core.xml
+++ b/example/phpunit.core.xml
@@ -24,7 +24,5 @@
-
-
diff --git a/example/phpunit.message.provider.xml b/example/phpunit.message.provider.xml
deleted file mode 100644
index 2844c97d..00000000
--- a/example/phpunit.message.provider.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
- ./tests/MessageProvider
-
-
-
diff --git a/example/src/Consumer/Service/HttpClientService.php b/example/src/Consumer/Service/HttpClientService.php
index 668ba75b..3ae6ad9d 100644
--- a/example/src/Consumer/Service/HttpClientService.php
+++ b/example/src/Consumer/Service/HttpClientService.php
@@ -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)
{
diff --git a/example/src/Consumer/publish_json_example.php b/example/src/Consumer/publish_json_example.php
deleted file mode 100644
index 9e91327b..00000000
--- a/example/src/Consumer/publish_json_example.php
+++ /dev/null
@@ -1,16 +0,0 @@
- 'someConsumer',
- 'provider' => 'someProvider'
-]);
-
-$httpService->publishJson('1.0.0', $json);
diff --git a/example/src/MessageConsumer/ExampleMessageConsumer.php b/example/src/MessageConsumer/ExampleMessageConsumer.php
index 0a821944..77851aa6 100644
--- a/example/src/MessageConsumer/ExampleMessageConsumer.php
+++ b/example/src/MessageConsumer/ExampleMessageConsumer.php
@@ -4,7 +4,7 @@
class ExampleMessageConsumer
{
- public function ProcessText($message)
+ public function ProcessText(string $message): object
{
$obj = \json_decode($message);
print ' [x] Processed ' . \print_r($obj->contents->text, true) . "\n";
@@ -12,7 +12,7 @@ public function ProcessText($message)
return $obj;
}
- public function ProcessSong($message)
+ public function ProcessSong(string $message): object
{
$obj = \json_decode($message);
print ' [x] Processed ' . \print_r($obj->contents->song, true) . "\n";
diff --git a/example/src/MessageProvider/ExampleMessageProvider.php b/example/src/MessageProvider/ExampleMessageProvider.php
index d038e8f2..49276a56 100644
--- a/example/src/MessageProvider/ExampleMessageProvider.php
+++ b/example/src/MessageProvider/ExampleMessageProvider.php
@@ -5,14 +5,14 @@
class ExampleMessageProvider
{
/** @var array */
- private $metadata;
+ private array $metadata;
/**
* @var mixed
*/
- private $contents;
+ private mixed $contents;
- public function __construct($metadata = [])
+ public function __construct(array $metadata = [])
{
$this->metadata = $metadata;
}
@@ -40,7 +40,7 @@ public function setMetadata(array $metadata): self
/**
* @return mixed
*/
- public function getContents()
+ public function getContents(): mixed
{
return $this->contents;
}
@@ -50,7 +50,7 @@ public function getContents()
*
* @return ExampleMessageProvider
*/
- public function setContents($contents)
+ public function setContents(mixed $contents): self
{
$this->contents = $contents;
@@ -62,7 +62,7 @@ public function setContents($contents)
*
* @return string
*/
- public function Build()
+ public function Build(): string
{
$obj = new \stdClass();
$obj->metadata = $this->metadata;
diff --git a/example/src/Provider/ExampleProvider.php b/example/src/Provider/ExampleProvider.php
new file mode 100644
index 00000000..66a07981
--- /dev/null
+++ b/example/src/Provider/ExampleProvider.php
@@ -0,0 +1,95 @@
+messages = [
+ 'an alligator named Mary exists' => [
+ 'metadata' => [
+ 'queue' => 'wind cries',
+ 'routing_key' => 'wind cries',
+ ],
+ 'contents' => [
+ 'text' => 'Hello Mary',
+ ]
+ ],
+ 'footprints dressed in red' => [
+ 'metadata' => [
+ 'queue' => 'And the clowns have all gone to bed',
+ 'routing_key' => 'And the clowns have all gone to bed',
+ ],
+ 'contents' => [
+ 'song' => 'And the wind whispers Mary',
+ ]
+ ],
+ ];
+ }
+
+ /**
+ * @param string $name
+ *
+ * @return string
+ */
+ public function sayHello(string $name): string
+ {
+ return "Hello, {$name}";
+ }
+
+ /**
+ * @param string $name
+ *
+ * @return string
+ */
+ public function sayGoodbye(string $name): string
+ {
+ return "Goodbye, {$name}";
+ }
+
+ /**
+ * @param string $description
+ * @param array $providerStates
+ *
+ * @return ExampleMessageProvider|null
+ */
+ public function dispatchMessage(string $description, array $providerStates): ?ExampleMessageProvider
+ {
+ if (!isset($this->messages[$description])) {
+ return null;
+ }
+
+ return (new ExampleMessageProvider())
+ ->setMetadata($this->messages[$description]['metadata'])
+ ->setContents($this->messages[$description]['contents']);
+ }
+
+ /**
+ * @param string $action
+ * @param string $state
+ * @param array $params
+ *
+ * @return ExampleMessageProvider
+ */
+ public function changeSate(string $action, string $state, array $params): void
+ {
+ $this->currentState = [
+ 'action' => $action,
+ 'state' => $state,
+ 'params' => $params,
+ ];
+ }
+}
diff --git a/example/src/Provider/public/index.php b/example/src/Provider/public/index.php
index 2a5406ed..42de647d 100644
--- a/example/src/Provider/public/index.php
+++ b/example/src/Provider/public/index.php
@@ -1,5 +1,6 @@
get('/hello/{name}', function (Request $request, Response $response) {
+$provider = new ExampleProvider();
+
+$app->get('/hello/{name}', function (Request $request, Response $response) use ($provider) {
$name = $request->getAttribute('name');
- $response->getBody()->write(\json_encode(['message' => "Hello, {$name}"]));
+ $response->getBody()->write(\json_encode(['message' => $provider->sayHello($name)]));
return $response->withHeader('Content-Type', 'application/json');
});
-$app->get('/goodbye/{name}', function (Request $request, Response $response) {
+$app->get('/goodbye/{name}', function (Request $request, Response $response) use ($provider) {
$name = $request->getAttribute('name');
- $response->getBody()->write(\json_encode(['message' => "Goodbye, {$name}"]));
+ $response->getBody()->write(\json_encode(['message' => $provider->sayGoodbye($name)]));
return $response->withHeader('Content-Type', 'application/json');
});
diff --git a/example/src/Provider/public/proxy.php b/example/src/Provider/public/proxy.php
new file mode 100644
index 00000000..54f04ddb
--- /dev/null
+++ b/example/src/Provider/public/proxy.php
@@ -0,0 +1,41 @@
+addBodyParsingMiddleware();
+
+$provider = new ExampleProvider();
+
+$app->post('/', function (Request $request, Response $response) use ($provider) {
+ $body = $request->getParsedBody();
+ $message = $provider->dispatchMessage($body['description'], $body['providerStates']);
+ if ($message) {
+ $response->getBody()->write(\json_encode($message->getContents()));
+
+ return $response
+ ->withHeader('Content-Type', 'application/json')
+ ->withHeader('pact_message_metadata', \base64_encode(\json_encode($message->getMetadata())));
+ }
+
+ return $response;
+});
+
+$app->post('/change-state', function (Request $request, Response $response) use ($provider) {
+ $body = $request->getParsedBody();
+ $provider->changeSate($body['action'], $body['state'], $body['params']);
+
+ return $response;
+});
+
+try {
+ $app->run();
+} catch (HttpNotFoundException $exception) {
+ return false;
+}
diff --git a/example/tests/Consumer/Service/ConsumerServiceGoodbyeTest.php b/example/tests/Consumer/Service/ConsumerServiceGoodbyeTest.php
index e5106f55..fcae2e02 100644
--- a/example/tests/Consumer/Service/ConsumerServiceGoodbyeTest.php
+++ b/example/tests/Consumer/Service/ConsumerServiceGoodbyeTest.php
@@ -41,7 +41,7 @@ public function testGetGoodbyeString()
$service = new HttpClientService($config->getBaseUri());
$result = $service->getGoodbyeString('Bob');
- $builder->verify();
+ $this->assertTrue($builder->verify());
$this->assertEquals('Goodbye, Bob', $result);
}
diff --git a/example/tests/Consumer/Service/ConsumerServiceHelloTest.php b/example/tests/Consumer/Service/ConsumerServiceHelloTest.php
index 2a80f394..73b6b6f2 100644
--- a/example/tests/Consumer/Service/ConsumerServiceHelloTest.php
+++ b/example/tests/Consumer/Service/ConsumerServiceHelloTest.php
@@ -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.
@@ -42,12 +42,12 @@ 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 FFI calls to register the interaction and start the mock server.
$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.
}
diff --git a/example/tests/MessageConsumer/ExampleMessageConsumerTest.php b/example/tests/MessageConsumer/ExampleMessageConsumerTest.php
index c02006c0..ccc5ffe4 100644
--- a/example/tests/MessageConsumer/ExampleMessageConsumerTest.php
+++ b/example/tests/MessageConsumer/ExampleMessageConsumerTest.php
@@ -2,11 +2,8 @@
namespace MessageConsumer;
-require_once __DIR__ . '/../../src/MessageConsumer/ExampleMessageConsumer.php';
-
-use PhpPact\Broker\Service\BrokerHttpClient;
use PhpPact\Consumer\MessageBuilder;
-use PhpPact\Http\GuzzleClient;
+use PhpPact\Standalone\PactConfigInterface;
use PhpPact\Standalone\PactMessage\PactMessageConfig;
use PHPUnit\Framework\TestCase;
@@ -15,7 +12,7 @@
*/
class ExampleMessageConsumerTest extends TestCase
{
- private static $config;
+ private static PactConfigInterface $config;
public static function setUpBeforeClass(): void
{
@@ -27,18 +24,6 @@ public static function setUpBeforeClass(): void
->setPactDir(__DIR__ . '/../../output/');
}
- public static function tearDownAfterClass(): void
- {
- parent::tearDownAfterClass();
-
- // build out brokerHttpService as your example
- /*
- $brokerHttpService = new BrokerHttpClient(new GuzzleClient(), new Uri($pactBrokerUri));
- $brokerHttpService->publishJson($json, $consumerVersion);
- $brokerHttpService->tag($this->mockServerConfig->getConsumer(), $consumerVersion, $tag);
- */
- }
-
/**
* @throws \Exception
*/
@@ -52,7 +37,7 @@ public function testProcessText()
$metadata = ['queue'=>'wind cries', 'routing_key'=>'wind cries'];
$builder
- ->given('a message', ['foo'])
+ ->given('a message', ['foo' => 'bar'])
->expectsToReceive('an alligator named Mary exists')
->withMetadata($metadata)
->withContent($contents);
@@ -62,11 +47,7 @@ public function testProcessText()
$callback = [$consumerMessage, 'ProcessText'];
$builder->setCallback($callback);
- $hasException = false;
-
- $builder->verify();
-
- $this->assertTrue(true, 'Expects to reach this true statement by running verify()');
+ $this->assertTrue($builder->verify());
}
/**
@@ -92,14 +73,6 @@ public function testProcessSong()
$callback = [$consumerMessage, 'ProcessSong'];
$builder->setCallback($callback);
- $hasException = false;
-
- try {
- $builder->verify();
- } catch (\Exception $e) {
- $hasException = true;
- }
-
- $this->assertFalse($hasException, 'Expects verification to pass without exceptions being thrown');
+ $this->assertTrue($builder->verify());
}
}
diff --git a/example/tests/MessageProvider/ExampleMessageProviderTest.php b/example/tests/MessageProvider/ExampleMessageProviderTest.php
deleted file mode 100644
index 21906049..00000000
--- a/example/tests/MessageProvider/ExampleMessageProviderTest.php
+++ /dev/null
@@ -1,61 +0,0 @@
-text ='Hello Mary';
-
- $metadata = [];
- $metadata['queue'] = 'myKey';
-
- $provider = (new ExampleMessageProvider())
- ->setContents($content)
- ->setMetadata($metadata);
-
- return $provider->Build();
- };
-
- $callbacks['footprints dressed in red'] = function () {
- $content = new \stdClass();
- $content->song ='And the wind whispers Mary';
-
- $metadata = [];
- $metadata['queue'] = 'myKey';
-
- $provider = (new ExampleMessageProvider())
- ->setContents($content)
- ->setMetadata($metadata);
-
- return $provider->Build();
- };
-
- $config = new VerifierConfig();
- $config
- ->setProviderName('someProvider') // Providers name to fetch.
- ->setPublishResults(false); // Flag the verifier service to publish the results to the Pact Broker.
-
- // Verify that the Consumer 'someConsumer' that is tagged with 'master' is valid.
- $verifier = (new MessageVerifier($config))
- ->setCallbacks($callbacks)
- ->verifyFiles([__DIR__ . '/../../pacts/test_consumer-test_provider.json']);
-
- // This will not be reached if the PACT verifier throws an error, otherwise it was successful.
- $this->assertTrue(true, 'Expects to reach true by running verification');
- }
-}
diff --git a/example/tests/Provider/PactVerifyTest.php b/example/tests/Provider/PactVerifyTest.php
index f1927caa..06b1f10e 100644
--- a/example/tests/Provider/PactVerifyTest.php
+++ b/example/tests/Provider/PactVerifyTest.php
@@ -5,8 +5,8 @@
use GuzzleHttp\Psr7\Uri;
use PhpPact\Standalone\ProviderVerifier\Model\VerifierConfig;
use PhpPact\Standalone\ProviderVerifier\Verifier;
-use PhpPact\Standalone\Runner\ProcessRunner;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\Process\Process;
/**
* This is an example on how you could use the included amphp/process wrapper to start your API to run PACT verification against a Provider.
@@ -14,8 +14,8 @@
*/
class PactVerifyTest extends TestCase
{
- /** @var ProcessRunner */
- private $processRunner;
+ /** @var Process */
+ private Process $process;
/**
* Run the PHP build-in web server.
@@ -24,9 +24,10 @@ protected function setUp(): void
{
$publicPath = __DIR__ . '/../../src/Provider/public/';
- $this->processRunner = new ProcessRunner('php', ['-S', 'localhost:7202', '-t', $publicPath]);
+ $this->process = new Process(['php', '-S', 'localhost:7202', '-t', $publicPath, $publicPath . 'proxy.php']);
- $this->processRunner->run();
+ $this->process->start();
+ $this->process->waitUntil(fn () => is_resource(fsockopen('localhost', 7202)));
}
/**
@@ -34,7 +35,7 @@ protected function setUp(): void
*/
protected function tearDown(): void
{
- $this->processRunner->stop();
+ $this->process->stop();
}
/**
@@ -47,14 +48,16 @@ public function testPactVerifyConsumer()
->setProviderName('someProvider') // Providers name to fetch.
->setProviderVersion('1.0.0') // Providers version.
->setProviderBranch('main') // Providers git branch
- ->setProviderBaseUrl(new Uri('http://localhost:7202')) // URL of the Provider.
+ ->setHost('localhost')
+ ->setPort(7202)
+ ->setStateChangeUrl(new Uri('http://localhost:7202/change-state'))
; // Flag the verifier service to publish the results to the Pact Broker.
// Verify that the Consumer 'someConsumer' that is tagged with 'master' is valid.
$verifier = new Verifier($config);
- $verifier->verifyFiles([__DIR__ . '/../../pacts/someconsumer-someprovider.json']);
+ $verifier->addFile(__DIR__ . '/../../pacts/someconsumer-someprovider.json');
+ $verifier->addFile(__DIR__ . '/../../pacts/test_consumer-test_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.');
+ $this->assertTrue($verifier->verify());
}
}
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 00000000..cef56b04
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,6 @@
+parameters:
+ excludePaths:
+ - src/PhpPact/Consumer/Model/AbstractPact.php
+ - src/PhpPact/Consumer/Model/Pact.php
+ - src/PhpPact/Standalone/PactMessage/PactMessage.php
+ - src/PhpPact/Standalone/ProviderVerifier/Verifier.php
diff --git a/phpunit.xml b/phpunit.xml
index 35d425c2..af9ebf68 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -26,6 +26,5 @@
-
diff --git a/src/PhpPact/Broker/Service/BrokerHttpClient.php b/src/PhpPact/Broker/Service/BrokerHttpClient.php
deleted file mode 100644
index e65b46ce..00000000
--- a/src/PhpPact/Broker/Service/BrokerHttpClient.php
+++ /dev/null
@@ -1,112 +0,0 @@
-httpClient = $httpClient;
- $this->baseUri = $baseUri;
- $this->headers = $headers;
-
- if (!\array_key_exists('Content-Type', $headers)) {
- $this->headers['Content-Type'] = 'application/json';
- }
- }
-
- /**
- * {@inheritdoc}
- */
- public function publishJson(string $version, string $json)
- {
- $array = \json_decode($json, true);
- $consumer = $array['consumer']['name'];
- $provider = $array['provider']['name'];
-
- /** @var UriInterface $uri */
- $uri = $this->baseUri->withPath("/pacts/provider/{$provider}/consumer/{$consumer}/version/{$version}");
-
- $this->httpClient->put($uri, [
- 'headers' => $this->headers,
- 'body' => $json,
- ]);
- }
-
- /**
- * {@inheritdoc}
- */
- public function tag(string $consumer, string $version, string $tag)
- {
- /** @var UriInterface $uri */
- $uri = $this->baseUri->withPath("/pacticipants/{$consumer}/versions/{$version}/tags/{$tag}");
- $this->httpClient->put($uri, [
- 'headers' => $this->headers,
- ]);
- }
-
- /**
- * {@inheritdoc}
- */
- public function getAllConsumerUrls(string $provider, string $version = 'latest'): array
- {
- if ($version !== 'latest') {
- @\trigger_error(\sprintf('The second argument "version" in "%s()" method makes no sense and will be removed in any upcoming major version', __METHOD__), E_USER_DEPRECATED);
- }
-
- $uri = $this->baseUri->withPath("/pacts/provider/{$provider}/latest");
-
- $response = $this->httpClient->get($uri, [
- 'headers' => $this->headers,
- ]);
-
- $json = \json_decode($response->getBody()->getContents(), true);
-
- $urls = [];
- foreach ($json['_links']['pacts'] as $pact) {
- $urls[] = $pact['href'];
- }
-
- return $urls;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getAllConsumerUrlsForTag(string $provider, string $tag): array
- {
- $uri = $this->baseUri->withPath("/pacts/provider/{$provider}/latest/{$tag}");
-
- $response = $this->httpClient->get($uri, [
- 'headers' => $this->headers,
- ]);
-
- $json = \json_decode($response->getBody()->getContents(), true);
-
- $urls = [];
- foreach ($json['_links']['pacts'] as $pact) {
- $urls[] = $pact['href'];
- }
-
- return $urls;
- }
-}
diff --git a/src/PhpPact/Broker/Service/BrokerHttpClientInterface.php b/src/PhpPact/Broker/Service/BrokerHttpClientInterface.php
deleted file mode 100644
index 05fb303e..00000000
--- a/src/PhpPact/Broker/Service/BrokerHttpClientInterface.php
+++ /dev/null
@@ -1,61 +0,0 @@
- 'An invalid handle was received. Handles should be created with `pactffi_new_pact`',
+ -2 => 'Transport_config is not valid JSON',
+ -3 => 'The mock server could not be started',
+ -4 => 'The method panicked',
+ -5 => 'The address is not valid',
+ default => 'Unknown error',
+ };
+ parent::__construct($message, $code);
+ }
+}
diff --git a/src/PhpPact/Consumer/Exception/PactFileNotWroteException.php b/src/PhpPact/Consumer/Exception/PactFileNotWroteException.php
new file mode 100644
index 00000000..708e494a
--- /dev/null
+++ b/src/PhpPact/Consumer/Exception/PactFileNotWroteException.php
@@ -0,0 +1,22 @@
+ 'The function panicked.',
+ 2 => 'The pact file was not able to be written.',
+ 3 => 'The pact for the given handle was not found.',
+ default => 'Unknown error',
+ };
+ parent::__construct($message, $code);
+ }
+}
diff --git a/src/PhpPact/Consumer/Hook/ContractDownloader.php b/src/PhpPact/Consumer/Hook/ContractDownloader.php
deleted file mode 100644
index 707d084c..00000000
--- a/src/PhpPact/Consumer/Hook/ContractDownloader.php
+++ /dev/null
@@ -1,98 +0,0 @@
-mockServerConfig = new MockServerEnvConfig();
- }
-
- /**
- * @throws AssertionFailedError
- * @throws RuntimeException
- */
- public function executeAfterLastTest(): void
- {
- try {
- $this->getMockServerService()->verifyInteractions();
- } catch (Exception $e) {
- throw new AssertionFailedError('Pact interaction verification failed', 0, $e);
- }
-
- try {
- \file_put_contents($this->getPactFilename(), $this->getPactJson());
- } catch (Exception $e) {
- throw new RuntimeException('Pact contract generation failed', 0, $e);
- }
- }
-
- private function getMockServerService(): MockServerHttpService
- {
- return new MockServerHttpService(
- $this->getClient(),
- $this->mockServerConfig
- );
- }
-
- private function getClient(): ClientInterface
- {
- if (!$this->client) {
- $this->client = new GuzzleClient();
- }
-
- return $this->client;
- }
-
- private function getPactFilename(): string
- {
- return $this->mockServerConfig->getPactDir()
- . DIRECTORY_SEPARATOR
- . $this->mockServerConfig->getConsumer()
- . '-'
- . $this->mockServerConfig->getProvider() . '.json';
- }
-
- private function getPactJson(): string
- {
- $uri = $this->mockServerConfig->getBaseUri()->withPath('/pact');
- $response = $this->getClient()->post(
- $uri,
- [
- 'headers' => [
- 'Content-Type' => 'application/json',
- 'X-Pact-Mock-Service' => true,
- ],
- 'body' => \json_encode([
- 'consumer' => ['name' => $this->mockServerConfig->getConsumer()],
- 'provider' => ['name' => $this->mockServerConfig->getProvider()]
- ])
- ]
- );
-
- return \json_encode(\json_decode($response->getBody()->getContents()));
- }
-}
diff --git a/src/PhpPact/Consumer/InteractionBuilder.php b/src/PhpPact/Consumer/InteractionBuilder.php
index 93ec41d6..a59696f8 100644
--- a/src/PhpPact/Consumer/InteractionBuilder.php
+++ b/src/PhpPact/Consumer/InteractionBuilder.php
@@ -4,10 +4,9 @@
use PhpPact\Consumer\Model\ConsumerRequest;
use PhpPact\Consumer\Model\Interaction;
+use PhpPact\Consumer\Model\Pact;
use PhpPact\Consumer\Model\ProviderResponse;
-use PhpPact\Http\GuzzleClient;
use PhpPact\Standalone\MockService\MockServerConfigInterface;
-use PhpPact\Standalone\MockService\Service\MockServerHttpService;
/**
* Build an interaction and send it to the Ruby Standalone Mock Service
@@ -15,13 +14,11 @@
*/
class InteractionBuilder implements BuilderInterface
{
- /** @var MockServerHttpService */
- protected $mockServerHttpService;
-
- /** @var MockServerConfigInterface */
- protected $config;
/** @var Interaction */
- private $interaction;
+ protected Interaction $interaction;
+
+ /** @var Pact */
+ protected Pact $pact;
/**
* InteractionBuilder constructor.
@@ -30,9 +27,8 @@ class InteractionBuilder implements BuilderInterface
*/
public function __construct(MockServerConfigInterface $config)
{
- $this->config = $config;
- $this->mockServerHttpService = new MockServerHttpService(new GuzzleClient(), $config);
$this->interaction = new Interaction();
+ $this->pact = new Pact($config);
}
/**
@@ -72,8 +68,6 @@ public function with(ConsumerRequest $request): self
}
/**
- * Make the http request to the Mock Service to register the interaction.
- *
* @param ProviderResponse $response mock of response received
*
* @return bool returns true on success
@@ -82,7 +76,7 @@ public function willRespondWith(ProviderResponse $response): bool
{
$this->interaction->setResponse($response);
- return $this->mockServerHttpService->registerInteraction($this->interaction);
+ return $this->pact->registerInteraction($this->interaction);
}
/**
@@ -90,31 +84,6 @@ public function willRespondWith(ProviderResponse $response): bool
*/
public function verify(): bool
{
- return $this->mockServerHttpService->verifyInteractions();
- }
-
- /**
- * Writes the file to disk and deletes interactions from mock server.
- */
- public function finalize(): bool
- {
- // Write the pact file to disk.
- $this->mockServerHttpService->getPactJson();
-
- // Delete the interactions.
- $this->mockServerHttpService->deleteAllInteractions();
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- */
- public function writePact(): bool
- {
- // Write the pact file to disk.
- $this->mockServerHttpService->getPactJson();
-
- return true;
+ return $this->pact->verifyInteractions();
}
}
diff --git a/src/PhpPact/Consumer/Listener/PactTestListener.php b/src/PhpPact/Consumer/Listener/PactTestListener.php
index 5d88ef18..6e25ccbf 100644
--- a/src/PhpPact/Consumer/Listener/PactTestListener.php
+++ b/src/PhpPact/Consumer/Listener/PactTestListener.php
@@ -3,12 +3,10 @@
namespace PhpPact\Consumer\Listener;
use GuzzleHttp\Psr7\Uri;
-use PhpPact\Broker\Service\BrokerHttpClient;
-use PhpPact\Http\GuzzleClient;
use PhpPact\Standalone\Exception\MissingEnvVariableException;
-use PhpPact\Standalone\MockService\MockServer;
+use PhpPact\Standalone\Broker\Broker;
+use PhpPact\Standalone\Broker\BrokerConfig;
use PhpPact\Standalone\MockService\MockServerEnvConfig;
-use PhpPact\Standalone\MockService\Service\MockServerHttpService;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestListener;
@@ -18,26 +16,25 @@
/**
* PACT listener that can be used with environment variables and easily attached to PHPUnit configuration.
* Class PactTestListener
+ *
+ * @internal
*/
class PactTestListener implements TestListener
{
use TestListenerDefaultImplementation;
- /** @var MockServer */
- private $server;
-
/**
* Name of the test suite configured in your phpunit config.
*
* @var string[]
*/
- private $testSuiteNames;
+ private array $testSuiteNames;
/** @var MockServerEnvConfig */
- private $mockServerConfig;
+ private MockServerEnvConfig $mockServerConfig;
/** @var bool */
- private $failed;
+ private bool $failed = false;
/**
* PactTestListener constructor.
@@ -52,19 +49,6 @@ public function __construct(array $testSuiteNames)
$this->mockServerConfig = new MockServerEnvConfig();
}
- /**
- * @param TestSuite $suite
- *
- * @throws \Exception
- */
- public function startTestSuite(TestSuite $suite): void
- {
- if (\in_array($suite->getName(), $this->testSuiteNames)) {
- $this->server = new MockServer($this->mockServerConfig);
- $this->server->start();
- }
- }
-
public function addError(Test $test, \Throwable $t, float $time): void
{
$this->failed = true;
@@ -83,15 +67,6 @@ public function addFailure(Test $test, AssertionFailedError $e, float $time): vo
public function endTestSuite(TestSuite $suite): void
{
if (\in_array($suite->getName(), $this->testSuiteNames)) {
- try {
- $httpService = new MockServerHttpService(new GuzzleClient(), $this->mockServerConfig);
- $httpService->verifyInteractions();
-
- $json = $httpService->getPactJson();
- } finally {
- $this->server->stop();
- }
-
if ($this->failed === true) {
print 'A unit test has failed. Skipping PACT file upload.';
} elseif (!($pactBrokerUri = \getenv('PACT_BROKER_URI'))) {
@@ -101,29 +76,27 @@ public function endTestSuite(TestSuite $suite): void
} elseif (!($tag = \getenv('PACT_CONSUMER_TAG'))) {
print 'PACT_CONSUMER_TAG environment variable was not set. Skipping PACT file upload.';
} else {
- $clientConfig = [];
+ $brokerConfig = new BrokerConfig();
+ $brokerConfig->setPacticipant($this->mockServerConfig->getConsumer());
+ $brokerConfig->setPactLocations($this->mockServerConfig->getPactDir());
+ $brokerConfig->setBrokerUri(new Uri($pactBrokerUri));
+ $brokerConfig->setConsumerVersion($consumerVersion);
+ $brokerConfig->setVersion($consumerVersion);
+ $brokerConfig->setTag($tag);
if (($user = \getenv('PACT_BROKER_HTTP_AUTH_USER')) &&
($pass = \getenv('PACT_BROKER_HTTP_AUTH_PASS'))
) {
- $clientConfig = [
- 'auth' => [$user, $pass],
- ];
- }
-
- if (($sslVerify = \getenv('PACT_BROKER_SSL_VERIFY'))) {
- $clientConfig['verify'] = $sslVerify !== 'no';
+ $brokerConfig->setBrokerUsername($user);
+ $brokerConfig->setBrokerPassword($pass);
}
- $headers = [];
if ($bearerToken = \getenv('PACT_BROKER_BEARER_TOKEN')) {
- $headers['Authorization'] = 'Bearer ' . $bearerToken;
+ $brokerConfig->setBrokerToken($bearerToken);
}
- $client = new GuzzleClient($clientConfig);
-
- $brokerHttpService = new BrokerHttpClient($client, new Uri($pactBrokerUri), $headers);
- $brokerHttpService->tag($this->mockServerConfig->getConsumer(), $consumerVersion, $tag);
- $brokerHttpService->publishJson($consumerVersion, $json);
+ $broker = new Broker($brokerConfig);
+ $broker->createVersionTag();
+ $broker->publish();
print 'Pact file has been uploaded to the Broker successfully.';
}
}
diff --git a/src/PhpPact/Consumer/Matcher/Matcher.php b/src/PhpPact/Consumer/Matcher/Matcher.php
index 3317c3d5..e8e9cf42 100644
--- a/src/PhpPact/Consumer/Matcher/Matcher.php
+++ b/src/PhpPact/Consumer/Matcher/Matcher.php
@@ -3,7 +3,9 @@
namespace PhpPact\Consumer\Matcher;
/**
- * Matcher implementation. Builds the Ruby Mock Server specification json for interaction publishing.
+ * Matcher implementation. Builds the Pact FFI specification json for interaction publishing.
+ * @see https://docs.pact.io/implementation_guides/rust/pact_ffi/integrationjson
+ *
* Class Matcher.
*/
class Matcher
@@ -46,8 +48,8 @@ public function like($value): array
}
return [
- 'contents' => $value,
- 'json_class' => 'Pact::SomethingLike',
+ 'value' => $value,
+ 'pact:matcher:type' => 'type',
];
}
@@ -61,14 +63,11 @@ public function like($value): array
*/
public function eachLike($value, int $min = 1): array
{
- $result = [
- 'contents' => $value,
- 'json_class' => 'Pact::ArrayLike',
+ return [
+ 'value' => array_fill(0, $min, $value),
+ 'pact:matcher:type' => 'type',
+ 'min' => $min,
];
-
- $result['min'] = $min;
-
- return $result;
}
/**
@@ -92,15 +91,9 @@ public function term($value, string $pattern): array
}
return [
- 'data' => [
- 'generate' => $value,
- 'matcher' => [
- 'json_class' => 'Regexp',
- 'o' => 0,
- 's' => $pattern,
- ],
- ],
- 'json_class' => 'Pact::Term',
+ 'value' => $value,
+ 'regex' => $pattern,
+ 'pact:matcher:type' => 'regex',
];
}
@@ -114,7 +107,7 @@ public function term($value, string $pattern): array
*
* @return array
*/
- public function regex($value, string $pattern)
+ public function regex($value, string $pattern): array
{
return $this->term($value, $pattern);
}
diff --git a/src/PhpPact/Consumer/MessageBuilder.php b/src/PhpPact/Consumer/MessageBuilder.php
index d78d527d..ae7e91fe 100644
--- a/src/PhpPact/Consumer/MessageBuilder.php
+++ b/src/PhpPact/Consumer/MessageBuilder.php
@@ -7,31 +7,27 @@
use PhpPact\Standalone\PactMessage\PactMessage;
/**
- * Build a message and send it to the Ruby Standalone Mock Service
+ * Build a message and send it to the Rust FFI Mock Service
* Class MessageBuilder.
*/
class MessageBuilder implements BuilderInterface
{
/** @var PactMessage */
- protected $pactMessage;
-
- /** @var PactConfigInterface */
- protected $config;
+ protected PactMessage $pactMessage;
/** @var array callable */
- protected $callback;
+ protected array $callback;
/** @var Message */
- private $message;
+ private Message $message;
/**
* @param PactConfigInterface $config
*/
public function __construct(PactConfigInterface $config)
{
- $this->config = $config;
$this->message = new Message();
- $this->pactMessage = new PactMessage();
+ $this->pactMessage = new PactMessage($config);
}
/**
@@ -129,20 +125,18 @@ public function verifyMessage(callable $callback, $description = false): bool
{
$this->setCallback($callback, $description);
- return $this->verify($description);
+ return $this->verify();
}
/**
* Verify the use of the pact by calling the callback
* It also calls finalize to write the pact
*
- * @param false|string $description description of the pact and thus callback
- *
* @throws \Exception if callback is not set
*
* @return bool
*/
- public function verify($description = false): bool
+ public function verify(): bool
{
if (\count($this->callback) < 1) {
throw new \Exception('Callbacks need to exist to run verify.');
@@ -170,9 +164,6 @@ public function verify($description = false): bool
*/
public function writePact(): bool
{
- // you do not want to save the reified json
- $pactJson = \json_encode($this->message);
-
- return $this->pactMessage->update($pactJson, $this->config->getConsumer(), $this->config->getProvider(), $this->config->getPactDir());
+ return $this->pactMessage->update();
}
}
diff --git a/src/PhpPact/Consumer/Model/AbstractPact.php b/src/PhpPact/Consumer/Model/AbstractPact.php
new file mode 100644
index 00000000..564bc3c4
--- /dev/null
+++ b/src/PhpPact/Consumer/Model/AbstractPact.php
@@ -0,0 +1,100 @@
+createFfi()
+ ->newPact()
+ ->withSpecification();
+ }
+
+ private function createFfi(): self
+ {
+ $this->ffi = FFI::cdef(\file_get_contents(Scripts::getHeader()), Scripts::getLibrary());
+
+ return $this;
+ }
+
+ private function newPact(): self
+ {
+ $this->id = $this->ffi->pactffi_new_pact($this->config->getConsumer(), $this->config->getProvider());
+
+ return $this;
+ }
+
+ protected function getSpecification(): int
+ {
+ $supportedVersions = [
+ '1.0.0' => $this->ffi->PactSpecification_V1,
+ '1.1.0' => $this->ffi->PactSpecification_V1_1,
+ '2.0.0' => $this->ffi->PactSpecification_V2,
+ '3.0.0' => $this->ffi->PactSpecification_V3,
+ '4.0.0' => $this->ffi->PactSpecification_V4,
+ ];
+ $version = $this->config->getPactSpecificationVersion();
+ if (isset($supportedVersions[$version])) {
+ $specification = $supportedVersions[$version];
+ } else {
+ trigger_error(sprintf("Specification version '%s' is unknown", $version), E_USER_WARNING);
+ $specification = $this->ffi->PactSpecification_Unknown;
+ }
+
+ return $specification;
+ }
+
+ private function withSpecification(): self
+ {
+ $this->ffi->pactffi_with_specification($this->id, $this->getSpecification());
+
+ return $this;
+ }
+
+ protected function newInteraction(?string $description): int
+ {
+ return $this->ffi->pactffi_new_interaction($this->id, $description);
+ }
+
+ protected function cleanUp(): void
+ {
+ $this->ffi->pactffi_free_pact_handle($this->id);
+ }
+
+ protected function writePact(): void
+ {
+ $error = $this->ffi->pactffi_pact_handle_write_file(
+ $this->id,
+ $this->config->getPactDir(),
+ $this->config->getPactFileWriteMode() === PactConfigInterface::MODE_OVERWRITE
+ );
+ if ($error) {
+ throw new PactFileNotWroteException($error);
+ }
+ }
+
+ protected function withBody(int $interaction, int $part, ?string $contentType = null, ?string $body = null): void
+ {
+ if (is_null($body)) {
+ return;
+ }
+ $success = $this->ffi->pactffi_with_body($interaction, $part, $contentType, $body);
+ if (!$success) {
+ throw new InteractionBodyNotAddedException();
+ }
+ }
+}
diff --git a/src/PhpPact/Consumer/Model/ConsumerRequest.php b/src/PhpPact/Consumer/Model/ConsumerRequest.php
index b54ce104..0efed279 100644
--- a/src/PhpPact/Consumer/Model/ConsumerRequest.php
+++ b/src/PhpPact/Consumer/Model/ConsumerRequest.php
@@ -6,32 +6,32 @@
* Request initiated by the consumer.
* Class ConsumerRequest.
*/
-class ConsumerRequest implements \JsonSerializable
+class ConsumerRequest
{
/**
* @var string
*/
- private $method;
+ private string $method;
/**
- * @var array|string
+ * @var string
*/
- private $path;
+ private string $path;
/**
- * @var string[]
+ * @var array
*/
- private $headers;
+ private array $headers = [];
/**
- * @var mixed
+ * @var null|string
*/
- private $body;
+ private ?string $body = null;
/**
- * @var string
+ * @var array
*/
- private $query;
+ private array $query = [];
/**
* @return string
@@ -54,9 +54,9 @@ public function setMethod(string $method): self
}
/**
- * @return array|string
+ * @return string
*/
- public function getPath()
+ public function getPath(): string
{
return $this->path;
}
@@ -66,17 +66,17 @@ public function getPath()
*
* @return ConsumerRequest
*/
- public function setPath($path): self
+ public function setPath(array|string $path): self
{
- $this->path = $path;
+ $this->path = is_array($path) ? json_encode($path) : $path;
return $this;
}
/**
- * @return string[]
+ * @return array
*/
- public function getHeaders()
+ public function getHeaders(): array
{
return $this->headers;
}
@@ -88,7 +88,10 @@ public function getHeaders()
*/
public function setHeaders(array $headers): self
{
- $this->headers = $headers;
+ $this->headers = [];
+ foreach ($headers as $header => $value) {
+ $this->addHeader($header, $value);
+ }
return $this;
}
@@ -99,17 +102,27 @@ public function setHeaders(array $headers): self
*
* @return ConsumerRequest
*/
- public function addHeader(string $header, $value): self
+ public function addHeader(string $header, array|string $value): self
{
- $this->headers[$header] = $value;
+ $this->headers[$header] = [];
+ if (is_array($value)) {
+ array_walk($value, fn (string $value) => $this->addHeaderValue($header, $value));
+ } else {
+ $this->addHeaderValue($header, $value);
+ }
return $this;
}
+ private function addHeaderValue(string $header, string $value): void
+ {
+ $this->headers[$header][] = $value;
+ }
+
/**
- * @return mixed
+ * @return null|string
*/
- public function getBody()
+ public function getBody(): ?string
{
return $this->body;
}
@@ -119,76 +132,63 @@ public function getBody()
*
* @return ConsumerRequest
*/
- public function setBody($body)
+ public function setBody(mixed $body): self
{
- $this->body = $body;
+ if (\is_string($body)) {
+ $this->body = $body;
+ } elseif (!\is_null($body)) {
+ $this->body = \json_encode($body);
+ $this->addHeader('Content-Type', 'application/json');
+ } else {
+ $this->body = null;
+ }
return $this;
}
/**
- * @return null|string
+ * @return array
*/
- public function getQuery()
+ public function getQuery(): array
{
return $this->query;
}
/**
- * @param string $query
+ * @param array $query
*
* @return ConsumerRequest
*/
- public function setQuery(string $query): self
+ public function setQuery(array $query): self
{
- $this->query = $query;
+ $this->query = [];
+ foreach ($query as $key => $value) {
+ $this->addQueryParameter($key, $value);
+ }
return $this;
}
/**
- * @param string $key
- * @param string $value
+ * @param string $key
+ * @param array|string $value
*
* @return ConsumerRequest
*/
- public function addQueryParameter(string $key, string $value): self
+ public function addQueryParameter(string $key, array|string $value): self
{
- if ($this->query === null) {
- $this->query = "{$key}={$value}";
+ $this->query[$key] = [];
+ if (is_array($value)) {
+ array_walk($value, fn (string $value) => $this->addQueryParameterValue($key, $value));
} else {
- $this->query = "{$this->query}&{$key}={$value}";
+ $this->addQueryParameterValue($key, $value);
}
return $this;
}
- /**
- * {@inheritdoc}
- */
- #[\ReturnTypeWillChange]
- public function jsonSerialize()
+ private function addQueryParameterValue(string $key, string $value): void
{
- $results = [];
-
- $results['method'] = $this->getMethod();
-
- if ($this->getHeaders() !== null) {
- $results['headers'] = $this->getHeaders();
- }
-
- if ($this->getPath() !== null) {
- $results['path'] = $this->getPath();
- }
-
- if ($this->getBody() !== null) {
- $results['body'] = $this->getBody();
- }
-
- if ($this->getQuery() !== null) {
- $results['query'] = $this->getQuery();
- }
-
- return $results;
+ $this->query[$key][] = $value;
}
}
diff --git a/src/PhpPact/Consumer/Model/Interaction.php b/src/PhpPact/Consumer/Model/Interaction.php
index 06577c06..8c40852f 100644
--- a/src/PhpPact/Consumer/Model/Interaction.php
+++ b/src/PhpPact/Consumer/Model/Interaction.php
@@ -6,27 +6,52 @@
* Request/Response Pair to be posted to the Ruby Standalone Mock Server for PACT tests.
* Class Interaction.
*/
-class Interaction implements \JsonSerializable
+class Interaction
{
+ /**
+ * @var int
+ */
+ private int $id;
+
/**
* @var string
*/
- private $description;
+ private string $description;
/**
* @var null|string
*/
- private $providerState;
+ private ?string $providerState = null;
/**
* @var ConsumerRequest
*/
- private $request;
+ private ConsumerRequest $request;
/**
* @var ProviderResponse
*/
- private $response;
+ private ProviderResponse $response;
+
+ /**
+ * @return int
+ */
+ public function getId(): int
+ {
+ return $this->id;
+ }
+
+ /**
+ * @param int $id
+ *
+ * @return Interaction
+ */
+ public function setId(int $id): self
+ {
+ $this->id = $id;
+
+ return $this;
+ }
/**
* @return string
@@ -107,26 +132,4 @@ public function setResponse(ProviderResponse $response): self
return $this;
}
-
- /**
- * {@inheritdoc}
- */
- #[\ReturnTypeWillChange]
- public function jsonSerialize()
- {
- if ($this->getProviderState()) {
- return [
- 'description' => $this->getDescription(),
- 'providerState' => $this->getProviderState(),
- 'request' => $this->getRequest(),
- 'response' => $this->getResponse(),
- ];
- }
-
- return [
- 'description' => $this->getDescription(),
- 'request' => $this->getRequest(),
- 'response' => $this->getResponse(),
- ];
- }
}
diff --git a/src/PhpPact/Consumer/Model/Message.php b/src/PhpPact/Consumer/Model/Message.php
index dbb1679b..c8f25a04 100644
--- a/src/PhpPact/Consumer/Model/Message.php
+++ b/src/PhpPact/Consumer/Model/Message.php
@@ -4,29 +4,54 @@
/**
* Request/Response Pair to be posted to the Ruby Standalone Mock Server for PACT tests.
- * Class Interaction.
+ * Class Message.
*/
-class Message implements \JsonSerializable
+class Message
{
+ /**
+ * @var int
+ */
+ private int $id;
+
/**
* @var string
*/
- private $description;
+ private string $description;
/**
* @var array
*/
- private $providerStates = [];
+ private array $providerStates = [];
/**
* @var array
*/
- private $metadata;
+ private array $metadata;
/**
* @var mixed
*/
- private $contents;
+ private mixed $contents;
+
+ /**
+ * @return int
+ */
+ public function getId(): int
+ {
+ return $this->id;
+ }
+
+ /**
+ * @param int $id
+ *
+ * @return $this
+ */
+ public function setId(int $id): self
+ {
+ $this->id = $id;
+
+ return $this;
+ }
/**
* @return string
@@ -39,7 +64,7 @@ public function getDescription(): string
/**
* @param string $description
*
- * @return Message
+ * @return $this
*/
public function setDescription(string $description): self
{
@@ -73,7 +98,7 @@ public function setProviderState(string $name, array $params = [], $overwrite =
* @param array $params
* @param bool $overwrite - if true reset the entire state
*
- * @return Message
+ * @return $this
*/
public function addProviderState(string $name, array $params, $overwrite = false): self
{
@@ -101,19 +126,27 @@ public function getMetadata(): array
/**
* @param array $metadata
*
- * @return Message
+ * @return $this
*/
public function setMetadata(array $metadata): self
{
- $this->metadata = $metadata;
+ $this->metadata = [];
+ foreach ($metadata as $key => $value) {
+ $this->setMetadataValue($key, $value);
+ }
return $this;
}
+ private function setMetadataValue(string $key, string $value): void
+ {
+ $this->metadata[$key] = $value;
+ }
+
/**
* @return mixed
*/
- public function getContents()
+ public function getContents(): mixed
{
return $this->contents;
}
@@ -121,36 +154,12 @@ public function getContents()
/**
* @param mixed $contents
*
- * @return Message
+ * @return $this
*/
- public function setContents($contents)
+ public function setContents(mixed $contents): self
{
$this->contents = $contents;
return $this;
}
-
- /**
- * {@inheritdoc}
- */
- #[\ReturnTypeWillChange]
- public function jsonSerialize()
- {
- $out = [];
- $out['description'] = $this->getDescription();
-
- if (\count($this->providerStates) > 0) {
- $out['providerStates'] = $this->getProviderStates();
- }
-
- if ($this->metadata) {
- $out['metadata'] = $this->getMetadata();
- }
-
- if ($this->contents) {
- $out['contents'] = $this->getContents();
- }
-
- return $out;
- }
}
diff --git a/src/PhpPact/Consumer/Model/Pact.php b/src/PhpPact/Consumer/Model/Pact.php
new file mode 100644
index 00000000..19f5122b
--- /dev/null
+++ b/src/PhpPact/Consumer/Model/Pact.php
@@ -0,0 +1,136 @@
+initWithLogLevel();
+ }
+
+ private function createMockServer(): void
+ {
+ $port = $this->ffi->pactffi_create_mock_server_for_transport(
+ $this->id,
+ $this->config->getHost(),
+ $this->config->getPort(),
+ $this->config->isSecure() ? 'https' : 'http',
+ null
+ );
+
+ if ($port < 0) {
+ throw new MockServerNotStartedException($port);
+ }
+ $this->config->setPort($port);
+ }
+
+ public function verifyInteractions(): bool
+ {
+ $matched = $this->ffi->pactffi_mock_server_matched($this->config->getPort());
+
+ try {
+ if ($matched) {
+ $this->writePact();
+ }
+ } finally {
+ $this->cleanUp();
+ }
+
+ return $matched;
+ }
+
+ protected function cleanUp(): void
+ {
+ parent::cleanUp();
+ $this->ffi->pactffi_cleanup_mock_server($this->config->getPort());
+ }
+
+ public function registerInteraction(Interaction $interaction): bool
+ {
+ $interaction->setId($this->newInteraction($interaction->getDescription()));
+ $this
+ ->given($interaction)
+ ->uponReceiving($interaction)
+ ->with($interaction)
+ ->willRespondWith($interaction)
+ ->createMockServer();
+
+ return true;
+ }
+
+ private function initWithLogLevel(): self
+ {
+ $logLevel = $this->config->getLogLevel();
+ if ($logLevel) {
+ $this->ffi->pactffi_init_with_log_level($logLevel);
+ }
+
+ return $this;
+ }
+
+ private function given(Interaction $interaction): self
+ {
+ $this->ffi->pactffi_given($interaction->getId(), $interaction->getProviderState());
+
+ return $this;
+ }
+
+ private function uponReceiving(Interaction $interaction): self
+ {
+ $this->ffi->pactffi_upon_receiving($interaction->getId(), $interaction->getDescription());
+
+ return $this;
+ }
+
+ private function with(Interaction $interaction): self
+ {
+ $id = $interaction->getId();
+ $request = $interaction->getRequest();
+ $this->ffi->pactffi_with_request($id, $request->getMethod(), $request->getPath());
+ $this->withHeaders($id, $this->ffi->InteractionPart_Request, $request->getHeaders());
+ $this->withQuery($id, $request->getQuery());
+ $this->withBody($id, $this->ffi->InteractionPart_Request, null, $request->getBody());
+
+ return $this;
+ }
+
+ private function willRespondWith(Interaction $interaction): self
+ {
+ $id = $interaction->getId();
+ $response = $interaction->getResponse();
+ $this->ffi->pactffi_response_status($id, $response->getStatus());
+ $this->withHeaders($id, $this->ffi->InteractionPart_Response, $response->getHeaders());
+ $this->withBody($id, $this->ffi->InteractionPart_Response, null, $response->getBody());
+
+ return $this;
+ }
+
+ private function withHeaders(int $interaction, int $part, array $headers): void
+ {
+ foreach ($headers as $header => $values) {
+ foreach (array_values($values) as $index => $value) {
+ $this->ffi->pactffi_with_header_v2($interaction, $part, (string) $header, (int) $index, (string) $value);
+ }
+ }
+ }
+
+ private function withQuery(int $interaction, array $query): void
+ {
+ foreach ($query as $key => $values) {
+ foreach (array_values($values) as $index => $value) {
+ $this->ffi->pactffi_with_query_parameter_v2($interaction, (string) $key, (int) $index, (string) $value);
+ }
+ }
+ }
+}
diff --git a/src/PhpPact/Consumer/Model/ProviderResponse.php b/src/PhpPact/Consumer/Model/ProviderResponse.php
index d138f555..abeaca25 100644
--- a/src/PhpPact/Consumer/Model/ProviderResponse.php
+++ b/src/PhpPact/Consumer/Model/ProviderResponse.php
@@ -6,22 +6,22 @@
* Response expectation that would be in response to a Consumer request from the Provider.
* Class ProviderResponse.
*/
-class ProviderResponse implements \JsonSerializable
+class ProviderResponse
{
/**
* @var int
*/
- private $status;
+ private int $status;
/**
- * @var null|string[]
+ * @var array
*/
- private $headers;
+ private array $headers = [];
/**
- * @var null|array
+ * @var null|string
*/
- private $body;
+ private ?string $body = null;
/**
* @return int
@@ -44,9 +44,9 @@ public function setStatus(int $status): self
}
/**
- * @return null|string[]
+ * @return array
*/
- public function getHeaders()
+ public function getHeaders(): array
{
return $this->headers;
}
@@ -58,7 +58,10 @@ public function getHeaders()
*/
public function setHeaders(array $headers): self
{
- $this->headers = $headers;
+ $this->headers = [];
+ foreach ($headers as $header => $value) {
+ $this->addHeader($header, $value);
+ }
return $this;
}
@@ -69,51 +72,47 @@ public function setHeaders(array $headers): self
*
* @return ProviderResponse
*/
- public function addHeader(string $header, $value): self
+ public function addHeader(string $header, array|string $value): self
{
- $this->headers[$header] = $value;
+ $this->headers[$header] = [];
+ if (is_array($value)) {
+ array_walk($value, fn (string $value) => $this->addHeaderValue($header, $value));
+ } else {
+ $this->addHeaderValue($header, $value);
+ }
return $this;
}
- /**
- * @return mixed
- */
- public function getBody()
+ private function addHeaderValue(string $header, string $value): void
{
- return $this->body;
+ $this->headers[$header][] = $value;
}
/**
- * @param iterable $body
- *
- * @return ProviderResponse
+ * @return null|string
*/
- public function setBody($body): self
+ public function getBody(): ?string
{
- $this->body = $body;
-
- return $this;
+ return $this->body;
}
/**
- * {@inheritdoc}
+ * @param mixed $body
+ *
+ * @return ProviderResponse
*/
- #[\ReturnTypeWillChange]
- public function jsonSerialize()
+ public function setBody(mixed $body): self
{
- $results = [
- 'status' => $this->getStatus(),
- ];
-
- if ($this->getHeaders() !== null) {
- $results['headers'] = $this->getHeaders();
- }
-
- if ($this->getBody() !== null) {
- $results['body'] = $this->getBody();
+ if (\is_string($body)) {
+ $this->body = $body;
+ } elseif (!\is_null($body)) {
+ $this->body = \json_encode($body);
+ $this->addHeader('Content-Type', 'application/json');
+ } else {
+ $this->body = null;
}
- return $results;
+ return $this;
}
}
diff --git a/src/PhpPact/Provider/MessageVerifier.php b/src/PhpPact/Provider/MessageVerifier.php
deleted file mode 100644
index d0ea1987..00000000
--- a/src/PhpPact/Provider/MessageVerifier.php
+++ /dev/null
@@ -1,245 +0,0 @@
-callbacks = [];
-
- $baseUrl = @$this->config->getProviderBaseUrl();
- if (!$baseUrl) {
- $config->setProviderBaseUrl(new Uri("http://{$this->defaultProxyHost}:{$this->defaultProxyPort}"));
- }
-
- // default verification delay
- $this->setVerificationDelaySec(\floor($config->getProcessIdleTimeout() / $this->defaultDelayFactor));
- }
-
- /**
- * @param array $callbacks
- *
- * @return self
- */
- public function setCallbacks(array $callbacks): self
- {
- $this->callbacks = $callbacks;
-
- return $this;
- }
-
- /**
- * Add an individual call back
- *
- * @param string $key
- * @param callable $callback
- *
- * @throws \Exception
- *
- * @return MessageVerifier
- */
- public function addCallback(string $key, callable $callback): self
- {
- if (!isset($this->callbacks[$key])) {
- $this->callbacks[$key] = $callback;
- } else {
- throw new \Exception("Callback with key ($key) already exists");
- }
-
- return $this;
- }
-
- /**
- * @param float $verificationDelaySec
- *
- * @return MessageVerifier
- */
- public function setVerificationDelaySec(float $verificationDelaySec): self
- {
- $this->verificationDelaySec = $verificationDelaySec;
-
- return $this;
- }
-
- /**
- * @param Logger $logger
- *
- * @return MessageVerifier
- */
- public function setLogger(Logger $logger): self
- {
- $this->logger = $logger;
-
- return $this;
- }
-
- /**
- * @param array $arguments
- *
- * @throws \Exception
- */
- protected function verifyAction(array $arguments)
- {
- if (\count($this->callbacks) < 1) {
- throw new \Exception('Callback needs to bet set when using message pacts');
- }
-
- $callbacks = $this->callbacks;
- $uri = $this->config->getProviderBaseUrl();
-
- $arguments = \array_merge([Scripts::getProviderVerifier()], $arguments);
-
- /**
- * @throws \Amp\Socket\SocketException
- * @throws \Error
- * @throws \TypeError
- *
- * @return \Generator
- */
- $lambdaLoop = function () use ($callbacks, $arguments, $uri) {
- // spin up a server
- $url = "{$uri->getHost()}:{$uri->getPort()}";
- $servers = [
- Socket\Server::listen($url)
- ];
-
- $logger = $this->getLogger();
-
- $server = new Server($servers, new CallableRequestHandler(function (Request $request) use ($callbacks) {
- if (\count($callbacks) === 1) {
- $callback = \array_pop($callbacks);
- } else {
- $payload = new Payload($request->getBody());
- $requestBody = yield $payload->buffer();
- $requestBody = \json_decode($requestBody);
- $description = $requestBody->description;
-
- $callback = false;
-
- if (isset($this->callbacks[$description])) {
- $callback = $this->callbacks[$description];
- }
-
- if ($callback === false) {
- throw new \Exception("Pacts with multiple states need to have callbacks key'ed by the description");
- }
- }
-
- //@todo pass $providerStates to the call back
- $out = \call_user_func($callback);
-
- // return response should only happen if the \call_user_fun()
- return new Response(Status::OK, [
- 'content-type' => 'application/json;',
- ], $out);
- }), $logger);
-
- yield $server->start();
-
- // delay long enough for the server to be stood up
- $delay = (int) ($this->verificationDelaySec * 1000);
-
- // call the provider-verification cmd
- Loop::delay($delay, function () use ($arguments) {
- $cmd = \implode(' ', $arguments);
- $process = new Process($cmd);
- yield $process->start();
-
- $payload = new Payload($process->getStdout());
- print yield $payload->buffer();
-
- $code = yield $process->join();
-
- // if the provider verification cmd returns a non-zero number, the test failed
- if ($code !== 0) {
- $this->getLogger()->warning(yield $process->getStderr()->read());
-
- throw new \Exception("Pact failed to validate. Exit code: {$code}");
- }
-
- Loop::stop();
- });
- };
-
- Loop::run($lambdaLoop);
- }
-
- /**
- * @return Logger
- */
- private function getLogger()
- {
- if (null === $this->logger) {
- $logHandler = new StreamHandler(new ResourceOutputStream(\STDOUT));
- $logHandler->setFormatter(new ConsoleFormatter(null, null, true));
- $this->logger = new Logger('server');
- $this->logger->pushHandler($logHandler);
- }
-
- return $this->logger;
- }
-}
diff --git a/src/PhpPact/Standalone/Broker/Broker.php b/src/PhpPact/Standalone/Broker/Broker.php
index 910706be..c5d44ccb 100644
--- a/src/PhpPact/Standalone/Broker/Broker.php
+++ b/src/PhpPact/Standalone/Broker/Broker.php
@@ -2,53 +2,30 @@
namespace PhpPact\Standalone\Broker;
-use Amp\ByteStream\ResourceOutputStream;
-use Amp\Log\ConsoleFormatter;
-use Amp\Log\StreamHandler;
-use Monolog\Logger;
use PhpPact\Standalone\Installer\Model\Scripts;
-use PhpPact\Standalone\Runner\ProcessRunner;
+use Symfony\Component\Process\Exception\ProcessFailedException;
+use Symfony\Component\Process\Process;
class Broker
{
- /** @var Logger */
- private $logger;
/** @var BrokerConfig */
- private $config;
+ private BrokerConfig $config;
/** @var string */
- private $command;
+ private string $command;
public function __construct(BrokerConfig $config)
{
$this->config = $config;
$this->command = Scripts::getBroker();
- $this->logger = (new Logger('console'))
- ->pushHandler(
- (new StreamHandler(new ResourceOutputStream(\STDOUT)))
- ->setFormatter(new ConsoleFormatter(null, null, true))
- );
}
- public function canIDeploy()
+ public function canIDeploy(): array
{
- $runner = new ProcessRunner(
- $this->command,
- \array_merge(
- [
- 'can-i-deploy',
- '--pacticipant=' . $this->config->getPacticipant(),
- '--version=' . $this->config->getVersion()
- ],
- $this->getArguments()
- )
- );
- $runner->runBlocking();
-
- if ($runner->getExitCode() !== 0) {
- throw new \Exception($runner->getStderr());
- }
-
- return \json_decode($runner->getOutput(), true);
+ return $this->run([
+ 'can-i-deploy',
+ '--pacticipant=' . $this->config->getPacticipant(),
+ '--version=' . $this->config->getVersion()
+ ]);
}
/**
@@ -77,149 +54,71 @@ public function getArguments(): array
return $parameters;
}
- public function createOrUpdatePacticipant()
+ public function createOrUpdatePacticipant(): array
{
- $runner = new ProcessRunner(
- $this->command,
- \array_merge(
- [
- 'create-or-update-pacticipant',
- '--name=' . $this->config->getName(),
- '--repository-url=' . $this->config->getRepositoryUrl(),
- ],
- $this->getArguments()
- )
- );
- $runner->runBlocking();
-
- if ($runner->getExitCode() !== 0) {
- throw new \Exception($runner->getStderr());
- }
-
- return \json_decode($runner->getOutput(), true);
+ return $this->run([
+ 'create-or-update-pacticipant',
+ '--name=' . $this->config->getName(),
+ '--repository-url=' . $this->config->getRepositoryUrl(),
+ ]);
}
- public function createOrUpdateWebhook()
+ public function createOrUpdateWebhook(): array
{
- $runner = new ProcessRunner(
- $this->command,
- \array_merge(
- [
- 'create-or-update-webhook',
- $this->config->getUrl(),
- '--request=' . $this->config->getRequest(),
- '--header=' . $this->config->getHeader(),
- '--data=' . $this->config->getData(),
- '--user=' . $this->config->getUser(),
- '--consumer=' . $this->config->getConsumer(),
- '--provider=' . $this->config->getProvider(),
- '--description=' . $this->config->getDescription(),
- '--uuid=' . $this->config->getUuid(),
- ],
- $this->getArguments()
- )
- );
- $runner->runBlocking();
-
- if ($runner->getExitCode() !== 0) {
- throw new \Exception($runner->getStderr());
- }
-
- return \json_decode($runner->getOutput(), true);
+ return $this->run([
+ 'create-or-update-webhook',
+ $this->config->getUrl(),
+ '--request=' . $this->config->getRequest(),
+ '--header=' . $this->config->getHeader(),
+ '--data=' . $this->config->getData(),
+ '--user=' . $this->config->getUser(),
+ '--consumer=' . $this->config->getConsumer(),
+ '--provider=' . $this->config->getProvider(),
+ '--description=' . $this->config->getDescription(),
+ '--uuid=' . $this->config->getUuid(),
+ ]);
}
- public function createVersionTag()
+ public function createVersionTag(): array
{
- $runner = new ProcessRunner(
- $this->command,
- \array_merge(
- [
- 'create-version-tag',
- '--pacticipant=' . $this->config->getPacticipant(),
- '--version=' . $this->config->getVersion(),
- '--tag=' . $this->config->getTag(),
- ],
- $this->getArguments()
- )
- );
- $runner->runBlocking();
-
- if ($runner->getExitCode() !== 0) {
- throw new \Exception($runner->getStderr());
- }
-
- return \json_decode($runner->getOutput(), true);
+ return $this->run([
+ 'create-version-tag',
+ '--pacticipant=' . $this->config->getPacticipant(),
+ '--version=' . $this->config->getVersion(),
+ '--tag=' . $this->config->getTag(),
+ ]);
}
- public function createWebhook()
+ public function createWebhook(): array
{
- $runner = new ProcessRunner(
- $this->command,
- \array_merge(
- [
- 'create-webhook',
- $this->config->getUrl(),
- '--request=' . $this->config->getRequest(),
- '--header=' . $this->config->getHeader(),
- '--data=' . $this->config->getData(),
- '--user=' . $this->config->getUser(),
- '--consumer=' . $this->config->getConsumer(),
- '--provider=' . $this->config->getProvider(),
- '--description=' . $this->config->getDescription(),
- ],
- $this->getArguments()
- )
- );
- $runner->runBlocking();
-
- if ($runner->getExitCode() !== 0) {
- throw new \Exception($runner->getStderr());
- }
-
- return \json_decode($runner->getOutput(), true);
+ return $this->run([
+ 'create-webhook',
+ $this->config->getUrl(),
+ '--request=' . $this->config->getRequest(),
+ '--header=' . $this->config->getHeader(),
+ '--data=' . $this->config->getData(),
+ '--user=' . $this->config->getUser(),
+ '--consumer=' . $this->config->getConsumer(),
+ '--provider=' . $this->config->getProvider(),
+ '--description=' . $this->config->getDescription(),
+ ]);
}
- public function describeVersion()
+ public function describeVersion(): array
{
- $runner = new ProcessRunner(
- $this->command,
- \array_merge(
- [
- 'describe-version',
- '--pacticipant=' . $this->config->getPacticipant(),
- '--output=json',
- ],
- $this->getArguments()
- )
- );
- $runner->runBlocking();
-
- if ($runner->getExitCode() !== 0) {
- throw new \Exception($runner->getStderr());
- }
-
- return \json_decode($runner->getOutput(), true);
+ return $this->run([
+ 'describe-version',
+ '--pacticipant=' . $this->config->getPacticipant(),
+ '--output=json',
+ ]);
}
- public function listLatestPactVersions()
+ public function listLatestPactVersions(): array
{
- $runner = new ProcessRunner(
- $this->command,
- \array_merge(
- [
- 'list-latest-pact-versions',
- '--output=json',
- ],
- $this->getArguments()
- )
- );
- $runner->runBlocking();
-
- if ($runner->getExitCode() !== 0) {
- throw new \Exception($runner->getStderr());
- }
-
- return \json_decode($runner->getOutput(), true);
+ return $this->run([
+ 'list-latest-pact-versions',
+ '--output=json',
+ ]);
}
public function publish(): void
@@ -238,48 +137,54 @@ public function publish(): void
$options[] = '--tag=' . $this->config->getTag();
}
- $runner = new ProcessRunner(
+ $process = new Process([
$this->command,
- \array_merge(
- $options,
- $this->getArguments()
- )
- );
- $runner->runBlocking();
-
- if ($runner->getExitCode() !== 0) {
- $this->logger->error($runner->getStderr());
+ ...$options,
+ ...$this->getArguments(),
+ ]);
+
+ $process->run(function ($type, $buffer) {
+ if (Process::ERR === $type) {
+ fputs(STDERR, $buffer);
+ } else {
+ fputs(STDOUT, $buffer);
+ }
+ });
+
+ if (!$process->isSuccessful()) {
+ throw new ProcessFailedException($process);
}
-
- $this->logger->debug($runner->getOutput());
}
- public function testWebhook()
+ public function testWebhook(): array
{
- $runner = new ProcessRunner(
- $this->command,
- \array_merge(
- [
- 'test-webhook',
- '--uuid=' . $this->config->getUuid(),
- ],
- $this->getArguments()
- )
- );
- $runner->runBlocking();
+ return $this->run([
+ 'test-webhook',
+ '--uuid=' . $this->config->getUuid(),
+ ]);
+ }
- if ($runner->getExitCode() !== 0) {
- throw new \Exception($runner->getStderr());
- }
+ public function generateUuid(): string
+ {
+ $process = new Process([$this->command, 'generate-uuid']);
+ $process->run();
- return \json_decode($runner->getOutput(), true);
+ return \rtrim($process->getOutput());
}
- public function generateUuid(): string
+ protected function run(array $options): array
{
- $runner = new ProcessRunner($this->command, ['generate-uuid']);
- $runner->runBlocking();
+ $process = new Process([
+ $this->command,
+ ...$options,
+ ...$this->getArguments(),
+ ]);
+ $process->run();
+
+ if (!$process->isSuccessful()) {
+ throw new ProcessFailedException($process);
+ }
- return \rtrim($runner->getOutput());
+ return \json_decode($process->getOutput(), true);
}
}
diff --git a/src/PhpPact/Standalone/Broker/BrokerConfig.php b/src/PhpPact/Standalone/Broker/BrokerConfig.php
index e54f668e..8e12ee45 100644
--- a/src/PhpPact/Standalone/Broker/BrokerConfig.php
+++ b/src/PhpPact/Standalone/Broker/BrokerConfig.php
@@ -7,55 +7,55 @@
class BrokerConfig
{
/** @var null|UriInterface */
- private $brokerUri;
+ private ?UriInterface $brokerUri = null;
/** @var null|string */
- private $brokerToken;
+ private ?string $brokerToken = null;
/** @var null|string */
- private $brokerUsername;
+ private ?string $brokerUsername = null;
/** @var null|string */
- private $brokerPassword;
+ private ?string $brokerPassword = null;
/** @var bool */
- private $verbose = false;
+ private bool $verbose = false;
/** @var null|string */
- private $pacticipant;
+ private ?string $pacticipant = null;
/** @var null|string */
- private $request;
+ private ?string $request = null;
/** @var null|string */
- private $header;
+ private ?string $header = null;
/** @var null|string */
- private $data;
+ private ?string $data = null;
/** @var null|string */
- private $user;
+ private ?string $user = null;
/** @var null|string */
- private $consumer;
+ private ?string $consumer = null;
/** @var null|string */
- private $provider;
+ private ?string $provider = null;
/** @var null|string */
- private $description;
+ private ?string $description = null;
/** @var null|string */
- private $uuid;
+ private ?string $uuid = null;
/** @var null|string */
- private $version;
+ private ?string $version = null;
/** @var null|string */
- private $branch = null;
+ private ?string $branch = null;
/** @var null|string */
- private $tag = null;
+ private ?string $tag = null;
/** @var null|string */
- private $name;
+ private ?string $name = null;
/** @var null|string */
- private $repositoryUrl;
+ private ?string $repositoryUrl = null;
/** @var null|string */
- private $url;
+ private ?string $url = null;
/** @var null|string */
- private $consumerVersion;
+ private ?string $consumerVersion = null;
/** @var null|string */
- private $pactLocations;
+ private ?string $pactLocations = null;
/**
* @return null|string
@@ -68,7 +68,7 @@ public function getRepositoryUrl(): ?string
/**
* @param null|string $repositoryUrl
*
- * @return BrokerConfig
+ * @return $this
*/
public function setRepositoryUrl(?string $repositoryUrl): self
{
@@ -88,7 +88,7 @@ public function getUrl(): ?string
/**
* @param null|string $url
*
- * @return BrokerConfig
+ * @return $this
*/
public function setUrl(?string $url): self
{
@@ -108,7 +108,7 @@ public function getVersion(): ?string
/**
* @param null|string $version
*
- * @return BrokerConfig
+ * @return $this
*/
public function setVersion(?string $version): self
{
@@ -128,7 +128,7 @@ public function getBranch(): ?string
/**
* @param null|string $branch
*
- * @return BrokerConfig
+ * @return $this
*/
public function setBranch(?string $branch): self
{
@@ -148,7 +148,7 @@ public function getTag(): ?string
/**
* @param null|string $tag
*
- * @return BrokerConfig
+ * @return $this
*/
public function setTag(?string $tag): self
{
@@ -168,7 +168,7 @@ public function getName(): ?string
/**
* @param null|string $name
*
- * @return BrokerConfig
+ * @return $this
*/
public function setName(?string $name): self
{
@@ -188,7 +188,7 @@ public function getRequest(): ?string
/**
* @param null|string $request
*
- * @return BrokerConfig
+ * @return $this
*/
public function setRequest(?string $request): self
{
@@ -208,7 +208,7 @@ public function getHeader(): ?string
/**
* @param null|string $header
*
- * @return BrokerConfig
+ * @return $this
*/
public function setHeader(?string $header): self
{
@@ -228,7 +228,7 @@ public function getData(): ?string
/**
* @param null|string $data
*
- * @return BrokerConfig
+ * @return $this
*/
public function setData(?string $data): self
{
@@ -248,7 +248,7 @@ public function getUser(): ?string
/**
* @param null|string $user
*
- * @return BrokerConfig
+ * @return $this
*/
public function setUser(?string $user): self
{
@@ -268,7 +268,7 @@ public function getConsumer(): ?string
/**
* @param null|string $consumer
*
- * @return BrokerConfig
+ * @return $this
*/
public function setConsumer(?string $consumer): self
{
@@ -288,7 +288,7 @@ public function getProvider(): ?string
/**
* @param null|string $provider
*
- * @return BrokerConfig
+ * @return $this
*/
public function setProvider(?string $provider): self
{
@@ -308,7 +308,7 @@ public function getDescription(): ?string
/**
* @param null|string $description
*
- * @return BrokerConfig
+ * @return $this
*/
public function setDescription(?string $description): self
{
@@ -328,7 +328,7 @@ public function getUuid(): ?string
/**
* @param null|string $uuid
*
- * @return BrokerConfig
+ * @return $this
*/
public function setUuid(?string $uuid): self
{
@@ -337,11 +337,26 @@ public function setUuid(?string $uuid): self
return $this;
}
- public function isVerbose()
+ /**
+ * @return bool
+ */
+ public function isVerbose(): bool
{
return $this->verbose;
}
+ /**
+ * @param bool $verbose
+ *
+ * @return $this
+ */
+ public function setVerbose(bool $verbose): self
+ {
+ $this->verbose = $verbose;
+
+ return $this;
+ }
+
/**
* @return null|UriInterface
*/
@@ -352,6 +367,8 @@ public function getBrokerUri(): ?UriInterface
/**
* @param null|UriInterface $brokerUri
+ *
+ * @return $this
*/
public function setBrokerUri(?UriInterface $brokerUri): self
{
@@ -370,6 +387,8 @@ public function getBrokerToken(): ?string
/**
* @param null|string $brokerToken
+ *
+ * @return $this
*/
public function setBrokerToken(?string $brokerToken): self
{
@@ -388,6 +407,8 @@ public function getBrokerUsername(): ?string
/**
* @param null|string $brokerUsername
+ *
+ * @return $this
*/
public function setBrokerUsername(?string $brokerUsername): self
{
@@ -406,6 +427,8 @@ public function getBrokerPassword(): ?string
/**
* @param null|string $brokerPassword
+ *
+ * @return $this
*/
public function setBrokerPassword(?string $brokerPassword): self
{
@@ -414,13 +437,18 @@ public function setBrokerPassword(?string $brokerPassword): self
return $this;
}
- public function getPacticipant()
+ /**
+ * @return null|string
+ */
+ public function getPacticipant(): ?string
{
return $this->pacticipant;
}
/**
* @param null|string $pacticipant
+ *
+ * @return $this
*/
public function setPacticipant(?string $pacticipant): self
{
@@ -440,7 +468,7 @@ public function getConsumerVersion(): ?string
/**
* @param null|string $consumerVersion
*
- * @return BrokerConfig
+ * @return $this
*/
public function setConsumerVersion(?string $consumerVersion): self
{
@@ -460,7 +488,7 @@ public function getPactLocations(): ?string
/**
* @param string $locations
*
- * @return BrokerConfig
+ * @return $this
*/
public function setPactLocations(string $locations): self
{
diff --git a/src/PhpPact/Standalone/Exception/HealthCheckFailedException.php b/src/PhpPact/Standalone/Exception/HealthCheckFailedException.php
deleted file mode 100644
index 665ad684..00000000
--- a/src/PhpPact/Standalone/Exception/HealthCheckFailedException.php
+++ /dev/null
@@ -1,17 +0,0 @@
-config = $config;
-
- if (!$httpService) {
- $this->httpService = new MockServerHttpService(new GuzzleClient(), $this->config);
- } else {
- $this->httpService = $httpService;
- }
- }
-
- /**
- * Start the Mock Server. Verify that it is running.
- *
- * @throws Exception
- *
- * @return int process ID of the started Mock Server
- */
- public function start(): int
- {
- $this->processRunner = new ProcessRunner(Scripts::getMockService(), $this->getArguments());
-
- $processId = $this->processRunner->run();
-
- $result = $this->verifyHealthCheck();
- if ($result) {
- $retrySec = $this->config->getHealthCheckRetrySec();
- \sleep($retrySec);
- }
-
- return $processId;
- }
-
- /**
- * Stop the Mock Server process.
- *
- * @return bool Was stopping successful?
- */
- public function stop(): bool
- {
- return $this->processRunner->stop();
- }
-
- /**
- * Build an array of command arguments.
- *
- * @return array
- */
- private function getArguments(): array
- {
- $results = [];
-
- $logLevel = $this->config->getLogLevel();
- $consumer = \escapeshellarg($this->config->getConsumer());
- $provider = \escapeshellarg($this->config->getProvider());
- $pactDir = \escapeshellarg($this->config->getPactDir());
-
- $results[] = 'service';
- $results[] = "--consumer={$consumer}";
- $results[] = "--provider={$provider}";
- $results[] = "--pact-dir={$pactDir}";
- $results[] = "--pact-file-write-mode={$this->config->getPactFileWriteMode()}";
- $results[] = "--host={$this->config->getHost()}";
- $results[] = "--port={$this->config->getPort()}";
-
- if ($logLevel) {
- $results[] = \sprintf('--log-level=%s', \escapeshellarg($logLevel));
- }
-
- if ($this->config->hasCors()) {
- $results[] = '--cors=true';
- }
-
- if ($this->config->getPactSpecificationVersion() !== null) {
- $results[] = "--pact-specification-version={$this->config->getPactSpecificationVersion()}";
- }
-
- if (!empty($this->config->getLog())) {
- $log = \escapeshellarg($this->config->getLog());
- $results[] = \sprintf('--log=%s', $log);
- }
-
- return $results;
- }
-
- /**
- * Make sure the server starts as expected.
- *
- * @throws Exception
- *
- * @return bool
- */
- private function verifyHealthCheck(): bool
- {
- $service = $this->httpService;
-
- // Verify that the service is up.
- $tries = 0;
- $maxTries = $this->config->getHealthCheckTimeout();
- $retrySec = $this->config->getHealthCheckRetrySec();
- do {
- ++$tries;
-
- try {
- return $service->healthCheck();
- } catch (ConnectionException $e) {
- \sleep($retrySec);
- }
- } while ($tries <= $maxTries);
-
- throw new HealthCheckFailedException("Failed to make connection to Mock Server in {$maxTries} attempts.");
- }
-}
diff --git a/src/PhpPact/Standalone/MockService/MockServerConfig.php b/src/PhpPact/Standalone/MockService/MockServerConfig.php
index 3b5c7621..05a03364 100644
--- a/src/PhpPact/Standalone/MockService/MockServerConfig.php
+++ b/src/PhpPact/Standalone/MockService/MockServerConfig.php
@@ -2,98 +2,34 @@
namespace PhpPact\Standalone\MockService;
-use Composer\Semver\VersionParser;
use GuzzleHttp\Psr7\Uri;
-use PhpPact\Standalone\PactConfigInterface;
+use PhpPact\Standalone\PactConfig;
use Psr\Http\Message\UriInterface;
/**
* Configuration defining the default PhpPact Ruby Standalone server.
* Class MockServerConfig.
*/
-class MockServerConfig implements MockServerConfigInterface, PactConfigInterface
+class MockServerConfig extends PactConfig implements MockServerConfigInterface
{
/**
* Host on which to bind the service.
*
* @var string
*/
- private $host = 'localhost';
+ private string $host = 'localhost';
/**
- * Port on which to run the service.
+ * Port on which to run the service. A value of zero will result in the operating system allocating an available port.
*
* @var int
*/
- private $port = 7200;
+ private int $port = 7200;
/**
* @var bool
*/
- private $secure = false;
-
- /**
- * Consumer name.
- *
- * @var string
- */
- private $consumer;
-
- /**
- * Provider name.
- *
- * @var string
- */
- private $provider;
-
- /**
- * Directory to which the pacts will be written.
- *
- * @var string
- */
- private $pactDir;
-
- /**
- * `overwrite` or `merge`. Use `merge` when running multiple mock service
- * instances in parallel for the same consumer/provider pair. Ensure the
- * pact file is deleted before running tests when using this option so that
- * interactions deleted from the code are not maintained in the file.
- *
- * @var string
- */
- private $pactFileWriteMode = 'overwrite';
-
- /**
- * The pact specification version to use when writing the pact. Note that only versions 1 and 2 are currently supported.
- *
- * @var string
- */
- private $pactSpecificationVersion;
-
- /**
- * File to which to log output.
- *
- * @var string
- */
- private $log;
-
- /** @var bool */
- private $cors = false;
-
- /**
- * The max allowed attempts the mock server has to be available in. Otherwise it is considered as sick.
- *
- * @var int
- */
- private $healthCheckTimeout;
-
- /**
- * The seconds between health checks of mock server
- *
- * @var int
- */
- private $healthCheckRetrySec;
- private $logLevel;
+ private bool $secure = false;
/**
* {@inheritdoc}
@@ -106,7 +42,7 @@ public function getHost(): string
/**
* {@inheritdoc}
*/
- public function setHost(string $host): MockServerConfigInterface
+ public function setHost(string $host): self
{
$this->host = $host;
@@ -124,7 +60,7 @@ public function getPort(): int
/**
* {@inheritdoc}
*/
- public function setPort(int $port): MockServerConfigInterface
+ public function setPort(int $port): self
{
$this->port = $port;
@@ -142,7 +78,7 @@ public function isSecure(): bool
/**
* {@inheritdoc}
*/
- public function setSecure(bool $secure): MockServerConfigInterface
+ public function setSecure(bool $secure): self
{
$this->secure = $secure;
@@ -158,214 +94,4 @@ public function getBaseUri(): UriInterface
return new Uri("{$protocol}://{$this->getHost()}:{$this->getPort()}");
}
-
- /**
- * {@inheritdoc}
- */
- public function getConsumer(): string
- {
- return $this->consumer;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setConsumer(string $consumer): PactConfigInterface
- {
- $this->consumer = $consumer;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getProvider(): string
- {
- return $this->provider;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setProvider(string $provider): PactConfigInterface
- {
- $this->provider = $provider;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getPactDir()
- {
- if ($this->pactDir === null) {
- return \sys_get_temp_dir();
- }
-
- return $this->pactDir;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setPactDir($pactDir): PactConfigInterface
- {
- if ($pactDir === null) {
- return $this;
- }
-
- if ('\\' !== \DIRECTORY_SEPARATOR) {
- $pactDir = \str_replace('\\', \DIRECTORY_SEPARATOR, $pactDir);
- }
-
- $this->pactDir = $pactDir;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getPactFileWriteMode(): string
- {
- return $this->pactFileWriteMode;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setPactFileWriteMode(string $pactFileWriteMode): MockServerConfigInterface
- {
- $options = ['overwrite', 'merge'];
-
- if (!\in_array($pactFileWriteMode, $options)) {
- $implodedOptions = \implode(', ', $options);
-
- throw new \InvalidArgumentException("Invalid PhpPact File Write Mode, value must be one of the following: {$implodedOptions}.");
- }
-
- $this->pactFileWriteMode = $pactFileWriteMode;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getPactSpecificationVersion()
- {
- return $this->pactSpecificationVersion;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setPactSpecificationVersion($pactSpecificationVersion): PactConfigInterface
- {
- /*
- * Parse the version but do not assign it. If it is an invalid version, an exception is thrown
- */
- $parser = new VersionParser();
- $parser->normalize($pactSpecificationVersion);
-
- $this->pactSpecificationVersion = $pactSpecificationVersion;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getLog()
- {
- return $this->log;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setLog(string $log): PactConfigInterface
- {
- $this->log = $log;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getLogLevel()
- {
- return $this->logLevel;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setLogLevel(string $logLevel): PactConfigInterface
- {
- $logLevel = \strtoupper($logLevel);
- if (!\in_array($logLevel, ['DEBUG', 'INFO', 'WARN', 'ERROR'])) {
- throw new \InvalidArgumentException('LogLevel ' . $logLevel . ' not supported.');
- }
- $this->logLevel = $logLevel;
-
- return $this;
- }
-
- public function hasCors(): bool
- {
- return $this->cors;
- }
-
- public function setCors($flag): MockServerConfigInterface
- {
- if ($flag === 'true') {
- $this->cors = true;
- } elseif ($flag === 'false') {
- $this->cors = false;
- } else {
- $this->cors = (bool) $flag;
- }
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setHealthCheckTimeout($timeout): MockServerConfigInterface
- {
- $this->healthCheckTimeout = $timeout;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getHealthCheckTimeout(): int
- {
- return $this->healthCheckTimeout;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setHealthCheckRetrySec($seconds): MockServerConfigInterface
- {
- $this->healthCheckRetrySec = $seconds;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getHealthCheckRetrySec(): int
- {
- return $this->healthCheckRetrySec;
- }
}
diff --git a/src/PhpPact/Standalone/MockService/MockServerConfigInterface.php b/src/PhpPact/Standalone/MockService/MockServerConfigInterface.php
index a1066ab3..fc6eb64c 100644
--- a/src/PhpPact/Standalone/MockService/MockServerConfigInterface.php
+++ b/src/PhpPact/Standalone/MockService/MockServerConfigInterface.php
@@ -2,13 +2,14 @@
namespace PhpPact\Standalone\MockService;
+use PhpPact\Standalone\PactConfigInterface;
use Psr\Http\Message\UriInterface;
/**
* Mock Server configuration interface to allow for simple overrides that are reusable.
* Interface MockServerConfigInterface.
*/
-interface MockServerConfigInterface
+interface MockServerConfigInterface extends PactConfigInterface
{
/**
* @return string the host of the mock service
@@ -50,52 +51,4 @@ public function setSecure(bool $secure): self;
* @return UriInterface
*/
public function getBaseUri(): UriInterface;
-
- /**
- * @return string 'merge' or 'overwrite' merge means that interactions are added and overwrite means that the entire file is overwritten
- */
- public function getPactFileWriteMode(): string;
-
- /**
- * @param string $pactFileWriteMode 'merge' or 'overwrite' merge means that interactions are added and overwrite means that the entire file is overwritten
- *
- * @return MockServerConfigInterface
- */
- public function setPactFileWriteMode(string $pactFileWriteMode): self;
-
- /**
- * @return bool
- */
- public function hasCors(): bool;
-
- /**
- * @param bool|string $flag
- *
- * @return MockServerConfigInterface
- */
- public function setCors($flag): self;
-
- /**
- * @param int $timeout
- *
- * @return MockServerConfigInterface
- */
- public function setHealthCheckTimeout($timeout): self;
-
- /**
- * @return int
- */
- public function getHealthCheckTimeout(): int;
-
- /**
- * @param int $seconds
- *
- * @return MockServerConfigInterface
- */
- public function setHealthCheckRetrySec($seconds): self;
-
- /**
- * @return int
- */
- public function getHealthCheckRetrySec(): int;
}
diff --git a/src/PhpPact/Standalone/MockService/MockServerEnvConfig.php b/src/PhpPact/Standalone/MockService/MockServerEnvConfig.php
index 0edc8251..f7f74a81 100644
--- a/src/PhpPact/Standalone/MockService/MockServerEnvConfig.php
+++ b/src/PhpPact/Standalone/MockService/MockServerEnvConfig.php
@@ -10,8 +10,6 @@
*/
class MockServerEnvConfig extends MockServerConfig
{
- public const DEFAULT_SPECIFICATION_VERSION = '2.0.0';
-
/**
* MockServerEnvConfig constructor.
*
@@ -24,7 +22,6 @@ public function __construct()
$this->setConsumer($this->parseEnv('PACT_CONSUMER_NAME'));
$this->setProvider($this->parseEnv('PACT_PROVIDER_NAME'));
$this->setPactDir($this->parseEnv('PACT_OUTPUT_DIR', false));
- $this->setCors($this->parseEnv('PACT_CORS', false));
if ($logDir = $this->parseEnv('PACT_LOG', false)) {
$this->setLog($logDir);
@@ -34,18 +31,6 @@ public function __construct()
$this->setLogLevel($logLevel);
}
- $timeout = $this->parseEnv('PACT_MOCK_SERVER_HEALTH_CHECK_TIMEOUT', false);
- if (!$timeout) {
- $timeout = 10;
- }
- $this->setHealthCheckTimeout($timeout);
-
- $seconds = $this->parseEnv('PACT_MOCK_SERVER_HEALTH_CHECK_RETRY_SEC', false);
- if (!$seconds) {
- $seconds = 1;
- }
- $this->setHealthCheckRetrySec($seconds);
-
$version = $this->parseEnv('PACT_SPECIFICATION_VERSION', false);
if (!$version) {
$version = static::DEFAULT_SPECIFICATION_VERSION;
@@ -64,7 +49,7 @@ public function __construct()
*
* @return null|string
*/
- private function parseEnv(string $variableName, bool $required = true)
+ private function parseEnv(string $variableName, bool $required = true): ?string
{
$result = null;
diff --git a/src/PhpPact/Standalone/MockService/Service/MockServerHttpService.php b/src/PhpPact/Standalone/MockService/Service/MockServerHttpService.php
deleted file mode 100644
index 2c13e83a..00000000
--- a/src/PhpPact/Standalone/MockService/Service/MockServerHttpService.php
+++ /dev/null
@@ -1,181 +0,0 @@
-client = $client;
- $this->config = $config;
- }
-
- /**
- * {@inheritdoc}
- */
- public function healthCheck(): bool
- {
- $uri = $this->config->getBaseUri()->withPath('/');
-
- try {
- $response = $this->client->get($uri, [
- 'headers' => [
- 'Content-Type' => 'application/json',
- 'X-Pact-Mock-Service' => true,
- ],
- ]);
-
- $body = $response->getBody()->getContents();
-
- if ($response->getStatusCode() !== 200
- || $body !== "Mock service running\n") {
- throw new ConnectionException('Failed to receive a successful response from the Mock Server.');
- }
- } catch (RequestException $e) {
- throw new ConnectionException('Failed to receive a successful response from the Mock Server.', $e);
- } catch (GuzzleConnectionException $e) {
- throw new ConnectionException('Failed to receive a successful response from the Mock Server.', $e);
- }
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- */
- public function deleteAllInteractions(): bool
- {
- $uri = $this->config->getBaseUri()->withPath('/interactions');
-
- $response = $this->client->delete($uri, [
- 'headers' => [
- 'Content-Type' => 'application/json',
- 'X-Pact-Mock-Service' => true,
- ],
- ]);
-
- if ($response->getStatusCode() !== 200) {
- return false;
- }
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- */
- public function registerInteraction(Interaction $interaction): bool
- {
- $uri = $this->config->getBaseUri()->withPath('/interactions');
-
- $body = \json_encode($interaction->jsonSerialize());
-
- $this->client->post($uri, [
- 'headers' => [
- 'Content-Type' => 'application/json',
- 'X-Pact-Mock-Service' => true,
- ],
- 'body' => $body,
- ]);
-
- return true;
- }
-
- /**
- * Separate function for messages, instead of interactions, as I am unsure what to do with the Ruby Standalone at the moment
- *
- * @param Message $message
- *
- * @return bool
- */
- public function registerMessage(Message $message): bool
- {
- $uri = $this->config->getBaseUri()->withPath('/interactions');
-
- $body = \json_encode($message->jsonSerialize());
-
- $this->client->post($uri, [
- 'headers' => [
- 'Content-Type' => 'application/json',
- 'X-Pact-Mock-Service' => true,
- ],
- 'body' => $body,
- ]);
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- */
- public function verifyInteractions(): bool
- {
- $uri = $this->config->getBaseUri()->withPath('/interactions/verification');
-
- $this->client->get($uri, [
- 'headers' => [
- 'Content-Type' => 'application/json',
- 'X-Pact-Mock-Service' => true,
- ],
- ]);
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getPactJson(): string
- {
- $uri = $this->config->getBaseUri()->withPath('/pact');
- $response = $this->client->post($uri, [
- 'headers' => [
- 'Content-Type' => 'application/json',
- 'X-Pact-Mock-Service' => true,
- ],
- ]);
-
- return \json_encode(\json_decode($response->getBody()->getContents()));
- }
-
- /**
- * Wrapper for getPactJson to force the Ruby server to write the pact file to disk
- *
- * If the Pact-PHP does not gracefully kill the Ruby Server, it will not write the
- * file to disk. This enables a work around.
- */
- public function writePact(): string
- {
- return $this->getPactJson();
- }
-}
diff --git a/src/PhpPact/Standalone/MockService/Service/MockServerHttpServiceInterface.php b/src/PhpPact/Standalone/MockService/Service/MockServerHttpServiceInterface.php
deleted file mode 100644
index e3bfbe62..00000000
--- a/src/PhpPact/Standalone/MockService/Service/MockServerHttpServiceInterface.php
+++ /dev/null
@@ -1,51 +0,0 @@
-consumer;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setConsumer(string $consumer): self
+ {
+ $this->consumer = $consumer;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getProvider(): string
+ {
+ return $this->provider;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setProvider(string $provider): self
+ {
+ $this->provider = $provider;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPactDir(): string
+ {
+ if ($this->pactDir === null) {
+ return \sys_get_temp_dir();
+ }
+
+ return $this->pactDir;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setPactDir(?string $pactDir): self
+ {
+ if ($pactDir === null) {
+ return $this;
+ }
+
+ if ('\\' !== \DIRECTORY_SEPARATOR) {
+ $pactDir = \str_replace('\\', \DIRECTORY_SEPARATOR, $pactDir);
+ }
+
+ $this->pactDir = $pactDir;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPactSpecificationVersion(): string
+ {
+ return $this->pactSpecificationVersion;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setPactSpecificationVersion(string $pactSpecificationVersion): self
+ {
+ $this->pactSpecificationVersion = $pactSpecificationVersion;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getLog(): ?string
+ {
+ return $this->log;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setLog(string $log): self
+ {
+ $this->log = $log;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getLogLevel(): ?string
+ {
+ return $this->logLevel;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setLogLevel(string $logLevel): self
+ {
+ $logLevel = \strtoupper($logLevel);
+ if (!\in_array($logLevel, ['DEBUG', 'INFO', 'WARN', 'ERROR'])) {
+ throw new \InvalidArgumentException('LogLevel ' . $logLevel . ' not supported.');
+ }
+ $this->logLevel = $logLevel;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPactFileWriteMode(): string
+ {
+ return $this->pactFileWriteMode;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setPactFileWriteMode(string $pactFileWriteMode): self
+ {
+ $options = [self::MODE_OVERWRITE, self::MODE_MERGE];
+
+ if (!\in_array($pactFileWriteMode, $options)) {
+ $implodedOptions = \implode(', ', $options);
+
+ throw new \InvalidArgumentException("Invalid PhpPact File Write Mode, value must be one of the following: {$implodedOptions}.");
+ }
+
+ $this->pactFileWriteMode = $pactFileWriteMode;
+
+ return $this;
+ }
+}
diff --git a/src/PhpPact/Standalone/PactConfigInterface.php b/src/PhpPact/Standalone/PactConfigInterface.php
index 3369e46e..e2febd82 100644
--- a/src/PhpPact/Standalone/PactConfigInterface.php
+++ b/src/PhpPact/Standalone/PactConfigInterface.php
@@ -8,6 +8,11 @@
*/
interface PactConfigInterface
{
+ public const DEFAULT_SPECIFICATION_VERSION = '3.0.0';
+
+ public const MODE_OVERWRITE = 'overwrite';
+ public const MODE_MERGE = 'merge';
+
/**
* @return string
*/
@@ -16,7 +21,7 @@ public function getConsumer(): string;
/**
* @param string $consumer consumers name
*
- * @return PactConfigInterface
+ * @return $this
*/
public function setConsumer(string $consumer): self;
@@ -28,50 +33,50 @@ public function getProvider(): string;
/**
* @param string $provider providers name
*
- * @return PactConfigInterface
+ * @return $this
*/
public function setProvider(string $provider): self;
/**
* @return string url to place the pact files when written to disk
*/
- public function getPactDir();
+ public function getPactDir(): string;
/**
* @param null|string $pactDir url to place the pact files when written to disk
*
- * @return PactConfigInterface
+ * @return $this
*/
- public function setPactDir($pactDir): self;
+ public function setPactDir(?string $pactDir): self;
/**
* @return string pact version
*/
- public function getPactSpecificationVersion();
+ public function getPactSpecificationVersion(): string;
/**
- * @param string $pactSpecificationVersion pact semver version
+ * @param string $pactSpecificationVersion pact version
*
- * @return PactConfigInterface
+ * @return $this
*/
- public function setPactSpecificationVersion($pactSpecificationVersion): self;
+ public function setPactSpecificationVersion(string $pactSpecificationVersion): self;
/**
- * @return string directory for log output
+ * @return null|string directory for log output
*/
- public function getLog();
+ public function getLog(): ?string;
/**
* @param string $log directory for log output
*
- * @return PactConfigInterface
+ * @return $this
*/
public function setLog(string $log): self;
/**
* @return null|string
*/
- public function getLogLevel();
+ public function getLogLevel(): ?string;
/**
* @param string $logLevel
@@ -79,4 +84,16 @@ public function getLogLevel();
* @return $this
*/
public function setLogLevel(string $logLevel): self;
+
+ /**
+ * @return string 'merge' or 'overwrite' merge means that interactions are added and overwrite means that the entire file is overwritten
+ */
+ public function getPactFileWriteMode(): string;
+
+ /**
+ * @param string $pactFileWriteMode 'merge' or 'overwrite' merge means that interactions are added and overwrite means that the entire file is overwritten
+ *
+ * @return $this
+ */
+ public function setPactFileWriteMode(string $pactFileWriteMode): self;
}
diff --git a/src/PhpPact/Standalone/PactMessage/PactMessage.php b/src/PhpPact/Standalone/PactMessage/PactMessage.php
index 8d840e41..4f20cb92 100644
--- a/src/PhpPact/Standalone/PactMessage/PactMessage.php
+++ b/src/PhpPact/Standalone/PactMessage/PactMessage.php
@@ -2,57 +2,85 @@
namespace PhpPact\Standalone\PactMessage;
+use PhpPact\Consumer\Model\AbstractPact;
use PhpPact\Consumer\Model\Message;
-use PhpPact\Standalone\Installer\Model\Scripts;
-use PhpPact\Standalone\Runner\ProcessRunner;
-class PactMessage
+class PactMessage extends AbstractPact
{
/**
- * Build an example from the data structure back into its generated form
- * i.e. strip out all of the matchers etc
- *
- * @param Message $pact
+ * @param Message $message
*
* @return string
*/
- public function reify(Message $pact): string
+ public function reify(Message $message): string
{
- $json = \json_encode($pact);
- $process = new ProcessRunner(Scripts::getPactMessage(), ['reify', "'" . $json . "'"]);
-
- $process->runBlocking();
-
- $output = $process->getOutput();
- \preg_replace("/\r|\n/", '', $output);
+ $message->setId($this->newInteraction($message->getDescription()));
+ $this
+ ->given($message)
+ ->expectsToReceive($message)
+ ->withMetadata($message)
+ ->withContent($message);
- return $output;
+ return $this->ffi->pactffi_message_reify($message->getId());
}
/**
- * Update a pact with the given message, or create the pact if it does not exist. The MESSAGE_JSON may be in the legacy Ruby JSON format or the v2+ format.
- *
- * @param string $pactJson
- * @param string $consumer
- * @param string $provider
- * @param string $pactDir
+ * Update a pact with the given message, or create the pact if it does not exist.
*
* @return bool
*/
- public function update(string $pactJson, string $consumer, string $provider, string $pactDir): bool
+ public function update(): bool
+ {
+ $this->writePact();
+ $this->cleanUp();
+
+ return true;
+ }
+
+ private function given(Message $message): self
{
- $arguments = [];
- $arguments[] = 'update';
- $arguments[] = "--consumer={$consumer}";
- $arguments[] = "--provider={$provider}";
- $arguments[] = "--pact-dir={$pactDir}";
- $arguments[] = "'" . $pactJson . "'";
+ foreach ($message->getProviderStates() as $providerState) {
+ foreach ($providerState->params as $key => $value) {
+ $this->ffi->pactffi_message_given_with_param($message->getId(), $providerState->name, (string) $key, $value);
+ }
+ }
- $process = new ProcessRunner(Scripts::getPactMessage(), $arguments);
- $process->runBlocking();
+ return $this;
+ }
- \sleep(1);
+ private function expectsToReceive(Message $message): self
+ {
+ $this->ffi->pactffi_message_expects_to_receive($message->getId(), $message->getDescription());
- return true;
+ return $this;
+ }
+
+ private function withMetadata(Message $message): self
+ {
+ foreach ($message->getMetadata() as $key => $value) {
+ $this->ffi->pactffi_message_with_metadata($message->getId(), (string) $key, (string) $value);
+ }
+
+ return $this;
+ }
+
+ private function withContent(Message $message): self
+ {
+ if (\is_string($message->getContents())) {
+ $contents = $message->getContents();
+ $contentType = 'text/plain';
+ } else {
+ $contents = \json_encode($message->getContents());
+ $contentType = 'application/json';
+ }
+
+ $this->withBody($message->getId(), $this->ffi->InteractionPart_Request, $contentType, $contents);
+
+ return $this;
+ }
+
+ protected function newInteraction(?string $description): int
+ {
+ return $this->ffi->pactffi_new_message_interaction($this->id, $description);
}
}
diff --git a/src/PhpPact/Standalone/PactMessage/PactMessageConfig.php b/src/PhpPact/Standalone/PactMessage/PactMessageConfig.php
index 2874e772..3ad3c782 100644
--- a/src/PhpPact/Standalone/PactMessage/PactMessageConfig.php
+++ b/src/PhpPact/Standalone/PactMessage/PactMessageConfig.php
@@ -2,168 +2,12 @@
namespace PhpPact\Standalone\PactMessage;
-use Composer\Semver\VersionParser;
-use PhpPact\Standalone\PactConfigInterface;
+use PhpPact\Standalone\PactConfig;
/**
* Configuration defining the default PhpPact Ruby Standalone server.
- * Class MockServerConfig.
+ * Class PactMessageConfig.
*/
-class PactMessageConfig implements PactConfigInterface
+class PactMessageConfig extends PactConfig
{
- /**
- * Consumer name.
- *
- * @var string
- */
- private $consumer;
-
- /**
- * Provider name.
- *
- * @var string
- */
- private $provider;
-
- /**
- * Directory to which the pacts will be written.
- *
- * @var string
- */
- private $pactDir;
-
- /**
- * The pact specification version to use when writing the pact. Note that only versions 1 and 2 are currently supported.
- *
- * @var string
- */
- private $pactSpecificationVersion;
-
- /**
- * File to which to log output.
- *
- * @var string
- */
- private $log;
-
- /** @var string */
- private $logLevel;
-
- /**
- * {@inheritdoc}
- */
- public function getConsumer(): string
- {
- return $this->consumer;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setConsumer(string $consumer): PactConfigInterface
- {
- $this->consumer = $consumer;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getProvider(): string
- {
- return $this->provider;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setProvider(string $provider): PactConfigInterface
- {
- $this->provider = $provider;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getPactDir()
- {
- if ($this->pactDir === null) {
- return \sys_get_temp_dir();
- }
-
- return $this->pactDir;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setPactDir($pactDir): PactConfigInterface
- {
- $this->pactDir = $pactDir;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getPactSpecificationVersion()
- {
- return $this->pactSpecificationVersion;
- }
-
- /**
- * {@inheritdoc}
- *
- * @throws \UnexpectedValueException
- */
- public function setPactSpecificationVersion($pactSpecificationVersion): PactConfigInterface
- {
- /*
- * Parse the version but do not assign it. If it is an invalid version, an exception is thrown
- */
- $parser = new VersionParser();
- $parser->normalize($pactSpecificationVersion);
-
- $this->pactSpecificationVersion = $pactSpecificationVersion;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getLog()
- {
- return $this->log;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setLog(string $log): PactConfigInterface
- {
- $this->log = $log;
-
- return $this;
- }
-
- public function getLogLevel()
- {
- return $this->logLevel;
- }
-
- public function setLogLevel(string $logLevel): PactConfigInterface
- {
- $logLevel = \strtoupper($logLevel);
- if (!\in_array($logLevel, ['DEBUG', 'INFO', 'WARN', 'ERROR'])) {
- throw new \InvalidArgumentException('LogLevel ' . $logLevel . ' not supported.');
- }
- $this->logLevel = $logLevel;
-
- return $this;
- }
}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Model/ArrayData.php b/src/PhpPact/Standalone/ProviderVerifier/Model/ArrayData.php
new file mode 100644
index 00000000..b6fcbc1b
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/ArrayData.php
@@ -0,0 +1,51 @@
+items;
+ }
+
+ public function getSize(): int
+ {
+ return $this->size;
+ }
+
+ public static function createFrom(iterable $values): self
+ {
+ $size = count($values);
+ if ($size === 0) {
+ return new self();
+ }
+
+ $items = FFI::new("char*[{$size}]");
+ foreach ($values as $index => $value) {
+ $length = \strlen($value);
+ $itemSize = $length + 1;
+ $item = FFI::new("char[{$itemSize}]", false);
+ FFI::memcpy($item, $value, $length);
+ $items[$index] = $item;
+ }
+
+ return new self($items, $size);
+ }
+
+ public function __destruct()
+ {
+ for ($i=0; $i < $this->size; $i++) {
+ FFI::free($this->items[$i]);
+ }
+ }
+}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ConsumerFilters.php b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ConsumerFilters.php
new file mode 100644
index 00000000..cddca6f4
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ConsumerFilters.php
@@ -0,0 +1,31 @@
+filterConsumerNames = $filterConsumerNames;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFilterConsumerNames(): array
+ {
+ return $this->filterConsumerNames;
+ }
+}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ConsumerFiltersInterface.php b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ConsumerFiltersInterface.php
new file mode 100644
index 00000000..5dca165b
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ConsumerFiltersInterface.php
@@ -0,0 +1,21 @@
+filterDescription;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setFilterDescription(string $filterDescription): VerifierConfigInterface
+ {
+ $this->filterDescription = $filterDescription;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFilterNoState(): bool
+ {
+ return $this->filterNoState;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setFilterNoState(bool $filterNoState): VerifierConfigInterface
+ {
+ $this->filterNoState = $filterNoState;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFilterState(): ?string
+ {
+ return $this->filterState;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setFilterState(string $filterState): VerifierConfigInterface
+ {
+ $this->filterState = $filterState;
+
+ return $this;
+ }
+}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Model/Config/FilterInfoInterface.php b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/FilterInfoInterface.php
new file mode 100644
index 00000000..33ee9068
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/FilterInfoInterface.php
@@ -0,0 +1,45 @@
+providerName;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setProviderName(string $providerName): VerifierConfigInterface
+ {
+ $this->providerName = $providerName;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getScheme(): ?string
+ {
+ return $this->scheme;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setScheme(string $scheme): VerifierConfigInterface
+ {
+ $this->scheme = $scheme;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getHost(): ?string
+ {
+ return $this->host;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setHost(string $host): VerifierConfigInterface
+ {
+ $this->host = $host;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPort(): ?int
+ {
+ return $this->port;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setPort(int $port): VerifierConfigInterface
+ {
+ $this->port = $port;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getBasePath(): ?string
+ {
+ return $this->basePath;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setBasePath(string $basePath): VerifierConfigInterface
+ {
+ $this->basePath = $basePath;
+
+ return $this;
+ }
+}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ProviderInfoInterface.php b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ProviderInfoInterface.php
new file mode 100644
index 00000000..afe49be5
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ProviderInfoInterface.php
@@ -0,0 +1,69 @@
+stateChangeUrl;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setStateChangeUrl(UriInterface $stateChangeUrl): VerifierConfigInterface
+ {
+ $this->stateChangeUrl = $stateChangeUrl;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setStateChangeAsBody(bool $stateChangeAsBody): VerifierConfigInterface
+ {
+ $this->stateChangeAsBody = $stateChangeAsBody;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isStateChangeAsBody(): bool
+ {
+ return $this->stateChangeAsBody;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setStateChangeTeardown(bool $stateChangeTeardown): VerifierConfigInterface
+ {
+ $this->stateChangeTeardown = $stateChangeTeardown;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isStateChangeTeardown(): bool
+ {
+ return $this->stateChangeTeardown;
+ }
+}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ProviderStateInterface.php b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ProviderStateInterface.php
new file mode 100644
index 00000000..c6ad97ab
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ProviderStateInterface.php
@@ -0,0 +1,47 @@
+publishResults;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setPublishResults(bool $publishResults): VerifierConfigInterface
+ {
+ $this->publishResults = $publishResults;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getProviderTags(): array
+ {
+ return $this->providerTags;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setProviderTags(string ...$providerTags): VerifierConfigInterface
+ {
+ $this->providerTags = $providerTags;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getProviderVersion(): string
+ {
+ return $this->providerVersion;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setProviderVersion(string $providerVersion): VerifierConfigInterface
+ {
+ $this->providerVersion = $providerVersion;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getBuildUrl(): ?UriInterface
+ {
+ return $this->buildUrl;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setBuildUrl(UriInterface $buildUrl): VerifierConfigInterface
+ {
+ $this->buildUrl = $buildUrl;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getProviderBranch(): ?string
+ {
+ return $this->providerBranch;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setProviderBranch(string $providerBranch): VerifierConfigInterface
+ {
+ $this->providerBranch = $providerBranch;
+
+ return $this;
+ }
+}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Model/Config/PublishOptionsInterface.php b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/PublishOptionsInterface.php
new file mode 100644
index 00000000..10d99982
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/PublishOptionsInterface.php
@@ -0,0 +1,71 @@
+disableSslVerification;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setDisableSslVerification(bool $disableSslVerification): VerifierConfigInterface
+ {
+ $this->disableSslVerification = $disableSslVerification;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setRequestTimeout(int $requestTimeout): VerifierConfigInterface
+ {
+ $this->requestTimeout = $requestTimeout;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getRequestTimeout(): int
+ {
+ return $this->requestTimeout;
+ }
+}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Model/Config/VerificationOptionsInterface.php b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/VerificationOptionsInterface.php
new file mode 100644
index 00000000..13673eb4
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/VerificationOptionsInterface.php
@@ -0,0 +1,33 @@
+selectors[$this->position];
}
- #[\ReturnTypeWillChange]
- public function next()
+ public function next(): void
{
++$this->position;
}
- #[\ReturnTypeWillChange]
- public function key()
+ public function key(): int
{
return $this->position;
}
- #[\ReturnTypeWillChange]
- public function valid()
+ public function valid(): bool
{
return isset($this->selectors[$this->position]);
}
- #[\ReturnTypeWillChange]
- public function rewind()
+ public function rewind(): void
{
$this->position = 0;
}
- #[\ReturnTypeWillChange]
- public function count()
+ public function count(): int
{
return \count($this->selectors);
}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Model/Source/Broker.php b/src/PhpPact/Standalone/ProviderVerifier/Model/Source/Broker.php
new file mode 100644
index 00000000..84dd0932
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Source/Broker.php
@@ -0,0 +1,131 @@
+consumerVersionSelectors = new ConsumerVersionSelectors();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isEnablePending(): bool
+ {
+ return $this->enablePending;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setEnablePending(bool $enablePending): BrokerInterface
+ {
+ $this->enablePending = $enablePending;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setIncludeWipPactSince(string $date): BrokerInterface
+ {
+ $this->wipPactSince = $date;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getIncludeWipPactSince(): ?string
+ {
+ return $this->wipPactSince;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getProviderTags(): array
+ {
+ return $this->providerTags;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setProviderTags(array $providerTags): BrokerInterface
+ {
+ $this->providerTags = $providerTags;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getProviderBranch(): ?string
+ {
+ return $this->providerBranch;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setProviderBranch(string $providerBranch): BrokerInterface
+ {
+ $this->providerBranch = $providerBranch;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getConsumerVersionSelectors(): ConsumerVersionSelectors
+ {
+ return $this->consumerVersionSelectors;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setConsumerVersionSelectors(ConsumerVersionSelectors $selectors): BrokerInterface
+ {
+ $this->consumerVersionSelectors = $selectors;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getConsumerVersionTags(): array
+ {
+ return $this->consumerVersionTags;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setConsumerVersionTags(array $consumerVersionTags): BrokerInterface
+ {
+ $this->consumerVersionTags = $consumerVersionTags;
+
+ return $this;
+ }
+}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Model/Source/BrokerInterface.php b/src/PhpPact/Standalone/ProviderVerifier/Model/Source/BrokerInterface.php
new file mode 100644
index 00000000..1a17fe91
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Source/BrokerInterface.php
@@ -0,0 +1,84 @@
+url;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setUrl(UriInterface $url): UrlInterface
+ {
+ $this->url = $url;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getToken(): ?string
+ {
+ return $this->token;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setToken(?string $token): UrlInterface
+ {
+ $this->token = $token;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getUsername(): ?string
+ {
+ return $this->username;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setUsername(string $username): UrlInterface
+ {
+ $this->username = $username;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPassword(): ?string
+ {
+ return $this->password;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setPassword(string $password): UrlInterface
+ {
+ $this->password = $password;
+
+ return $this;
+ }
+}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Model/Source/UrlInterface.php b/src/PhpPact/Standalone/ProviderVerifier/Model/Source/UrlInterface.php
new file mode 100644
index 00000000..c9a7b380
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Source/UrlInterface.php
@@ -0,0 +1,59 @@
+consumerVersionSelectors = new ConsumerVersionSelectors();
- }
-
- /**
- * {@inheritdoc}
- */
- public function getProviderBaseUrl()
- {
- return $this->providerBaseUrl;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setProviderBaseUrl(UriInterface $providerBaseUrl): VerifierConfigInterface
- {
- $this->providerBaseUrl = $providerBaseUrl;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getProviderStatesSetupUrl()
- {
- return $this->providerStatesSetupUrl;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setProviderStatesSetupUrl(string $providerStatesSetupUrl): VerifierConfigInterface
- {
- $this->providerStatesSetupUrl = $providerStatesSetupUrl;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getProviderName(): string
- {
- return (string) $this->providerName;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setProviderName(string $providerName): VerifierConfigInterface
- {
- $this->providerName = $providerName;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getProviderVersion()
- {
- return $this->providerVersion;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setProviderVersion(string $providerVersion): VerifierConfigInterface
- {
- $this->providerVersion = $providerVersion;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getProviderVersionTag()
- {
- return $this->providerVersionTag;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setProviderVersionTag(string $providerVersionTag): VerifierConfigInterface
- {
- return $this->addProviderVersionTag($providerVersionTag);
- }
-
- /**
- * {@inheritdoc}
- */
- public function getConsumerVersionTag()
- {
- return $this->consumerVersionTag;
- }
-
- /**
- * {@inheritdoc}
- */
- public function addConsumerVersionTag(string $consumerVersionTag): VerifierConfigInterface
- {
- $this->consumerVersionTag[] = $consumerVersionTag;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function addProviderVersionTag(string $providerVersionTag): VerifierConfigInterface
- {
- $this->providerVersionTag[] = $providerVersionTag;
-
- return $this;
- }
-
- /**
- * @param string $consumerVersionTag
- *
- * @return VerifierConfigInterface
- */
- public function setConsumerVersionTag(string $consumerVersionTag): VerifierConfigInterface
- {
- return $this->addConsumerVersionTag($consumerVersionTag);
- }
-
- public function getConsumerVersionSelectors(): ConsumerVersionSelectors
- {
- return $this->consumerVersionSelectors;
- }
-
- public function setConsumerVersionSelectors(ConsumerVersionSelectors $selectors): VerifierConfigInterface
- {
- $this->consumerVersionSelectors = $selectors;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function isPublishResults(): bool
- {
- return $this->publishResults;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setPublishResults(bool $publishResults): VerifierConfigInterface
- {
- $this->publishResults = $publishResults;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getBrokerUri()
- {
- return $this->brokerUri;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setBrokerUri(UriInterface $brokerUri): VerifierConfigInterface
- {
- $this->brokerUri = $brokerUri;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}}
- */
- public function getBrokerToken(): ?string
- {
- return $this->brokerToken;
- }
-
- /**
- * {@inheritdoc }
- */
- public function setBrokerToken(?string $brokerToken): VerifierConfigInterface
- {
- $this->brokerToken = $brokerToken;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getBrokerUsername()
- {
- return $this->brokerUsername;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setBrokerUsername(string $brokerUsername)
- {
- $this->brokerUsername = $brokerUsername;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getBrokerPassword()
- {
- return $this->brokerPassword;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setBrokerPassword(string $brokerPassword)
- {
- $this->brokerPassword = $brokerPassword;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getCustomProviderHeaders()
- {
- return $this->customProviderHeaders;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setCustomProviderHeaders(array $customProviderHeaders): VerifierConfigInterface
- {
- $this->customProviderHeaders = $customProviderHeaders;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function addCustomProviderHeader(string $name, string $value): VerifierConfigInterface
- {
- $this->customProviderHeaders[] = "{$name}: {$value}";
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function isVerbose(): bool
- {
- return $this->verbose;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setVerbose(bool $verbose): VerifierConfigInterface
- {
- $this->verbose = $verbose;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getLogDirectory()
- {
- return $this->logDirectory;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setLogDirectory(string $log): VerifierConfigInterface
- {
- $this->logDirectory = $log;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFormat()
- {
- return $this->format;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setFormat(string $format): VerifierConfigInterface
- {
- $this->format = $format;
-
- return $this;
- }
-
- /**
- * @param int $timeout
- *
- * @return VerifierConfigInterface
- */
- public function setProcessTimeout(int $timeout): VerifierConfigInterface
- {
- $this->processTimeout = $timeout;
-
- return $this;
- }
-
- /**
- * @param int $timeout
- *
- * @return VerifierConfigInterface
- */
- public function setProcessIdleTimeout(int $timeout): VerifierConfigInterface
- {
- $this->processIdleTimeout = $timeout;
-
- return $this;
- }
-
- /**
- * @return int
- */
- public function getProcessTimeout(): int
- {
- return $this->processTimeout;
- }
-
- /**
- * @return int
- */
- public function getProcessIdleTimeout(): int
- {
- return $this->processIdleTimeout;
- }
-
- /**
- * {@inheritdoc}
- */
- public function isEnablePending(): bool
- {
- return $this->enablePending;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setEnablePending(bool $pending): VerifierConfigInterface
- {
- $this->enablePending = $pending;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setIncludeWipPactSince(string $date): VerifierConfigInterface
- {
- $this->wipPactSince = $date;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getIncludeWipPactSince()
- {
- return $this->wipPactSince;
- }
-
- public function getRequestFilter(): ?callable
- {
- return $this->requestFilter;
- }
-
- public function setRequestFilter(callable $requestFilter): VerifierConfigInterface
- {
- $this->requestFilter = $requestFilter;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setProviderBranch(string $providerBranch): VerifierConfigInterface
- {
- $this->providerBranch = $providerBranch;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getProviderBranch(): ?string
- {
- return $this->providerBranch;
- }
+ use ProviderInfo;
+ use FilterInfo;
+ use ProviderState;
+ use VerificationOptions;
+ use PublishOptions;
+ use ConsumerFilters;
}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Model/VerifierConfigInterface.php b/src/PhpPact/Standalone/ProviderVerifier/Model/VerifierConfigInterface.php
index b1acbdf6..066bd6de 100644
--- a/src/PhpPact/Standalone/ProviderVerifier/Model/VerifierConfigInterface.php
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/VerifierConfigInterface.php
@@ -2,291 +2,16 @@
namespace PhpPact\Standalone\ProviderVerifier\Model;
-use Psr\Http\Message\UriInterface;
+use PhpPact\Standalone\ProviderVerifier\Model\Config\ConsumerFiltersInterface;
+use PhpPact\Standalone\ProviderVerifier\Model\Config\FilterInfoInterface;
+use PhpPact\Standalone\ProviderVerifier\Model\Config\ProviderInfoInterface;
+use PhpPact\Standalone\ProviderVerifier\Model\Config\ProviderStateInterface;
+use PhpPact\Standalone\ProviderVerifier\Model\Config\PublishOptionsInterface;
+use PhpPact\Standalone\ProviderVerifier\Model\Config\VerificationOptionsInterface;
/**
- * Configuration to use with the verifier server.
- * Interface VerifierServerConfigInterface.
+ * Interface VerifierConfigInterface.
*/
-interface VerifierConfigInterface
+interface VerifierConfigInterface extends ProviderInfoInterface, FilterInfoInterface, ProviderStateInterface, VerificationOptionsInterface, PublishOptionsInterface, ConsumerFiltersInterface
{
- /**
- * @return null|UriInterface providers base url
- */
- public function getProviderBaseUrl();
-
- /**
- * @param UriInterface $providerBaseUrl providers base url
- *
- * @return VerifierConfigInterface
- */
- public function setProviderBaseUrl(UriInterface $providerBaseUrl): self;
-
- /**
- * @return null|string Base URL to setup the provider states at
- */
- public function getProviderStatesSetupUrl();
-
- /**
- * @param string $providerStatesSetupUrl Base URL to setup the provider states at
- *
- * @return VerifierConfigInterface
- */
- public function setProviderStatesSetupUrl(string $providerStatesSetupUrl): self;
-
- /**
- * @return null|string name of the provider
- */
- public function getProviderName();
-
- /**
- * @param string $name Name of the provider
- *
- * @return VerifierConfigInterface
- */
- public function setProviderName(string $name): self;
-
- /**
- * @return null|string providers version
- */
- public function getProviderVersion();
-
- /**
- * @param string $providerAppVersion providers version
- *
- * @return VerifierConfigInterface
- */
- public function setProviderVersion(string $providerAppVersion): self;
-
- /**
- * @param string $providerBranch providers branch name
- *
- * @return VerifierConfigInterface
- */
- public function setProviderBranch(string $providerBranch): self;
-
- /**
- * @return array providers version tag
- */
- public function getProviderVersionTag();
-
- /**
- * @return null|string providers branch name
- */
- public function getProviderBranch(): ?string;
-
- /**
- * @param string $providerVersionTag providers version tag
- *
- * @return VerifierConfigInterface
- */
- public function setProviderVersionTag(string $providerVersionTag): self;
-
- /**
- * @return array consumers version tag
- */
- public function getConsumerVersionTag();
-
- /**
- * @param string $consumerVersionTag consumers version tag
- *
- * @return VerifierConfigInterface
- */
- public function addConsumerVersionTag(string $consumerVersionTag): self;
-
- /**
- * @param string $providerVersionTag provider version tag
- *
- * @return VerifierConfigInterface
- */
- public function addProviderVersionTag(string $providerVersionTag): self;
-
- /**
- * @return ConsumerVersionSelectors
- */
- public function getConsumerVersionSelectors(): ConsumerVersionSelectors;
-
- /**
- * @param ConsumerVersionSelectors $selectors Consumer version selectors
- *
- * @return $this
- */
- public function setConsumerVersionSelectors(ConsumerVersionSelectors $selectors): self;
-
- /**
- * @return bool are results going to be published
- */
- public function isPublishResults(): bool;
-
- /**
- * @param bool $publishResults flag to publish results
- *
- * @return VerifierConfigInterface
- */
- public function setPublishResults(bool $publishResults): self;
-
- /**
- * @return null|UriInterface url to the pact broker
- */
- public function getBrokerUri();
-
- /**
- * @param UriInterface $brokerUri uri to the pact broker
- *
- * @return VerifierConfigInterface
- */
- public function setBrokerUri(UriInterface $brokerUri): self;
-
- /**
- * @return null|string token for the pact broker
- */
- public function getBrokerToken(): ?string;
-
- /**
- * @param null|string $brokerToken token for the pact broker
- *
- * @return VerifierConfigInterface
- */
- public function setBrokerToken(?string $brokerToken): self;
-
- /**
- * @return null|string username for the pact broker if secured
- */
- public function getBrokerUsername();
-
- /**
- * @param string $brokerUsername username for the pact broker if secured
- *
- * @return VerifierConfigInterface
- */
- public function setBrokerUsername(string $brokerUsername);
-
- /**
- * @return null|string password for the pact broker if secured
- */
- public function getBrokerPassword();
-
- /**
- * @param string $brokerPassword password for the pact broker if secured
- *
- * @return VerifierConfigInterface
- */
- public function setBrokerPassword(string $brokerPassword);
-
- /**
- * @return null|string[] custom headers for the request to the provider such as authorization
- */
- public function getCustomProviderHeaders();
-
- /**
- * @param string[] $customProviderHeaders custom headers for the requests to the provider such as authorization
- *
- * @return VerifierConfigInterface
- */
- public function setCustomProviderHeaders(array $customProviderHeaders): self;
-
- /**
- * @param string $name
- * @param string $value
- *
- * @return VerifierConfigInterface
- */
- public function addCustomProviderHeader(string $name, string $value): self;
-
- /**
- * @return bool is verbosity level increased
- */
- public function isVerbose(): bool;
-
- /**
- * @param bool $verbose increase verbosity level
- *
- * @return VerifierConfigInterface
- */
- public function setVerbose(bool $verbose): self;
-
- /**
- * @return null|string set the directory for the pact.log file
- */
- public function getLogDirectory();
-
- /**
- * @param string $log set the directory for the pact.log file
- *
- * @return VerifierConfigInterface
- */
- public function setLogDirectory(string $log): self;
-
- /**
- * @return null|string RSpec formatter. Defaults to custom Pact formatter. json and RspecJunitFormatter may also be used
- */
- public function getFormat();
-
- /**
- * @param string $format RSpec formatter. Defaults to custom Pact formatter. json and RspecJunitFormatter may also be used
- *
- * @return VerifierConfigInterface
- */
- public function setFormat(string $format): self;
-
- /**
- * @param int $timeout
- *
- * @return VerifierConfigInterface
- */
- public function setProcessTimeout(int $timeout): self;
-
- /**
- * @param int $timeout
- *
- * @return VerifierConfigInterface
- */
- public function setProcessIdleTimeout(int $timeout): self;
-
- /**
- * @return int
- */
- public function getProcessTimeout(): int;
-
- /**
- * @return int
- */
- public function getProcessIdleTimeout(): int;
-
- /**
- * @param bool $pending allow pacts which are in pending state to be verified without causing the overall task to fail
- *
- * @return VerifierConfigInterface
- */
- public function setEnablePending(bool $pending): self;
-
- /**
- * @return bool is enabled pending pacts
- */
- public function isEnablePending(): bool;
-
- /**
- * @param string $date Includes pact marked as WIP since this date.
- * Accepted formats: Y-m-d (2020-01-30) or c (ISO 8601 date 2004-02-12T15:19:21+00:00)
- *
- * @return VerifierConfigInterface
- */
- public function setIncludeWipPactSince(string $date): self;
-
- /**
- * @return null|string get start date of included WIP Pacts
- */
- public function getIncludeWipPactSince();
-
- /**
- * @return null|callable
- */
- public function getRequestFilter(): ?callable;
-
- /**
- * @param callable $requestFilter
- *
- * @return $this
- */
- public function setRequestFilter(callable $requestFilter): self;
}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/ProcessRunnerFactory.php b/src/PhpPact/Standalone/ProviderVerifier/ProcessRunnerFactory.php
deleted file mode 100644
index 872c999b..00000000
--- a/src/PhpPact/Standalone/ProviderVerifier/ProcessRunnerFactory.php
+++ /dev/null
@@ -1,36 +0,0 @@
-providerVerifier = $providerVerifier ?? Scripts::getProviderVerifier();
- }
-
- /**
- * @param array $arguments
- * @param LoggerInterface|null $logger
- *
- * @return ProcessRunner
- */
- public function createRunner(array $arguments, LoggerInterface $logger = null)
- {
- $processRunner = new ProcessRunner($this->providerVerifier, $arguments);
- if ($logger) {
- $processRunner->setLogger($logger);
- }
-
- return $processRunner;
- }
-}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Verifier.php b/src/PhpPact/Standalone/ProviderVerifier/Verifier.php
index ab79dae8..5e6a4781 100644
--- a/src/PhpPact/Standalone/ProviderVerifier/Verifier.php
+++ b/src/PhpPact/Standalone/ProviderVerifier/Verifier.php
@@ -2,267 +2,168 @@
namespace PhpPact\Standalone\ProviderVerifier;
-use GuzzleHttp\HandlerStack;
-use GuzzleHttp\Middleware;
-use PhpPact\Broker\Service\BrokerHttpClient;
-use PhpPact\Broker\Service\BrokerHttpClientInterface;
-use PhpPact\Http\GuzzleClient;
+use FFI;
+use FFI\CData;
+use PhpPact\Standalone\Installer\Model\Scripts;
+use PhpPact\Standalone\ProviderVerifier\Model\ArrayData;
+use PhpPact\Standalone\ProviderVerifier\Model\Source\BrokerInterface;
+use PhpPact\Standalone\ProviderVerifier\Model\Source\UrlInterface;
use PhpPact\Standalone\ProviderVerifier\Model\VerifierConfigInterface;
/**
- * Wrapper for the Ruby Standalone Verifier service.
- * Class VerifierServer.
+ * Class Verifier.
*/
class Verifier
{
- /** @var int */
- protected $processTimeout = 60;
-
- /** @var int */
- protected $processIdleTimeout = 10;
-
- /** @var VerifierConfigInterface */
- protected $config;
-
- /** @var null|BrokerHttpClientInterface */
- protected $brokerHttpClient;
-
- /** @var null|VerifierProcess */
- protected $verifierProcess;
-
- public function __construct(
- VerifierConfigInterface $config,
- VerifierProcess $verifierProcess = null,
- BrokerHttpClient $brokerHttpClient = null
- ) {
- $this->config = $config;
- $this->verifierProcess = $verifierProcess ?: new VerifierProcess();
- $this->processTimeout = $config->getProcessTimeout();
- $this->processIdleTimeout = $config->getProcessIdleTimeout();
-
- if ($brokerHttpClient) {
- $this->brokerHttpClient = $brokerHttpClient;
- }
- }
+ protected FFI $ffi;
+ protected CData $handle;
/**
- * @throws \Exception
- *
- * @return array parameters to be passed into the process
+ * Verifier constructor.
*/
- public function getArguments(): array
+ public function __construct(VerifierConfigInterface $config)
{
- $parameters = [];
-
- if (!empty($this->config->getProviderName())) {
- $parameters[] = "--provider='{$this->config->getProviderName()}'";
- }
-
- if ($this->config->getProviderBaseUrl() !== null) {
- $parameters[] = "--provider-base-url={$this->config->getProviderBaseUrl()}";
- }
-
- if ($this->config->getProviderVersion() !== null) {
- $parameters[] = "--provider-app-version={$this->config->getProviderVersion()}";
- }
-
- if ($this->config->getProviderBranch() !== null) {
- $parameters[] = "--provider-version-branch={$this->config->getProviderBranch()}";
- }
-
- if (\count($this->config->getConsumerVersionTag()) > 0) {
- foreach ($this->config->getConsumerVersionTag() as $tag) {
- $parameters[] = "--consumer-version-tag={$tag}";
- }
- }
-
- if (\count($this->config->getConsumerVersionSelectors()) > 0) {
- foreach ($this->config->getConsumerVersionSelectors() as $selector) {
- $parameters[] = "--consumer-version-selector='{$selector}'";
- }
- }
-
- if (\count($this->config->getProviderVersionTag()) > 0) {
- foreach ($this->config->getProviderVersionTag() as $tag) {
- $parameters[] = "--provider-version-tag={$tag}";
- }
- }
-
- if ($this->config->getProviderStatesSetupUrl() !== null) {
- $parameters[] = "--provider-states-setup-url={$this->config->getProviderStatesSetupUrl()}";
- }
-
- if ($this->config->isPublishResults() === true) {
- $parameters[] = '--publish-verification-results';
- }
-
- if ($this->config->getBrokerToken() !== null) {
- $parameters[] = "--broker-token={$this->config->getBrokerToken()}";
- }
-
- if ($this->config->getBrokerUsername() !== null) {
- $parameters[] = "--broker-username={$this->config->getBrokerUsername()}";
- }
-
- if ($this->config->getBrokerPassword() !== null) {
- $parameters[] = "--broker-password={$this->config->getBrokerPassword()}";
- }
-
- if ($this->config->getCustomProviderHeaders() !== null) {
- foreach ($this->config->getCustomProviderHeaders() as $customProviderHeader) {
- $parameters[] = "--custom-provider-header=\"{$customProviderHeader}\"";
- }
- }
-
- if ($this->config->isVerbose() === true) {
- $parameters[] = '--verbose=VERBOSE';
+ $this->ffi = FFI::cdef(\file_get_contents(Scripts::getHeader()), Scripts::getLibrary());
+ if ($level = \getenv('PACT_LOGLEVEL')) {
+ $this->ffi->pactffi_init_with_log_level($level);
}
-
- if ($this->config->getLogDirectory() !== null) {
- $parameters[] = "--log-dir={$this->config->getLogDirectory()}";
- }
-
- if ($this->config->getFormat() !== null) {
- $parameters[] = "--format={$this->config->getFormat()}";
- }
-
- if ($this->config->isEnablePending() === true) {
- $parameters[] = '--enable-pending';
- }
-
- if ($this->config->getIncludeWipPactSince() !== null) {
- $parameters[] = "--include-wip-pacts-since={$this->config->getIncludeWipPactSince()}";
- }
-
- if ($this->config->getBrokerUri() !== null) {
- $parameters[] = "--pact-broker-base-url={$this->config->getBrokerUri()->__toString()}";
- }
-
- return $parameters;
+ $this->newHandle($config);
}
/**
- * Make the request to the PACT Verifier Service to run a Pact file tests from the Pact Broker.
- *
- * @param string $consumerName name of the consumer to be compared against
- * @param null|string $tag optional tag of the consumer such as a branch name
- * @param null|string $consumerVersion optional specific version of the consumer; this is overridden by tag
+ * @param VerifierConfigInterface $config
*
- * @return Verifier
+ * @return $this
*/
- public function verify(string $consumerName, string $tag = null, string $consumerVersion = null): self
+ private function newHandle(VerifierConfigInterface $config): void
{
- $path = "/pacts/provider/{$this->config->getProviderName()}/consumer/{$consumerName}/";
-
- if ($tag) {
- $path .= "latest/{$tag}/";
- } elseif ($consumerVersion) {
- $path .= "version/{$consumerVersion}/";
- } else {
- $path .= 'latest/';
- }
-
- $uri = $this->config->getBrokerUri()->withPath($path);
-
- $arguments = \array_merge([$uri->__toString()], $this->getArguments());
-
- $this->verifyAction($arguments);
-
- return $this;
+ $this->handle = $this->ffi->pactffi_verifier_new();
+ $this->ffi->pactffi_verifier_set_provider_info(
+ $this->handle,
+ $config->getProviderName(),
+ $config->getScheme(),
+ $config->getHost(),
+ $config->getPort(),
+ $config->getBasePath()
+ );
+ $this->ffi->pactffi_verifier_set_filter_info(
+ $this->handle,
+ $config->getFilterDescription(),
+ $config->getFilterState(),
+ $config->getFilterNoState()
+ );
+ $this->ffi->pactffi_verifier_set_provider_state(
+ $this->handle,
+ $config->getStateChangeUrl() ? (string) $config->getStateChangeUrl() : null,
+ $config->isStateChangeTeardown(),
+ $config->isStateChangeAsBody()
+ );
+ $this->ffi->pactffi_verifier_set_verification_options(
+ $this->handle,
+ $config->isDisableSslVerification(),
+ $config->getRequestTimeout()
+ );
+ if ($config->isPublishResults()) {
+ $providerTags = ArrayData::createFrom($config->getProviderTags());
+ $this->ffi->pactffi_verifier_set_publish_options(
+ $this->handle,
+ $config->getProviderVersion(),
+ $config->getBuildUrl(),
+ $providerTags->getItems(),
+ $providerTags->getSize(),
+ $config->getProviderBranch()
+ );
+ }
+ $filterConsumerNames = ArrayData::createFrom($config->getFilterConsumerNames());
+ $this->ffi->pactffi_verifier_set_consumer_filters(
+ $this->handle,
+ $filterConsumerNames->getItems(),
+ $filterConsumerNames->getSize()
+ );
}
/**
- * Provides a way to validate local Pact JSON files.
- *
- * @param array $files paths to pact json files
+ * @param string $file
*
- * @return Verifier
+ * @return $this
*/
- public function verifyFiles(array $files): self
+ public function addFile(string $file): self
{
- $arguments = \array_merge($files, $this->getArguments());
-
- $this->verifyAction($arguments);
+ $this->ffi->pactffi_verifier_add_file_source($this->handle, $file);
return $this;
}
/**
- * Verify all Pacts from the Pact Broker are valid for the Provider.
+ * @param string $directory
+ *
+ * @return $this
*/
- public function verifyAll()
+ public function addDirectory(string $directory): self
{
- $arguments = $this->getBrokerHttpClient()->getAllConsumerUrls($this->config->getProviderName());
-
- $arguments = \array_merge($arguments, $this->getArguments());
+ $this->ffi->pactffi_verifier_add_directory_source($this->handle, $directory);
- $this->verifyAction($arguments);
+ return $this;
}
/**
- * Verify all PACTs for a given tag.
+ * @param UrlInterface $url
*
- * @param string $tag
+ * @return $this
*/
- public function verifyAllForTag(string $tag)
+ public function addUrl(UrlInterface $url): self
{
- $arguments = $this->getBrokerHttpClient()->getAllConsumerUrlsForTag($this->config->getProviderName(), $tag);
-
- $arguments = \array_merge($arguments, $this->getArguments());
+ $this->ffi->pactffi_verifier_url_source(
+ $this->handle,
+ (string) $url->getUrl(),
+ $url->getUsername(),
+ $url->getPassword(),
+ $url->getToken()
+ );
- $this->verifyAction($arguments);
+ return $this;
}
/**
- * Verify all PACTs that match the VerifierConfig
+ * @param BrokerInterface $broker
+ *
+ * @return $this
*/
- public function verifyFromConfig()
+ public function addBroker(BrokerInterface $broker): self
{
- $this->verifyAction($this->getArguments());
- }
+ $providerTags = ArrayData::createFrom($broker->getProviderTags());
+ $consumerVersionSelectors = ArrayData::createFrom($broker->getConsumerVersionSelectors());
+ $consumerVersionTags = ArrayData::createFrom($broker->getConsumerVersionTags());
+ $this->ffi->pactffi_verifier_broker_source_with_selectors(
+ $this->handle,
+ (string) $broker->getUrl(),
+ $broker->getUsername(),
+ $broker->getPassword(),
+ $broker->getToken(),
+ $broker->isEnablePending(),
+ $broker->getIncludeWipPactSince(),
+ $providerTags->getItems(),
+ $providerTags->getSize(),
+ $broker->getProviderBranch(),
+ $consumerVersionSelectors->getItems(),
+ $consumerVersionSelectors->getSize(),
+ $consumerVersionTags->getItems(),
+ $consumerVersionTags->getSize()
+ );
- public function getTimeoutValues(): array
- {
- return ['process_timeout' => $this->processTimeout, 'process_idle_timeout' => $this->processIdleTimeout];
+ return $this;
}
/**
- * Trigger execution of the Pact Verifier Service.
+ * Verifier a provider.
*
- * @param array $arguments
+ * @return bool
*/
- protected function verifyAction(array $arguments)
+ public function verify(): bool
{
- $this->verifierProcess->run($arguments, $this->processTimeout, $this->processIdleTimeout);
- }
-
- protected function getBrokerHttpClient(): BrokerHttpClientInterface
- {
- if (!$this->brokerHttpClient) {
- $user = $this->config->getBrokerUsername();
- $password = $this->config->getBrokerPassword();
- $token = $this->config->getBrokerToken();
- $reqFilter = $this->config->getRequestFilter();
-
- $config = [];
- if (\strlen($token) > 0) {
- $config = ['headers' => ['Authorization' => 'Bearer ' . $token]];
- } elseif ($user && $password) {
- $config = ['auth' => [$user, $password]];
- }
- if (\is_callable($reqFilter)) {
- $stack = HandlerStack::create();
- $stack->push(Middleware::mapRequest($reqFilter), 'requestFilter');
- $config['handler'] = $stack;
- }
- if (($sslVerify = \getenv('PACT_BROKER_SSL_VERIFY'))) {
- $client['verify'] = $sslVerify !== 'no';
- }
- $client = new GuzzleClient($config);
-
- $this->brokerHttpClient = new BrokerHttpClient($client, $this->config->getBrokerUri());
- }
+ $error = $this->ffi->pactffi_verifier_execute($this->handle);
+ $this->ffi->pactffi_verifier_shutdown($this->handle);
- return $this->brokerHttpClient;
+ return !$error;
}
}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/VerifierProcess.php b/src/PhpPact/Standalone/ProviderVerifier/VerifierProcess.php
deleted file mode 100644
index 712a54f4..00000000
--- a/src/PhpPact/Standalone/ProviderVerifier/VerifierProcess.php
+++ /dev/null
@@ -1,89 +0,0 @@
-processRunnerFactory = $processRunnerFactory ?: new ProcessRunnerFactory();
- }
-
- /**
- * @param LoggerInterface $logger
- *
- * @return VerifierProcess
- */
- public function setLogger(LoggerInterface $logger): self
- {
- $this->logger = $logger;
-
- return $this;
- }
-
- /**
- * Execute the Pact Verifier Service.
- *
- * @param array $arguments
- * @param int $processTimeout
- * @param int $processIdleTimeout
- */
- public function run(array $arguments, $processTimeout, $processIdleTimeout)
- {
- $logger = $this->getLogger();
- $processRunner = $this->processRunnerFactory->createRunner(
- $arguments,
- $logger
- );
-
- $logger->info("Verifying PACT with script:\n{$processRunner->getCommand()}\n\n");
-
- try {
- $processRunner->runBlocking();
-
- $logger->info('out > ' . $processRunner->getOutput());
- $logger->error('err > ' . $processRunner->getStderr());
- } catch (\Exception $e) {
- $logger->info('out > ' . $processRunner->getOutput());
- $logger->error('err > ' . $processRunner->getStderr());
-
- throw $e;
- }
- }
-
- /**
- * @return LoggerInterface
- */
- private function getLogger()
- {
- if (null === $this->logger) {
- $logHandler = new StreamHandler(new ResourceOutputStream(\STDOUT));
- $logHandler->setFormatter(new ConsoleFormatter(null, null, true));
- $this->logger = new Logger('console');
- $this->logger->pushHandler($logHandler);
- }
-
- return $this->logger;
- }
-}
diff --git a/src/PhpPact/Standalone/Runner/ProcessRunner.php b/src/PhpPact/Standalone/Runner/ProcessRunner.php
deleted file mode 100644
index bcd8325f..00000000
--- a/src/PhpPact/Standalone/Runner/ProcessRunner.php
+++ /dev/null
@@ -1,228 +0,0 @@
-exitCode = -1;
- $this->process = new Process($command . ' ' . \implode(' ', $arguments));
- }
-
- /**
- * @param LoggerInterface $logger
- *
- * @return ProcessRunner
- */
- public function setLogger(LoggerInterface $logger): self
- {
- $this->logger = $logger;
-
- return $this;
- }
-
- /**
- * @return string
- */
- public function getOutput(): ?string
- {
- return $this->output;
- }
-
- /**
- * @param string $output
- */
- public function setOutput(string $output): void
- {
- $this->output = $output;
- }
-
- /**
- * @return int
- */
- public function getExitCode(): int
- {
- return $this->exitCode;
- }
-
- /**
- * @param int $exitCode
- */
- public function setExitCode(int $exitCode): void
- {
- $this->exitCode = $exitCode;
- }
-
- public function getCommand(): string
- {
- return $this->process->getCommand();
- }
-
- /**
- * @return string
- */
- public function getStderr(): ?string
- {
- return $this->stderr;
- }
-
- /**
- * @param string $stderr
- */
- public function setStderr(string $stderr): void
- {
- $this->stderr = $stderr;
- }
-
- /**
- * Run a blocking, synchronous process
- */
- public function runBlocking(): int
- {
- $logger = $this->getLogger();
- $pid = null;
- $lambdaLoop = function () use ($logger, &$pid) {
- $logger->debug("Process command: {$this->process->getCommand()}");
-
- $pid = yield $this->process->start();
-
- $this->output .= yield ByteStream\buffer($this->process->getStdout());
- $this->stderr .= yield ByteStream\buffer($this->process->getStderr());
-
- $exitCode = yield $this->process->join();
- $this->setExitCode($exitCode);
- $logger->debug("Exit code: {$this->getExitCode()}");
-
- if ($this->getExitCode() !== 0) {
- throw new \Exception("PactPHP Process returned non-zero exit code: {$this->getExitCode()}");
- }
-
- Loop::stop();
- };
-
- Loop::run($lambdaLoop);
-
- return $pid;
- }
-
- /**
- * Run a blocking, synchronous process
- */
- public function runNonBlocking(): int
- {
- $logger = $this->getLogger();
-
- $pid = null;
-
- $lambdaLoop = function () use ($logger, &$pid) {
- $logger->debug("start background command: {$this->process->getCommand()}");
-
- $pid = yield $this->process->start();
-
- $this->process->getStdout()->read()->onResolve(function (\Throwable $reason = null, $value) {
- $this->output .= $value;
- });
- $this->process->getStderr()->read()->onResolve(function (\Throwable $reason = null, $value) {
- $this->output .= $value;
- });
-
- Loop::stop();
- };
-
- Loop::run($lambdaLoop);
-
- $logger->debug("started process pid=$pid");
-
- return $pid;
- }
-
- /**
- * Run the process and set output
- *
- * @param bool $blocking
- *
- * @return int Process Id
- */
- public function run($blocking = false): int
- {
- return $blocking
- ? $this->runBlocking()
- : $this->runNonBlocking();
- }
-
- /**
- * Stop the running process
- *
- * @throws ProcessException
- *
- * @return bool
- */
- public function stop(): bool
- {
- $pid = $this->process->getPid();
-
- print "\nStopping Process Id: {$pid}\n";
-
- if ('\\' === \DIRECTORY_SEPARATOR) {
- \exec(\sprintf('taskkill /F /T /PID %d 2>&1', $pid), $output, $exitCode);
- }
-
- $this->process->kill();
-
- if ($this->process->isRunning()) {
- throw new ProcessException(\sprintf('Error while killing process "%s".', $pid));
- }
-
- return true;
- }
-
- /**
- * @return LoggerInterface
- */
- private function getLogger()
- {
- if (null === $this->logger) {
- $logHandler = new StreamHandler(new ResourceOutputStream(\STDOUT));
- $logHandler->setFormatter(new ConsoleFormatter(null, null, true));
- $this->logger = new Logger('server');
- $this->logger->pushHandler($logHandler);
- }
-
- return $this->logger;
- }
-}
diff --git a/src/PhpPact/Standalone/StubService/Service/StubServerHttpService.php b/src/PhpPact/Standalone/StubService/Service/StubServerHttpService.php
index b3b98fed..a96cfdd9 100644
--- a/src/PhpPact/Standalone/StubService/Service/StubServerHttpService.php
+++ b/src/PhpPact/Standalone/StubService/Service/StubServerHttpService.php
@@ -17,12 +17,12 @@ class StubServerHttpService implements StubServerHttpServiceInterface
/**
* @var ClientInterface
*/
- private $client;
+ private ClientInterface $client;
/**
* @var StubServerConfigInterface
*/
- private $config;
+ private StubServerConfigInterface $config;
/**
* StubServerHttpService constructor.
diff --git a/src/PhpPact/Standalone/StubService/StubServer.php b/src/PhpPact/Standalone/StubService/StubServer.php
index ff474b2c..2de2fb70 100644
--- a/src/PhpPact/Standalone/StubService/StubServer.php
+++ b/src/PhpPact/Standalone/StubService/StubServer.php
@@ -4,7 +4,7 @@
use Exception;
use PhpPact\Standalone\Installer\Model\Scripts;
-use PhpPact\Standalone\Runner\ProcessRunner;
+use Symfony\Component\Process\Process;
/**
* Ruby Standalone Stub Server Wrapper
@@ -13,33 +13,33 @@
class StubServer
{
/** @var StubServerConfigInterface */
- private $config;
+ private StubServerConfigInterface $config;
- /** @var ProcessRunner */
- private $processRunner;
+ /** @var Process */
+ private Process $process;
public function __construct(StubServerConfigInterface $config)
{
- $this->config = $config;
+ $this->config = $config;
}
/**
* Start the Stub Server. Verify that it is running.
*
- * @param int $wait seconds to delay for the server to come up
- *
* @throws Exception
*
- * @return int process ID of the started Stub Server
+ * @return int|null process ID of the started Stub Server if running, null otherwise
*/
- public function start($wait = 1): int
+ public function start(): ?int
{
- $this->processRunner = new ProcessRunner(Scripts::getStubService(), $this->getArguments());
+ $this->process = new Process([Scripts::getStubService(), ...$this->getArguments()]);
- $processId = $this->processRunner->run();
- \sleep($wait); // wait for server to start
+ $this->process->start();
+ $this->process->waitUntil(function ($type, $output) {
+ return false !== \strpos($output, 'Server started on port');
+ });
- return $processId;
+ return $this->process->getPid();
}
/**
@@ -49,7 +49,9 @@ public function start($wait = 1): int
*/
public function stop(): bool
{
- return $this->processRunner->stop();
+ $this->process->stop();
+
+ return true;
}
/**
@@ -61,12 +63,64 @@ private function getArguments(): array
{
$results = [];
- $results[] = $this->config->getPactLocation();
- $results[] = "--host={$this->config->getHost()}";
- $results[] = "--port={$this->config->getPort()}";
+ if ($this->config->getBrokerUrl() !== null) {
+ $results[] = "--broker-url={$this->config->getBrokerUrl()}";
+ }
+
+ foreach ($this->config->getDirs() as $dir) {
+ $results[] = "--dir={$dir}";
+ }
+
+ if ($this->config->getExtension() !== null) {
+ $results[] = "--extension={$this->config->getExtension()}";
+ }
+
+ foreach ($this->config->getFiles() as $file) {
+ $results[] = "--file={$file}";
+ }
+
+ if ($this->config->getLogLevel() !== null) {
+ $results[] = "--loglevel={$this->config->getLogLevel()}";
+ }
+
+ if ($this->config->getPort() !== null) {
+ $results[] = "--port={$this->config->getPort()}";
+ }
+
+ if ($this->config->getProviderState() !== null) {
+ $results[] = "--provider-state={$this->config->getProviderState()}";
+ }
+
+ if ($this->config->getProviderStateHeaderName() !== null) {
+ $results[] = "--provider-state-header-name={$this->config->getProviderStateHeaderName()}";
+ }
+
+ if ($this->config->getToken() !== null) {
+ $results[] = "--token={$this->config->getToken()}";
+ }
+
+ foreach ($this->config->getUrls() as $url) {
+ $results[] = "--url={$url}";
+ }
+
+ if ($this->config->getUser() !== null) {
+ $results[] = "--user={$this->config->getUser()}";
+ }
+
+ if ($this->config->isCors()) {
+ $results[] = '--cors';
+ }
+
+ if ($this->config->isCorsReferer()) {
+ $results[] = '--cors-referer';
+ }
+
+ if ($this->config->isEmptyProviderState()) {
+ $results[] = '--empty-provider-state';
+ }
- if ($this->config->getLog() !== null) {
- $results[] = "--log={$this->config->getLog()}";
+ if ($this->config->isInsecureTls()) {
+ $results[] = '--insecure-tls';
}
return $results;
diff --git a/src/PhpPact/Standalone/StubService/StubServerConfig.php b/src/PhpPact/Standalone/StubService/StubServerConfig.php
index 0b179366..0d54cc57 100644
--- a/src/PhpPact/Standalone/StubService/StubServerConfig.php
+++ b/src/PhpPact/Standalone/StubService/StubServerConfig.php
@@ -11,49 +11,107 @@
*/
class StubServerConfig implements StubServerConfigInterface
{
+ private ?UriInterface $brokerUrl = null;
+ private ?int $port = null;
+
+ private ?string $extension = null;
+ private ?string $logLevel = null;
+ private ?string $providerState = null;
+ private ?string $providerStateHeaderName = null;
+ private ?string $token = null;
+ private ?string $user = null;
+
+ private array $dirs = [];
+ private array $files = [];
+ private array $urls = [];
+ private array $consumerNames = [];
+ private array $providerNames = [];
+
+ private bool $cors = false;
+ private bool $corsReferer = false;
+ private bool $emptyProviderState = false;
+ private bool $insecureTls = false;
+
+ private string $endpoint;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getBrokerUrl(): ?UriInterface
+ {
+ return $this->brokerUrl;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setBrokerUrl(UriInterface $brokerUrl): StubServerConfigInterface
+ {
+ $this->brokerUrl = $brokerUrl;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setDirs(string ...$dirs): StubServerConfigInterface
+ {
+ $this->dirs = $dirs;
+
+ return $this;
+ }
+
/**
- * Host on which to bind the service.
- *
- * @var string
+ * {@inheritdoc}
*/
- private $host = 'localhost';
+ public function getDirs(): array
+ {
+ return $this->dirs;
+ }
/**
- * Port on which to run the service.
- *
- * @var int
+ * {@inheritdoc}
*/
- private $port = 7201;
+ public function getExtension(): ?string
+ {
+ return $this->extension;
+ }
/**
- * @var bool
+ * {@inheritdoc}
*/
- private $secure = false;
+ public function setExtension(string $extension): StubServerConfigInterface
+ {
+ $this->extension = $extension;
+
+ return $this;
+ }
/**
- * File to which to log output.
- *
- * @var string
+ * {@inheritdoc}
*/
- private $log;
+ public function setFiles(string ...$files): StubServerConfigInterface
+ {
+ $this->files = $files;
- private $pactLocation;
- private $endpoint;
+ return $this;
+ }
/**
* {@inheritdoc}
*/
- public function getHost(): string
+ public function getFiles(): array
{
- return $this->host;
+ return $this->files;
}
/**
* {@inheritdoc}
*/
- public function setHost(string $host): StubServerConfigInterface
+ public function setLogLevel(string $logLevel): StubServerConfigInterface
{
- $this->host = $host;
+ $this->logLevel = $logLevel;
return $this;
}
@@ -61,7 +119,15 @@ public function setHost(string $host): StubServerConfigInterface
/**
* {@inheritdoc}
*/
- public function getPort(): int
+ public function getLogLevel(): ?string
+ {
+ return $this->logLevel;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPort(): ?int
{
return $this->port;
}
@@ -79,17 +145,17 @@ public function setPort(int $port): StubServerConfigInterface
/**
* {@inheritdoc}
*/
- public function isSecure(): bool
+ public function getProviderState(): ?string
{
- return $this->secure;
+ return $this->providerState;
}
/**
* {@inheritdoc}
*/
- public function setSecure(bool $secure): StubServerConfigInterface
+ public function setProviderState(string $providerState): StubServerConfigInterface
{
- $this->secure = $secure;
+ $this->providerState = $providerState;
return $this;
}
@@ -97,49 +163,203 @@ public function setSecure(bool $secure): StubServerConfigInterface
/**
* {@inheritdoc}
*/
- public function getBaseUri(): UriInterface
+ public function getProviderStateHeaderName(): ?string
+ {
+ return $this->providerStateHeaderName;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setProviderStateHeaderName(string $providerStateHeaderName): StubServerConfigInterface
{
- $protocol = $this->secure ? 'https' : 'http';
+ $this->providerStateHeaderName = $providerStateHeaderName;
- return new Uri("{$protocol}://{$this->getHost()}:{$this->getPort()}");
+ return $this;
}
/**
* {@inheritdoc}
*/
- public function getLog()
+ public function getToken(): ?string
{
- return $this->log;
+ return $this->token;
}
/**
* {@inheritdoc}
*/
- public function setLog(string $log): StubServerConfigInterface
+ public function setToken(?string $token): StubServerConfigInterface
{
- $this->log = $log;
+ $this->token = $token;
return $this;
}
- public function getPactLocation(): string
+ /**
+ * {@inheritdoc}
+ */
+ public function setUrls(string ...$urls): StubServerConfigInterface
{
- return $this->pactLocation;
+ $this->urls = $urls;
+
+ return $this;
}
- public function setPactLocation(string $location)
+ /**
+ * {@inheritdoc}
+ */
+ public function getUrls(): array
{
- $this->pactLocation = $location;
+ return $this->urls;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getUser(): ?string
+ {
+ return $this->user;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setUser(string $user): StubServerConfigInterface
+ {
+ $this->user = $user;
return $this;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function isCors(): bool
+ {
+ return $this->cors;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setCors(bool $cors): StubServerConfigInterface
+ {
+ $this->cors = $cors;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isCorsReferer(): bool
+ {
+ return $this->corsReferer;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setCorsReferer(bool $corsReferer): StubServerConfigInterface
+ {
+ $this->corsReferer = $corsReferer;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isEmptyProviderState(): bool
+ {
+ return $this->emptyProviderState;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setEmptyProviderState(bool $emptyProviderState): StubServerConfigInterface
+ {
+ $this->emptyProviderState = $emptyProviderState;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isInsecureTls(): bool
+ {
+ return $this->insecureTls;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setInsecureTls(bool $insecureTls): StubServerConfigInterface
+ {
+ $this->insecureTls = $insecureTls;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setConsumerNames(string ...$consumerNames): StubServerConfigInterface
+ {
+ $this->consumerNames = $consumerNames;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getConsumerNames(): array
+ {
+ return $this->consumerNames;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setProviderNames(string ...$providerNames): StubServerConfigInterface
+ {
+ $this->providerNames = $providerNames;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getProviderNames(): array
+ {
+ return $this->providerNames;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getBaseUri(): UriInterface
+ {
+ return new Uri("http://localhost:{$this->getPort()}");
+ }
+
+ /**
+ * {@inheritdoc}
+ */
public function getEndpoint(): string
{
return $this->endpoint;
}
- public function setEndpoint(string $endpoint)
+ /**
+ * {@inheritdoc}
+ */
+ public function setEndpoint(string $endpoint): StubServerConfigInterface
{
$this->endpoint = $endpoint;
diff --git a/src/PhpPact/Standalone/StubService/StubServerConfigInterface.php b/src/PhpPact/Standalone/StubService/StubServerConfigInterface.php
index 2585fd88..dd2848fa 100644
--- a/src/PhpPact/Standalone/StubService/StubServerConfigInterface.php
+++ b/src/PhpPact/Standalone/StubService/StubServerConfigInterface.php
@@ -11,63 +11,225 @@
interface StubServerConfigInterface
{
/**
- * @return string the host of the stub service
+ * @return null|UriInterface url to the pact broker
*/
- public function getHost(): string;
+ public function getBrokerUrl(): ?UriInterface;
/**
- * @param string $host The host of the stub service
+ * URL of the pact broker to fetch pacts from
+ *
+ * @param UriInterface $brokerUrl
+ *
+ * @return StubServerConfigInterface
+ */
+ public function setBrokerUrl(UriInterface $brokerUrl): self;
+
+ /**
+ * @param string ...$dirs Directory of pact files to load
*
* @return StubServerConfigInterface
*/
- public function setHost(string $host): self;
+ public function setDirs(string ...$dirs): self;
/**
- * @return int the port of the stub service
+ * @return array
*/
- public function getPort(): int;
+ public function getDirs(): array;
/**
- * @param int $port the port of the stub service
+ * @return null|string
+ */
+ public function getExtension(): ?string;
+
+ /**
+ * @param string $extension File extension to use when loading from a directory (default is json)
+ *
+ * @return StubServerConfigInterface
+ */
+ public function setExtension(string $extension): self;
+
+ /**
+ * @param string ...$files Pact file to load
+ *
+ * @return StubServerConfigInterface
+ */
+ public function setFiles(string ...$files): self;
+
+ /**
+ * @return array
+ */
+ public function getFiles(): array;
+
+ /**
+ * @return null|string
+ */
+ public function getLogLevel(): ?string;
+
+ /**
+ * @param string $logLevel Log level (defaults to info) [possible values: error, warn, info, debug, trace, none]
+ *
+ * @return StubServerConfigInterface
+ */
+ public function setLogLevel(string $logLevel): self;
+
+ /**
+ * @return null|int the port of the stub service
+ */
+ public function getPort(): ?int;
+
+ /**
+ * @param int $port Port to run on (defaults to random port assigned by the OS)
*
* @return StubServerConfigInterface
*/
public function setPort(int $port): self;
/**
- * @return bool true if https
+ * @return null|string state of the provider
*/
- public function isSecure(): bool;
+ public function getProviderState(): ?string;
/**
- * @param bool $secure set to true for https
+ * @param string $providerState Provider state regular expression to filter the responses by
*
* @return StubServerConfigInterface
*/
- public function setSecure(bool $secure): self;
+ public function setProviderState(string $providerState): self;
/**
- * @return UriInterface
+ * @return null|string name of the header
*/
- public function getBaseUri(): UriInterface;
+ public function getProviderStateHeaderName(): ?string;
+
+ /**
+ * @param string $providerStateHeaderName Name of the header parameter containing the provider state to be used in case multiple matching interactions are found
+ *
+ * @return StubServerConfigInterface
+ */
+ public function setProviderStateHeaderName(string $providerStateHeaderName): self;
+
+ /**
+ * @return null|string token for the pact broker
+ */
+ public function getToken(): ?string;
/**
- * @return string directory for log output
+ * @param null|string $token Bearer token to use when fetching pacts from URLS or Pact Broker
+ *
+ * @return StubServerConfigInterface
*/
- public function getLog();
+ public function setToken(?string $token): self;
/**
- * @param string $log directory for log output
+ * @param string ...$urls URL of pact file to fetch
*
* @return StubServerConfigInterface
*/
- public function setLog(string $log): self;
+ public function setUrls(string ...$urls): self;
+
+ /**
+ * @return array
+ */
+ public function getUrls(): array;
+
+ /**
+ * @return null|string user and password
+ */
+ public function getUser(): ?string;
- public function getPactLocation(): string;
+ /**
+ * @param string $user User and password to use when fetching pacts from URLS or Pact Broker in user:password form
+ *
+ * @return StubServerConfigInterface
+ */
+ public function setUser(string $user): self;
- public function setPactLocation(string $location);
+ /**
+ * @return bool
+ */
+ public function isCors(): bool;
+ /**
+ * @param bool $cors
+ *
+ * @return StubServerConfigInterface
+ */
+ public function setCors(bool $cors): self;
+
+ /**
+ * @return bool
+ */
+ public function isCorsReferer(): bool;
+
+ /**
+ * @param bool $corsReferer
+ *
+ * @return StubServerConfigInterface
+ */
+ public function setCorsReferer(bool $corsReferer): self;
+
+ /**
+ * @return bool
+ */
+ public function isEmptyProviderState(): bool;
+
+ /**
+ * @param bool $emptyProviderState
+ *
+ * @return StubServerConfigInterface
+ */
+ public function setEmptyProviderState(bool $emptyProviderState): self;
+
+ /**
+ * @return bool
+ */
+ public function isInsecureTls(): bool;
+
+ /**
+ * @param bool $insecureTls
+ *
+ * @return StubServerConfigInterface
+ */
+ public function setInsecureTls(bool $insecureTls): self;
+
+ /**
+ * @param string ...$consumerNames Consumer name to use to filter the Pacts fetched from the Pact broker
+ *
+ * @return StubServerConfigInterface
+ */
+ public function setConsumerNames(string ...$consumerNames): self;
+
+ /**
+ * @return array
+ */
+ public function getConsumerNames(): array;
+
+ /**
+ * @param string ...$providerNames Provider name to use to filter the Pacts fetched from the Pact broker
+ *
+ * @return StubServerConfigInterface
+ */
+ public function setProviderNames(string ...$providerNames): self;
+
+ /**
+ * @return array
+ */
+ public function getProviderNames(): array;
+
+ /**
+ * @return UriInterface
+ */
+ public function getBaseUri(): UriInterface;
+
+ /**
+ * @return string
+ */
public function getEndpoint(): string;
- public function setEndpoint(string $location);
+ /**
+ * @param string $endpoint
+ *
+ * @return StubServerConfigInterface
+ */
+ public function setEndpoint(string $endpoint): self;
}
diff --git a/tests/PhpPact/Broker/Service/BrokerHttpClientTest.php b/tests/PhpPact/Broker/Service/BrokerHttpClientTest.php
deleted file mode 100644
index 7c510e79..00000000
--- a/tests/PhpPact/Broker/Service/BrokerHttpClientTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
- [
- 'pacts' => [
- ['href' => 'pact-url-1'],
- ['href' => 'pact-url-2'],
- ],
- ],
- ]
- );
-
- $streamMock = $this->createMock(StreamInterface::class);
- $streamMock->expects($this->once())
- ->method('getContents')
- ->will($this->returnValue($expectedContents));
-
- $responseMock = $this->createMock(ResponseInterface::class);
- $responseMock->expects($this->once())
- ->method('getBody')
- ->will($this->returnValue($streamMock));
-
- $httpClientMock = $this->createMock(ClientInterface::class);
- $httpClientMock->expects($this->once())
- ->method('get')
- ->will($this->returnValue($responseMock));
-
- $uriMock = $this->createMock(UriInterface::class);
- $uriMock->expects($this->once())
- ->method('withPath')
- ->with($this->equalTo($expectedPath))
- ->will($this->returnValue($uriMock));
-
- $broker = new BrokerHttpClient($httpClientMock, $uriMock);
- $broker->getAllConsumerUrls($provider);
- }
-}
diff --git a/tests/PhpPact/Consumer/InteractionBuilderTest.php b/tests/PhpPact/Consumer/InteractionBuilderTest.php
index 3bdd9a14..713a2f15 100644
--- a/tests/PhpPact/Consumer/InteractionBuilderTest.php
+++ b/tests/PhpPact/Consumer/InteractionBuilderTest.php
@@ -6,39 +6,12 @@
use PhpPact\Consumer\Matcher\Matcher;
use PhpPact\Consumer\Model\ConsumerRequest;
use PhpPact\Consumer\Model\ProviderResponse;
-use PhpPact\Http\GuzzleClient;
use PhpPact\Standalone\Exception\MissingEnvVariableException;
-use PhpPact\Standalone\MockService\MockServer;
use PhpPact\Standalone\MockService\MockServerEnvConfig;
-use PhpPact\Standalone\MockService\Service\MockServerHttpService;
-use PhpPact\Standalone\MockService\Service\MockServerHttpServiceInterface;
use PHPUnit\Framework\TestCase;
class InteractionBuilderTest extends TestCase
{
- /** @var MockServerHttpServiceInterface */
- private $service;
-
- /** @var MockServer */
- private $mockServer;
-
- /**
- * @throws MissingEnvVariableException
- * @throws \Exception
- */
- protected function setUp(): void
- {
- $config = new MockServerEnvConfig();
- $this->mockServer = new MockServer($config);
- $this->mockServer->start();
- $this->service = new MockServerHttpService(new GuzzleClient(), $config);
- }
-
- protected function tearDown(): void
- {
- $this->mockServer->stop();
- }
-
/**
* @throws MissingEnvVariableException
* @throws \Exception
@@ -63,13 +36,13 @@ public function testSimpleGet()
->addHeader('Content-Type', 'application/json');
$builder = new InteractionBuilder(new MockServerEnvConfig());
- $result = $builder
+ $builder
->given('A test request.')
->uponReceiving('A test response.')
->with($request)
->willRespondWith($response);
- $this->assertTrue($result);
+ $this->assertFalse($builder->verify());
}
/**
@@ -101,13 +74,13 @@ public function testPostWithBody()
]);
$builder = new InteractionBuilder(new MockServerEnvConfig());
- $result = $builder
+ $builder
->given('A test request.')
->uponReceiving('A test response.')
->with($request)
->willRespondWith($response);
- $this->assertTrue($result);
+ $this->assertFalse($builder->verify());
}
/**
@@ -135,12 +108,12 @@ public function testBuildWithEachLikeMatcher()
]);
$builder = new InteractionBuilder(new MockServerEnvConfig());
- $result = $builder
+ $builder
->given('A test request.')
->uponReceiving('A test response.')
->with($request)
->willRespondWith($response);
- $this->assertTrue($result);
+ $this->assertFalse($builder->verify());
}
}
diff --git a/tests/PhpPact/Consumer/Matcher/MatcherTest.php b/tests/PhpPact/Consumer/Matcher/MatcherTest.php
index 789bdf57..b406def8 100644
--- a/tests/PhpPact/Consumer/Matcher/MatcherTest.php
+++ b/tests/PhpPact/Consumer/Matcher/MatcherTest.php
@@ -9,7 +9,7 @@
class MatcherTest extends TestCase
{
/** @var Matcher */
- private $matcher;
+ private Matcher $matcher;
protected function setUp(): void
{
@@ -32,7 +32,7 @@ public function testLike()
{
$json = \json_encode($this->matcher->like(12));
- $this->assertEquals('{"contents":12,"json_class":"Pact::SomethingLike"}', $json);
+ $this->assertEquals('{"value":12,"pact:matcher:type":"type"}', $json);
}
/**
@@ -45,15 +45,17 @@ public function testEachLikeStdClass()
$object->value2 = 2;
$expected = \json_encode([
- 'contents' => [
- 'value1' => [
- 'contents' => 1,
- 'json_class' => 'Pact::SomethingLike',
- ],
- 'value2' => 2,
+ 'value' => [
+ [
+ 'value1' => [
+ 'value' => 1,
+ 'pact:matcher:type' => 'type',
+ ],
+ 'value2' => 2,
+ ]
],
- 'json_class' => 'Pact::ArrayLike',
- 'min' => 1,
+ 'pact:matcher:type' => 'type',
+ 'min' => 1,
]);
$actual = \json_encode($this->matcher->eachLike($object, 1));
@@ -72,15 +74,17 @@ public function testEachLikeArray()
];
$expected = \json_encode([
- 'contents' => [
- 'value1' => [
- 'contents' => 1,
- 'json_class' => 'Pact::SomethingLike',
- ],
- 'value2' => 2,
+ 'value' => [
+ [
+ 'value1' => [
+ 'value' => 1,
+ 'pact:matcher:type' => 'type',
+ ],
+ 'value2' => 2,
+ ]
],
- 'json_class' => 'Pact::ArrayLike',
- 'min' => 1,
+ 'pact:matcher:type' => 'type',
+ 'min' => 1,
]);
$actual = \json_encode($this->matcher->eachLike($object, 1));
@@ -103,15 +107,9 @@ public function testRegexNoMatch()
public function testRegex()
{
$expected = [
- 'data' => [
- 'generate' => 'Games',
- 'matcher' => [
- 'json_class' => 'Regexp',
- 'o' => 0,
- 's' => 'Games|Other',
- ],
- ],
- 'json_class' => 'Pact::Term',
+ 'value' => 'Games',
+ 'regex' => 'Games|Other',
+ 'pact:matcher:type' => 'regex',
];
$actual = $this->matcher->regex('Games', 'Games|Other');
@@ -125,15 +123,9 @@ public function testRegex()
public function testDate()
{
$expected = [
- 'data' => [
- 'generate' => '2010-01-17',
- 'matcher' => [
- 'json_class' => 'Regexp',
- 'o' => 0,
- 's' => '^([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))?)$',
- ],
- ],
- 'json_class' => 'Pact::Term',
+ 'value' => '2010-01-17',
+ 'regex' => '^([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))?)$',
+ 'pact:matcher:type' => 'regex',
];
$actual = $this->matcher->dateISO8601('2010-01-17');
@@ -149,15 +141,9 @@ public function testDate()
public function testTime($time)
{
$expected = [
- 'data' => [
- 'generate' => $time,
- 'matcher' => [
- 'json_class' => 'Regexp',
- 'o' => 0,
- 's' => '^(T\\d\\d:\\d\\d(:\\d\\d)?(\\.\\d+)?([+-][0-2]\\d(?:|:?[0-5]\\d)|Z)?)$',
- ],
- ],
- 'json_class' => 'Pact::Term',
+ 'value' => $time,
+ 'regex' => '^(T\\d\\d:\\d\\d(:\\d\\d)?(\\.\\d+)?([+-][0-2]\\d(?:|:?[0-5]\\d)|Z)?)$',
+ 'pact:matcher:type' => 'regex',
];
$actual = $this->matcher->timeISO8601($time);
@@ -189,15 +175,9 @@ public function dataProviderForTimeTest()
public function testDateTime($dateTime)
{
$expected = [
- 'data' => [
- 'generate' => $dateTime,
- 'matcher' => [
- 'json_class' => 'Regexp',
- 'o' => 0,
- 's' => '^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d([+-][0-2]\\d(?:|:?[0-5]\\d)|Z)?$',
- ],
- ],
- 'json_class' => 'Pact::Term',
+ 'value' => $dateTime,
+ 'regex' => '^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d([+-][0-2]\\d(?:|:?[0-5]\\d)|Z)?$',
+ 'pact:matcher:type' => 'regex',
];
$actual = $this->matcher->dateTimeISO8601($dateTime);
@@ -227,15 +207,9 @@ public function dataProviderForDateTimeTest()
public function testDateTimeWithMillis($dateTime)
{
$expected = [
- 'data' => [
- 'generate' => $dateTime,
- 'matcher' => [
- 'json_class' => 'Regexp',
- 'o' => 0,
- 's' => '^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d{3}([+-][0-2]\\d(?:|:?[0-5]\\d)|Z)?$',
- ],
- ],
- 'json_class' => 'Pact::Term',
+ 'value' => $dateTime,
+ 'regex' => '^\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d{3}([+-][0-2]\\d(?:|:?[0-5]\\d)|Z)?$',
+ 'pact:matcher:type' => 'regex',
];
$actual = $this->matcher->dateTimeWithMillisISO8601($dateTime);
@@ -263,15 +237,9 @@ public function dataProviderForDateTimeWithMillisTest()
public function testTimestampRFC3339()
{
$expected = [
- 'data' => [
- 'generate' => 'Mon, 31 Oct 2016 15:21:41 -0400',
- 'matcher' => [
- 'json_class' => 'Regexp',
- 'o' => 0,
- 's' => '^(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\\s\\d{2}\\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s\\d{4}\\s\\d{2}:\\d{2}:\\d{2}\\s(\\+|-)\\d{4}$',
- ],
- ],
- 'json_class' => 'Pact::Term',
+ 'value' => 'Mon, 31 Oct 2016 15:21:41 -0400',
+ 'regex' => '^(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\\s\\d{2}\\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s\\d{4}\\s\\d{2}:\\d{2}:\\d{2}\\s(\\+|-)\\d{4}$',
+ 'pact:matcher:type' => 'regex',
];
$actual = $this->matcher->timestampRFC3339('Mon, 31 Oct 2016 15:21:41 -0400');
@@ -286,7 +254,7 @@ public function testInteger()
{
$json = \json_encode($this->matcher->integer());
- $this->assertEquals('{"contents":13,"json_class":"Pact::SomethingLike"}', $json);
+ $this->assertEquals('{"value":13,"pact:matcher:type":"type"}', $json);
}
/**
@@ -296,7 +264,7 @@ public function testBoolean()
{
$json = \json_encode($this->matcher->boolean());
- $this->assertEquals('{"contents":true,"json_class":"Pact::SomethingLike"}', $json);
+ $this->assertEquals('{"value":true,"pact:matcher:type":"type"}', $json);
}
/**
@@ -306,7 +274,7 @@ public function testDecimal()
{
$json = \json_encode($this->matcher->decimal());
- $this->assertEquals('{"contents":13.01,"json_class":"Pact::SomethingLike"}', $json);
+ $this->assertEquals('{"value":13.01,"pact:matcher:type":"type"}', $json);
}
/**
@@ -315,15 +283,9 @@ public function testDecimal()
public function testHexadecimal()
{
$expected = [
- 'data' => [
- 'generate' => '3F',
- 'matcher' => [
- 'json_class' => 'Regexp',
- 'o' => 0,
- 's' => '^[0-9a-fA-F]+$',
- ],
- ],
- 'json_class' => 'Pact::Term',
+ 'value' => '3F',
+ 'regex' => '^[0-9a-fA-F]+$',
+ 'pact:matcher:type' => 'regex',
];
$this->assertEquals($expected, $this->matcher->hexadecimal());
@@ -335,15 +297,9 @@ public function testHexadecimal()
public function testUuid()
{
$expected = [
- 'data' => [
- 'generate' => 'ce118b6e-d8e1-11e7-9296-cec278b6b50a',
- 'matcher' => [
- 'json_class' => 'Regexp',
- 'o' => 0,
- 's' => '^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$',
- ],
- ],
- 'json_class' => 'Pact::Term',
+ 'value' => 'ce118b6e-d8e1-11e7-9296-cec278b6b50a',
+ 'regex' => '^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$',
+ 'pact:matcher:type' => 'regex',
];
$this->assertEquals($expected, $this->matcher->uuid());
@@ -355,15 +311,9 @@ public function testUuid()
public function testIpv4Address()
{
$expected = [
- 'data' => [
- 'generate' => '127.0.0.13',
- 'matcher' => [
- 'json_class' => 'Regexp',
- 'o' => 0,
- 's' => '^(\\d{1,3}\\.)+\\d{1,3}$',
- ],
- ],
- 'json_class' => 'Pact::Term',
+ 'value' => '127.0.0.13',
+ 'regex' => '^(\\d{1,3}\\.)+\\d{1,3}$',
+ 'pact:matcher:type' => 'regex',
];
$this->assertEquals($expected, $this->matcher->ipv4Address());
@@ -375,15 +325,9 @@ public function testIpv4Address()
public function testIpv6Address()
{
$expected = [
- 'data' => [
- 'generate' => '::ffff:192.0.2.128',
- 'matcher' => [
- 'json_class' => 'Regexp',
- 'o' => 0,
- 's' => '^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$',
- ],
- ],
- 'json_class' => 'Pact::Term',
+ 'value' => '::ffff:192.0.2.128',
+ 'regex' => '^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$',
+ 'pact:matcher:type' => 'regex',
];
$this->assertEquals($expected, $this->matcher->ipv6Address());
diff --git a/tests/PhpPact/Consumer/Model/ConsumerRequestTest.php b/tests/PhpPact/Consumer/Model/ConsumerRequestTest.php
index 4e760bbe..f30aeca7 100644
--- a/tests/PhpPact/Consumer/Model/ConsumerRequestTest.php
+++ b/tests/PhpPact/Consumer/Model/ConsumerRequestTest.php
@@ -15,16 +15,16 @@ public function testSerializing()
->setMethod('PUT')
->setPath('/somepath')
->addHeader('Content-Type', 'application/json')
+ ->addQueryParameter('fruit', ['apple', 'banana'])
->setBody([
'currentCity' => 'Austin',
]);
- $data = \json_decode(\json_encode($model->jsonSerialize()), true);
-
- $this->assertEquals('PUT', $data['method']);
- $this->assertEquals('application/json', $data['headers']['Content-Type']);
- $this->assertEquals('/somepath', $data['path']);
- $this->assertEquals('Austin', $data['body']['currentCity']);
+ $this->assertEquals('PUT', $model->getMethod());
+ $this->assertEquals(['Content-Type' => ['application/json']], $model->getHeaders());
+ $this->assertEquals(['fruit' => ['apple', 'banana']], $model->getQuery());
+ $this->assertEquals('/somepath', $model->getPath());
+ $this->assertEquals('{"currentCity":"Austin"}', $model->getBody());
}
public function testSerializingWhenPathUsingMatcher()
@@ -36,17 +36,15 @@ public function testSerializingWhenPathUsingMatcher()
->setMethod('PATCH')
->setPath($matcher->regex("/somepath/$pathVariable/status", '\/somepath\/[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}\/status'))
->addHeader('Content-Type', 'application/json')
+ ->addQueryParameter('food', 'milk')
->setBody([
'status' => 'finished',
]);
- $data = \json_decode(\json_encode($model->jsonSerialize()), true);
-
- $this->assertEquals('PATCH', $data['method']);
- $this->assertEquals('application/json', $data['headers']['Content-Type']);
- $this->assertIsArray($data['path']);
- $this->assertArrayHasKey('data', $data['path']);
- $this->assertArrayHasKey('json_class', $data['path']);
- $this->assertEquals('finished', $data['body']['status']);
+ $this->assertEquals('PATCH', $model->getMethod());
+ $this->assertEquals(['Content-Type' => ['application/json']], $model->getHeaders());
+ $this->assertEquals(['food' => ['milk']], $model->getQuery());
+ $this->assertEquals('{"value":"\/somepath\/474d610b-c6e3-45bd-9f70-529e7ad21df0\/status","regex":"\\\\\\/somepath\\\\\\/[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}\\\\\\/status","pact:matcher:type":"regex"}', $model->getPath());
+ $this->assertEquals('{"status":"finished"}', $model->getBody());
}
}
diff --git a/tests/PhpPact/Consumer/Model/MessageTest.php b/tests/PhpPact/Consumer/Model/MessageTest.php
new file mode 100644
index 00000000..22ef0b9a
--- /dev/null
+++ b/tests/PhpPact/Consumer/Model/MessageTest.php
@@ -0,0 +1,32 @@
+ 'bar'];
+ $metadata = ['queue' => 'foo', 'routing_key' => 'bar'];
+ $contents = 'test';
+
+ $subject = (new Message())
+ ->setId($id)
+ ->setDescription($description)
+ ->addProviderState($providerStateName, $providerStateParams)
+ ->setMetadata($metadata)
+ ->setContents($contents);
+
+ static::assertSame($id, $subject->getId());
+ static::assertSame($description, $subject->getDescription());
+ static::assertEquals([(object) ['name' => $providerStateName, 'params' => $providerStateParams]], $subject->getProviderStates());
+ static::assertSame($metadata, $subject->getMetadata());
+ static::assertSame($contents, $subject->getContents());
+ }
+}
diff --git a/tests/PhpPact/Consumer/Model/ProviderResponseTest.php b/tests/PhpPact/Consumer/Model/ProviderResponseTest.php
index 49e2c7f1..2da664be 100644
--- a/tests/PhpPact/Consumer/Model/ProviderResponseTest.php
+++ b/tests/PhpPact/Consumer/Model/ProviderResponseTest.php
@@ -17,10 +17,8 @@ public function testSerializing()
'currentCity' => 'Austin',
]);
- $data = \json_decode(\json_encode($model->jsonSerialize()), true);
-
- $this->assertEquals(200, $data['status']);
- $this->assertEquals('application/json', $data['headers']['Content-Type']);
- $this->assertEquals('Austin', $data['body']['currentCity']);
+ $this->assertEquals(200, $model->getStatus());
+ $this->assertEquals(['Content-Type' => ['application/json']], $model->getHeaders());
+ $this->assertEquals('{"currentCity":"Austin"}', $model->getBody());
}
}
diff --git a/tests/PhpPact/Standalone/Broker/BrokerConfigTest.php b/tests/PhpPact/Standalone/Broker/BrokerConfigTest.php
index 8a35baf3..2e799e12 100644
--- a/tests/PhpPact/Standalone/Broker/BrokerConfigTest.php
+++ b/tests/PhpPact/Standalone/Broker/BrokerConfigTest.php
@@ -2,42 +2,87 @@
namespace PhpPactTest\Standalone\Broker;
-use PhpPact\Standalone\MockService\MockServerConfig;
+use GuzzleHttp\Psr7\Uri;
+use PhpPact\Standalone\Broker\BrokerConfig;
use PHPUnit\Framework\TestCase;
class BrokerConfigTest extends TestCase
{
public function testSetters()
{
- $host = 'test-host';
- $port = 1234;
- $provider = 'test-provider';
- $consumer = 'test-consumer';
- $pactDir = 'test-pact-dir/';
- $pactFileWriteMode = 'merge';
- $log = 'test-log-dir/';
- $cors = true;
- $pactSpecificationVersion = 2.0;
-
- $subject = (new MockServerConfig())
- ->setHost($host)
- ->setPort($port)
- ->setProvider($provider)
+ $brokerUri = new Uri('http://localhost');
+ $brokerToken = 'abc-123';
+ $brokerUsername = 'user';
+ $brokerPassword = 'pass';
+
+ $verbose = true;
+ $pacticipant = 'a pacticipant';
+
+ $request = 'POST';
+ $header = 'Accept application/json';
+ $data = '{"key": "value"}';
+ $user = 'username:password';
+ $url = 'https://example.org/webhook';
+ $consumer = 'test-consumer';
+ $provider = 'test-provider';
+ $description = 'an example webhook';
+ $uuid = 'd2181b32-8b03-4daf-8cc0-d9168b2f6fac';
+
+ $version = '1.2.3';
+ $branch = 'new-feature';
+ $tag = 'prod';
+
+ $name = 'My Project';
+ $repositoryUrl = 'https://github.com/vendor/my-project';
+
+ $consumerVersion = '1.1.2';
+ $pactLocations = '/path/to/pacts';
+
+ $subject = (new BrokerConfig())
+ ->setBrokerUri($brokerUri)
+ ->setBrokerToken($brokerToken)
+ ->setBrokerUsername($brokerUsername)
+ ->setBrokerPassword($brokerPassword)
+ ->setVerbose($verbose)
+ ->setPacticipant($pacticipant)
+ ->setRequest($request)
+ ->setHeader($header)
+ ->setData($data)
+ ->setUser($user)
+ ->setUrl($url)
->setConsumer($consumer)
- ->setPactDir($pactDir)
- ->setPactFileWriteMode($pactFileWriteMode)
- ->setLog($log)
- ->setPactSpecificationVersion($pactSpecificationVersion)
- ->setCors($cors);
-
- static::assertSame($host, $subject->getHost());
- static::assertSame($port, $subject->getPort());
- static::assertSame($provider, $subject->getProvider());
+ ->setProvider($provider)
+ ->setDescription($description)
+ ->setUuid($uuid)
+ ->setVersion($version)
+ ->setBranch($branch)
+ ->setTag($tag)
+ ->setName($name)
+ ->setRepositoryUrl($repositoryUrl)
+ ->setConsumerVersion($consumerVersion)
+ ->setPactLocations($pactLocations);
+
+ static::assertSame($brokerUri, $subject->getBrokerUri());
+ static::assertSame($brokerToken, $subject->getBrokerToken());
+ static::assertSame($brokerUsername, $subject->getBrokerUsername());
+ static::assertSame($brokerPassword, $subject->getBrokerPassword());
+ static::assertSame($verbose, $subject->isVerbose());
+ static::assertSame($pacticipant, $subject->getPacticipant());
+ static::assertSame($request, $subject->getRequest());
+ static::assertSame($header, $subject->getHeader());
+ static::assertSame($data, $subject->getData());
+ static::assertSame($user, $subject->getUser());
+ static::assertSame($url, $subject->getUrl());
static::assertSame($consumer, $subject->getConsumer());
- static::assertSame($pactDir, $subject->getPactDir());
- static::assertSame($pactFileWriteMode, $subject->getPactFileWriteMode());
- static::assertSame($log, $subject->getLog());
- static::assertSame($pactSpecificationVersion, $subject->getPactSpecificationVersion());
- static::assertSame($cors, $subject->hasCors());
+ static::assertSame($provider, $subject->getProvider());
+ static::assertSame($description, $subject->getDescription());
+ static::assertSame($uuid, $subject->getUuid());
+ static::assertSame($version, $subject->getVersion());
+ static::assertSame($branch, $subject->getBranch());
+ static::assertSame($tag, $subject->getTag());
+ static::assertSame($name, $subject->getName());
+ static::assertSame($repositoryUrl, $subject->getRepositoryUrl());
+ static::assertSame($consumerVersion, $subject->getConsumerVersion());
+ static::assertSame($pactLocations, $subject->getPactLocations());
}
}
diff --git a/tests/PhpPact/Standalone/MockServer/MockServerConfigTest.php b/tests/PhpPact/Standalone/MockServer/MockServerConfigTest.php
index 60dcad2b..419461b5 100644
--- a/tests/PhpPact/Standalone/MockServer/MockServerConfigTest.php
+++ b/tests/PhpPact/Standalone/MockServer/MockServerConfigTest.php
@@ -16,8 +16,7 @@ public function testSetters()
$pactDir = 'test-pact-dir/';
$pactFileWriteMode = 'merge';
$log = 'test-log-dir/';
- $cors = true;
- $pactSpecificationVersion = 2.0;
+ $pactSpecificationVersion = '2.0.0';
$subject = (new MockServerConfig())
->setHost($host)
@@ -27,8 +26,7 @@ public function testSetters()
->setPactDir($pactDir)
->setPactFileWriteMode($pactFileWriteMode)
->setLog($log)
- ->setPactSpecificationVersion($pactSpecificationVersion)
- ->setCors($cors);
+ ->setPactSpecificationVersion($pactSpecificationVersion);
static::assertSame($host, $subject->getHost());
static::assertSame($port, $subject->getPort());
@@ -38,6 +36,5 @@ public function testSetters()
static::assertSame($pactFileWriteMode, $subject->getPactFileWriteMode());
static::assertSame($log, $subject->getLog());
static::assertSame($pactSpecificationVersion, $subject->getPactSpecificationVersion());
- static::assertSame($cors, $subject->hasCors());
}
}
diff --git a/tests/PhpPact/Standalone/MockServer/MockServerTest.php b/tests/PhpPact/Standalone/MockServer/MockServerTest.php
deleted file mode 100644
index 6d356742..00000000
--- a/tests/PhpPact/Standalone/MockServer/MockServerTest.php
+++ /dev/null
@@ -1,68 +0,0 @@
-start();
- $this->assertTrue(\is_int($pid));
- } finally {
- $result = $mockServer->stop();
- $this->assertTrue($result);
- }
- }
-
- /**
- * @throws MissingEnvVariableException
- * @throws \Exception
- */
- public function testStartAndStopWithRecognizedTimeout()
- {
- // the mock server actually takes more than one second to be ready
- // we use this fact to test the timeout
- $orig = \getenv('PACT_MOCK_SERVER_HEALTH_CHECK_TIMEOUT');
- \putenv('PACT_MOCK_SERVER_HEALTH_CHECK_TIMEOUT=1');
-
- $httpService = $this->getMockBuilder(MockServerHttpService::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $connectionException = $this->getMockBuilder(ConnectionException::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- // take sth lower than the default value
- $httpService->expects($this->atMost(5))
- ->method('healthCheck')
- ->will($this->returnCallback(function () use ($connectionException) {
- throw $connectionException;
- }));
-
- try {
- $mockServer = new MockServer(new MockServerEnvConfig(), $httpService);
- $mockServer->start();
- $this->fail('MockServer should not pass defined health check.');
- } catch (HealthCheckFailedException $e) {
- $this->assertTrue(true);
- } finally {
- $mockServer->stop();
- \putenv('PACT_MOCK_SERVER_HEALTH_CHECK_TIMEOUT=' . $orig);
- }
- }
-}
diff --git a/tests/PhpPact/Standalone/MockServer/Service/MockServerHttpServiceTest.php b/tests/PhpPact/Standalone/MockServer/Service/MockServerHttpServiceTest.php
deleted file mode 100644
index 9f831646..00000000
--- a/tests/PhpPact/Standalone/MockServer/Service/MockServerHttpServiceTest.php
+++ /dev/null
@@ -1,212 +0,0 @@
-config = new MockServerEnvConfig();
- $this->mockServer = new MockServer($this->config);
- $this->mockServer->start();
- $this->service = new MockServerHttpService(new GuzzleClient(), $this->config);
- }
-
- protected function tearDown(): void
- {
- $this->mockServer->stop();
- }
-
- /**
- * @throws ConnectionException
- */
- public function testHealthCheck()
- {
- $result = $this->service->healthCheck();
- $this->assertTrue($result);
- }
-
- public function testRegisterInteraction()
- {
- $request = new ConsumerRequest();
- $request
- ->setPath('/example')
- ->setMethod('GET');
- $response = new ProviderResponse();
- $response->setStatus(200);
-
- $interaction = new Interaction();
- $interaction
- ->setDescription('Fake description')
- ->setProviderState('Fake provider state')
- ->setRequest($request)
- ->setResponse($response);
-
- $result = $this->service->registerInteraction($interaction);
-
- $this->assertTrue($result);
- }
-
- public function testDeleteAllInteractions()
- {
- $result = $this->service->deleteAllInteractions();
- $this->assertTrue($result);
- }
-
- public function testVerifyInteractions()
- {
- $result = $this->service->verifyInteractions();
- $this->assertTrue($result);
- }
-
- public function testVerifyInteractionsFailure()
- {
- $request = new ConsumerRequest();
- $request
- ->setPath('/example')
- ->setMethod('GET');
-
- $response = new ProviderResponse();
- $response->setStatus(200);
-
- $interaction = new Interaction();
- $interaction
- ->setDescription('Some description')
- ->setProviderState('Some state')
- ->setRequest($request)
- ->setResponse($response);
- $this->service->registerInteraction($interaction);
-
- $this->expectException(ServerException::class);
- $result = $this->service->verifyInteractions();
- $this->assertFalse($result);
- }
-
- public function testGetPactJson()
- {
- $result = $this->service->getPactJson();
- $this->assertEquals('{"consumer":{"name":"someConsumer"},"provider":{"name":"someProvider"},"interactions":[],"metadata":{"pactSpecification":{"version":"2.0.0"}}}', $result);
- }
-
- public function testFullGetInteraction()
- {
- $request = new ConsumerRequest();
- $request
- ->setPath('/example')
- ->setMethod('GET')
- ->setQuery('enabled=true')
- ->addQueryParameter('order', 'asc')
- ->addQueryParameter('value', '12')
- ->addHeader('Content-Type', 'application/json');
-
- $expectedResponseBody = [
- 'message' => 'Hello, world!',
- ];
- $response = new ProviderResponse();
- $response
- ->setStatus(200)
- ->setBody($expectedResponseBody)
- ->addHeader('Content-Type', 'application/json');
-
- $interaction = new Interaction();
- $interaction
- ->setDescription('Fake description')
- ->setProviderState('Fake provider state')
- ->setRequest($request)
- ->setResponse($response);
-
- $result = $this->service->registerInteraction($interaction);
-
- $this->assertTrue($result);
-
- $client = new GuzzleClient();
- $uri = $this->config->getBaseUri()->withPath('/example')->withQuery('enabled=true&order=asc&value=12');
- $response = $client->get($uri, [
- 'headers' => [
- 'Content-Type' => 'application/json',
- ],
- ]);
-
- $body = $response->getBody()->getContents();
- $this->assertEquals(\json_encode($expectedResponseBody), $body);
- $this->assertEquals($response->getHeaderLine('Access-Control-Allow-Origin'), '*', 'CORS flag not set properly');
- $this->assertEquals(200, $response->getStatusCode());
- }
-
- /**
- * @throws MissingEnvVariableException
- * @throws \Exception
- */
- public function testMatcherWithMockServer()
- {
- $matcher = new Matcher();
-
- $category = new stdClass();
- $category->name = $matcher->term('Games', '[gbBG]');
-
- $request = new ConsumerRequest();
- $request
- ->setPath('/test')
- ->setMethod('GET');
-
- $response = new ProviderResponse();
- $response
- ->setStatus(200)
- ->addHeader('Content-Type', 'application/json')
- ->setBody([
- 'results' => $matcher->eachLike($category),
- ]);
-
- $config = new MockServerEnvConfig();
- $interaction = new InteractionBuilder($config);
- $interaction
- ->given('Something')
- ->uponReceiving('Stuff')
- ->with($request)
- ->willRespondWith($response);
-
- $client = new GuzzleClient();
- $uri = $this->config->getBaseUri()->withPath('/test');
- $client->get($uri, [
- 'headers' => [
- 'Content-Type' => 'application/json',
- ],
- ]);
-
- $httpClient = new MockServerHttpService(new GuzzleClient(), $config);
-
- $pact = \json_decode($httpClient->getPactJson(), true);
-
- $this->assertArrayHasKey('$.body.results[*].name', $pact['interactions'][0]['response']['matchingRules']);
- }
-}
diff --git a/tests/PhpPact/Standalone/PactConfigTest.php b/tests/PhpPact/Standalone/PactConfigTest.php
new file mode 100644
index 00000000..ba374d4c
--- /dev/null
+++ b/tests/PhpPact/Standalone/PactConfigTest.php
@@ -0,0 +1,59 @@
+config = new PactConfig();
+ }
+
+ public function testSetters(): void
+ {
+ $provider = 'test-provider';
+ $consumer = 'test-consumer';
+ $pactDir = 'test-pact-dir/';
+ $pactSpecificationVersion = '2.0.0';
+ $log = 'test-log-dir/';
+ $logLevel = 'ERROR';
+ $pactFileWriteMode = 'merge';
+
+ $this->config
+ ->setProvider($provider)
+ ->setConsumer($consumer)
+ ->setPactDir($pactDir)
+ ->setPactSpecificationVersion($pactSpecificationVersion)
+ ->setLog($log)
+ ->setLogLevel($logLevel)
+ ->setPactFileWriteMode($pactFileWriteMode);
+
+ static::assertSame($provider, $this->config->getProvider());
+ static::assertSame($consumer, $this->config->getConsumer());
+ static::assertSame($pactDir, $this->config->getPactDir());
+ static::assertSame($pactSpecificationVersion, $this->config->getPactSpecificationVersion());
+ static::assertSame($log, $this->config->getLog());
+ static::assertSame($logLevel, $this->config->getLogLevel());
+ static::assertSame($pactFileWriteMode, $this->config->getPactFileWriteMode());
+ }
+
+ public function testInvalidLogLevel(): void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('LogLevel TRACE not supported.');
+ $this->config->setLogLevel('TRACE');
+ }
+
+ public function testInvalidPactFileWriteMode(): void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage("Invalid PhpPact File Write Mode, value must be one of the following: overwrite, merge.");
+ $this->config->setPactFileWriteMode('APPEND');
+ }
+}
diff --git a/tests/PhpPact/Standalone/ProviderVerifier/Model/ArrayDataTest.php b/tests/PhpPact/Standalone/ProviderVerifier/Model/ArrayDataTest.php
new file mode 100644
index 00000000..734bade3
--- /dev/null
+++ b/tests/PhpPact/Standalone/ProviderVerifier/Model/ArrayDataTest.php
@@ -0,0 +1,21 @@
+assertSame(count($branches), $arrayData->getSize());
+ foreach ($branches as $index => $branch) {
+ $this->assertSame($branch, FFI::string($arrayData->getItems()[$index]));
+ }
+ }
+}
diff --git a/tests/PhpPact/Standalone/ProviderVerifier/Model/Source/BrokerTest.php b/tests/PhpPact/Standalone/ProviderVerifier/Model/Source/BrokerTest.php
new file mode 100644
index 00000000..23106df1
--- /dev/null
+++ b/tests/PhpPact/Standalone/ProviderVerifier/Model/Source/BrokerTest.php
@@ -0,0 +1,37 @@
+addSelector('{"tag":"foo","latest":true}')
+ ->addSelector('{"tag":"bar","latest":true}');
+ $consumerVersionTags = ['dev'];
+
+ $subject = (new Broker())
+ ->setEnablePending($enablePending)
+ ->setIncludeWipPactSince($wipPactSince)
+ ->setProviderTags($providerTags)
+ ->setProviderBranch($providerBranch)
+ ->setConsumerVersionSelectors($consumerVersionSelectors)
+ ->setConsumerVersionTags($consumerVersionTags);
+
+ static::assertSame($enablePending, $subject->isEnablePending());
+ static::assertSame($wipPactSince, $subject->getIncludeWipPactSince());
+ static::assertSame($providerTags, $subject->getProviderTags());
+ static::assertSame($providerBranch, $subject->getProviderBranch());
+ static::assertSame($consumerVersionSelectors, $subject->getConsumerVersionSelectors());
+ static::assertSame($consumerVersionTags, $subject->getConsumerVersionTags());
+ }
+}
diff --git a/tests/PhpPact/Standalone/ProviderVerifier/Model/Source/UrlTest.php b/tests/PhpPact/Standalone/ProviderVerifier/Model/Source/UrlTest.php
new file mode 100644
index 00000000..20716b8a
--- /dev/null
+++ b/tests/PhpPact/Standalone/ProviderVerifier/Model/Source/UrlTest.php
@@ -0,0 +1,29 @@
+setUrl($url)
+ ->setToken($token)
+ ->setUsername($username)
+ ->setPassword($password);
+
+ static::assertSame($url, $subject->getUrl());
+ static::assertSame($token, $subject->getToken());
+ static::assertSame($username, $subject->getUsername());
+ static::assertSame($password, $subject->getPassword());
+ }
+}
diff --git a/tests/PhpPact/Standalone/ProviderVerifier/Model/VerifierConfigTest.php b/tests/PhpPact/Standalone/ProviderVerifier/Model/VerifierConfigTest.php
new file mode 100644
index 00000000..01fc935b
--- /dev/null
+++ b/tests/PhpPact/Standalone/ProviderVerifier/Model/VerifierConfigTest.php
@@ -0,0 +1,80 @@
+setProviderName($providerName)
+ ->setScheme($scheme)
+ ->setHost($host)
+ ->setPort($port)
+ ->setBasePath($basePath)
+ // Filter Info
+ ->setFilterDescription($filterDescription)
+ ->setFilterNoState($filterNoState)
+ ->setFilterState($filterState)
+ // Provider State
+ ->setStateChangeUrl($stateChangeUrl)
+ ->setStateChangeAsBody($stateChangeAsBody)
+ ->setStateChangeTeardown($stateChangeTeardown)
+ // Verification Options
+ ->setRequestTimeout($requestTimeout)
+ ->setDisableSslVerification($disableSslVerification)
+ // Publish Options
+ ->setPublishResults($publishResults)
+ ->setProviderTags(...$providerTags)
+ ->setProviderVersion($providerVersion)
+ ->setBuildUrl($buildUrl)
+ ->setProviderBranch($providerBranch)
+ // Consumer Filters
+ ->setFilterConsumerNames(...$filterConsumerNames);
+
+ static::assertSame($providerName, $subject->getProviderName());
+ static::assertSame($scheme, $subject->getScheme());
+ static::assertSame($host, $subject->getHost());
+ static::assertSame($port, $subject->getPort());
+ static::assertSame($basePath, $subject->getBasePath());
+ static::assertSame($filterDescription, $subject->getFilterDescription());
+ static::assertSame($filterNoState, $subject->getFilterNoState());
+ static::assertSame($filterState, $subject->getFilterState());
+ static::assertSame($stateChangeUrl, $subject->getStateChangeUrl());
+ static::assertSame($stateChangeAsBody, $subject->isStateChangeAsBody());
+ static::assertSame($stateChangeTeardown, $subject->isStateChangeTeardown());
+ static::assertSame($requestTimeout, $subject->getRequestTimeout());
+ static::assertSame($disableSslVerification, $subject->isDisableSslVerification());
+ static::assertSame($publishResults, $subject->isPublishResults());
+ static::assertSame($providerTags, $subject->getProviderTags());
+ static::assertSame($providerVersion, $subject->getProviderVersion());
+ static::assertSame($buildUrl, $subject->getBuildUrl());
+ static::assertSame($providerBranch, $subject->getProviderBranch());
+ static::assertSame($filterConsumerNames, $subject->getFilterConsumerNames());
+ }
+}
diff --git a/tests/PhpPact/Standalone/ProviderVerifier/VerifierProcessTest.php b/tests/PhpPact/Standalone/ProviderVerifier/VerifierProcessTest.php
deleted file mode 100644
index 72449939..00000000
--- a/tests/PhpPact/Standalone/ProviderVerifier/VerifierProcessTest.php
+++ /dev/null
@@ -1,77 +0,0 @@
- 'bar'];
-
- $logger = $this->createMock(LoggerInterface::class);
-
- $processRunner = $this->createMock(ProcessRunner::class);
-
- $processRunnerFactory = $this->createMock(ProcessRunnerFactory::class);
- $processRunnerFactory->expects($this->once())
- ->method('createRunner')
- ->with($this->equalTo($arguments), $this->equalTo($logger))
- ->will($this->returnValue($processRunner));
-
- $process = new VerifierProcess($processRunnerFactory);
- $process->setLogger($logger);
- $process->run($arguments, 42, 23);
- }
-
- public function testRunWithDefaultLogger()
- {
- $arguments = ['foo' => 'bar'];
-
- $processRunner = $this->createMock(ProcessRunner::class);
-
- $processRunnerFactory = $this->createMock(ProcessRunnerFactory::class);
- $processRunnerFactory->expects($this->once())
- ->method('createRunner')
- ->with($this->equalTo($arguments))
- ->will($this->returnValue($processRunner));
-
- $process = new VerifierProcess($processRunnerFactory);
- $process->run($arguments, 42, 23);
- }
-
- public function testRunForwardsException()
- {
- $this->expectExceptionMessage('foo');
- $this->expectException(\RuntimeException::class);
-
- $arguments = ['foo' => 'bar'];
-
- $expectedException = new \RuntimeException('foo');
-
- $processRunner = $this->createMock(ProcessRunner::class);
- $processRunner->expects($this->once())
- ->method('runBlocking')
- ->will(
- $this->returnCallback(
- function () use ($expectedException) {
- throw $expectedException;
- }
- )
- );
-
- $processRunnerFactory = $this->createMock(ProcessRunnerFactory::class);
- $processRunnerFactory->expects($this->once())
- ->method('createRunner')
- ->with($this->equalTo($arguments))
- ->will($this->returnValue($processRunner));
-
- $process = new VerifierProcess($processRunnerFactory);
- $process->run($arguments, 42, 23);
- }
-}
diff --git a/tests/PhpPact/Standalone/ProviderVerifier/VerifierTest.php b/tests/PhpPact/Standalone/ProviderVerifier/VerifierTest.php
index 8a5d896a..c5de05bf 100644
--- a/tests/PhpPact/Standalone/ProviderVerifier/VerifierTest.php
+++ b/tests/PhpPact/Standalone/ProviderVerifier/VerifierTest.php
@@ -2,235 +2,49 @@
namespace PhpPactTest\Standalone\ProviderVerifier;
-use GuzzleHttp\Psr7\Uri;
-use Monolog\Handler\TestHandler;
-use Monolog\Logger;
-use PhpPact\Broker\Service\BrokerHttpClient;
-use PhpPact\Broker\Service\BrokerHttpClientInterface;
-use PhpPact\Standalone\ProviderVerifier\Model\ConsumerVersionSelectors;
use PhpPact\Standalone\ProviderVerifier\Model\VerifierConfig;
-use PhpPact\Standalone\ProviderVerifier\ProcessRunnerFactory;
use PhpPact\Standalone\ProviderVerifier\Verifier;
-use PhpPact\Standalone\ProviderVerifier\VerifierProcess;
use PHPUnit\Framework\TestCase;
-use Psr\Http\Message\RequestInterface;
+use Symfony\Component\Process\Process;
class VerifierTest extends TestCase
{
- public function testGetArguments()
- {
- $consumerVersionSelectors = (new ConsumerVersionSelectors())
- ->addSelector('{"tag":"foo","latest":true}')
- ->addSelector('{"tag":"bar","latest":true}');
-
- $config = new VerifierConfig();
- $config
- ->setProviderName('some provider with whitespace')
- ->setProviderVersion('1.0.0')
- ->setProviderBranch('main')
- ->addProviderVersionTag('prod')
- ->addProviderVersionTag('dev')
- ->addConsumerVersionTag('dev')
- ->setProviderBaseUrl(new Uri('http://myprovider:1234'))
- ->setProviderStatesSetupUrl(new Uri('http://someurl:1234'))
- ->setPublishResults(true)
- ->setBrokerToken('someToken')
- ->setBrokerUsername('someusername')
- ->setBrokerPassword('somepassword')
- ->setBrokerUri(new Uri('https://example.broker/'))
- ->addCustomProviderHeader('key1', 'value1')
- ->addCustomProviderHeader('key2', 'value2')
- ->setVerbose(true)
- ->setLogDirectory('my/log/directory')
- ->setFormat('someformat')
- ->setProcessTimeout(30)
- ->setProcessIdleTimeout(5)
- ->setEnablePending(true)
- ->setIncludeWipPactSince('2020-01-30')
- ->setRequestFilter(
- function (RequestInterface $r) {
- return $r->withHeader('MY_SPECIAL_HEADER', 'my special value');
- }
- )
- ->setConsumerVersionSelectors($consumerVersionSelectors);
-
- /** @var BrokerHttpClientInterface $brokerHttpService */
- $server = new Verifier($config);
- $arguments = $server->getArguments();
-
- $this->assertContains('--provider-base-url=http://myprovider:1234', $arguments);
- $this->assertContains('--provider-states-setup-url=http://someurl:1234', $arguments);
- $this->assertContains('--publish-verification-results', $arguments);
- $this->assertContains('--broker-token=someToken', $arguments);
- $this->assertContains('--broker-username=someusername', $arguments);
- $this->assertContains('--broker-password=somepassword', $arguments);
- $this->assertContains('--custom-provider-header="key1: value1"', $arguments);
- $this->assertContains('--custom-provider-header="key2: value2"', $arguments);
- $this->assertContains('--verbose=VERBOSE', $arguments);
- $this->assertContains('--log-dir=my/log/directory', $arguments);
- $this->assertContains('--format=someformat', $arguments);
- $this->assertContains('--provider-version-tag=prod', $arguments);
- $this->assertContains('--provider-version-tag=dev', $arguments);
- $this->assertContains('--provider-version-branch=main', $arguments);
- $this->assertContains('--consumer-version-tag=dev', $arguments);
- $this->assertSame(['process_timeout' => 30, 'process_idle_timeout' => 5], $server->getTimeoutValues());
- $this->assertContains('--enable-pending', $arguments);
- $this->assertContains('--include-wip-pacts-since=2020-01-30', $arguments);
- $this->assertContains('--consumer-version-selector=\'{"tag":"foo","latest":true}\'', $this->stripSpaces($arguments));
- $this->assertContains('--consumer-version-selector=\'{"tag":"bar","latest":true}\'', $this->stripSpaces($arguments));
- $this->assertContains('--provider=\'some provider with whitespace\'', $arguments);
- $this->assertContains('--pact-broker-base-url=https://example.broker/', $arguments);
- }
+ /** @var Process */
+ private Process $process;
/**
- * Strip spaces for Windows CMD
+ * Run the PHP build-in web server.
*/
- private function stripSpaces($arr)
- {
- $newArr = [];
- foreach ($arr as $str) {
- $newArr[] = str_ireplace(' ', '', $str);
- }
- return $newArr;
- }
-
- public function testGetArgumentsEmptyConfig()
+ protected function setUp(): void
{
- $this->assertEmpty((new Verifier(new VerifierConfig()))->getArguments());
- }
-
- /**
- * @dataProvider dataProviderForBrokerPathTest
- *
- * @param string $consumerName
- * @param string $providerName
- * @param null|string $tag
- * @param null|string $version
- * @param string $path
- */
- public function testBuildValidPathToPactBroker($consumerName, $providerName, $tag, $version, $path)
- {
- $expectedUrltoBroker = 'http://mock/' . $path;
-
- /** @var Uri $uriMock */
- $uriMock = $this->createMock(Uri::class);
- $uriMock->expects($this->once())
- ->method('withPath')
- ->with($path)
- ->willReturn($uriMock);
-
- $uriMock->expects($this->any())
- ->method('__toString')
- ->willReturn($expectedUrltoBroker);
-
- $verifierProcessMock = $this->createMock(VerifierProcess::class);
- $verifierProcessMock->expects($this->once())
- ->method('run')
- ->with(
- $this->callback(function ($args) use ($expectedUrltoBroker) {
- return \in_array($expectedUrltoBroker, $args);
- })
- );
-
- $config = new VerifierConfig();
- $config->setProviderName($providerName)
- ->setProviderBaseUrl(new Uri('http://myprovider:1234'))
- ->setProviderStatesSetupUrl(new Uri('http://someurl:1234'))
- ->setBrokerUri($uriMock)
- ->setVerbose(true);
+ $publicPath = __DIR__ . '/../../../_public/';
- $verifier = new Verifier($config, $verifierProcessMock);
+ $this->process = new Process(['php', '-S', 'localhost:7202', '-t', $publicPath]);
- $verifier->verify($consumerName, $tag, $version);
- }
-
- public function dataProviderForBrokerPathTest()
- {
- $consumerName = 'someProviderName';
- $providerName = 'someProviderName';
- $tag = '1.0.0';
- $version = '11111';
-
- return [
- [$consumerName, $providerName, null, $version, "/pacts/provider/$providerName/consumer/$consumerName/version/$version/"],
- [$consumerName, $providerName, $tag, null, "/pacts/provider/$providerName/consumer/$consumerName/latest/$tag/"],
- [$consumerName, $providerName, $tag, $version, "/pacts/provider/$providerName/consumer/$consumerName/latest/$tag/"],
- [$consumerName, $providerName, null, null, "/pacts/provider/$providerName/consumer/$consumerName/latest/"],
- ];
+ $this->process->start();
+ $this->process->waitUntil(fn () => is_resource(fsockopen('localhost', 7202)));
}
/**
- * @dataProvider provideDataForVerifyAll
- *
- * @param string $providerName
- * @param string $providerVersion
- * @param bool $forceLatest
- * @param mixed $expectedProviderVersion
+ * Stop the web server process once complete.
*/
- public function testIfDataForVerifyAllIsConvertedCorrectly($providerName, $providerVersion)
- {
- $expectedUrl1 = 'expectedUrl1';
- $expectedUrl2 = 'expectedUrl2';
- $expectedPactUrls = [$expectedUrl1, $expectedUrl2];
-
- $verifierProcessMock = $this->createMock(VerifierProcess::class);
- $verifierProcessMock->expects($this->once())
- ->method('run')
- ->with(
- $this->callback(function ($args) use ($expectedUrl1, $expectedUrl2) {
- return \in_array($expectedUrl1, $args) && \in_array($expectedUrl2, $args);
- })
- );
-
- $brokerHttpClient = $this->createMock(BrokerHttpClient::class);
-
- $brokerHttpClient->expects($this->once())
- ->method('getAllConsumerUrls')
- ->with($this->equalTo($providerName))
- ->willReturn($expectedPactUrls);
-
- $config = new VerifierConfig();
- $config->setProviderName($providerName);
- $config->setProviderVersion($providerVersion);
-
- $verifier = new Verifier($config, $verifierProcessMock, $brokerHttpClient);
- $verifier->verifyAll();
- }
-
- public function provideDataForVerifyAll()
+ protected function tearDown(): void
{
- return [
- ['someProvider', '1.0.0'],
- ['someProvider', '1.2.3'],
- ];
+ $this->process->stop();
}
- public function testRunShouldLogOutputIfCmdFails()
+ public function testVerify(): void
{
- if ('\\' !== \DIRECTORY_SEPARATOR) {
- $cmd = __DIR__ . \DIRECTORY_SEPARATOR . 'verifier.sh';
- } else {
- $cmd = 'cmd /c' . __DIR__ . \DIRECTORY_SEPARATOR . 'verifier.bat';
- }
-
- $process = new VerifierProcess(new ProcessRunnerFactory($cmd));
-
- $logger = new Logger('console', [$handler = new TestHandler()]);
- $process->setLogger($logger);
-
- try {
- $exception = null;
- $process->run([], 60, 10);
- } catch (\Exception $e) {
- $exception = $e;
- }
-
- $logMessages = $handler->getRecords();
+ $config = new VerifierConfig();
+ $config
+ ->setPort(7202)
+ ->setProviderName('someProvider')
+ ->setProviderVersion('1.0.0')
+ ->setProviderBranch('main');
- $this->assertGreaterThan(2, \count($logMessages));
- $this->assertStringContainsString('first line', $logMessages[\count($logMessages) - 2]['message']);
- $this->assertStringContainsString('second line', $logMessages[\count($logMessages) - 1]['message']);
+ $verifier = new Verifier($config);
+ $verifier->addDirectory(__DIR__ . '/../../../_resources');
- $this->assertNotNull($exception);
+ $this->assertTrue($verifier->verify());
}
}
diff --git a/tests/PhpPact/Standalone/ProviderVerifier/verifier.bat b/tests/PhpPact/Standalone/ProviderVerifier/verifier.bat
deleted file mode 100755
index 4cdede33..00000000
--- a/tests/PhpPact/Standalone/ProviderVerifier/verifier.bat
+++ /dev/null
@@ -1,8 +0,0 @@
-@ECHO OFF
-
-REM this script simulates a command (like pact-verifier) which prints several lines to stdout and stderr
-
-ECHO "first line"
-ECHO "second line" 1>&2
-
-exit 42
\ No newline at end of file
diff --git a/tests/PhpPact/Standalone/ProviderVerifier/verifier.sh b/tests/PhpPact/Standalone/ProviderVerifier/verifier.sh
deleted file mode 100755
index c196300a..00000000
--- a/tests/PhpPact/Standalone/ProviderVerifier/verifier.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/env bash
-
-# this script simulates a command (like pact-verifier) which prints several lines to stdout and stderr
-
-echoerr() { echo "$@" 1>&2; }
-
-echo "first line"
-echoerr "second line"
-
-exit 42
\ No newline at end of file
diff --git a/tests/PhpPact/Standalone/Runner/ProcessRunnerTest.php b/tests/PhpPact/Standalone/Runner/ProcessRunnerTest.php
deleted file mode 100644
index 7d7c50ca..00000000
--- a/tests/PhpPact/Standalone/Runner/ProcessRunnerTest.php
+++ /dev/null
@@ -1,74 +0,0 @@
-runBlocking();
- $exitCode = $p->getExitCode();
-
- $this->assertEquals($exitCode, 0, 'Expect the exit code to be 0');
- $this->assertStringContainsString($expectedOutput, $p->getOutput(), "Expect '{$expectedOutput}' to be in the output");
- $this->assertEquals(null, $p->getStderr(), 'Expect a null stderr');
-
- // try an app that does not exists
- if ('\\' !== \DIRECTORY_SEPARATOR) {
- $p = new ProcessRunner('failedApp', []);
- $expectedErr = 'failedApp';
- } else {
- $p = new ProcessRunner('cmd /c echo myError 1>&2 && exit 42', []);
- $expectedErr = 'myError';
- }
-
- try {
- $p->runBlocking();
- } catch (\Exception $e) {
- }
-
- $exitCode = $p->getExitCode();
-
- $this->assertNotEquals($exitCode, 0, 'Expect the exit code to be non-zero: ' . $exitCode);
- $this->assertStringContainsString($expectedErr, $p->getStderr(), "Expect '{$expectedErr}' to be in the stderr");
- $this->assertEquals(null, $p->getOutput(), 'Expect a null stdout');
- }
-
- /**
- * @throws \Exception
- */
- public function testProcessRunnerShouldReturnCompleteOutput()
- {
- if ('\\' !== \DIRECTORY_SEPARATOR) {
- $cmd = __DIR__ . \DIRECTORY_SEPARATOR . 'verifier.sh';
- } else {
- $cmd = 'cmd /c' . __DIR__ . \DIRECTORY_SEPARATOR . 'verifier.bat';
- }
-
- $p = new ProcessRunner($cmd, []);
- $expectedOutput = 'third line';
- $expectedErr = 'fourth line';
-
- try {
- $p->runBlocking();
- } catch (\Exception $e) {
- }
-
- $this->assertTrue((\stripos($p->getOutput(), $expectedOutput) !== false), "Expect '{$expectedOutput}' to be in the output:");
- $this->assertTrue((\stripos($p->getStderr(), $expectedErr) !== false), "Expect '{$expectedErr}' to be in the stderr");
- }
-}
diff --git a/tests/PhpPact/Standalone/Runner/verifier.bat b/tests/PhpPact/Standalone/Runner/verifier.bat
deleted file mode 100755
index 3639d40f..00000000
--- a/tests/PhpPact/Standalone/Runner/verifier.bat
+++ /dev/null
@@ -1,11 +0,0 @@
-@ECHO OFF
-
-REM this script simulates a command (like pact-verifier) which prints several lines to stdout and stderr
-
-ECHO "first line"
-ECHO "second line" 1>&2
-ECHO "third line"
-ECHO "fourth line" 1>&2
-ECHO "fifth line"
-
-exit 42
\ No newline at end of file
diff --git a/tests/PhpPact/Standalone/Runner/verifier.sh b/tests/PhpPact/Standalone/Runner/verifier.sh
deleted file mode 100755
index 8290c308..00000000
--- a/tests/PhpPact/Standalone/Runner/verifier.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env bash
-
-# this script simulates a command (like pact-verifier) which prints several lines to stdout and stderr
-
-echoerr() { echo "$@" 1>&2; }
-
-echo "first line"
-echoerr "second line"
-echo "third line"
-echoerr "fourth line"
-echo "fifth line"
-
-exit 42
\ No newline at end of file
diff --git a/tests/PhpPact/Standalone/StubServer/Service/StubServerHttpServiceTest.php b/tests/PhpPact/Standalone/StubServer/Service/StubServerHttpServiceTest.php
index bc580efa..0bd0dd9c 100644
--- a/tests/PhpPact/Standalone/StubServer/Service/StubServerHttpServiceTest.php
+++ b/tests/PhpPact/Standalone/StubServer/Service/StubServerHttpServiceTest.php
@@ -14,13 +14,13 @@
class StubServerHttpServiceTest extends TestCase
{
/** @var StubServerHttpServiceInterface */
- private $service;
+ private StubServerHttpServiceInterface $service;
/** @var StubServer */
- private $stubServer;
+ private StubServer $stubServer;
/** @var StubServerConfigInterface */
- private $config;
+ private StubServerConfigInterface $config;
/**
* @throws MissingEnvVariableException
@@ -29,18 +29,16 @@ class StubServerHttpServiceTest extends TestCase
protected function setUp(): void
{
$pactLocation = __DIR__ . '/../../../../_resources/someconsumer-someprovider.json';
- $host = 'localhost';
$port = 7201;
$endpoint = 'test';
$this->config = (new StubServerConfig())
- ->setPactLocation($pactLocation)
- ->setHost($host)
+ ->setFiles($pactLocation)
->setPort($port)
->setEndpoint($endpoint);
$this->stubServer = new StubServer($this->config);
- $this->stubServer->start(10);
+ $this->stubServer->start();
$this->service = new StubServerHttpService(new GuzzleClient(), $this->config);
}
diff --git a/tests/PhpPact/Standalone/StubServer/StubServerConfigTest.php b/tests/PhpPact/Standalone/StubServer/StubServerConfigTest.php
index db9aed12..8e7cea03 100644
--- a/tests/PhpPact/Standalone/StubServer/StubServerConfigTest.php
+++ b/tests/PhpPact/Standalone/StubServer/StubServerConfigTest.php
@@ -2,6 +2,7 @@
namespace PhpPactTest\Standalone\StubServer;
+use GuzzleHttp\Psr7\Uri;
use PhpPact\Standalone\StubService\StubServerConfig;
use PHPUnit\Framework\TestCase;
@@ -9,20 +10,59 @@ class StubServerConfigTest extends TestCase
{
public function testSetters()
{
- $pactLocation = __DIR__ . '/../../../_resources/someconsumer-someprovider.json';
- $host = 'test-host';
- $port = 1234;
- $log = 'test-log-dir/';
+ $brokerUrl = new Uri('http://localhost');
+ $port = 1234;
+ $extension = 'json';
+ $logLevel = 'debug';
+ $providerState = 'state';
+ $providerStateHeaderName = 'header';
+ $token = 'token';
+ $user = 'user:password';
+ $pactLocation = __DIR__ . '/../../../_resources';
+ $files = ['/path/to/pact.json'];
+ $urls = ['http://example.com/path/to/file.json'];
+ $consumerNames = ['consumer-1', 'consumer-2'];
+ $providerNames = ['provider-1', 'provider-2'];
+ $cors = true;
+ $corsReferer = true;
+ $emptyProviderState = true;
+ $insecureTls = true;
$subject = (new StubServerConfig())
- ->setPactLocation($pactLocation)
- ->setHost($host)
+ ->setBrokerUrl($brokerUrl)
->setPort($port)
- ->setLog($log);
+ ->setExtension($extension)
+ ->setLogLevel($logLevel)
+ ->setProviderState($providerState)
+ ->setProviderStateHeaderName($providerStateHeaderName)
+ ->setToken($token)
+ ->setUser($user)
+ ->setDirs($pactLocation)
+ ->setFiles(...$files)
+ ->setUrls(...$urls)
+ ->setConsumerNames(...$consumerNames)
+ ->setProviderNames(...$providerNames)
+ ->setCors($cors)
+ ->setCorsReferer($corsReferer)
+ ->setEmptyProviderState($emptyProviderState)
+ ->setInsecureTls($insecureTls);
- static::assertSame($pactLocation, $subject->getPactLocation());
- static::assertSame($host, $subject->getHost());
+ static::assertSame($brokerUrl, $subject->getBrokerUrl());
static::assertSame($port, $subject->getPort());
- static::assertSame($log, $subject->getLog());
+ static::assertSame($extension, $subject->getExtension());
+ static::assertSame($logLevel, $subject->getLogLevel());
+ static::assertSame($providerState, $subject->getProviderState());
+ static::assertSame($providerStateHeaderName, $subject->getProviderStateHeaderName());
+ static::assertSame($token, $subject->getToken());
+ static::assertSame($user, $subject->getUser());
+ static::assertSame([$pactLocation], $subject->getDirs());
+ static::assertSame($files, $subject->getFiles());
+ static::assertSame($urls, $subject->getUrls());
+ static::assertSame($consumerNames, $subject->getConsumerNames());
+ static::assertSame($providerNames, $subject->getProviderNames());
+ static::assertSame($cors, $subject->isCors());
+ static::assertSame($corsReferer, $subject->isCorsReferer());
+ static::assertSame($emptyProviderState, $subject->isEmptyProviderState());
+ static::assertSame($insecureTls, $subject->isInsecureTls());
}
}
diff --git a/tests/PhpPact/Standalone/StubServer/StubServerTest.php b/tests/PhpPact/Standalone/StubServer/StubServerTest.php
index 963fabf8..3fb609fa 100644
--- a/tests/PhpPact/Standalone/StubServer/StubServerTest.php
+++ b/tests/PhpPact/Standalone/StubServer/StubServerTest.php
@@ -15,15 +15,13 @@ public function testStartAndStop()
{
try {
$pactLocation = __DIR__ . '/../../../_resources/someconsumer-someprovider.json';
- $host = 'localhost';
$port = 7201;
$endpoint = 'test';
$subject = (new StubServerConfig())
- ->setPactLocation($pactLocation)
- ->setHost($host)
+ ->setFiles($pactLocation)
->setPort($port)
- ->setEndpoint($endpoint);
+ ->setEndpoint($endpoint);
$stubServer = new StubServer($subject);
$pid = $stubServer->start();
diff --git a/tests/_public/index.php b/tests/_public/index.php
new file mode 100644
index 00000000..cc71ddcc
--- /dev/null
+++ b/tests/_public/index.php
@@ -0,0 +1,10 @@
+ [
+ [
+ 'name' => 'g',
+ ],
+ ],
+]);