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