Skip to content

Commit

Permalink
feat: Add compatibility suite
Browse files Browse the repository at this point in the history
  • Loading branch information
tienvx committed Oct 10, 2023
1 parent ddbd9e0 commit a4e3dc9
Show file tree
Hide file tree
Showing 17 changed files with 1,969 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "compatibility-suite/pact-compatibility-suite"]
path = compatibility-suite/pact-compatibility-suite
url = https://github.com/pact-foundation/pact-compatibility-suite.git
1 change: 1 addition & 0 deletions .php-cs-fixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
->in(__DIR__ . '/src')
->in(__DIR__ . '/tests')
->in(__DIR__ . '/example')
->in(__DIR__ . '/compatibility-suite/tests')
->name('*.php');

$config = new PhpCsFixer\Config();
Expand Down
20 changes: 20 additions & 0 deletions behat.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
default:
suites:
default:
paths: [ '%paths.base%/compatibility-suite/pact-compatibility-suite/features' ]
contexts:
- 'PhpPactTest\CompatibilitySuite\FeatureContext'
- 'PhpPactTest\CompatibilitySuite\Context\Shared\InteractionsContext':
- '@interactions_storage'
- 'PhpPactTest\CompatibilitySuite\Context\Shared\ConsumerContext':
- '@interactions_storage'
- '@mock_server'
- 'PhpPactTest\CompatibilitySuite\Context\Transform\InteractionsContext':
- '@interactions_table_transformer'
services:
interactions_table_transformer:
class: 'PhpPactTest\CompatibilitySuite\Service\InteractionsTableTransformer'
interactions_storage:
class: 'PhpPactTest\CompatibilitySuite\Service\InteractionsStorage'
mock_server:
class: 'PhpPactTest\CompatibilitySuite\Service\MockServer'
1 change: 1 addition & 0 deletions compatibility-suite/pact-compatibility-suite
1 change: 1 addition & 0 deletions compatibility-suite/pacts/v1/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.json
169 changes: 169 additions & 0 deletions compatibility-suite/tests/Context/Shared/ConsumerContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
<?php

namespace PhpPactTest\CompatibilitySuite\Context\Shared;

use Behat\Behat\Context\Context;
use GuzzleHttp\Client;
use PhpPact\Consumer\Model\Body\Text;
use PhpPactTest\CompatibilitySuite\Service\InteractionsStorageInterface;
use PhpPactTest\CompatibilitySuite\Service\MockServerInterface;
use PHPUnit\Framework\Assert;
use Psr\Http\Message\ResponseInterface;

