From a943508f092d4f15edf9b7ced9c842d3e1f8bdb1 Mon Sep 17 00:00:00 2001 From: "tien.xuan.vo" Date: Thu, 5 Sep 2024 01:25:54 +0700 Subject: [PATCH] chore: Add form urlencoded example --- composer.json | 8 +- example/form-urlencoded/consumer/phpunit.xml | 11 ++ .../src/Service/HttpClientService.php | 40 +++++ .../tests/Service/HttpClientServiceTest.php | 81 +++++++++++ ...ncodedConsumer-formUrlEncodedProvider.json | 137 ++++++++++++++++++ example/form-urlencoded/provider/phpunit.xml | 11 ++ .../form-urlencoded/provider/public/index.php | 30 ++++ .../provider/tests/PactVerifyTest.php | 52 +++++++ 8 files changed, 369 insertions(+), 1 deletion(-) create mode 100644 example/form-urlencoded/consumer/phpunit.xml create mode 100644 example/form-urlencoded/consumer/src/Service/HttpClientService.php create mode 100644 example/form-urlencoded/consumer/tests/Service/HttpClientServiceTest.php create mode 100644 example/form-urlencoded/pacts/formUrlEncodedConsumer-formUrlEncodedProvider.json create mode 100644 example/form-urlencoded/provider/phpunit.xml create mode 100644 example/form-urlencoded/provider/public/index.php create mode 100644 example/form-urlencoded/provider/tests/PactVerifyTest.php diff --git a/composer.json b/composer.json index 8d5ec29e..e92ad978 100644 --- a/composer.json +++ b/composer.json @@ -76,7 +76,8 @@ "composer run-example:protobuf-sync-message", "composer run-example:stub-server", "composer run-example:xml", - "composer run-example:graphql" + "composer run-example:graphql", + "composer run-example:form-urlencoded" ], "run-example:binary": [ "rm -f example/binary/pacts/binaryConsumer-binaryProvider.json", @@ -137,6 +138,11 @@ "rm -f example/graphql/pacts/graphqlConsumer-graphqlProvider.json", "cd example/graphql/consumer && phpunit", "cd example/graphql/provider && phpunit" + ], + "run-example:form-urlencoded": [ + "rm -f example/graphql/pacts/formUrlEncodedConsumer-formUrlEncodedProvider.json", + "cd example/form-urlencoded/consumer && phpunit", + "cd example/form-urlencoded/provider && phpunit" ] }, "extra": { diff --git a/example/form-urlencoded/consumer/phpunit.xml b/example/form-urlencoded/consumer/phpunit.xml new file mode 100644 index 00000000..62a9eb00 --- /dev/null +++ b/example/form-urlencoded/consumer/phpunit.xml @@ -0,0 +1,11 @@ + + + + + ./tests + + + + + + diff --git a/example/form-urlencoded/consumer/src/Service/HttpClientService.php b/example/form-urlencoded/consumer/src/Service/HttpClientService.php new file mode 100644 index 00000000..dd428548 --- /dev/null +++ b/example/form-urlencoded/consumer/src/Service/HttpClientService.php @@ -0,0 +1,40 @@ +httpClient = new Client(); + $this->baseUri = $baseUri; + } + + public function createUser(): string + { + $response = $this->httpClient->post(new Uri("{$this->baseUri}/users"), [ + 'body' => http_build_query([ + 'empty' => '', + 'agree' => 'true', + 'fullname' => 'First Last Name', + 'email' => 'user@example.test', + 'password' => 'very@secure&password123', + 'age' => 41, + ]) . + '&roles[]=User&roles[]=Manager', + 'headers' => [ + 'Accept' => 'application/json', + 'Content-Type' => 'application/x-www-form-urlencoded', + ], + ]); + + return $response->getBody(); + } +} diff --git a/example/form-urlencoded/consumer/tests/Service/HttpClientServiceTest.php b/example/form-urlencoded/consumer/tests/Service/HttpClientServiceTest.php new file mode 100644 index 00000000..d5f81772 --- /dev/null +++ b/example/form-urlencoded/consumer/tests/Service/HttpClientServiceTest.php @@ -0,0 +1,81 @@ +setMethod('POST') + ->setPath('/users') + ->addHeader('Content-Type', 'application/x-www-form-urlencoded') + ->addHeader('Accept', 'application/json') + ->setBody( + new Text( + json_encode([ + 'null' => $matcher->nullValue(), + 'empty' => $matcher->equal(''), + 'agree' => $matcher->regex('false', 'true|false'), + 'fullname' => $matcher->string('User name'), + 'email' => $matcher->email('user@email.test'), + 'password' => $matcher->regex('user@password111', '^[\w\d@$!%*#?&^_-]{8,}$'), + 'age' => $matcher->number(27), + 'roles[]' => $matcher->eachValue(['User'], [$matcher->regex('User', 'Admin|User|Manager')]), + // Boolean value is not supported, and will panic + // 'boolean' => $matcher->booleanV3(true), + // Object value is not supported, and will panic + // 'object' => $matcher->like([ + // 'key' => $matcher->string('value',) + // ]), + ]), + 'application/x-www-form-urlencoded' + ) + ) + ; + + $response = new ProviderResponse(); + $response + ->setStatus(201) + ->addHeader('Content-Type', 'application/json') + ->setBody([ + 'id' => $matcher->uuid('6e58b1df-ff80-4031-b7b9-5191e4c74ee8'), + ]); + + $config = new MockServerConfig(); + $config + ->setConsumer('formUrlEncodedConsumer') + ->setProvider('formUrlEncodedProvider') + ->setPactDir(__DIR__.'/../../../pacts'); + if ($logLevel = \getenv('PACT_LOGLEVEL')) { + $config->setLogLevel($logLevel); + } + $builder = new InteractionBuilder($config); + $builder + ->given('Endpoint is protected') + ->uponReceiving('A post request to /users') + ->with($request) + ->willRespondWith($response); + + $service = new HttpClientService($config->getBaseUri()); + $body = json_decode($service->createUser(), true); + $verifyResult = $builder->verify(); + + $this->assertTrue($verifyResult); + $this->assertArrayHasKey('id', $body); + $pattern = Matcher::UUID_V4_FORMAT; + $this->assertEquals(1, preg_match("/{$pattern}/", $body['id'])); + } +} diff --git a/example/form-urlencoded/pacts/formUrlEncodedConsumer-formUrlEncodedProvider.json b/example/form-urlencoded/pacts/formUrlEncodedConsumer-formUrlEncodedProvider.json new file mode 100644 index 00000000..3e876e4b --- /dev/null +++ b/example/form-urlencoded/pacts/formUrlEncodedConsumer-formUrlEncodedProvider.json @@ -0,0 +1,137 @@ +{ + "consumer": { + "name": "formUrlEncodedConsumer" + }, + "interactions": [ + { + "description": "A post request to /users", + "providerStates": [ + { + "name": "Endpoint is protected" + } + ], + "request": { + "body": "age=27&agree=false&email=user%40email.test&empty=&fullname=User+name&password=user%40password111&roles%5B%5D=User", + "headers": { + "Accept": "application/json", + "Content-Type": "application/x-www-form-urlencoded" + }, + "matchingRules": { + "body": { + "$.age": { + "combine": "AND", + "matchers": [ + { + "match": "number" + } + ] + }, + "$.agree": { + "combine": "AND", + "matchers": [ + { + "match": "regex", + "regex": "true|false" + } + ] + }, + "$.email": { + "combine": "AND", + "matchers": [ + { + "match": "regex", + "regex": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$" + } + ] + }, + "$.empty": { + "combine": "AND", + "matchers": [ + { + "match": "equality" + } + ] + }, + "$.fullname": { + "combine": "AND", + "matchers": [ + { + "match": "type" + } + ] + }, + "$.null": { + "combine": "AND", + "matchers": [ + { + "match": "null" + } + ] + }, + "$.password": { + "combine": "AND", + "matchers": [ + { + "match": "regex", + "regex": "^[\\w\\d@$!%*#?&^_-]{8,}$" + } + ] + }, + "$['roles[]']": { + "combine": "AND", + "matchers": [ + { + "match": "eachValue", + "rules": [ + { + "match": "regex", + "regex": "Admin|User|Manager" + } + ], + "value": "[\"User\"]" + } + ] + } + } + }, + "method": "POST", + "path": "/users" + }, + "response": { + "body": { + "id": "6e58b1df-ff80-4031-b7b9-5191e4c74ee8" + }, + "headers": { + "Content-Type": "application/json" + }, + "matchingRules": { + "body": { + "$.id": { + "combine": "AND", + "matchers": [ + { + "match": "regex", + "regex": "^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$" + } + ] + } + } + }, + "status": 201 + } + } + ], + "metadata": { + "pactRust": { + "ffi": "0.4.24", + "mockserver": "1.2.10", + "models": "1.2.5" + }, + "pactSpecification": { + "version": "3.0.0" + } + }, + "provider": { + "name": "formUrlEncodedProvider" + } +} \ No newline at end of file diff --git a/example/form-urlencoded/provider/phpunit.xml b/example/form-urlencoded/provider/phpunit.xml new file mode 100644 index 00000000..62a9eb00 --- /dev/null +++ b/example/form-urlencoded/provider/phpunit.xml @@ -0,0 +1,11 @@ + + + + + ./tests + + + + + + diff --git a/example/form-urlencoded/provider/public/index.php b/example/form-urlencoded/provider/public/index.php new file mode 100644 index 00000000..50ee7750 --- /dev/null +++ b/example/form-urlencoded/provider/public/index.php @@ -0,0 +1,30 @@ +post('/users', function (ServerRequestInterface $request) { + $response = new Response(); + $auth = $request->getHeader('Authorization'); + if ($auth != 'Bearer 1a2b3c4d5e6f7g8h9i0k') { + $response->withStatus(403); + } + + error_log(sprintf('request body: %s', (string) $request->getBody())); + + $response->getBody()->write(\json_encode(['id' => '49dcfd3f-a5c9-49cb-a09e-a40a1da936b9'])); + + return $response + ->withStatus(201) + ->withHeader('Content-Type', 'application/json'); +}); + +$app->post('/pact-change-state', function (ServerRequestInterface $request) { + return new Response(); +}); + +$app->run(); diff --git a/example/form-urlencoded/provider/tests/PactVerifyTest.php b/example/form-urlencoded/provider/tests/PactVerifyTest.php new file mode 100644 index 00000000..23971590 --- /dev/null +++ b/example/form-urlencoded/provider/tests/PactVerifyTest.php @@ -0,0 +1,52 @@ +process = new PhpProcess(__DIR__ . '/../public/'); + $this->process->start(); + } + + protected function tearDown(): void + { + $this->process->stop(); + } + + /** + * This test will run after the web server is started. + */ + public function testPactVerifyConsumer() + { + $config = new VerifierConfig(); + $config->getProviderInfo() + ->setName('formUrlEncodedProvider') // Providers name to fetch. + ->setHost('localhost') + ->setPort($this->process->getPort()); + $config->getProviderState() + ->setStateChangeUrl(new Uri(sprintf('http://localhost:%d/pact-change-state', $this->process->getPort()))) + ; + $config->getCustomHeaders() + ->addHeader('Authorization', 'Bearer 1a2b3c4d5e6f7g8h9i0k'); + if ($level = \getenv('PACT_LOGLEVEL')) { + $config->setLogLevel($level); + } + + $verifier = new Verifier($config); + $verifier->addFile(__DIR__ . '/../../pacts/formUrlEncodedConsumer-formUrlEncodedProvider.json'); + + $verifyResult = $verifier->verify(); + + $this->assertTrue($verifyResult); + } +}