From 99ea25f147005a1ca168ec51c4d8a0f2c57bb261 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 --- .github/workflows/build.yml | 2 + composer.json | 5 +- 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 | 31 ++++ .../provider/tests/PactVerifyTest.php | 52 +++++++ phpunit.xml | 3 + 10 files changed, 372 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/.github/workflows/build.yml b/.github/workflows/build.yml index 942b1c6c..0f3b9339 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -69,6 +69,8 @@ jobs: example: 'protobuf-async-message' - os: windows-latest example: 'stub-server' + - os: windows-latest + example: 'form-urlencoded-message' timeout-minutes: 5 steps: diff --git a/composer.json b/composer.json index 412dc172..8303f915 100644 --- a/composer.json +++ b/composer.json @@ -92,7 +92,10 @@ "ProtobufAsyncMessageConsumer\\Tests\\": "example/protobuf-async-message/consumer/tests", "ProtobufAsyncMessageProvider\\Tests\\": "example/protobuf-async-message/provider/tests", "StubServerConsumer\\": "example/stub-server/consumer/src", - "StubServerConsumer\\Tests\\": "example/stub-server/consumer/tests" + "StubServerConsumer\\Tests\\": "example/stub-server/consumer/tests", + "FormUrlEncodedConsumer\\": "example/form-urlencoded/consumer/src", + "FormUrlEncodedConsumer\\Tests\\": "example/form-urlencoded/consumer/tests", + "FormUrlEncodedProvider\\Tests\\": "example/form-urlencoded/provider/tests" } }, "scripts": { 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..515b517c --- /dev/null +++ b/example/form-urlencoded/provider/public/index.php @@ -0,0 +1,31 @@ +addBodyParsingMiddleware(); + +$app->post('/users', function (Request $request, Response $response) { + $auth = $request->getHeader('Authorization'); + if ($auth != 'Bearer 1a2b3c4d5e6f7g8h9i0k') { + $response->withStatus(403); + } + + error_log(sprintf('request body: %s', json_encode($request->getParsedBody()))); + + $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 (Request $request, Response $response) { + return $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); + } +} diff --git a/phpunit.xml b/phpunit.xml index c44f4f24..7a76e113 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -43,6 +43,9 @@ ./example/stub-server + + ./example/form-urlencoded +