diff --git a/example/matchers/consumer/src/Service/HttpClientService.php b/example/matchers/consumer/src/Service/HttpClientService.php index efbf8338c..5b74689f6 100644 --- a/example/matchers/consumer/src/Service/HttpClientService.php +++ b/example/matchers/consumer/src/Service/HttpClientService.php @@ -3,6 +3,7 @@ namespace MatchersConsumer\Service; use GuzzleHttp\Client; +use Psr\Http\Message\ResponseInterface; class HttpClientService { @@ -10,19 +11,31 @@ class HttpClientService private string $baseUri; + private ResponseInterface $response; + public function __construct(string $baseUri) { $this->httpClient = new Client(); $this->baseUri = $baseUri; + $this->sendRequest(); + } + + public function getResponseStatusCode(): int + { + return $this->response->getStatusCode(); } public function getMatchers(): array { - $response = $this->httpClient->get("{$this->baseUri}/matchers", [ + return \json_decode($this->response->getBody(), true, 512, JSON_THROW_ON_ERROR); + } + + private function sendRequest(): void + { + $this->response = $this->httpClient->get("{$this->baseUri}/matchers", [ 'headers' => ['Accept' => 'application/json'], - 'query' => 'ignore=statusCode&pages=2&pages=3&locales[]=fr-BE&locales[]=ru-RU', + 'query' => 'pages=2&pages=3&locales[]=fr-BE&locales[]=ru-RU', + 'http_errors' => false, ]); - - return \json_decode($response->getBody(), true, 512, JSON_THROW_ON_ERROR); } } diff --git a/example/matchers/consumer/tests/Service/MatchersTest.php b/example/matchers/consumer/tests/Service/MatchersTest.php index 1eb560a08..9e4c9b05c 100644 --- a/example/matchers/consumer/tests/Service/MatchersTest.php +++ b/example/matchers/consumer/tests/Service/MatchersTest.php @@ -4,6 +4,7 @@ use MatchersConsumer\Service\HttpClientService; use PhpPact\Consumer\InteractionBuilder; +use PhpPact\Consumer\Matcher\HttpStatus; use PhpPact\Consumer\Matcher\Matcher; use PhpPact\Consumer\Model\ConsumerRequest; use PhpPact\Consumer\Model\ProviderResponse; @@ -26,7 +27,6 @@ public function testGetMatchers() ->setMethod('GET') ->setPath($this->matcher->regex('/matchers', '^\/matchers$')) ->setQuery([ - 'ignore' => 'statusCode', 'pages' => [ // Consumer send multiple values, but provider receive single (last) value json_encode($this->matcher->regex([1, 22], '\d+')), ], @@ -38,7 +38,7 @@ public function testGetMatchers() $response = new ProviderResponse(); $response - ->setStatus(200) + ->setStatus($this->matcher->statusCode(HttpStatus::SERVER_ERROR, 512)) ->addHeader('Content-Type', 'application/json') ->setBody([ 'like' => $this->matcher->like(['key' => 'value']), @@ -104,7 +104,6 @@ public function testGetMatchers() [$this->matcher->regex(null, 'car|bike|motorbike')] ), 'query' => [ - 'ignore' => 'statusCode', 'pages' => '22', 'locales' => ['en-US', 'en-AU'], ], @@ -127,10 +126,10 @@ public function testGetMatchers() ->willRespondWith($response); $service = new HttpClientService($config->getBaseUri()); - $matchersResult = $service->getMatchers(); $verifyResult = $builder->verify(); $this->assertTrue($verifyResult); + $this->assertSame(512, $service->getResponseStatusCode()); $this->assertEquals([ 'like' => ['key' => 'value'], 'likeNull' => null, @@ -193,10 +192,9 @@ public function testGetMatchers() 'vehicle 1' => 'car', ], 'query' => [ - 'ignore' => 'statusCode', 'pages' => '22', 'locales' => ['en-US', 'en-AU'], ], - ], $matchersResult); + ], $service->getMatchers()); } } diff --git a/example/matchers/pacts/matchersConsumer-matchersProvider.json b/example/matchers/pacts/matchersConsumer-matchersProvider.json index 4495facdd..b7ac9422f 100644 --- a/example/matchers/pacts/matchersConsumer-matchersProvider.json +++ b/example/matchers/pacts/matchersConsumer-matchersProvider.json @@ -52,9 +52,6 @@ "method": "GET", "path": "/matchers", "query": { - "ignore": [ - "statusCode" - ], "locales[]": [ "en-US", "en-AU" @@ -132,7 +129,6 @@ "nullValue": null, "number": 123, "query": { - "ignore": "statusCode", "locales": [ "en-US", "en-AU" @@ -564,9 +560,20 @@ ] } }, - "header": {} + "header": {}, + "status": { + "$": { + "combine": "AND", + "matchers": [ + { + "match": "statusCode", + "status": "serverError" + } + ] + } + } }, - "status": 200 + "status": 512 }, "transport": "http", "type": "Synchronous/HTTP" @@ -574,9 +581,9 @@ ], "metadata": { "pactRust": { - "ffi": "0.4.10", + "ffi": "0.4.11", "mockserver": "1.2.4", - "models": "1.1.11" + "models": "1.1.12" }, "pactSpecification": { "version": "4.0" diff --git a/example/matchers/provider/public/index.php b/example/matchers/provider/public/index.php index 1876e5551..b6ece2d15 100644 --- a/example/matchers/provider/public/index.php +++ b/example/matchers/provider/public/index.php @@ -88,7 +88,9 @@ 'query' => $request->getQueryParams(), ])); - return $response->withHeader('Content-Type', 'application/json'); + return $response + ->withHeader('Content-Type', 'application/json') + ->withStatus(503); }); $app->post('/pact-change-state', function (Request $request, Response $response) { diff --git a/src/PhpPact/Consumer/Matcher/Matcher.php b/src/PhpPact/Consumer/Matcher/Matcher.php index 7e13a9996..0bcbe3eab 100644 --- a/src/PhpPact/Consumer/Matcher/Matcher.php +++ b/src/PhpPact/Consumer/Matcher/Matcher.php @@ -625,14 +625,36 @@ public function semver(string $value): array * * @return array */ - public function statusCode(string $status): array + public function statusCode(string $status, ?int $value = null): array { if (!in_array($status, HttpStatus::all())) { throw new Exception(sprintf("Status '%s' is not supported. Supported status are: %s", $status, implode(', ', HttpStatus::all()))); } + if (null === $value) { + [$min, $max] = match($status) { + HttpStatus::INFORMATION => [100, 199], + HttpStatus::SUCCESS => [200, 299], + HttpStatus::REDIRECT => [300, 399], + HttpStatus::CLIENT_ERROR => [400, 499], + HttpStatus::SERVER_ERROR => [500, 599], + HttpStatus::NON_ERROR => [100, 399], + HttpStatus::ERROR => [400, 599], + default => [100, 199], // Can't happen, just to make PHPStan happy + }; + + return [ + 'pact:generator:type' => 'RandomInt', + 'min' => $min, + 'max' => $max, + 'status' => $status, + 'pact:matcher:type' => 'statusCode', + ]; + } + return [ - 'status' => $status, + 'value' => $value, + 'status' => $status, 'pact:matcher:type' => 'statusCode', ]; } diff --git a/src/PhpPact/Consumer/Model/Interaction/StatusTrait.php b/src/PhpPact/Consumer/Model/Interaction/StatusTrait.php index 285148061..d381f2977 100644 --- a/src/PhpPact/Consumer/Model/Interaction/StatusTrait.php +++ b/src/PhpPact/Consumer/Model/Interaction/StatusTrait.php @@ -4,16 +4,16 @@ trait StatusTrait { - private int $status = 200; + private string $status = '200'; - public function getStatus(): int + public function getStatus(): string { return $this->status; } - public function setStatus(int $status): self + public function setStatus(int|array $status): self { - $this->status = $status; + $this->status = is_array($status) ? json_encode($status, JSON_THROW_ON_ERROR) : (string) $status; return $this; } diff --git a/src/PhpPact/Consumer/Registry/Interaction/Part/ResponseRegistry.php b/src/PhpPact/Consumer/Registry/Interaction/Part/ResponseRegistry.php index b74c00781..e5c0d4d39 100644 --- a/src/PhpPact/Consumer/Registry/Interaction/Part/ResponseRegistry.php +++ b/src/PhpPact/Consumer/Registry/Interaction/Part/ResponseRegistry.php @@ -19,9 +19,9 @@ public function __construct( parent::__construct($client, $interactionRegistry, $responseBodyRegistry ?? new ResponseBodyRegistry($client, $interactionRegistry)); } - public function withResponse(int $status): self + public function withResponse(string $status): self { - $this->client->call('pactffi_response_status', $this->getInteractionId(), $status); + $this->client->call('pactffi_response_status_v2', $this->getInteractionId(), $status); return $this; } diff --git a/src/PhpPact/Consumer/Registry/Interaction/Part/ResponseRegistryInterface.php b/src/PhpPact/Consumer/Registry/Interaction/Part/ResponseRegistryInterface.php index 05f56e523..fdcf0258d 100644 --- a/src/PhpPact/Consumer/Registry/Interaction/Part/ResponseRegistryInterface.php +++ b/src/PhpPact/Consumer/Registry/Interaction/Part/ResponseRegistryInterface.php @@ -4,5 +4,5 @@ interface ResponseRegistryInterface extends PartRegistryInterface { - public function withResponse(int $status): self; + public function withResponse(string $status): self; } diff --git a/tests/PhpPact/Consumer/Matcher/MatcherTest.php b/tests/PhpPact/Consumer/Matcher/MatcherTest.php index ff8dda224..23aeb0fe7 100644 --- a/tests/PhpPact/Consumer/Matcher/MatcherTest.php +++ b/tests/PhpPact/Consumer/Matcher/MatcherTest.php @@ -851,8 +851,11 @@ public function testInvalidStatusCode() public function testValidStatusCode() { $expected = [ - 'status' => 'success', - 'pact:matcher:type' => 'statusCode', + 'pact:generator:type' => 'RandomInt', + 'min' => 200, + 'max' => 299, + 'status' => 'success', + 'pact:matcher:type' => 'statusCode', ]; $actual = $this->matcher->statusCode(HttpStatus::SUCCESS);