From ea31ea38850d3f82211feb32bee40253e371a66d Mon Sep 17 00:00:00 2001 From: "tien.xuan.vo" Date: Sat, 25 Nov 2023 20:28:39 +0700 Subject: [PATCH] chore: Add generators example --- composer.json | 5 +- example/generators/consumer/phpunit.xml | 11 + .../src/Service/HttpClientService.php | 28 ++ .../consumer/tests/Service/GeneratorsTest.php | 103 +++++++ ...generatorsConsumer-generatorsProvider.json | 259 ++++++++++++++++++ example/generators/provider/phpunit.xml | 11 + example/generators/provider/public/index.php | 42 +++ .../provider/tests/PactVerifyTest.php | 47 ++++ 8 files changed, 505 insertions(+), 1 deletion(-) create mode 100644 example/generators/consumer/phpunit.xml create mode 100644 example/generators/consumer/src/Service/HttpClientService.php create mode 100644 example/generators/consumer/tests/Service/GeneratorsTest.php create mode 100644 example/generators/pacts/generatorsConsumer-generatorsProvider.json create mode 100644 example/generators/provider/phpunit.xml create mode 100644 example/generators/provider/public/index.php create mode 100644 example/generators/provider/tests/PactVerifyTest.php 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); + } +}