Skip to content

Commit c07b72a

Browse files
authored
Merge pull request #429 from tienvx/v1-provider
test(compatibility-suite): Implement V1 Provider scenarios
2 parents a6fca91 + 3b57f20 commit c07b72a

25 files changed

+988
-1
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: pact-php
1+
name: Pact-PHP Code Analysis & Test
22

33
on:
44
push:
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Pact-PHP Compatibility Suite
2+
3+
on: [push, pull_request]
4+
5+
env:
6+
pact_do_not_track: true
7+
8+
jobs:
9+
v1:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v3
13+
with:
14+
submodules: recursive
15+
16+
- uses: shivammathur/setup-php@v2
17+
with:
18+
php-version: 8.2
19+
coverage: none
20+
21+
- uses: ramsey/composer-install@v2
22+
23+
- name: Run Behat
24+
run: vendor/bin/behat compatibility-suite/pact-compatibility-suite/features/V1

behat.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
imports:
2+
- 'compatibility-suite/suites/v1/http/consumer.yml'
3+
- 'compatibility-suite/suites/v1/http/provider.yml'
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.json
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
use Psr\Http\Message\ResponseInterface as Response;
4+
use Psr\Http\Message\ServerRequestInterface as Request;
5+
use Slim\Factory\AppFactory;
6+
7+
require __DIR__ . '/../../../vendor/autoload.php';
8+
9+
$app = AppFactory::create();
10+
$app->addBodyParsingMiddleware();
11+
12+
$path = __DIR__ . '/provider-states.json';
13+
$get = fn (): array => json_decode(file_get_contents($path), true);
14+
$set = fn (array $providerStates) => file_put_contents($path, json_encode($providerStates));
15+
16+
if (!file_exists($path)) {
17+
$set([]);
18+
}
19+
20+
$stateChangeHandler = function (Request $request, Response $response) use ($get, $set) {
21+
$body = $request->getParsedBody();
22+
23+
$providerStates = $get();
24+
$providerStates[] = $body;
25+
$set($providerStates);
26+
27+
return $response;
28+
};
29+
30+
$app->get('/has-action', function (Request $request, Response $response) use ($get) {
31+
$action = $request->getQueryParams()['action'];
32+
$hasAction = !empty(array_filter(
33+
$get(),
34+
fn (array $providerState) => $providerState['action'] === $action
35+
));
36+
37+
$response->getBody()->write((string) $hasAction);
38+
39+
return $response->withHeader('Content-Type', 'text/plain');
40+
});
41+
42+
$app->get('/has-state', function (Request $request, Response $response) use ($get) {
43+
$params = $request->getQueryParams();
44+
$action = $params['action'];
45+
$state = $params['state'];
46+
unset($params['action'], $params['state']);
47+
$hasState = !empty(array_filter(
48+
$get(),
49+
fn (array $providerState) =>
50+
$providerState['action'] === $action
51+
&& $providerState['state'] === $state
52+
&& $providerState['params'] == $params
53+
));
54+
55+
$response->getBody()->write((string) $hasState);
56+
57+
return $response->withHeader('Content-Type', 'text/plain');
58+
});
59+
60+
$app->post('/pact-change-state', $stateChangeHandler);
61+
62+
$app->post('/failed-pact-change-state', function (Request $request, Response $response) use ($stateChangeHandler) {
63+
$stateChangeHandler($request, $response);
64+
65+
throw new \Exception('Cant do it');
66+
});
67+
68+
$app->run();
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
default:
2+
suites:
3+
v1_http_consumer:
4+
paths: [ '%paths.base%/compatibility-suite/pact-compatibility-suite/features/V1/http_consumer.feature' ]
5+
6+
contexts:
7+
- 'PhpPactTest\CompatibilitySuite\Context\Shared\Hook\SetUpContext'
8+
- 'PhpPactTest\CompatibilitySuite\Context\Shared\InteractionsContext':
9+
- '@interactions_storage'
10+
- '@request_matching_rule_builder'
11+
- '@response_matching_rule_builder'
12+
- '@matching_rules_storage'
13+
- 'PhpPactTest\CompatibilitySuite\Context\Shared\Transform\InteractionsContext':
14+
- '@interaction_builder'
15+
- '@matching_rules_storage'
16+
- 'PhpPactTest\CompatibilitySuite\Context\V1\Http\ConsumerContext':
17+
- '@server'
18+
- '@request_builder'
19+
- '@client'
20+
- '@interactions_storage'
21+
- '@fixture_loader'
22+
23+
services: PhpPactTest\CompatibilitySuite\ServiceContainer\V1
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
default:
2+
suites:
3+
v1_http_provider:
4+
paths: [ '%paths.base%/compatibility-suite/pact-compatibility-suite/features/V1/http_provider.feature' ]
5+
6+
contexts:
7+
- 'PhpPactTest\CompatibilitySuite\Context\Shared\Hook\SetUpContext'
8+
- 'PhpPactTest\CompatibilitySuite\Context\Shared\InteractionsContext':
9+
- '@interactions_storage'
10+
- '@request_matching_rule_builder'
11+
- '@response_matching_rule_builder'
12+
- '@matching_rules_storage'
13+
- 'PhpPactTest\CompatibilitySuite\Context\Shared\Transform\InteractionsContext':
14+
- '@interaction_builder'
15+
- '@matching_rules_storage'
16+
- 'PhpPactTest\CompatibilitySuite\Context\Shared\Hook\PactBrokerContext':
17+
- '@pact_broker'
18+
- 'PhpPactTest\CompatibilitySuite\Context\Shared\Hook\ProviderStateContext':
19+
- '@provider_state_server'
20+
- 'PhpPactTest\CompatibilitySuite\Context\Shared\ProviderContext':
21+
- '@server'
22+
- '@provider_verifier'
23+
- '@provider_state_server'
24+
- 'PhpPactTest\CompatibilitySuite\Context\V1\Http\ProviderContext':
25+
- '@server'
26+
- '@pact_writer'
27+
- '@pact_broker'
28+
- '@response_builder'
29+
- '@interactions_storage'
30+
- '@provider_verifier'
31+
32+
services: PhpPactTest\CompatibilitySuite\ServiceContainer\V1
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace PhpPactTest\CompatibilitySuite\Context\Shared\Hook;
4+
5+
use Behat\Behat\Context\Context;
6+
use Behat\Behat\Hook\Scope\AfterScenarioScope;
7+
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
8+
use PhpPactTest\CompatibilitySuite\Service\PactBrokerInterface;
9+
10+
final class PactBrokerContext implements Context
11+
{
12+
public function __construct(
13+
private PactBrokerInterface $pactBroker
14+
) {
15+
}
16+
17+
/**
18+
* @BeforeScenario
19+
*/
20+
public function startPactBroker(BeforeScenarioScope $scope): void
21+
{
22+
if (str_contains($scope->getScenario()->getTitle(), 'via a Pact broker')) {
23+
$this->pactBroker->start();
24+
}
25+
}
26+
27+
/**
28+
* @AfterScenario
29+
*/
30+
public function stopPactBroker(AfterScenarioScope $scope): void
31+
{
32+
if (str_contains($scope->getScenario()->getTitle(), 'via a Pact broker')) {
33+
$this->pactBroker->stop();
34+
}
35+
}
36+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace PhpPactTest\CompatibilitySuite\Context\Shared\Hook;
4+
5+
use Behat\Behat\Context\Context;
6+
use Behat\Behat\Hook\Scope\AfterScenarioScope;
7+
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
8+
use PhpPactTest\CompatibilitySuite\Service\ProviderStateServerInterface;
9+
10+
final class ProviderStateContext implements Context
11+
{
12+
public function __construct(
13+
private ProviderStateServerInterface $providerStateServer
14+
) {
15+
}
16+
17+
/**
18+
* @BeforeScenario
19+
*/
20+
public function startProviderState(BeforeScenarioScope $scope): void
21+
{
22+
if (preg_match('/^Verifying .* provider state/', $scope->getScenario()->getTitle())) {
23+
$this->providerStateServer->start();
24+
}
25+
}
26+
27+
/**
28+
* @AfterScenario
29+
*/
30+
public function stopProviderState(AfterScenarioScope $scope): void
31+
{
32+
if (preg_match('/^Verifying .* provider state/', $scope->getScenario()->getTitle())) {
33+
$this->providerStateServer->stop();
34+
}
35+
}
36+
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
<?php
2+
3+
namespace PhpPactTest\CompatibilitySuite\Context\Shared;
4+
5+
use Behat\Behat\Context\Context;
6+
use GuzzleHttp\Psr7\Uri;
7+
use PhpPactTest\CompatibilitySuite\Constant\Mismatch;
8+
use PhpPactTest\CompatibilitySuite\Service\ProviderStateServerInterface;
9+
use PhpPactTest\CompatibilitySuite\Service\ProviderVerifierInterface;
10+
use PhpPactTest\CompatibilitySuite\Service\ServerInterface;
11+
use PHPUnit\Framework\Assert;
12+
13+
final class ProviderContext implements Context
14+
{
15+
public function __construct(
16+
private ServerInterface $server,
17+
private ProviderVerifierInterface $providerVerifier,
18+
private ProviderStateServerInterface $providerStateServer,
19+
) {
20+
}
21+
22+
/**
23+
* @When the verification is run
24+
*/
25+
public function theVerificationIsRun(): void
26+
{
27+
$this->providerVerifier->getConfig()->getProviderInfo()->setPort($this->server->getPort());
28+
$this->providerVerifier->verify();
29+
}
30+
31+
/**
32+
* @Then the verification will be successful
33+
*/
34+
public function theVerificationWillBeSuccessful(): void
35+
{
36+
Assert::assertTrue($this->providerVerifier->getVerifyResult()->isSuccess());
37+
}
38+
39+
/**
40+
* @Then the verification will NOT be successful
41+
*/
42+
public function theVerificationWillNotBeSuccessful(): void
43+
{
44+
Assert::assertFalse($this->providerVerifier->getVerifyResult()->isSuccess());
45+
}
46+
47+
/**
48+
* @Given a provider state callback is configured
49+
*/
50+
public function aProviderStateCallbackIsConfigured(): void
51+
{
52+
$port = $this->providerStateServer->getPort();
53+
$this->providerVerifier
54+
->getConfig()
55+
->getProviderState()
56+
->setStateChangeUrl(new Uri("http://localhost:$port/pact-change-state"))
57+
->setStateChangeTeardown(true);
58+
;
59+
}
60+
61+
/**
62+
* @Then the provider state callback will be called before the verification is run
63+
*/
64+
public function theProviderStateCallbackWillBeCalledBeforeTheVerificationIsRun(): void
65+
{
66+
Assert::assertTrue($this->providerStateServer->hasAction(ProviderStateServerInterface::ACTION_SETUP));
67+
}
68+
69+
/**
70+
* @Then the provider state callback will receive a setup call with :state as the provider state parameter
71+
*/
72+
public function theProviderStateCallbackWillReceiveASetupCallWithAsTheProviderStateParameter(string $state): void
73+
{
74+
Assert::assertTrue($this->providerStateServer->hasState(ProviderStateServerInterface::ACTION_SETUP, $state));
75+
}
76+
77+
/**
78+
* @Then the provider state callback will be called after the verification is run
79+
*/
80+
public function theProviderStateCallbackWillBeCalledAfterTheVerificationIsRun(): void
81+
{
82+
Assert::assertTrue($this->providerStateServer->hasAction(ProviderStateServerInterface::ACTION_TEARDOWN));
83+
}
84+
85+
/**
86+
* @Then the provider state callback will receive a teardown call :state as the provider state parameter
87+
*/
88+
public function theProviderStateCallbackWillReceiveATeardownCallAsTheProviderStateParameter(string $state): void
89+
{
90+
Assert::assertTrue($this->providerStateServer->hasState(ProviderStateServerInterface::ACTION_TEARDOWN, $state));
91+
}
92+
93+
/**
94+
* @Given a provider state callback is configured, but will return a failure
95+
*/
96+
public function aProviderStateCallbackIsConfiguredButWillReturnAFailure(): void
97+
{
98+
$port = $this->providerStateServer->getPort();
99+
$this->providerVerifier
100+
->getConfig()
101+
->getProviderState()
102+
->setStateChangeUrl(new Uri("http://localhost:$port/failed-pact-change-state"))
103+
->setStateChangeTeardown(true);
104+
;
105+
}
106+
107+
/**
108+
* @Then the provider state callback will NOT receive a teardown call
109+
*/
110+
public function theProviderStateCallbackWillNotReceiveATeardownCall(): void
111+
{
112+
Assert::assertFalse($this->providerStateServer->hasAction(ProviderStateServerInterface::ACTION_TEARDOWN));
113+
}
114+
115+
/**
116+
* @Then the verification results will contain a :error error
117+
*/
118+
public function theVerificationResultsWillContainAError(string $error): void
119+
{
120+
$output = json_decode($this->providerVerifier->getVerifyResult()->getOutput(), true);
121+
$errors = array_reduce(
122+
$output['errors'],
123+
function (array $errors, array $error) {
124+
switch ($error['mismatch']['type']) {
125+
case 'error':
126+
$errors[] = Mismatch::VERIFIER_MISMATCH_ERROR_MAP[$error['mismatch']['message']];
127+
break;
128+
129+
case 'mismatches':
130+
foreach ($error['mismatch']['mismatches'] as $mismatch) {
131+
$errors[] = Mismatch::VERIFIER_MISMATCH_TYPE_MAP[$mismatch['type']];
132+
}
133+
break;
134+
135+
default:
136+
break;
137+
}
138+
139+
return $errors;
140+
},
141+
[]
142+
);
143+
Assert::assertContains($error, $errors);
144+
}
145+
}

0 commit comments

Comments
 (0)