final class ConsumerContext implements Context
{
private ResponseInterface $response;
private bool $verifyResult;
private string $path = __DIR__ . '/../../../pacts/v1/v1-compatibility-suite-c-p.json';
private array $pact;

public function __construct(
private InteractionsStorageInterface $storage,
private MockServerInterface $mockServer
) {
unlink($this->path);
}

/**
* @When the mock server is started with interaction :id
*/
public function theMockServerIsStartedWithInteraction(int $id): void
{
$interaction = $this->storage->get($id);
$this->mockServer->register($interaction);
}

/**
* @When request :id is made to the mock server
*/
public function requestIsMadeToTheMockServer(int $id): void
{
$request = $this->storage->get($id)->getRequest();
$options = [];
$options['query'] = $request->getQuery();
$options['headers'] = $request->getHeaders();
$body = $request->getBody();
if ($body instanceof Text) {
$options['body'] = $body->getContents();
$options['headers']['Content-Type'] = $body->getContentType();
}
$client = new Client();
$this->response = $client->request($request->getMethod(), $this->mockServer->getBaseUri()->withPath($request->getPath()), $options);
}

/**
* @Then a :code success response is returned
*/
public function aSuccessResponseIsReturned(int $code): void
{
Assert::assertSame($code, $this->response->getStatusCode());
}

/**
* @Then the payload will contain the :name JSON document
*/
public function thePayloadWillContainTheJsonDocument(string $name): void
{
$path = sprintf(__DIR__ . '/../../../pact-compatibility-suite/fixtures/%s.json', $name);
Assert::assertSame(file_get_contents($path), (string) $this->response->getBody());
}

/**
* @Then the content type will be set as :contentType
*/
public function theContentTypeWillBeSetAs(string $contentType): void
{
Assert::assertSame($contentType, $this->response->getHeaderLine('Content-Type'));
}

/**
* @When the pact test is done
*/
public function thePactTestIsDone(): void
{
$this->verifyResult = $this->mockServer->verify();
}

/**
* @Then the mock server status will be OK
*/
public function theMockServerStatusWillBeOk(): void
{
Assert::assertTrue($this->verifyResult);
}

/**
* @Then the mock server will write out a Pact file for the interaction when done
*/
public function theMockServerWillWriteOutAPactFileForTheInteractionWhenDone(): void
{
Assert::assertTrue(file_exists($this->path));
}

/**
* @Then the pact file will contain {:num} interaction(s)
*/
public function thePactFileWillContainInteraction(int $num): void
{
$this->pact = json_decode(file_get_contents($this->path), true);
Assert::assertEquals($num, count($this->pact['interactions'] ?? []));
}

/**
* @Then the {first} interaction request will be for a :method
*/
public function theFirstInteractionRequestWillBeForA(string $method): void
{
Assert::assertSame($method, $this->pact['interactions'][0]['request']['method'] ?? null);
}

/**
* @Then the {first} interaction response will contain the :fixture document
*/
public function theFirstInteractionResponseWillContainTheDocument(string $fixture): void
{
$path = sprintf(__DIR__ . '/../../../pact-compatibility-suite/fixtures/%s', $fixture);
Assert::assertEquals(json_decode(file_get_contents($path), true), $this->pact['interactions'][0]['response']['body'] ?? null);
}

/**
* @When the mock server is started with interactions :ids
*/
public function theMockServerIsStartedWithInteractions(string $ids): void
{
$ids = array_map(fn (string $id) => (int) trim($id), explode(',', $ids));
$interactions = array_map(fn (int $id) => $this->storage->get($id), $ids);
$this->mockServer->register(...$interactions);
}

/**
* @Then the mock server status will NOT be OK
*/
public function theMockServerStatusWillNotBeOk(): void
{
Assert::assertFalse($this->verifyResult);
}

/**
* @Then the mock server will NOT write out a Pact file for the interactions when done
*/
public function theMockServerWillNotWriteOutAPactFileForTheInteractionsWhenDone(): void
{
Assert::assertFalse(file_exists($this->path));
}

/**
* @Then the mock server status will be an expected but not received error for interaction {:id}
*/
public function theMockServerStatusWillBeAnExpectedButNotReceivedErrorForInteraction(int $id): void
{
$request = $this->storage->get($id)->getRequest();
$mismatches = $this->mockServer->getMismatches();
Assert::assertCount(1, $mismatches);
$mismatch = current($mismatches);
Assert::assertSame('missing-request', $mismatch['type']);
Assert::assertSame($request->getMethod(), $mismatch['request']['method']);
Assert::assertSame($request->getPath(), $mismatch['request']['path']);
Assert::assertSame($request->getQuery(), $mismatch['request']['query']);
}
}
21 changes: 21 additions & 0 deletions compatibility-suite/tests/Context/Shared/InteractionsContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace PhpPactTest\CompatibilitySuite\Context\Shared;

use Behat\Behat\Context\Context;
use PhpPactTest\CompatibilitySuite\Service\InteractionsStorageInterface;

final class InteractionsContext implements Context
{
public function __construct(private InteractionsStorageInterface $storage)
{
}

/**
* @Given the following HTTP interactions have been defined:
*/
public function theFollowingHttpInteractionsHaveBeenDefined(array $interactions): void
{
$this->storage->set($interactions);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace PhpPactTest\CompatibilitySuite\Context\Transform;

use Behat\Behat\Context\Context;
use Behat\Gherkin\Node\TableNode;
use PhpPact\Consumer\Model\Interaction;
use PhpPactTest\CompatibilitySuite\Service\InteractionsTableTransformerInterface;

final class InteractionsContext implements Context
{
public function __construct(private InteractionsTableTransformerInterface $transformer)
{
}

/**
* @Transform table:No,method,path,query,headers,body,response,response content,response body
* @Transform table:No,method,path,query,headers,body,response,response headers,response content,response body
*
* @return array<int, Interaction>
*/
public function getInteractions(TableNode $table): array
{
return $this->transformer->transform($table);
}
}
Loading

0 comments on commit a4e3dc9

Please sign in to comment.