diff --git a/composer.json b/composer.json
index a518e3b88..1c1df9579 100644
--- a/composer.json
+++ b/composer.json
@@ -66,7 +66,10 @@
"XmlProvider\\Tests\\": "example/xml/provider/tests",
"MatchersConsumer\\": "example/matchers/consumer/src",
"MatchersConsumer\\Tests\\": "example/matchers/consumer/tests",
- "MatchersProvider\\Tests\\": "example/matchers/provider/tests"
+ "MatchersProvider\\Tests\\": "example/matchers/provider/tests",
+ "GeneratorsConsumer\\": "example/generators/consumer/src",
+ "GeneratorsConsumer\\Tests\\": "example/generators/consumer/tests",
+ "GeneratorsProvider\\Tests\\": "example/generators/provider/tests"
}
},
"scripts": {
diff --git a/example/generators/consumer/phpunit.xml b/example/generators/consumer/phpunit.xml
new file mode 100644
index 000000000..62a9eb007
--- /dev/null
+++ b/example/generators/consumer/phpunit.xml
@@ -0,0 +1,11 @@
+
+
+
+
+ ./tests
+
+
+
+
+
+
diff --git a/example/generators/consumer/src/Service/HttpClientService.php b/example/generators/consumer/src/Service/HttpClientService.php
new file mode 100644
index 000000000..909edd0c9
--- /dev/null
+++ b/example/generators/consumer/src/Service/HttpClientService.php
@@ -0,0 +1,28 @@
+httpClient = new Client();
+ $this->baseUri = $baseUri;
+ }
+
+ private function sendRequest(): ResponseInterface
+ {
+ return $this->httpClient->get("{$this->baseUri}/generators", [
+ 'headers' => ['Accept' => 'application/json'],
+ 'json' => ['id' => 112],
+ 'http_errors' => false,
+ ]);
+ }
+}
diff --git a/example/generators/consumer/tests/Service/GeneratorsTest.php b/example/generators/consumer/tests/Service/GeneratorsTest.php
new file mode 100644
index 000000000..55cedaef6
--- /dev/null
+++ b/example/generators/consumer/tests/Service/GeneratorsTest.php
@@ -0,0 +1,103 @@
+matcher = new Matcher();
+ }
+
+ public function testGetMatchers()
+ {
+ $request = new ConsumerRequest();
+ $request
+ ->setMethod('GET')
+ ->setPath('/generators')
+ ->addHeader('Accept', 'application/json')
+ ->setBody([
+ 'id' => $this->matcher->fromProviderState($this->matcher->integer(111), '${id}')
+ ]);
+
+ $response = new ProviderResponse();
+ $response
+ ->setStatus($this->matcher->statusCode(HttpStatus::CLIENT_ERROR))
+ ->addHeader('Content-Type', 'application/json')
+ ->setBody([
+ 'regex' => $this->matcher->regex(null, $regexNoAnchors = '\d+ (miles|kilometers)'),
+ 'boolean' => $this->matcher->booleanV3(null),
+ 'integer' => $this->matcher->integerV3(null),
+ 'decimal' => $this->matcher->decimalV3(null),
+ 'hexadecimal' => $this->matcher->hexadecimal(null),
+ 'uuid' => $this->matcher->uuid(null),
+ 'date' => $this->matcher->date('yyyy-MM-dd', null),
+ 'time' => $this->matcher->time('HH:mm::ss', null),
+ 'datetime' => $this->matcher->datetime("YYYY-MM-D'T'HH:mm:ss", null),
+ 'string' => $this->matcher->string(null),
+ 'number' => $this->matcher->number(null),
+ ]);
+
+ $config = new MockServerConfig();
+ $config
+ ->setConsumer('generatorsConsumer')
+ ->setProvider('generatorsProvider')
+ ->setPactDir(__DIR__.'/../../../pacts')
+ ->setPactSpecificationVersion('4.0.0');
+ if ($logLevel = \getenv('PACT_LOGLEVEL')) {
+ $config->setLogLevel($logLevel);
+ }
+ $builder = new InteractionBuilder($config);
+ $builder
+ ->given('Get Generators')
+ ->uponReceiving('A get request to /generators')
+ ->with($request)
+ ->willRespondWith($response);
+
+ $service = new HttpClientService($config->getBaseUri());
+ $response = $service->sendRequest();
+ $verifyResult = $builder->verify();
+
+ $statusCode = $response->getStatusCode();
+ $body = \json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR);
+
+ $this->assertTrue($verifyResult);
+ $this->assertThat(
+ $statusCode,
+ $this->logicalAnd(
+ $this->greaterThanOrEqual(400),
+ $this->lessThanOrEqual(499)
+ )
+ );
+ $this->assertMatchesRegularExpression('/^' . $regexNoAnchors . '$/', $body['regex']);
+ $this->assertIsBool($body['boolean']);
+ $this->assertIsInt($body['integer']);
+ $this->assertIsFloat($body['decimal'] + 0);
+ $this->assertMatchesRegularExpression('/' . Matcher::HEX_FORMAT . '/', $body['hexadecimal']);
+ $this->assertMatchesRegularExpression('/' . Matcher::UUID_V4_FORMAT . '/', $body['uuid']);
+ $this->assertTrue($this->validateDateTime($body['date'], 'Y-m-d'));
+ $this->assertTrue($this->validateDateTime($body['time'], 'H:i::s'));
+ $this->assertTrue($this->validateDateTime($body['datetime'], "Y-m-z\TH:i:s"));
+ $this->assertIsString($body['string']);
+ $this->assertIsNumeric($body['number']);
+ }
+
+ private function validateDateTime(string $datetime, string $format): bool
+ {
+ $value = DateTime::createFromFormat($format, $datetime);
+
+ return $value && $value->format($format) === $datetime;
+ }
+}
diff --git a/example/generators/pacts/generatorsConsumer-generatorsProvider.json b/example/generators/pacts/generatorsConsumer-generatorsProvider.json
new file mode 100644
index 000000000..8639fde22
--- /dev/null
+++ b/example/generators/pacts/generatorsConsumer-generatorsProvider.json
@@ -0,0 +1,259 @@
+{
+ "consumer": {
+ "name": "generatorsConsumer"
+ },
+ "interactions": [
+ {
+ "description": "A get request to /generators",
+ "pending": false,
+ "providerStates": [
+ {
+ "name": "Get Generators"
+ }
+ ],
+ "request": {
+ "body": {
+ "content": {
+ "id": 111
+ },
+ "contentType": "application/json",
+ "encoded": false
+ },
+ "generators": {
+ "body": {
+ "$.id": {
+ "expression": "${id}",
+ "type": "ProviderState"
+ }
+ }
+ },
+ "headers": {
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json"
+ ]
+ },
+ "matchingRules": {
+ "body": {
+ "$.id": {
+ "combine": "AND",
+ "matchers": [
+ {
+ "match": "type"
+ }
+ ]
+ }
+ },
+ "header": {}
+ },
+ "method": "GET",
+ "path": "/generators"
+ },
+ "response": {
+ "body": {
+ "content": {
+ "boolean": null,
+ "date": null,
+ "datetime": null,
+ "decimal": null,
+ "hexadecimal": null,
+ "integer": null,
+ "number": null,
+ "regex": null,
+ "string": "some string",
+ "time": null,
+ "uuid": null
+ },
+ "contentType": "application/json",
+ "encoded": false
+ },
+ "generators": {
+ "body": {
+ "$.boolean": {
+ "type": "RandomBoolean"
+ },
+ "$.date": {
+ "format": "yyyy-MM-dd",
+ "type": "Date"
+ },
+ "$.datetime": {
+ "format": "YYYY-MM-D'T'HH:mm:ss",
+ "type": "DateTime"
+ },
+ "$.decimal": {
+ "digits": 10,
+ "type": "RandomDecimal"
+ },
+ "$.hexadecimal": {
+ "digits": 10,
+ "type": "RandomHexadecimal"
+ },
+ "$.integer": {
+ "max": 10,
+ "min": 0,
+ "type": "RandomInt"
+ },
+ "$.number": {
+ "max": 10,
+ "min": 0,
+ "type": "RandomInt"
+ },
+ "$.regex": {
+ "regex": "\\d+ (miles|kilometers)",
+ "type": "Regex"
+ },
+ "$.string": {
+ "size": 10,
+ "type": "RandomString"
+ },
+ "$.time": {
+ "format": "HH:mm::ss",
+ "type": "Time"
+ },
+ "$.uuid": {
+ "type": "Uuid"
+ }
+ },
+ "status": {
+ "max": 499,
+ "min": 400,
+ "type": "RandomInt"
+ }
+ },
+ "headers": {
+ "Content-Type": [
+ "application/json"
+ ]
+ },
+ "matchingRules": {
+ "body": {
+ "$.boolean": {
+ "combine": "AND",
+ "matchers": [
+ {
+ "match": "boolean"
+ }
+ ]
+ },
+ "$.date": {
+ "combine": "AND",
+ "matchers": [
+ {
+ "format": "yyyy-MM-dd",
+ "match": "date"
+ }
+ ]
+ },
+ "$.datetime": {
+ "combine": "AND",
+ "matchers": [
+ {
+ "format": "YYYY-MM-D'T'HH:mm:ss",
+ "match": "datetime"
+ }
+ ]
+ },
+ "$.decimal": {
+ "combine": "AND",
+ "matchers": [
+ {
+ "match": "decimal"
+ }
+ ]
+ },
+ "$.hexadecimal": {
+ "combine": "AND",
+ "matchers": [
+ {
+ "match": "regex",
+ "regex": "^[0-9a-fA-F]+$"
+ }
+ ]
+ },
+ "$.integer": {
+ "combine": "AND",
+ "matchers": [
+ {
+ "match": "integer"
+ }
+ ]
+ },
+ "$.number": {
+ "combine": "AND",
+ "matchers": [
+ {
+ "match": "number"
+ }
+ ]
+ },
+ "$.regex": {
+ "combine": "AND",
+ "matchers": [
+ {
+ "match": "regex",
+ "regex": "\\d+ (miles|kilometers)"
+ }
+ ]
+ },
+ "$.string": {
+ "combine": "AND",
+ "matchers": [
+ {
+ "match": "type"
+ }
+ ]
+ },
+ "$.time": {
+ "combine": "AND",
+ "matchers": [
+ {
+ "format": "HH:mm::ss",
+ "match": "time"
+ }
+ ]
+ },
+ "$.uuid": {
+ "combine": "AND",
+ "matchers": [
+ {
+ "match": "regex",
+ "regex": "^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$"
+ }
+ ]
+ }
+ },
+ "header": {},
+ "status": {
+ "$": {
+ "combine": "AND",
+ "matchers": [
+ {
+ "match": "statusCode",
+ "status": "clientError"
+ }
+ ]
+ }
+ }
+ },
+ "status": 0
+ },
+ "transport": "http",
+ "type": "Synchronous/HTTP"
+ }
+ ],
+ "metadata": {
+ "pactRust": {
+ "ffi": "0.4.11",
+ "mockserver": "1.2.4",
+ "models": "1.1.12"
+ },
+ "pactSpecification": {
+ "version": "4.0"
+ }
+ },
+ "provider": {
+ "name": "generatorsProvider"
+ }
+}
\ No newline at end of file
diff --git a/example/generators/provider/phpunit.xml b/example/generators/provider/phpunit.xml
new file mode 100644
index 000000000..62a9eb007
--- /dev/null
+++ b/example/generators/provider/phpunit.xml
@@ -0,0 +1,11 @@
+
+
+
+
+ ./tests
+
+
+
+
+
+
diff --git a/example/generators/provider/public/index.php b/example/generators/provider/public/index.php
new file mode 100644
index 000000000..20e97c720
--- /dev/null
+++ b/example/generators/provider/public/index.php
@@ -0,0 +1,42 @@
+addBodyParsingMiddleware();
+
+$app->get('/generators', function (Request $request, Response $response) {
+ $response->getBody()->write(\json_encode([
+ 'regex' => '800 kilometers',
+ 'boolean' => true,
+ 'integer' => 11,
+ 'decimal' => 25.1,
+ 'hexadecimal' => '20AC',
+ 'uuid' => 'e9d2f3a5-6ecc-4bff-8935-84bb6141325a',
+ 'date' => '1997-12-11',
+ 'time' => '11:01::02',
+ 'datetime' => '1997-07-16T19:20:30',
+ 'string' => 'another string',
+ 'number' => 112.3,
+ ]));
+
+ return $response
+ ->withHeader('Content-Type', 'application/json')
+ ->withStatus(400);
+});
+
+$app->post('/pact-change-state', function (Request $request, Response $response) {
+ $response->getBody()->write(\json_encode([
+ 'id' => 222,
+ ]));
+
+ return $response
+ ->withHeader('Content-Type', 'application/json')
+ ->withStatus(200);
+});
+
+$app->run();
diff --git a/example/generators/provider/tests/PactVerifyTest.php b/example/generators/provider/tests/PactVerifyTest.php
new file mode 100644
index 000000000..86f8ea05f
--- /dev/null
+++ b/example/generators/provider/tests/PactVerifyTest.php
@@ -0,0 +1,47 @@
+process = new ProviderProcess(__DIR__ . '/../public/');
+ $this->process->start();
+ }
+
+ protected function tearDown(): void
+ {
+ $this->process->stop();
+ }
+
+ public function testPactVerifyConsumer()
+ {
+ $config = new VerifierConfig();
+ $config->getProviderInfo()
+ ->setName('generatorsProvider')
+ ->setHost('localhost')
+ ->setPort(7202);
+ $config->getProviderState()
+ ->setStateChangeUrl(new Uri('http://localhost:7202/pact-change-state'))
+ ;
+ if ($level = \getenv('PACT_LOGLEVEL')) {
+ $config->setLogLevel($level);
+ }
+
+ $verifier = new Verifier($config);
+ $verifier->addFile(__DIR__ . '/../../pacts/generatorsConsumer-generatorsProvider.json');
+
+ $verifyResult = $verifier->verify();
+
+ $this->assertTrue($verifyResult);
+ }
+}