diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 5a9e172f..427531c8 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -27,6 +27,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
+ coverage: none
- uses: ramsey/composer-install@v2
with:
@@ -48,6 +49,7 @@ jobs:
operating-system: [ ubuntu-latest, macos-latest, windows-latest ]
php: [ '8.0', '8.1', '8.2' ]
dependencies: [ 'lowest', 'locked' ]
+ timeout-minutes: 5
name: PHP ${{ matrix.php }} on ${{ matrix.operating-system }} with ${{ matrix.dependencies }} dependencies
@@ -58,8 +60,9 @@ 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 }}
+ coverage: none
- name: Composer install
uses: ramsey/composer-install@v2
diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php
index cac6eabb..cf318188 100644
--- a/.php-cs-fixer.php
+++ b/.php-cs-fixer.php
@@ -7,7 +7,8 @@
->name('*.php');
$config = new PhpCsFixer\Config();
-$config->setRules(['@PSR12' => true,
+$config->setRules([
+ '@PSR12' => true,
'strict_param' => false,
'array_syntax' => ['syntax' => 'short'],
])
diff --git a/README.md b/README.md
index f7230c5f..5df03992 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ Table of contents
- [Specifications](#specifications)
- [Installation](#installation)
- [Basic Consumer Usage](#basic-consumer-usage)
- - [Start and Stop the Mock Server](#start-and-stop-the-mock-server)
+ - [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)
@@ -30,7 +30,7 @@ Table of contents
- [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 in Directory](#verify-files-in-directory)
- [Verify Files by Path](#verify-files-by-path)
- [Tips](#tips)
- [Starting API Asynchronously](#starting-api-asynchronously)
@@ -42,7 +42,7 @@ Table of contents
- [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 updates internal dependencies and libraries + 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
@@ -59,7 +59,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
@@ -75,39 +81,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.
@@ -193,10 +172,10 @@ Now that we have the request and response, we need to build the interaction and
$config = new MockServerEnvConfig();
$builder = new InteractionBuilder($config);
$builder
- ->given('a person exists')
+ ->given('a person exists', ['name' => 'Bob'])
->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
@@ -212,7 +191,8 @@ 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();
+$verifyResult = $verifier->verify();
+$this->assertTrue($verifyResult);
```
### Make Assertions
@@ -250,51 +230,62 @@ $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);
+
+$verifyResult = $verifier->verify();
+
+$this->assertTrue($verifyResult);
```
-##### 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');
+
+ $verifyResult = $verifier->verify();
+
+ $this->assertTrue($verifyResult);
}
```
@@ -303,25 +294,13 @@ 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');
+
+ $verifyResult = $verifier->verify();
+
+ $this->assertTrue($verifyResult);
}
```
@@ -353,7 +332,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
@@ -393,68 +371,41 @@ $consumerMessage = new ExampleMessageConsumer();
$callback = [$consumerMessage, 'ProcessSong'];
$builder->setCallback($callback);
-$builder->verify();
+$verifyResult = $builder->verify();
+
+$this->assertTrue($verifyResult);
```
### 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.
-
-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. 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
+Handle these requests on your provider:
- $callbacks = array();
+1. POST /pact-change-state
+ 1. Set up your database to meet the expectations of the request
+ 2. Reset the database to its original state.
+2. POST /pact-messages
+ 1. Return message's content in body
+ 2. Return message's metadata in header `PACT-MESSAGE-METADATA`
- // 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/index.php) to see the full sample file.
## Usage for the optional `pact-stub-service`
If you would like to test with fixtures, you can use the `pact-stub-service` like this:
```php
-$pactLocation = __DIR__ . '/someconsumer-someprovider.json';
-$host = 'localhost';
-$port = 7201;
-$endpoint = 'test';
+$files = [__DIR__ . '/someconsumer-someprovider.json'];
+$port = 7201;
+$endpoint = 'test';
$config = (new StubServerConfig())
- ->setPactLocation($pactLocation)
- ->setHost($host)
- ->setPort($port)
- ->setEndpoint($endpoint);
+ ->setFiles($files)
+ ->setPort($port);
$stubServer = new StubServer($config);
$stubServer->start();
$service = new StubServerHttpService(new GuzzleClient(), $config);
-echo $service->getJson(); // output: {"results":[{"name":"Games"}]}
+echo $service->getJson($endpoint); // output: {"results":[{"name":"Games"}]}
```
diff --git a/UPGRADE-9.0.md b/UPGRADE-9.0.md
new file mode 100644
index 00000000..323b034a
--- /dev/null
+++ b/UPGRADE-9.0.md
@@ -0,0 +1,59 @@
+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);
+
+ $verifyResult = $verifier->verify();
+
+ $this->assertTrue($verifyResult);
+ ```
+
+* Stub Server
+ * Endpoint now can be set by:
+ ```php
+ $service = new StubServerHttpService(new GuzzleClient(), $this->config);
+ $service->getJson($endpoint);
+ ```
diff --git a/composer.json b/composer.json
index 2404f9fa..68129809 100644
--- a/composer.json
+++ b/composer.json
@@ -24,23 +24,14 @@
"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",
"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,14 +57,17 @@
"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=7",
- "lint": "php-cs-fixer fix --config .php-cs-fixer.php --dry-run",
- "fix": "php-cs-fixer fix --config .php-cs-fixer.php",
+ "static-code-analysis": "phpstan",
+ "lint": "php-cs-fixer fix --dry-run",
+ "fix": "php-cs-fixer fix",
"test": "phpunit --debug -c example/phpunit.all.xml"
},
"extra": {
@@ -83,10 +77,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.4",
+ "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.4",
+ "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.3",
+ "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/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..a8f8c60e 100644
--- a/example/src/MessageProvider/ExampleMessageProvider.php
+++ b/example/src/MessageProvider/ExampleMessageProvider.php
@@ -4,32 +4,20 @@
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;
}
- /**
- * @return array
- */
public function getMetadata(): array
{
return $this->metadata;
}
- /**
- * @param array $metadata
- *
- * @return ExampleMessageProvider
- */
public function setMetadata(array $metadata): self
{
$this->metadata = $metadata;
@@ -37,20 +25,12 @@ public function setMetadata(array $metadata): self
return $this;
}
- /**
- * @return mixed
- */
- public function getContents()
+ public function getContents(): mixed
{
return $this->contents;
}
- /**
- * @param mixed $contents
- *
- * @return ExampleMessageProvider
- */
- public function setContents($contents)
+ public function setContents(mixed $contents): self
{
$this->contents = $contents;
@@ -59,10 +39,8 @@ public function setContents($contents)
/**
* Build metadata and content for message
- *
- * @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..c7fddb72
--- /dev/null
+++ b/example/src/Provider/ExampleProvider.php
@@ -0,0 +1,66 @@
+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',
+ ]
+ ],
+ ];
+ }
+
+ public function sayHello(string $name): string
+ {
+ return "Hello, {$name}";
+ }
+
+ public function sayGoodbye(string $name): string
+ {
+ return "Goodbye, {$name}";
+ }
+
+ 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']);
+ }
+
+ 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..48e8a188 100644
--- a/example/src/Provider/public/index.php
+++ b/example/src/Provider/public/index.php
@@ -1,5 +1,6 @@
addBodyParsingMiddleware();
-$app->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');
});
+$app->post('/pact-messages', 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('/pact-change-state', function (Request $request, Response $response) use ($provider) {
+ $body = $request->getParsedBody();
+ $provider->changeSate($body['action'], $body['state'], $body['params']);
+
+ return $response;
+});
+
$app->run();
diff --git a/example/tests/Consumer/Service/ConsumerServiceGoodbyeTest.php b/example/tests/Consumer/Service/ConsumerServiceGoodbyeTest.php
index e5106f55..b3140154 100644
--- a/example/tests/Consumer/Service/ConsumerServiceGoodbyeTest.php
+++ b/example/tests/Consumer/Service/ConsumerServiceGoodbyeTest.php
@@ -39,10 +39,10 @@ public function testGetGoodbyeString()
->willRespondWith($response);
$service = new HttpClientService($config->getBaseUri());
- $result = $service->getGoodbyeString('Bob');
+ $goodbyeResult = $service->getGoodbyeString('Bob');
+ $verifyResult = $builder->verify();
- $builder->verify();
-
- $this->assertEquals('Goodbye, Bob', $result);
+ $this->assertTrue($verifyResult);
+ $this->assertEquals('Goodbye, Bob', $goodbyeResult);
}
}
diff --git a/example/tests/Consumer/Service/ConsumerServiceHelloTest.php b/example/tests/Consumer/Service/ConsumerServiceHelloTest.php
index 2a80f394..d10782ff 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,13 +42,13 @@ public function testGetHelloString()
$builder
->uponReceiving('A get request to /hello/{name}')
->with($request)
- ->willRespondWith($response); // This has to be last. This is what makes an API request to the Mock Server to set the interaction.
+ ->willRespondWith($response); // This has to be last. This is what makes 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.
+ $helloResult = $service->getHelloString('Bob'); // Make the real API request against the Mock Server.
+ $verifyResult = $builder->verify(); // This will verify that the interactions took place.
- $builder->verify(); // This will verify that the interactions took place.
-
- $this->assertEquals('Hello, Bob', $result); // Make your assertions.
+ $this->assertTrue($verifyResult); // Make your assertions.
+ $this->assertEquals('Hello, Bob', $helloResult);
}
}
diff --git a/example/tests/MessageConsumer/ExampleMessageConsumerTest.php b/example/tests/MessageConsumer/ExampleMessageConsumerTest.php
index 6c17c6ce..0056d35d 100644
--- a/example/tests/MessageConsumer/ExampleMessageConsumerTest.php
+++ b/example/tests/MessageConsumer/ExampleMessageConsumerTest.php
@@ -2,11 +2,9 @@
namespace MessageConsumer;
-require_once __DIR__ . '/../../src/MessageConsumer/ExampleMessageConsumer.php';
-
use Exception;
use PhpPact\Consumer\MessageBuilder;
-use PhpPact\Standalone\PactConfigInterface;
+use PhpPact\Config\PactConfigInterface;
use PhpPact\Standalone\PactMessage\PactMessageConfig;
use PHPUnit\Framework\TestCase;
use stdClass;
@@ -28,18 +26,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
*/
@@ -53,7 +39,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);
@@ -63,11 +49,9 @@ public function testProcessText()
$callback = [$consumerMessage, 'ProcessText'];
$builder->setCallback($callback);
- $hasException = false;
-
- $builder->verify();
+ $verifyResult = $builder->verify();
- $this->assertTrue(true, 'Expects to reach this true statement by running verify()');
+ $this->assertTrue($verifyResult);
}
/**
@@ -93,14 +77,8 @@ public function testProcessSong()
$callback = [$consumerMessage, 'ProcessSong'];
$builder->setCallback($callback);
- $hasException = false;
-
- try {
- $builder->verify();
- } catch (Exception $e) {
- $hasException = true;
- }
+ $verifyResult = $builder->verify();
- $this->assertFalse($hasException, 'Expects verification to pass without exceptions being thrown');
+ $this->assertTrue($verifyResult);
}
}
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..e09a09d7 100644
--- a/example/tests/Provider/PactVerifyTest.php
+++ b/example/tests/Provider/PactVerifyTest.php
@@ -3,6 +3,7 @@
namespace Provider;
use GuzzleHttp\Psr7\Uri;
+use PhpPact\Standalone\ProviderVerifier\Model\Config\ProviderTransport;
use PhpPact\Standalone\ProviderVerifier\Model\VerifierConfig;
use PhpPact\Standalone\ProviderVerifier\Verifier;
use PhpPact\Standalone\Runner\ProcessRunner;
@@ -15,7 +16,7 @@
class PactVerifyTest extends TestCase
{
/** @var ProcessRunner */
- private $processRunner;
+ private ProcessRunner $processRunner;
/**
* Run the PHP build-in web server.
@@ -27,6 +28,7 @@ protected function setUp(): void
$this->processRunner = new ProcessRunner('php', ['-S', 'localhost:7202', '-t', $publicPath]);
$this->processRunner->run();
+ \usleep(300000); // wait for server to start
}
/**
@@ -43,18 +45,31 @@ protected function tearDown(): void
public function testPactVerifyConsumer()
{
$config = new VerifierConfig();
- $config
- ->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.
- ; // Flag the verifier service to publish the results to the Pact Broker.
+ $config->getProviderInfo()
+ ->setName('someProvider') // Providers name to fetch.
+ ->setHost('localhost')
+ ->setPort(7202);
+ $config->getProviderState()
+ ->setStateChangeUrl(new Uri('http://localhost:7202/pact-change-state'))
+ ;
+ $config->addProviderTransport(
+ (new ProviderTransport())
+ ->setProtocol(ProviderTransport::MESSAGE_PROTOCOL)
+ ->setPort(7202)
+ ->setPath('/pact-messages')
+ ->setScheme('http')
+ );
+ if ($level = \getenv('PACT_LOGLEVEL')) {
+ $config->setLogLevel($level);
+ }
// 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.');
+ $verifyResult = $verifier->verify();
+
+ $this->assertTrue($verifyResult);
}
}
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 00000000..1d5823a9
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,4 @@
+parameters:
+ level: 7
+ paths:
+ - src
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 53be68e7..00000000
--- a/src/PhpPact/Broker/Service/BrokerHttpClient.php
+++ /dev/null
@@ -1,109 +0,0 @@
-
- */
- private array $headers;
-
- /**
- * {@inheritdoc}
- */
- public function __construct(ClientInterface $httpClient, UriInterface $baseUri, array $headers = [])
- {
- $this->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): void
- {
- $array = \json_decode($json, true, 512, JSON_THROW_ON_ERROR);
- $consumer = $array['consumer']['name'];
- $provider = $array['provider']['name'];
-
- $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): void
- {
- $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, 512, JSON_THROW_ON_ERROR);
-
- $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, 512, JSON_THROW_ON_ERROR);
-
- $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 e4baa16a..00000000
--- a/src/PhpPact/Broker/Service/BrokerHttpClientInterface.php
+++ /dev/null
@@ -1,49 +0,0 @@
- $headers additional headers
- */
- public function __construct(ClientInterface $client, UriInterface $baseUri, array $headers);
-
- /**
- * Publish JSON.
- *
- * @param string $version Consumer version
- * @param string $json PACT File JSON
- */
- public function publishJson(string $version, string $json): void;
-
- /**
- * Tag a consumer version with a tag.
- */
- public function tag(string $consumer, string $version, string $tag): void;
-
- /**
- * Get all Pact urls for the consumer.
- *
- * @param string $provider provider name
- * @param string $version version of the provider
- *
- * @return array
- */
- public function getAllConsumerUrls(string $provider, string $version = 'latest'): array;
-
- /**
- * Get all Pact URLs for a specific tag.
- *
- * @return array
- */
- public function getAllConsumerUrlsForTag(string $provider, string $tag): array;
-}
diff --git a/src/PhpPact/Config/LogLevelTrait.php b/src/PhpPact/Config/LogLevelTrait.php
new file mode 100644
index 00000000..de5265ce
--- /dev/null
+++ b/src/PhpPact/Config/LogLevelTrait.php
@@ -0,0 +1,24 @@
+logLevel;
+ }
+
+ public function setLogLevel(string $logLevel): self
+ {
+ $logLevel = \strtoupper($logLevel);
+ if (!\in_array($logLevel, ['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'])) {
+ throw new \InvalidArgumentException('LogLevel ' . $logLevel . ' not supported.');
+ }
+ $this->logLevel = $logLevel;
+
+ return $this;
+ }
+}
diff --git a/src/PhpPact/Config/PactConfig.php b/src/PhpPact/Config/PactConfig.php
new file mode 100644
index 00000000..833f91fb
--- /dev/null
+++ b/src/PhpPact/Config/PactConfig.php
@@ -0,0 +1,179 @@
+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}
+ *
+ * @throws \UnexpectedValueException
+ */
+ public function setPactSpecificationVersion(string $pactSpecificationVersion): self
+ {
+ /*
+ * 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(): ?string
+ {
+ return $this->log;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setLog(string $log): self
+ {
+ $this->log = $log;
+
+ 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/Config/PactConfigInterface.php
similarity index 65%
rename from src/PhpPact/Standalone/PactConfigInterface.php
rename to src/PhpPact/Config/PactConfigInterface.php
index 63b6b818..0f97bd11 100644
--- a/src/PhpPact/Standalone/PactConfigInterface.php
+++ b/src/PhpPact/Config/PactConfigInterface.php
@@ -1,12 +1,19 @@
message = new Message();
+ }
+
+ /**
+ * @param string $name what is given to the request
+ * @param array $params for that request
+ * @param bool $overwrite clear pass states completely and start this array
+ */
+ public function given(string $name, array $params = [], bool $overwrite = false): self
+ {
+ $this->message->setProviderState($name, $params, $overwrite);
+
+ return $this;
+ }
+
+ /**
+ * @param string $description what is received when the request is made
+ */
+ public function expectsToReceive(string $description): self
+ {
+ $this->message->setDescription($description);
+
+ return $this;
+ }
+
+ /**
+ * @param array $metadata what is the additional metadata of the message
+ */
+ public function withMetadata(array $metadata): self
+ {
+ $this->message->setMetadata($metadata);
+
+ return $this;
+ }
+
+ /**
+ * Make the http request to the Mock Service to register the message. Content is required.
+ *
+ * @param mixed $contents required to be in the message
+ */
+ public function withContent(mixed $contents): self
+ {
+ $this->message->setContents($contents);
+
+ return $this;
+ }
+}
diff --git a/src/PhpPact/Consumer/BuilderInterface.php b/src/PhpPact/Consumer/BuilderInterface.php
index 5fc039dc..58ef8eda 100644
--- a/src/PhpPact/Consumer/BuilderInterface.php
+++ b/src/PhpPact/Consumer/BuilderInterface.php
@@ -11,9 +11,4 @@ interface BuilderInterface
* Verify that the interactions are valid.
*/
public function verify(): bool;
-
- /**
- * Write the Pact without deleting the interactions.
- */
- public function writePact(): bool;
}
diff --git a/src/PhpPact/Consumer/Driver/Interaction/InteractionDriver.php b/src/PhpPact/Consumer/Driver/Interaction/InteractionDriver.php
new file mode 100644
index 00000000..d9fbde15
--- /dev/null
+++ b/src/PhpPact/Consumer/Driver/Interaction/InteractionDriver.php
@@ -0,0 +1,32 @@
+mockServer->verify();
+ }
+
+ public function registerInteraction(Interaction $interaction): bool
+ {
+ $this->pactDriver->setUp();
+ $this->interactionRegistry->registerInteraction($interaction);
+ $this->mockServer->start();
+
+ return true;
+ }
+}
diff --git a/src/PhpPact/Consumer/Driver/Interaction/InteractionDriverInterface.php b/src/PhpPact/Consumer/Driver/Interaction/InteractionDriverInterface.php
new file mode 100644
index 00000000..fe5a3e07
--- /dev/null
+++ b/src/PhpPact/Consumer/Driver/Interaction/InteractionDriverInterface.php
@@ -0,0 +1,12 @@
+client->call('pactffi_message_reify', $this->messageRegistry->getId());
+ }
+
+ public function writePactAndCleanUp(): bool
+ {
+ $this->pactDriver->writePact();
+ $this->pactDriver->cleanUp();
+
+ return true;
+ }
+
+ public function registerMessage(Message $message): void
+ {
+ $this->pactDriver->setUp();
+ $this->messageRegistry->registerMessage($message);
+ }
+}
diff --git a/src/PhpPact/Consumer/Driver/Interaction/MessageDriverInterface.php b/src/PhpPact/Consumer/Driver/Interaction/MessageDriverInterface.php
new file mode 100644
index 00000000..be9a3089
--- /dev/null
+++ b/src/PhpPact/Consumer/Driver/Interaction/MessageDriverInterface.php
@@ -0,0 +1,14 @@
+pactRegistry->deletePact();
+ }
+
+ public function writePact(): void
+ {
+ $error = $this->client->call(
+ 'pactffi_pact_handle_write_file',
+ $this->pactRegistry->getId(),
+ $this->config->getPactDir(),
+ $this->config->getPactFileWriteMode() === PactConfigInterface::MODE_OVERWRITE
+ );
+ if ($error) {
+ throw new PactFileNotWroteException($error);
+ }
+ }
+
+ public function setUp(): void
+ {
+ $this
+ ->initWithLogLevel()
+ ->registerPact();
+ }
+
+ protected function getSpecification(): int
+ {
+ return match (true) {
+ $this->versionEqualTo('1.0.0') => $this->client->get('PactSpecification_V1'),
+ $this->versionEqualTo('1.1.0') => $this->client->get('PactSpecification_V1_1'),
+ $this->versionEqualTo('2.0.0') => $this->client->get('PactSpecification_V2'),
+ $this->versionEqualTo('3.0.0') => $this->client->get('PactSpecification_V3'),
+ $this->versionEqualTo('4.0.0') => $this->client->get('PactSpecification_V4'),
+ default => function () {
+ trigger_error(sprintf("Specification version '%s' is unknown", $this->config->getPactSpecificationVersion()), E_USER_WARNING);
+
+ return $this->client->get('PactSpecification_Unknown');
+ },
+ };
+ }
+
+ private function versionEqualTo(string $version): bool
+ {
+ return Comparator::equalTo($this->config->getPactSpecificationVersion(), $version);
+ }
+
+ private function initWithLogLevel(): self
+ {
+ $logLevel = $this->config->getLogLevel();
+ if ($logLevel) {
+ $this->client->call('pactffi_init_with_log_level', $logLevel);
+ }
+
+ return $this;
+ }
+
+ private function registerPact(): self
+ {
+ $this->pactRegistry->registerPact(
+ $this->config->getConsumer(),
+ $this->config->getProvider(),
+ $this->getSpecification()
+ );
+
+ return $this;
+ }
+}
diff --git a/src/PhpPact/Consumer/Driver/Pact/PactDriverInterface.php b/src/PhpPact/Consumer/Driver/Pact/PactDriverInterface.php
new file mode 100644
index 00000000..69ccdc42
--- /dev/null
+++ b/src/PhpPact/Consumer/Driver/Pact/PactDriverInterface.php
@@ -0,0 +1,12 @@
+ '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/MockServerNotWrotePactFileException.php b/src/PhpPact/Consumer/Exception/MockServerNotWrotePactFileException.php
new file mode 100644
index 00000000..01209897
--- /dev/null
+++ b/src/PhpPact/Consumer/Exception/MockServerNotWrotePactFileException.php
@@ -0,0 +1,19 @@
+ 'A general panic was caught',
+ 2 => 'The pact file was not able to be written',
+ 3 => 'A mock server with the provided port was not found',
+ 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..e55a5e09
--- /dev/null
+++ b/src/PhpPact/Consumer/Exception/PactFileNotWroteException.php
@@ -0,0 +1,19 @@
+ '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/Exception/PactNotRegisteredException.php b/src/PhpPact/Consumer/Exception/PactNotRegisteredException.php
new file mode 100644
index 00000000..8af9a00b
--- /dev/null
+++ b/src/PhpPact/Consumer/Exception/PactNotRegisteredException.php
@@ -0,0 +1,9 @@
+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';
- }
-
- /**
- * @throws \JsonException
- */
- 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(), null, 512, JSON_THROW_ON_ERROR), JSON_THROW_ON_ERROR);
- }
-}
diff --git a/src/PhpPact/Consumer/InteractionBuilder.php b/src/PhpPact/Consumer/InteractionBuilder.php
index 0e25dab5..000a08ae 100644
--- a/src/PhpPact/Consumer/InteractionBuilder.php
+++ b/src/PhpPact/Consumer/InteractionBuilder.php
@@ -2,37 +2,36 @@
namespace PhpPact\Consumer;
+use PhpPact\Consumer\Driver\Interaction\InteractionDriverInterface;
+use PhpPact\Consumer\Factory\InteractionDriverFactory;
+use PhpPact\Consumer\Factory\InteractionDriverFactoryInterface;
use PhpPact\Consumer\Model\ConsumerRequest;
use PhpPact\Consumer\Model\Interaction;
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
*/
class InteractionBuilder implements BuilderInterface
{
- protected MockServerHttpService $mockServerHttpService;
-
- protected MockServerConfigInterface $config;
-
+ private InteractionDriverInterface $driver;
private Interaction $interaction;
- public function __construct(MockServerConfigInterface $config)
+ public function __construct(MockServerConfigInterface $config, ?InteractionDriverFactoryInterface $driverFactory = null)
{
- $this->config = $config;
- $this->mockServerHttpService = new MockServerHttpService(new GuzzleClient(), $config);
- $this->interaction = new Interaction();
+ $this->driver = ($driverFactory ?? new InteractionDriverFactory())->create($config);
+ $this->interaction = new Interaction();
}
/**
* @param string $providerState what is given to the request
+ * @param array $params for that request
+ * @param bool $overwrite clear pass states completely and start this array
*/
- public function given(string $providerState): self
+ public function given(string $providerState, array $params = [], bool $overwrite = false): self
{
- $this->interaction->setProviderState($providerState);
+ $this->interaction->setProviderState($providerState, $params, $overwrite);
return $this;
}
@@ -58,18 +57,15 @@ 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
- * @throws \JsonException
*/
public function willRespondWith(ProviderResponse $response): bool
{
$this->interaction->setResponse($response);
- return $this->mockServerHttpService->registerInteraction($this->interaction);
+ return $this->driver->registerInteraction($this->interaction);
}
/**
@@ -77,33 +73,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.
- * @throws \JsonException
- */
- public function finalize(): bool
- {
- // Write the pact file to disk.
- $this->mockServerHttpService->getPactJson();
-
- // Delete the interactions.
- $this->mockServerHttpService->deleteAllInteractions();
-
- return true;
- }
-
- /**
- * {@inheritdoc}
- * @throws \JsonException
- */
- public function writePact(): bool
- {
- // Write the pact file to disk.
- $this->mockServerHttpService->getPactJson();
-
- return true;
+ return $this->driver->verifyInteractions();
}
}
diff --git a/src/PhpPact/Consumer/Listener/PactTestListener.php b/src/PhpPact/Consumer/Listener/PactTestListener.php
index a27a1622..f4a19c6b 100644
--- a/src/PhpPact/Consumer/Listener/PactTestListener.php
+++ b/src/PhpPact/Consumer/Listener/PactTestListener.php
@@ -2,16 +2,11 @@
namespace PhpPact\Consumer\Listener;
-use Amp\Process\ProcessException;
-use Exception;
use GuzzleHttp\Psr7\Uri;
-use JsonException;
-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;
@@ -24,12 +19,13 @@
/**
* PACT listener that can be used with environment variables and easily attached to PHPUnit configuration.
+ *
+ * @internal
*/
class PactTestListener implements TestListener
{
use TestListenerDefaultImplementation;
- private MockServer $server;
/**
* Name of the test suite configured in your phpunit config.
*
@@ -52,17 +48,6 @@ public function __construct(array $testSuiteNames)
$this->mockServerConfig = new MockServerEnvConfig();
}
- /**
- * @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;
@@ -75,20 +60,10 @@ public function addFailure(Test $test, AssertionFailedError $e, float $time): vo
/**
* Publish JSON results to PACT Broker and stop the Mock Server.
- * @throws JsonException|ProcessException
*/
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'))) {
@@ -98,29 +73,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],
- ];
+ $brokerConfig->setBrokerUsername($user);
+ $brokerConfig->setBrokerPassword($pass);
}
- if (($sslVerify = getenv('PACT_BROKER_SSL_VERIFY'))) {
- $clientConfig['verify'] = $sslVerify !== 'no';
- }
-
- $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 6d27e1d1..c3a41538 100644
--- a/src/PhpPact/Consumer/Matcher/Matcher.php
+++ b/src/PhpPact/Consumer/Matcher/Matcher.php
@@ -8,7 +8,8 @@
use function preg_match;
/**
- * 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
{
@@ -45,12 +46,12 @@ public function somethingLike(mixed $value): array
public function like(mixed $value): array
{
if ($value === null) {
- throw new \Exception('Value must not be null.');
+ throw new Exception('Value must not be null.');
}
return [
- 'contents' => $value,
- 'json_class' => 'Pact::SomethingLike',
+ 'value' => $value,
+ 'pact:matcher:type' => 'type',
];
}
@@ -64,14 +65,11 @@ public function like(mixed $value): array
*/
public function eachLike(mixed $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;
}
/**
@@ -95,15 +93,9 @@ public function term(mixed $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',
];
}
@@ -262,7 +254,7 @@ public function ipv6Address(string $ip = '::ffff:192.0.2.128'): array
/**
* @return array
*
- * @throws \Exception
+ * @throws Exception
*/
public function email(string $email = 'hello@pact.io'): array
{
diff --git a/src/PhpPact/Consumer/MessageBuilder.php b/src/PhpPact/Consumer/MessageBuilder.php
index b1b383c6..6d5be6d5 100644
--- a/src/PhpPact/Consumer/MessageBuilder.php
+++ b/src/PhpPact/Consumer/MessageBuilder.php
@@ -2,31 +2,27 @@
namespace PhpPact\Consumer;
-use PhpPact\Consumer\Model\Message;
-use PhpPact\Standalone\PactConfigInterface;
-use PhpPact\Standalone\PactMessage\PactMessage;
+use PhpPact\Consumer\Driver\Interaction\MessageDriverInterface;
+use PhpPact\Config\PactConfigInterface;
+use PhpPact\Consumer\Factory\MessageDriverFactory;
+use PhpPact\Consumer\Factory\MessageDriverFactoryInterface;
/**
* Build a message and send it to the Ruby Standalone Mock Service
*/
-class MessageBuilder implements BuilderInterface
+class MessageBuilder extends AbstractMessageBuilder
{
- protected PactMessage $pactMessage;
-
- protected PactConfigInterface $config;
+ protected MessageDriverInterface $driver;
/**
* @var array
*/
protected array $callback;
- private Message $message;
-
- public function __construct(PactConfigInterface $config)
+ public function __construct(PactConfigInterface $config, ?MessageDriverFactoryInterface $driverFactory = null)
{
- $this->config = $config;
- $this->message = new Message();
- $this->pactMessage = new PactMessage();
+ parent::__construct();
+ $this->driver = ($driverFactory ?? new MessageDriverFactory())->create($config);
}
/**
@@ -45,56 +41,14 @@ public function setCallback(callable $callback, ?string $description = null): se
return $this;
}
- /**
- * @param string $name what is given to the request
- * @param array $params for that request
- * @param bool $overwrite clear pass states completely and start this array
- */
- public function given(string $name, array $params = [], bool $overwrite = false): self
- {
- $this->message->setProviderState($name, $params, $overwrite);
-
- return $this;
- }
-
- /**
- * @param string $description what is received when the request is made
- */
- public function expectsToReceive(string $description): self
- {
- $this->message->setDescription($description);
-
- return $this;
- }
-
- /**
- * @param array $metadata what is the additional metadata of the message
- */
- public function withMetadata(array $metadata): self
- {
- $this->message->setMetadata($metadata);
-
- return $this;
- }
-
- /**
- * Make the http request to the Mock Service to register the message. Content is required.
- *
- * @param mixed $contents required to be in the message
- */
- public function withContent($contents): self
- {
- $this->message->setContents($contents);
-
- return $this;
- }
-
/**
* Run reify to create an example pact from the message (i.e. create messages from matchers)
*/
public function reify(): string
{
- return $this->pactMessage->reify($this->message);
+ $this->driver->registerMessage($this->message);
+
+ return $this->driver->reify();
}
/**
@@ -107,18 +61,16 @@ public function verifyMessage(callable $callback, ?string $description = null):
{
$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 null|string $description description of the pact and thus callback
- *
* @throws \Exception if callback is not set
*/
- public function verify(?string $description = null): bool
+ public function verify(): bool
{
if (\count($this->callback) < 1) {
throw new \Exception('Callbacks need to exist to run verify.');
@@ -133,21 +85,9 @@ public function verify(?string $description = null): bool
\call_user_func($callback, $pactJson);
}
- return $this->writePact();
+ return $this->driver->writePactAndCleanUp();
} catch (\Exception $e) {
return false;
}
}
-
- /**
- * Write the Pact without deleting the interactions.
- * @throws \JsonException
- */
- public function writePact(): bool
- {
- // you do not want to save the reified json
- $pactJson = \json_encode($this->message, JSON_THROW_ON_ERROR);
-
- return $this->pactMessage->update($pactJson, $this->config->getConsumer(), $this->config->getProvider(), $this->config->getPactDir());
- }
}
diff --git a/src/PhpPact/Consumer/Model/ConsumerRequest.php b/src/PhpPact/Consumer/Model/ConsumerRequest.php
index cc5d797a..4632dee1 100644
--- a/src/PhpPact/Consumer/Model/ConsumerRequest.php
+++ b/src/PhpPact/Consumer/Model/ConsumerRequest.php
@@ -2,26 +2,28 @@
namespace PhpPact\Consumer\Model;
+use JsonException;
+
/**
* Request initiated by the consumer.
*/
-class ConsumerRequest implements \JsonSerializable
+class ConsumerRequest
{
private string $method;
- /**
- * @var string|array
- */
- private string|array $path;
+ private string $path;
/**
- * @var array
+ * @var array
*/
private array $headers = [];
- private mixed $body = null;
+ private ?string $body = null;
- private ?string $query = null;
+ /**
+ * @var array
+ */
+ private array $query = [];
public function getMethod(): string
{
@@ -35,26 +37,25 @@ public function setMethod(string $method): self
return $this;
}
- /**
- * @return string|array
- */
- public function getPath(): string|array
+ public function getPath(): string
{
return $this->path;
}
/**
* @param string|array $path
+ *
+ * @throws JsonException
*/
public function setPath(string|array $path): self
{
- $this->path = $path;
+ $this->path = is_array($path) ? json_encode($path, JSON_THROW_ON_ERROR) : $path;
return $this;
}
/**
- * @return array
+ * @return array
*/
public function getHeaders(): array
{
@@ -62,82 +63,98 @@ public function getHeaders(): array
}
/**
- * @param string[] $headers
+ * @param array $headers
*/
public function setHeaders(array $headers): self
{
- $this->headers = $headers;
+ $this->headers = [];
+ foreach ($headers as $header => $value) {
+ $this->addHeader($header, $value);
+ }
return $this;
}
- public function addHeader(string $header, string $value): self
+ /**
+ * @param string|string[] $value
+ */
+ 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;
}
- public function getBody(): mixed
+ private function addHeaderValue(string $header, string $value): void
+ {
+ $this->headers[$header][] = $value;
+ }
+
+ public function getBody(): ?string
{
return $this->body;
}
- public function setBody(mixed $body): self
+ /**
+ * @param array|string|null $body
+ *
+ * @throws JsonException
+ */
+ public function setBody(array|string|null $body): self
{
- $this->body = $body;
+ if (\is_string($body) || \is_null($body)) {
+ $this->body = $body;
+ } else {
+ $this->body = \json_encode($body, JSON_THROW_ON_ERROR);
+ $this->addHeader('Content-Type', 'application/json');
+ }
return $this;
}
- public function getQuery(): ?string
+ /**
+ * @return array
+ */
+ public function getQuery(): array
{
return $this->query;
}
- public function setQuery(string $query): self
+ /**
+ * @param array $query
+ */
+ public function setQuery(array $query): self
{
- $this->query = $query;
+ $this->query = [];
+ foreach ($query as $key => $value) {
+ $this->addQueryParameter($key, $value);
+ }
return $this;
}
- public function addQueryParameter(string $key, string $value): self
+ /**
+ * @param string|string[] $value
+ */
+ 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;
}
- /**
- * @return array
- */
- public function jsonSerialize(): array
+ private function addQueryParameterValue(string $key, string $value): void
{
- $results = [];
-
- $results['method'] = $this->getMethod();
-
- if (count($this->getHeaders()) > 0) {
- $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 44feaf67..5cc23cab 100644
--- a/src/PhpPact/Consumer/Model/Interaction.php
+++ b/src/PhpPact/Consumer/Model/Interaction.php
@@ -3,13 +3,13 @@
namespace PhpPact\Consumer\Model;
/**
- * Request/Response Pair to be posted to the Ruby Standalone Mock Server for PACT tests.
+ * Request/Response Pair to be posted to the Mock Server for PACT tests.
*/
-class Interaction implements \JsonSerializable
+class Interaction
{
- private string $description;
+ use ProviderStates;
- private ?string $providerState = null;
+ private string $description;
private ConsumerRequest $request;
@@ -27,18 +27,6 @@ public function setDescription(string $description): self
return $this;
}
- public function getProviderState(): ?string
- {
- return $this->providerState;
- }
-
- public function setProviderState(string $providerState): self
- {
- $this->providerState = $providerState;
-
- return $this;
- }
-
public function getRequest(): ConsumerRequest
{
return $this->request;
@@ -62,25 +50,4 @@ public function setResponse(ProviderResponse $response): self
return $this;
}
-
- /**
- * @return array
- */
- public function jsonSerialize(): array
- {
- 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 8025b20d..1b8b3a4e 100644
--- a/src/PhpPact/Consumer/Model/Message.php
+++ b/src/PhpPact/Consumer/Model/Message.php
@@ -3,21 +3,18 @@
namespace PhpPact\Consumer\Model;
/**
- * Request/Response Pair to be posted to the Ruby Standalone Mock Server for PACT tests.
+ * Message metadata and contents to be posted to the Mock Server for PACT tests.
*/
-class Message implements \JsonSerializable
+class Message
{
- private string $description;
+ use ProviderStates;
- /**
- * @var array
- */
- private array $providerStates = [];
+ private string $description;
/**
* @var array
*/
- private array $metadata;
+ private array $metadata = [];
private mixed $contents;
@@ -33,46 +30,6 @@ public function setDescription(string $description): self
return $this;
}
- /**
- * @return array
- */
- public function getProviderStates(): array
- {
- return $this->providerStates;
- }
-
- /**
- * @param array $params
- *
- * @return array
- */
- public function setProviderState(string $name, array $params = [], bool $overwrite = true): array
- {
- $this->addProviderState($name, $params, $overwrite);
-
- return $this->providerStates;
- }
-
- /**
- * @param string $name
- * @param array $params
- * @param bool $overwrite - if true reset the entire state
- */
- public function addProviderState(string $name, array $params, bool $overwrite = false): self
- {
- $providerState = new \stdClass();
- $providerState->name = $name;
- $providerState->params = $params;
-
- if ($overwrite === true) {
- $this->providerStates = [];
- }
-
- $this->providerStates[] = $providerState;
-
- return $this;
- }
-
/**
* @return array
*/
@@ -86,11 +43,19 @@ public function getMetadata(): array
*/
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;
+ }
+
public function getContents(): mixed
{
return $this->contents;
@@ -102,29 +67,4 @@ public function setContents(mixed $contents): self
return $this;
}
-
- /**
- * {@inheritdoc}
- *
- * @return array
- */
- public function jsonSerialize(): array
- {
- $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/ProviderResponse.php b/src/PhpPact/Consumer/Model/ProviderResponse.php
index 61b19298..4ec6ba51 100644
--- a/src/PhpPact/Consumer/Model/ProviderResponse.php
+++ b/src/PhpPact/Consumer/Model/ProviderResponse.php
@@ -2,22 +2,21 @@
namespace PhpPact\Consumer\Model;
+use JsonException;
+
/**
* Response expectation that would be in response to a Consumer request from the Provider.
*/
-class ProviderResponse implements \JsonSerializable
+class ProviderResponse
{
private int $status;
/**
- * @var array
+ * @var array
*/
private array $headers = [];
- /**
- * @var ?array
- */
- private ?array $body = null;
+ private ?string $body = null;
public function getStatus(): int
{
@@ -32,7 +31,7 @@ public function setStatus(int $status): self
}
/**
- * @return array
+ * @return array
*/
public function getHeaders(): array
{
@@ -40,57 +39,57 @@ public function getHeaders(): array
}
/**
- * @param array $headers
+ * @param array $headers
*/
public function setHeaders(array $headers): self
{
- $this->headers = $headers;
+ $this->headers = [];
+ foreach ($headers as $header => $value) {
+ $this->addHeader($header, $value);
+ }
return $this;
}
- public function addHeader(string $header, string $value): self
+ /**
+ * @param string[]|string $value
+ */
+ 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 ?array
- */
- public function getBody(): ?array
+ private function addHeaderValue(string $header, string $value): void
{
- return $this->body;
+ $this->headers[$header][] = $value;
}
- /**
- * @param array $body
- */
- public function setBody(array $body): self
+ public function getBody(): ?string
{
- $this->body = $body;
-
- return $this;
+ return $this->body;
}
/**
- * @return array
+ * @param array|string|null $body
+ *
+ * @throws JsonException
*/
- public function jsonSerialize(): array
+ public function setBody(array|string|null $body): self
{
- $results = [
- 'status' => $this->getStatus(),
- ];
-
- if (count($this->getHeaders()) > 0) {
- $results['headers'] = $this->getHeaders();
- }
-
- if ($this->getBody() !== null) {
- $results['body'] = $this->getBody();
+ if (\is_string($body) || \is_null($body)) {
+ $this->body = $body;
+ } else {
+ $this->body = \json_encode($body, JSON_THROW_ON_ERROR);
+ $this->addHeader('Content-Type', 'application/json');
}
- return $results;
+ return $this;
}
}
diff --git a/src/PhpPact/Consumer/Model/ProviderState.php b/src/PhpPact/Consumer/Model/ProviderState.php
new file mode 100644
index 00000000..acfa571d
--- /dev/null
+++ b/src/PhpPact/Consumer/Model/ProviderState.php
@@ -0,0 +1,46 @@
+
+ */
+ private array $params = [];
+
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ public function setName(string $name): void
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * @return array
+ */
+ public function getParams(): array
+ {
+ return $this->params;
+ }
+
+ /**
+ * @param array $params
+ */
+ public function setParams(array $params = []): void
+ {
+ foreach ($params as $key => $value) {
+ $this->addParam($key, $value);
+ }
+ }
+
+ public function addParam(string $key, string $value): void
+ {
+ $this->params[$key] = $value;
+ }
+}
diff --git a/src/PhpPact/Consumer/Model/ProviderStates.php b/src/PhpPact/Consumer/Model/ProviderStates.php
new file mode 100644
index 00000000..09c09ece
--- /dev/null
+++ b/src/PhpPact/Consumer/Model/ProviderStates.php
@@ -0,0 +1,55 @@
+
+ */
+ private array $providerStates = [];
+
+ /**
+ * @return array
+ */
+ public function getProviderStates(): array
+ {
+ return $this->providerStates;
+ }
+
+ /**
+ * @param string $name
+ * @param array $params
+ * @param bool $overwrite
+ *
+ * @return array
+ */
+ public function setProviderState(string $name, array $params = [], bool $overwrite = true): array
+ {
+ $this->addProviderState($name, $params, $overwrite);
+
+ return $this->providerStates;
+ }
+
+ /**
+ * @param string $name
+ * @param array $params
+ * @param bool $overwrite - if true reset the entire state
+ *
+ * @return $this
+ */
+ public function addProviderState(string $name, array $params, bool $overwrite = false): self
+ {
+ $providerState = new ProviderState();
+ $providerState->setName($name);
+ $providerState->setParams($params);
+
+ if ($overwrite === true) {
+ $this->providerStates = [];
+ }
+
+ $this->providerStates[] = $providerState;
+
+ return $this;
+ }
+}
diff --git a/src/PhpPact/Consumer/Registry/Interaction/AbstractRegistry.php b/src/PhpPact/Consumer/Registry/Interaction/AbstractRegistry.php
new file mode 100644
index 00000000..82e1d76d
--- /dev/null
+++ b/src/PhpPact/Consumer/Registry/Interaction/AbstractRegistry.php
@@ -0,0 +1,24 @@
+id;
+ }
+
+ abstract protected function newInteraction(string $description): self;
+}
diff --git a/src/PhpPact/Consumer/Registry/Interaction/Contents/AbstractBodyRegistry.php b/src/PhpPact/Consumer/Registry/Interaction/Contents/AbstractBodyRegistry.php
new file mode 100644
index 00000000..8f096540
--- /dev/null
+++ b/src/PhpPact/Consumer/Registry/Interaction/Contents/AbstractBodyRegistry.php
@@ -0,0 +1,29 @@
+client->call('pactffi_with_body', $this->interactionRegistry->getId(), $this->getPart(), $contentType, $body);
+ if (!$success) {
+ throw new InteractionBodyNotAddedException();
+ }
+ }
+
+ abstract protected function getPart(): int;
+}
diff --git a/src/PhpPact/Consumer/Registry/Interaction/Contents/ContentsRegistryInterface.php b/src/PhpPact/Consumer/Registry/Interaction/Contents/ContentsRegistryInterface.php
new file mode 100644
index 00000000..3e3394ff
--- /dev/null
+++ b/src/PhpPact/Consumer/Registry/Interaction/Contents/ContentsRegistryInterface.php
@@ -0,0 +1,8 @@
+client->call('pactffi_message_with_contents', $this->messageRegistry->getId(), $contentType, $data->getValue(), $data->getSize());
+ }
+}
diff --git a/src/PhpPact/Consumer/Registry/Interaction/Contents/RequestBodyRegistry.php b/src/PhpPact/Consumer/Registry/Interaction/Contents/RequestBodyRegistry.php
new file mode 100644
index 00000000..1a4ed86e
--- /dev/null
+++ b/src/PhpPact/Consumer/Registry/Interaction/Contents/RequestBodyRegistry.php
@@ -0,0 +1,10 @@
+requestRegistry = $requestRegistry ?? new RequestRegistry($client, $this);
+ $this->responseRegistry = $responseRegistry ?? new ResponseRegistry($client, $this);
+ }
+
+ public function registerInteraction(Interaction $interaction): bool
+ {
+ $this
+ ->newInteraction($interaction->getDescription())
+ ->given($interaction->getProviderStates())
+ ->uponReceiving($interaction->getDescription())
+ ->with($interaction->getRequest())
+ ->willRespondWith($interaction->getResponse());
+
+ return true;
+ }
+
+ protected function newInteraction(string $description): self
+ {
+ $this->id = $this->client->call('pactffi_new_interaction', $this->pactRegistry->getId(), $description);
+
+ return $this;
+ }
+
+ private function uponReceiving(string $description): self
+ {
+ $this->client->call('pactffi_upon_receiving', $this->id, $description);
+
+ return $this;
+ }
+
+ /**
+ * @param ProviderState[] $providerStates
+ */
+ private function given(array $providerStates): self
+ {
+ foreach ($providerStates as $providerState) {
+ $this->client->call('pactffi_given', $this->id, $providerState->getName());
+ foreach ($providerState->getParams() as $key => $value) {
+ $this->client->call('pactffi_given_with_param', $this->id, $providerState->getName(), (string) $key, (string) $value);
+ }
+ }
+
+ return $this;
+ }
+
+ private function with(ConsumerRequest $request): self
+ {
+ $this->requestRegistry
+ ->withRequest($request->getMethod(), $request->getPath())
+ ->withQueryParameters($request->getQuery())
+ ->withHeaders($request->getHeaders())
+ ->withBody(null, $request->getBody());
+
+ return $this;
+ }
+
+ private function willRespondWith(ProviderResponse $response): self
+ {
+ $this->responseRegistry
+ ->withResponse($response->getStatus())
+ ->withHeaders($response->getHeaders())
+ ->withBody(null, $response->getBody());
+
+ return $this;
+ }
+}
diff --git a/src/PhpPact/Consumer/Registry/Interaction/InteractionRegistryInterface.php b/src/PhpPact/Consumer/Registry/Interaction/InteractionRegistryInterface.php
new file mode 100644
index 00000000..c3f1c7fc
--- /dev/null
+++ b/src/PhpPact/Consumer/Registry/Interaction/InteractionRegistryInterface.php
@@ -0,0 +1,10 @@
+messageContentsRegistry = $messageContentsRegistry ?? new MessageContentsRegistry($client, $this);
+ }
+
+
+ public function registerMessage(Message $message): void
+ {
+ if (\is_string($message->getContents())) {
+ $contents = $message->getContents();
+ $contentType = 'text/plain';
+ } else {
+ $contents = \json_encode($message->getContents(), JSON_THROW_ON_ERROR);
+ $contentType = 'application/json';
+ }
+
+ $this
+ ->newInteraction($message->getDescription())
+ ->given($message->getProviderStates())
+ ->expectsToReceive($message->getDescription())
+ ->withMetadata($message->getMetadata())
+ ->withContents($contentType, $contents);
+ }
+
+ protected function newInteraction(string $description): self
+ {
+ $this->id = $this->client->call('pactffi_new_message_interaction', $this->pactRegistry->getId(), $description);
+
+ return $this;
+ }
+
+ private function withContents(?string $contentType = null, ?string $contents = null): self
+ {
+ $this->messageContentsRegistry->withContents($contentType, $contents);
+
+ return $this;
+ }
+
+ private function expectsToReceive(string $description): self
+ {
+ $this->client->call('pactffi_message_expects_to_receive', $this->id, $description);
+
+ return $this;
+ }
+
+ /**
+ * @param ProviderState[] $providerStates
+ */
+ private function given(array $providerStates): self
+ {
+ foreach ($providerStates as $providerState) {
+ $this->client->call('pactffi_message_given', $this->id, $providerState->getName());
+ foreach ($providerState->getParams() as $key => $value) {
+ $this->client->call('pactffi_message_given_with_param', $this->id, $providerState->getName(), (string) $key, (string) $value);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param array $metadata
+ */
+ private function withMetadata(array $metadata): self
+ {
+ foreach ($metadata as $key => $value) {
+ $this->client->call('pactffi_message_with_metadata', $this->id, (string) $key, (string) $value);
+ }
+
+ return $this;
+ }
+}
diff --git a/src/PhpPact/Consumer/Registry/Interaction/MessageRegistryInterface.php b/src/PhpPact/Consumer/Registry/Interaction/MessageRegistryInterface.php
new file mode 100644
index 00000000..ca3af5c2
--- /dev/null
+++ b/src/PhpPact/Consumer/Registry/Interaction/MessageRegistryInterface.php
@@ -0,0 +1,10 @@
+contentsRegistry->withContents($contentType, $body);
+
+ return $this;
+ }
+
+ public function withHeaders(array $headers): self
+ {
+ foreach ($headers as $header => $values) {
+ foreach (array_values($values) as $index => $value) {
+ $this->client->call('pactffi_with_header_v2', $this->getInteractionId(), $this->getPart(), (string) $header, (int) $index, (string) $value);
+ }
+ }
+
+ return $this;
+ }
+
+ protected function getInteractionId(): int
+ {
+ return $this->interactionRegistry->getId();
+ }
+
+ abstract protected function getPart(): int;
+}
diff --git a/src/PhpPact/Consumer/Registry/Interaction/Part/PartRegistryInterface.php b/src/PhpPact/Consumer/Registry/Interaction/Part/PartRegistryInterface.php
new file mode 100644
index 00000000..90a6516b
--- /dev/null
+++ b/src/PhpPact/Consumer/Registry/Interaction/Part/PartRegistryInterface.php
@@ -0,0 +1,13 @@
+ $headers
+ */
+ public function withHeaders(array $headers): self;
+}
diff --git a/src/PhpPact/Consumer/Registry/Interaction/Part/RequestPartTrait.php b/src/PhpPact/Consumer/Registry/Interaction/Part/RequestPartTrait.php
new file mode 100644
index 00000000..89897944
--- /dev/null
+++ b/src/PhpPact/Consumer/Registry/Interaction/Part/RequestPartTrait.php
@@ -0,0 +1,11 @@
+client->get('InteractionPart_Request');
+ }
+}
diff --git a/src/PhpPact/Consumer/Registry/Interaction/Part/RequestRegistry.php b/src/PhpPact/Consumer/Registry/Interaction/Part/RequestRegistry.php
new file mode 100644
index 00000000..026dc57c
--- /dev/null
+++ b/src/PhpPact/Consumer/Registry/Interaction/Part/RequestRegistry.php
@@ -0,0 +1,39 @@
+ $values) {
+ foreach (array_values($values) as $index => $value) {
+ $this->client->call('pactffi_with_query_parameter_v2', $this->getInteractionId(), (string) $key, (int) $index, (string) $value);
+ }
+ }
+
+ return $this;
+ }
+
+ public function withRequest(string $method, string $path): self
+ {
+ $this->client->call('pactffi_with_request', $this->getInteractionId(), $method, $path);
+
+ return $this;
+ }
+}
diff --git a/src/PhpPact/Consumer/Registry/Interaction/Part/RequestRegistryInterface.php b/src/PhpPact/Consumer/Registry/Interaction/Part/RequestRegistryInterface.php
new file mode 100644
index 00000000..548c964f
--- /dev/null
+++ b/src/PhpPact/Consumer/Registry/Interaction/Part/RequestRegistryInterface.php
@@ -0,0 +1,13 @@
+ $queryParams
+ */
+ public function withQueryParameters(array $queryParams): self;
+
+ public function withRequest(string $method, string $path): self;
+}
diff --git a/src/PhpPact/Consumer/Registry/Interaction/Part/ResponsePartTrait.php b/src/PhpPact/Consumer/Registry/Interaction/Part/ResponsePartTrait.php
new file mode 100644
index 00000000..389fe949
--- /dev/null
+++ b/src/PhpPact/Consumer/Registry/Interaction/Part/ResponsePartTrait.php
@@ -0,0 +1,11 @@
+client->get('InteractionPart_Response');
+ }
+}
diff --git a/src/PhpPact/Consumer/Registry/Interaction/Part/ResponseRegistry.php b/src/PhpPact/Consumer/Registry/Interaction/Part/ResponseRegistry.php
new file mode 100644
index 00000000..3649a408
--- /dev/null
+++ b/src/PhpPact/Consumer/Registry/Interaction/Part/ResponseRegistry.php
@@ -0,0 +1,28 @@
+client->call('pactffi_response_status', $this->getInteractionId(), $status);
+
+ return $this;
+ }
+}
diff --git a/src/PhpPact/Consumer/Registry/Interaction/Part/ResponseRegistryInterface.php b/src/PhpPact/Consumer/Registry/Interaction/Part/ResponseRegistryInterface.php
new file mode 100644
index 00000000..05f56e52
--- /dev/null
+++ b/src/PhpPact/Consumer/Registry/Interaction/Part/ResponseRegistryInterface.php
@@ -0,0 +1,8 @@
+id)) {
+ throw new PactNotRegisteredException('New pact must be registered.');
+ }
+ return $this->id;
+ }
+
+ public function deletePact(): void
+ {
+ $this->client->call('pactffi_free_pact_handle', $this->id);
+ unset($this->id);
+ }
+
+ public function registerPact(string $consumer, string $provider, int $specification): void
+ {
+ $this
+ ->newPact($consumer, $provider)
+ ->withSpecification($specification);
+ }
+
+ private function newPact(string $consumer, string $provider): self
+ {
+ $this->id = $this->client->call('pactffi_new_pact', $consumer, $provider);
+
+ return $this;
+ }
+
+ private function withSpecification(int $specification): self
+ {
+ $this->client->call('pactffi_with_specification', $this->id, $specification);
+
+ return $this;
+ }
+}
diff --git a/src/PhpPact/Consumer/Registry/Pact/PactRegistryInterface.php b/src/PhpPact/Consumer/Registry/Pact/PactRegistryInterface.php
new file mode 100644
index 00000000..c9ffb883
--- /dev/null
+++ b/src/PhpPact/Consumer/Registry/Pact/PactRegistryInterface.php
@@ -0,0 +1,12 @@
+client->call(
+ 'pactffi_create_mock_server_for_transport',
+ $this->pactRegistry->getId(),
+ $this->config->getHost(),
+ $this->config->getPort(),
+ $this->getTransport(),
+ $this->getTransportConfig()
+ );
+
+ if ($port < 0) {
+ throw new MockServerNotStartedException($port);
+ }
+ $this->config->setPort($port);
+ }
+
+ public function verify(): bool
+ {
+ $matched = $this->client->call('pactffi_mock_server_matched', $this->config->getPort());
+
+ try {
+ if ($matched) {
+ $this->writePact();
+ }
+ } finally {
+ $this->cleanUp();
+ }
+
+ return $matched;
+ }
+
+ protected function getTransport(): string
+ {
+ return $this->config->isSecure() ? 'https' : 'http';
+ }
+
+ protected function getTransportConfig(): ?string
+ {
+ return null;
+ }
+
+ private function writePact(): void
+ {
+ $error = $this->client->call(
+ 'pactffi_write_pact_file',
+ $this->config->getPort(),
+ $this->config->getPactDir(),
+ $this->config->getPactFileWriteMode() === PactConfigInterface::MODE_OVERWRITE
+ );
+ if ($error) {
+ throw new MockServerNotWrotePactFileException($error);
+ }
+ }
+
+ private function cleanUp(): void
+ {
+ $this->client->call('pactffi_cleanup_mock_server', $this->config->getPort());
+ $this->pactRegistry->deletePact();
+ }
+}
diff --git a/src/PhpPact/Consumer/Service/MockServerInterface.php b/src/PhpPact/Consumer/Service/MockServerInterface.php
new file mode 100644
index 00000000..5737269c
--- /dev/null
+++ b/src/PhpPact/Consumer/Service/MockServerInterface.php
@@ -0,0 +1,10 @@
+ffi = FFI::cdef($code, Scripts::getLibrary());
+ }
+
+ public function call(string $name, ...$arguments): mixed
+ {
+ return $this->ffi->{$name}(...$arguments);
+ }
+
+ public function get(string $name): mixed
+ {
+ return $this->ffi->{$name};
+ }
+}
diff --git a/src/PhpPact/FFI/ClientInterface.php b/src/PhpPact/FFI/ClientInterface.php
new file mode 100644
index 00000000..c0e767ff
--- /dev/null
+++ b/src/PhpPact/FFI/ClientInterface.php
@@ -0,0 +1,13 @@
+ $arguments
+ */
+ public function call(string $name, ...$arguments): mixed;
+
+ public function get(string $name): mixed;
+}
diff --git a/src/PhpPact/FFI/Exception/CDataNotCreatedException.php b/src/PhpPact/FFI/Exception/CDataNotCreatedException.php
new file mode 100644
index 00000000..2e17f429
--- /dev/null
+++ b/src/PhpPact/FFI/Exception/CDataNotCreatedException.php
@@ -0,0 +1,9 @@
+items;
+ }
+
+ public function getSize(): int
+ {
+ return $this->size;
+ }
+
+ /**
+ * @param array $values
+ */
+ public static function createFrom(array $values): ?self
+ {
+ $size = count($values);
+ if ($size === 0) {
+ return null;
+ }
+
+ $items = FFI::new("char*[{$size}]");
+ if ($items === null) {
+ return throw new CDataNotCreatedException();
+ }
+ foreach ($values as $index => $value) {
+ $length = \strlen($value);
+ $itemSize = $length + 1;
+ $item = FFI::new("char[{$itemSize}]", false);
+ if ($item === null) {
+ return throw new CDataNotCreatedException();
+ }
+ FFI::memcpy($item, $value, $length);
+ $items[$index] = $item; // @phpstan-ignore-line
+ }
+
+ return new self($items, $size);
+ }
+
+ public function __destruct()
+ {
+ for ($i=0; $i < $this->size; $i++) {
+ FFI::free($this->items[$i]); // @phpstan-ignore-line
+ }
+ }
+}
diff --git a/src/PhpPact/FFI/Model/StringData.php b/src/PhpPact/FFI/Model/StringData.php
new file mode 100644
index 00000000..622f457a
--- /dev/null
+++ b/src/PhpPact/FFI/Model/StringData.php
@@ -0,0 +1,35 @@
+value;
+ }
+
+ public function getSize(): int
+ {
+ return $this->size;
+ }
+
+ public static function createFrom(string $value): ?self
+ {
+ $length = \strlen($value);
+ $size = $length + 1;
+ $cData = FFI::new("uint8_t[{$size}]");
+ FFI::memcpy($cData, $value, $length);
+
+ return new self($cData, $size);
+ }
+}
diff --git a/src/PhpPact/Provider/MessageVerifier.php b/src/PhpPact/Provider/MessageVerifier.php
deleted file mode 100644
index 7c5a7113..00000000
--- a/src/PhpPact/Provider/MessageVerifier.php
+++ /dev/null
@@ -1,208 +0,0 @@
- */
- protected array $callbacks = [];
-
- /**
- * Default host name for the proxy server
- */
- protected string $defaultProxyHost = 'localhost';
-
- /**
- * Default port for the proxy server to listen on
- */
- protected int $defaultProxyPort = 7201;
-
- /**
- * floor(provider-verification timeout / this value) = default verificationDelaySec
- */
- protected int $defaultDelayFactor = 3;
-
- /**
- * Set the number of seconds to delay the verification test to allow the proxy server to be stood up
- *
- * By default, it is a third of the provider-verification timeout
- */
- protected float $verificationDelaySec;
-
- private ?LoggerInterface $logger = null;
-
- public function __construct(VerifierConfigInterface $config)
- {
- parent::__construct($config);
-
- $this->callbacks = [];
-
- $baseUrl = $this->config->getProviderBaseUrl();
- if ($baseUrl === null) {
- $config->setProviderBaseUrl(new Uri("http://{$this->defaultProxyHost}:{$this->defaultProxyPort}"));
- }
-
- // default verification delay
- $this->setVerificationDelaySec(\floor($config->getProcessIdleTimeout() / $this->defaultDelayFactor));
- }
-
- /**
- * @param array $callbacks
- */
- public function setCallbacks(array $callbacks): self
- {
- $this->callbacks = $callbacks;
-
- return $this;
- }
-
- /**
- * Add an individual call back
- *
- * @throws \Exception
- */
- public function addCallback(string $key, callable $callback): self
- {
- if (isset($this->callbacks[$key])) {
- throw new \Exception("Callback with key ($key) already exists");
- }
-
- $this->callbacks[$key] = $callback;
-
- return $this;
- }
-
- public function setVerificationDelaySec(float $verificationDelaySec): self
- {
- $this->verificationDelaySec = $verificationDelaySec;
-
- return $this;
- }
-
- public function setLogger(LoggerInterface $logger): self
- {
- $this->logger = $logger;
-
- return $this;
- }
-
- /**
- * @throws \Exception
- */
- protected function verifyAction(array $arguments): void
- {
- 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);
- }
-
- private function getLogger(): LoggerInterface
- {
- 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/BrokerConfig.php b/src/PhpPact/Standalone/Broker/BrokerConfig.php
index 1ab80b26..48eac520 100644
--- a/src/PhpPact/Standalone/Broker/BrokerConfig.php
+++ b/src/PhpPact/Standalone/Broker/BrokerConfig.php
@@ -223,6 +223,13 @@ public function isVerbose(): bool
return $this->verbose;
}
+ public function setVerbose(bool $verbose): self
+ {
+ $this->verbose = $verbose;
+
+ return $this;
+ }
+
public function getBrokerUri(): ?UriInterface
{
return $this->brokerUri;
diff --git a/src/PhpPact/Standalone/Exception/HealthCheckFailedException.php b/src/PhpPact/Standalone/Exception/HealthCheckFailedException.php
deleted file mode 100644
index a2e98a23..00000000
--- a/src/PhpPact/Standalone/Exception/HealthCheckFailedException.php
+++ /dev/null
@@ -1,16 +0,0 @@
-config = $config;
- $this->httpService = $httpService ?: new MockServerHttpService(new GuzzleClient(), $this->config);
- }
-
- /**
- * 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?
- * @throws ProcessException
- */
- 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 !== null) {
- $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 ($this->config->getLog() !== null) {
- $log = \escapeshellarg($this->config->getLog());
- $results[] = \sprintf('--log=%s', $log);
- }
-
- return $results;
- }
-
- /**
- * Make sure the server starts as expected.
- *
- * @throws Exception
- */
- 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 b575800f..39bbe743 100644
--- a/src/PhpPact/Standalone/MockService/MockServerConfig.php
+++ b/src/PhpPact/Standalone/MockService/MockServerConfig.php
@@ -2,15 +2,14 @@
namespace PhpPact\Standalone\MockService;
-use Composer\Semver\VersionParser;
use GuzzleHttp\Psr7\Uri;
-use PhpPact\Standalone\PactConfigInterface;
+use PhpPact\Config\PactConfig;
use Psr\Http\Message\UriInterface;
/**
* Configuration defining the default PhpPact Ruby Standalone server.
*/
-class MockServerConfig implements MockServerConfigInterface, PactConfigInterface
+class MockServerConfig extends PactConfig implements MockServerConfigInterface
{
/**
* Host on which to bind the service.
@@ -18,58 +17,14 @@ class MockServerConfig implements MockServerConfigInterface, PactConfigInterface
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.
*/
private int $port = 7200;
- private bool $secure = false;
-
- /**
- * Consumer name.
- */
- private string $consumer;
-
- /**
- * Provider name.
- */
- private string $provider;
-
- /**
- * Directory to which the pacts will be written.
- */
- private ?string $pactDir = null;
-
- /**
- * `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.
- */
- private string $pactFileWriteMode = 'overwrite';
-
- /**
- * The pact specification version to use when writing the pact. Note that only versions 1 and 2 are currently supported.
- */
- private string $pactSpecificationVersion;
-
- /**
- * File to which to log output.
- */
- private ?string $log = null;
-
- private bool $cors = false;
-
- /**
- * The max allowed attempts the mock server has to be available in. Otherwise it is considered as sick.
- */
- private int $healthCheckTimeout;
-
/**
- * The seconds between health checks of mock server
+ * @var bool
*/
- private int $healthCheckRetrySec;
-
- private ?string $logLevel = null;
+ private bool $secure = false;
/**
* {@inheritdoc}
@@ -118,7 +73,7 @@ public function isSecure(): bool
/**
* {@inheritdoc}
*/
- public function setSecure(bool $secure): MockServerConfigInterface
+ public function setSecure(bool $secure): self
{
$this->secure = $secure;
@@ -134,196 +89,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): 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 getPactFileWriteMode(): string
- {
- return $this->pactFileWriteMode;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setPactFileWriteMode(string $pactFileWriteMode): self
- {
- $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(): string
- {
- return $this->pactSpecificationVersion;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setPactSpecificationVersion(string $pactSpecificationVersion): self
- {
- /*
- * 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(): ?string
- {
- return $this->log;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setLog(string $log): self
- {
- $this->log = $log;
-
- return $this;
- }
-
- public function getLogLevel(): ?string
- {
- 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;
- }
-
- public function hasCors(): bool
- {
- return $this->cors;
- }
-
- public function setCors(mixed $flag): self
- {
- if ($flag === 'true') {
- $this->cors = true;
- } elseif ($flag === 'false') {
- $this->cors = false;
- } else {
- $this->cors = (bool) $flag;
- }
-
- return $this;
- }
-
- public function setHealthCheckTimeout(int $timeout): MockServerConfigInterface
- {
- $this->healthCheckTimeout = $timeout;
-
- return $this;
- }
-
- public function getHealthCheckTimeout(): int
- {
- return $this->healthCheckTimeout;
- }
-
- public function setHealthCheckRetrySec(int $seconds): MockServerConfigInterface
- {
- $this->healthCheckRetrySec = $seconds;
-
- return $this;
- }
-
- public function getHealthCheckRetrySec(): int
- {
- return $this->healthCheckRetrySec;
- }
}
diff --git a/src/PhpPact/Standalone/MockService/MockServerConfigInterface.php b/src/PhpPact/Standalone/MockService/MockServerConfigInterface.php
index b61f8d1f..3b25bfe4 100644
--- a/src/PhpPact/Standalone/MockService/MockServerConfigInterface.php
+++ b/src/PhpPact/Standalone/MockService/MockServerConfigInterface.php
@@ -2,12 +2,13 @@
namespace PhpPact\Standalone\MockService;
+use PhpPact\Config\PactConfigInterface;
use Psr\Http\Message\UriInterface;
/**
* Mock Server configuration interface to allow for simple overrides that are reusable.
*/
-interface MockServerConfigInterface
+interface MockServerConfigInterface extends PactConfigInterface
{
/**
* @return string the host of the mock service
@@ -40,26 +41,4 @@ public function isSecure(): bool;
public function setSecure(bool $secure): self;
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
- */
- public function setPactFileWriteMode(string $pactFileWriteMode): self;
-
- public function hasCors(): bool;
-
- public function setCors(mixed $flag): self;
-
- public function setHealthCheckTimeout(int $timeout): self;
-
- public function getHealthCheckTimeout(): int;
-
- public function setHealthCheckRetrySec(int $seconds): self;
-
- public function getHealthCheckRetrySec(): int;
}
diff --git a/src/PhpPact/Standalone/MockService/MockServerEnvConfig.php b/src/PhpPact/Standalone/MockService/MockServerEnvConfig.php
index fae19e83..7e90a93d 100644
--- a/src/PhpPact/Standalone/MockService/MockServerEnvConfig.php
+++ b/src/PhpPact/Standalone/MockService/MockServerEnvConfig.php
@@ -9,8 +9,6 @@
*/
class MockServerEnvConfig extends MockServerConfig
{
- public const DEFAULT_SPECIFICATION_VERSION = '2.0.0';
-
/**
* @throws MissingEnvVariableException
*/
@@ -21,7 +19,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);
@@ -31,18 +28,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;
@@ -56,17 +41,12 @@ public function __construct()
*
* @throws MissingEnvVariableException
*/
- private function parseEnv(string $variableName, bool $required = true): mixed
+ private function parseEnv(string $variableName, bool $required = true): ?string
{
- $result = null;
+ $result = \getenv($variableName);
- if (\getenv($variableName) === 'false') {
- $result = false;
- } elseif (\getenv($variableName) === 'true') {
- $result = true;
- }
- if (\getenv($variableName) !== false) {
- $result = \getenv($variableName);
+ if (is_bool($result)) {
+ $result = null;
}
if ($required === true && $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 c41bb2df..00000000
--- a/src/PhpPact/Standalone/MockService/Service/MockServerHttpService.php
+++ /dev/null
@@ -1,166 +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(), JSON_THROW_ON_ERROR);
-
- $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
- */
- public function registerMessage(Message $message): bool
- {
- $uri = $this->config->getBaseUri()->withPath('/interactions');
-
- $body = \json_encode($message->jsonSerialize(), JSON_THROW_ON_ERROR);
-
- $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}
- * @throws \JsonException
- */
- 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()), JSON_THROW_ON_ERROR);
- }
-
- /**
- * 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.
- * @throws \JsonException
- */
- 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 caf095f5..00000000
--- a/src/PhpPact/Standalone/MockService/Service/MockServerHttpServiceInterface.php
+++ /dev/null
@@ -1,36 +0,0 @@
-runBlocking();
-
- $output = $process->getOutput();
- \preg_replace("/\r|\n/", '', $output);
-
- return $output;
- }
-
- /**
- * 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.
- */
- public function update(string $pactJson, string $consumer, string $provider, string $pactDir): bool
- {
- $arguments = [];
- $arguments[] = 'update';
- $arguments[] = "--consumer={$consumer}";
- $arguments[] = "--provider={$provider}";
- $arguments[] = "--pact-dir={$pactDir}";
- $arguments[] = "'" . $pactJson . "'";
-
- $process = new ProcessRunner(Scripts::getPactMessage(), $arguments);
- $process->runBlocking();
-
- \sleep(1);
-
- return true;
- }
-}
diff --git a/src/PhpPact/Standalone/PactMessage/PactMessageConfig.php b/src/PhpPact/Standalone/PactMessage/PactMessageConfig.php
index d386fe88..56314f6c 100644
--- a/src/PhpPact/Standalone/PactMessage/PactMessageConfig.php
+++ b/src/PhpPact/Standalone/PactMessage/PactMessageConfig.php
@@ -2,157 +2,11 @@
namespace PhpPact\Standalone\PactMessage;
-use Composer\Semver\VersionParser;
-use PhpPact\Standalone\PactConfigInterface;
+use PhpPact\Config\PactConfig;
/**
* Configuration defining the default PhpPact Ruby Standalone server.
- * Class MockServerConfig.
*/
-class PactMessageConfig implements PactConfigInterface
+class PactMessageConfig extends PactConfig
{
- /**
- * Consumer name.
- */
- private string $consumer;
-
- /**
- * Provider name.
- */
- private string $provider;
-
- /**
- * Directory to which the pacts will be written.
- */
- private ?string $pactDir = null;
-
- /**
- * The pact specification version to use when writing the pact. Note that only versions 1 and 2 are currently supported.
- */
- private string $pactSpecificationVersion;
-
- /**
- * File to which to log output.
- */
- private string $log;
-
- private string $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(): string
- {
- 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(): string
- {
- 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(): string
- {
- return $this->log;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setLog(string $log): PactConfigInterface
- {
- $this->log = $log;
-
- return $this;
- }
-
- public function getLogLevel(): string
- {
- 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/Config/CallingApp.php b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/CallingApp.php
new file mode 100644
index 00000000..2b7630fa
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/CallingApp.php
@@ -0,0 +1,33 @@
+name;
+ }
+
+ public function setName(?string $name): CallingAppInterface
+ {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ public function getVersion(): ?string
+ {
+ return $this->version;
+ }
+
+ public function setVersion(?string $version): CallingAppInterface
+ {
+ $this->version = $version;
+
+ return $this;
+ }
+}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Model/Config/CallingAppInterface.php b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/CallingAppInterface.php
new file mode 100644
index 00000000..bae0ec05
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/CallingAppInterface.php
@@ -0,0 +1,14 @@
+
+ */
+ private array $filterConsumerNames = [];
+
+ public function setFilterConsumerNames(array $filterConsumerNames): self
+ {
+ $this->filterConsumerNames = [];
+ foreach ($filterConsumerNames as $filterConsumerName) {
+ $this->addFilterConsumerName($filterConsumerName);
+ }
+
+ return $this;
+ }
+
+ public function addFilterConsumerName(string $filterConsumerName): self
+ {
+ $this->filterConsumerNames[] = $filterConsumerName;
+
+ return $this;
+ }
+
+ 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..91402e7b
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ConsumerFiltersInterface.php
@@ -0,0 +1,18 @@
+ $filterConsumerNames
+ */
+ public function setFilterConsumerNames(array $filterConsumerNames): self;
+
+ public function addFilterConsumerName(string $filterConsumerName): self;
+
+ /**
+ * @return array
+ */
+ public function getFilterConsumerNames(): array;
+}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Model/Config/FilterInfo.php b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/FilterInfo.php
new file mode 100644
index 00000000..1f5cdce6
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/FilterInfo.php
@@ -0,0 +1,46 @@
+filterDescription;
+ }
+
+ public function setFilterDescription(?string $filterDescription): self
+ {
+ $this->filterDescription = $filterDescription;
+
+ return $this;
+ }
+
+ public function getFilterNoState(): bool
+ {
+ return $this->filterNoState;
+ }
+
+ public function setFilterNoState(bool $filterNoState): self
+ {
+ $this->filterNoState = $filterNoState;
+
+ return $this;
+ }
+
+ public function getFilterState(): ?string
+ {
+ return $this->filterState;
+ }
+
+ public function setFilterState(?string $filterState): self
+ {
+ $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..51420ae8
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/FilterInfoInterface.php
@@ -0,0 +1,18 @@
+pluginDir;
+ }
+
+ public function setPluginDir(?string $pluginDir): self
+ {
+ $this->pluginDir = $pluginDir;
+
+ return $this;
+ }
+}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ProviderInfo.php b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ProviderInfo.php
new file mode 100644
index 00000000..375af81c
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ProviderInfo.php
@@ -0,0 +1,72 @@
+name;
+ }
+
+ public function setName(string $name): self
+ {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ public function getHost(): ?string
+ {
+ return $this->host;
+ }
+
+ public function setHost(string $host): self
+ {
+ $this->host = $host;
+
+ return $this;
+ }
+
+ public function getScheme(): ?string
+ {
+ return $this->scheme;
+ }
+
+ public function setScheme(?string $scheme): self
+ {
+ $this->scheme = $scheme;
+
+ return $this;
+ }
+
+ public function getPort(): ?int
+ {
+ return $this->port;
+ }
+
+ public function setPort(?int $port): self
+ {
+ $this->port = $port;
+
+ return $this;
+ }
+
+ public function getPath(): ?string
+ {
+ return $this->path;
+ }
+
+ public function setPath(?string $path): self
+ {
+ $this->path = $path;
+
+ 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..d76d297a
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ProviderInfoInterface.php
@@ -0,0 +1,26 @@
+stateChangeUrl;
+ }
+
+ public function setStateChangeUrl(?UriInterface $stateChangeUrl): self
+ {
+ $this->stateChangeUrl = $stateChangeUrl;
+
+ return $this;
+ }
+
+ public function setStateChangeAsBody(bool $stateChangeAsBody): self
+ {
+ $this->stateChangeAsBody = $stateChangeAsBody;
+
+ return $this;
+ }
+
+ public function isStateChangeAsBody(): bool
+ {
+ return $this->stateChangeAsBody;
+ }
+
+ public function setStateChangeTeardown(bool $stateChangeTeardown): self
+ {
+ $this->stateChangeTeardown = $stateChangeTeardown;
+
+ return $this;
+ }
+
+ 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..a51f8619
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ProviderStateInterface.php
@@ -0,0 +1,20 @@
+protocol;
+ }
+
+ public function setProtocol(?string $protocol): self
+ {
+ $this->protocol = $protocol;
+
+ return $this;
+ }
+
+ public function getScheme(): ?string
+ {
+ return $this->scheme;
+ }
+
+ public function setScheme(?string $scheme): self
+ {
+ $this->scheme = $scheme;
+
+ return $this;
+ }
+
+ public function getPort(): ?int
+ {
+ return $this->port;
+ }
+
+ public function setPort(?int $port): self
+ {
+ $this->port = $port;
+
+ return $this;
+ }
+
+ public function getPath(): ?string
+ {
+ return $this->path;
+ }
+
+ public function setPath(?string $path): self
+ {
+ $this->path = $path;
+
+ return $this;
+ }
+}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ProviderTransportInterface.php b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ProviderTransportInterface.php
new file mode 100644
index 00000000..ad269b16
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/ProviderTransportInterface.php
@@ -0,0 +1,28 @@
+
+ */
+ private array $providerTags = [];
+ private string $providerVersion;
+ private ?UriInterface $buildUrl = null;
+ private ?string $providerBranch = null;
+
+ public function getProviderTags(): array
+ {
+ return $this->providerTags;
+ }
+
+ public function setProviderTags(array $providerTags): self
+ {
+ $this->providerTags = [];
+ foreach ($providerTags as $providerTag) {
+ $this->addProviderTag($providerTag);
+ }
+
+ return $this;
+ }
+
+ public function addProviderTag(string $providerTag): self
+ {
+ $this->providerTags[] = $providerTag;
+
+ return $this;
+ }
+
+ public function getProviderVersion(): string
+ {
+ return $this->providerVersion;
+ }
+
+ public function setProviderVersion(string $providerVersion): self
+ {
+ $this->providerVersion = $providerVersion;
+
+ return $this;
+ }
+
+ public function getBuildUrl(): ?UriInterface
+ {
+ return $this->buildUrl;
+ }
+
+ public function setBuildUrl(?UriInterface $buildUrl): self
+ {
+ $this->buildUrl = $buildUrl;
+
+ return $this;
+ }
+
+ public function getProviderBranch(): ?string
+ {
+ return $this->providerBranch;
+ }
+
+ public function setProviderBranch(?string $providerBranch): self
+ {
+ $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..00429e89
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/PublishOptionsInterface.php
@@ -0,0 +1,32 @@
+
+ */
+ public function getProviderTags(): array;
+
+ /**
+ * @param array $providerTags
+ */
+ public function setProviderTags(array $providerTags): self;
+
+ public function addProviderTag(string $providerTag): self;
+
+ public function getProviderVersion(): string;
+
+ public function setProviderVersion(string $providerVersion): self;
+
+ public function getBuildUrl(): ?UriInterface;
+
+ public function setBuildUrl(UriInterface $buildUrl): self;
+
+ public function getProviderBranch(): ?string;
+
+ public function setProviderBranch(?string $providerBranch): self;
+}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Model/Config/VerificationOptions.php b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/VerificationOptions.php
new file mode 100644
index 00000000..5e6c25a8
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/VerificationOptions.php
@@ -0,0 +1,33 @@
+disableSslVerification;
+ }
+
+ public function setDisableSslVerification(bool $disableSslVerification): self
+ {
+ $this->disableSslVerification = $disableSslVerification;
+
+ return $this;
+ }
+
+ public function setRequestTimeout(int $requestTimeout): self
+ {
+ $this->requestTimeout = $requestTimeout;
+
+ return $this;
+ }
+
+ 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..093fbbf7
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Config/VerificationOptionsInterface.php
@@ -0,0 +1,14 @@
+> */
- private array $selectors;
+ /** @var array */
+ private array $selectors = [];
/**
* @param array $selectors
@@ -30,37 +30,31 @@ public function addSelector(string $selector): self
return $this;
}
- #[\ReturnTypeWillChange]
- public function current()
+ public function current(): string
{
return $this->selectors[$this->position];
}
- #[\ReturnTypeWillChange]
- public function next()
+ public function next(): void
{
++$this->position;
}
- #[\ReturnTypeWillChange]
public function key(): int
{
return $this->position;
}
- #[\ReturnTypeWillChange]
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(): 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..8b1cc29d
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Source/Broker.php
@@ -0,0 +1,118 @@
+
+ */
+ private array $providerTags = [];
+ protected ?string $providerBranch = null;
+ protected ConsumerVersionSelectors $consumerVersionSelectors;
+ /**
+ * @var array
+ */
+ private array $consumerVersionTags = [];
+
+ public function __construct()
+ {
+ $this->consumerVersionSelectors = new ConsumerVersionSelectors();
+ }
+
+ public function isEnablePending(): bool
+ {
+ return $this->enablePending;
+ }
+
+ public function setEnablePending(bool $enablePending): self
+ {
+ $this->enablePending = $enablePending;
+
+ return $this;
+ }
+
+ public function setIncludeWipPactSince(?string $date): self
+ {
+ $this->wipPactSince = $date;
+
+ return $this;
+ }
+
+ public function getIncludeWipPactSince(): ?string
+ {
+ return $this->wipPactSince;
+ }
+
+ public function getProviderTags(): array
+ {
+ return $this->providerTags;
+ }
+
+ public function setProviderTags(array $providerTags): self
+ {
+ $this->providerTags = [];
+ foreach ($providerTags as $providerTag) {
+ $this->addProviderTag($providerTag);
+ }
+
+ return $this;
+ }
+
+ public function addProviderTag(string $providerTag): self
+ {
+ $this->providerTags[] = $providerTag;
+
+ return $this;
+ }
+
+ public function getProviderBranch(): ?string
+ {
+ return $this->providerBranch;
+ }
+
+ public function setProviderBranch(?string $providerBranch): self
+ {
+ $this->providerBranch = $providerBranch;
+
+ return $this;
+ }
+
+ public function getConsumerVersionSelectors(): ConsumerVersionSelectors
+ {
+ return $this->consumerVersionSelectors;
+ }
+
+ public function setConsumerVersionSelectors(ConsumerVersionSelectors $selectors): self
+ {
+ $this->consumerVersionSelectors = $selectors;
+
+ return $this;
+ }
+
+ public function getConsumerVersionTags(): array
+ {
+ return $this->consumerVersionTags;
+ }
+
+ public function setConsumerVersionTags(array $consumerVersionTags): self
+ {
+ $this->consumerVersionTags = [];
+ foreach ($consumerVersionTags as $consumerVersionTag) {
+ $this->addConsumerVersionTag($consumerVersionTag);
+ }
+
+ return $this;
+ }
+
+ public function addConsumerVersionTag(string $consumerVersionTag): self
+ {
+ $this->consumerVersionTags[] = $consumerVersionTag;
+
+ 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..45419b23
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Source/BrokerInterface.php
@@ -0,0 +1,52 @@
+
+ */
+ public function getProviderTags(): array;
+
+ /**
+ * @param array $providerTags
+ */
+ public function setProviderTags(array $providerTags): self;
+
+ public function addProviderTag(string $providerTag): self;
+
+ public function getProviderBranch(): ?string;
+
+ public function setProviderBranch(?string $providerBranch): self;
+
+ public function getConsumerVersionSelectors(): ConsumerVersionSelectors;
+
+ public function setConsumerVersionSelectors(ConsumerVersionSelectors $selectors): self;
+
+ /**
+ * @return array
+ */
+ public function getConsumerVersionTags(): array;
+
+ /**
+ * @param array $consumerVersionTags
+ */
+ public function setConsumerVersionTags(array $consumerVersionTags): self;
+
+ public function addConsumerVersionTag(string $consumerVersionTag): self;
+}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Model/Source/Url.php b/src/PhpPact/Standalone/ProviderVerifier/Model/Source/Url.php
new file mode 100644
index 00000000..eb3dd1fb
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Source/Url.php
@@ -0,0 +1,61 @@
+url;
+ }
+
+ public function setUrl(UriInterface $url): self
+ {
+ $this->url = $url;
+
+ return $this;
+ }
+
+ public function getToken(): ?string
+ {
+ return $this->token;
+ }
+
+ public function setToken(?string $token): self
+ {
+ $this->token = $token;
+
+ return $this;
+ }
+
+ public function getUsername(): ?string
+ {
+ return $this->username;
+ }
+
+ public function setUsername(string $username): self
+ {
+ $this->username = $username;
+
+ return $this;
+ }
+
+ public function getPassword(): ?string
+ {
+ return $this->password;
+ }
+
+ public function setPassword(string $password): self
+ {
+ $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..cfc8effa
--- /dev/null
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/Source/UrlInterface.php
@@ -0,0 +1,24 @@
+
- */
- private array $providerVersionTag = [];
-
- private bool $publishResults = false;
-
- private ?UriInterface $brokerUri = null;
-
- private ?string $brokerToken = null;
-
- private ?string $brokerUsername = null;
-
- private ?string $brokerPassword = null;
+ private CallingAppInterface $callingApp;
+ private ProviderInfoInterface $providerInfo;
/**
- * @var array
+ * @var array
*/
- private array $customProviderHeaders = [];
-
- private bool $verbose = false;
-
- private ?string $logDirectory = null;
-
- private ?string $format = null;
-
- private int $processTimeout = 60;
-
- private int $processIdleTimeout = 10;
+ private array $providerTransports = [];
- private bool $enablePending = false;
-
- private ?string $wipPactSince = null;
-
- /**
- * @var array
- */
- private array $consumerVersionTag = [];
-
- private ConsumerVersionSelectors $consumerVersionSelectors;
-
- /** @var null|callable */
- private $requestFilter = null;
+ private FilterInfoInterface $filterInfo;
+ private ProviderStateInterface $providerState;
+ private VerificationOptionsInterface $verificationOptions;
+ private ?PublishOptionsInterface $publishOptions = null;
+ private ConsumerFiltersInterface $consumerFilters;
public function __construct()
{
- $this->consumerVersionSelectors = new ConsumerVersionSelectors();
- }
-
- /**
- * {@inheritdoc}
- */
- public function getProviderBaseUrl(): ?UriInterface
- {
- return $this->providerBaseUrl;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setProviderBaseUrl(UriInterface $providerBaseUrl): VerifierConfigInterface
- {
- $this->providerBaseUrl = $providerBaseUrl;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getProviderStatesSetupUrl(): ?string
- {
- return $this->providerStatesSetupUrl;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setProviderStatesSetupUrl(string $providerStatesSetupUrl): VerifierConfigInterface
- {
- $this->providerStatesSetupUrl = $providerStatesSetupUrl;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getProviderName(): ?string
- {
- return $this->providerName;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setProviderName(string $providerName): VerifierConfigInterface
- {
- $this->providerName = $providerName;
-
- 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 getProviderVersionTag(): array
- {
- return $this->providerVersionTag;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setProviderVersionTag(string $providerVersionTag): VerifierConfigInterface
- {
- return $this->addProviderVersionTag($providerVersionTag);
- }
-
- /**
- * {@inheritdoc}
- */
- public function getConsumerVersionTag(): array
- {
- 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;
- }
-
- 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;
+ $this->callingApp = new CallingApp();
+ $this->providerInfo = new ProviderInfo();
+ $this->filterInfo = new FilterInfo();
+ $this->providerState = new ProviderState();
+ $this->verificationOptions = new VerificationOptions();
+ $this->consumerFilters = new ConsumerFilters();
}
- /**
- * {@inheritdoc}
- */
- public function setPublishResults(bool $publishResults): VerifierConfigInterface
+ public function setCallingApp(CallingAppInterface $callingApp): self
{
- $this->publishResults = $publishResults;
+ $this->callingApp = $callingApp;
return $this;
}
- /**
- * {@inheritdoc}
- */
- public function getBrokerUri(): ?UriInterface
+ public function getCallingApp(): CallingAppInterface
{
- return $this->brokerUri;
+ return $this->callingApp;
}
- /**
- * {@inheritdoc}
- */
- public function setBrokerUri(UriInterface $brokerUri): VerifierConfigInterface
+ public function setProviderInfo(ProviderInfoInterface $providerInfo): self
{
- $this->brokerUri = $brokerUri;
+ $this->providerInfo = $providerInfo;
return $this;
}
- /**
- * {@inheritdoc}}
- */
- public function getBrokerToken(): ?string
+ public function getProviderInfo(): ProviderInfoInterface
{
- return $this->brokerToken;
- }
-
- /**
- * {@inheritdoc }
- */
- public function setBrokerToken(?string $brokerToken): VerifierConfigInterface
- {
- $this->brokerToken = $brokerToken;
-
- return $this;
+ return $this->providerInfo;
}
/**
* {@inheritdoc}
*/
- public function getBrokerUsername(): ?string
+ public function setProviderTransports(array $providerTransports): self
{
- return $this->brokerUsername;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setBrokerUsername(string $brokerUsername): VerifierConfigInterface
- {
- $this->brokerUsername = $brokerUsername;
+ $this->providerTransports = [];
+ foreach ($providerTransports as $providerTransport) {
+ $this->addProviderTransport($providerTransport);
+ }
return $this;
}
- /**
- * {@inheritdoc}
- */
- public function getBrokerPassword(): ?string
+ public function addProviderTransport(ProviderTransportInterface $providerTransport): self
{
- return $this->brokerPassword;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setBrokerPassword(string $brokerPassword): self
- {
- $this->brokerPassword = $brokerPassword;
+ $this->providerTransports[] = $providerTransport;
return $this;
}
@@ -294,169 +95,73 @@ public function setBrokerPassword(string $brokerPassword): self
/**
* {@inheritdoc}
*/
- public function getCustomProviderHeaders(): array
- {
- return $this->customProviderHeaders;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setCustomProviderHeaders(array $customProviderHeaders): VerifierConfigInterface
+ public function getProviderTransports(): array
{
- $this->customProviderHeaders = $customProviderHeaders;
-
- return $this;
+ return $this->providerTransports;
}
- public function addCustomProviderHeader(string $name, string $value): VerifierConfigInterface
+ public function setFilterInfo(FilterInfoInterface $filterInfo): self
{
- $this->customProviderHeaders[] = "$name: $value";
+ $this->filterInfo = $filterInfo;
return $this;
}
- /**
- * {@inheritdoc}
- */
- public function isVerbose(): bool
+ public function getFilterInfo(): FilterInfoInterface
{
- return $this->verbose;
+ return $this->filterInfo;
}
- /**
- * {@inheritdoc}
- */
- public function setVerbose(bool $verbose): VerifierConfigInterface
+ public function setProviderState(ProviderStateInterface $providerState): self
{
- $this->verbose = $verbose;
+ $this->providerState = $providerState;
return $this;
}
- /**
- * {@inheritdoc}
- */
- public function getLogDirectory(): ?string
+ public function getProviderState(): ProviderStateInterface
{
- return $this->logDirectory;
+ return $this->providerState;
}
- /**
- * {@inheritdoc}
- */
- public function setLogDirectory(string $log): VerifierConfigInterface
- {
- $this->logDirectory = $log;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFormat(): ?string
- {
- return $this->format;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setFormat(string $format): VerifierConfigInterface
+ public function setPublishOptions(?PublishOptionsInterface $publishOptions): self
{
- $this->format = $format;
+ $this->publishOptions = $publishOptions;
return $this;
}
- public function setProcessTimeout(int $timeout): VerifierConfigInterface
- {
- $this->processTimeout = $timeout;
-
- return $this;
- }
-
- public function setProcessIdleTimeout(int $timeout): VerifierConfigInterface
- {
- $this->processIdleTimeout = $timeout;
-
- return $this;
- }
-
- public function getProcessTimeout(): int
- {
- return $this->processTimeout;
- }
-
- public function getProcessIdleTimeout(): int
- {
- return $this->processIdleTimeout;
- }
-
- /**
- * {@inheritdoc}
- */
- public function isEnablePending(): bool
+ public function getPublishOptions(): ?PublishOptionsInterface
{
- return $this->enablePending;
+ return $this->publishOptions;
}
- /**
- * {@inheritdoc}
- */
- public function setEnablePending(bool $pending): VerifierConfigInterface
+ public function isPublishResults(): bool
{
- $this->enablePending = $pending;
-
- return $this;
+ return $this->publishOptions !== null;
}
- /**
- * {@inheritdoc}
- */
- public function setIncludeWipPactSince(string $date): VerifierConfigInterface
+ public function setConsumerFilters(ConsumerFiltersInterface $consumerFilters): self
{
- $this->wipPactSince = $date;
+ $this->consumerFilters = $consumerFilters;
return $this;
}
- /**
- * {@inheritdoc}
- */
- public function getIncludeWipPactSince(): ?string
- {
- return $this->wipPactSince;
- }
-
- public function getRequestFilter(): ?callable
+ public function getConsumerFilters(): ConsumerFiltersInterface
{
- return $this->requestFilter;
+ return $this->consumerFilters;
}
- public function setRequestFilter(callable $requestFilter): VerifierConfigInterface
+ public function setVerificationOptions(VerificationOptionsInterface $verificationOptions): self
{
- $this->requestFilter = $requestFilter;
+ $this->verificationOptions = $verificationOptions;
return $this;
}
- /**
- * {@inheritdoc}
- */
- public function setProviderBranch(string $providerBranch): VerifierConfigInterface
- {
- $this->providerBranch = $providerBranch;
-
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getProviderBranch(): ?string
+ public function getVerificationOptions(): VerificationOptionsInterface
{
- return $this->providerBranch;
+ return $this->verificationOptions;
}
}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/Model/VerifierConfigInterface.php b/src/PhpPact/Standalone/ProviderVerifier/Model/VerifierConfigInterface.php
index 9b748f9e..6f13cb83 100644
--- a/src/PhpPact/Standalone/ProviderVerifier/Model/VerifierConfigInterface.php
+++ b/src/PhpPact/Standalone/ProviderVerifier/Model/VerifierConfigInterface.php
@@ -2,223 +2,64 @@
namespace PhpPact\Standalone\ProviderVerifier\Model;
-use Psr\Http\Message\UriInterface;
+use PhpPact\Standalone\ProviderVerifier\Model\Config\CallingAppInterface;
+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\ProviderTransportInterface;
+use PhpPact\Standalone\ProviderVerifier\Model\Config\PublishOptionsInterface;
+use PhpPact\Standalone\ProviderVerifier\Model\Config\VerificationOptionsInterface;
-/**
- * Configuration to use with the verifier server.
- */
interface VerifierConfigInterface
{
- /**
- * @return null|UriInterface providers base url
- */
- public function getProviderBaseUrl(): ?UriInterface;
+ public function setCallingApp(CallingAppInterface $callingApp): self;
- /**
- * @param UriInterface $providerBaseUrl providers base url
- */
- public function setProviderBaseUrl(UriInterface $providerBaseUrl): self;
+ public function getCallingApp(): CallingAppInterface;
- /**
- * @return null|string Base URL to setup the provider states at
- */
- public function getProviderStatesSetupUrl(): ?string;
+ public function setProviderInfo(ProviderInfoInterface $providerInfo): self;
- /**
- * @param string $providerStatesSetupUrl Base URL to setup the provider states at
- */
- public function setProviderStatesSetupUrl(string $providerStatesSetupUrl): self;
+ public function getProviderInfo(): ProviderInfoInterface;
/**
- * @return null|string name of the provider
+ * @param array $providerTransports
*/
- public function getProviderName(): ?string;
+ public function setProviderTransports(array $providerTransports): self;
- /**
- * @param string $providerName Name of the provider
- */
- public function setProviderName(string $providerName): self;
+ public function addProviderTransport(ProviderTransportInterface $providerTransport): self;
/**
- * @return null|string providers version
+ * @return array
*/
- public function getProviderVersion(): ?string;
+ public function getProviderTransports(): array;
- /**
- * @param string $providerVersion providers version
- */
- public function setProviderVersion(string $providerVersion): self;
+ public function setFilterInfo(FilterInfoInterface $filterInfo): self;
- /**
- * @param string $providerBranch providers branch name
- */
- public function setProviderBranch(string $providerBranch): self;
-
- /**
- * @return array providers version tag
- */
- public function getProviderVersionTag(): array;
-
- /**
- * @return null|string providers branch name
- */
- public function getProviderBranch(): ?string;
-
- /**
- * @param string $providerVersionTag providers version tag
- */
- public function setProviderVersionTag(string $providerVersionTag): self;
-
- /**
- * @return array consumers version tag
- */
- public function getConsumerVersionTag(): array;
+ public function getFilterInfo(): FilterInfoInterface;
- /**
- * @param string $consumerVersionTag consumers version tag
- */
- public function addConsumerVersionTag(string $consumerVersionTag): self;
+ public function setProviderState(ProviderStateInterface $providerState): self;
- /**
- * @param string $providerVersionTag provider version tag
- */
- public function addProviderVersionTag(string $providerVersionTag): self;
+ public function getProviderState(): ProviderStateInterface;
- public function getConsumerVersionSelectors(): ConsumerVersionSelectors;
+ public function setPublishOptions(?PublishOptionsInterface $publishOptions): self;
- /**
- * @param ConsumerVersionSelectors $selectors Consumer version selectors
- */
- public function setConsumerVersionSelectors(ConsumerVersionSelectors $selectors): self;
+ public function getPublishOptions(): ?PublishOptionsInterface;
- /**
- * @return bool are results going to be published
- */
public function isPublishResults(): bool;
- /**
- * @param bool $publishResults flag to publish results
- */
- public function setPublishResults(bool $publishResults): self;
-
- /**
- * @return null|UriInterface url to the pact broker
- */
- public function getBrokerUri(): ?UriInterface;
-
- /**
- * @param UriInterface $brokerUri uri to the pact broker
- */
- 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
- */
- public function setBrokerToken(?string $brokerToken): self;
-
- /**
- * @return null|string username for the pact broker if secured
- */
- public function getBrokerUsername(): ?string;
-
- /**
- * @param string $brokerUsername username for the pact broker if secured
- */
- public function setBrokerUsername(string $brokerUsername): self;
-
- /**
- * @return null|string password for the pact broker if secured
- */
- public function getBrokerPassword(): ?string;
-
- /**
- * @param string $brokerPassword password for the pact broker if secured
- */
- public function setBrokerPassword(string $brokerPassword): self;
-
- /**
- * @return array custom headers for the request to the provider such as authorization
- */
- public function getCustomProviderHeaders(): array;
-
- /**
- * @param array $customProviderHeaders custom headers for the requests to the provider such as authorization
- */
- public function setCustomProviderHeaders(array $customProviderHeaders): self;
-
- public function addCustomProviderHeader(string $name, string $value): self;
-
- /**
- * @return bool is verbosity level increased
- */
- public function isVerbose(): bool;
-
- /**
- * @param bool $verbose increase verbosity level
- */
- public function setVerbose(bool $verbose): self;
-
- /**
- * @return null|string set the directory for the pact.log file
- */
- public function getLogDirectory(): ?string;
-
- /**
- * @param string $log set the directory for the pact.log file
- */
- public function setLogDirectory(string $log): self;
+ public function setConsumerFilters(ConsumerFiltersInterface $consumerFilters): self;
- /**
- * @return null|string RSpec formatter. Defaults to custom Pact formatter. json and RspecJunitFormatter may also be used
- */
- public function getFormat(): ?string;
+ public function getConsumerFilters(): ConsumerFiltersInterface;
- /**
- * @param string $format RSpec formatter. Defaults to custom Pact formatter. json and RspecJunitFormatter may also be used
- */
- public function setFormat(string $format): self;
-
- public function setProcessTimeout(int $timeout): self;
+ public function setVerificationOptions(VerificationOptionsInterface $verificationOptions): self;
- public function setProcessIdleTimeout(int $timeout): self;
+ public function getVerificationOptions(): VerificationOptionsInterface;
- public function getProcessTimeout(): int;
+ public function getLogLevel(): ?string;
- public function getProcessIdleTimeout(): int;
+ public function setLogLevel(string $logLevel): self;
- /**
- * @param bool $pending allow pacts which are in pending state to be verified without causing the overall task to fail
- */
- public function setEnablePending(bool $pending): self;
+ public function getPluginDir(): ?string;
- /**
- * @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)
- */
- 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
- */
- public function setRequestFilter(callable $requestFilter): self;
+ public function setPluginDir(?string $pluginDir): self;
}
diff --git a/src/PhpPact/Standalone/ProviderVerifier/ProcessRunnerFactory.php b/src/PhpPact/Standalone/ProviderVerifier/ProcessRunnerFactory.php
deleted file mode 100644
index 257e44a6..00000000
--- a/src/PhpPact/Standalone/ProviderVerifier/ProcessRunnerFactory.php
+++ /dev/null
@@ -1,30 +0,0 @@
-providerVerifier = $providerVerifier ?: Scripts::getProviderVerifier();
- }
-
- /**
- * @param array $arguments
- */
- public function createRunner(array $arguments, LoggerInterface $logger = null): ProcessRunner
- {
- $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 2bc678b3..d5b6d900 100644
--- a/src/PhpPact/Standalone/ProviderVerifier/Verifier.php
+++ b/src/PhpPact/Standalone/ProviderVerifier/Verifier.php
@@ -2,264 +2,223 @@
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\CData;
+use PhpPact\FFI\Client;
+use PhpPact\FFI\ClientInterface;
+use PhpPact\FFI\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 Verifier
{
- protected int $processTimeout = 60;
+ protected ClientInterface $client;
+ protected CData $handle;
- protected int $processIdleTimeout = 10;
-
- protected VerifierConfigInterface $config;
-
- protected ?BrokerHttpClientInterface $brokerHttpClient = null;
-
- protected ?VerifierProcess $verifierProcess = null;
-
- 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;
- }
+ public function __construct(VerifierConfigInterface $config)
+ {
+ $this->client = new Client();
+ $this
+ ->newHandle($config)
+ ->setProviderInfo($config)
+ ->setProviderTransports($config)
+ ->setFilterInfo($config)
+ ->setProviderState($config)
+ ->setVerificationOptions($config)
+ ->setPublishOptions($config)
+ ->setConsumerFilters($config)
+ ->setLogLevel($config)
+ ->setPluginDir($config);
}
- /**
- * @throws \Exception
- *
- * @return array parameters to be passed into the process
- */
- public function getArguments(): array
+ private function newHandle(VerifierConfigInterface $config): self
{
- $parameters = [];
-
- if ($this->config->getProviderName() !== null) {
- $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}";
- }
- }
+ $this->handle = $this->client->call(
+ 'pactffi_verifier_new_for_application',
+ $config->getCallingApp()->getName(),
+ $config->getCallingApp()->getVersion()
+ );
- if (\count($this->config->getConsumerVersionSelectors()) > 0) {
- foreach ($this->config->getConsumerVersionSelectors() as $selector) {
- $parameters[] = "--consumer-version-selector='{$selector}'";
- }
- }
+ return $this;
+ }
- if (\count($this->config->getProviderVersionTag()) > 0) {
- foreach ($this->config->getProviderVersionTag() as $tag) {
- $parameters[] = "--provider-version-tag={$tag}";
- }
- }
+ private function setProviderInfo(VerifierConfigInterface $config): self
+ {
+ $this->client->call(
+ 'pactffi_verifier_set_provider_info',
+ $this->handle,
+ $config->getProviderInfo()->getName(),
+ $config->getProviderInfo()->getScheme(),
+ $config->getProviderInfo()->getHost(),
+ $config->getProviderInfo()->getPort(),
+ $config->getProviderInfo()->getPath()
+ );
- if ($this->config->getProviderStatesSetupUrl() !== null) {
- $parameters[] = "--provider-states-setup-url={$this->config->getProviderStatesSetupUrl()}";
- }
+ return $this;
+ }
- if ($this->config->isPublishResults() === true) {
- $parameters[] = '--publish-verification-results';
+ private function setProviderTransports(VerifierConfigInterface $config): self
+ {
+ foreach ($config->getProviderTransports() as $transport) {
+ $this->client->call(
+ 'pactffi_verifier_add_provider_transport',
+ $this->handle,
+ $transport->getProtocol(),
+ $transport->getPort(),
+ $transport->getPath(),
+ $transport->getScheme()
+ );
}
- if ($this->config->getBrokerToken() !== null) {
- $parameters[] = "--broker-token={$this->config->getBrokerToken()}";
- }
+ return $this;
+ }
- if ($this->config->getBrokerUsername() !== null) {
- $parameters[] = "--broker-username={$this->config->getBrokerUsername()}";
- }
+ private function setFilterInfo(VerifierConfigInterface $config): self
+ {
+ $this->client->call(
+ 'pactffi_verifier_set_provider_state',
+ $this->handle,
+ $config->getProviderState()->getStateChangeUrl() ? (string) $config->getProviderState()->getStateChangeUrl() : null,
+ $config->getProviderState()->isStateChangeTeardown(),
+ $config->getProviderState()->isStateChangeAsBody()
+ );
- if ($this->config->getBrokerPassword() !== null) {
- $parameters[] = "--broker-password={$this->config->getBrokerPassword()}";
- }
+ return $this;
+ }
- if (count($this->config->getCustomProviderHeaders()) > 0) {
- foreach ($this->config->getCustomProviderHeaders() as $customProviderHeader) {
- $parameters[] = "--custom-provider-header=\"{$customProviderHeader}\"";
- }
- }
+ private function setProviderState(VerifierConfigInterface $config): self
+ {
+ $this->client->call(
+ 'pactffi_verifier_set_filter_info',
+ $this->handle,
+ $config->getFilterInfo()->getFilterDescription(),
+ $config->getFilterInfo()->getFilterState(),
+ $config->getFilterInfo()->getFilterNoState()
+ );
- if ($this->config->isVerbose() === true) {
- $parameters[] = '--verbose=VERBOSE';
- }
+ return $this;
+ }
- if ($this->config->getLogDirectory() !== null) {
- $parameters[] = "--log-dir={$this->config->getLogDirectory()}";
- }
+ private function setVerificationOptions(VerifierConfigInterface $config): self
+ {
+ $this->client->call(
+ 'pactffi_verifier_set_verification_options',
+ $this->handle,
+ $config->getVerificationOptions()->isDisableSslVerification(),
+ $config->getVerificationOptions()->getRequestTimeout()
+ );
- if ($this->config->getFormat() !== null) {
- $parameters[] = "--format={$this->config->getFormat()}";
- }
+ return $this;
+ }
- if ($this->config->isEnablePending() === true) {
- $parameters[] = '--enable-pending';
+ private function setPublishOptions(VerifierConfigInterface $config): self
+ {
+ if ($config->isPublishResults()) {
+ $providerTags = ArrayData::createFrom($config->getPublishOptions()->getProviderTags());
+ $this->client->call(
+ 'pactffi_verifier_set_publish_options',
+ $this->handle,
+ $config->getPublishOptions()->getProviderVersion(),
+ $config->getPublishOptions()->getBuildUrl(),
+ $providerTags?->getItems(),
+ $providerTags?->getSize(),
+ $config->getPublishOptions()->getProviderBranch()
+ );
}
- if ($this->config->getIncludeWipPactSince() !== null) {
- $parameters[] = "--include-wip-pacts-since={$this->config->getIncludeWipPactSince()}";
- }
+ return $this;
+ }
- if ($this->config->getBrokerUri() !== null) {
- $parameters[] = "--pact-broker-base-url={$this->config->getBrokerUri()->__toString()}";
- }
+ private function setConsumerFilters(VerifierConfigInterface $config): self
+ {
+ $filterConsumerNames = ArrayData::createFrom($config->getConsumerFilters()->getFilterConsumerNames());
+ $this->client->call(
+ 'pactffi_verifier_set_consumer_filters',
+ $this->handle,
+ $filterConsumerNames?->getItems(),
+ $filterConsumerNames?->getSize()
+ );
- return $parameters;
+ return $this;
}
- /**
- * 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
- * @throws \Exception
- */
- public function verify(string $consumerName, string $tag = null, string $consumerVersion = null): self
+ private function setLogLevel(VerifierConfigInterface $config): self
{
- $path = "/pacts/provider/{$this->config->getProviderName()}/consumer/{$consumerName}/";
-
- if ($tag) {
- $path .= "latest/{$tag}/";
- } elseif ($consumerVersion) {
- $path .= "version/{$consumerVersion}/";
- } else {
- $path .= 'latest/';
+ if ($logLevel = $config->getLogLevel()) {
+ $this->client->call('pactffi_init_with_log_level', $logLevel);
}
- $uri = $this->config->getBrokerUri()->withPath($path);
-
- $arguments = \array_merge([$uri->__toString()], $this->getArguments());
-
- $this->verifyAction($arguments);
-
return $this;
}
- /**
- * Provides a way to validate local Pact JSON files.
- *
- * @param array $files paths to pact json files
- * @throws \Exception
- */
- public function verifyFiles(array $files): self
+ private function setPluginDir(VerifierConfigInterface $config): self
{
- $arguments = \array_merge($files, $this->getArguments());
-
- $this->verifyAction($arguments);
+ if ($pluginDir = $config->getPluginDir()) {
+ \putenv("PACT_PLUGIN_DIR={$pluginDir}");
+ }
return $this;
}
- /**
- * Verify all Pacts from the Pact Broker are valid for the Provider.
- * @throws \Exception
- */
- public function verifyAll(): void
+ public function addFile(string $file): self
{
- $arguments = $this->getBrokerHttpClient()->getAllConsumerUrls($this->config->getProviderName());
+ $this->client->call('pactffi_verifier_add_file_source', $this->handle, $file);
- $arguments = \array_merge($arguments, $this->getArguments());
-
- $this->verifyAction($arguments);
+ return $this;
}
- /**
- * Verify all PACTs for a given tag.
- * @throws \Exception
- */
- public function verifyAllForTag(string $tag): void
+ public function addDirectory(string $directory): self
{
- $arguments = $this->getBrokerHttpClient()->getAllConsumerUrlsForTag($this->config->getProviderName(), $tag);
+ $this->client->call('pactffi_verifier_add_directory_source', $this->handle, $directory);
- $arguments = \array_merge($arguments, $this->getArguments());
-
- $this->verifyAction($arguments);
+ return $this;
}
- /**
- * Verify all PACTs that match the VerifierConfig
- * @throws \Exception
- */
- public function verifyFromConfig(): void
+ public function addUrl(UrlInterface $url): self
{
- $this->verifyAction($this->getArguments());
- }
+ $this->client->call(
+ 'pactffi_verifier_url_source',
+ $this->handle,
+ (string) $url->getUrl(),
+ $url->getUsername(),
+ $url->getPassword(),
+ $url->getToken()
+ );
- /**
- * @return array
- */
- public function getTimeoutValues(): array
- {
- return ['process_timeout' => $this->processTimeout, 'process_idle_timeout' => $this->processIdleTimeout];
+ return $this;
}
- /**
- * Trigger execution of the Pact Verifier Service.
- *
- * @param array $arguments
- * @throws \Exception
- */
- protected function verifyAction(array $arguments): void
+ public function addBroker(BrokerInterface $broker): self
{
- $this->verifierProcess->run($arguments, $this->processTimeout, $this->processIdleTimeout);
+ $providerTags = ArrayData::createFrom($broker->getProviderTags());
+ $consumerVersionSelectors = ArrayData::createFrom(iterator_to_array($broker->getConsumerVersionSelectors()));
+ $consumerVersionTags = ArrayData::createFrom($broker->getConsumerVersionTags());
+ $this->client->call(
+ '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()
+ );
+
+ return $this;
}
- protected function getBrokerHttpClient(): BrokerHttpClientInterface
+ public function verify(): bool
{
- 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->client->call('pactffi_verifier_execute', $this->handle);
+ $this->client->call('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 6201c53f..00000000
--- a/src/PhpPact/Standalone/ProviderVerifier/VerifierProcess.php
+++ /dev/null
@@ -1,57 +0,0 @@
-processRunnerFactory = $processRunnerFactory ?: new ProcessRunnerFactory();
- }
-
- public function setLogger(LoggerInterface $logger): self
- {
- $this->logger = $logger;
-
- return $this;
- }
-
- /**
- * @param array $arguments
- * @throws \Exception
- */
- public function run(array $arguments, ?int $processTimeout = null, ?int $processIdleTimeout = null): void
- {
- $logger = $this->getLogger();
- $processRunner = $this->processRunnerFactory->createRunner(
- $arguments,
- $logger
- );
-
- $logger->info("Verifying PACT with script:\n{$processRunner->getCommand()}\n\n");
-
- $processRunner->runBlocking();
- }
-
- private function getLogger(): LoggerInterface
- {
- 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/StubService/Service/StubServerHttpService.php b/src/PhpPact/Standalone/StubService/Service/StubServerHttpService.php
index 8aa9a849..f40100a1 100644
--- a/src/PhpPact/Standalone/StubService/Service/StubServerHttpService.php
+++ b/src/PhpPact/Standalone/StubService/Service/StubServerHttpService.php
@@ -2,7 +2,6 @@
namespace PhpPact\Standalone\StubService\Service;
-use PhpPact\Exception\ConnectionException;
use PhpPact\Http\ClientInterface;
use PhpPact\Standalone\StubService\StubServerConfigInterface;
@@ -23,37 +22,13 @@ public function __construct(ClientInterface $client, StubServerConfigInterface $
$this->config = $config;
}
- /**
- * {@inheritdoc}
- */
- public function healthCheck(): bool
- {
- $uri = $this->config->getBaseUri()->withPath('/');
-
- $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 Stub Server.');
- }
-
- return true;
- }
-
/**
* {@inheritdoc}
* @throws \JsonException
*/
- public function getJson(): string
+ public function getJson(string $endpoint): string
{
- $uri = $this->config->getBaseUri()->withPath('/' . $this->config->getEndpoint());
+ $uri = $this->config->getBaseUri()->withPath('/' . $endpoint);
$response = $this->client->get($uri, [
'headers' => [
'Content-Type' => 'application/json',
diff --git a/src/PhpPact/Standalone/StubService/Service/StubServerHttpServiceInterface.php b/src/PhpPact/Standalone/StubService/Service/StubServerHttpServiceInterface.php
index 307a9448..325637e2 100644
--- a/src/PhpPact/Standalone/StubService/Service/StubServerHttpServiceInterface.php
+++ b/src/PhpPact/Standalone/StubService/Service/StubServerHttpServiceInterface.php
@@ -2,19 +2,10 @@
namespace PhpPact\Standalone\StubService\Service;
-use PhpPact\Exception\ConnectionException;
-
interface StubServerHttpServiceInterface
{
- /**
- * Verify that the Ruby PhpPact Stub Server is running.
- *
- * @throws ConnectionException
- */
- public function healthCheck(): bool;
-
/**
* Get the current state of the PACT JSON file and write it to disk.
*/
- public function getJson(): string;
+ public function getJson(string $endpoint): string;
}
diff --git a/src/PhpPact/Standalone/StubService/StubServer.php b/src/PhpPact/Standalone/StubService/StubServer.php
index f19b4c67..4805770c 100644
--- a/src/PhpPact/Standalone/StubService/StubServer.php
+++ b/src/PhpPact/Standalone/StubService/StubServer.php
@@ -43,6 +43,8 @@ public function start(int $wait = 1): int
/**
* Stop the Stub Server process.
*
+ * @throws ProcessException
+ *
* @return bool Was stopping successful?
* @throws ProcessException
*/
@@ -60,12 +62,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 e12779a6..42d62315 100644
--- a/src/PhpPact/Standalone/StubService/StubServerConfig.php
+++ b/src/PhpPact/Standalone/StubService/StubServerConfig.php
@@ -10,55 +10,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;
+
/**
- * Host on which to bind the service.
+ * @var array
*/
- private string $host = 'localhost';
-
+ private array $dirs = [];
/**
- * Port on which to run the service.
+ * @var array
*/
- private int $port = 7201;
-
- private bool $secure = false;
-
+ private array $files = [];
/**
- * File to which to log output.
+ * @var array
*/
- private ?string $log = null;
-
- private string $pactLocation;
- private string $endpoint;
-
+ private array $urls = [];
/**
- * {@inheritdoc}
+ * @var array
*/
- public function getHost(): string
+ private array $consumerNames = [];
+ /**
+ * @var array
+ */
+ private array $providerNames = [];
+
+ private bool $cors = false;
+ private bool $corsReferer = false;
+ private bool $emptyProviderState = false;
+ private bool $insecureTls = false;
+
+ public function getBrokerUrl(): ?UriInterface
{
- return $this->host;
+ return $this->brokerUrl;
}
- /**
- * {@inheritdoc}
- */
- public function setHost(string $host): StubServerConfigInterface
+ public function setBrokerUrl(UriInterface $brokerUrl): StubServerConfigInterface
{
- $this->host = $host;
+ $this->brokerUrl = $brokerUrl;
return $this;
}
- /**
- * {@inheritdoc}
- */
- public function getPort(): int
+ public function setDirs(array $dirs): StubServerConfigInterface
+ {
+ $this->dirs = array_map(fn (string $dir) => $dir, $dirs);
+
+ return $this;
+ }
+
+ public function getDirs(): array
+ {
+ return $this->dirs;
+ }
+
+ public function getExtension(): ?string
+ {
+ return $this->extension;
+ }
+
+ public function setExtension(string $extension): StubServerConfigInterface
+ {
+ $this->extension = $extension;
+
+ return $this;
+ }
+
+ public function setFiles(array $files): StubServerConfigInterface
+ {
+ $this->files = array_map(fn (string $file) => $file, $files);
+
+ return $this;
+ }
+
+ public function getFiles(): array
+ {
+ return $this->files;
+ }
+
+ public function setLogLevel(string $logLevel): StubServerConfigInterface
+ {
+ $this->logLevel = $logLevel;
+
+ return $this;
+ }
+
+ public function getLogLevel(): ?string
+ {
+ return $this->logLevel;
+ }
+
+ public function getPort(): ?int
{
return $this->port;
}
- /**
- * {@inheritdoc}
- */
public function setPort(int $port): StubServerConfigInterface
{
$this->port = $port;
@@ -66,73 +118,140 @@ public function setPort(int $port): StubServerConfigInterface
return $this;
}
- /**
- * {@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;
}
- /**
- * {@inheritdoc}
- */
- public function getBaseUri(): UriInterface
+ public function getProviderStateHeaderName(): ?string
+ {
+ return $this->providerStateHeaderName;
+ }
+
+ 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(): ?string
+ public function getToken(): ?string
{
- return $this->log;
+ return $this->token;
}
- /**
- * {@inheritdoc}
- */
- public function setLog(string $log): StubServerConfigInterface
+ public function setToken(?string $token): StubServerConfigInterface
+ {
+ $this->token = $token;
+
+ return $this;
+ }
+
+ public function setUrls(array $urls): StubServerConfigInterface
+ {
+ $this->urls = array_map(fn (string $url) => $url, $urls);
+
+ return $this;
+ }
+
+ public function getUrls(): array
+ {
+ return $this->urls;
+ }
+
+ public function getUser(): ?string
+ {
+ return $this->user;
+ }
+
+ public function setUser(string $user): StubServerConfigInterface
+ {
+ $this->user = $user;
+
+ return $this;
+ }
+
+ public function isCors(): bool
+ {
+ return $this->cors;
+ }
+
+ public function setCors(bool $cors): StubServerConfigInterface
{
- $this->log = $log;
+ $this->cors = $cors;
return $this;
}
- public function getPactLocation(): string
+ public function isCorsReferer(): bool
{
- return $this->pactLocation;
+ return $this->corsReferer;
}
- public function setPactLocation(string $location): self
+ public function setCorsReferer(bool $corsReferer): StubServerConfigInterface
{
- $this->pactLocation = $location;
+ $this->corsReferer = $corsReferer;
return $this;
}
- public function getEndpoint(): string
+ public function isEmptyProviderState(): bool
{
- return $this->endpoint;
+ return $this->emptyProviderState;
}
- public function setEndpoint(string $endpoint): self
+ public function setEmptyProviderState(bool $emptyProviderState): StubServerConfigInterface
{
- $this->endpoint = $endpoint;
+ $this->emptyProviderState = $emptyProviderState;
return $this;
}
+
+ public function isInsecureTls(): bool
+ {
+ return $this->insecureTls;
+ }
+
+ public function setInsecureTls(bool $insecureTls): StubServerConfigInterface
+ {
+ $this->insecureTls = $insecureTls;
+
+ return $this;
+ }
+
+ public function setConsumerNames(array $consumerNames): StubServerConfigInterface
+ {
+ $this->consumerNames = array_map(fn (string $consumerName) => $consumerName, $consumerNames);
+
+ return $this;
+ }
+
+ public function getConsumerNames(): array
+ {
+ return $this->consumerNames;
+ }
+
+ public function setProviderNames(array $providerNames): StubServerConfigInterface
+ {
+ $this->providerNames = array_map(fn (string $providerName) => $providerName, $providerNames);
+
+ return $this;
+ }
+
+ public function getProviderNames(): array
+ {
+ return $this->providerNames;
+ }
+
+ public function getBaseUri(): UriInterface
+ {
+ return new Uri("http://localhost:{$this->getPort()}");
+ }
}
diff --git a/src/PhpPact/Standalone/StubService/StubServerConfigInterface.php b/src/PhpPact/Standalone/StubService/StubServerConfigInterface.php
index 7f232923..25d0decb 100644
--- a/src/PhpPact/Standalone/StubService/StubServerConfigInterface.php
+++ b/src/PhpPact/Standalone/StubService/StubServerConfigInterface.php
@@ -10,55 +10,144 @@
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
+ * @param UriInterface $brokerUrl URL of the pact broker to fetch pacts from
*/
- public function setHost(string $host): self;
+ public function setBrokerUrl(UriInterface $brokerUrl): self;
/**
- * @return int the port of the stub service
+ * @param array $dirs Directory of pact files to load
*/
- public function getPort(): int;
+ public function setDirs(array $dirs): self;
/**
- * @param int $port the port of the stub service
+ * @return array
+ */
+ public function getDirs(): array;
+
+ public function getExtension(): ?string;
+
+ /**
+ * @param string $extension File extension to use when loading from a directory (default is json)
+ */
+ public function setExtension(string $extension): self;
+
+ /**
+ * @param array $files Pact file to load
+ */
+ public function setFiles(array $files): self;
+
+ /**
+ * @return array
+ */
+ public function getFiles(): array;
+
+ public function getLogLevel(): ?string;
+
+ /**
+ * @param string $logLevel Log level (defaults to info) [possible values: error, warn, info, debug, trace, none]
+ */
+ 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)
*/
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
*/
- 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
+ */
+ 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
*/
- public function getLog(): ?string;
+ public function setToken(?string $token): self;
/**
- * @param string $log directory for log output
+ * @param array $urls URL of pact file to fetch
*/
- public function setLog(string $log): self;
+ public function setUrls(array $urls): self;
- public function getPactLocation(): string;
+ /**
+ * @return array
+ */
+ public function getUrls(): array;
+
+ /**
+ * @return null|string user and password
+ */
+ public function getUser(): ?string;
+
+ /**
+ * @param string $user User and password to use when fetching pacts from URLS or Pact Broker in user:password form
+ */
+ public function setUser(string $user): self;
+
+ public function isCors(): bool;
+
+ public function setCors(bool $cors): self;
+
+ public function isCorsReferer(): bool;
+
+ public function setCorsReferer(bool $corsReferer): self;
+
+ public function isEmptyProviderState(): bool;
+
+ public function setEmptyProviderState(bool $emptyProviderState): self;
- public function setPactLocation(string $location): self;
+ public function isInsecureTls(): bool;
- public function getEndpoint(): string;
+ public function setInsecureTls(bool $insecureTls): self;
- public function setEndpoint(string $endpoint): self;
+ /**
+ * @param array $consumerNames Consumer name to use to filter the Pacts fetched from the Pact broker
+ */
+ public function setConsumerNames(array $consumerNames): self;
+
+ /**
+ * @return array
+ */
+ public function getConsumerNames(): array;
+
+ /**
+ * @param array $providerNames Provider name to use to filter the Pacts fetched from the Pact broker
+ */
+ public function setProviderNames(array $providerNames): self;
+
+ /**
+ * @return array
+ */
+ public function getProviderNames(): array;
+
+ public function getBaseUri(): UriInterface;
}
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/Config/PactConfigTest.php b/tests/PhpPact/Config/PactConfigTest.php
new file mode 100644
index 00000000..105586d3
--- /dev/null
+++ b/tests/PhpPact/Config/PactConfigTest.php
@@ -0,0 +1,66 @@
+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 testInvalidPactSpecificationVersion(): void
+ {
+ $this->expectException(\UnexpectedValueException::class);
+ $this->expectExceptionMessage('Invalid version string "invalid"');
+ $this->config->setPactSpecificationVersion('invalid');
+ }
+
+ public function testInvalidLogLevel(): void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('LogLevel VERBOSE not supported.');
+ $this->config->setLogLevel('VERBOSE');
+ }
+
+ 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/Consumer/InteractionBuilderTest.php b/tests/PhpPact/Consumer/InteractionBuilderTest.php
index b9bf75b3..6d97ccbf 100644
--- a/tests/PhpPact/Consumer/InteractionBuilderTest.php
+++ b/tests/PhpPact/Consumer/InteractionBuilderTest.php
@@ -6,37 +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
{
- private MockServerHttpServiceInterface $service;
-
- private MockServer $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
@@ -61,13 +36,15 @@ public function testSimpleGet()
->addHeader('Content-Type', 'application/json');
$builder = new InteractionBuilder(new MockServerEnvConfig());
- $result = $builder
- ->given('A test request.')
+ $builder
+ ->given('A test request.', ['key' => 'value'])
->uponReceiving('A test response.')
->with($request)
->willRespondWith($response);
- $this->assertTrue($result);
+ $verifyResult = $builder->verify();
+
+ $this->assertFalse($verifyResult);
}
/**
@@ -99,13 +76,15 @@ public function testPostWithBody()
]);
$builder = new InteractionBuilder(new MockServerEnvConfig());
- $result = $builder
- ->given('A test request.')
+ $builder
+ ->given('A test request.', ['key' => 'value'])
->uponReceiving('A test response.')
->with($request)
->willRespondWith($response);
- $this->assertTrue($result);
+ $verifyResult = $builder->verify();
+
+ $this->assertFalse($verifyResult);
}
/**
@@ -133,12 +112,14 @@ public function testBuildWithEachLikeMatcher()
]);
$builder = new InteractionBuilder(new MockServerEnvConfig());
- $result = $builder
- ->given('A test request.')
+ $builder
+ ->given('A test request.', ['key' => 'value'])
->uponReceiving('A test response.')
->with($request)
->willRespondWith($response);
- $this->assertTrue($result);
+ $verifyResult = $builder->verify();
+
+ $this->assertFalse($verifyResult);
}
}
diff --git a/tests/PhpPact/Consumer/Matcher/MatcherTest.php b/tests/PhpPact/Consumer/Matcher/MatcherTest.php
index 8b72a7ac..6ad9025a 100644
--- a/tests/PhpPact/Consumer/Matcher/MatcherTest.php
+++ b/tests/PhpPact/Consumer/Matcher/MatcherTest.php
@@ -31,7 +31,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);
}
/**
@@ -44,15 +44,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));
@@ -71,15 +73,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));
@@ -102,15 +106,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');
@@ -124,15 +122,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');
@@ -148,15 +140,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);
@@ -188,15 +174,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);
@@ -226,15 +206,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);
@@ -262,15 +236,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');
@@ -285,7 +253,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);
}
/**
@@ -295,7 +263,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);
}
/**
@@ -305,7 +273,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);
}
/**
@@ -314,15 +282,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());
@@ -334,15 +296,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());
@@ -354,15 +310,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());
@@ -374,15 +324,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());
@@ -394,17 +338,10 @@ public function testIpv6Address()
*/
public function testEmail()
{
-
$expected = [
- 'data' => [
- 'generate' => 'hello@pact.io',
- 'matcher' => [
- 'json_class' => 'Regexp',
- 'o' => 0,
- 's' => '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$',
- ],
- ],
- 'json_class' => 'Pact::Term',
+ 'value' => 'hello@pact.io',
+ 'regex' => '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$',
+ 'pact:matcher:type' => 'regex',
];
$this->assertEquals($expected, $this->matcher->email());
}
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..165ed8a8
--- /dev/null
+++ b/tests/PhpPact/Consumer/Model/MessageTest.php
@@ -0,0 +1,34 @@
+ 'bar'];
+ $metadata = ['queue' => 'foo', 'routing_key' => 'bar'];
+ $contents = 'test';
+
+ $subject = (new Message())
+ ->setDescription($description)
+ ->addProviderState($providerStateName, $providerStateParams)
+ ->setMetadata($metadata)
+ ->setContents($contents);
+
+ static::assertSame($description, $subject->getDescription());
+ $providerStates = $subject->getProviderStates();
+ static::assertCount(1, $providerStates);
+ static::assertContainsOnlyInstancesOf(ProviderState::class, $providerStates);
+ static::assertEquals($providerStateName, $providerStates[0]->getName());
+ static::assertEquals($providerStateParams, $providerStates[0]->getParams());
+ 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/FFI/Model/ArrayDataTest.php b/tests/PhpPact/FFI/Model/ArrayDataTest.php
new file mode 100644
index 00000000..b55a5f65
--- /dev/null
+++ b/tests/PhpPact/FFI/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/Broker/BrokerConfigTest.php b/tests/PhpPact/Standalone/Broker/BrokerConfigTest.php
index c5b2bc5c..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 30a43b0b..b85bca85 100644
--- a/tests/PhpPact/Standalone/MockServer/MockServerConfigTest.php
+++ b/tests/PhpPact/Standalone/MockServer/MockServerConfigTest.php
@@ -16,7 +16,6 @@ public function testSetters()
$pactDir = 'test-pact-dir/';
$pactFileWriteMode = 'merge';
$log = 'test-log-dir/';
- $cors = true;
$pactSpecificationVersion = '2.0';
$subject = (new MockServerConfig())
@@ -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 c389dad7..00000000
--- a/tests/PhpPact/Standalone/MockServer/Service/MockServerHttpServiceTest.php
+++ /dev/null
@@ -1,209 +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(): void
- {
- $result = $this->service->healthCheck();
- $this->assertTrue($result);
- }
-
- public function testRegisterInteraction(): void
- {
- $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(): void
- {
- $result = $this->service->deleteAllInteractions();
- $this->assertTrue($result);
- }
-
- public function testVerifyInteractions(): void
- {
- $result = $this->service->verifyInteractions();
- $this->assertTrue($result);
- }
-
- public function testVerifyInteractionsFailure(): void
- {
- $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(): void
- {
- $result = $this->service->getPactJson();
- $this->assertEquals('{"consumer":{"name":"someConsumer"},"provider":{"name":"someProvider"},"interactions":[],"metadata":{"pactSpecification":{"version":"2.0.0"}}}', $result);
- }
-
- public function testFullGetInteraction(): void
- {
- $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(): void
- {
- $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/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..48446b0a
--- /dev/null
+++ b/tests/PhpPact/Standalone/ProviderVerifier/Model/VerifierConfigTest.php
@@ -0,0 +1,88 @@
+getProviderInfo()
+ ->setName($providerName)
+ ->setScheme($scheme)
+ ->setHost($host)
+ ->setPort($port)
+ ->setPath($basePath);
+ $subject->getFilterInfo()
+ ->setFilterDescription($filterDescription)
+ ->setFilterNoState($filterNoState)
+ ->setFilterState($filterState);
+ $subject->getProviderState()
+ ->setStateChangeUrl($stateChangeUrl)
+ ->setStateChangeAsBody($stateChangeAsBody)
+ ->setStateChangeTeardown($stateChangeTeardown);
+ $subject->getVerificationOptions()
+ ->setRequestTimeout($requestTimeout)
+ ->setDisableSslVerification($disableSslVerification);
+ $publishOptions = new PublishOptions();
+ $publishOptions
+ ->setProviderTags($providerTags)
+ ->setProviderVersion($providerVersion)
+ ->setBuildUrl($buildUrl)
+ ->setProviderBranch($providerBranch);
+ $subject->setPublishOptions($publishOptions);
+ $subject->getConsumerFilters()
+ ->setFilterConsumerNames($filterConsumerNames);
+
+ $providerInfo = $subject->getProviderInfo();
+ static::assertSame($providerName, $providerInfo->getName());
+ static::assertSame($scheme, $providerInfo->getScheme());
+ static::assertSame($host, $providerInfo->getHost());
+ static::assertSame($port, $providerInfo->getPort());
+ static::assertSame($basePath, $providerInfo->getPath());
+ $filterInfo = $subject->getFilterInfo();
+ static::assertSame($filterDescription, $filterInfo->getFilterDescription());
+ static::assertSame($filterNoState, $filterInfo->getFilterNoState());
+ static::assertSame($filterState, $filterInfo->getFilterState());
+ $providerState = $subject->getProviderState();
+ static::assertSame($stateChangeUrl, $providerState->getStateChangeUrl());
+ static::assertSame($stateChangeAsBody, $providerState->isStateChangeAsBody());
+ static::assertSame($stateChangeTeardown, $providerState->isStateChangeTeardown());
+ $verificationOptions = $subject->getVerificationOptions();
+ static::assertSame($requestTimeout, $verificationOptions->getRequestTimeout());
+ static::assertSame($disableSslVerification, $verificationOptions->isDisableSslVerification());
+ static::assertSame($publishResults, $subject->isPublishResults());
+ $publishOptions = $subject->getPublishOptions();
+ static::assertSame($providerTags, $publishOptions->getProviderTags());
+ static::assertSame($providerVersion, $publishOptions->getProviderVersion());
+ static::assertSame($buildUrl, $publishOptions->getBuildUrl());
+ static::assertSame($providerBranch, $publishOptions->getProviderBranch());
+ $consumerFilters = $subject->getConsumerFilters();
+ static::assertSame($filterConsumerNames, $consumerFilters->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..c6cf11f2 100644
--- a/tests/PhpPact/Standalone/ProviderVerifier/VerifierTest.php
+++ b/tests/PhpPact/Standalone/ProviderVerifier/VerifierTest.php
@@ -2,235 +2,55 @@
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 PhpPact\Standalone\Runner\ProcessRunner;
use PHPUnit\Framework\TestCase;
-use Psr\Http\Message\RequestInterface;
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 ProcessRunner */
+ private ProcessRunner $processRunner;
/**
- * 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);
-
- $verifier = new Verifier($config, $verifierProcessMock);
+ $publicPath = __DIR__ . '/../../../_public/';
- $verifier->verify($consumerName, $tag, $version);
- }
-
- public function dataProviderForBrokerPathTest()
- {
- $consumerName = 'someProviderName';
- $providerName = 'someProviderName';
- $tag = '1.0.0';
- $version = '11111';
+ $this->processRunner = new ProcessRunner('php', ['-S', 'localhost:7202', '-t', $publicPath]);
- 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->processRunner->run();
+ \usleep(300000); // wait for server to start
}
/**
- * @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)
+ protected function tearDown(): void
{
- $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();
+ $this->processRunner->stop();
}
- public function provideDataForVerifyAll()
+ public function testVerify(): void
{
- return [
- ['someProvider', '1.0.0'],
- ['someProvider', '1.2.3'],
- ];
- }
-
- public function testRunShouldLogOutputIfCmdFails()
- {
- 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;
+ $config = new VerifierConfig();
+ $config->getProviderInfo()
+ ->setName('someProvider')
+ ->setHost('localhost')
+ ->setPort(7202)
+ ->setScheme('http')
+ ->setPath('/');
+ if ($level = \getenv('PACT_LOGLEVEL')) {
+ $config->setLogLevel($level);
}
- $logMessages = $handler->getRecords();
+ $verifier = new Verifier($config);
+ $verifier->addDirectory(__DIR__ . '/../../../_resources');
- $this->assertGreaterThan(2, \count($logMessages));
- $this->assertStringContainsString('first line', $logMessages[\count($logMessages) - 2]['message']);
- $this->assertStringContainsString('second line', $logMessages[\count($logMessages) - 1]['message']);
+ $verifyResult = $verifier->verify();
- $this->assertNotNull($exception);
+ $this->assertTrue($verifyResult);
}
}
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/StubServer/Service/StubServerHttpServiceTest.php b/tests/PhpPact/Standalone/StubServer/Service/StubServerHttpServiceTest.php
index 2990bd58..734e978c 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
@@ -28,19 +28,15 @@ class StubServerHttpServiceTest extends TestCase
*/
protected function setUp(): void
{
- $pactLocation = __DIR__ . '/../../../../_resources/someconsumer-someprovider.json';
- $host = 'localhost';
- $port = 7201;
- $endpoint = 'test';
+ $files = [__DIR__ . '/../../../../_resources/someconsumer-someprovider.json'];
+ $port = 7201;
$this->config = (new StubServerConfig())
- ->setPactLocation($pactLocation)
- ->setHost($host)
- ->setPort($port)
- ->setEndpoint($endpoint);
+ ->setFiles($files)
+ ->setPort($port);
$this->stubServer = new StubServer($this->config);
- $this->stubServer->start(10);
+ $this->stubServer->start();
$this->service = new StubServerHttpService(new GuzzleClient(), $this->config);
}
@@ -49,15 +45,10 @@ protected function tearDown(): void
$this->stubServer->stop();
}
- public function testHealthCheck()
- {
- $result = $this->service->healthCheck();
- $this->assertTrue($result);
- }
-
public function testGetJson()
{
- $result = $this->service->getJson();
+ $endpoint = 'test';
+ $result = $this->service->getJson($endpoint);
$this->assertEquals('{"results":[{"name":"Games"}]}', $result);
}
}
diff --git a/tests/PhpPact/Standalone/StubServer/StubServerConfigTest.php b/tests/PhpPact/Standalone/StubServer/StubServerConfigTest.php
index db9aed12..6ae7c400 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';
+ $dirs = [__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($dirs)
+ ->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($dirs, $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..4ea6cb51 100644
--- a/tests/PhpPact/Standalone/StubServer/StubServerTest.php
+++ b/tests/PhpPact/Standalone/StubServer/StubServerTest.php
@@ -14,16 +14,12 @@ class StubServerTest extends TestCase
public function testStartAndStop()
{
try {
- $pactLocation = __DIR__ . '/../../../_resources/someconsumer-someprovider.json';
- $host = 'localhost';
- $port = 7201;
- $endpoint = 'test';
+ $files = [__DIR__ . '/../../../_resources/someconsumer-someprovider.json'];
+ $port = 7201;
$subject = (new StubServerConfig())
- ->setPactLocation($pactLocation)
- ->setHost($host)
- ->setPort($port)
- ->setEndpoint($endpoint);
+ ->setFiles($files)
+ ->setPort($port);
$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',
+ ],
+ ],
+]);