diff --git a/composer.json b/composer.json index 365f558..2562657 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,8 @@ "psr/http-message": "^1.0", "php-http/discovery": "^1.0", "php-http/client-common": "^1.2", - "php-http/message": "^1.3" + "php-http/message": "^1.3", + "tolerance/tolerance": "^0.3.2" }, "require-dev": { "silex/silex": "~1.0", diff --git a/features/bootstrap/FeatureContext.php b/features/bootstrap/FeatureContext.php index 319e9c9..753c5a0 100644 --- a/features/bootstrap/FeatureContext.php +++ b/features/bootstrap/FeatureContext.php @@ -90,9 +90,9 @@ public function iRunBehat($arguments) public function itShouldTerminateWithStatusAndContent($exitStatus, PyStringNode $string) { if ('fail' === $exitStatus) { - $this->asserter->variable($this->getExitCode())->isEqualTo(1); - } elseif ('success' === $exitStatus) { - $this->asserter->variable($this->getExitCode())->isEqualTo(0); + $this->asserter->integer($this->getExitCode())->isEqualTo(1); + } elseif ('pass' === $exitStatus) { + $this->asserter->integer($this->getExitCode())->isEqualTo(0); } else { throw new \LogicException('Accepts only "fail" or "pass"'); } diff --git a/features/send_request_until.feature b/features/send_request_until.feature new file mode 100644 index 0000000..1eb02e2 --- /dev/null +++ b/features/send_request_until.feature @@ -0,0 +1,126 @@ +Feature: Send request until + In order to test async system + As a developer + I should be able to try to send HTTP request until it validates my requirement + + Background: + Given a file named "behat.yml" with: + """ + default: + extensions: + Rezzza\RestApiBehatExtension\Extension: + rest: + base_url: http://localhost:8888 + suites: + default: + contexts: + - FeatureContext + - Rezzza\RestApiBehatExtension\RestApiContext + """ + + Scenario: Send request until it works + Given a file named "features/send_request_until.feature" with: + """ + Feature: Send request until + In order to deal with async system + As a feature runner + I need to continue to send request until it works + + Scenario: Send request that could fail + When I call my microservice + And I call my microservice + And I call my microservice + Then print response + """ + And a file named "features/bootstrap/FeatureContext.php" with: + """ + restApiBrowser = $restApiBrowser; + $this->asserter = new asserter\generator; + } + + /** + * @When I call my microservice + */ + public function callMyMicroservice() + { + $restApiBrowser = $this->restApiBrowser; + $asserter = $this->asserter; + $this->restApiBrowser->sendRequestUntil( + 'GET', 'error_random', null, function () use ($restApiBrowser, $asserter) { + $asserter->integer($restApiBrowser->getResponse()->getStatusCode())->isEqualTo(200); + } + ); + } + } + """ + When I run behat "features/send_request_until.feature" + Then it should pass with: + """ + 200 OK + """ + + Scenario: Send request that will fail always + Given a file named "features/send_request_until.feature" with: + """ + Feature: Send request until + In order to deal with async system + As a feature runner + I need to continue to send request until it works + + Scenario: Send request that fail + When I call my microservice + Then print response + """ + And a file named "features/bootstrap/FeatureContext.php" with: + """ + restApiBrowser = $restApiBrowser; + $this->asserter = new asserter\generator; + } + + /** + * @When I call my microservice + */ + public function callMyMicroservice() + { + $restApiBrowser = $this->restApiBrowser; + $asserter = $this->asserter; + $this->restApiBrowser->sendRequestUntil( + 'GET', 'always_error', null, function () use ($restApiBrowser, $asserter) { + $asserter->integer($restApiBrowser->getResponse()->getStatusCode())->isEqualTo(200); + }, + 5 + ); + } + } + """ + When I run behat "features/send_request_until.feature" + Then it should fail with: + """ + integer(502) is not equal to integer(200) + """ diff --git a/src/Rest/RestApiBrowser.php b/src/Rest/RestApiBrowser.php index e46e820..937fed0 100644 --- a/src/Rest/RestApiBrowser.php +++ b/src/Rest/RestApiBrowser.php @@ -7,6 +7,11 @@ use Http\Discovery\MessageFactoryDiscovery; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; +use Tolerance\Operation\Callback; +use Tolerance\Operation\Runner\RetryOperationRunner; +use Tolerance\Operation\Runner\CallbackOperationRunner; +use Tolerance\Waiter\SleepWaiter; +use Rezzza\RestApiBehatExtension\Tolerance\ExecutionTimeLimited; class RestApiBrowser { @@ -102,6 +107,20 @@ public function sendRequest($method, $uri, $body = null) } } + public function sendRequestUntil($method, $uri, $body, callable $assertion, $maxExecutionTime = 10) + { + $runner = new RetryOperationRunner( + new CallbackOperationRunner(), + new ExecutionTimeLimited(new SleepWaiter(), $maxExecutionTime) + ); + $restApiBrowser = $this; + $runner->run(new Callback(function () use ($restApiBrowser, $method, $uri, $body, $assertion) { + $restApiBrowser->sendRequest($method, $uri, $body); + + return $assertion(); + })); + } + /** * @param string $name * @param string $value diff --git a/src/Tolerance/ExecutionTimeLimited.php b/src/Tolerance/ExecutionTimeLimited.php new file mode 100644 index 0000000..0239fd7 --- /dev/null +++ b/src/Tolerance/ExecutionTimeLimited.php @@ -0,0 +1,46 @@ +waiter = $waiter; + $this->maxExecutionTime = $maxExecutionTime; + $this->timeEllapsed = 0; + } + + /** + * {@inheritdoc} + */ + public function wait($seconds = 1) + { + $this->timeEllapsed += $seconds; + if ($this->maxExecutionTime < $this->timeEllapsed) { + throw MaxExecutionTimeReached::withValue($this->maxExecutionTime); + } + $this->waiter->wait($seconds); + } + + /** + * {@inheritdoc} + */ + public function resetState() + { + // wait for 0.4.0 to have https://github.com/Tolerance/Tolerance/pull/67 + // $this->timeEllapsed = 0; + } +} diff --git a/src/Tolerance/MaxExecutionTimeReached.php b/src/Tolerance/MaxExecutionTimeReached.php new file mode 100644 index 0000000..6128c5b --- /dev/null +++ b/src/Tolerance/MaxExecutionTimeReached.php @@ -0,0 +1,13 @@ +match( + 'error_random', + function (Request $request) { + $statusCode = time() % 3 <= 0 ? 200 : 502 ; + + return new JsonResponse([], $statusCode); + } +); +$app->match( + 'always_error', + function (Request $request) { + return new JsonResponse([], 502); + } +); $app->run();