diff --git a/src/PhpPact/Consumer/Matcher/Exception/GeneratorNotRequiredException.php b/src/PhpPact/Consumer/Matcher/Exception/GeneratorNotRequiredException.php deleted file mode 100644 index c4be0ea9..00000000 --- a/src/PhpPact/Consumer/Matcher/Exception/GeneratorNotRequiredException.php +++ /dev/null @@ -1,7 +0,0 @@ - sprintf("'%s'", $value), - 'boolean' => $value ? 'true' : 'false', - 'integer' => (string) $value, - 'double' => (string) $value, - 'NULL' => 'null', - default => throw new InvalidValueException(sprintf("Expression doesn't support value of type %s", gettype($value))), - }; - } - - protected function getMatcherNotSupportedException(MatcherInterface $matcher): MatcherNotSupportedException - { - return new MatcherNotSupportedException(sprintf('Matcher %s is not supported by %s', $matcher->getType(), static::class)); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/BooleanFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/BooleanFormatter.php deleted file mode 100644 index c7e4efeb..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/BooleanFormatter.php +++ /dev/null @@ -1,23 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - $value = $matcher->getValue(); - if (!is_bool($value)) { - throw new InvalidValueException(sprintf("Boolean formatter doesn't support value of type %s", gettype($value))); - } - - return sprintf('matching(boolean, %s)', $this->normalize($value)); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/ContentTypeFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/ContentTypeFormatter.php deleted file mode 100644 index c440effe..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/ContentTypeFormatter.php +++ /dev/null @@ -1,18 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - - return sprintf("matching(contentType, %s, %s)", $this->normalize($matcher->getContentType()), $this->normalize($matcher->getValue())); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/DateTimeFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/DateTimeFormatter.php deleted file mode 100644 index 55ba830e..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/DateTimeFormatter.php +++ /dev/null @@ -1,23 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - $value = $matcher->getValue(); - if (!is_string($value)) { - throw new InvalidValueException(sprintf("DateTime formatter doesn't support value of type %s", gettype($value))); - } - - return sprintf("matching(%s, %s, %s)", $matcher->getType(), $this->normalize($matcher->getFormat()), $this->normalize($value)); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/DecimalFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/DecimalFormatter.php deleted file mode 100644 index f3fd0868..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/DecimalFormatter.php +++ /dev/null @@ -1,23 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - $value = $matcher->getValue(); - if (!is_float($value)) { - throw new InvalidValueException(sprintf("Decimal formatter doesn't support value of type %s", gettype($value))); - } - - return sprintf('matching(decimal, %s)', $this->normalize($value)); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/EachKeyFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/EachKeyFormatter.php deleted file mode 100644 index 1de593f8..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/EachKeyFormatter.php +++ /dev/null @@ -1,24 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - $rules = $matcher->getRules(); - if (count($rules) !== 1) { - throw new MatchingExpressionException(sprintf("Matcher 'eachKey' only support 1 rule in expression, %d provided", count($rules))); - } - $rule = reset($rules); - - return sprintf('eachKey(%s)', $rule->createExpressionFormatter()->format($rule)); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/EachValueFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/EachValueFormatter.php deleted file mode 100644 index dfe9d8d5..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/EachValueFormatter.php +++ /dev/null @@ -1,24 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - $rules = $matcher->getRules(); - if (count($rules) !== 1) { - throw new MatchingExpressionException(sprintf("Matcher 'eachValue' only support 1 rule in expression, %d provided", count($rules))); - } - $rule = reset($rules); - - return sprintf('eachValue(%s)', $rule->createExpressionFormatter()->format($rule)); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/EqualityFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/EqualityFormatter.php deleted file mode 100644 index bb9941e4..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/EqualityFormatter.php +++ /dev/null @@ -1,18 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - - return sprintf('matching(equalTo, %s)', $this->normalize($matcher->getValue())); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/ExpressionFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/ExpressionFormatter.php new file mode 100644 index 00000000..61153445 --- /dev/null +++ b/src/PhpPact/Consumer/Matcher/Formatters/Expression/ExpressionFormatter.php @@ -0,0 +1,21 @@ +formatExpression(); + } +} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/IncludesFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/IncludesFormatter.php deleted file mode 100644 index e67a0f37..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/IncludesFormatter.php +++ /dev/null @@ -1,18 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - - return sprintf('matching(include, %s)', $this->normalize($matcher->getValue())); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/IntegerFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/IntegerFormatter.php deleted file mode 100644 index 7d7bba14..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/IntegerFormatter.php +++ /dev/null @@ -1,23 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - $value = $matcher->getValue(); - if (!is_int($value)) { - throw new InvalidValueException(sprintf("Integer formatter doesn't support value of type %s", gettype($value))); - } - - return sprintf('matching(integer, %d)', $value); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/MatchAllFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/MatchAllFormatter.php deleted file mode 100644 index 7f00041e..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/MatchAllFormatter.php +++ /dev/null @@ -1,26 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - $matchers = $matcher->getMatchers(); - if (empty($matchers)) { - throw new MatchingExpressionException("Matcher 'matchAll' need at least 1 matchers"); - } - - return implode(', ', array_map( - fn (MatcherInterface $matcher): string => $matcher->createExpressionFormatter()->format($matcher), - $matchers - )); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/MatchingFieldFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/MatchingFieldFormatter.php deleted file mode 100644 index 58321804..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/MatchingFieldFormatter.php +++ /dev/null @@ -1,18 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - - return sprintf("matching($%s)", $this->normalize($matcher->getFieldName())); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/MaxTypeFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/MaxTypeFormatter.php deleted file mode 100644 index 8a8e8695..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/MaxTypeFormatter.php +++ /dev/null @@ -1,21 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - if ($matcher->isMatchingType()) { - return sprintf('atMost(%u), eachValue(matching(type, %s)', $matcher->getMax(), $this->normalize($matcher->getValue())); - } - - return sprintf('atMost(%u)', $matcher->getMax()); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/MinMaxTypeFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/MinMaxTypeFormatter.php deleted file mode 100644 index 5c99fbcf..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/MinMaxTypeFormatter.php +++ /dev/null @@ -1,18 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - - return sprintf('atLeast(%u), atMost(%u), eachValue(matching(type, %s)', $matcher->getMin(), $matcher->getMax(), $this->normalize($matcher->getValue())); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/MinTypeFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/MinTypeFormatter.php deleted file mode 100644 index 24521718..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/MinTypeFormatter.php +++ /dev/null @@ -1,21 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - if ($matcher->isMatchingType()) { - return sprintf('atLeast(%u), eachValue(matching(type, %s)', $matcher->getMin(), $this->normalize($matcher->getValue())); - } - - return sprintf('atLeast(%u)', $matcher->getMin()); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/NotEmptyFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/NotEmptyFormatter.php deleted file mode 100644 index 56689db0..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/NotEmptyFormatter.php +++ /dev/null @@ -1,18 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - - return sprintf('notEmpty(%s)', $this->normalize($matcher->getValue())); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/NullValueFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/NullValueFormatter.php deleted file mode 100644 index c08ea8e1..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/NullValueFormatter.php +++ /dev/null @@ -1,13 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - $value = $matcher->getValue(); - if (null === $value) { - throw new InvalidValueException(sprintf("Number formatter doesn't support value of type %s", gettype($value))); - } - - return sprintf('matching(number, %s)', $this->normalize($value)); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/RegexFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/RegexFormatter.php deleted file mode 100644 index 4ebbde5f..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/RegexFormatter.php +++ /dev/null @@ -1,23 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - $value = $matcher->getValue(); - if (!is_string($value)) { - throw new InvalidValueException(sprintf("Regex formatter doesn't support value of type %s", gettype($value))); - } - - return sprintf("matching(regex, %s, %s)", $this->normalize($matcher->getRegex()), $this->normalize($value)); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/SemverFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/SemverFormatter.php deleted file mode 100644 index 5f323fb2..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/SemverFormatter.php +++ /dev/null @@ -1,23 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - $value = $matcher->getValue(); - if (!is_string($value)) { - throw new InvalidValueException(sprintf("Semver formatter doesn't support value of type %s", gettype($value))); - } - - return sprintf('matching(semver, %s)', $this->normalize($value)); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/StringValueFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/StringValueFormatter.php deleted file mode 100644 index c7e32bc5..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/StringValueFormatter.php +++ /dev/null @@ -1,18 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - - return sprintf('matching(type, %s)', $this->normalize($matcher->getValue())); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Expression/TypeFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Expression/TypeFormatter.php deleted file mode 100644 index 4a51435b..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Expression/TypeFormatter.php +++ /dev/null @@ -1,18 +0,0 @@ -getMatcherNotSupportedException($matcher); - } - - return sprintf('matching(type, %s)', $this->normalize($matcher->getValue())); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Json/ContentTypeFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Json/ContentTypeFormatter.php deleted file mode 100644 index 8359e3da..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Json/ContentTypeFormatter.php +++ /dev/null @@ -1,26 +0,0 @@ - - */ - public function format(MatcherInterface $matcher): array - { - if (!$matcher instanceof ContentType) { - throw new MatcherNotSupportedException(sprintf('Matcher %s is not supported by %s', $matcher->getType(), self::class)); - } - - return [ - 'pact:matcher:type' => $matcher->getType(), - 'value' => $matcher->getContentType(), - ]; - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Json/HasGeneratorFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Json/HasGeneratorFormatter.php deleted file mode 100644 index 8583c25d..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Json/HasGeneratorFormatter.php +++ /dev/null @@ -1,32 +0,0 @@ - - */ - public function format(MatcherInterface $matcher): array - { - if (!$matcher instanceof GeneratorAwareInterface) { - throw new MatcherNotSupportedException(sprintf('Matcher %s is not supported by %s', $matcher->getType(), self::class)); - } - - $generator = $matcher->getGenerator(); - - if ($generator) { - return [ - 'pact:matcher:type' => $matcher->getType(), - 'pact:generator:type' => $generator->getType(), - ...$matcher->getAttributes()->merge($generator->getAttributes())->getData(), - ]; - } - - return parent::format($matcher); - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Json/JsonFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Json/JsonFormatter.php new file mode 100644 index 00000000..ade01d0f --- /dev/null +++ b/src/PhpPact/Consumer/Matcher/Formatters/Json/JsonFormatter.php @@ -0,0 +1,21 @@ +formatJson(); + } +} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Json/MatchAllFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Json/MatchAllFormatter.php deleted file mode 100644 index 4a8c0e63..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Json/MatchAllFormatter.php +++ /dev/null @@ -1,26 +0,0 @@ - - */ - public function format(MatcherInterface $matcher): array - { - if (!$matcher instanceof MatchAll) { - throw new MatcherNotSupportedException(sprintf('Matcher %s is not supported by %s', $matcher->getType(), self::class)); - } - - return [ - 'pact:matcher:type' => $matcher->getMatchers(), - 'value' => $matcher->getValue(), - ]; - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Json/MaxTypeFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Json/MaxTypeFormatter.php deleted file mode 100644 index 141c7327..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Json/MaxTypeFormatter.php +++ /dev/null @@ -1,27 +0,0 @@ - - */ - public function format(MatcherInterface $matcher): array - { - if (!$matcher instanceof MaxType) { - throw new MatcherNotSupportedException(sprintf('Matcher %s is not supported by %s', $matcher->getType(), self::class)); - } - - return [ - 'pact:matcher:type' => $matcher->getType(), - 'max' => $matcher->getMax(), - 'value' => [$matcher->getValue()], - ]; - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Json/MinMaxTypeFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Json/MinMaxTypeFormatter.php deleted file mode 100644 index 27198f0a..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Json/MinMaxTypeFormatter.php +++ /dev/null @@ -1,29 +0,0 @@ - - */ - public function format(MatcherInterface $matcher): array - { - if (!$matcher instanceof MinMaxType) { - throw new MatcherNotSupportedException(sprintf('Matcher %s is not supported by %s', $matcher->getType(), self::class)); - } - $examples = max($matcher->getMin(), 1); // Min can be zero, but number of examples must be at least 1 - - return [ - 'pact:matcher:type' => $matcher->getType(), - 'min' => $matcher->getMin(), - 'max' => $matcher->getMax(), - 'value' => array_fill(0, $examples, $matcher->getValue()), - ]; - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Json/MinTypeFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Json/MinTypeFormatter.php deleted file mode 100644 index f3079c86..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Json/MinTypeFormatter.php +++ /dev/null @@ -1,28 +0,0 @@ - - */ - public function format(MatcherInterface $matcher): array - { - if (!$matcher instanceof MinType) { - throw new MatcherNotSupportedException(sprintf('Matcher %s is not supported by %s', $matcher->getType(), self::class)); - } - $examples = max($matcher->getMin(), 1); // Min can be zero, but number of examples must be at least 1 - - return [ - 'pact:matcher:type' => $matcher->getType(), - 'min' => $matcher->getMin(), - 'value' => array_fill(0, $examples, $matcher->getValue()), - ]; - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Json/NoGeneratorFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Json/NoGeneratorFormatter.php deleted file mode 100644 index 74e1d9d4..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Json/NoGeneratorFormatter.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ - public function format(MatcherInterface $matcher): array - { - return [ - 'pact:matcher:type' => $matcher->getType(), - ...$matcher->getAttributes()->getData(), - 'value' => $matcher->getValue(), - ]; - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Json/NullValueFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Json/NullValueFormatter.php deleted file mode 100644 index e90426f2..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Json/NullValueFormatter.php +++ /dev/null @@ -1,19 +0,0 @@ - - */ - public function format(MatcherInterface $matcher): array - { - return [ - 'pact:matcher:type' => 'null', - ]; - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Json/StringValueFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Json/StringValueFormatter.php deleted file mode 100644 index beedb0b9..00000000 --- a/src/PhpPact/Consumer/Matcher/Formatters/Json/StringValueFormatter.php +++ /dev/null @@ -1,25 +0,0 @@ - - */ - public function format(MatcherInterface $matcher): array - { - if (!$matcher instanceof StringValue) { - throw new MatcherNotSupportedException(sprintf('Matcher %s is not supported by %s', $matcher->getType(), self::class)); - } - - return [ - ...parent::format($matcher), - 'value' => $matcher->getValue(), - ]; - } -} diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Xml/XmlContentFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Xml/XmlContentFormatter.php index 859f0a97..2e87ed7b 100644 --- a/src/PhpPact/Consumer/Matcher/Formatters/Xml/XmlContentFormatter.php +++ b/src/PhpPact/Consumer/Matcher/Formatters/Xml/XmlContentFormatter.php @@ -2,31 +2,35 @@ namespace PhpPact\Consumer\Matcher\Formatters\Xml; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; +use PhpPact\Consumer\Matcher\Formatters\Json\JsonFormatter; use PhpPact\Consumer\Matcher\Model\Attributes; -use PhpPact\Consumer\Matcher\Model\FormatterInterface; -use PhpPact\Consumer\Matcher\Model\GeneratorAwareInterface; use PhpPact\Consumer\Matcher\Model\MatcherInterface; -class XmlContentFormatter implements FormatterInterface +class XmlContentFormatter extends JsonFormatter { - /** - * @return array - */ - public function format(MatcherInterface $matcher): array + public function format(MatcherInterface $matcher): Attributes { - $generator = $matcher instanceof GeneratorAwareInterface ? $matcher->getGenerator() : null; - $data = [ - 'content' => $matcher->getValue(), - 'matcher' => [ - 'pact:matcher:type' => $matcher->getType(), - ...$matcher->getAttributes()->merge($generator ? $generator->getAttributes() : new Attributes($matcher))->getData(), - ], - ]; + $attributes = parent::format($matcher); + $data = []; + foreach ($attributes as $key => $value) { + switch ($key) { + case 'value': + if (!is_string($value) && !is_float($value) && !is_int($value) && !is_bool($value) && !is_null($value)) { + throw new InvalidValueException('Value of xml content must be string, float, int, bool or null'); + } + $data['content'] = $value; + break; - if ($generator) { - $data['pact:generator:type'] = $generator->getType(); + case 'pact:generator:type': + $data['pact:generator:type'] = $value; + break; + default: + $data['matcher'][$key] = $value; + break; + } } - return $data; + return new Attributes($data); } } diff --git a/src/PhpPact/Consumer/Matcher/Formatters/Xml/XmlElementFormatter.php b/src/PhpPact/Consumer/Matcher/Formatters/Xml/XmlElementFormatter.php index 05ef94c2..9178ab92 100644 --- a/src/PhpPact/Consumer/Matcher/Formatters/Xml/XmlElementFormatter.php +++ b/src/PhpPact/Consumer/Matcher/Formatters/Xml/XmlElementFormatter.php @@ -3,34 +3,28 @@ namespace PhpPact\Consumer\Matcher\Formatters\Xml; use PhpPact\Consumer\Matcher\Exception\InvalidValueException; -use PhpPact\Consumer\Matcher\Model\FormatterInterface; +use PhpPact\Consumer\Matcher\Formatters\Json\JsonFormatter; +use PhpPact\Consumer\Matcher\Model\Attributes; use PhpPact\Consumer\Matcher\Model\MatcherInterface; use PhpPact\Xml\XmlElement; -class XmlElementFormatter implements FormatterInterface +class XmlElementFormatter extends JsonFormatter { - /** - * @return array - */ - public function format(MatcherInterface $matcher): array + public function format(MatcherInterface $matcher): Attributes { - $value = $matcher->getValue(); + $attributes = parent::format($matcher); + $value = $attributes->get('value'); if (!$value instanceof XmlElement) { throw new InvalidValueException('Value must be xml element'); } - $result = [ - 'pact:matcher:type' => $matcher->getType(), - ...$matcher->getAttributes()->getData(), - 'value' => $matcher->getValue(), - ]; $examples = $value->getExamples(); if (null !== $examples) { - $result['examples'] = $examples; + $attributes->set('examples', $examples); $value->setExamples(null); } - return $result; + return $attributes; } } diff --git a/src/PhpPact/Consumer/Matcher/Generators/AbstractDateTime.php b/src/PhpPact/Consumer/Matcher/Generators/AbstractDateTime.php index 45d8e43b..1d7aecdf 100644 --- a/src/PhpPact/Consumer/Matcher/Generators/AbstractDateTime.php +++ b/src/PhpPact/Consumer/Matcher/Generators/AbstractDateTime.php @@ -2,18 +2,21 @@ namespace PhpPact\Consumer\Matcher\Generators; -abstract class AbstractDateTime extends AbstractGenerator +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Generator\JsonFormattableInterface; +use PhpPact\Consumer\Matcher\Model\GeneratorInterface; + +abstract class AbstractDateTime implements GeneratorInterface, JsonFormattableInterface { public function __construct(private ?string $format = null, private ?string $expression = null) { } - /** - * @return array - */ - protected function getAttributesData(): array + public function formatJson(): Attributes { - $data = []; + $data = [ + 'pact:generator:type' => $this->getType(), + ]; if ($this->format !== null) { $data['format'] = $this->format; } @@ -22,6 +25,8 @@ protected function getAttributesData(): array $data['expression'] = $this->expression; } - return $data; + return new Attributes($data); } + + abstract protected function getType(): string; } diff --git a/src/PhpPact/Consumer/Matcher/Generators/AbstractGenerator.php b/src/PhpPact/Consumer/Matcher/Generators/AbstractGenerator.php index df1a956c..ce270693 100644 --- a/src/PhpPact/Consumer/Matcher/Generators/AbstractGenerator.php +++ b/src/PhpPact/Consumer/Matcher/Generators/AbstractGenerator.php @@ -2,18 +2,8 @@ namespace PhpPact\Consumer\Matcher\Generators; -use PhpPact\Consumer\Matcher\Model\Attributes; use PhpPact\Consumer\Matcher\Model\GeneratorInterface; abstract class AbstractGenerator implements GeneratorInterface { - public function getAttributes(): Attributes - { - return new Attributes($this, $this->getAttributesData()); - } - - /** - * @return array - */ - abstract protected function getAttributesData(): array; } diff --git a/src/PhpPact/Consumer/Matcher/Generators/Date.php b/src/PhpPact/Consumer/Matcher/Generators/Date.php index 942f3c76..9caefa82 100644 --- a/src/PhpPact/Consumer/Matcher/Generators/Date.php +++ b/src/PhpPact/Consumer/Matcher/Generators/Date.php @@ -16,7 +16,7 @@ */ class Date extends AbstractDateTime { - public function getType(): string + protected function getType(): string { return 'Date'; } diff --git a/src/PhpPact/Consumer/Matcher/Generators/DateTime.php b/src/PhpPact/Consumer/Matcher/Generators/DateTime.php index 7c71e2fa..8d682ed9 100644 --- a/src/PhpPact/Consumer/Matcher/Generators/DateTime.php +++ b/src/PhpPact/Consumer/Matcher/Generators/DateTime.php @@ -16,7 +16,7 @@ */ class DateTime extends AbstractDateTime { - public function getType(): string + protected function getType(): string { return 'DateTime'; } diff --git a/src/PhpPact/Consumer/Matcher/Generators/MockServerURL.php b/src/PhpPact/Consumer/Matcher/Generators/MockServerURL.php index 14430870..5af0fd33 100644 --- a/src/PhpPact/Consumer/Matcher/Generators/MockServerURL.php +++ b/src/PhpPact/Consumer/Matcher/Generators/MockServerURL.php @@ -2,31 +2,28 @@ namespace PhpPact\Consumer\Matcher\Generators; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Generator\JsonFormattableInterface; +use PhpPact\Consumer\Matcher\Model\GeneratorInterface; + /** * Generates a URL with the mock server as the base URL. * * Example regex: .*(/path)$ * Example example: http://localhost:1234/path */ -class MockServerURL extends AbstractGenerator +class MockServerURL implements GeneratorInterface, JsonFormattableInterface { public function __construct(private string $regex, private string $example) { } - public function getType(): string - { - return 'MockServerURL'; - } - - /** - * @return array - */ - protected function getAttributesData(): array + public function formatJson(): Attributes { - return [ + return new Attributes([ + 'pact:generator:type' => 'MockServerURL', 'regex' => $this->regex, 'example' => $this->example, - ]; + ]); } } diff --git a/src/PhpPact/Consumer/Matcher/Generators/ProviderState.php b/src/PhpPact/Consumer/Matcher/Generators/ProviderState.php index 0c364421..8efad2bb 100644 --- a/src/PhpPact/Consumer/Matcher/Generators/ProviderState.php +++ b/src/PhpPact/Consumer/Matcher/Generators/ProviderState.php @@ -2,29 +2,25 @@ namespace PhpPact\Consumer\Matcher\Generators; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Generator\JsonFormattableInterface; + /** * Generates a value that is looked up from the provider state context using the given expression * * Example expression: /products/${id} */ -class ProviderState extends AbstractGenerator +class ProviderState extends AbstractGenerator implements JsonFormattableInterface { public function __construct(private string $expression) { } - public function getType(): string - { - return 'ProviderState'; - } - - /** - * @return array - */ - protected function getAttributesData(): array + public function formatJson(): Attributes { - return [ + return new Attributes([ + 'pact:generator:type' => 'ProviderState', 'expression' => $this->expression, - ]; + ]); } } diff --git a/src/PhpPact/Consumer/Matcher/Generators/RandomBoolean.php b/src/PhpPact/Consumer/Matcher/Generators/RandomBoolean.php index 7bbe480a..ce830eaa 100644 --- a/src/PhpPact/Consumer/Matcher/Generators/RandomBoolean.php +++ b/src/PhpPact/Consumer/Matcher/Generators/RandomBoolean.php @@ -2,21 +2,19 @@ namespace PhpPact\Consumer\Matcher\Generators; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Generator\JsonFormattableInterface; +use PhpPact\Consumer\Matcher\Model\GeneratorInterface; + /** * Generates a random boolean value */ -class RandomBoolean extends AbstractGenerator +class RandomBoolean implements GeneratorInterface, JsonFormattableInterface { - public function getType(): string - { - return 'RandomBoolean'; - } - - /** - * @return array - */ - protected function getAttributesData(): array + public function formatJson(): Attributes { - return []; + return new Attributes([ + 'pact:generator:type' => 'RandomBoolean', + ]); } } diff --git a/src/PhpPact/Consumer/Matcher/Generators/RandomDecimal.php b/src/PhpPact/Consumer/Matcher/Generators/RandomDecimal.php index 0965578e..6b20ef79 100644 --- a/src/PhpPact/Consumer/Matcher/Generators/RandomDecimal.php +++ b/src/PhpPact/Consumer/Matcher/Generators/RandomDecimal.php @@ -2,27 +2,24 @@ namespace PhpPact\Consumer\Matcher\Generators; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Generator\JsonFormattableInterface; +use PhpPact\Consumer\Matcher\Model\GeneratorInterface; + /** * Generates a random big decimal value with the provided number of digits */ -class RandomDecimal extends AbstractGenerator +class RandomDecimal implements GeneratorInterface, JsonFormattableInterface { public function __construct(private int $digits = 10) { } - public function getType(): string - { - return 'RandomDecimal'; - } - - /** - * @return array - */ - protected function getAttributesData(): array + public function formatJson(): Attributes { - return [ + return new Attributes([ + 'pact:generator:type' => 'RandomDecimal', 'digits' => $this->digits, - ]; + ]); } } diff --git a/src/PhpPact/Consumer/Matcher/Generators/RandomHexadecimal.php b/src/PhpPact/Consumer/Matcher/Generators/RandomHexadecimal.php index 8a4a990f..dc160c6c 100644 --- a/src/PhpPact/Consumer/Matcher/Generators/RandomHexadecimal.php +++ b/src/PhpPact/Consumer/Matcher/Generators/RandomHexadecimal.php @@ -2,27 +2,24 @@ namespace PhpPact\Consumer\Matcher\Generators; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Generator\JsonFormattableInterface; +use PhpPact\Consumer\Matcher\Model\GeneratorInterface; + /** * Generates a random hexadecimal value of the given number of digits */ -class RandomHexadecimal extends AbstractGenerator +class RandomHexadecimal implements GeneratorInterface, JsonFormattableInterface { public function __construct(private int $digits = 10) { } - public function getType(): string - { - return 'RandomHexadecimal'; - } - - /** - * @return array - */ - protected function getAttributesData(): array + public function formatJson(): Attributes { - return [ + return new Attributes([ + 'pact:generator:type' => 'RandomHexadecimal', 'digits' => $this->digits, - ]; + ]); } } diff --git a/src/PhpPact/Consumer/Matcher/Generators/RandomInt.php b/src/PhpPact/Consumer/Matcher/Generators/RandomInt.php index 2f4ea5e8..210225b4 100644 --- a/src/PhpPact/Consumer/Matcher/Generators/RandomInt.php +++ b/src/PhpPact/Consumer/Matcher/Generators/RandomInt.php @@ -2,28 +2,25 @@ namespace PhpPact\Consumer\Matcher\Generators; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Generator\JsonFormattableInterface; +use PhpPact\Consumer\Matcher\Model\GeneratorInterface; + /** * Generates a random integer between the min and max values (inclusive) */ -class RandomInt extends AbstractGenerator +class RandomInt implements GeneratorInterface, JsonFormattableInterface { public function __construct(private int $min = 0, private int $max = 10) { } - public function getType(): string - { - return 'RandomInt'; - } - - /** - * @return array - */ - protected function getAttributesData(): array + public function formatJson(): Attributes { - return [ + return new Attributes([ + 'pact:generator:type' => 'RandomInt', 'min' => $this->min, 'max' => $this->max, - ]; + ]); } } diff --git a/src/PhpPact/Consumer/Matcher/Generators/RandomString.php b/src/PhpPact/Consumer/Matcher/Generators/RandomString.php index 911f7363..79ae10dd 100644 --- a/src/PhpPact/Consumer/Matcher/Generators/RandomString.php +++ b/src/PhpPact/Consumer/Matcher/Generators/RandomString.php @@ -2,27 +2,24 @@ namespace PhpPact\Consumer\Matcher\Generators; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Generator\JsonFormattableInterface; +use PhpPact\Consumer\Matcher\Model\GeneratorInterface; + /** * Generates a random alphanumeric string of the provided length */ -class RandomString extends AbstractGenerator +class RandomString implements GeneratorInterface, JsonFormattableInterface { public function __construct(private int $size = 10) { } - public function getType(): string - { - return 'RandomString'; - } - - /** - * @return array - */ - protected function getAttributesData(): array + public function formatJson(): Attributes { - return [ + return new Attributes([ + 'pact:generator:type' => 'RandomString', 'size' => $this->size, - ]; + ]); } } diff --git a/src/PhpPact/Consumer/Matcher/Generators/Regex.php b/src/PhpPact/Consumer/Matcher/Generators/Regex.php index c80c3b18..bcdebb36 100644 --- a/src/PhpPact/Consumer/Matcher/Generators/Regex.php +++ b/src/PhpPact/Consumer/Matcher/Generators/Regex.php @@ -2,27 +2,24 @@ namespace PhpPact\Consumer\Matcher\Generators; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Generator\JsonFormattableInterface; +use PhpPact\Consumer\Matcher\Model\GeneratorInterface; + /** * Generates a random string from the provided regular expression */ -class Regex extends AbstractGenerator +class Regex implements GeneratorInterface, JsonFormattableInterface { public function __construct(private string $regex) { } - public function getType(): string - { - return 'Regex'; - } - - /** - * @return array - */ - protected function getAttributesData(): array + public function formatJson(): Attributes { - return [ + return new Attributes([ + 'pact:generator:type' => 'Regex', 'regex' => $this->regex, - ]; + ]); } } diff --git a/src/PhpPact/Consumer/Matcher/Generators/Time.php b/src/PhpPact/Consumer/Matcher/Generators/Time.php index 2ce4e359..a535ae0a 100644 --- a/src/PhpPact/Consumer/Matcher/Generators/Time.php +++ b/src/PhpPact/Consumer/Matcher/Generators/Time.php @@ -16,7 +16,7 @@ */ class Time extends AbstractDateTime { - public function getType(): string + protected function getType(): string { return 'Time'; } diff --git a/src/PhpPact/Consumer/Matcher/Generators/Uuid.php b/src/PhpPact/Consumer/Matcher/Generators/Uuid.php index 80b52591..750d0027 100644 --- a/src/PhpPact/Consumer/Matcher/Generators/Uuid.php +++ b/src/PhpPact/Consumer/Matcher/Generators/Uuid.php @@ -4,6 +4,9 @@ use PhpPact\Consumer\Matcher\Enum\UuidFormat; use PhpPact\Consumer\Matcher\Exception\InvalidUuidFormatException; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Generator\JsonFormattableInterface; +use PhpPact\Consumer\Matcher\Model\GeneratorInterface; /** * Generates a random UUID. @@ -13,7 +16,7 @@ * - upper-case-hyphenated (e.g 936DA01F-9ABD-4D9D-80C7-02AF85C822A8) * - URN (e.g. urn:uuid:936da01f-9abd-4d9d-80c7-02af85c822a8) */ -class Uuid extends AbstractGenerator +class Uuid implements GeneratorInterface, JsonFormattableInterface { /** * @deprecated Use UuidFormat::SIMPLE instead @@ -34,6 +37,9 @@ class Uuid extends AbstractGenerator private null|UuidFormat $format; + /** + * @param null|string|UuidFormat $format Default to lower-case-hyphenated if null + */ public function __construct(null|string|UuidFormat $format = null) { if (is_string($format)) { @@ -50,18 +56,11 @@ public function __construct(null|string|UuidFormat $format = null) $this->format = $format; } - public function getType(): string - { - return 'Uuid'; - } - - /** - * @return array - */ - protected function getAttributesData(): array + public function formatJson(): Attributes { - return $this->format !== null ? [ - 'format' => $this->format->value, - ] : []; + return new Attributes([ + 'pact:generator:type' => 'Uuid', + ...(is_null($this->format) ? [] : ['format' => $this->format->value]), + ]); } } diff --git a/src/PhpPact/Consumer/Matcher/Matcher.php b/src/PhpPact/Consumer/Matcher/Matcher.php index 92e8629f..ac63860b 100644 --- a/src/PhpPact/Consumer/Matcher/Matcher.php +++ b/src/PhpPact/Consumer/Matcher/Matcher.php @@ -4,6 +4,8 @@ use PhpPact\Consumer\Matcher\Enum\HttpStatus; use PhpPact\Consumer\Matcher\Exception\MatcherException; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; +use PhpPact\Consumer\Matcher\Formatters\Json\JsonFormatter; use PhpPact\Consumer\Matcher\Generators\MockServerURL; use PhpPact\Consumer\Matcher\Generators\ProviderState; use PhpPact\Consumer\Matcher\Generators\RandomHexadecimal; @@ -21,7 +23,9 @@ use PhpPact\Consumer\Matcher\Matchers\Integer; use PhpPact\Consumer\Matcher\Matchers\MatchAll; use PhpPact\Consumer\Matcher\Matchers\MatchingField; +use PhpPact\Consumer\Matcher\Matchers\Max; use PhpPact\Consumer\Matcher\Matchers\MaxType; +use PhpPact\Consumer\Matcher\Matchers\Min; use PhpPact\Consumer\Matcher\Matchers\MinMaxType; use PhpPact\Consumer\Matcher\Matchers\MinType; use PhpPact\Consumer\Matcher\Matchers\NotEmpty; @@ -34,6 +38,7 @@ use PhpPact\Consumer\Matcher\Matchers\Time; use PhpPact\Consumer\Matcher\Matchers\Type; use PhpPact\Consumer\Matcher\Matchers\Values; +use PhpPact\Consumer\Matcher\Model\FormatterInterface; use PhpPact\Consumer\Matcher\Model\GeneratorAwareInterface; use PhpPact\Consumer\Matcher\Model\MatcherInterface; @@ -61,7 +66,7 @@ public function __construct(private bool $plugin = false) /** * Alias for the `like()` function. */ - public function somethingLike(mixed $value): MatcherInterface + public function somethingLike(mixed $value): Type { return $this->like($value); } @@ -69,15 +74,17 @@ public function somethingLike(mixed $value): MatcherInterface /** * This executes a type based match against the values, that is, they are equal if they are the same type. */ - public function like(mixed $value): MatcherInterface + public function like(mixed $value): Type { - return $this->withFormatter(new Type($value)); + $matcher = new Type($value); + + return $matcher->withFormatter($this->createFormatter()); } /** * Array where each element must match the given value */ - public function eachLike(mixed $value): MatcherInterface + public function eachLike(mixed $value): MinType { return $this->atLeastLike($value, 1); } @@ -85,7 +92,7 @@ public function eachLike(mixed $value): MatcherInterface /** * An array that has to have at least one element and each element must match the given value */ - public function atLeastOneLike(mixed $value): MatcherInterface + public function atLeastOneLike(mixed $value): MinType { return $this->atLeastLike($value, 1); } @@ -93,25 +100,31 @@ public function atLeastOneLike(mixed $value): MatcherInterface /** * An array that has to have at least the required number of elements and each element must match the given value */ - public function atLeastLike(mixed $value, int $min): MatcherInterface + public function atLeastLike(mixed $value, int $min): MinType { - return $this->withFormatter(new MinType($value, $min)); + $matcher = new MinType($value, $min); + + return $matcher->withFormatter($this->createFormatter()); } /** * An array that has to have at most the required number of elements and each element must match the given value */ - public function atMostLike(mixed $value, int $max): MatcherInterface + public function atMostLike(mixed $value, int $max): MaxType { - return $this->withFormatter(new MaxType($value, $max)); + $matcher = new MaxType($value, $max); + + return $matcher->withFormatter($this->createFormatter()); } /** * An array whose size is constrained to the minimum and maximum number of elements and each element must match the given value */ - public function constrainedArrayLike(mixed $value, int $min, int $max): MatcherInterface + public function constrainedArrayLike(mixed $value, int $min, int $max): MinMaxType { - return $this->withFormatter(new MinMaxType($value, $min, $max)); + $matcher = new MinMaxType($value, $min, $max); + + return $matcher->withFormatter($this->createFormatter()); } /** @@ -121,9 +134,11 @@ public function constrainedArrayLike(mixed $value, int $min, int $max): MatcherI * * @throws MatcherException */ - public function term(string|array|null $values, string $pattern): MatcherInterface + public function term(string|array|null $values, string $pattern): Regex { - return $this->withFormatter(new Regex($pattern, $values)); + $matcher = new Regex($pattern, $values); + + return $matcher->withFormatter($this->createFormatter()); } /** @@ -133,7 +148,7 @@ public function term(string|array|null $values, string $pattern): MatcherInterfa * * @throws MatcherException */ - public function regex(string|array|null $values, string $pattern): MatcherInterface + public function regex(string|array|null $values, string $pattern): Regex { return $this->term($values, $pattern); } @@ -145,7 +160,7 @@ public function regex(string|array|null $values, string $pattern): MatcherInterf * * @throws MatcherException */ - public function dateISO8601(string $value = '2013-02-01'): MatcherInterface + public function dateISO8601(string $value = '2013-02-01'): Regex { return $this->term($value, self::ISO8601_DATE_FORMAT); } @@ -157,7 +172,7 @@ public function dateISO8601(string $value = '2013-02-01'): MatcherInterface * * @throws MatcherException */ - public function timeISO8601(string $value = 'T22:44:30.652Z'): MatcherInterface + public function timeISO8601(string $value = 'T22:44:30.652Z'): Regex { return $this->term($value, self::ISO8601_TIME_FORMAT); } @@ -169,7 +184,7 @@ public function timeISO8601(string $value = 'T22:44:30.652Z'): MatcherInterface * * @throws MatcherException */ - public function dateTimeISO8601(string $value = '2015-08-06T16:53:10+01:00'): MatcherInterface + public function dateTimeISO8601(string $value = '2015-08-06T16:53:10+01:00'): Regex { return $this->term($value, self::ISO8601_DATETIME_FORMAT); } @@ -181,7 +196,7 @@ public function dateTimeISO8601(string $value = '2015-08-06T16:53:10+01:00'): Ma * * @throws MatcherException */ - public function dateTimeWithMillisISO8601(string $value = '2015-08-06T16:53:10.123+01:00'): MatcherInterface + public function dateTimeWithMillisISO8601(string $value = '2015-08-06T16:53:10.123+01:00'): Regex { return $this->term($value, self::ISO8601_DATETIME_WITH_MILLIS_FORMAT); } @@ -193,80 +208,82 @@ public function dateTimeWithMillisISO8601(string $value = '2015-08-06T16:53:10.1 * * @throws MatcherException */ - public function timestampRFC3339(string $value = 'Mon, 31 Oct 2016 15:21:41 -0400'): MatcherInterface + public function timestampRFC3339(string $value = 'Mon, 31 Oct 2016 15:21:41 -0400'): Regex { return $this->term($value, self::RFC3339_TIMESTAMP_FORMAT); } - public function boolean(): MatcherInterface + public function boolean(): Type { return $this->like(true); } - public function integer(int $int = 13): MatcherInterface + public function integer(int $int = 13): Type { return $this->like($int); } - public function decimal(float $float = 13.01): MatcherInterface + public function decimal(float $float = 13.01): Type { return $this->like($float); } - public function booleanV3(?bool $value = null): MatcherInterface + public function booleanV3(?bool $value = null): Boolean { - return $this->withFormatter(new Boolean($value)); + $matcher = new Boolean($value); + + return $matcher->withFormatter($this->createFormatter()); } - public function integerV3(?int $value = null): MatcherInterface + public function integerV3(?int $value = null): Integer { - return $this->withFormatter(new Integer($value)); + $matcher = new Integer($value); + + return $matcher->withFormatter($this->createFormatter()); } - public function decimalV3(?float $value = null): MatcherInterface + public function decimalV3(?float $value = null): Decimal { - return $this->withFormatter(new Decimal($value)); + $matcher = new Decimal($value); + + return $matcher->withFormatter($this->createFormatter()); } /** * @throws MatcherException */ - public function hexadecimal(?string $value = null): MatcherInterface + public function hexadecimal(?string $value = null): Regex { $matcher = new Regex(self::HEX_FORMAT, $value); - if (null === $value) { - $matcher->setGenerator(new RandomHexadecimal()); - } - - return $this->withFormatter($matcher); + return $matcher + ->withGenerator(is_null($value) ? new RandomHexadecimal() : null) + ->withFormatter($this->createFormatter()); } /** * @throws MatcherException */ - public function uuid(?string $value = null): MatcherInterface + public function uuid(?string $value = null): Regex { $matcher = new Regex(self::UUID_V4_FORMAT, $value); - if (null === $value) { - $matcher->setGenerator(new Uuid()); - } - - return $this->withFormatter($matcher); + return $matcher + ->withGenerator(is_null($value) ? new Uuid() : null) + ->withFormatter($this->createFormatter()); } - public function ipv4Address(?string $ip = '127.0.0.13'): MatcherInterface + public function ipv4Address(?string $ip = '127.0.0.13'): Regex { return $this->term($ip, self::IPV4_FORMAT); } - public function ipv6Address(?string $ip = '::ffff:192.0.2.128'): MatcherInterface + public function ipv6Address(?string $ip = '::ffff:192.0.2.128'): Regex { return $this->term($ip, self::IPV6_FORMAT); } - public function email(?string $email = 'hello@pact.io'): MatcherInterface + public function email(?string $email = 'hello@pact.io'): Regex { return $this->term($email, self::EMAIL_FORMAT); } @@ -275,9 +292,11 @@ public function email(?string $email = 'hello@pact.io'): MatcherInterface * Value that must be null. This will only match the JSON Null value. For other content types, it will * match if the attribute is missing. */ - public function nullValue(): MatcherInterface + public function nullValue(): NullValue { - return $this->withFormatter(new NullValue()); + $matcher = new NullValue(); + + return $matcher->withFormatter($this->createFormatter()); } /** @@ -287,9 +306,11 @@ public function nullValue(): MatcherInterface * For Java one, see https://www.digitalocean.com/community/tutorials/java-simpledateformat-java-date-format#patterns * For PHP one, see https://www.php.net/manual/en/datetime.format.php#refsect1-datetime.format-parameters */ - public function date(string $format = 'yyyy-MM-dd', ?string $value = null): MatcherInterface + public function date(string $format = 'yyyy-MM-dd', ?string $value = null): Date { - return $this->withFormatter(new Date($format, $value)); + $matcher = new Date($format, $value); + + return $matcher->withFormatter($this->createFormatter()); } /** @@ -299,9 +320,11 @@ public function date(string $format = 'yyyy-MM-dd', ?string $value = null): Matc * For Java one, see https://www.digitalocean.com/community/tutorials/java-simpledateformat-java-date-format#patterns * For PHP one, see https://www.php.net/manual/en/datetime.format.php#refsect1-datetime.format-parameters */ - public function time(string $format = 'HH:mm:ss', ?string $value = null): MatcherInterface + public function time(string $format = 'HH:mm:ss', ?string $value = null): Time { - return $this->withFormatter(new Time($format, $value)); + $matcher = new Time($format, $value); + + return $matcher->withFormatter($this->createFormatter()); } /** @@ -311,40 +334,46 @@ public function time(string $format = 'HH:mm:ss', ?string $value = null): Matche * For Java one, see https://www.digitalocean.com/community/tutorials/java-simpledateformat-java-date-format#patterns * For PHP one, see https://www.php.net/manual/en/datetime.format.php#refsect1-datetime.format-parameters */ - public function datetime(string $format = "yyyy-MM-dd'T'HH:mm:ss", ?string $value = null): MatcherInterface + public function datetime(string $format = "yyyy-MM-dd'T'HH:mm:ss", ?string $value = null): DateTime { - return $this->withFormatter(new DateTime($format, $value)); + $matcher = new DateTime($format, $value); + + return $matcher->withFormatter($this->createFormatter()); } - public function string(?string $value = null): MatcherInterface + public function string(?string $value = null): StringValue { - return $this->withFormatter(new StringValue($value)); + $matcher = new StringValue($value); + + return $matcher->withFormatter($this->createFormatter()); } /** * Generates a value that is looked up from the provider state context using the given expression */ - public function fromProviderState(MatcherInterface&GeneratorAwareInterface $matcher, string $expression): MatcherInterface + public function fromProviderState(MatcherInterface&GeneratorAwareInterface $matcher, string $expression): MatcherInterface&GeneratorAwareInterface { - $matcher->setGenerator(new ProviderState($expression)); - - return $matcher; + return $matcher->withGenerator(new ProviderState($expression)); } /** * Value that must be equal to the example. This is mainly used to reset the matching rules which cascade. */ - public function equal(mixed $value): MatcherInterface + public function equal(mixed $value): Equality { - return $this->withFormatter(new Equality($value)); + $matcher = new Equality($value); + + return $matcher->withFormatter($this->createFormatter()); } /** * Value that must include the example value as a substring. */ - public function includes(string $value): MatcherInterface + public function includes(string $value): Includes { - return $this->withFormatter(new Includes($value)); + $matcher = new Includes($value); + + return $matcher->withFormatter($this->createFormatter()); } /** @@ -352,9 +381,11 @@ public function includes(string $value): MatcherInterface * * @param int|float|null $value Example value. If omitted a random integer value will be generated. */ - public function number(int|float|null $value = null): MatcherInterface + public function number(int|float|null $value = null): Number { - return $this->withFormatter(new Number($value)); + $matcher = new Number($value); + + return $matcher->withFormatter($this->createFormatter()); } /** @@ -363,33 +394,41 @@ public function number(int|float|null $value = null): MatcherInterface * * @param array $variants */ - public function arrayContaining(array $variants): MatcherInterface + public function arrayContaining(array $variants): ArrayContains { - return $this->withFormatter(new ArrayContains($variants)); + $matcher = new ArrayContains($variants); + + return $matcher->withFormatter($this->createFormatter()); } /** * Value must be present and not empty (not null or the empty string or empty array or empty object) */ - public function notEmpty(mixed $value): MatcherInterface + public function notEmpty(mixed $value): NotEmpty { - return $this->withFormatter(new NotEmpty($value)); + $matcher = new NotEmpty($value); + + return $matcher->withFormatter($this->createFormatter()); } /** * Value must be valid based on the semver specification */ - public function semver(string $value): MatcherInterface + public function semver(?string $value = null): Semver { - return $this->withFormatter(new Semver($value)); + $matcher = new Semver($value); + + return $matcher->withFormatter($this->createFormatter()); } /** * Matches the response status code. */ - public function statusCode(string|HttpStatus $status, ?int $value = null): MatcherInterface + public function statusCode(string|HttpStatus $status, ?int $value = null): StatusCode { - return $this->withFormatter(new StatusCode($status, $value)); + $matcher = new StatusCode($status, $value); + + return $matcher->withFormatter($this->createFormatter()); } /** @@ -399,97 +438,104 @@ public function statusCode(string|HttpStatus $status, ?int $value = null): Match * * @param array $values */ - public function values(array $values): MatcherInterface + public function values(array $values): Values { - return $this->withFormatter(new Values($values)); + $matcher = new Values($values); + + return $matcher->withFormatter($this->createFormatter()); } /** * Match binary data by its content type (magic file check) */ - public function contentType(string $contentType): MatcherInterface + public function contentType(string $contentType): ContentType { - return $this->withFormatter(new ContentType($contentType)); + $matcher = new ContentType($contentType); + + return $matcher->withFormatter($this->createFormatter()); } /** * Allows defining matching rules to apply to the keys in a map * - * @param array $values - * @param MatcherInterface[] $rules + * @param array|object $values + * @param MatcherInterface[] $rules */ - public function eachKey(array $values, array $rules): MatcherInterface + public function eachKey(array|object $values, array $rules): EachKey { - return $this->withFormatter(new EachKey($values, $rules)); + $matcher = new EachKey($values, $rules); + + return $matcher->withFormatter($this->createFormatter()); } /** * Allows defining matching rules to apply to the values in a collection. For maps, delgates to the Values matcher. * - * @param array $values + * @param array|object $values * @param MatcherInterface[] $rules */ - public function eachValue(array $values, array $rules): MatcherInterface + public function eachValue(array|object $values, array $rules): EachValue { - return $this->withFormatter(new EachValue($values, $rules)); + $matcher = new EachValue($values, $rules); + + return $matcher->withFormatter($this->createFormatter()); } /** * @throws MatcherException */ - public function url(string $url, string $regex, bool $useMockServerBasePath = true): MatcherInterface + public function url(string $url, string $regex, bool $useMockServerBasePath = true): Regex { $matcher = new Regex($regex, $useMockServerBasePath ? null : $url); - if ($useMockServerBasePath) { - $matcher->setGenerator(new MockServerURL($regex, $url)); - } - - return $this->withFormatter($matcher); + return $matcher + ->withGenerator($useMockServerBasePath ? new MockServerURL($regex, $url) : null) + ->withFormatter($this->createFormatter()); } /** * Generates a value that is looked up from the provider state context using the given expression */ - public function matchingField(string $fieldName): MatcherInterface + public function matchingField(string $fieldName): MatchingField { - return $this->withFormatter(new MatchingField($fieldName)); + $matcher = new MatchingField($fieldName); + + return $matcher->withFormatter($this->createFormatter()); } /** * @param array|object $value * @param MatcherInterface[] $matchers */ - public function matchAll(object|array $value, array $matchers): MatcherInterface + public function matchAll(object|array $value, array $matchers): MatchAll { - return $this->withFormatter(new MatchAll($value, $matchers)); + $matcher = new MatchAll($value, $matchers); + + return $matcher->withFormatter($this->createFormatter()); } /** * An array that has to have at least the required number of elements */ - public function atLeast(int $min): MatcherInterface + public function atLeast(int $min): Min { - return $this->withFormatter(new MinType(null, $min, false)); + $matcher = new Min($min); + + return $matcher->withFormatter($this->createFormatter()); } /** * An array that has to have at most the required number of elements */ - public function atMost(int $max): MatcherInterface + public function atMost(int $max): Max { - return $this->withFormatter(new MaxType(null, $max, false)); + $matcher = new Max($max); + + return $matcher->withFormatter($this->createFormatter()); } - private function withFormatter(MatcherInterface $matcher): MatcherInterface + private function createFormatter(): FormatterInterface { - if ($this->plugin) { - $formatter = $matcher->createExpressionFormatter(); - } else { - $formatter = $matcher->createJsonFormatter(); - } - $matcher->setFormatter($formatter); - - return $matcher; + return $this->plugin ? new ExpressionFormatter() : new JsonFormatter(); } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/AbstractDateTime.php b/src/PhpPact/Consumer/Matcher/Matchers/AbstractDateTime.php index c4061848..02d302b0 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/AbstractDateTime.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/AbstractDateTime.php @@ -2,43 +2,47 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\DateTimeFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\HasGeneratorFormatter; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; - -abstract class AbstractDateTime extends GeneratorAwareMatcher +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Expression; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; +use PhpPact\Consumer\Matcher\Trait\JsonFormattableTrait; + +abstract class AbstractDateTime extends GeneratorAwareMatcher implements JsonFormattableInterface, ExpressionFormattableInterface { + use JsonFormattableTrait; + public function __construct(protected string $format, private ?string $value = null) { parent::__construct(); } - public function getFormat(): string - { - return $this->format; - } - - /** - * @return array - */ - protected function getAttributesData(): array + public function formatJson(): Attributes { - return ['format' => $this->format]; + return $this->mergeJson(new Attributes([ + 'pact:matcher:type' => $this->getType(), + 'format' => $this->format, + 'value' => $this->value, + ])); } - public function getValue(): ?string + public function formatExpression(): Expression { - return $this->value; + if (!is_string($this->value)) { + throw new InvalidValueException(sprintf("DateTime matching expression doesn't support value of type %s", gettype($this->value))); + } + + $type = $this->getType(); + + return new Expression( + "matching({$type}, %format%, %value%)", + [ + 'format' => $this->format, + 'value' => $this->value, + ], + ); } - public function createJsonFormatter(): JsonFormatterInterface - { - return new HasGeneratorFormatter(); - } - - public function createExpressionFormatter(): ExpressionFormatterInterface - { - return new DateTimeFormatter(); - } + abstract protected function getType(): string; } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/AbstractMatcher.php b/src/PhpPact/Consumer/Matcher/Matchers/AbstractMatcher.php index 8c3f2558..4a323c18 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/AbstractMatcher.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/AbstractMatcher.php @@ -2,7 +2,7 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Formatters\Json\JsonFormatter; use PhpPact\Consumer\Matcher\Model\MatcherInterface; use PhpPact\Consumer\Matcher\Trait\FormatterAwareTrait; @@ -12,24 +12,11 @@ abstract class AbstractMatcher implements MatcherInterface public function __construct() { - $this->setFormatter($this->createJsonFormatter()); + $this->setFormatter(new JsonFormatter()); } - /** - * @return string|array - */ - public function jsonSerialize(): string|array + public function jsonSerialize(): mixed { return $this->getFormatter()->format($this); } - - public function getAttributes(): Attributes - { - return new Attributes($this, $this->getAttributesData()); - } - - /** - * @return array - */ - abstract protected function getAttributesData(): array; } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/AbstractValues.php b/src/PhpPact/Consumer/Matcher/Matchers/AbstractValues.php new file mode 100644 index 00000000..880d02e0 --- /dev/null +++ b/src/PhpPact/Consumer/Matcher/Matchers/AbstractValues.php @@ -0,0 +1,74 @@ +|object $values + * @param MatcherInterface[] $rules + */ + public function __construct(private object|array $values, private array $rules) + { + $this->setRules($rules); + parent::__construct(); + } + + public function formatJson(): Attributes + { + return new Attributes([ + 'pact:matcher:type' => $this->getType(), + 'rules' => array_map( + fn (MatcherInterface $rule) => $this->getFormatter()->format($rule), + $this->rules + ), + 'value' => $this->values, + ]); + } + + public function formatExpression(): Expression + { + if (count($this->rules) !== 1) { + throw new MatchingExpressionException(sprintf("Matcher '%s' only support 1 rule in expression, %d provided", $this->getType(), count($this->rules))); + } + $rule = reset($this->rules); + if (!$rule instanceof ExpressionFormattableInterface) { + throw new MatcherNotSupportedException(sprintf("Rule '%s' must implement '%s' to be formatted as expression", get_class($rule), ExpressionFormattableInterface::class)); + } + + return new Expression(sprintf('%s(%s)', $this->getType(), $rule->formatExpression())); + } + + abstract protected function getType(): string; + + /** + * @param MatcherInterface[] $rules + */ + private function setRules(array $rules): void + { + if (empty($rules)) { + throw new InvalidValueException("Rules should not be empty"); + } + $this->rules = []; + foreach ($rules as $rule) { + $this->addRule($rule); + } + } + + private function addRule(MatcherInterface $rule): void + { + $this->rules[] = $rule; + } +} diff --git a/src/PhpPact/Consumer/Matcher/Matchers/ArrayContains.php b/src/PhpPact/Consumer/Matcher/Matchers/ArrayContains.php index dbce8c31..682a87e2 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/ArrayContains.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/ArrayContains.php @@ -2,52 +2,32 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Exception\MatcherNotSupportedException; -use PhpPact\Consumer\Matcher\Formatters\Json\NoGeneratorFormatter; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; /** * Checks if all the variants are present in an array. */ -class ArrayContains extends AbstractMatcher +class ArrayContains extends AbstractMatcher implements JsonFormattableInterface { /** * @param array $variants */ public function __construct(private array $variants) { + if (empty($variants)) { + throw new InvalidValueException('Variants should not be empty'); + } parent::__construct(); } - /** - * @return array - */ - protected function getAttributesData(): array - { - return ['variants' => $this->getValue()]; - } - - /** - * @return array - */ - public function getValue(): array - { - return array_values($this->variants); - } - - public function getType(): string - { - return 'arrayContains'; - } - - public function createJsonFormatter(): JsonFormatterInterface - { - return new NoGeneratorFormatter(); - } - - public function createExpressionFormatter(): ExpressionFormatterInterface + public function formatJson(): Attributes { - throw new MatcherNotSupportedException("ArrayContains matcher doesn't support expression formatter"); + return new Attributes([ + 'pact:matcher:type' => 'arrayContains', + 'variants' => array_values($this->variants), + 'value' => array_values($this->variants), + ]); } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/Boolean.php b/src/PhpPact/Consumer/Matcher/Matchers/Boolean.php index 51f46454..bccd03d7 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/Boolean.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/Boolean.php @@ -2,17 +2,21 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\BooleanFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\HasGeneratorFormatter; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; use PhpPact\Consumer\Matcher\Generators\RandomBoolean; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Expression; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; +use PhpPact\Consumer\Matcher\Trait\JsonFormattableTrait; /** * Match if the value is a boolean value (booleans and the string values `true` and `false`) */ -class Boolean extends GeneratorAwareMatcher +class Boolean extends GeneratorAwareMatcher implements JsonFormattableInterface, ExpressionFormattableInterface { + use JsonFormattableTrait; + public function __construct(private ?bool $value = null) { if ($value === null) { @@ -21,28 +25,20 @@ public function __construct(private ?bool $value = null) parent::__construct(); } - public function getType(): string - { - return 'boolean'; - } - - protected function getAttributesData(): array - { - return []; - } - - public function getValue(): ?bool + public function formatJson(): Attributes { - return $this->value; + return $this->mergeJson(new Attributes([ + 'pact:matcher:type' => 'boolean', + 'value' => $this->value, + ])); } - public function createJsonFormatter(): JsonFormatterInterface + public function formatExpression(): Expression { - return new HasGeneratorFormatter(); - } + if (!is_bool($this->value)) { + throw new InvalidValueException(sprintf("Boolean matching expression doesn't support value of type %s", gettype($this->value))); + } - public function createExpressionFormatter(): ExpressionFormatterInterface - { - return new BooleanFormatter(); + return new Expression('matching(boolean, %value%)', ['value' => $this->value]); } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/CombinedMatchers.php b/src/PhpPact/Consumer/Matcher/Matchers/CombinedMatchers.php deleted file mode 100644 index 2fe3cfba..00000000 --- a/src/PhpPact/Consumer/Matcher/Matchers/CombinedMatchers.php +++ /dev/null @@ -1,41 +0,0 @@ -|object $value - * @param MatcherInterface[] $matchers - */ - public function __construct(private object|array $value, array $matchers) - { - foreach ($matchers as $matcher) { - if ($matcher instanceof CombinedMatchersInterface) { - throw new MatcherNotSupportedException('Nested combined matchers are not supported'); - } - $this->addMatcher($matcher); - } - parent::__construct(); - } - - protected function getAttributesData(): array - { - return []; - } - - /** - * @return array|object - */ - public function getValue(): object|array - { - return $this->value; - } -} diff --git a/src/PhpPact/Consumer/Matcher/Matchers/ContentType.php b/src/PhpPact/Consumer/Matcher/Matchers/ContentType.php index 9e075cb8..b4619e29 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/ContentType.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/ContentType.php @@ -2,48 +2,37 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\ContentTypeFormatter as ExpressionFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\ContentTypeFormatter as JsonFormatter; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Expression; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; /** * Match binary data by its content type (magic file check) */ -class ContentType extends AbstractMatcher +class ContentType extends AbstractMatcher implements JsonFormattableInterface, ExpressionFormattableInterface { public function __construct(private string $contentType, private string $value = '') { parent::__construct(); } - protected function getAttributesData(): array + public function formatJson(): Attributes { - return []; + return new Attributes([ + 'pact:matcher:type' => 'contentType', + 'value' => $this->contentType, + ]); } - public function getValue(): string + public function formatExpression(): Expression { - return $this->value; - } - - public function getType(): string - { - return 'contentType'; - } - - public function getContentType(): string - { - return $this->contentType; - } - - public function createJsonFormatter(): JsonFormatterInterface - { - return new JsonFormatter(); - } - - public function createExpressionFormatter(): ExpressionFormatterInterface - { - return new ExpressionFormatter(); + return new Expression( + 'matching(contentType, %contentType%, %value%)', + [ + 'contentType' => $this->contentType, + 'value' => $this->value, + ] + ); } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/Decimal.php b/src/PhpPact/Consumer/Matcher/Matchers/Decimal.php index cd5a7004..a0c7efe0 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/Decimal.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/Decimal.php @@ -2,17 +2,21 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\DecimalFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\HasGeneratorFormatter; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; use PhpPact\Consumer\Matcher\Generators\RandomDecimal; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Expression; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; +use PhpPact\Consumer\Matcher\Trait\JsonFormattableTrait; /** * This checks if the type of the value is a number with decimal places. */ -class Decimal extends GeneratorAwareMatcher +class Decimal extends GeneratorAwareMatcher implements JsonFormattableInterface, ExpressionFormattableInterface { + use JsonFormattableTrait; + public function __construct(private ?float $value = null) { if ($value === null) { @@ -21,28 +25,19 @@ public function __construct(private ?float $value = null) parent::__construct(); } - public function getType(): string - { - return 'decimal'; - } - - protected function getAttributesData(): array + public function formatJson(): Attributes { - return []; + return $this->mergeJson(new Attributes([ + 'pact:matcher:type' => 'decimal', + 'value' => $this->value, + ])); } - public function getValue(): ?float + public function formatExpression(): Expression { - return $this->value; - } - - public function createJsonFormatter(): JsonFormatterInterface - { - return new HasGeneratorFormatter(); - } - - public function createExpressionFormatter(): ExpressionFormatterInterface - { - return new DecimalFormatter(); + if (!is_float($this->value)) { + throw new InvalidValueException(sprintf("Decimal matching expression doesn't support value of type %s", gettype($this->value))); + } + return new Expression('matching(decimal, %value%)', ['value' => $this->value]); } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/EachKey.php b/src/PhpPact/Consumer/Matcher/Matchers/EachKey.php index f13aaf6b..d472db79 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/EachKey.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/EachKey.php @@ -2,62 +2,13 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\EachKeyFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\NoGeneratorFormatter; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; -use PhpPact\Consumer\Matcher\Model\MatcherInterface; - /** * Allows defining matching rules to apply to the keys in a map */ -class EachKey extends AbstractMatcher +class EachKey extends AbstractValues { - /** - * @param array|object $value - * @param MatcherInterface[] $rules - */ - public function __construct(private object|array $value, private array $rules) - { - parent::__construct(); - } - - /** - * @return array - */ - protected function getAttributesData(): array - { - return ['rules' => array_map(fn (MatcherInterface $rule) => $rule, $this->rules)]; - } - - /** - * @return array|object - */ - public function getValue(): object|array - { - return $this->value; - } - - public function getType(): string + protected function getType(): string { return 'eachKey'; } - - /** - * @return MatcherInterface[] - */ - public function getRules(): array - { - return $this->rules; - } - - public function createJsonFormatter(): JsonFormatterInterface - { - return new NoGeneratorFormatter(); - } - - public function createExpressionFormatter(): ExpressionFormatterInterface - { - return new EachKeyFormatter(); - } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/EachValue.php b/src/PhpPact/Consumer/Matcher/Matchers/EachValue.php index c7f319e5..16c1372f 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/EachValue.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/EachValue.php @@ -2,62 +2,13 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\EachValueFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\NoGeneratorFormatter; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; -use PhpPact\Consumer\Matcher\Model\MatcherInterface; - /** * Allows defining matching rules to apply to the values in a collection. For maps, delgates to the Values matcher. */ -class EachValue extends AbstractMatcher +class EachValue extends AbstractValues { - /** - * @param array|object $value - * @param MatcherInterface[] $rules - */ - public function __construct(private object|array $value, private array $rules) - { - parent::__construct(); - } - - /** - * @return array - */ - protected function getAttributesData(): array - { - return ['rules' => array_map(fn (MatcherInterface $rule) => $rule, $this->rules)]; - } - - /** - * @return array|object - */ - public function getValue(): object|array - { - return $this->value; - } - - public function getType(): string + protected function getType(): string { return 'eachValue'; } - - /** - * @return MatcherInterface[] - */ - public function getRules(): array - { - return $this->rules; - } - - public function createJsonFormatter(): JsonFormatterInterface - { - return new NoGeneratorFormatter(); - } - - public function createExpressionFormatter(): ExpressionFormatterInterface - { - return new EachValueFormatter(); - } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/Equality.php b/src/PhpPact/Consumer/Matcher/Matchers/Equality.php index 39a95490..2ac4bb76 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/Equality.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/Equality.php @@ -2,43 +2,31 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\EqualityFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\NoGeneratorFormatter; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Expression; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; /** * This is the default matcher, and relies on the equals operator */ -class Equality extends AbstractMatcher +class Equality extends AbstractMatcher implements JsonFormattableInterface, ExpressionFormattableInterface { public function __construct(private mixed $value) { parent::__construct(); } - protected function getAttributesData(): array + public function formatJson(): Attributes { - return []; + return new Attributes([ + 'pact:matcher:type' => 'equality', + 'value' => $this->value, + ]); } - public function getValue(): mixed + public function formatExpression(): Expression { - return $this->value; - } - - public function getType(): string - { - return 'equality'; - } - - public function createJsonFormatter(): JsonFormatterInterface - { - return new NoGeneratorFormatter(); - } - - public function createExpressionFormatter(): ExpressionFormatterInterface - { - return new EqualityFormatter(); + return new Expression('matching(equalTo, %value%)', ['value' => $this->value]); } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/GeneratorAwareMatcher.php b/src/PhpPact/Consumer/Matcher/Matchers/GeneratorAwareMatcher.php index 4d2d5bfb..c7c3a731 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/GeneratorAwareMatcher.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/GeneratorAwareMatcher.php @@ -2,28 +2,10 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Exception\GeneratorNotRequiredException; -use PhpPact\Consumer\Matcher\Exception\GeneratorRequiredException; use PhpPact\Consumer\Matcher\Model\GeneratorAwareInterface; use PhpPact\Consumer\Matcher\Trait\GeneratorAwareTrait; abstract class GeneratorAwareMatcher extends AbstractMatcher implements GeneratorAwareInterface { use GeneratorAwareTrait; - - /** - * @return string|array - */ - public function jsonSerialize(): string|array - { - if (null === $this->getValue()) { - if (!$this->generator) { - throw new GeneratorRequiredException(sprintf("Generator is required for matcher '%s' when example value is not set", $this->getType())); - } - } elseif ($this->generator) { - throw new GeneratorNotRequiredException(sprintf("Generator '%s' is not required for matcher '%s' when example value is set", $this->generator->getType(), $this->getType())); - } - - return $this->getFormatter()->format($this); - } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/Includes.php b/src/PhpPact/Consumer/Matcher/Matchers/Includes.php index c04514f5..cbf3e774 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/Includes.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/Includes.php @@ -2,43 +2,31 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\IncludesFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\NoGeneratorFormatter; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Expression; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; /** * This checks if the string representation of a value contains the substring. */ -class Includes extends AbstractMatcher +class Includes extends AbstractMatcher implements JsonFormattableInterface, ExpressionFormattableInterface { public function __construct(private string $value) { parent::__construct(); } - protected function getAttributesData(): array + public function formatJson(): Attributes { - return []; + return new Attributes([ + 'pact:matcher:type' => 'include', + 'value' => $this->value, + ]); } - public function getValue(): string + public function formatExpression(): Expression { - return $this->value; - } - - public function getType(): string - { - return 'include'; - } - - public function createJsonFormatter(): JsonFormatterInterface - { - return new NoGeneratorFormatter(); - } - - public function createExpressionFormatter(): ExpressionFormatterInterface - { - return new IncludesFormatter(); + return new Expression('matching(include, %value%)', ['value' => $this->value]); } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/Integer.php b/src/PhpPact/Consumer/Matcher/Matchers/Integer.php index a3d1c0d4..bfa524a5 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/Integer.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/Integer.php @@ -2,17 +2,21 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\IntegerFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\HasGeneratorFormatter; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; use PhpPact\Consumer\Matcher\Generators\RandomInt; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Expression; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; +use PhpPact\Consumer\Matcher\Trait\JsonFormattableTrait; /** * This checks if the type of the value is an integer. */ -class Integer extends GeneratorAwareMatcher +class Integer extends GeneratorAwareMatcher implements JsonFormattableInterface, ExpressionFormattableInterface { + use JsonFormattableTrait; + public function __construct(private ?int $value = null) { if ($value === null) { @@ -21,28 +25,19 @@ public function __construct(private ?int $value = null) parent::__construct(); } - public function getType(): string - { - return 'integer'; - } - - protected function getAttributesData(): array + public function formatJson(): Attributes { - return []; + return $this->mergeJson(new Attributes([ + 'pact:matcher:type' => 'integer', + 'value' => $this->value, + ])); } - public function getValue(): ?int + public function formatExpression(): Expression { - return $this->value; - } - - public function createJsonFormatter(): JsonFormatterInterface - { - return new HasGeneratorFormatter(); - } - - public function createExpressionFormatter(): ExpressionFormatterInterface - { - return new IntegerFormatter(); + if (!is_int($this->value)) { + throw new InvalidValueException(sprintf("Integer matching expression doesn't support value of type %s", gettype($this->value))); + } + return new Expression('matching(integer, %value%)', ['value' => $this->value]); } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/MatchAll.php b/src/PhpPact/Consumer/Matcher/Matchers/MatchAll.php index 68ed6c1e..17016e8c 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/MatchAll.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/MatchAll.php @@ -2,25 +2,64 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\MatchAllFormatter as ExpressionFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\MatchAllFormatter as JsonFormatter; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; +use PhpPact\Consumer\Matcher\Exception\MatcherNotSupportedException; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Expression; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; -class MatchAll extends CombinedMatchers +class MatchAll extends AbstractMatcher implements JsonFormattableInterface, ExpressionFormattableInterface { - public function getType(): string + /** + * @param array|object $value + * @param MatcherInterface[] $matchers + */ + public function __construct(private object|array $value, private array $matchers) { - return 'matchAll'; + $this->setMatchers($matchers); + parent::__construct(); } - public function createJsonFormatter(): JsonFormatterInterface + public function formatJson(): Attributes { - return new JsonFormatter(); + return new Attributes([ + 'pact:matcher:type' => array_map( + fn (MatcherInterface $matcher) => $this->getFormatter()->format($matcher), + $this->matchers + ), + 'value' => $this->value, + ]); } - public function createExpressionFormatter(): ExpressionFormatterInterface + public function formatExpression(): Expression { - return new ExpressionFormatter(); + return new Expression(implode(', ', array_map( + fn (MatcherInterface $matcher) => $this->getFormatter()->format($matcher), + $this->matchers + ))); + } + + /** + * @param MatcherInterface[] $matchers + */ + private function setMatchers(array $matchers): void + { + if (empty($matchers)) { + throw new InvalidValueException('Matchers should not be empty'); + } + $this->matchers = []; + foreach ($matchers as $matcher) { + if ($matcher instanceof self) { + throw new MatcherNotSupportedException("Nested 'matcherAll' matcher is not supported"); + } + $this->addMatcher($matcher); + } + } + + private function addMatcher(MatcherInterface $matcher): void + { + $this->matchers[] = $matcher; } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/MatchingField.php b/src/PhpPact/Consumer/Matcher/Matchers/MatchingField.php index 14701d9d..d933b720 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/MatchingField.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/MatchingField.php @@ -2,55 +2,17 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Exception\MatcherNotSupportedException; -use PhpPact\Consumer\Matcher\Formatters\Expression\MatchingFieldFormatter; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Model\Expression; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; -class MatchingField extends AbstractMatcher +class MatchingField extends AbstractMatcher implements ExpressionFormattableInterface { public function __construct(private string $fieldName) { - $this->setFormatter($this->createExpressionFormatter()); } - public function getFieldName(): string + public function formatExpression(): Expression { - return $this->fieldName; - } - - public function getValue(): string - { - return $this->getFieldName(); - } - - public function getType(): string - { - return 'matchingField'; - } - - protected function getAttributesData(): array - { - return []; - } - - public function jsonSerialize(): string - { - $result = parent::jsonSerialize(); - if (is_array($result)) { - throw new MatcherNotSupportedException("MatchingField matcher doesn't support json formatter"); - } - - return $result; - } - - public function createJsonFormatter(): JsonFormatterInterface - { - throw new MatcherNotSupportedException("MatchingField matcher doesn't support json formatter"); - } - - public function createExpressionFormatter(): ExpressionFormatterInterface - { - return new MatchingFieldFormatter(); + return new Expression("matching($%fieldName%)", ['fieldName' => $this->fieldName]); } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/Max.php b/src/PhpPact/Consumer/Matcher/Matchers/Max.php new file mode 100644 index 00000000..1e7c4140 --- /dev/null +++ b/src/PhpPact/Consumer/Matcher/Matchers/Max.php @@ -0,0 +1,38 @@ +max = 0; + } + parent::__construct(); + } + + public function formatJson(): Attributes + { + return new Attributes([ + 'pact:matcher:type' => 'type', + 'max' => $this->max, + 'value' => [null], + ]); + } + + public function formatExpression(): Expression + { + return new Expression(sprintf('atMost(%u)', $this->max)); + } +} diff --git a/src/PhpPact/Consumer/Matcher/Matchers/MaxType.php b/src/PhpPact/Consumer/Matcher/Matchers/MaxType.php index d1741c6c..dccce5a9 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/MaxType.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/MaxType.php @@ -2,21 +2,20 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\MaxTypeFormatter as ExpressionFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\MaxTypeFormatter as JsonFormatter; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Expression; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; /** * This executes a type based match against the values, that is, they are equal if they are the same type. * In addition, if the values represent a collection, the length of the actual value is compared against the maximum. */ -class MaxType extends AbstractMatcher +class MaxType extends AbstractMatcher implements JsonFormattableInterface, ExpressionFormattableInterface { public function __construct( private mixed $value, private int $max, - private bool $matchingType = true ) { if ($max < 0) { trigger_error("[WARN] max value to an array matcher can't be less than zero", E_USER_WARNING); @@ -25,41 +24,17 @@ public function __construct( parent::__construct(); } - /** - * @return array - */ - protected function getAttributesData(): array + public function formatJson(): Attributes { - return ['max' => $this->max]; + return new Attributes([ + 'pact:matcher:type' => 'type', + 'max' => $this->max, + 'value' => [$this->value], + ]); } - public function getValue(): mixed + public function formatExpression(): Expression { - return $this->value; - } - - public function getType(): string - { - return 'type'; - } - - public function getMax(): int - { - return $this->max; - } - - public function isMatchingType(): bool - { - return $this->matchingType; - } - - public function createJsonFormatter(): JsonFormatterInterface - { - return new JsonFormatter(); - } - - public function createExpressionFormatter(): ExpressionFormatterInterface - { - return new ExpressionFormatter(); + return new Expression("atMost({$this->max}), eachValue(matching(type, %value%)", ['value' => $this->value]); } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/Min.php b/src/PhpPact/Consumer/Matcher/Matchers/Min.php new file mode 100644 index 00000000..640b8e76 --- /dev/null +++ b/src/PhpPact/Consumer/Matcher/Matchers/Min.php @@ -0,0 +1,38 @@ +min = 0; + } + parent::__construct(); + } + + public function formatJson(): Attributes + { + return new Attributes([ + 'pact:matcher:type' => 'type', + 'min' => $this->min, + 'value' => array_fill(0, max($this->min, 1), null), + ]); + } + + public function formatExpression(): Expression + { + return new Expression(sprintf('atLeast(%u)', $this->min)); + } +} diff --git a/src/PhpPact/Consumer/Matcher/Matchers/MinMaxType.php b/src/PhpPact/Consumer/Matcher/Matchers/MinMaxType.php index bc79245c..e695cc46 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/MinMaxType.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/MinMaxType.php @@ -2,16 +2,16 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\MinMaxTypeFormatter as ExpressionFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\MinMaxTypeFormatter as JsonFormatter; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Expression; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; /** * This executes a type based match against the values, that is, they are equal if they are the same type. * In addition, if the values represent a collection, the length of the actual value is compared against the minimum and maximum. */ -class MinMaxType extends AbstractMatcher +class MinMaxType extends AbstractMatcher implements JsonFormattableInterface, ExpressionFormattableInterface { public function __construct( private mixed $value, @@ -29,44 +29,20 @@ public function __construct( parent::__construct(); } - /** - * @return array - */ - protected function getAttributesData(): array + public function formatJson(): Attributes { - return [ + $examples = max($this->min, 1); // Min can be zero, but number of examples must be at least 1 + + return new Attributes([ + 'pact:matcher:type' => 'type', 'min' => $this->min, 'max' => $this->max, - ]; - } - - public function getValue(): mixed - { - return $this->value; - } - - public function getType(): string - { - return 'type'; - } - - public function getMin(): int - { - return $this->min; - } - - public function getMax(): int - { - return $this->max; - } - - public function createJsonFormatter(): JsonFormatterInterface - { - return new JsonFormatter(); + 'value' => array_fill(0, $examples, $this->value), + ]); } - public function createExpressionFormatter(): ExpressionFormatterInterface + public function formatExpression(): Expression { - return new ExpressionFormatter(); + return new Expression("atLeast({$this->min}), atMost({$this->max}), eachValue(matching(type, %value%)", ['value' => $this->value]); } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/MinType.php b/src/PhpPact/Consumer/Matcher/Matchers/MinType.php index 3fae024d..f9891fa5 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/MinType.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/MinType.php @@ -2,21 +2,20 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\MinTypeFormatter as ExpressionFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\MinTypeFormatter as JsonFormatter; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Expression; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; /** * This executes a type based match against the values, that is, they are equal if they are the same type. * In addition, if the values represent a collection, the length of the actual value is compared against the minimum. */ -class MinType extends AbstractMatcher +class MinType extends AbstractMatcher implements JsonFormattableInterface, ExpressionFormattableInterface { public function __construct( private mixed $value, private int $min, - private bool $matchingType = true ) { if ($min < 0) { trigger_error("[WARN] min value to an array matcher can't be less than zero", E_USER_WARNING); @@ -25,41 +24,19 @@ public function __construct( parent::__construct(); } - public function getType(): string + public function formatJson(): Attributes { - return 'type'; - } - - /** - * @return array - */ - protected function getAttributesData(): array - { - return ['min' => $this->min]; - } - - public function getValue(): mixed - { - return $this->value; - } + $examples = max($this->min, 1); // Min can be zero, but number of examples must be at least 1 - public function getMin(): int - { - return $this->min; - } - - public function isMatchingType(): bool - { - return $this->matchingType; - } - - public function createJsonFormatter(): JsonFormatterInterface - { - return new JsonFormatter(); + return new Attributes([ + 'pact:matcher:type' => 'type', + 'min' => $this->min, + 'value' => array_fill(0, $examples, $this->value), + ]); } - public function createExpressionFormatter(): ExpressionFormatterInterface + public function formatExpression(): Expression { - return new ExpressionFormatter(); + return new Expression("atLeast({$this->min}), eachValue(matching(type, %value%)", ['value' => $this->value]); } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/NotEmpty.php b/src/PhpPact/Consumer/Matcher/Matchers/NotEmpty.php index d6e398e0..d82e0d3c 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/NotEmpty.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/NotEmpty.php @@ -2,43 +2,31 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\NotEmptyFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\NoGeneratorFormatter; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Expression; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; /** * Value must be present and not empty (not null or the empty string) */ -class NotEmpty extends AbstractMatcher +class NotEmpty extends AbstractMatcher implements JsonFormattableInterface, ExpressionFormattableInterface { public function __construct(private mixed $value) { parent::__construct(); } - protected function getAttributesData(): array + public function formatJson(): Attributes { - return []; + return new Attributes([ + 'pact:matcher:type' => 'notEmpty', + 'value' => $this->value, + ]); } - public function getValue(): mixed + public function formatExpression(): Expression { - return $this->value; - } - - public function getType(): string - { - return 'notEmpty'; - } - - public function createJsonFormatter(): JsonFormatterInterface - { - return new NoGeneratorFormatter(); - } - - public function createExpressionFormatter(): ExpressionFormatterInterface - { - return new NotEmptyFormatter(); + return new Expression('notEmpty(%value%)', ['value' => $this->value]); } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/NullValue.php b/src/PhpPact/Consumer/Matcher/Matchers/NullValue.php index 8350ff77..1cd9b6bb 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/NullValue.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/NullValue.php @@ -2,41 +2,25 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\NullValueFormatter as ExpressionFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\NullValueFormatter as JsonFormatter; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Expression; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; /** * Match if the value is a null value (this is content specific, for JSON will match a JSON null) */ -class NullValue extends AbstractMatcher +class NullValue extends AbstractMatcher implements JsonFormattableInterface, ExpressionFormattableInterface { - public function getType(): string + public function formatJson(): Attributes { - return 'null'; + return new Attributes([ + 'pact:matcher:type' => 'null', + ]); } - protected function getAttributesData(): array + public function formatExpression(): Expression { - return []; - } - - /** - * @todo Change return type to `null` - */ - public function getValue(): mixed - { - return null; - } - - public function createJsonFormatter(): JsonFormatterInterface - { - return new JsonFormatter(); - } - - public function createExpressionFormatter(): ExpressionFormatterInterface - { - return new ExpressionFormatter(); + return new Expression('matching(type, null)'); } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/Number.php b/src/PhpPact/Consumer/Matcher/Matchers/Number.php index 4acae2fb..7c89b901 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/Number.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/Number.php @@ -2,17 +2,21 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\NumberFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\HasGeneratorFormatter; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; use PhpPact\Consumer\Matcher\Generators\RandomInt; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Expression; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; +use PhpPact\Consumer\Matcher\Trait\JsonFormattableTrait; /** * This checks if the type of the value is a number. */ -class Number extends GeneratorAwareMatcher +class Number extends GeneratorAwareMatcher implements JsonFormattableInterface, ExpressionFormattableInterface { + use JsonFormattableTrait; + public function __construct(private int|float|null $value = null) { if ($value === null) { @@ -21,28 +25,19 @@ public function __construct(private int|float|null $value = null) parent::__construct(); } - public function getType(): string - { - return 'number'; - } - - protected function getAttributesData(): array + public function formatJson(): Attributes { - return []; + return $this->mergeJson(new Attributes([ + 'pact:matcher:type' => 'number', + 'value' => $this->value, + ])); } - public function getValue(): int|float|null + public function formatExpression(): Expression { - return $this->value; - } - - public function createJsonFormatter(): JsonFormatterInterface - { - return new HasGeneratorFormatter(); - } - - public function createExpressionFormatter(): ExpressionFormatterInterface - { - return new NumberFormatter(); + if (null === $this->value) { + throw new InvalidValueException(sprintf("Number matching expression doesn't support value of type %s", gettype($this->value))); + } + return new Expression('matching(number, %value%)', ['value' => $this->value]); } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/Regex.php b/src/PhpPact/Consumer/Matcher/Matchers/Regex.php index 642353c3..91d49e46 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/Regex.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/Regex.php @@ -2,89 +2,69 @@ namespace PhpPact\Consumer\Matcher\Matchers; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; use PhpPact\Consumer\Matcher\Exception\InvalidRegexException; -use PhpPact\Consumer\Matcher\Formatters\Expression\RegexFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\HasGeneratorFormatter; use PhpPact\Consumer\Matcher\Generators\Regex as RegexGenerator; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Expression; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; +use PhpPact\Consumer\Matcher\Trait\JsonFormattableTrait; use function preg_last_error; use function preg_match; -class Regex extends GeneratorAwareMatcher +class Regex extends GeneratorAwareMatcher implements JsonFormattableInterface, ExpressionFormattableInterface { + use JsonFormattableTrait; + /** * @param string|string[]|null $values */ public function __construct( private string $regex, - protected string|array|null $values = null, + private string|array|null $values = null, ) { if ($values === null) { $this->setGenerator(new RegexGenerator($this->regex)); + } else { + $this->validateRegex(); } parent::__construct(); } /** - * @return string|array + * @todo Use json_validate() */ - public function jsonSerialize(): string|array - { - if (null !== $this->values) { - $this->validateRegex(); - } - - return parent::jsonSerialize(); - } - private function validateRegex(): void { foreach ((array) $this->values as $value) { $result = preg_match("/$this->regex/", $value); - if ($result === false || $result === 0) { + if ($result !== 1) { $errorCode = preg_last_error(); - throw new InvalidRegexException("The pattern '{$this->regex}' is not valid for value '{$value}'. Failed with error code {$errorCode}."); + throw new InvalidRegexException("The value '{$value}' doesn't match pattern '{$this->regex}'. Failed with error code {$errorCode}."); } } } - public function getType(): string - { - return 'regex'; - } - - /** - * @return string|string[]|null - */ - public function getValue(): string|array|null - { - return $this->values; - } - - /** - * @return array - */ - protected function getAttributesData(): array - { - return ['regex' => $this->regex]; - } - - public function getRegex(): string + public function formatJson(): Attributes { - return $this->regex; + return $this->mergeJson(new Attributes([ + 'pact:matcher:type' => 'regex', + 'regex' => $this->regex, + 'value' => $this->values, + ])); } - public function createJsonFormatter(): JsonFormatterInterface + public function formatExpression(): Expression { - return new HasGeneratorFormatter(); - } + $value = $this->values; + if (!is_string($value)) { + throw new InvalidValueException(sprintf("Regex matching expression doesn't support value of type %s", gettype($value))); + } - public function createExpressionFormatter(): ExpressionFormatterInterface - { - return new RegexFormatter(); + return new Expression("matching(regex, %regex%, %value%)", ['regex' => $this->regex, 'value' => $value]); } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/Semver.php b/src/PhpPact/Consumer/Matcher/Matchers/Semver.php index 6f36e587..c1e94aaa 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/Semver.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/Semver.php @@ -2,17 +2,21 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\SemverFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\HasGeneratorFormatter; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; use PhpPact\Consumer\Matcher\Generators\Regex; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Expression; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; +use PhpPact\Consumer\Matcher\Trait\JsonFormattableTrait; /** * Value must be valid based on the semver specification */ -class Semver extends GeneratorAwareMatcher +class Semver extends GeneratorAwareMatcher implements JsonFormattableInterface, ExpressionFormattableInterface { + use JsonFormattableTrait; + public function __construct(private ?string $value = null) { if ($value === null) { @@ -21,28 +25,19 @@ public function __construct(private ?string $value = null) parent::__construct(); } - public function getType(): string - { - return 'semver'; - } - - protected function getAttributesData(): array + public function formatJson(): Attributes { - return []; + return $this->mergeJson(new Attributes([ + 'pact:matcher:type' => 'semver', + 'value' => $this->value, + ])); } - public function getValue(): ?string + public function formatExpression(): Expression { - return $this->value; - } - - public function createJsonFormatter(): JsonFormatterInterface - { - return new HasGeneratorFormatter(); - } - - public function createExpressionFormatter(): ExpressionFormatterInterface - { - return new SemverFormatter(); + if (!is_string($this->value)) { + throw new InvalidValueException(sprintf("Semver matching expression doesn't support value of type %s", gettype($this->value))); + } + return new Expression('matching(semver, %value%)', ['value' => $this->value]); } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/StatusCode.php b/src/PhpPact/Consumer/Matcher/Matchers/StatusCode.php index 0d995008..70940880 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/StatusCode.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/StatusCode.php @@ -4,17 +4,18 @@ use PhpPact\Consumer\Matcher\Enum\HttpStatus; use PhpPact\Consumer\Matcher\Exception\InvalidHttpStatusException; -use PhpPact\Consumer\Matcher\Exception\MatcherNotSupportedException; -use PhpPact\Consumer\Matcher\Formatters\Json\HasGeneratorFormatter; use PhpPact\Consumer\Matcher\Generators\RandomInt; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; +use PhpPact\Consumer\Matcher\Trait\JsonFormattableTrait; /** * Matches the response status code. */ -class StatusCode extends GeneratorAwareMatcher +class StatusCode extends GeneratorAwareMatcher implements JsonFormattableInterface { + use JsonFormattableTrait; + private HttpStatus $status; public function __construct(string|HttpStatus $status, private ?int $value = null) @@ -40,31 +41,12 @@ public function __construct(string|HttpStatus $status, private ?int $value = nul parent::__construct(); } - public function getType(): string - { - return 'statusCode'; - } - - /** - * @return array - */ - protected function getAttributesData(): array - { - return ['status' => $this->status->value]; - } - - public function getValue(): ?int - { - return $this->value; - } - - public function createJsonFormatter(): JsonFormatterInterface - { - return new HasGeneratorFormatter(); - } - - public function createExpressionFormatter(): ExpressionFormatterInterface + public function formatJson(): Attributes { - throw new MatcherNotSupportedException("StatusCode matcher doesn't support expression formatter"); + return $this->mergeJson(new Attributes([ + 'pact:matcher:type' => 'statusCode', + 'status' => $this->status->value, + 'value' => $this->value, + ])); } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/StringValue.php b/src/PhpPact/Consumer/Matcher/Matchers/StringValue.php index 611e8080..7bf689fc 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/StringValue.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/StringValue.php @@ -2,17 +2,20 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\StringValueFormatter as ExpressionFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\StringValueFormatter as JsonFormatter; use PhpPact\Consumer\Matcher\Generators\RandomString; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Expression; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; +use PhpPact\Consumer\Matcher\Trait\JsonFormattableTrait; /** * There is no matcher for string. We re-use `type` matcher. */ -class StringValue extends GeneratorAwareMatcher +class StringValue extends GeneratorAwareMatcher implements JsonFormattableInterface, ExpressionFormattableInterface { + use JsonFormattableTrait; + public const DEFAULT_VALUE = 'some string'; public function __construct(private ?string $value = null) @@ -23,36 +26,21 @@ public function __construct(private ?string $value = null) parent::__construct(); } - public function getType(): string + public function formatJson(): Attributes { - return 'type'; + return $this->mergeJson(new Attributes([ + 'pact:matcher:type' => 'type', + 'value' => $this->getValue(), + ])); } - /** - * @return string|array - */ - public function jsonSerialize(): string|array + public function formatExpression(): Expression { - return $this->getFormatter()->format($this); + return new Expression('matching(type, %value%)', ['value' => $this->getValue()]); } - protected function getAttributesData(): array - { - return []; - } - - public function getValue(): string + private function getValue(): string { return $this->value ?? self::DEFAULT_VALUE; } - - public function createJsonFormatter(): JsonFormatterInterface - { - return new JsonFormatter(); - } - - public function createExpressionFormatter(): ExpressionFormatterInterface - { - return new ExpressionFormatter(); - } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/Type.php b/src/PhpPact/Consumer/Matcher/Matchers/Type.php index 0148f60c..fc7f4e43 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/Type.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/Type.php @@ -2,43 +2,31 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\TypeFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\NoGeneratorFormatter; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Expression; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; /** * This executes a type based match against the values, that is, they are equal if they are the same type. */ -class Type extends AbstractMatcher +class Type extends AbstractMatcher implements JsonFormattableInterface, ExpressionFormattableInterface { public function __construct(private mixed $value) { parent::__construct(); } - protected function getAttributesData(): array + public function formatJson(): Attributes { - return []; + return new Attributes([ + 'pact:matcher:type' => 'type', + 'value' => $this->value, + ]); } - public function getValue(): mixed + public function formatExpression(): Expression { - return $this->value; - } - - public function getType(): string - { - return 'type'; - } - - public function createJsonFormatter(): JsonFormatterInterface - { - return new NoGeneratorFormatter(); - } - - public function createExpressionFormatter(): ExpressionFormatterInterface - { - return new TypeFormatter(); + return new Expression('matching(type, %value%)', ['value' => $this->value]); } } diff --git a/src/PhpPact/Consumer/Matcher/Matchers/Values.php b/src/PhpPact/Consumer/Matcher/Matchers/Values.php index 0daa67e0..685403f2 100644 --- a/src/PhpPact/Consumer/Matcher/Matchers/Values.php +++ b/src/PhpPact/Consumer/Matcher/Matchers/Values.php @@ -2,17 +2,15 @@ namespace PhpPact\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Exception\MatcherNotSupportedException; -use PhpPact\Consumer\Matcher\Formatters\Json\NoGeneratorFormatter; -use PhpPact\Consumer\Matcher\Model\ExpressionFormatterInterface; -use PhpPact\Consumer\Matcher\Model\JsonFormatterInterface; +use PhpPact\Consumer\Matcher\Model\Attributes; +use PhpPact\Consumer\Matcher\Model\Matcher\JsonFormattableInterface; /** * Match the values in a map, ignoring the keys * * @deprecated use EachKey or EachValue */ -class Values extends AbstractMatcher +class Values extends AbstractMatcher implements JsonFormattableInterface { /** * @param array $values @@ -22,31 +20,11 @@ public function __construct(private array $values) parent::__construct(); } - protected function getAttributesData(): array + public function formatJson(): Attributes { - return []; - } - - /** - * @return array - */ - public function getValue(): array - { - return $this->values; - } - - public function getType(): string - { - return 'values'; - } - - public function createJsonFormatter(): JsonFormatterInterface - { - return new NoGeneratorFormatter(); - } - - public function createExpressionFormatter(): ExpressionFormatterInterface - { - throw new MatcherNotSupportedException("Values matcher doesn't support expression formatter"); + return new Attributes([ + 'pact:matcher:type' => 'values', + 'value' => $this->values, + ]); } } diff --git a/src/PhpPact/Consumer/Matcher/Model/Attributes.php b/src/PhpPact/Consumer/Matcher/Model/Attributes.php index 8da2fc7a..c72aa073 100644 --- a/src/PhpPact/Consumer/Matcher/Model/Attributes.php +++ b/src/PhpPact/Consumer/Matcher/Model/Attributes.php @@ -2,58 +2,67 @@ namespace PhpPact\Consumer\Matcher\Model; +use ArrayIterator; +use IteratorAggregate; +use JsonSerializable; use PhpPact\Consumer\Matcher\Exception\AttributeConflictException; +use Traversable; -class Attributes +/** + * @implements IteratorAggregate + */ +class Attributes implements IteratorAggregate, JsonSerializable { /** * @param array $data */ - public function __construct(private GeneratorInterface|MatcherInterface $parent, private array $data = []) + public function __construct(private array $data = []) { + $this->data = []; + foreach ($data as $key => $value) { + $this->set($key, $value); + } } - public function getParent(): GeneratorInterface|MatcherInterface + public function has(string $key): bool { - return $this->parent; + return isset($this->data[$key]); } - /** - * @return array - */ - public function getData(): array + public function get(string $key): mixed { - return $this->data; + return $this->data[$key] ?? null; } - public function has(string $key): bool + public function set(string $key, mixed $value): void { - return isset($this->data[$key]); + $this->data[$key] = $value; } - public function get(string $key): mixed + public function getIterator(): Traversable { - return $this->data[$key] ?? null; + return new ArrayIterator($this->data); } public function merge(self $attributes): self { - foreach ($this->data as $key => $value) { - if ($attributes->has($key) && $value !== $attributes->get($key)) { - throw new AttributeConflictException(sprintf("Attribute '%s' of %s '%s' and %s '%s' are conflict", $key, $this->getParentType(), $this->getParentName(), $attributes->getParentType(), $attributes->getParentName())); + $return = new self($this->data); + foreach ($attributes as $key => $value) { + if (!$return->has($key)) { + $return->set($key, $value); + } elseif ($return->has($key) && $value !== $return->get($key)) { + throw new AttributeConflictException(sprintf("Can not merge attributes: Values of attribute '%s' are conflict", $key)); } } - return new self($this->parent, $this->data + $attributes->getData()); - } - - private function getParentType(): string - { - return $this->parent instanceof GeneratorInterface ? 'generator' : 'matcher'; + return $return; } - private function getParentName(): string + /** + * @return array + */ + public function jsonSerialize(): array { - return $this->parent->getType(); + return $this->data; } } diff --git a/src/PhpPact/Consumer/Matcher/Model/CombinedMatchersInterface.php b/src/PhpPact/Consumer/Matcher/Model/CombinedMatchersInterface.php deleted file mode 100644 index 233b9ca2..00000000 --- a/src/PhpPact/Consumer/Matcher/Model/CombinedMatchersInterface.php +++ /dev/null @@ -1,11 +0,0 @@ - $values + */ + public function __construct(private string $format, private array $values = []) + { + $this->setValues($values); + } + + public function jsonSerialize(): string + { + return $this->format(); + } + + public function __toString(): string + { + return $this->format(); + } + + private function format(): string + { + return strtr( + $this->format, + array_combine( + array_map(fn (string $key) => "%{$key}%", array_keys($this->values)), + array_map(fn (mixed $value) => $this->normalize($value), array_values($this->values)), + ) + ); + } + + /** + * @param array $values + */ + private function setValues(array $values): void + { + $this->values = []; + foreach ($values as $key => $value) { + $this->setValue($key, $value); + } + } + + private function setValue(string $key, mixed $value): void + { + $this->values[$key] = $value; + } + + private function normalize(mixed $value): string + { + if (is_string($value)) { + $value = addcslashes($value, "'"); + } + return match (gettype($value)) { + 'string' => sprintf("'%s'", $value), + 'boolean' => $value ? 'true' : 'false', + 'integer' => (string) $value, + 'double' => (string) $value, + 'NULL' => 'null', + default => throw new InvalidValueException(sprintf("Expression doesn't support value of type %s", gettype($value))), + }; + } +} diff --git a/src/PhpPact/Consumer/Matcher/Model/ExpressionFormatterInterface.php b/src/PhpPact/Consumer/Matcher/Model/ExpressionFormatterInterface.php deleted file mode 100644 index bf683237..00000000 --- a/src/PhpPact/Consumer/Matcher/Model/ExpressionFormatterInterface.php +++ /dev/null @@ -1,8 +0,0 @@ - - */ - public function format(MatcherInterface $matcher): string|array; + public function format(MatcherInterface $matcher): mixed; } diff --git a/src/PhpPact/Consumer/Matcher/Model/Generator/JsonFormattableInterface.php b/src/PhpPact/Consumer/Matcher/Model/Generator/JsonFormattableInterface.php new file mode 100644 index 00000000..a61e529e --- /dev/null +++ b/src/PhpPact/Consumer/Matcher/Model/Generator/JsonFormattableInterface.php @@ -0,0 +1,10 @@ + - */ - public function format(MatcherInterface $matcher): array; -} diff --git a/src/PhpPact/Consumer/Matcher/Model/Matcher/ExpressionFormattableInterface.php b/src/PhpPact/Consumer/Matcher/Model/Matcher/ExpressionFormattableInterface.php new file mode 100644 index 00000000..676e7e57 --- /dev/null +++ b/src/PhpPact/Consumer/Matcher/Model/Matcher/ExpressionFormattableInterface.php @@ -0,0 +1,10 @@ +formatter; } + + public function withFormatter(FormatterInterface $formatter): static + { + $matcher = clone $this; + $matcher->setFormatter($formatter); + + return $matcher; + } } diff --git a/src/PhpPact/Consumer/Matcher/Trait/GeneratorAwareTrait.php b/src/PhpPact/Consumer/Matcher/Trait/GeneratorAwareTrait.php index 78a7f1a0..d9c7793c 100644 --- a/src/PhpPact/Consumer/Matcher/Trait/GeneratorAwareTrait.php +++ b/src/PhpPact/Consumer/Matcher/Trait/GeneratorAwareTrait.php @@ -17,4 +17,12 @@ public function getGenerator(): ?GeneratorInterface { return $this->generator; } + + public function withGenerator(?GeneratorInterface $generator): static + { + $matcher = clone $this; + $matcher->setGenerator($generator); + + return $matcher; + } } diff --git a/src/PhpPact/Consumer/Matcher/Trait/JsonFormattableTrait.php b/src/PhpPact/Consumer/Matcher/Trait/JsonFormattableTrait.php new file mode 100644 index 00000000..df3c9a9f --- /dev/null +++ b/src/PhpPact/Consumer/Matcher/Trait/JsonFormattableTrait.php @@ -0,0 +1,21 @@ +getGenerator(); + if ($generator instanceof GeneratorJsonFormattableInterface) { + return $generator->formatJson()->merge($attributes); + } + } + return $attributes; + } +} diff --git a/src/PhpPact/Consumer/Matcher/Trait/MatchersTrait.php b/src/PhpPact/Consumer/Matcher/Trait/MatchersTrait.php deleted file mode 100644 index a9b61073..00000000 --- a/src/PhpPact/Consumer/Matcher/Trait/MatchersTrait.php +++ /dev/null @@ -1,26 +0,0 @@ -matchers[] = $matcher; - } - - /** - * @return MatcherInterface[] - */ - public function getMatchers(): array - { - return $this->matchers; - } -} diff --git a/src/PhpPact/Xml/Model/Builder/ElementTrait.php b/src/PhpPact/Xml/Model/Builder/ElementTrait.php index 0d088f05..663735f7 100644 --- a/src/PhpPact/Xml/Model/Builder/ElementTrait.php +++ b/src/PhpPact/Xml/Model/Builder/ElementTrait.php @@ -41,8 +41,7 @@ public function eachLike(callable ...$options): callable return function (XmlElement $element) use ($options): void { $child = new XmlElement(...$options); $matcher = new Type($child); - $matcher->setFormatter(new XmlElementFormatter()); - $element->addChild($matcher); + $element->addChild($matcher->withFormatter(new XmlElementFormatter())); }; } } diff --git a/src/PhpPact/Xml/Model/Builder/TextTrait.php b/src/PhpPact/Xml/Model/Builder/TextTrait.php index e82ea791..aa01474f 100644 --- a/src/PhpPact/Xml/Model/Builder/TextTrait.php +++ b/src/PhpPact/Xml/Model/Builder/TextTrait.php @@ -19,8 +19,7 @@ public function contentLike(string|float|int|bool|null $content): callable { return function (XmlElement $element) use ($content): void { $matcher = new Type($content); - $matcher->setFormatter(new XmlContentFormatter()); - $text = new XmlText($matcher); + $text = new XmlText($matcher->withFormatter(new XmlContentFormatter())); $element->setText($text); }; } diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/BooleanFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/BooleanFormatterTest.php deleted file mode 100644 index 8fe89d97..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/BooleanFormatterTest.php +++ /dev/null @@ -1,48 +0,0 @@ -formatter = new BooleanFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - public function testInvalidValue(): void - { - $matcher = new Boolean(); - $this->expectException(InvalidValueException::class); - $this->expectExceptionMessage(sprintf("Boolean formatter doesn't support value of type %s", gettype(null))); - $this->formatter->format($matcher); - } - - #[TestWith([new Boolean(true), 'matching(boolean, true)'])] - #[TestWith([new Boolean(false), 'matching(boolean, false)'])] - public function testFormat(MatcherInterface $matcher, string $expression): void - { - $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/ContentTypeFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/ContentTypeFormatterTest.php deleted file mode 100644 index 83a4c44d..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/ContentTypeFormatterTest.php +++ /dev/null @@ -1,43 +0,0 @@ -formatter = new ContentTypeFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new ContentType("contains single quote '", 'testing'), "matching(contentType, 'contains single quote \'', 'testing')"])] - #[TestWith([new ContentType('plain/text', "contains single quote '"), "matching(contentType, 'plain/text', 'contains single quote \'')"])] - #[TestWith([new ContentType('plain/text'), "matching(contentType, 'plain/text', '')"])] - #[TestWith([new ContentType('application/json', '{"key":"value"}'), "matching(contentType, 'application/json', '{\"key\":\"value\"}')"])] - #[TestWith([new ContentType('application/xml', ''), "matching(contentType, 'application/xml', '')"])] - public function testFormat(MatcherInterface $matcher, string $expression): void - { - $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/DateTimeFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/DateTimeFormatterTest.php deleted file mode 100644 index d13ab4ff..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/DateTimeFormatterTest.php +++ /dev/null @@ -1,53 +0,0 @@ -formatter = new DateTimeFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - public function testInvalidValue(): void - { - $matcher = new Time('HH:mm'); - $this->expectException(InvalidValueException::class); - $this->expectExceptionMessage(sprintf("DateTime formatter doesn't support value of type %s", gettype(null))); - $this->formatter->format($matcher); - } - - #[TestWith([new Time("contains single quote '", '22:04'), "matching(time, 'contains single quote \'', '22:04')"])] - #[TestWith([new Time('HH:mm', "contains single quote '"), "matching(time, 'HH:mm', 'contains single quote \'')"])] - #[TestWith([new DateTime('yyyy-MM-dd HH:mm:ssZZZZZ', '2020-05-21 16:44:32+10:00'), "matching(datetime, 'yyyy-MM-dd HH:mm:ssZZZZZ', '2020-05-21 16:44:32+10:00')"])] - #[TestWith([new Date('yyyy-MM-dd', '2012-04-12'), "matching(date, 'yyyy-MM-dd', '2012-04-12')"])] - #[TestWith([new Time('HH:mm', '22:04'), "matching(time, 'HH:mm', '22:04')"])] - public function testFormat(MatcherInterface $matcher, string $expression): void - { - $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/DecimalFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/DecimalFormatterTest.php deleted file mode 100644 index 802e7f3f..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/DecimalFormatterTest.php +++ /dev/null @@ -1,50 +0,0 @@ -formatter = new DecimalFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - public function testInvalidValue(): void - { - $matcher = new Decimal(); - $this->expectException(InvalidValueException::class); - $this->expectExceptionMessage(sprintf("Decimal formatter doesn't support value of type %s", gettype(null))); - $this->formatter->format($matcher); - } - - #[TestWith([new Decimal(-99), 'matching(decimal, -99)'])] // Provider verification will fail on this case - #[TestWith([new Decimal(100), 'matching(decimal, 100)'])] // Provider verification will fail on this case - #[TestWith([new Decimal(100.01), 'matching(decimal, 100.01)'])] - #[TestWith([new Decimal(-100.003), 'matching(decimal, -100.003)'])] - public function testFormat(MatcherInterface $matcher, string $expression): void - { - $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/EachKeyFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/EachKeyFormatterTest.php deleted file mode 100644 index f16b9144..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/EachKeyFormatterTest.php +++ /dev/null @@ -1,55 +0,0 @@ -formatter = new EachKeyFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new EachKey(['value'], [])])] - #[TestWith([new EachKey(['value'], [new Type(1), new Type(2)])])] - public function testInvalidRules(EachKey $matcher): void - { - $this->expectException(MatchingExpressionException::class); - $this->expectExceptionMessage(sprintf("Matcher 'eachKey' only support 1 rule in expression, %d provided", count($matcher->getRules()))); - $this->formatter->format($matcher); - } - - #[TestWith([new EachKey(['value'], [new StringValue("contains single quote '")]), "eachKey(matching(type, 'contains single quote \''))"])] - #[TestWith([new EachKey(['value'], [new Integer(123)]), 'eachKey(matching(integer, 123))'])] - #[TestWith([new EachKey(new stdClass(), [new Regex('\w+', 'example value')]), "eachKey(matching(regex, '\w+', 'example value'))"])] - public function testFormat(MatcherInterface $matcher, string $expression): void - { - $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/EachValueFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/EachValueFormatterTest.php deleted file mode 100644 index 094e09d4..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/EachValueFormatterTest.php +++ /dev/null @@ -1,54 +0,0 @@ -formatter = new EachValueFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new EachValue(['value'], [])])] - #[TestWith([new EachValue(['value'], [new Type(1), new Type(2), new Type(3)])])] - public function testInvalidRules(EachValue $matcher): void - { - $this->expectException(MatchingExpressionException::class); - $this->expectExceptionMessage(sprintf("Matcher 'eachValue' only support 1 rule in expression, %d provided", count($matcher->getRules()))); - $this->formatter->format($matcher); - } - - #[TestWith([new EachValue(['value'], [new StringValue("contains single quote '")]), "eachValue(matching(type, 'contains single quote \''))"])] - #[TestWith([new EachValue(['value'], [new StringValue('example value')]), "eachValue(matching(type, 'example value'))"])] - #[TestWith([new EachValue(new stdClass(), [new Regex('\w \d', 'a 1')]), "eachValue(matching(regex, '\w \d', 'a 1'))"])] - public function testFormat(MatcherInterface $matcher, string $expression): void - { - $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/EqualityFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/EqualityFormatterTest.php deleted file mode 100644 index ed618f0c..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/EqualityFormatterTest.php +++ /dev/null @@ -1,56 +0,0 @@ -formatter = new EqualityFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new Equality(new \stdClass()), 'object'])] - #[TestWith([new Equality(['key' => 'value']), 'array'])] - public function testInvalidValue(MatcherInterface $matcher, string $type): void - { - $this->expectException(InvalidValueException::class); - $this->expectExceptionMessage(sprintf("Expression doesn't support value of type %s", $type)); - $this->formatter->format($matcher); - } - - #[TestWith([new Equality("contains single quote '"), "matching(equalTo, 'contains single quote \'')"])] - #[TestWith([new Equality('example value'), "matching(equalTo, 'example value')"])] - #[TestWith([new Equality(100.09), 'matching(equalTo, 100.09)'])] - #[TestWith([new Equality(-99.99), 'matching(equalTo, -99.99)'])] - #[TestWith([new Equality(100), 'matching(equalTo, 100)'])] - #[TestWith([new Equality(-99), 'matching(equalTo, -99)'])] - #[TestWith([new Equality(true), 'matching(equalTo, true)'])] - #[TestWith([new Equality(false), 'matching(equalTo, false)'])] - #[TestWith([new Equality(null), 'matching(equalTo, null)'])] - public function testFormat(MatcherInterface $matcher, string $expression): void - { - $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/StringValueFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/ExpressionFormatterTest.php similarity index 52% rename from tests/PhpPact/Consumer/Matcher/Formatters/Expression/StringValueFormatterTest.php rename to tests/PhpPact/Consumer/Matcher/Formatters/Expression/ExpressionFormatterTest.php index 7cf53408..d72bace5 100644 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/StringValueFormatterTest.php +++ b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/ExpressionFormatterTest.php @@ -2,39 +2,38 @@ namespace PhpPactTest\Consumer\Matcher\Formatters\Expression; -use PhpPact\Consumer\Matcher\Exception\InvalidValueException; use PhpPact\Consumer\Matcher\Exception\MatcherNotSupportedException; -use PhpPact\Consumer\Matcher\Formatters\Expression\StringValueFormatter; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; +use PhpPact\Consumer\Matcher\Matchers\ArrayContains; +use PhpPact\Consumer\Matcher\Matchers\Boolean; use PhpPact\Consumer\Matcher\Matchers\NullValue; -use PhpPact\Consumer\Matcher\Matchers\StringValue; use PhpPact\Consumer\Matcher\Model\FormatterInterface; use PhpPact\Consumer\Matcher\Model\MatcherInterface; use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; -class StringValueFormatterTest extends TestCase +class ExpressionFormatterTest extends TestCase { private FormatterInterface $formatter; protected function setUp(): void { - $this->formatter = new StringValueFormatter(); + $this->formatter = new ExpressionFormatter(); } public function testNotSupportedMatcher(): void { - $matcher = new NullValue(); + $matcher = new ArrayContains([1]); $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); + $this->expectExceptionMessage('Matcher does not support expression format'); $this->formatter->format($matcher); } - #[TestWith([new StringValue("contains single quote '"), "matching(type, 'contains single quote \'')"])] - #[TestWith([new StringValue('value'), "matching(type, 'value')"])] + #[TestWith([new NullValue(), '"matching(type, null)"'])] + #[TestWith([new Boolean(false), '"matching(boolean, false)"'])] public function testFormat(MatcherInterface $matcher, string $expression): void { $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); + $this->assertSame($expression, json_encode($result)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/IncludesFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/IncludesFormatterTest.php deleted file mode 100644 index f0e304f7..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/IncludesFormatterTest.php +++ /dev/null @@ -1,40 +0,0 @@ -formatter = new IncludesFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new Includes("contains single quote '"), "matching(include, 'contains single quote \'')"])] - #[TestWith([new Includes('example value'), "matching(include, 'example value')"])] - public function testFormat(MatcherInterface $matcher, string $expression): void - { - $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/IntegerFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/IntegerFormatterTest.php deleted file mode 100644 index cb82d8df..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/IntegerFormatterTest.php +++ /dev/null @@ -1,48 +0,0 @@ -formatter = new IntegerFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - public function testInvalidValue(): void - { - $matcher = new Integer(); - $this->expectException(InvalidValueException::class); - $this->expectExceptionMessage(sprintf("Integer formatter doesn't support value of type %s", gettype(null))); - $this->formatter->format($matcher); - } - - #[TestWith([new Integer(-99), 'matching(integer, -99)'])] - #[TestWith([new Integer(100), 'matching(integer, 100)'])] - public function testFormat(MatcherInterface $matcher, string $expression): void - { - $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/MatchAllFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/MatchAllFormatterTest.php deleted file mode 100644 index d359fbe0..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/MatchAllFormatterTest.php +++ /dev/null @@ -1,57 +0,0 @@ -formatter = new MatchAllFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new MatchAll(['value'], [])])] - public function testInvalidMatchers(MatchAll $matcher): void - { - $this->expectException(MatchingExpressionException::class); - $this->expectExceptionMessage("Matcher 'matchAll' need at least 1 matchers"); - $this->formatter->format($matcher); - } - - #[TestWith([new MatchAll(['abc' => 'xyz'], [new EachKey(["doesn't matter"], [new StringValue("contains single quote '")]), new EachValue(["doesn't matter"], [new StringValue("contains single quote '")])]), "eachKey(matching(type, 'contains single quote \'')), eachValue(matching(type, 'contains single quote \''))"])] - #[TestWith([new MatchAll(['abc' => 1, 'def' => 234], [new MinType(null, 2, false)]), 'atLeast(2)'])] - #[TestWith([new MatchAll(['abc' => 1, 'def' => 234], [new MinType(null, 1, false), new MaxType(null, 2, false), new EachKey(["doesn't matter"], [new Regex('\w+', 'abc')]), new EachValue(["doesn't matter"], [new Type(100)])]), "atLeast(1), atMost(2), eachKey(matching(regex, '\w+', 'abc')), eachValue(matching(type, 100))"])] - public function testFormat(MatcherInterface $matcher, string $expression): void - { - $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/MaxTypeFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/MaxTypeFormatterTest.php deleted file mode 100644 index ad5d06b1..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/MaxTypeFormatterTest.php +++ /dev/null @@ -1,42 +0,0 @@ -formatter = new MaxTypeFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new MaxType("contains single quote '", 2), "atMost(2), eachValue(matching(type, 'contains single quote \'')"])] - #[TestWith([new MaxType(null, 2, false), 'atMost(2)'])] - #[TestWith([new MaxType('example value', 2, false), 'atMost(2)'])] - #[TestWith([new MaxType(null, 2), 'atMost(2), eachValue(matching(type, null)'])] - #[TestWith([new MaxType('example value', 2), "atMost(2), eachValue(matching(type, 'example value')"])] - public function testFormat(MatcherInterface $matcher, string $expression): void - { - $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/MinMaxTypeFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/MinMaxTypeFormatterTest.php deleted file mode 100644 index c931b879..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/MinMaxTypeFormatterTest.php +++ /dev/null @@ -1,40 +0,0 @@ -formatter = new MinMaxTypeFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new MinMaxType("contains single quote '", 2, 3), "atLeast(2), atMost(3), eachValue(matching(type, 'contains single quote \'')"])] - #[TestWith([new MinMaxType(null, 2, 3), 'atLeast(2), atMost(3), eachValue(matching(type, null)'])] - #[TestWith([new MinMaxType('example value', 2, 3), "atLeast(2), atMost(3), eachValue(matching(type, 'example value')"])] - public function testFormat(MatcherInterface $matcher, string $expression): void - { - $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/MinTypeFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/MinTypeFormatterTest.php deleted file mode 100644 index b4d29dcc..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/MinTypeFormatterTest.php +++ /dev/null @@ -1,42 +0,0 @@ -formatter = new MinTypeFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new MinType("contains single quote '", 1), "atLeast(1), eachValue(matching(type, 'contains single quote \'')"])] - #[TestWith([new MinType(null, 1, false), 'atLeast(1)'])] - #[TestWith([new MinType('example value', 1, false), 'atLeast(1)'])] - #[TestWith([new MinType(null, 1), 'atLeast(1), eachValue(matching(type, null)'])] - #[TestWith([new MinType('example value', 1), "atLeast(1), eachValue(matching(type, 'example value')"])] - public function testFormat(MatcherInterface $matcher, string $expression): void - { - $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/NotEmptyFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/NotEmptyFormatterTest.php deleted file mode 100644 index ad0d5a33..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/NotEmptyFormatterTest.php +++ /dev/null @@ -1,53 +0,0 @@ -formatter = new NotEmptyFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new NotEmpty(new \stdClass()), 'object'])] - #[TestWith([new NotEmpty(['key' => 'value']), 'array'])] - public function testInvalidValue(MatcherInterface $matcher, string $type): void - { - $this->expectException(InvalidValueException::class); - $this->expectExceptionMessage(sprintf("Expression doesn't support value of type %s", $type)); - $this->formatter->format($matcher); - } - - #[TestWith([new NotEmpty("contains single quote '"), "notEmpty('contains single quote \'')"])] - #[TestWith([new NotEmpty('example value'), "notEmpty('example value')"])] - #[TestWith([new NotEmpty(100.09), 'notEmpty(100.09)'])] - #[TestWith([new NotEmpty(100), 'notEmpty(100)'])] - #[TestWith([new NotEmpty(true), 'notEmpty(true)'])] - #[TestWith([new NotEmpty(false), 'notEmpty(false)'])] - public function testFormat(MatcherInterface $matcher, string $expression): void - { - $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/NullValueFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/NullValueFormatterTest.php deleted file mode 100644 index fa98d436..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/NullValueFormatterTest.php +++ /dev/null @@ -1,28 +0,0 @@ -formatter = new NullValueFormatter(); - } - - #[TestWith([new NullValue(), 'matching(type, null)'])] - public function testFormat(MatcherInterface $matcher, string $expression): void - { - $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/NumberFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/NumberFormatterTest.php deleted file mode 100644 index cd843d9f..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/NumberFormatterTest.php +++ /dev/null @@ -1,50 +0,0 @@ -formatter = new NumberFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - public function testInvalidValue(): void - { - $matcher = new Number(); - $this->expectException(InvalidValueException::class); - $this->expectExceptionMessage(sprintf("Number formatter doesn't support value of type %s", gettype(null))); - $this->formatter->format($matcher); - } - - #[TestWith([new Number(-99), 'matching(number, -99)'])] - #[TestWith([new Number(100), 'matching(number, 100)'])] - #[TestWith([new Number(100.01), 'matching(number, 100.01)'])] - #[TestWith([new Number(-100.003), 'matching(number, -100.003)'])] - public function testFormat(MatcherInterface $matcher, string $expression): void - { - $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/RegexFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/RegexFormatterTest.php deleted file mode 100644 index 771ca406..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/RegexFormatterTest.php +++ /dev/null @@ -1,50 +0,0 @@ -formatter = new RegexFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new Regex('\w \d'), 'NULL'])] - #[TestWith([new Regex('\w \d', ['key' => 'value']), 'array'])] - public function testInvalidValue(MatcherInterface $matcher, string $type): void - { - $this->expectException(InvalidValueException::class); - $this->expectExceptionMessage(sprintf("Regex formatter doesn't support value of type %s", $type)); - $this->formatter->format($matcher); - } - - #[TestWith([new Regex("contains single quote '", 'value'), "matching(regex, 'contains single quote \'', 'value')"])] - #[TestWith([new Regex('\w \d', "contains single quote '"), "matching(regex, '\w \d', 'contains single quote \'')"])] - #[TestWith([new Regex('\w{3}\d+', 'abc123'), "matching(regex, '\w{3}\d+', 'abc123')"])] - public function testFormat(MatcherInterface $matcher, string $expression): void - { - $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/SemverFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/SemverFormatterTest.php deleted file mode 100644 index d8833e25..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/SemverFormatterTest.php +++ /dev/null @@ -1,48 +0,0 @@ -formatter = new SemverFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new Semver(), 'NULL'])] - public function testInvalidValue(MatcherInterface $matcher, string $type): void - { - $this->expectException(InvalidValueException::class); - $this->expectExceptionMessage(sprintf("Semver formatter doesn't support value of type %s", $type)); - $this->formatter->format($matcher); - } - - #[TestWith([new Semver("contains single quote '"), "matching(semver, 'contains single quote \'')"])] - #[TestWith([new Semver('1.0.0'), "matching(semver, '1.0.0')"])] - public function testFormat(MatcherInterface $matcher, string $expression): void - { - $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/TypeFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Expression/TypeFormatterTest.php deleted file mode 100644 index 3eda0df3..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/TypeFormatterTest.php +++ /dev/null @@ -1,58 +0,0 @@ -formatter = new TypeFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new Type(new \stdClass()), 'object'])] - #[TestWith([new Type(['key' => 'value']), 'array'])] - public function testInvalidValue(MatcherInterface $matcher, string $type): void - { - $this->expectException(InvalidValueException::class); - $this->expectExceptionMessage(sprintf("Expression doesn't support value of type %s", $type)); - $this->formatter->format($matcher); - } - - #[TestWith([new Type("contains single quote '"), "matching(type, 'contains single quote \'')"])] - #[TestWith([new Type('example value'), "matching(type, 'example value')"])] - #[TestWith([new Type(100.09), 'matching(type, 100.09)'])] - #[TestWith([new Type(-99.99), 'matching(type, -99.99)'])] - #[TestWith([new Type(100), 'matching(type, 100)'])] - #[TestWith([new Type(-99), 'matching(type, -99)'])] - #[TestWith([new Type(true), 'matching(type, true)'])] - #[TestWith([new Type(false), 'matching(type, false)'])] - #[TestWith([new Type(null), 'matching(type, null)'])] - public function testFormat(MatcherInterface $matcher, string $expression): void - { - $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Json/ContentTypeFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Json/ContentTypeFormatterTest.php deleted file mode 100644 index ec72ff9c..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Json/ContentTypeFormatterTest.php +++ /dev/null @@ -1,40 +0,0 @@ -formatter = new ContentTypeFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new ContentType('plain/text'), '{"pact:matcher:type": "contentType", "value": "plain/text"}'])] - #[TestWith([new ContentType('application/json', '{"key":"value"}'), '{"pact:matcher:type": "contentType", "value": "application/json"}'])] - #[TestWith([new ContentType('application/xml', ''), '{"pact:matcher:type": "contentType", "value": "application/xml"}'])] - public function testFormat(MatcherInterface $matcher, string $json): void - { - $jsonEncoded = json_encode($this->formatter->format($matcher)); - $this->assertIsString($jsonEncoded); - $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Json/HasGeneratorFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Json/HasGeneratorFormatterTest.php deleted file mode 100644 index 0c7e0236..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Json/HasGeneratorFormatterTest.php +++ /dev/null @@ -1,42 +0,0 @@ -formatter = new HasGeneratorFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new Time('HH:mm'), true, '{"pact:matcher:type": "time", "pact:generator:type": "Regex", "format": "HH:mm", "regex": "\\\\d{2}:\\\\d{2}"}'])] - #[TestWith([new Time('HH:mm', '12:34'), false, '{"pact:matcher:type": "time", "format": "HH:mm", "value": "12:34"}'])] - public function testFormat(Time $matcher, bool $hasGenerator, string $json): void - { - if ($hasGenerator) { - $matcher->setGenerator(new Regex('\d{2}:\d{2}')); - } - $jsonEncoded = json_encode($this->formatter->format($matcher)); - $this->assertIsString($jsonEncoded); - $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/MatchingFieldFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Json/JsonFormatterTest.php similarity index 50% rename from tests/PhpPact/Consumer/Matcher/Formatters/Expression/MatchingFieldFormatterTest.php rename to tests/PhpPact/Consumer/Matcher/Formatters/Json/JsonFormatterTest.php index dbf1712b..c03dabdf 100644 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Expression/MatchingFieldFormatterTest.php +++ b/tests/PhpPact/Consumer/Matcher/Formatters/Json/JsonFormatterTest.php @@ -1,10 +1,10 @@ formatter = new MatchingFieldFormatter(); + $this->formatter = new JsonFormatter(); } public function testNotSupportedMatcher(): void { - $matcher = new NullValue(); + $matcher = new MatchingField('name'); $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); + $this->expectExceptionMessage('Matcher does not support json format'); $this->formatter->format($matcher); } - #[TestWith([new MatchingField("contains single quote '"), "matching($'contains single quote \'')"])] - #[TestWith([new MatchingField('product'), "matching($'product')"])] - public function testFormat(MatcherInterface $matcher, string $expression): void + #[TestWith([new NullValue(), '{"pact:matcher:type":"null"}'])] + #[TestWith([new Boolean(false), '{"pact:matcher:type":"boolean","value":false}'])] + public function testFormat(MatcherInterface $matcher, string $json): void { $result = $this->formatter->format($matcher); - $this->assertIsString($result); - $this->assertSame($expression, $result); + $this->assertSame($json, json_encode($result)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Json/MatchAllFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Json/MatchAllFormatterTest.php deleted file mode 100644 index ac9b0804..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Json/MatchAllFormatterTest.php +++ /dev/null @@ -1,112 +0,0 @@ -formatter = new MatchAllFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new MatchAll(['abc' => 1, 'def' => 234], [new MinType(null, 2)]), << 1, 'def' => 234], [new MinType(null, 1), new MaxType(null, 2), new EachKey(["doesn't matter"], [new Regex('\w+', 'abc')]), new EachValue(["doesn't matter"], [new Type(100)])]), <<formatter->format($matcher)); - $this->assertIsString($jsonEncoded); - $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Json/MaxTypeFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Json/MaxTypeFormatterTest.php deleted file mode 100644 index acb3f9f8..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Json/MaxTypeFormatterTest.php +++ /dev/null @@ -1,40 +0,0 @@ -formatter = new MaxTypeFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new MaxType('example text', 2, true), '{"pact:matcher:type": "type", "value": ["example text"], "max": 2}'])] - #[TestWith([new MaxType('example text', -2, true), '{"pact:matcher:type": "type", "value": ["example text"], "max": 0}'])] - #[TestWith([new MaxType('example text', 2, false), '{"pact:matcher:type": "type", "value": ["example text"], "max": 2}'])] - #[TestWith([new MaxType('example text', -2, false), '{"pact:matcher:type": "type", "value": ["example text"], "max": 0}'])] - public function testFormat(MaxType $matcher, string $json): void - { - $jsonEncoded = json_encode($this->formatter->format($matcher)); - $this->assertIsString($jsonEncoded); - $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Json/MinMaxTypeFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Json/MinMaxTypeFormatterTest.php deleted file mode 100644 index d5b6956e..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Json/MinMaxTypeFormatterTest.php +++ /dev/null @@ -1,40 +0,0 @@ -formatter = new MinMaxTypeFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new MinMaxType('example text', 2, 3), '{"pact:matcher:type": "type", "value": ["example text", "example text"], "min": 2, "max": 3}'])] - #[TestWith([new MinMaxType('example text', -2, 3), '{"pact:matcher:type": "type", "value": ["example text"], "min": 0, "max": 3}'])] - #[TestWith([new MinMaxType('example text', 2, -3), '{"pact:matcher:type": "type", "value": ["example text", "example text"], "min": 2, "max": 0}'])] - #[TestWith([new MinMaxType('example text', -2, -3), '{"pact:matcher:type": "type", "value": ["example text"], "min": 0, "max": 0}'])] - public function testFormat(MinMaxType $matcher, string $json): void - { - $jsonEncoded = json_encode($this->formatter->format($matcher)); - $this->assertIsString($jsonEncoded); - $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Json/MinTypeFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Json/MinTypeFormatterTest.php deleted file mode 100644 index ab454d22..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Json/MinTypeFormatterTest.php +++ /dev/null @@ -1,40 +0,0 @@ -formatter = new MinTypeFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new MinType('example text', 2, true), '{"pact:matcher:type": "type", "value": ["example text", "example text"], "min": 2}'])] - #[TestWith([new MinType('example text', -2, true), '{"pact:matcher:type": "type", "value": ["example text"], "min": 0}'])] - #[TestWith([new MinType('example text', 2, false), '{"pact:matcher:type": "type", "value": ["example text", "example text"], "min": 2}'])] - #[TestWith([new MinType('example text', -2, false), '{"pact:matcher:type": "type", "value": ["example text"], "min": 0}'])] - public function testFormat(MinType $matcher, string $json): void - { - $jsonEncoded = json_encode($this->formatter->format($matcher)); - $this->assertIsString($jsonEncoded); - $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Json/NoGeneratorFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Json/NoGeneratorFormatterTest.php deleted file mode 100644 index 20c320c7..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Json/NoGeneratorFormatterTest.php +++ /dev/null @@ -1,43 +0,0 @@ -formatter = new NoGeneratorFormatter(); - } - - #[TestWith([new EachKey(['key' => 'value'], [new Includes('value')]), <<formatter->format($matcher)); - $this->assertIsString($jsonEncoded); - $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Json/NullValueFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Json/NullValueFormatterTest.php deleted file mode 100644 index fc93c515..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Json/NullValueFormatterTest.php +++ /dev/null @@ -1,28 +0,0 @@ -formatter = new NullValueFormatter(); - } - - #[TestWith([new NullValue(), '{"pact:matcher:type": "null"}'])] - public function testFormat(MatcherInterface $matcher, string $json): void - { - $jsonEncoded = json_encode($this->formatter->format($matcher)); - $this->assertIsString($jsonEncoded); - $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Json/StringValueFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Json/StringValueFormatterTest.php deleted file mode 100644 index ea911947..00000000 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Json/StringValueFormatterTest.php +++ /dev/null @@ -1,42 +0,0 @@ -formatter = new StringValueFormatter(); - } - - public function testNotSupportedMatcher(): void - { - $matcher = new NullValue(); - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage(sprintf('Matcher %s is not supported by %s', $matcher->getType(), $this->formatter::class)); - $this->formatter->format($matcher); - } - - #[TestWith([new StringValue(), true, '{"pact:matcher:type": "type", "pact:generator:type": "Regex", "regex": "\\\\w{3}", "value": "some string"}'])] - #[TestWith([new StringValue('example text'), false, '{"pact:matcher:type": "type", "value": "example text"}'])] - public function testFormat(StringValue $matcher, bool $hasGenerator, string $json): void - { - if ($hasGenerator) { - $matcher->setGenerator(new Regex('\w{3}')); - } - $jsonEncoded = json_encode($this->formatter->format($matcher)); - $this->assertIsString($jsonEncoded); - $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Formatters/Xml/XmlContentFormatterTest.php b/tests/PhpPact/Consumer/Matcher/Formatters/Xml/XmlContentFormatterTest.php index ae6f60d7..dc7536ce 100644 --- a/tests/PhpPact/Consumer/Matcher/Formatters/Xml/XmlContentFormatterTest.php +++ b/tests/PhpPact/Consumer/Matcher/Formatters/Xml/XmlContentFormatterTest.php @@ -2,24 +2,53 @@ namespace PhpPactTest\Consumer\Matcher\Formatters\Xml; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; use PhpPact\Consumer\Matcher\Formatters\Xml\XmlContentFormatter; use PhpPact\Consumer\Matcher\Generators\RandomString; use PhpPact\Consumer\Matcher\Matchers\Date; +use PhpPact\Consumer\Matcher\Matchers\Type; +use PhpPact\Consumer\Matcher\Model\GeneratorInterface; use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; class XmlContentFormatterTest extends TestCase { - #[TestWith([true, '2001-01-02', '{"content": "2001-01-02", "matcher": {"pact:matcher:type": "date", "format": "yyyy-MM-dd", "size": 10}, "pact:generator:type": "RandomString"}'])] - #[TestWith([false, '2002-02-03', '{"content": "2002-02-03", "matcher": {"pact:matcher:type": "date", "format": "yyyy-MM-dd"}}'])] - public function testFormat(bool $hasGenerator, ?string $value, string $result): void + #[TestWith([['key' => 'value'], 'array'])] + #[TestWith([['key' => 'value'], 'object'])] + public function testFormatInvalidValue(mixed $value, string $type): void { - $matcher = new Date('yyyy-MM-dd', $value); - $generator = $hasGenerator ? new RandomString(10) : null; + settype($value, $type); + $this->expectException(InvalidValueException::class); + $this->expectExceptionMessage('Value of xml content must be string, float, int, bool or null'); + $matcher = new Type($value); + $formatter = new XmlContentFormatter(); + $formatter->format($matcher); + } + + #[TestWith([new RandomString(10), '{"content": "2001-01-02", "matcher": {"pact:matcher:type": "date", "format": "yyyy-MM-dd", "size": 10}, "pact:generator:type": "RandomString"}'])] + #[TestWith([null, '{"content": "2001-01-02", "matcher": {"pact:matcher:type": "date", "format": "yyyy-MM-dd"}}'])] + public function testFormatGenerator(?GeneratorInterface $generator, string $result): void + { + $matcher = new Date('yyyy-MM-dd', '2001-01-02'); $matcher->setGenerator($generator); $formatter = new XmlContentFormatter(); $jsonEncoded = json_encode($formatter->format($matcher)); $this->assertIsString($jsonEncoded); $this->assertJsonStringEqualsJsonString($result, $jsonEncoded); } + + #[TestWith(['text', '{"content": "text", "matcher": {"pact:matcher:type": "type"}}'])] + #[TestWith([-123.45, '{"content": -123.45, "matcher": {"pact:matcher:type": "type"}}'])] + #[TestWith([-12, '{"content": -12, "matcher": {"pact:matcher:type": "type"}}'])] + #[TestWith([true, '{"content": true, "matcher": {"pact:matcher:type": "type"}}'])] + #[TestWith([false, '{"content": false, "matcher": {"pact:matcher:type": "type"}}'])] + #[TestWith([null, '{"content": null, "matcher": {"pact:matcher:type": "type"}}'])] + public function testFormatValue(mixed $value, string $result): void + { + $matcher = new Type($value); + $formatter = new XmlContentFormatter(); + $jsonEncoded = json_encode($formatter->format($matcher)); + $this->assertIsString($jsonEncoded); + $this->assertJsonStringEqualsJsonString($result, $jsonEncoded); + } } diff --git a/tests/PhpPact/Consumer/Matcher/Generators/DateTest.php b/tests/PhpPact/Consumer/Matcher/Generators/DateTest.php index 93d82b1b..29f2724a 100644 --- a/tests/PhpPact/Consumer/Matcher/Generators/DateTest.php +++ b/tests/PhpPact/Consumer/Matcher/Generators/DateTest.php @@ -8,24 +8,16 @@ class DateTest extends TestCase { - public function testType(): void - { - $generator = new Date(); - $this->assertSame('Date', $generator->getType()); - } - - /** - * @param array $data - */ - #[TestWith([null, null, []])] - #[TestWith(['yyyy-MM-dd', null, ['format' => 'yyyy-MM-dd']])] - #[TestWith([null, '+1 day', ['expression' => '+1 day']])] - #[TestWith(['yyyy-MM-dd', '+1 day', ['format' => 'yyyy-MM-dd', 'expression' => '+1 day']])] - public function testAttributes(?string $format, ?string $expression, array $data): void + #[TestWith([null, null, '{"pact:generator:type":"Date"}'])] + #[TestWith(['yyyy-MM-dd', null, '{"pact:generator:type":"Date","format":"yyyy-MM-dd"}'])] + #[TestWith([null, '+1 day', '{"pact:generator:type":"Date","expression":"+1 day"}'])] + #[TestWith(['yyyy-MM-dd', '+1 day', '{"pact:generator:type":"Date","format":"yyyy-MM-dd","expression":"+1 day"}'])] + public function testFormatJson(?string $format, ?string $expression, string $json): void { $generator = new Date($format, $expression); - $attributes = $generator->getAttributes(); - $this->assertSame($generator, $attributes->getParent()); - $this->assertSame($data, $attributes->getData()); + $attributes = $generator->formatJson(); + $result = json_encode($attributes); + $this->assertIsString($result); + $this->assertSame($json, $result); } } diff --git a/tests/PhpPact/Consumer/Matcher/Generators/DateTimeTest.php b/tests/PhpPact/Consumer/Matcher/Generators/DateTimeTest.php index 96210be5..7929c52c 100644 --- a/tests/PhpPact/Consumer/Matcher/Generators/DateTimeTest.php +++ b/tests/PhpPact/Consumer/Matcher/Generators/DateTimeTest.php @@ -8,24 +8,16 @@ class DateTimeTest extends TestCase { - public function testType(): void - { - $generator = new DateTime(); - $this->assertSame('DateTime', $generator->getType()); - } - - /** - * @param array $data - */ - #[TestWith([null, null, []])] - #[TestWith(["yyyy-MM-dd'T'HH:mm:ss", null, ['format' => "yyyy-MM-dd'T'HH:mm:ss"]])] - #[TestWith([null, '+1 day', ['expression' => '+1 day']])] - #[TestWith(["yyyy-MM-dd'T'HH:mm:ss", '+1 day', ['format' => "yyyy-MM-dd'T'HH:mm:ss", 'expression' => '+1 day']])] - public function testAttributes(?string $format, ?string $expression, array $data): void + #[TestWith([null, null, '{"pact:generator:type":"DateTime"}'])] + #[TestWith(["yyyy-MM-dd'T'HH:mm:ss", null, '{"pact:generator:type":"DateTime","format":"yyyy-MM-dd\'T\'HH:mm:ss"}'])] + #[TestWith([null, '+1 day', '{"pact:generator:type":"DateTime","expression":"+1 day"}'])] + #[TestWith(["yyyy-MM-dd'T'HH:mm:ss", '+1 day', '{"pact:generator:type":"DateTime","format":"yyyy-MM-dd\'T\'HH:mm:ss","expression":"+1 day"}'])] + public function testFormatJson(?string $format, ?string $expression, string $json): void { $generator = new DateTime($format, $expression); - $attributes = $generator->getAttributes(); - $this->assertSame($generator, $attributes->getParent()); - $this->assertSame($data, $attributes->getData()); + $attributes = $generator->formatJson(); + $result = json_encode($attributes); + $this->assertIsString($result); + $this->assertSame($json, $result); } } diff --git a/tests/PhpPact/Consumer/Matcher/Generators/MockServerURLTest.php b/tests/PhpPact/Consumer/Matcher/Generators/MockServerURLTest.php index de6dbfde..c6747ef2 100644 --- a/tests/PhpPact/Consumer/Matcher/Generators/MockServerURLTest.php +++ b/tests/PhpPact/Consumer/Matcher/Generators/MockServerURLTest.php @@ -3,27 +3,16 @@ namespace PhpPactTest\Consumer\Matcher\Generators; use PhpPact\Consumer\Matcher\Generators\MockServerURL; -use PhpPact\Consumer\Matcher\Model\GeneratorInterface; use PHPUnit\Framework\TestCase; class MockServerURLTest extends TestCase { - private GeneratorInterface $generator; - - protected function setUp(): void - { - $this->generator = new MockServerURL('.*(/path)$', 'http://localhost:1234/path'); - } - - public function testType(): void - { - $this->assertSame('MockServerURL', $this->generator->getType()); - } - - public function testAttributes(): void + public function testFormatJson(): void { - $attributes = $this->generator->getAttributes(); - $this->assertSame($this->generator, $attributes->getParent()); - $this->assertSame(['regex' => '.*(/path)$', 'example' => 'http://localhost:1234/path'], $attributes->getData()); + $generator = new MockServerURL('.*(/path)$', 'http://localhost:1234/path'); + $attributes = $generator->formatJson(); + $result = json_encode($attributes); + $this->assertIsString($result); + $this->assertSame('{"pact:generator:type":"MockServerURL","regex":".*(\/path)$","example":"http:\/\/localhost:1234\/path"}', $result); } } diff --git a/tests/PhpPact/Consumer/Matcher/Generators/ProviderStateTest.php b/tests/PhpPact/Consumer/Matcher/Generators/ProviderStateTest.php index eaec9144..bb16473d 100644 --- a/tests/PhpPact/Consumer/Matcher/Generators/ProviderStateTest.php +++ b/tests/PhpPact/Consumer/Matcher/Generators/ProviderStateTest.php @@ -3,27 +3,22 @@ namespace PhpPactTest\Consumer\Matcher\Generators; use PhpPact\Consumer\Matcher\Generators\ProviderState; -use PhpPact\Consumer\Matcher\Model\GeneratorInterface; use PHPUnit\Framework\TestCase; class ProviderStateTest extends TestCase { - private GeneratorInterface $generator; + private ProviderState $generator; protected function setUp(): void { $this->generator = new ProviderState('/products/${id}'); } - public function testType(): void + public function testFormatJson(): void { - $this->assertSame('ProviderState', $this->generator->getType()); - } - - public function testAttributes(): void - { - $attributes = $this->generator->getAttributes(); - $this->assertSame($this->generator, $attributes->getParent()); - $this->assertSame(['expression' => '/products/${id}'], $attributes->getData()); + $attributes = $this->generator->formatJson(); + $result = json_encode($attributes); + $this->assertIsString($result); + $this->assertSame('{"pact:generator:type":"ProviderState","expression":"\/products\/${id}"}', $result); } } diff --git a/tests/PhpPact/Consumer/Matcher/Generators/RandomBooleanTest.php b/tests/PhpPact/Consumer/Matcher/Generators/RandomBooleanTest.php index 5bd8e790..815194b7 100644 --- a/tests/PhpPact/Consumer/Matcher/Generators/RandomBooleanTest.php +++ b/tests/PhpPact/Consumer/Matcher/Generators/RandomBooleanTest.php @@ -3,27 +3,16 @@ namespace PhpPactTest\Consumer\Matcher\Generators; use PhpPact\Consumer\Matcher\Generators\RandomBoolean; -use PhpPact\Consumer\Matcher\Model\GeneratorInterface; use PHPUnit\Framework\TestCase; class RandomBooleanTest extends TestCase { - private GeneratorInterface $generator; - - protected function setUp(): void - { - $this->generator = new RandomBoolean(); - } - - public function testType(): void - { - $this->assertSame('RandomBoolean', $this->generator->getType()); - } - - public function testAttributes(): void + public function testFormatJson(): void { - $attributes = $this->generator->getAttributes(); - $this->assertSame($this->generator, $attributes->getParent()); - $this->assertSame([], $attributes->getData()); + $generator = new RandomBoolean(); + $attributes = $generator->formatJson(); + $result = json_encode($attributes); + $this->assertIsString($result); + $this->assertSame('{"pact:generator:type":"RandomBoolean"}', $result); } } diff --git a/tests/PhpPact/Consumer/Matcher/Generators/RandomDecimalTest.php b/tests/PhpPact/Consumer/Matcher/Generators/RandomDecimalTest.php index 3b0b354e..81ee837b 100644 --- a/tests/PhpPact/Consumer/Matcher/Generators/RandomDecimalTest.php +++ b/tests/PhpPact/Consumer/Matcher/Generators/RandomDecimalTest.php @@ -3,27 +3,16 @@ namespace PhpPactTest\Consumer\Matcher\Generators; use PhpPact\Consumer\Matcher\Generators\RandomDecimal; -use PhpPact\Consumer\Matcher\Model\GeneratorInterface; use PHPUnit\Framework\TestCase; class RandomDecimalTest extends TestCase { - private GeneratorInterface $generator; - - protected function setUp(): void - { - $this->generator = new RandomDecimal(12); - } - - public function testType(): void - { - $this->assertSame('RandomDecimal', $this->generator->getType()); - } - - public function testAttributes(): void + public function testFormatJson(): void { - $attributes = $this->generator->getAttributes(); - $this->assertSame($this->generator, $attributes->getParent()); - $this->assertSame(['digits' => 12], $attributes->getData()); + $generator = new RandomDecimal(12); + $attributes = $generator->formatJson(); + $result = json_encode($attributes); + $this->assertIsString($result); + $this->assertSame('{"pact:generator:type":"RandomDecimal","digits":12}', $result); } } diff --git a/tests/PhpPact/Consumer/Matcher/Generators/RandomHexadecimalTest.php b/tests/PhpPact/Consumer/Matcher/Generators/RandomHexadecimalTest.php index dd95abc9..59b42e1f 100644 --- a/tests/PhpPact/Consumer/Matcher/Generators/RandomHexadecimalTest.php +++ b/tests/PhpPact/Consumer/Matcher/Generators/RandomHexadecimalTest.php @@ -3,27 +3,16 @@ namespace PhpPactTest\Consumer\Matcher\Generators; use PhpPact\Consumer\Matcher\Generators\RandomHexadecimal; -use PhpPact\Consumer\Matcher\Model\GeneratorInterface; use PHPUnit\Framework\TestCase; class RandomHexadecimalTest extends TestCase { - private GeneratorInterface $generator; - - protected function setUp(): void - { - $this->generator = new RandomHexadecimal(8); - } - - public function testType(): void - { - $this->assertSame('RandomHexadecimal', $this->generator->getType()); - } - - public function testAttributes(): void + public function testFormatJson(): void { - $attributes = $this->generator->getAttributes(); - $this->assertSame($this->generator, $attributes->getParent()); - $this->assertSame(['digits' => 8], $attributes->getData()); + $generator = new RandomHexadecimal(8); + $attributes = $generator->formatJson(); + $result = json_encode($attributes); + $this->assertIsString($result); + $this->assertSame('{"pact:generator:type":"RandomHexadecimal","digits":8}', $result); } } diff --git a/tests/PhpPact/Consumer/Matcher/Generators/RandomIntTest.php b/tests/PhpPact/Consumer/Matcher/Generators/RandomIntTest.php index ef8b8c6e..4511b9c7 100644 --- a/tests/PhpPact/Consumer/Matcher/Generators/RandomIntTest.php +++ b/tests/PhpPact/Consumer/Matcher/Generators/RandomIntTest.php @@ -3,27 +3,16 @@ namespace PhpPactTest\Consumer\Matcher\Generators; use PhpPact\Consumer\Matcher\Generators\RandomInt; -use PhpPact\Consumer\Matcher\Model\GeneratorInterface; use PHPUnit\Framework\TestCase; class RandomIntTest extends TestCase { - private GeneratorInterface $generator; - - protected function setUp(): void - { - $this->generator = new RandomInt(5, 15); - } - - public function testType(): void - { - $this->assertSame('RandomInt', $this->generator->getType()); - } - - public function testAttributes(): void + public function testFormatJson(): void { - $attributes = $this->generator->getAttributes(); - $this->assertSame($this->generator, $attributes->getParent()); - $this->assertSame(['min' => 5, 'max' => 15], $attributes->getData()); + $generator = new RandomInt(5, 15); + $attributes = $generator->formatJson(); + $result = json_encode($attributes); + $this->assertIsString($result); + $this->assertSame('{"pact:generator:type":"RandomInt","min":5,"max":15}', $result); } } diff --git a/tests/PhpPact/Consumer/Matcher/Generators/RandomStringTest.php b/tests/PhpPact/Consumer/Matcher/Generators/RandomStringTest.php index 5afdb898..4f4fdcfa 100644 --- a/tests/PhpPact/Consumer/Matcher/Generators/RandomStringTest.php +++ b/tests/PhpPact/Consumer/Matcher/Generators/RandomStringTest.php @@ -3,27 +3,16 @@ namespace PhpPactTest\Consumer\Matcher\Generators; use PhpPact\Consumer\Matcher\Generators\RandomString; -use PhpPact\Consumer\Matcher\Model\GeneratorInterface; use PHPUnit\Framework\TestCase; class RandomStringTest extends TestCase { - private GeneratorInterface $generator; - - protected function setUp(): void - { - $this->generator = new RandomString(11); - } - - public function testType(): void - { - $this->assertSame('RandomString', $this->generator->getType()); - } - - public function testAttributes(): void + public function testFormatJson(): void { - $attributes = $this->generator->getAttributes(); - $this->assertSame($this->generator, $attributes->getParent()); - $this->assertSame(['size' => 11], $attributes->getData()); + $generator = new RandomString(11); + $attributes = $generator->formatJson(); + $result = json_encode($attributes); + $this->assertIsString($result); + $this->assertSame('{"pact:generator:type":"RandomString","size":11}', $result); } } diff --git a/tests/PhpPact/Consumer/Matcher/Generators/RegexTest.php b/tests/PhpPact/Consumer/Matcher/Generators/RegexTest.php index 35a1c158..240a50ec 100644 --- a/tests/PhpPact/Consumer/Matcher/Generators/RegexTest.php +++ b/tests/PhpPact/Consumer/Matcher/Generators/RegexTest.php @@ -3,27 +3,16 @@ namespace PhpPactTest\Consumer\Matcher\Generators; use PhpPact\Consumer\Matcher\Generators\Regex; -use PhpPact\Consumer\Matcher\Model\GeneratorInterface; use PHPUnit\Framework\TestCase; class RegexTest extends TestCase { - private GeneratorInterface $generator; - - protected function setUp(): void - { - $this->generator = new Regex('[\w\d]+'); - } - - public function testType(): void - { - $this->assertSame('Regex', $this->generator->getType()); - } - - public function testAttributes(): void + public function testFormatJson(): void { - $attributes = $this->generator->getAttributes(); - $this->assertSame($this->generator, $attributes->getParent()); - $this->assertSame(['regex' => '[\w\d]+'], $attributes->getData()); + $generator = new Regex('[\w\d]+'); + $attributes = $generator->formatJson(); + $result = json_encode($attributes); + $this->assertIsString($result); + $this->assertSame('{"pact:generator:type":"Regex","regex":"[\\\\w\\\\d]+"}', $result); } } diff --git a/tests/PhpPact/Consumer/Matcher/Generators/TimeTest.php b/tests/PhpPact/Consumer/Matcher/Generators/TimeTest.php index 7c74f0c5..28869a1c 100644 --- a/tests/PhpPact/Consumer/Matcher/Generators/TimeTest.php +++ b/tests/PhpPact/Consumer/Matcher/Generators/TimeTest.php @@ -8,24 +8,16 @@ class TimeTest extends TestCase { - public function testType(): void - { - $generator = new Time(); - $this->assertSame('Time', $generator->getType()); - } - - /** - * @param array $data - */ - #[TestWith([null, null, []])] - #[TestWith(['HH:mm:ss', null, ['format' => 'HH:mm:ss']])] - #[TestWith([null, '+1 hour', ['expression' => '+1 hour']])] - #[TestWith(['HH:mm:ss', '+1 hour', ['format' => 'HH:mm:ss', 'expression' => '+1 hour']])] - public function testAttributes(?string $format, ?string $expression, array $data): void + #[TestWith([null, null, '{"pact:generator:type":"Time"}'])] + #[TestWith(['HH:mm:ss', null, '{"pact:generator:type":"Time","format":"HH:mm:ss"}'])] + #[TestWith([null, '+1 hour', '{"pact:generator:type":"Time","expression":"+1 hour"}'])] + #[TestWith(['HH:mm:ss', '+1 hour', '{"pact:generator:type":"Time","format":"HH:mm:ss","expression":"+1 hour"}'])] + public function testFormatJson(?string $format, ?string $expression, string $json): void { $generator = new Time($format, $expression); - $attributes = $generator->getAttributes(); - $this->assertSame($generator, $attributes->getParent()); - $this->assertSame($data, $attributes->getData()); + $attributes = $generator->formatJson(); + $result = json_encode($attributes); + $this->assertIsString($result); + $this->assertSame($json, $result); } } diff --git a/tests/PhpPact/Consumer/Matcher/Generators/UuidTest.php b/tests/PhpPact/Consumer/Matcher/Generators/UuidTest.php index e2ef9adf..92884b85 100644 --- a/tests/PhpPact/Consumer/Matcher/Generators/UuidTest.php +++ b/tests/PhpPact/Consumer/Matcher/Generators/UuidTest.php @@ -9,30 +9,24 @@ class UuidTest extends TestCase { - public function testType(): void + public function testInvalidFormat(): void { - $generator = new Uuid(); - $this->assertSame('Uuid', $generator->getType()); + $this->expectException(InvalidUuidFormatException::class); + $this->expectExceptionMessage('Format invalid is not supported. Supported formats are: simple, lower-case-hyphenated, upper-case-hyphenated, URN'); + new Uuid('invalid'); } - /** - * @param null|array $data - */ - #[TestWith([null, []])] - #[TestWith(['simple', ['format' => 'simple']])] - #[TestWith(['lower-case-hyphenated', ['format' => 'lower-case-hyphenated']])] - #[TestWith(['upper-case-hyphenated', ['format' => 'upper-case-hyphenated']])] - #[TestWith(['URN', ['format' => 'URN']])] - #[TestWith(['invalid', null])] - public function testAttributes(?string $format, ?array $data): void + #[TestWith([null, '{"pact:generator:type":"Uuid"}'])] + #[TestWith(['simple', '{"pact:generator:type":"Uuid","format":"simple"}'])] + #[TestWith(['lower-case-hyphenated', '{"pact:generator:type":"Uuid","format":"lower-case-hyphenated"}'])] + #[TestWith(['upper-case-hyphenated', '{"pact:generator:type":"Uuid","format":"upper-case-hyphenated"}'])] + #[TestWith(['URN', '{"pact:generator:type":"Uuid","format":"URN"}'])] + public function testAttributes(?string $format, string $json): void { - if (null === $data) { - $this->expectException(InvalidUuidFormatException::class); - $this->expectExceptionMessage('Format invalid is not supported. Supported formats are: simple, lower-case-hyphenated, upper-case-hyphenated, URN'); - } $generator = new Uuid($format); - $attributes = $generator->getAttributes(); - $this->assertSame($generator, $attributes->getParent()); - $this->assertSame($data, $attributes->getData()); + $attributes = $generator->formatJson(); + $result = json_encode($attributes); + $this->assertIsString($result); + $this->assertSame($json, $result); } } diff --git a/tests/PhpPact/Consumer/Matcher/MatcherTest.php b/tests/PhpPact/Consumer/Matcher/MatcherTest.php index e76c7845..6c0e7cbe 100644 --- a/tests/PhpPact/Consumer/Matcher/MatcherTest.php +++ b/tests/PhpPact/Consumer/Matcher/MatcherTest.php @@ -4,8 +4,8 @@ use PhpPact\Consumer\Matcher\Enum\HttpStatus; use PhpPact\Consumer\Matcher\Exception\MatcherNotSupportedException; -use PhpPact\Consumer\Matcher\Formatters\Expression\RegexFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\HasGeneratorFormatter; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; +use PhpPact\Consumer\Matcher\Formatters\Json\JsonFormatter; use PhpPact\Consumer\Matcher\Generators\MockServerURL; use PhpPact\Consumer\Matcher\Generators\ProviderState; use PhpPact\Consumer\Matcher\Generators\RandomHexadecimal; @@ -24,7 +24,9 @@ use PhpPact\Consumer\Matcher\Matchers\Integer; use PhpPact\Consumer\Matcher\Matchers\MatchAll; use PhpPact\Consumer\Matcher\Matchers\MatchingField; +use PhpPact\Consumer\Matcher\Matchers\Max; use PhpPact\Consumer\Matcher\Matchers\MaxType; +use PhpPact\Consumer\Matcher\Matchers\Min; use PhpPact\Consumer\Matcher\Matchers\MinMaxType; use PhpPact\Consumer\Matcher\Matchers\MinType; use PhpPact\Consumer\Matcher\Matchers\NotEmpty; @@ -51,63 +53,62 @@ protected function setUp(): void public function testSomethingLike(): void { - $this->assertInstanceOf(Type::class, $this->matcher->somethingLike(123)); + $this->assertInstanceOf(Type::class, $result = $this->matcher->somethingLike(123)); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testLike(): void { - $this->assertInstanceOf(Type::class, $this->matcher->like('abc')); + $this->assertInstanceOf(Type::class, $result = $this->matcher->like('abc')); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testEachLike(): void { $this->assertInstanceOf(MinType::class, $result = $this->matcher->eachLike('test')); - $this->assertSame('test', $result->getValue()); - $this->assertSame(1, $result->getMin()); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testAtLeastOneLike(): void { $this->assertInstanceOf(MinType::class, $result = $this->matcher->atLeastOneLike('test')); - $this->assertSame('test', $result->getValue()); - $this->assertSame(1, $result->getMin()); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testAtLeastLike(): void { $this->assertInstanceOf(MinType::class, $result = $this->matcher->atLeastLike('test', 2)); - $this->assertSame('test', $result->getValue()); - $this->assertSame(2, $result->getMin()); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testAtMostLike(): void { $this->assertInstanceOf(MaxType::class, $result = $this->matcher->atMostLike('test', 2)); - $this->assertSame('test', $result->getValue()); - $this->assertSame(2, $result->getMax()); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testConstrainedArrayLike(): void { $this->assertInstanceOf(MinMaxType::class, $result = $this->matcher->constrainedArrayLike('test', 2, 4)); - $this->assertSame('test', $result->getValue()); - $this->assertSame(2, $result->getMin()); - $this->assertSame(4, $result->getMax()); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testTerm(): void { - $this->assertInstanceOf(Regex::class, $this->matcher->term('123', '\d+')); + $this->assertInstanceOf(Regex::class, $result = $this->matcher->term('123', '\d+')); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testRegex(): void { - $this->assertInstanceOf(Regex::class, $this->matcher->regex('Games', 'Games|Other')); + $this->assertInstanceOf(Regex::class, $result = $this->matcher->regex('Games', 'Games|Other')); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testDateISO8601(): void { - $this->assertInstanceOf(Regex::class, $this->matcher->dateISO8601('2010-01-17')); + $this->assertInstanceOf(Regex::class, $result = $this->matcher->dateISO8601('2010-01-17')); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } #[TestWith(['T22:44:30.652Z'])] @@ -122,7 +123,8 @@ public function testDateISO8601(): void #[TestWith(['T22:44:30+14'])] public function testTimeISO8601(string $time): void { - $this->assertInstanceOf(Regex::class, $this->matcher->timeISO8601($time)); + $this->assertInstanceOf(Regex::class, $result = $this->matcher->timeISO8601($time)); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } #[TestWith(['2015-08-06T16:53:10+01:00'])] @@ -135,7 +137,8 @@ public function testTimeISO8601(string $time): void #[TestWith(['2015-08-06T16:53:10+14'])] public function testDateTimeISO8601(string $dateTime): void { - $this->assertInstanceOf(Regex::class, $this->matcher->dateTimeISO8601($dateTime)); + $this->assertInstanceOf(Regex::class, $result = $this->matcher->dateTimeISO8601($dateTime)); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } #[TestWith(['2015-08-06T16:53:10.123+01:00'])] @@ -148,42 +151,50 @@ public function testDateTimeISO8601(string $dateTime): void #[TestWith(['2015-08-06T16:53:10.123+14'])] public function testDateTimeWithMillisISO8601(string $dateTime): void { - $this->assertInstanceOf(Regex::class, $this->matcher->dateTimeWithMillisISO8601($dateTime)); + $this->assertInstanceOf(Regex::class, $result = $this->matcher->dateTimeWithMillisISO8601($dateTime)); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testTimestampRFC3339(): void { - $this->assertInstanceOf(Regex::class, $this->matcher->timestampRFC3339('Mon, 31 Oct 2016 15:21:41 -0400')); + $this->assertInstanceOf(Regex::class, $result = $this->matcher->timestampRFC3339('Mon, 31 Oct 2016 15:21:41 -0400')); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testInteger(): void { - $this->assertInstanceOf(Type::class, $this->matcher->integer()); + $this->assertInstanceOf(Type::class, $result = $this->matcher->integer()); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testBoolean(): void { - $this->assertInstanceOf(Type::class, $this->matcher->boolean()); + $this->assertInstanceOf(Type::class, $result = $this->matcher->boolean()); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testDecimal(): void { - $this->assertInstanceOf(Type::class, $this->matcher->decimal()); + $this->assertInstanceOf(Type::class, $result = $this->matcher->decimal()); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testIntegerV3(): void { - $this->assertInstanceOf(Integer::class, $this->matcher->integerV3(13)); + $this->assertInstanceOf(Integer::class, $result = $this->matcher->integerV3(13)); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testBooleanV3(): void { - $this->assertInstanceOf(Boolean::class, $this->matcher->booleanV3(true)); + $this->assertInstanceOf(Boolean::class, $result = $this->matcher->booleanV3(true)); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testDecimalV3(): void { - $this->assertInstanceOf(Decimal::class, $this->matcher->decimalV3(13.01)); + $this->assertInstanceOf(Decimal::class, $result = $this->matcher->decimalV3(13.01)); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } #[TestWith([null, true])] @@ -197,6 +208,7 @@ public function testHexadecimal(?string $value, bool $hasGenerator): void } else { $this->assertNull($hexadecimal->getGenerator()); } + $this->assertInstanceOf(JsonFormatter::class, $hexadecimal->getFormatter()); } #[TestWith([null, true])] @@ -210,46 +222,55 @@ public function testUuid(?string $value, bool $hasGenerator): void } else { $this->assertNull($uuid->getGenerator()); } + $this->assertInstanceOf(JsonFormatter::class, $uuid->getFormatter()); } public function testIpv4Address(): void { - $this->assertInstanceOf(Regex::class, $this->matcher->ipv4Address()); + $this->assertInstanceOf(Regex::class, $result = $this->matcher->ipv4Address()); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testIpv6Address(): void { - $this->assertInstanceOf(Regex::class, $this->matcher->ipv6Address()); + $this->assertInstanceOf(Regex::class, $result = $this->matcher->ipv6Address()); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testEmail(): void { - $this->assertInstanceOf(Regex::class, $this->matcher->email()); + $this->assertInstanceOf(Regex::class, $result = $this->matcher->email()); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testNullValue(): void { - $this->assertInstanceOf(NullValue::class, $this->matcher->nullValue()); + $this->assertInstanceOf(NullValue::class, $result = $this->matcher->nullValue()); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testDate(): void { - $this->assertInstanceOf(Date::class, $this->matcher->date('yyyy-MM-dd', '2022-11-21')); + $this->assertInstanceOf(Date::class, $result = $this->matcher->date('yyyy-MM-dd', '2022-11-21')); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testTime(): void { - $this->assertInstanceOf(Time::class, $this->matcher->time('HH:mm:ss', '21:45::31')); + $this->assertInstanceOf(Time::class, $result = $this->matcher->time('HH:mm:ss', '21:45::31')); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testDateTime(): void { - $this->assertInstanceOf(DateTime::class, $this->matcher->datetime("yyyy-MM-dd'T'HH:mm:ss", '2015-08-06T16:53:10')); + $this->assertInstanceOf(DateTime::class, $result = $this->matcher->datetime("yyyy-MM-dd'T'HH:mm:ss", '2015-08-06T16:53:10')); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testString(): void { - $this->assertInstanceOf(StringValue::class, $this->matcher->string('test string')); + $this->assertInstanceOf(StringValue::class, $result = $this->matcher->string('test string')); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testFromProviderState(): void @@ -257,59 +278,68 @@ public function testFromProviderState(): void $uuid = $this->matcher->uuid(); $this->assertInstanceOf(Regex::class, $uuid); $this->assertInstanceOf(Uuid::class, $uuid->getGenerator()); - $this->assertSame($uuid, $this->matcher->fromProviderState($uuid, '${id}')); - $this->assertInstanceOf(ProviderState::class, $uuid->getGenerator()); + $this->assertNotSame($uuid, $result = $this->matcher->fromProviderState($uuid, '${id}')); + $this->assertInstanceOf(ProviderState::class, $result->getGenerator()); } public function testEqual(): void { - $this->assertInstanceOf(Equality::class, $this->matcher->equal('test string')); + $this->assertInstanceOf(Equality::class, $result = $this->matcher->equal('test string')); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testIncludes(): void { - $this->assertInstanceOf(Includes::class, $this->matcher->includes('test string')); + $this->assertInstanceOf(Includes::class, $result = $this->matcher->includes('test string')); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testNumber(): void { - $this->assertInstanceOf(Number::class, $this->matcher->number(13.01)); + $this->assertInstanceOf(Number::class, $result = $this->matcher->number(13.01)); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testArrayContaining(): void { - $this->assertInstanceOf(ArrayContains::class, $this->matcher->arrayContaining([ + $this->assertInstanceOf(ArrayContains::class, $result = $this->matcher->arrayContaining([ 'item 1', 'item 2' ])); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testNotEmpty(): void { - $this->assertInstanceOf(NotEmpty::class, $this->matcher->notEmpty('not empty string')); + $this->assertInstanceOf(NotEmpty::class, $result = $this->matcher->notEmpty('not empty string')); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testSemver(): void { - $this->assertInstanceOf(Semver::class, $this->matcher->semver('1.2.3')); + $this->assertInstanceOf(Semver::class, $result = $this->matcher->semver('1.2.3')); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testValidStatusCode(): void { - $this->assertInstanceOf(StatusCode::class, $this->matcher->statusCode(HttpStatus::SUCCESS)); + $this->assertInstanceOf(StatusCode::class, $result = $this->matcher->statusCode(HttpStatus::SUCCESS)); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testValues(): void { - $this->assertInstanceOf(Values::class, $this->matcher->values([ + $this->assertInstanceOf(Values::class, $result = $this->matcher->values([ 'item 1', 'item 2' ])); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testContentType(): void { - $this->assertInstanceOf(ContentType::class, $this->matcher->contentType('image/jpeg')); + $this->assertInstanceOf(ContentType::class, $result = $this->matcher->contentType('image/jpeg')); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testEachKey(): void @@ -321,7 +351,8 @@ public function testEachKey(): void $rules = [ $this->matcher->regex('page 3', '^page \d+$'), ]; - $this->assertInstanceOf(EachKey::class, $this->matcher->eachKey($values, $rules)); + $this->assertInstanceOf(EachKey::class, $result = $this->matcher->eachKey($values, $rules)); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testEachValue(): void @@ -334,56 +365,52 @@ public function testEachValue(): void $rules = [ $this->matcher->regex('car', 'car|bike|motorbike'), ]; - $this->assertInstanceOf(EachValue::class, $this->matcher->eachValue($values, $rules)); + $this->assertInstanceOf(EachValue::class, $result = $this->matcher->eachValue($values, $rules)); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } #[TestWith([true, true])] #[TestWith([false, false])] public function testUrl(bool $useMockServerBasePath, bool $hasGenerator): void { - $url = $this->matcher->url('http://localhost:1234/path', '.*(/path)$', $useMockServerBasePath); + $url = $this->matcher->url('http://localhost:1234/path', '.*(\/path)$', $useMockServerBasePath); $this->assertInstanceOf(Regex::class, $url); if ($hasGenerator) { $this->assertInstanceOf(MockServerURL::class, $url->getGenerator()); } else { $this->assertNull($url->getGenerator()); } - } - - public function testMatchingFieldWithNotSupportedFormat(): void - { - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage("MatchingField matcher doesn't support json formatter"); - $this->matcher->matchingField('address'); + $this->assertInstanceOf(JsonFormatter::class, $url->getFormatter()); } public function testMatchingField(): void { - $matcher = new Matcher(plugin: true); - $this->assertInstanceOf(MatchingField::class, $matcher->matchingField('address')); + $this->assertInstanceOf(MatchingField::class, $result = $this->matcher->matchingField('address')); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testMatchAll(): void { - $this->assertInstanceOf(MatchAll::class, $this->matcher->matchAll(['key' => 'value'], [])); + $this->assertInstanceOf(MatchAll::class, $result = $this->matcher->matchAll(['key' => 'value'], [$this->matcher->like('text')])); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testAtLeast(): void { - $this->assertInstanceOf(MinType::class, $this->matcher->atLeast(123)); + $this->assertInstanceOf(Min::class, $result = $this->matcher->atLeast(123)); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } public function testAtMost(): void { - $this->assertInstanceOf(MaxType::class, $this->matcher->atMost(123)); + $this->assertInstanceOf(Max::class, $result = $this->matcher->atMost(123)); + $this->assertInstanceOf(JsonFormatter::class, $result->getFormatter()); } - public function testWithFormatter(): void + public function testExpressionFormat(): void { - $uuid = $this->matcher->uuid(); - $this->assertInstanceOf(HasGeneratorFormatter::class, $uuid->getFormatter()); $matcher = new Matcher(plugin: true); - $uuid = $matcher->uuid(); - $this->assertInstanceOf(RegexFormatter::class, $uuid->getFormatter()); + $this->assertInstanceOf(MatchingField::class, $result = $matcher->matchingField('address')); + $this->assertInstanceOf(ExpressionFormatter::class, $result->getFormatter()); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/AbstractDateTimeTestCase.php b/tests/PhpPact/Consumer/Matcher/Matchers/AbstractDateTimeTestCase.php deleted file mode 100644 index 46ec7f0b..00000000 --- a/tests/PhpPact/Consumer/Matcher/Matchers/AbstractDateTimeTestCase.php +++ /dev/null @@ -1,21 +0,0 @@ -getMatcherWithoutExampleValue(); - $this->assertInstanceOf(HasGeneratorFormatter::class, $matcher->createJsonFormatter()); - } - - public function testCreateExpressionFormatter(): void - { - $matcher = $this->getMatcherWithoutExampleValue(); - $this->assertInstanceOf(DateTimeFormatter::class, $matcher->createExpressionFormatter()); - } -} diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/ArrayContainsTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/ArrayContainsTest.php index 23e3ae79..4556cb0d 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/ArrayContainsTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/ArrayContainsTest.php @@ -2,8 +2,7 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Exception\MatcherNotSupportedException; -use PhpPact\Consumer\Matcher\Formatters\Json\NoGeneratorFormatter; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; use PhpPact\Consumer\Matcher\Matchers\ArrayContains; use PhpPact\Consumer\Matcher\Matchers\Integer; use PhpPact\Consumer\Matcher\Matchers\Type; @@ -11,29 +10,55 @@ class ArrayContainsTest extends TestCase { - public function testSerialize(): void + public function testMissingVariants(): void + { + $this->expectException(InvalidValueException::class); + $this->expectExceptionMessage('Variants should not be empty'); + new ArrayContains([]); + } + + public function testFormatJson(): void { $variants = [ new Type('string'), new Integer(), ]; - $array = new ArrayContains($variants); - $this->assertSame( - '{"pact:matcher:type":"arrayContains","variants":[{"pact:matcher:type":"type","value":"string"},{"pact:matcher:type":"integer","pact:generator:type":"RandomInt","min":0,"max":10}],"value":[{"pact:matcher:type":"type","value":"string"},{"pact:matcher:type":"integer","pact:generator:type":"RandomInt","min":0,"max":10}]}', - json_encode($array) + $matcher = new ArrayContains($variants); + $jsonEncoded = json_encode($matcher); + $this->assertIsString($jsonEncoded); + $this->assertJsonStringEqualsJsonString( + <<assertInstanceOf(NoGeneratorFormatter::class, $matcher->createJsonFormatter()); - } - - public function testCreateExpressionFormatter(): void - { - $matcher = new ArrayContains([]); - $this->expectExceptionObject(new MatcherNotSupportedException("ArrayContains matcher doesn't support expression formatter")); - $matcher->createExpressionFormatter(); - } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/BooleanTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/BooleanTest.php index e21f2509..114604bb 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/BooleanTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/BooleanTest.php @@ -2,42 +2,38 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\BooleanFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\HasGeneratorFormatter; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\Boolean; -use PhpPact\Consumer\Matcher\Matchers\GeneratorAwareMatcher; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; use PHPUnit\Framework\Attributes\TestWith; +use PHPUnit\Framework\TestCase; -class BooleanTest extends GeneratorAwareMatcherTestCase +class BooleanTest extends TestCase { - protected function getMatcherWithoutExampleValue(): GeneratorAwareMatcher + #[TestWith([new Boolean(null), '{"pact:generator:type":"RandomBoolean","pact:matcher:type":"boolean","value":null}'])] + #[TestWith([new Boolean(true), '{"pact:matcher:type":"boolean","value":true}'])] + #[TestWith([new Boolean(false), '{"pact:matcher:type":"boolean","value":false}'])] + public function testFormatJson(MatcherInterface $matcher, string $json): void { - return new Boolean(); + $jsonEncoded = json_encode($matcher); + $this->assertIsString($jsonEncoded); + $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); } - protected function getMatcherWithExampleValue(): GeneratorAwareMatcher + public function testInvalidValue(): void { - return new Boolean(false); + $matcher = (new Boolean())->withFormatter(new ExpressionFormatter()); + $this->expectException(InvalidValueException::class); + $this->expectExceptionMessage(sprintf("Boolean matching expression doesn't support value of type %s", gettype(null))); + json_encode($matcher); } - #[TestWith([null, '{"pact:matcher:type":"boolean","pact:generator:type":"RandomBoolean"}'])] - #[TestWith([true, '{"pact:matcher:type":"boolean","value":true}'])] - #[TestWith([false, '{"pact:matcher:type":"boolean","value":false}'])] - public function testSerialize(?bool $value, string $json): void + #[TestWith([new Boolean(true), '"matching(boolean, true)"'])] + #[TestWith([new Boolean(false), '"matching(boolean, false)"'])] + public function testFormatExpression(MatcherInterface $matcher, string $expression): void { - $matcher = new Boolean($value); - $this->assertSame($json, json_encode($matcher)); - } - - public function testCreateJsonFormatter(): void - { - $matcher = $this->getMatcherWithoutExampleValue(); - $this->assertInstanceOf(HasGeneratorFormatter::class, $matcher->createJsonFormatter()); - } - - public function testCreateExpressionFormatter(): void - { - $matcher = $this->getMatcherWithoutExampleValue(); - $this->assertInstanceOf(BooleanFormatter::class, $matcher->createExpressionFormatter()); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/ContentTypeTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/ContentTypeTest.php index 53a90658..f8756ed3 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/ContentTypeTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/ContentTypeTest.php @@ -2,33 +2,32 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\ContentTypeFormatter as ExpressionFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\ContentTypeFormatter as JsonFormatter; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\ContentType; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; class ContentTypeTest extends TestCase { - public function testSerialize(): void + #[TestWith([new ContentType('plain/text'), '{"pact:matcher:type": "contentType", "value": "plain/text"}'])] + #[TestWith([new ContentType('application/json', '{"key":"value"}'), '{"pact:matcher:type": "contentType", "value": "application/json"}'])] + #[TestWith([new ContentType('application/xml', ''), '{"pact:matcher:type": "contentType", "value": "application/xml"}'])] + public function testFormatJson(MatcherInterface $matcher, string $json): void { - $matcher = new ContentType('text/csv'); $jsonEncoded = json_encode($matcher); $this->assertIsString($jsonEncoded); - $this->assertJsonStringEqualsJsonString( - '{"value":"text\/csv","pact:matcher:type":"contentType"}', - $jsonEncoded - ); + $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); } - public function testCreateJsonFormatter(): void + #[TestWith([new ContentType("contains single quote '", 'testing'), "\"matching(contentType, 'contains single quote \\\'', 'testing')\""])] + #[TestWith([new ContentType('plain/text', "contains single quote '"), "\"matching(contentType, 'plain\\/text', 'contains single quote \\\'')\""])] + #[TestWith([new ContentType('plain/text'), "\"matching(contentType, 'plain\\/text', '')\""])] + #[TestWith([new ContentType('application/json', '{"key":"value"}'), "\"matching(contentType, 'application\\/json', '{\\\"key\\\":\\\"value\\\"}')\""])] + #[TestWith([new ContentType('application/xml', ''), "\"matching(contentType, 'application\\/xml', '')\""])] + public function testFormatExpression(MatcherInterface $matcher, string $expression): void { - $matcher = new ContentType('text/plain'); - $this->assertInstanceOf(JsonFormatter::class, $matcher->createJsonFormatter()); - } - - public function testCreateExpressionFormatter(): void - { - $matcher = new ContentType('text/plain'); - $this->assertInstanceOf(ExpressionFormatter::class, $matcher->createExpressionFormatter()); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/DateTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/DateTest.php index 49cb0911..a7c74d74 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/DateTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/DateTest.php @@ -2,28 +2,38 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\Date; -use PhpPact\Consumer\Matcher\Matchers\GeneratorAwareMatcher; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; use PHPUnit\Framework\Attributes\TestWith; +use PHPUnit\Framework\TestCase; -class DateTest extends AbstractDateTimeTestCase +class DateTest extends TestCase { - protected function getMatcherWithoutExampleValue(): GeneratorAwareMatcher + #[TestWith([new Date('yyyy-MM-dd', null), '{"pact:generator:type":"Date","format":"yyyy-MM-dd","pact:matcher:type":"date","value":null}'])] + #[TestWith([new Date('yyyy-MM-dd', '1995-02-04'), '{"pact:matcher:type":"date","format":"yyyy-MM-dd","value":"1995-02-04"}'])] + public function testFormatJson(MatcherInterface $matcher, string $json): void { - return new Date(); + $jsonEncoded = json_encode($matcher); + $this->assertIsString($jsonEncoded); + $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); } - protected function getMatcherWithExampleValue(): GeneratorAwareMatcher + public function testInvalidValue(): void { - return new Date('yyyy-MM-dd', '2001-09-17'); + $matcher = (new Date('yyyy-MM-dd'))->withFormatter(new ExpressionFormatter()); + $this->expectException(InvalidValueException::class); + $this->expectExceptionMessage(sprintf("DateTime matching expression doesn't support value of type %s", gettype(null))); + json_encode($matcher); } - #[TestWith([null, '{"pact:matcher:type":"date","pact:generator:type":"Date","format":"yyyy-MM-dd"}'])] - #[TestWith(['1995-02-04', '{"pact:matcher:type":"date","format":"yyyy-MM-dd","value":"1995-02-04"}'])] - public function testSerialize(?string $value, string $json): void + #[TestWith([new Date("contains single quote '", '2012-04-12'), "\"matching(date, 'contains single quote \\\'', '2012-04-12')\""])] + #[TestWith([new Date('yyyy-MM-dd', "contains single quote '"), "\"matching(date, 'yyyy-MM-dd', 'contains single quote \\\'')\""])] + #[TestWith([new Date('yyyy-MM-dd', '2012-04-12'), "\"matching(date, 'yyyy-MM-dd', '2012-04-12')\""])] + public function testFormatExpression(MatcherInterface $matcher, string $expression): void { - $format = 'yyyy-MM-dd'; - $matcher = new Date($format, $value); - $this->assertSame($json, json_encode($matcher)); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/DateTimeTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/DateTimeTest.php index 08ca50fb..cf7dfbfb 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/DateTimeTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/DateTimeTest.php @@ -2,28 +2,39 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\DateTime; -use PhpPact\Consumer\Matcher\Matchers\GeneratorAwareMatcher; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; use PHPUnit\Framework\Attributes\TestWith; +use PHPUnit\Framework\TestCase; -class DateTimeTest extends AbstractDateTimeTestCase +class DateTimeTest extends TestCase { - protected function getMatcherWithoutExampleValue(): GeneratorAwareMatcher + #[TestWith([new DateTime("yyyy-MM-dd'T'HH:mm:ss"), '{"pact:matcher:type":"datetime","pact:generator:type":"DateTime","format":"yyyy-MM-dd\'T\'HH:mm:ss","value": null}'])] + #[TestWith([new DateTime("yyyy-MM-dd'T'HH:mm:ss", '1995-02-04T22:45:00'), '{"pact:matcher:type":"datetime","format":"yyyy-MM-dd\'T\'HH:mm:ss","value":"1995-02-04T22:45:00"}'])] + public function testFormatJson(MatcherInterface $matcher, string $json): void { - return new DateTime(); + $jsonEncoded = json_encode($matcher); + $this->assertIsString($jsonEncoded); + $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); } - protected function getMatcherWithExampleValue(): GeneratorAwareMatcher + + public function testInvalidValue(): void { - return new DateTime("yyyy-MM-dd HH:mm", '2011-07-13 16:41'); + $matcher = (new DateTime("yyyy-MM-dd'T'HH:mm:ss"))->withFormatter(new ExpressionFormatter()); + $this->expectException(InvalidValueException::class); + $this->expectExceptionMessage(sprintf("DateTime matching expression doesn't support value of type %s", gettype(null))); + json_encode($matcher); } - #[TestWith([null, '{"pact:matcher:type":"datetime","pact:generator:type":"DateTime","format":"yyyy-MM-dd\'T\'HH:mm:ss"}'])] - #[TestWith(['1995-02-04T22:45:00', '{"pact:matcher:type":"datetime","format":"yyyy-MM-dd\'T\'HH:mm:ss","value":"1995-02-04T22:45:00"}'])] - public function testSerialize(?string $value, string $json): void + #[TestWith([new DateTime("contains single quote '", '2020-05-21 16:44:32+10:00'), "\"matching(datetime, 'contains single quote \\\'', '2020-05-21 16:44:32+10:00')\""])] + #[TestWith([new DateTime('yyyy-MM-dd HH:mm:ssZZZZZ', "contains single quote '"), "\"matching(datetime, 'yyyy-MM-dd HH:mm:ssZZZZZ', 'contains single quote \\\'')\""])] + #[TestWith([new DateTime('yyyy-MM-dd HH:mm:ssZZZZZ', '2020-05-21 16:44:32+10:00'), "\"matching(datetime, 'yyyy-MM-dd HH:mm:ssZZZZZ', '2020-05-21 16:44:32+10:00')\""])] + public function testFormatExpression(MatcherInterface $matcher, string $expression): void { - $format = "yyyy-MM-dd'T'HH:mm:ss"; - $matcher = new DateTime($format, $value); - $this->assertSame($json, json_encode($matcher)); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/DecimalTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/DecimalTest.php index c4c8fd85..4a7265f2 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/DecimalTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/DecimalTest.php @@ -2,41 +2,39 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\DecimalFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\HasGeneratorFormatter; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\Decimal; -use PhpPact\Consumer\Matcher\Matchers\GeneratorAwareMatcher; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; use PHPUnit\Framework\Attributes\TestWith; +use PHPUnit\Framework\TestCase; -class DecimalTest extends GeneratorAwareMatcherTestCase +class DecimalTest extends TestCase { - protected function getMatcherWithoutExampleValue(): GeneratorAwareMatcher + #[TestWith([new Decimal(null), '{"pact:matcher:type":"decimal","pact:generator:type":"RandomDecimal","digits":10,"value":null}'])] + #[TestWith([new Decimal(1.23), '{"pact:matcher:type":"decimal","value":1.23}'])] + public function testFormatJson(MatcherInterface $matcher, string $json): void { - return new Decimal(); + $jsonEncoded = json_encode($matcher); + $this->assertIsString($jsonEncoded); + $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); } - protected function getMatcherWithExampleValue(): GeneratorAwareMatcher + public function testInvalidValue(): void { - return new Decimal(15.68); + $matcher = (new Decimal())->withFormatter(new ExpressionFormatter()); + $this->expectException(InvalidValueException::class); + $this->expectExceptionMessage(sprintf("Decimal matching expression doesn't support value of type %s", gettype(null))); + json_encode($matcher); } - #[TestWith([null, '{"pact:matcher:type":"decimal","pact:generator:type":"RandomDecimal","digits":10}'])] - #[TestWith([1.23, '{"pact:matcher:type":"decimal","value":1.23}'])] - public function testSerialize(?float $value, string $json): void + #[TestWith([new Decimal(-99), '"matching(decimal, -99)"'])] // Provider verification will fail on this case + #[TestWith([new Decimal(100), '"matching(decimal, 100)"'])] // Provider verification will fail on this case + #[TestWith([new Decimal(100.01), '"matching(decimal, 100.01)"'])] + #[TestWith([new Decimal(-100.003), '"matching(decimal, -100.003)"'])] + public function testFormatExpression(MatcherInterface $matcher, string $expression): void { - $matcher = new Decimal($value); - $this->assertSame($json, json_encode($matcher)); - } - - public function testCreateJsonFormatter(): void - { - $matcher = $this->getMatcherWithoutExampleValue(); - $this->assertInstanceOf(HasGeneratorFormatter::class, $matcher->createJsonFormatter()); - } - - public function testCreateExpressionFormatter(): void - { - $matcher = $this->getMatcherWithoutExampleValue(); - $this->assertInstanceOf(DecimalFormatter::class, $matcher->createExpressionFormatter()); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/EachKeyTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/EachKeyTest.php index 88859d6c..b01341d5 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/EachKeyTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/EachKeyTest.php @@ -2,16 +2,25 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\EachKeyFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\NoGeneratorFormatter; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; +use PhpPact\Consumer\Matcher\Exception\MatcherNotSupportedException; +use PhpPact\Consumer\Matcher\Exception\MatchingExpressionException; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\EachKey; +use PhpPact\Consumer\Matcher\Matchers\Integer; use PhpPact\Consumer\Matcher\Matchers\Regex; +use PhpPact\Consumer\Matcher\Matchers\StatusCode; +use PhpPact\Consumer\Matcher\Matchers\StringValue; use PhpPact\Consumer\Matcher\Matchers\Type; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; +use stdClass; class EachKeyTest extends TestCase { - public function testSerialize(): void + public function testFormatJson(): void { $value = [ 'abc' => 123, @@ -28,20 +37,63 @@ public function testSerialize(): void $jsonEncoded = json_encode($matcher); $this->assertIsString($jsonEncoded); $this->assertJsonStringEqualsJsonString( - '{"pact:matcher:type":"eachKey","value":{"abc":123,"def":111,"ghi":{"test":"value"}},"rules":[{"pact:matcher:type":"type","value":"string"},{"pact:matcher:type":"regex","pact:generator:type":"Regex","regex":"\\\\w{3}"}]}', + <<assertInstanceOf(NoGeneratorFormatter::class, $matcher->createJsonFormatter()); + $this->expectException(InvalidValueException::class); + $this->expectExceptionMessage('Rules should not be empty'); + new EachKey(['key' => 'value'], []); } - public function testCreateExpressionFormatter(): void + public function testTooManyRules(): void { - $matcher = new EachKey([], []); - $this->assertInstanceOf(EachKeyFormatter::class, $matcher->createExpressionFormatter()); + $matcher = (new EachKey(['key' => 'value'], [new Type(1), new Type(2)]))->withFormatter(new ExpressionFormatter()); + $this->expectException(MatchingExpressionException::class); + $this->expectExceptionMessage(sprintf("Matcher 'eachKey' only support 1 rule in expression, %d provided", 2)); + json_encode($matcher); + } + + public function testInvalidRules(): void + { + $matcher = (new EachKey(['key' => 'value'], [new StatusCode('info')]))->withFormatter(new ExpressionFormatter()); + $this->expectException(MatcherNotSupportedException::class); + $this->expectExceptionMessage(sprintf("Rule '%s' must implement '%s' to be formatted as expression", StatusCode::class, ExpressionFormattableInterface::class)); + json_encode($matcher); + } + + #[TestWith([new EachKey(['key' => 'value'], [new StringValue("contains single quote '")]), "\"eachKey(matching(type, 'contains single quote \\\''))\""])] + #[TestWith([new EachKey(['key' => 'value'], [new Integer(123)]), '"eachKey(matching(integer, 123))"'])] + #[TestWith([new EachKey(new stdClass(), [new Regex('\w+', 'example value')]), "\"eachKey(matching(regex, '\\\w+', 'example value'))\""])] + public function testFormatExpression(MatcherInterface $matcher, string $expression): void + { + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/EachValueTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/EachValueTest.php index f220d5b9..98a16879 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/EachValueTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/EachValueTest.php @@ -2,16 +2,25 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\EachValueFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\NoGeneratorFormatter; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; +use PhpPact\Consumer\Matcher\Exception\MatcherNotSupportedException; +use PhpPact\Consumer\Matcher\Exception\MatchingExpressionException; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\EachValue; +use PhpPact\Consumer\Matcher\Matchers\Includes; use PhpPact\Consumer\Matcher\Matchers\Regex; +use PhpPact\Consumer\Matcher\Matchers\StatusCode; +use PhpPact\Consumer\Matcher\Matchers\StringValue; use PhpPact\Consumer\Matcher\Matchers\Type; +use PhpPact\Consumer\Matcher\Model\Matcher\ExpressionFormattableInterface; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; +use stdClass; class EachValueTest extends TestCase { - public function testSerialize(): void + public function testFormatJson(): void { $value = [ 'ab1', @@ -26,20 +35,61 @@ public function testSerialize(): void $jsonEncoded = json_encode($matcher); $this->assertIsString($jsonEncoded); $this->assertJsonStringEqualsJsonString( - '{"pact:matcher:type":"eachValue","value":["ab1","cd2","ef9"],"rules":[{"pact:matcher:type":"type","value":"string"},{"pact:matcher:type":"regex","pact:generator:type":"Regex","regex":"\\\\w{2}\\\\d"}]}', + <<assertInstanceOf(NoGeneratorFormatter::class, $matcher->createJsonFormatter()); + $this->expectException(InvalidValueException::class); + $this->expectExceptionMessage('Rules should not be empty'); + new EachValue(['value'], []); } - public function testCreateExpressionFormatter(): void + public function testTooManyRules(): void { - $matcher = new EachValue([], []); - $this->assertInstanceOf(EachValueFormatter::class, $matcher->createExpressionFormatter()); + $matcher = (new EachValue(['value'], [new Includes('a'), new Includes('b')]))->withFormatter(new ExpressionFormatter()); + $this->expectException(MatchingExpressionException::class); + $this->expectExceptionMessage(sprintf("Matcher 'eachValue' only support 1 rule in expression, %d provided", 2)); + json_encode($matcher); + } + + public function testInvalidRules(): void + { + $matcher = (new EachValue(['value'], [new StatusCode('info')]))->withFormatter(new ExpressionFormatter()); + $this->expectException(MatcherNotSupportedException::class); + $this->expectExceptionMessage(sprintf("Rule '%s' must implement '%s' to be formatted as expression", StatusCode::class, ExpressionFormattableInterface::class)); + json_encode($matcher); + } + + #[TestWith([new EachValue(['value'], [new StringValue("contains single quote '")]), "\"eachValue(matching(type, 'contains single quote \\\''))\""])] + #[TestWith([new EachValue(['value'], [new StringValue('example value')]), "\"eachValue(matching(type, 'example value'))\""])] + #[TestWith([new EachValue(new stdClass(), [new Regex('\w \d', 'a 1')]), "\"eachValue(matching(regex, '\\\w \\\d', 'a 1'))\""])] + public function testFormatExpression(MatcherInterface $matcher, string $expression): void + { + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/EqualityTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/EqualityTest.php index d1c373a2..35a857e2 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/EqualityTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/EqualityTest.php @@ -2,14 +2,16 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\EqualityFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\NoGeneratorFormatter; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\Equality; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; class EqualityTest extends TestCase { - public function testSerialize(): void + public function testFormatJson(): void { $string = new Equality('exact this string'); $this->assertSame( @@ -18,15 +20,28 @@ public function testSerialize(): void ); } - public function testCreateJsonFormatter(): void + #[TestWith([new Equality(new \stdClass()), 'object'])] + #[TestWith([new Equality(['key' => 'value']), 'array'])] + public function testInvalidValue(MatcherInterface $matcher, string $type): void { - $matcher = new Equality(null); - $this->assertInstanceOf(NoGeneratorFormatter::class, $matcher->createJsonFormatter()); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->expectException(InvalidValueException::class); + $this->expectExceptionMessage(sprintf("Expression doesn't support value of type %s", $type)); + json_encode($matcher); } - public function testCreateExpressionFormatter(): void + #[TestWith([new Equality("contains single quote '"), "\"matching(equalTo, 'contains single quote \\\'')\""])] + #[TestWith([new Equality('example value'), "\"matching(equalTo, 'example value')\""])] + #[TestWith([new Equality(100.09), '"matching(equalTo, 100.09)"'])] + #[TestWith([new Equality(-99.99), '"matching(equalTo, -99.99)"'])] + #[TestWith([new Equality(100), '"matching(equalTo, 100)"'])] + #[TestWith([new Equality(-99), '"matching(equalTo, -99)"'])] + #[TestWith([new Equality(true), '"matching(equalTo, true)"'])] + #[TestWith([new Equality(false), '"matching(equalTo, false)"'])] + #[TestWith([new Equality(null), '"matching(equalTo, null)"'])] + public function testFormatExpression(MatcherInterface $matcher, string $expression): void { - $matcher = new Equality(null); - $this->assertInstanceOf(EqualityFormatter::class, $matcher->createExpressionFormatter()); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/GeneratorAwareMatcherTestCase.php b/tests/PhpPact/Consumer/Matcher/Matchers/GeneratorAwareMatcherTestCase.php deleted file mode 100644 index 1ddd669d..00000000 --- a/tests/PhpPact/Consumer/Matcher/Matchers/GeneratorAwareMatcherTestCase.php +++ /dev/null @@ -1,59 +0,0 @@ -getMatcherWithoutExampleValue(); - $this->expectException(GeneratorRequiredException::class); - $this->expectExceptionMessage(sprintf("Generator is required for matcher '%s' when example value is not set", $matcher->getType())); - $matcher->setGenerator(null); - json_encode($matcher); - } - - #[TestWith([new Date()])] - #[TestWith([new DateTime()])] - #[TestWith([new MockServerURL('.*(/\d+)$', 'http://example.com/123')])] - #[TestWith([new ProviderState('${key}')])] - #[TestWith([new RandomBoolean()])] - #[TestWith([new RandomDecimal()])] - #[TestWith([new RandomHexadecimal()])] - #[TestWith([new RandomInt()])] - #[TestWith([new RandomString()])] - #[TestWith([new Regex('\w')])] - #[TestWith([new Time()])] - #[TestWith([new Uuid()])] - public function testGeneratorNotRequired(GeneratorInterface $generator): void - { - $matcher = $this->getMatcherWithExampleValue(); - $this->expectException(GeneratorNotRequiredException::class); - $this->expectExceptionMessage(sprintf("Generator '%s' is not required for matcher '%s' when example value is set", $generator->getType(), $matcher->getType())); - $matcher->setGenerator($generator); - json_encode($matcher); - } - - abstract protected function getMatcherWithExampleValue(): GeneratorAwareMatcher; - - abstract protected function getMatcherWithoutExampleValue(): GeneratorAwareMatcher; -} diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/IncludesTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/IncludesTest.php index 903a54e8..23cc86f4 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/IncludesTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/IncludesTest.php @@ -2,14 +2,15 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\IncludesFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\NoGeneratorFormatter; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\Includes; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; class IncludesTest extends TestCase { - public function testSerialize(): void + public function testFormatJson(): void { $string = new Includes('contains this string'); $this->assertSame( @@ -18,15 +19,11 @@ public function testSerialize(): void ); } - public function testCreateJsonFormatter(): void + #[TestWith([new Includes("contains single quote '"), "\"matching(include, 'contains single quote \\\'')\""])] + #[TestWith([new Includes('example value'), "\"matching(include, 'example value')\""])] + public function testFormatExpression(MatcherInterface $matcher, string $expression): void { - $matcher = new Includes('text'); - $this->assertInstanceOf(NoGeneratorFormatter::class, $matcher->createJsonFormatter()); - } - - public function testCreateExpressionFormatter(): void - { - $matcher = new Includes('text'); - $this->assertInstanceOf(IncludesFormatter::class, $matcher->createExpressionFormatter()); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/IntegerTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/IntegerTest.php index 8fdba186..fbe40e8b 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/IntegerTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/IntegerTest.php @@ -2,41 +2,38 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\IntegerFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\HasGeneratorFormatter; -use PhpPact\Consumer\Matcher\Matchers\GeneratorAwareMatcher; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\Integer; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; use PHPUnit\Framework\Attributes\TestWith; +use PHPUnit\Framework\TestCase; -class IntegerTest extends GeneratorAwareMatcherTestCase +class IntegerTest extends TestCase { - protected function getMatcherWithoutExampleValue(): GeneratorAwareMatcher + #[TestWith([new Integer(null), '{"pact:matcher:type":"integer","pact:generator:type":"RandomInt","min":0,"max":10,"value": null}'])] + #[TestWith([new Integer(123), '{"pact:matcher:type":"integer","value":123}'])] + public function testFormatJson(MatcherInterface $matcher, string $json): void { - return new Integer(); + $jsonEncoded = json_encode($matcher); + $this->assertIsString($jsonEncoded); + $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); } - protected function getMatcherWithExampleValue(): GeneratorAwareMatcher + public function testInvalidValue(): void { - return new Integer(189); + $matcher = new Integer(); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->expectException(InvalidValueException::class); + $this->expectExceptionMessage(sprintf("Integer matching expression doesn't support value of type %s", gettype(null))); + json_encode($matcher); } - #[TestWith([null, '{"pact:matcher:type":"integer","pact:generator:type":"RandomInt","min":0,"max":10}'])] - #[TestWith([123, '{"pact:matcher:type":"integer","value":123}'])] - public function testSerialize(?int $value, string $json): void + #[TestWith([new Integer(-99), '"matching(integer, -99)"'])] + #[TestWith([new Integer(100), '"matching(integer, 100)"'])] + public function testFormatExpression(MatcherInterface $matcher, string $expression): void { - $matcher = new Integer($value); - $this->assertSame($json, json_encode($matcher)); - } - - public function testCreateJsonFormatter(): void - { - $matcher = new Integer(123); - $this->assertInstanceOf(HasGeneratorFormatter::class, $matcher->createJsonFormatter()); - } - - public function testCreateExpressionFormatter(): void - { - $matcher = new Integer(123); - $this->assertInstanceOf(IntegerFormatter::class, $matcher->createExpressionFormatter()); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/MatchAllTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/MatchAllTest.php index 9fdba8ff..bc61f720 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/MatchAllTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/MatchAllTest.php @@ -2,41 +2,166 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; use PhpPact\Consumer\Matcher\Exception\MatcherNotSupportedException; -use PhpPact\Consumer\Matcher\Formatters\Expression\MatchAllFormatter as ExpressionFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\MatchAllFormatter as JsonFormatter; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\EachKey; use PhpPact\Consumer\Matcher\Matchers\EachValue; use PhpPact\Consumer\Matcher\Matchers\MatchAll; -use PhpPact\Consumer\Matcher\Matchers\MaxType; -use PhpPact\Consumer\Matcher\Matchers\MinType; +use PhpPact\Consumer\Matcher\Matchers\Max; +use PhpPact\Consumer\Matcher\Matchers\Min; +use PhpPact\Consumer\Matcher\Matchers\Regex; +use PhpPact\Consumer\Matcher\Matchers\StringValue; use PhpPact\Consumer\Matcher\Matchers\Type; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; class MatchAllTest extends TestCase { - public function testNestedCombinedMatchers(): void + public function testNestedMatchAllMatchers(): void { $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage('Nested combined matchers are not supported'); - new MatchAll([], [new MatchAll([], [])]); + $this->expectExceptionMessage("Nested 'matcherAll' matcher is not supported"); + new MatchAll([], [new MatchAll([], [new Type(123)])]); } - public function testSerialize(): void + #[TestWith([new MatchAll(['abc' => 1, 'def' => 234], [new Min(2)]), << 1, 'def' => 234], [new Min(1), new Max(2), new EachKey(["doesn't matter"], [new Regex('\w+', 'abc')]), new EachValue(["doesn't matter"], [new Type(100)])]), << 123], [new Min(1), new Max(2), new EachKey([], [new Type('test')]), new EachValue([], [new Type(123)])]), << 123], [new MinType(null, 1), new MaxType(null, 2), new EachKey([], [new Type('test')]), new EachValue([], [new Type(123)])]); - $this->assertSame('{"pact:matcher:type":[{"pact:matcher:type":"type","min":1,"value":[null]},{"pact:matcher:type":"type","max":2,"value":[null]},{"pact:matcher:type":"eachKey","rules":[{"pact:matcher:type":"type","value":"test"}],"value":[]},{"pact:matcher:type":"eachValue","rules":[{"pact:matcher:type":"type","value":123}],"value":[]}],"value":{"key":123}}', json_encode($matcher)); + $jsonEncoded = json_encode($matcher); + $this->assertIsString($jsonEncoded); + $this->assertJsonStringEqualsJsonString( + $json, + $jsonEncoded + ); } - public function testCreateJsonFormatter(): void + public function testMissingMatchers(): void { - $matcher = new MatchAll([], []); - $this->assertInstanceOf(JsonFormatter::class, $matcher->createJsonFormatter()); + $this->expectException(InvalidValueException::class); + $this->expectExceptionMessage('Matchers should not be empty'); + new MatchAll(['value'], []); } - public function testCreateExpressionFormatter(): void + #[TestWith([new MatchAll(['abc' => 'xyz'], [new EachKey(["doesn't matter"], [new StringValue("contains single quote '")]), new EachValue(["doesn't matter"], [new StringValue("contains single quote '")])]), "\"eachKey(matching(type, 'contains single quote \\\'')), eachValue(matching(type, 'contains single quote \\\''))\""])] + #[TestWith([new MatchAll(['abc' => 1, 'def' => 234], [new Min(2)]), '"atLeast(2)"'])] + #[TestWith([new MatchAll(['abc' => 1, 'def' => 234], [new Min(1), new Max(2), new EachKey(["doesn't matter"], [new Regex('\w+', 'abc')]), new EachValue(["doesn't matter"], [new Type(100)])]), "\"atLeast(1), atMost(2), eachKey(matching(regex, '\\\w+', 'abc')), eachValue(matching(type, 100))\""])] + public function testFormatExpression(MatcherInterface $matcher, string $expression): void { - $matcher = new MatchAll([], []); - $this->assertInstanceOf(ExpressionFormatter::class, $matcher->createExpressionFormatter()); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/MatchingFieldTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/MatchingFieldTest.php index 8ea9755e..d0f832b2 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/MatchingFieldTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/MatchingFieldTest.php @@ -2,45 +2,19 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Exception\MatcherNotSupportedException; -use PhpPact\Consumer\Matcher\Formatters\Expression\MatchingFieldFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\NoGeneratorFormatter; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\MatchingField; -use PhpPact\Consumer\Matcher\Model\FormatterInterface; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; class MatchingFieldTest extends TestCase { - public function testSerialize(): void + #[TestWith([new MatchingField("contains single quote '"), "\"matching($'contains single quote \\\'')\""])] + #[TestWith([new MatchingField('product'), "\"matching($'product')\""])] + public function testFormatExpression(MatcherInterface $matcher, string $expression): void { - $matcher = new MatchingField('person'); - $this->assertSame( - "\"matching($'person')\"", - json_encode($matcher) - ); - } - - #[TestWith([new NoGeneratorFormatter()])] - public function testNotSupportedFormatter(FormatterInterface $formatter): void - { - $this->expectException(MatcherNotSupportedException::class); - $this->expectExceptionMessage("MatchingField matcher doesn't support json formatter"); - $matcher = new MatchingField('person'); - $matcher->setFormatter($formatter); - json_encode($matcher); - } - - public function testCreateJsonFormatter(): void - { - $matcher = new MatchingField('product'); - $this->expectExceptionObject(new MatcherNotSupportedException("MatchingField matcher doesn't support json formatter")); - $matcher->createJsonFormatter(); - } - - public function testCreateExpressionFormatter(): void - { - $matcher = new MatchingField('product'); - $this->assertInstanceOf(MatchingFieldFormatter::class, $matcher->createExpressionFormatter()); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/MaxTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/MaxTest.php new file mode 100644 index 00000000..b65c1b85 --- /dev/null +++ b/tests/PhpPact/Consumer/Matcher/Matchers/MaxTest.php @@ -0,0 +1,30 @@ +assertIsString($jsonEncoded); + $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); + } + + #[TestWith([2, '"atMost(2)"'])] + #[TestWith([-22, '"atMost(0)"'])] + public function testFormatExpression(int $max, string $expression): void + { + $matcher = new Max($max); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); + } +} diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/MaxTypeTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/MaxTypeTest.php index 7196e4f5..290928a3 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/MaxTypeTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/MaxTypeTest.php @@ -2,31 +2,30 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\MaxTypeFormatter as ExpressionFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\MaxTypeFormatter as JsonFormatter; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\MaxType; use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; class MaxTypeTest extends TestCase { - #[TestWith([-3, '{"pact:matcher:type":"type","max":0,"value":["string value"]}'])] - #[TestWith([3, '{"pact:matcher:type":"type","max":3,"value":["string value"]}'])] - public function testSerialize(int $max, string $json): void + #[TestWith(['example text', 2, '{"pact:matcher:type": "type", "value": ["example text"], "max": 2}'])] + #[TestWith(['example text', -2, '{"pact:matcher:type": "type", "value": ["example text"], "max": 0}'])] + public function testFormatJson(mixed $value, int $max, string $json): void { - $matcher = new MaxType('string value', $max); - $this->assertSame($json, json_encode($matcher)); + $matcher = new MaxType($value, $max); + $jsonEncoded = json_encode($matcher); + $this->assertIsString($jsonEncoded); + $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); } - public function testCreateJsonFormatter(): void + #[TestWith(["contains single quote '", 2, "\"atMost(2), eachValue(matching(type, 'contains single quote \\\'')\""])] + #[TestWith([null, 2, '"atMost(2), eachValue(matching(type, null)"'])] + #[TestWith(['example value', 2, "\"atMost(2), eachValue(matching(type, 'example value')\""])] + public function testFormatExpression(mixed $value, int $max, string $expression): void { - $matcher = new MaxType(null, 0); - $this->assertInstanceOf(JsonFormatter::class, $matcher->createJsonFormatter()); - } - - public function testCreateExpressionFormatter(): void - { - $matcher = new MaxType(null, 0); - $this->assertInstanceOf(ExpressionFormatter::class, $matcher->createExpressionFormatter()); + $matcher = new MaxType($value, $max); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/MinMaxTypeTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/MinMaxTypeTest.php index 287532a0..e5ba5317 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/MinMaxTypeTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/MinMaxTypeTest.php @@ -2,33 +2,32 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\MinMaxTypeFormatter as ExpressionFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\MinMaxTypeFormatter as JsonFormatter; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\MinMaxType; use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; class MinMaxTypeTest extends TestCase { - #[TestWith([-2, 5, '{"pact:matcher:type":"type","min":0,"max":5,"value":[1.23]}'])] - #[TestWith([-2, -5, '{"pact:matcher:type":"type","min":0,"max":0,"value":[1.23]}'])] - #[TestWith([2, 5, '{"pact:matcher:type":"type","min":2,"max":5,"value":[1.23,1.23]}'])] - #[TestWith([2, -5, '{"pact:matcher:type":"type","min":2,"max":0,"value":[1.23,1.23]}'])] - public function testSerialize(int $min, int $max, string $json): void + #[TestWith(['example text', 2, 3, '{"pact:matcher:type": "type", "value": ["example text", "example text"], "min": 2, "max": 3}'])] + #[TestWith(['example text', -2, 3, '{"pact:matcher:type": "type", "value": ["example text"], "min": 0, "max": 3}'])] + #[TestWith(['example text', 2, -3, '{"pact:matcher:type": "type", "value": ["example text", "example text"], "min": 2, "max": 0}'])] + #[TestWith(['example text', -2, -3, '{"pact:matcher:type": "type", "value": ["example text"], "min": 0, "max": 0}'])] + public function testFormatJson(mixed $value, int $min, int $max, string $json): void { - $matcher = new MinMaxType(1.23, $min, $max); - $this->assertSame($json, json_encode($matcher)); + $matcher = new MinMaxType($value, $min, $max); + $jsonEncoded = json_encode($matcher); + $this->assertIsString($jsonEncoded); + $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); } - public function testCreateJsonFormatter(): void + #[TestWith(["contains single quote '", 2, 3, "\"atLeast(2), atMost(3), eachValue(matching(type, 'contains single quote \\\'')\""])] + #[TestWith([null, 2, 3, '"atLeast(2), atMost(3), eachValue(matching(type, null)"'])] + #[TestWith(['example value', 2, 3, "\"atLeast(2), atMost(3), eachValue(matching(type, 'example value')\""])] + public function testFormatExpression(mixed $value, int $min, int $max, string $expression): void { - $matcher = new MinMaxType(null, 0, 1); - $this->assertInstanceOf(JsonFormatter::class, $matcher->createJsonFormatter()); - } - - public function testCreateExpressionFormatter(): void - { - $matcher = new MinMaxType(null, 0, 1); - $this->assertInstanceOf(ExpressionFormatter::class, $matcher->createExpressionFormatter()); + $matcher = new MinMaxType($value, $min, $max); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/MinTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/MinTest.php new file mode 100644 index 00000000..c1bfea70 --- /dev/null +++ b/tests/PhpPact/Consumer/Matcher/Matchers/MinTest.php @@ -0,0 +1,30 @@ +assertIsString($jsonEncoded); + $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); + } + + #[TestWith([1, '"atLeast(1)"'])] + #[TestWith([-1, '"atLeast(0)"'])] + public function testFormatExpression(int $min, string $expression): void + { + $matcher = new Min($min); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); + } +} diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/MinTypeTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/MinTypeTest.php index df1b66f1..12e0ae3b 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/MinTypeTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/MinTypeTest.php @@ -2,31 +2,30 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\MinTypeFormatter as ExpressionFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\MinTypeFormatter as JsonFormatter; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\MinType; use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; class MinTypeTest extends TestCase { - #[TestWith([-3, '{"pact:matcher:type":"type","min":0,"value":[123]}'])] - #[TestWith([3, '{"pact:matcher:type":"type","min":3,"value":[123,123,123]}'])] - public function testSerialize(int $min, string $json): void + #[TestWith(['example text', 2, '{"pact:matcher:type": "type", "value": ["example text", "example text"], "min": 2}'])] + #[TestWith(['example text', -2, '{"pact:matcher:type": "type", "value": ["example text"], "min": 0}'])] + public function testFormatJson(mixed $value, int $min, string $json): void { - $matcher = new MinType(123, $min); - $this->assertSame($json, json_encode($matcher)); + $matcher = new MinType($value, $min); + $jsonEncoded = json_encode($matcher); + $this->assertIsString($jsonEncoded); + $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); } - public function testCreateJsonFormatter(): void + #[TestWith(["contains single quote '", 1, "\"atLeast(1), eachValue(matching(type, 'contains single quote \\\'')\""])] + #[TestWith([null, 1, '"atLeast(1), eachValue(matching(type, null)"'])] + #[TestWith(['example value', 1, "\"atLeast(1), eachValue(matching(type, 'example value')\""])] + public function testFormatExpression(mixed $value, int $min, string $expression): void { - $matcher = new MinType(null, 0); - $this->assertInstanceOf(JsonFormatter::class, $matcher->createJsonFormatter()); - } - - public function testCreateExpressionFormatter(): void - { - $matcher = new MinType(null, 0); - $this->assertInstanceOf(ExpressionFormatter::class, $matcher->createExpressionFormatter()); + $matcher = new MinType($value, $min); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/NotEmptyTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/NotEmptyTest.php index 18f0b332..cfb65db0 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/NotEmptyTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/NotEmptyTest.php @@ -2,14 +2,16 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\NotEmptyFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\NoGeneratorFormatter; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\NotEmpty; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; class NotEmptyTest extends TestCase { - public function testSerialize(): void + public function testFormatJson(): void { $array = new NotEmpty(['some text']); $this->assertSame( @@ -18,15 +20,25 @@ public function testSerialize(): void ); } - public function testCreateJsonFormatter(): void + #[TestWith([new NotEmpty(new \stdClass()), 'object'])] + #[TestWith([new NotEmpty(['key' => 'value']), 'array'])] + public function testInvalidValue(MatcherInterface $matcher, string $type): void { - $matcher = new NotEmpty('test'); - $this->assertInstanceOf(NoGeneratorFormatter::class, $matcher->createJsonFormatter()); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->expectException(InvalidValueException::class); + $this->expectExceptionMessage(sprintf("Expression doesn't support value of type %s", $type)); + json_encode($matcher); } - public function testCreateExpressionFormatter(): void + #[TestWith([new NotEmpty("contains single quote '"), "\"notEmpty('contains single quote \\\'')\""])] + #[TestWith([new NotEmpty('example value'), "\"notEmpty('example value')\""])] + #[TestWith([new NotEmpty(100.09), '"notEmpty(100.09)"'])] + #[TestWith([new NotEmpty(100), '"notEmpty(100)"'])] + #[TestWith([new NotEmpty(true), '"notEmpty(true)"'])] + #[TestWith([new NotEmpty(false), '"notEmpty(false)"'])] + public function testFormatExpression(MatcherInterface $matcher, string $expression): void { - $matcher = new NotEmpty('test'); - $this->assertInstanceOf(NotEmptyFormatter::class, $matcher->createExpressionFormatter()); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/NullValueTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/NullValueTest.php index 7b73fdda..8c15f73f 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/NullValueTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/NullValueTest.php @@ -2,31 +2,26 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\NullValueFormatter as ExpressionFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\NullValueFormatter as JsonFormatter; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\NullValue; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; class NullValueTest extends TestCase { - public function testSerialize(): void + #[TestWith([new NullValue(), '{"pact:matcher:type": "null"}'])] + public function testFormatJson(MatcherInterface $matcher, string $json): void { - $null = new NullValue(); - $this->assertSame( - '{"pact:matcher:type":"null"}', - json_encode($null) - ); + $jsonEncoded = json_encode($matcher); + $this->assertIsString($jsonEncoded); + $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); } - public function testCreateJsonFormatter(): void + #[TestWith([new NullValue(), '"matching(type, null)"'])] + public function testFormatExpression(MatcherInterface $matcher, string $expression): void { - $matcher = new NullValue(); - $this->assertInstanceOf(JsonFormatter::class, $matcher->createJsonFormatter()); - } - - public function testCreateExpressionFormatter(): void - { - $matcher = new NullValue(); - $this->assertInstanceOf(ExpressionFormatter::class, $matcher->createExpressionFormatter()); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/NumberTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/NumberTest.php index 3fa237c0..aa80a66f 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/NumberTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/NumberTest.php @@ -2,42 +2,41 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\NumberFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\HasGeneratorFormatter; -use PhpPact\Consumer\Matcher\Matchers\GeneratorAwareMatcher; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\Number; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; use PHPUnit\Framework\Attributes\TestWith; +use PHPUnit\Framework\TestCase; -class NumberTest extends GeneratorAwareMatcherTestCase +class NumberTest extends TestCase { - protected function getMatcherWithoutExampleValue(): GeneratorAwareMatcher + #[TestWith([new Number(null), '{"pact:matcher:type":"number","pact:generator:type":"RandomInt","min":0,"max":10,"value":null}'])] + #[TestWith([new Number(123), '{"pact:matcher:type":"number","value":123}'])] + #[TestWith([new Number(12.3), '{"pact:matcher:type":"number","value":12.3}'])] + public function testFormatJson(MatcherInterface $matcher, string $json): void { - return new Number(); + $jsonEncoded = json_encode($matcher); + $this->assertIsString($jsonEncoded); + $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); } - protected function getMatcherWithExampleValue(): GeneratorAwareMatcher + public function testInvalidValue(): void { - return new Number(56.73); + $matcher = new Number(); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->expectException(InvalidValueException::class); + $this->expectExceptionMessage(sprintf("Number matching expression doesn't support value of type %s", gettype(null))); + json_encode($matcher); } - #[TestWith([null, '{"pact:matcher:type":"number","pact:generator:type":"RandomInt","min":0,"max":10}'])] - #[TestWith([123, '{"pact:matcher:type":"number","value":123}'])] - #[TestWith([12.3, '{"pact:matcher:type":"number","value":12.3}'])] - public function testSerialize(int|float|null $value, string $json): void + #[TestWith([new Number(-99), '"matching(number, -99)"'])] + #[TestWith([new Number(100), '"matching(number, 100)"'])] + #[TestWith([new Number(100.01), '"matching(number, 100.01)"'])] + #[TestWith([new Number(-100.003), '"matching(number, -100.003)"'])] + public function testFormatExpression(MatcherInterface $matcher, string $expression): void { - $matcher = new Number($value); - $this->assertSame($json, json_encode($matcher)); - } - - public function testCreateJsonFormatter(): void - { - $matcher = new Number(123); - $this->assertInstanceOf(HasGeneratorFormatter::class, $matcher->createJsonFormatter()); - } - - public function testCreateExpressionFormatter(): void - { - $matcher = new Number(123); - $this->assertInstanceOf(NumberFormatter::class, $matcher->createExpressionFormatter()); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/RegexTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/RegexTest.php index 6aa8171c..9306fd84 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/RegexTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/RegexTest.php @@ -3,54 +3,56 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; use PhpPact\Consumer\Matcher\Exception\InvalidRegexException; -use PhpPact\Consumer\Matcher\Formatters\Expression\RegexFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\HasGeneratorFormatter; -use PhpPact\Consumer\Matcher\Matchers\GeneratorAwareMatcher; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\Regex; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; use PHPUnit\Framework\Attributes\TestWith; +use PHPUnit\Framework\TestCase; -class RegexTest extends GeneratorAwareMatcherTestCase +class RegexTest extends TestCase { private string $regex = '\d+'; - protected function getMatcherWithoutExampleValue(): GeneratorAwareMatcher + #[TestWith(['number', true])] + #[TestWith(['integer', false])] + public function testInvalidRegex(string $value, bool $isArray): void { - return new Regex($this->regex); - } - - protected function getMatcherWithExampleValue(): GeneratorAwareMatcher - { - return new Regex($this->regex, ['1', '23']); + $values = $isArray ? [$value] : $value; + $this->expectException(InvalidRegexException::class); + $value = is_array($values) ? $values[0] : $values; + $this->expectExceptionMessage("The value '{$value}' doesn't match pattern '{$this->regex}'. Failed with error code 0."); + new Regex($this->regex, $values); } /** * @param string|string[]|null $values */ - #[TestWith([null, '{"pact:matcher:type":"regex","pact:generator:type":"Regex","regex":"\\\\d+"}'])] - #[TestWith(['number', null])] - #[TestWith([['integer'], null])] + #[TestWith([null, '{"pact:matcher:type":"regex","pact:generator:type":"Regex","regex":"\\\\d+","value":null}'])] #[TestWith(['12+', '{"pact:matcher:type":"regex","regex":"\\\\d+","value":"12+"}'])] #[TestWith([['12.3', '456'], '{"pact:matcher:type":"regex","regex":"\\\\d+","value":["12.3","456"]}'])] - public function testSerialize(string|array|null $values, ?string $json): void + public function testFormatJson(string|array|null $values, string $json): void { - if (!$json && $values) { - $this->expectException(InvalidRegexException::class); - $value = is_array($values) ? $values[0] : $values; - $this->expectExceptionMessage("The pattern '{$this->regex}' is not valid for value '{$value}'. Failed with error code 0."); - } $matcher = new Regex($this->regex, $values); - $this->assertSame($json, json_encode($matcher)); + $jsonEncoded = json_encode($matcher); + $this->assertIsString($jsonEncoded); + $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); } - public function testCreateJsonFormatter(): void + #[TestWith([new Regex('\w \d', ['key' => 'a 1']), 'array'])] + public function testInvalidValue(MatcherInterface $matcher, string $type): void { - $matcher = new Regex($this->regex); - $this->assertInstanceOf(HasGeneratorFormatter::class, $matcher->createJsonFormatter()); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->expectException(InvalidValueException::class); + $this->expectExceptionMessage(sprintf("Regex matching expression doesn't support value of type %s", $type)); + json_encode($matcher); } - public function testCreateExpressionFormatter(): void + #[TestWith([new Regex("['\w]+", "contains single quote '"), "\"matching(regex, '[\\\\'\\\\w]+', 'contains single quote \\\\'')\""])] + #[TestWith([new Regex('\w{3}\d+', 'abc123'), "\"matching(regex, '\\\w{3}\\\d+', 'abc123')\""])] + public function testFormatExpression(MatcherInterface $matcher, string $expression): void { - $matcher = new Regex($this->regex); - $this->assertInstanceOf(RegexFormatter::class, $matcher->createExpressionFormatter()); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/SemverTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/SemverTest.php index fe75828b..78edf764 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/SemverTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/SemverTest.php @@ -2,41 +2,38 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\SemverFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\HasGeneratorFormatter; -use PhpPact\Consumer\Matcher\Matchers\GeneratorAwareMatcher; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\Semver; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; use PHPUnit\Framework\Attributes\TestWith; +use PHPUnit\Framework\TestCase; -class SemverTest extends GeneratorAwareMatcherTestCase +class SemverTest extends TestCase { - protected function getMatcherWithoutExampleValue(): GeneratorAwareMatcher + #[TestWith([new Semver(null), '{"pact:matcher:type":"semver","pact:generator:type":"Regex","regex":"\\\\d+\\\\.\\\\d+\\\\.\\\\d+","value": null}'])] + #[TestWith([new Semver('1.2.3'), '{"pact:matcher:type":"semver","value":"1.2.3"}'])] + public function testFormatJson(MatcherInterface $matcher, string $json): void { - return new Semver(); + $jsonEncoded = json_encode($matcher); + $this->assertIsString($jsonEncoded); + $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); } - protected function getMatcherWithExampleValue(): GeneratorAwareMatcher - { - return new Semver('10.21.0-rc.1'); - } - - #[TestWith([null, '{"pact:matcher:type":"semver","pact:generator:type":"Regex","regex":"\\\\d+\\\\.\\\\d+\\\\.\\\\d+"}'])] - #[TestWith(['1.2.3', '{"pact:matcher:type":"semver","value":"1.2.3"}'])] - public function testSerialize(?string $value, string $json): void - { - $matcher = new Semver($value); - $this->assertSame($json, json_encode($matcher)); - } - - public function testCreateJsonFormatter(): void + public function testInvalidValue(): void { $matcher = new Semver(); - $this->assertInstanceOf(HasGeneratorFormatter::class, $matcher->createJsonFormatter()); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->expectException(InvalidValueException::class); + $this->expectExceptionMessage("Semver matching expression doesn't support value of type NULL"); + json_encode($matcher); } - public function testCreateExpressionFormatter(): void + #[TestWith([new Semver("contains single quote '"), "\"matching(semver, 'contains single quote \\\'')\""])] + #[TestWith([new Semver('1.0.0'), "\"matching(semver, '1.0.0')\""])] + public function testFormatExpression(MatcherInterface $matcher, string $expression): void { - $matcher = new Semver(); - $this->assertInstanceOf(SemverFormatter::class, $matcher->createExpressionFormatter()); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/StatusCodeTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/StatusCodeTest.php index 32470866..886c5e95 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/StatusCodeTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/StatusCodeTest.php @@ -3,34 +3,22 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; use PhpPact\Consumer\Matcher\Exception\InvalidHttpStatusException; -use PhpPact\Consumer\Matcher\Exception\MatcherNotSupportedException; -use PhpPact\Consumer\Matcher\Formatters\Json\HasGeneratorFormatter; -use PhpPact\Consumer\Matcher\Matchers\GeneratorAwareMatcher; use PhpPact\Consumer\Matcher\Matchers\StatusCode; use PHPUnit\Framework\Attributes\TestWith; +use PHPUnit\Framework\TestCase; -class StatusCodeTest extends GeneratorAwareMatcherTestCase +class StatusCodeTest extends TestCase { - protected function getMatcherWithoutExampleValue(): GeneratorAwareMatcher - { - return new StatusCode('info'); - } - - protected function getMatcherWithExampleValue(): GeneratorAwareMatcher - { - return new StatusCode('error', 404); - } - #[TestWith(['invalid', null, null])] - #[TestWith(['info', null, '{"pact:matcher:type":"statusCode","pact:generator:type":"RandomInt","status":"info","min":100,"max":199}'])] - #[TestWith(['success', null, '{"pact:matcher:type":"statusCode","pact:generator:type":"RandomInt","status":"success","min":200,"max":299}'])] - #[TestWith(['redirect', null, '{"pact:matcher:type":"statusCode","pact:generator:type":"RandomInt","status":"redirect","min":300,"max":399}'])] - #[TestWith(['clientError', null, '{"pact:matcher:type":"statusCode","pact:generator:type":"RandomInt","status":"clientError","min":400,"max":499}'])] - #[TestWith(['serverError', null, '{"pact:matcher:type":"statusCode","pact:generator:type":"RandomInt","status":"serverError","min":500,"max":599}'])] - #[TestWith(['nonError', null, '{"pact:matcher:type":"statusCode","pact:generator:type":"RandomInt","status":"nonError","min":100,"max":399}'])] - #[TestWith(['error', null, '{"pact:matcher:type":"statusCode","pact:generator:type":"RandomInt","status":"error","min":400,"max":599}'])] + #[TestWith(['info', null, '{"pact:matcher:type":"statusCode","pact:generator:type":"RandomInt","status":"info","min":100,"max":199,"value": null}'])] + #[TestWith(['success', null, '{"pact:matcher:type":"statusCode","pact:generator:type":"RandomInt","status":"success","min":200,"max":299,"value": null}'])] + #[TestWith(['redirect', null, '{"pact:matcher:type":"statusCode","pact:generator:type":"RandomInt","status":"redirect","min":300,"max":399,"value": null}'])] + #[TestWith(['clientError', null, '{"pact:matcher:type":"statusCode","pact:generator:type":"RandomInt","status":"clientError","min":400,"max":499,"value": null}'])] + #[TestWith(['serverError', null, '{"pact:matcher:type":"statusCode","pact:generator:type":"RandomInt","status":"serverError","min":500,"max":599,"value": null}'])] + #[TestWith(['nonError', null, '{"pact:matcher:type":"statusCode","pact:generator:type":"RandomInt","status":"nonError","min":100,"max":399,"value": null}'])] + #[TestWith(['error', null, '{"pact:matcher:type":"statusCode","pact:generator:type":"RandomInt","status":"error","min":400,"max":599,"value": null}'])] #[TestWith(['info', 123, '{"pact:matcher:type":"statusCode","status":"info","value":123}'])] - public function testSerialize(string $status, ?int $value, ?string $json): void + public function testFormatJson(string $status, ?int $value, ?string $json): void { if (!$json) { $this->expectException(InvalidHttpStatusException::class); @@ -43,17 +31,4 @@ public function testSerialize(string $status, ?int $value, ?string $json): void $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); } } - - public function testCreateJsonFormatter(): void - { - $matcher = new StatusCode('success'); - $this->assertInstanceOf(HasGeneratorFormatter::class, $matcher->createJsonFormatter()); - } - - public function testCreateExpressionFormatter(): void - { - $matcher = new StatusCode('success'); - $this->expectExceptionObject(new MatcherNotSupportedException("StatusCode matcher doesn't support expression formatter")); - $matcher->createExpressionFormatter(); - } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/StringValueTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/StringValueTest.php index ac1698a2..e146a1bd 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/StringValueTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/StringValueTest.php @@ -2,33 +2,35 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\StringValueFormatter as ExpressionFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\StringValueFormatter as JsonFormatter; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; +use PhpPact\Consumer\Matcher\Generators\Regex; use PhpPact\Consumer\Matcher\Matchers\StringValue; use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; class StringValueTest extends TestCase { - #[TestWith([null, '{"pact:matcher:type":"type","value":"some string","pact:generator:type":"RandomString","size":10}'])] - #[TestWith(['test', '{"pact:matcher:type":"type","value":"test"}'])] - public function testSerialize(?string $value, string $json): void + #[TestWith([null, false, '{"pact:matcher:type": "type", "pact:generator:type": "RandomString", "size": 10, "value": "some string"}'])] + #[TestWith([null, true, '{"pact:matcher:type": "type", "pact:generator:type": "Regex", "regex": "\\\\w{3}", "value": "some string"}'])] + #[TestWith(['example text', false, '{"pact:matcher:type": "type", "value": "example text"}'])] + public function testFormatJson(?string $value, bool $hasGenerator, string $json): void { $matcher = new StringValue($value); + if ($hasGenerator) { + $matcher->setGenerator(new Regex('\w{3}')); + } $jsonEncoded = json_encode($matcher); $this->assertIsString($jsonEncoded); $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); } - public function testCreateJsonFormatter(): void + #[TestWith(["contains single quote '", "\"matching(type, 'contains single quote \\\'')\""])] + #[TestWith(['value', "\"matching(type, 'value')\""])] + #[TestWith([null, "\"matching(type, 'some string')\""])] + public function testFormatExpression(?string $value, string $expression): void { - $matcher = new StringValue('abc'); - $this->assertInstanceOf(JsonFormatter::class, $matcher->createJsonFormatter()); - } - - public function testCreateExpressionFormatter(): void - { - $matcher = new StringValue('abc'); - $this->assertInstanceOf(ExpressionFormatter::class, $matcher->createExpressionFormatter()); + $matcher = new StringValue($value); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/TimeTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/TimeTest.php index 94329db4..1d4e4fdd 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/TimeTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/TimeTest.php @@ -2,28 +2,38 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Matchers\GeneratorAwareMatcher; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\Time; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; use PHPUnit\Framework\Attributes\TestWith; +use PHPUnit\Framework\TestCase; -class TimeTest extends AbstractDateTimeTestCase +class TimeTest extends TestCase { - protected function getMatcherWithoutExampleValue(): GeneratorAwareMatcher + #[TestWith([new Time('HH:mm:ss', null), '{"pact:matcher:type":"time","pact:generator:type":"Time","format":"HH:mm:ss","value":null}'])] + #[TestWith([new Time('HH:mm:ss', '12:02::34'), '{"pact:matcher:type":"time","format":"HH:mm:ss","value":"12:02::34"}'])] + public function testFormatJson(MatcherInterface $matcher, string $json): void { - return new Time(); + $jsonEncoded = json_encode($matcher); + $this->assertIsString($jsonEncoded); + $this->assertJsonStringEqualsJsonString($json, $jsonEncoded); } - protected function getMatcherWithExampleValue(): GeneratorAwareMatcher + public function testInvalidValue(): void { - return new Time('HH:mm', '21:15'); + $matcher = (new Time('HH:mm'))->withFormatter(new ExpressionFormatter()); + $this->expectException(InvalidValueException::class); + $this->expectExceptionMessage(sprintf("DateTime matching expression doesn't support value of type %s", gettype(null))); + json_encode($matcher); } - #[TestWith([null, '{"pact:matcher:type":"time","pact:generator:type":"Time","format":"HH:mm:ss"}'])] - #[TestWith(['12:02::34', '{"pact:matcher:type":"time","format":"HH:mm:ss","value":"12:02::34"}'])] - public function testSerialize(?string $value, string $json): void + #[TestWith([new Time("contains single quote '", '22:04'), "\"matching(time, 'contains single quote \\\'', '22:04')\""])] + #[TestWith([new Time('HH:mm', "contains single quote '"), "\"matching(time, 'HH:mm', 'contains single quote \\\'')\""])] + #[TestWith([new Time('HH:mm', '22:04'), "\"matching(time, 'HH:mm', '22:04')\""])] + public function testFormatExpression(MatcherInterface $matcher, string $json): void { - $format = 'HH:mm:ss'; - $matcher = new Time($format, $value); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); $this->assertSame($json, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/TypeTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/TypeTest.php index 6363311e..f821b7d6 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/TypeTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/TypeTest.php @@ -2,14 +2,16 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Formatters\Expression\TypeFormatter; -use PhpPact\Consumer\Matcher\Formatters\Json\NoGeneratorFormatter; +use PhpPact\Consumer\Matcher\Exception\InvalidValueException; +use PhpPact\Consumer\Matcher\Formatters\Expression\ExpressionFormatter; use PhpPact\Consumer\Matcher\Matchers\Type; +use PhpPact\Consumer\Matcher\Model\MatcherInterface; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; class TypeTest extends TestCase { - public function testSerialize(): void + public function testFormatJson(): void { $value = (object) ['key' => 'value']; $object = new Type($value); @@ -19,15 +21,28 @@ public function testSerialize(): void ); } - public function testCreateJsonFormatter(): void + #[TestWith([new Type(new \stdClass()), 'object'])] + #[TestWith([new Type(['key' => 'value']), 'array'])] + public function testInvalidValue(MatcherInterface $matcher, string $type): void { - $matcher = new Type('abc'); - $this->assertInstanceOf(NoGeneratorFormatter::class, $matcher->createJsonFormatter()); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->expectException(InvalidValueException::class); + $this->expectExceptionMessage(sprintf("Expression doesn't support value of type %s", $type)); + json_encode($matcher); } - public function testCreateExpressionFormatter(): void + #[TestWith([new Type("contains single quote '"), "\"matching(type, 'contains single quote \\\'')\""])] + #[TestWith([new Type('example value'), "\"matching(type, 'example value')\""])] + #[TestWith([new Type(100.09), '"matching(type, 100.09)"'])] + #[TestWith([new Type(-99.99), '"matching(type, -99.99)"'])] + #[TestWith([new Type(100), '"matching(type, 100)"'])] + #[TestWith([new Type(-99), '"matching(type, -99)"'])] + #[TestWith([new Type(true), '"matching(type, true)"'])] + #[TestWith([new Type(false), '"matching(type, false)"'])] + #[TestWith([new Type(null), '"matching(type, null)"'])] + public function testFormatExpression(MatcherInterface $matcher, string $expression): void { - $matcher = new Type('abc'); - $this->assertInstanceOf(TypeFormatter::class, $matcher->createExpressionFormatter()); + $matcher = $matcher->withFormatter(new ExpressionFormatter()); + $this->assertSame($expression, json_encode($matcher)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Matchers/ValuesTest.php b/tests/PhpPact/Consumer/Matcher/Matchers/ValuesTest.php index cb1de138..39a2d60d 100644 --- a/tests/PhpPact/Consumer/Matcher/Matchers/ValuesTest.php +++ b/tests/PhpPact/Consumer/Matcher/Matchers/ValuesTest.php @@ -2,8 +2,6 @@ namespace PhpPactTest\Consumer\Matcher\Matchers; -use PhpPact\Consumer\Matcher\Exception\MatcherNotSupportedException; -use PhpPact\Consumer\Matcher\Formatters\Json\NoGeneratorFormatter; use PhpPact\Consumer\Matcher\Matchers\Values; use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; @@ -15,22 +13,9 @@ class ValuesTest extends TestCase */ #[TestWith([['value 1', 'value 2'], '{"pact:matcher:type":"values","value":["value 1","value 2"]}'])] #[TestWith([['key 1' => 'value 1', 'key 2' => 'value 2'], '{"pact:matcher:type":"values","value":{"key 1":"value 1","key 2":"value 2"}}'])] - public function testSerialize(array $values, string $json): void + public function testFormatJson(array $values, string $json): void { $array = new Values($values); $this->assertSame($json, json_encode($array)); } - - public function testCreateJsonFormatter(): void - { - $matcher = new Values([]); - $this->assertInstanceOf(NoGeneratorFormatter::class, $matcher->createJsonFormatter()); - } - - public function testCreateExpressionFormatter(): void - { - $matcher = new Values([]); - $this->expectExceptionObject(new MatcherNotSupportedException("Values matcher doesn't support expression formatter")); - $matcher->createExpressionFormatter(); - } } diff --git a/tests/PhpPact/Consumer/Matcher/Model/AttributesTest.php b/tests/PhpPact/Consumer/Matcher/Model/AttributesTest.php index 1b8ce95c..c9aac920 100644 --- a/tests/PhpPact/Consumer/Matcher/Model/AttributesTest.php +++ b/tests/PhpPact/Consumer/Matcher/Model/AttributesTest.php @@ -3,47 +3,38 @@ namespace PhpPactTest\Consumer\Matcher\Model; use PhpPact\Consumer\Matcher\Exception\AttributeConflictException; -use PhpPact\Consumer\Matcher\Generators\RandomBoolean; -//use PhpPact\Consumer\Matcher\Matchers\NullValue; use PhpPact\Consumer\Matcher\Model\Attributes; use PHPUnit\Framework\TestCase; class AttributesTest extends TestCase { - public function testParent(): void - { - $subject = new Attributes($generator = new RandomBoolean()); - $this->assertSame($generator, $subject->getParent()); - - //$subject = new Attributes($matcher = new NullValue()); - //$this->assertSame($matcher, $subject->getParent()); - } - public function testData(): void { - $subject = new Attributes(new RandomBoolean(), $data = ['key' => 'value']); - $this->assertSame($data, $subject->getData()); + $subject = new Attributes(['key' => 'value']); $this->assertFalse($subject->has('new key')); $this->assertNull($subject->get('new key')); $this->assertTrue($subject->has('key')); $this->assertSame('value', $subject->get('key')); } + public function testJsonSerialize(): void + { + $subject = new Attributes(['key' => 'value']); + $this->assertSame('{"key":"value"}', json_encode($subject)); + } + public function testMergeConflict(): void { - $attributes = new Attributes(new RandomBoolean(), ['key' => 'value 1']); + $attributes = new Attributes(['key' => 'value 1']); $this->expectException(AttributeConflictException::class); - //$this->expectExceptionMessage("Attribute 'key' of generator 'RandomBoolean' and matcher 'null' are conflict"); - $this->expectExceptionMessage("Attribute 'key' of generator 'RandomBoolean' and generator 'RandomBoolean' are conflict"); - $attributes->merge(new Attributes(new RandomBoolean(), ['key' => 'value 2'])); + $this->expectExceptionMessage("Can not merge attributes: Values of attribute 'key' are conflict"); + $attributes->merge(new Attributes(['key' => 'value 2'])); } public function testMerge(): void { - $parent = new RandomBoolean(); - $attributes = new Attributes($parent, ['key' => 'value', 'key 2' => 123]); - $merged = $attributes->merge(new Attributes(new RandomBoolean(), ['key' => 'value', 'key 3' => ['value 1', 'value 2']])); - $this->assertSame($parent, $merged->getParent()); - $this->assertSame(['key' => 'value', 'key 2' => 123, 'key 3' => ['value 1', 'value 2']], $merged->getData()); + $attributes = new Attributes(['key' => 'value', 'key 2' => 123]); + $merged = $attributes->merge(new Attributes(['key' => 'value', 'key 3' => ['value 1', 'value 2']])); + $this->assertSame(['key' => 'value', 'key 2' => 123, 'key 3' => ['value 1', 'value 2']], iterator_to_array($merged)); } } diff --git a/tests/PhpPact/Consumer/Matcher/Model/ExpressionTest.php b/tests/PhpPact/Consumer/Matcher/Model/ExpressionTest.php new file mode 100644 index 00000000..04d28573 --- /dev/null +++ b/tests/PhpPact/Consumer/Matcher/Model/ExpressionTest.php @@ -0,0 +1,36 @@ + "it's fine", + 'noQuote' => 'right', + 'true' => true, + 'false' => false, + 'integer' => -123, + 'double' => -12.3, + 'empty' => null, + ]); + $this->assertSame("\"values('it\\\'s fine', 'right', true, false, -123, -12.3, null)\"", json_encode($expression)); + $this->assertSame("values('it\'s fine', 'right', true, false, -123, -12.3, null)", (string)$expression); + } + + #[TestWith([new \stdClass(), 'object'])] + #[TestWith([['key' => 'value'], 'array'])] + public function testInvalidValue(mixed $value, string $type): void + { + $expression = new Expression('%key%', ['key' => $value]); + $this->expectException(InvalidValueException::class); + $this->expectExceptionMessage(sprintf("Expression doesn't support value of type %s", $type)); + json_encode($expression); + } +} diff --git a/tests/PhpPact/Xml/XmlBuilderTest.php b/tests/PhpPact/Xml/XmlBuilderTest.php index 0c5de8d6..e5325d43 100644 --- a/tests/PhpPact/Xml/XmlBuilderTest.php +++ b/tests/PhpPact/Xml/XmlBuilderTest.php @@ -153,6 +153,10 @@ public function testJsonSerialize(): void ], ]; - $this->assertSame(json_encode($expectedArray), json_encode($this->builder)); + $expected = json_encode($expectedArray); + $this->assertIsString($expected); + $actual = json_encode($this->builder); + $this->assertIsString($actual); + $this->assertJsonStringEqualsJsonString($expected, $actual); } } diff --git a/tests/PhpPact/Xml/XmlTextTest.php b/tests/PhpPact/Xml/XmlTextTest.php index cfe9a9cf..88e6e431 100644 --- a/tests/PhpPact/Xml/XmlTextTest.php +++ b/tests/PhpPact/Xml/XmlTextTest.php @@ -29,17 +29,18 @@ public function testJsonSerializeMatcher(): void $matcher->setGenerator(new RandomInt(2, 8)); $matcher->setFormatter(new XmlContentFormatter()); $text = new XmlText($matcher); - $this->assertSame( - json_encode([ - 'content' => null, - 'matcher' => [ - 'pact:matcher:type' => 'integer', - 'min' => 2, - 'max' => 8, - ], - 'pact:generator:type' => 'RandomInt' - ]), - json_encode($text) - ); + $expected = json_encode([ + 'content' => null, + 'matcher' => [ + 'pact:matcher:type' => 'integer', + 'min' => 2, + 'max' => 8, + ], + 'pact:generator:type' => 'RandomInt' + ]); + $this->assertIsString($expected); + $actual = json_encode($text); + $this->assertIsString($actual); + $this->assertJsonStringEqualsJsonString($expected, $actual); } }