Skip to content

Commit

Permalink
Merge pull request #427 from tienvx/define-interactions
Browse files Browse the repository at this point in the history
test(compatibility-suite): Define interactions
  • Loading branch information
tienvx committed Dec 21, 2023
2 parents d60745e + e287839 commit ec1452a
Show file tree
Hide file tree
Showing 25 changed files with 704 additions and 8 deletions.
39 changes: 39 additions & 0 deletions compatibility-suite/tests/Context/Shared/InteractionsContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace PhpPactTest\CompatibilitySuite\Context\Shared;

use Behat\Behat\Context\Context;
use PhpPactTest\CompatibilitySuite\Service\InteractionsStorageInterface;
use PhpPactTest\CompatibilitySuite\Service\MatchingRulesStorageInterface;
use PhpPactTest\CompatibilitySuite\Service\RequestMatchingRuleBuilderInterface;
use PhpPactTest\CompatibilitySuite\Service\ResponseMatchingRuleBuilderInterface;

class InteractionsContext implements Context
{
public function __construct(
private InteractionsStorageInterface $storage,
private RequestMatchingRuleBuilderInterface $requestMatchingRuleBuilder,
private ResponseMatchingRuleBuilderInterface $responseMatchingRuleBuilder,
private MatchingRulesStorageInterface $matchingRulesStorage,
) {
}

/**
* @Given the following HTTP interactions have been defined:
*/
public function theFollowingHttpInteractionsHaveBeenDefined(array $interactions): void
{
foreach ($interactions as $id => $interaction) {
$this->storage->add(InteractionsStorageInterface::MOCK_SERVER_DOMAIN, $id, $interaction);
$this->storage->add(InteractionsStorageInterface::MOCK_SERVER_CLIENT_DOMAIN, $id, $interaction, true);
$this->storage->add(InteractionsStorageInterface::PROVIDER_DOMAIN, $id, $interaction, true);
$this->storage->add(InteractionsStorageInterface::PACT_WRITER_DOMAIN, $id, $interaction);
if ($file = $this->matchingRulesStorage->get(MatchingRulesStorageInterface::REQUEST_DOMAIN, $id)) {
$this->requestMatchingRuleBuilder->build($interaction->getRequest(), $file);
}
if ($file = $this->matchingRulesStorage->get(MatchingRulesStorageInterface::RESPONSE_DOMAIN, $id)) {
$this->responseMatchingRuleBuilder->build($interaction->getResponse(), $file);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace PhpPactTest\CompatibilitySuite\Context\Shared\Transform;

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

class InteractionsContext implements Context
{
public function __construct(
private InteractionBuilderInterface $builder,
private MatchingRulesStorageInterface $matchingRulesStorage,
) {
}

/**
* @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
* @Transform table:No,method,path,query,headers,body,matching rules
* @Transform table:No,method,path,response,response headers,response content,response body,response matching rules
*
* @return array<int, Interaction>
*/
public function getInteractions(TableNode $table): array
{
$interactions = [];
foreach ($table->getHash() as $data) {
$id = (int) $data['No'];
$interactions[$id] = $this->builder->build($data);
$this->storeMatchingRules($id, $data);
}

return $interactions;
}

private function storeMatchingRules(int $id, array $data): void
{
if (isset($data['matching rules'])) {
$this->matchingRulesStorage->add(MatchingRulesStorageInterface::REQUEST_DOMAIN, $id, $data['matching rules']);
}
if (isset($data['response matching rules'])) {
$this->matchingRulesStorage->add(MatchingRulesStorageInterface::RESPONSE_DOMAIN, $id, $data['response matching rules']);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace PhpPact\Consumer\Exception;
namespace PhpPactTest\CompatibilitySuite\Exception;

use Exception;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace PhpPact\Consumer\Exception;
namespace PhpPactTest\CompatibilitySuite\Exception;

class FixtureNotFoundException extends CompatibilitySuiteException
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace PhpPactTest\CompatibilitySuite\Exception;

class IntegrationJsonFormatException extends CompatibilitySuiteException
{
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace PhpPact\Consumer\Exception;
namespace PhpPactTest\CompatibilitySuite\Exception;

class InvalidJsonFixtureException extends CompatibilitySuiteException
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace PhpPactTest\CompatibilitySuite\Exception;

class InvalidXmlFixtureException extends CompatibilitySuiteException
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace PhpPactTest\CompatibilitySuite\Exception;

class MatchingRuleConditionException extends CompatibilitySuiteException
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace PhpPactTest\CompatibilitySuite\Exception;

class UndefinedInteractionException extends CompatibilitySuiteException
{
}
39 changes: 39 additions & 0 deletions compatibility-suite/tests/Model/MatchingRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace PhpPactTest\CompatibilitySuite\Model;

class MatchingRule
{
public function __construct(
private string $matcher,
private string $category,
private string $subCategory,
private array $matcherAttributes
) {
}

public function getMatcher(): string
{
return $this->matcher;
}

public function getCategory(): string
{
return $this->category;
}

public function getSubCategory(): string
{
return $this->subCategory;
}

public function getMatcherAttributes(): array
{
return $this->matcherAttributes;
}

public function getMatcherAttribute(string $attribute): mixed
{
return $this->matcherAttributes[$attribute] ?? null;
}
}
4 changes: 2 additions & 2 deletions compatibility-suite/tests/Service/FixtureLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
namespace PhpPactTest\CompatibilitySuite\Service;

use JsonException;
use PhpPact\Consumer\Exception\FixtureNotFoundException;
use PhpPact\Consumer\Exception\InvalidJsonFixtureException;
use PhpPactTest\CompatibilitySuite\Constant\Path;
use PhpPactTest\CompatibilitySuite\Exception\FixtureNotFoundException;
use PhpPactTest\CompatibilitySuite\Exception\InvalidJsonFixtureException;

class FixtureLoader implements FixtureLoaderInterface
{
Expand Down
43 changes: 43 additions & 0 deletions compatibility-suite/tests/Service/InteractionsStorage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace PhpPactTest\CompatibilitySuite\Service;

use PhpPact\Consumer\Model\Interaction;
use PhpPactTest\CompatibilitySuite\Exception\UndefinedInteractionException;

final class InteractionsStorage implements InteractionsStorageInterface
{
/**
* @var array<int, Interaction>
*/
private array $interactions = [];

public function add(string $domain, int $id, Interaction $interaction, bool $clone = false): void
{
$this->interactions[$domain][$id] = $clone ? $this->cloneInteraction($interaction) : $interaction;
}

public function get(string $domain, int $id): Interaction
{
if (!isset($this->interactions[$domain][$id])) {
throw new UndefinedInteractionException(sprintf('Interaction %s is not defined in domain %s', $id, $domain));
}

return $this->interactions[$domain][$id];
}

private function cloneInteraction(Interaction $interaction): Interaction
{
$result = clone $interaction;
$result->setRequest(clone $interaction->getRequest());
if ($interaction->getRequest()->getBody()) {
$result->getRequest()->setBody(clone $interaction->getRequest()->getBody());
}
$result->setResponse(clone $interaction->getResponse());
if ($interaction->getResponse()->getBody()) {
$result->getResponse()->setBody(clone $interaction->getResponse()->getBody());
}

return $result;
}
}
17 changes: 17 additions & 0 deletions compatibility-suite/tests/Service/InteractionsStorageInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace PhpPactTest\CompatibilitySuite\Service;

use PhpPact\Consumer\Model\Interaction;

interface InteractionsStorageInterface
{
public const MOCK_SERVER_DOMAIN = 'mock-server';
public const MOCK_SERVER_CLIENT_DOMAIN = 'mock-server-client';
public const PROVIDER_DOMAIN = 'provider';
public const PACT_WRITER_DOMAIN = 'pact-writer';

public function add(string $domain, int $id, Interaction $interaction, bool $clone = false): void;

public function get(string $domain, int $id): Interaction;
}
134 changes: 134 additions & 0 deletions compatibility-suite/tests/Service/MatchingRuleConverter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<?php

namespace PhpPactTest\CompatibilitySuite\Service;

use PhpPact\Consumer\Matcher\Matchers\ArrayContains;
use PhpPact\Consumer\Matcher\Matchers\Boolean;
use PhpPact\Consumer\Matcher\Matchers\ContentType;
use PhpPact\Consumer\Matcher\Matchers\Date;
use PhpPact\Consumer\Matcher\Matchers\Decimal;
use PhpPact\Consumer\Matcher\Matchers\EachKey;
use PhpPact\Consumer\Matcher\Matchers\EachValue;
use PhpPact\Consumer\Matcher\Matchers\Equality;
use PhpPact\Consumer\Matcher\Matchers\Includes;
use PhpPact\Consumer\Matcher\Matchers\Integer;
use PhpPact\Consumer\Matcher\Matchers\MaxType;
use PhpPact\Consumer\Matcher\Matchers\MinMaxType;
use PhpPact\Consumer\Matcher\Matchers\MinType;
use PhpPact\Consumer\Matcher\Matchers\NotEmpty;
use PhpPact\Consumer\Matcher\Matchers\NullValue;
use PhpPact\Consumer\Matcher\Matchers\Number;
use PhpPact\Consumer\Matcher\Matchers\Regex;
use PhpPact\Consumer\Matcher\Matchers\Semver;
use PhpPact\Consumer\Matcher\Matchers\StatusCode;
use PhpPact\Consumer\Matcher\Matchers\Type;
use PhpPact\Consumer\Matcher\Matchers\Values;
use PhpPact\Consumer\Matcher\Model\MatcherInterface;
use PhpPactTest\CompatibilitySuite\Model\MatchingRule;

final class MatchingRuleConverter implements MatchingRuleConverterInterface
{
public function convert(MatchingRule $rule, mixed $value): ?MatcherInterface
{
switch ($rule->getMatcher()) {
case 'type':
$min = $rule->getMatcherAttribute('min');
$max = $rule->getMatcherAttribute('max');
if (null !== $min && null !== $max) {
return new MinMaxType($value, $min, $max);
}
if (null !== $min) {
return new MinType($value, $min);
}
if (null !== $max) {
return new MaxType($value, $max);
}
return new Type($value);

case 'equality':
return new Equality($value);

case 'include':
return new Includes($rule->getMatcherAttribute('value'));

case 'number':
return new Number($this->getNumber($value));

case 'integer':
return new Integer($this->getNumber($value));

case 'decimal':
return new Decimal($this->getNumber($value));

case 'null':
return new NullValue();

case 'date':
return new Date($rule->getMatcherAttribute('format'), $value);

case 'boolean':
return new Boolean($value);

case 'contentType':
return new ContentType($rule->getMatcherAttribute('value'));

case 'values':
return new Values($value);

case 'notEmpty':
return new NotEmpty($value);

case 'semver':
return new Semver($value);

case 'eachKey':
return new EachKey($value, $rule->getMatcherAttribute('rules'));

case 'eachValue':
return new EachValue($value, $rule->getMatcherAttribute('rules'));

case 'arrayContains':
return new ArrayContains($rule->getMatcherAttribute('variants'));

case 'regex':
$regex = $rule->getMatcherAttribute('regex');
return new Regex($regex, $this->ignoreInvalidValue($regex, $value));

case 'statusCode':
return new StatusCode($rule->getMatcherAttribute('status'));

default:
return null;
}
}

private function getNumber(mixed $value): int|float|null
{
if (is_numeric($value)) {
$value = $value + 0;
} else {
$value = null;
}

return $value;
}

private function ignoreInvalidValue(string $regex, mixed $value): string|array|null
{
if (is_string($value)) {
if (!preg_match("/$regex/", $value)) {
$value = null;
}
} elseif (is_array($value)) {
foreach (array_keys($value) as $key) {
if (!preg_match("/$regex/", $value[$key])) {
$value[$key] = null;
}
}
} else {
$value = null;
}

return $value;
}
}

0 comments on commit ec1452a

Please sign in to comment.