mirror of
https://github.com/Neur0toxine/pock.git
synced 2024-11-28 15:56:09 +03:00
better xml matcher, refactor comparators
This commit is contained in:
parent
b08ba6f6cd
commit
e352c310d7
@ -47,7 +47,8 @@
|
|||||||
"jms/serializer": "^2 | ^3.12",
|
"jms/serializer": "^2 | ^3.12",
|
||||||
"symfony/phpunit-bridge": "^5.2",
|
"symfony/phpunit-bridge": "^5.2",
|
||||||
"symfony/serializer": "^5.2",
|
"symfony/serializer": "^5.2",
|
||||||
"symfony/property-access": "^5.2"
|
"symfony/property-access": "^5.2",
|
||||||
|
"php-mock/php-mock": "^2.3"
|
||||||
},
|
},
|
||||||
"provide": {
|
"provide": {
|
||||||
"psr/http-client-implementation": "1.0",
|
"psr/http-client-implementation": "1.0",
|
||||||
|
29
src/Comparator/ComparatorInterface.php
Normal file
29
src/Comparator/ComparatorInterface.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PHP 7.3
|
||||||
|
*
|
||||||
|
* @category ComparatorInterface
|
||||||
|
* @package Pock\Comparator
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Pock\Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface ComparatorInterface
|
||||||
|
*
|
||||||
|
* @category ComparatorInterface
|
||||||
|
* @package Pock\Comparator
|
||||||
|
*/
|
||||||
|
interface ComparatorInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Compare two values.
|
||||||
|
*
|
||||||
|
* @param mixed $first
|
||||||
|
* @param mixed $second
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function compare($first, $second): bool;
|
||||||
|
}
|
44
src/Comparator/ComparatorLocator.php
Normal file
44
src/Comparator/ComparatorLocator.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PHP 7.3
|
||||||
|
*
|
||||||
|
* @category ComparatorLocator
|
||||||
|
* @package Pock\Comparator
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Pock\Comparator;
|
||||||
|
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ComparatorLocator
|
||||||
|
*
|
||||||
|
* @category ComparatorLocator
|
||||||
|
* @package Pock\Comparator
|
||||||
|
*/
|
||||||
|
class ComparatorLocator
|
||||||
|
{
|
||||||
|
/** @var \Pock\Comparator\ComparatorInterface[] */
|
||||||
|
private static $comparators = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns comparator.
|
||||||
|
*
|
||||||
|
* @param string $fqn
|
||||||
|
*
|
||||||
|
* @return \Pock\Comparator\ComparatorInterface
|
||||||
|
*/
|
||||||
|
public static function get(string $fqn): ComparatorInterface
|
||||||
|
{
|
||||||
|
if (!class_exists($fqn)) {
|
||||||
|
throw new RuntimeException('Comparator ' . $fqn . ' does not exist.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!array_key_exists($fqn, static::$comparators)) {
|
||||||
|
static::$comparators[$fqn] = new $fqn();
|
||||||
|
}
|
||||||
|
|
||||||
|
return static::$comparators[$fqn];
|
||||||
|
}
|
||||||
|
}
|
53
src/Comparator/LtrScalarArrayComparator.php
Normal file
53
src/Comparator/LtrScalarArrayComparator.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PHP 7.3
|
||||||
|
*
|
||||||
|
* @category LtrScalarArrayComparator
|
||||||
|
* @package Pock\Comparator
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Pock\Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class LtrScalarArrayComparator
|
||||||
|
*
|
||||||
|
* @category LtrScalarArrayComparator
|
||||||
|
* @package Pock\Comparator
|
||||||
|
*/
|
||||||
|
class LtrScalarArrayComparator implements ComparatorInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function compare($first, $second): bool
|
||||||
|
{
|
||||||
|
if (!is_array($first) || !is_array($second)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static::isNeedlePresentInHaystack($first, $second);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if all needle values is present in haystack.
|
||||||
|
* Doesn't work for multidimensional arrays.
|
||||||
|
*
|
||||||
|
* @phpstan-ignore-next-line
|
||||||
|
* @param array $needle
|
||||||
|
* @phpstan-ignore-next-line
|
||||||
|
* @param array $haystack
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected static function isNeedlePresentInHaystack(array $needle, array $haystack): bool
|
||||||
|
{
|
||||||
|
foreach ($needle as $value) {
|
||||||
|
if (!in_array($value, $haystack, true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
64
src/Comparator/RecursiveArrayComparator.php
Normal file
64
src/Comparator/RecursiveArrayComparator.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PHP 7.3
|
||||||
|
*
|
||||||
|
* @category RecursiveArrayComparator
|
||||||
|
* @package Pock\Comparator
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Pock\Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class RecursiveArrayComparator
|
||||||
|
*
|
||||||
|
* @category RecursiveArrayComparator
|
||||||
|
* @package Pock\Comparator
|
||||||
|
*/
|
||||||
|
class RecursiveArrayComparator implements ComparatorInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function compare($first, $second): bool
|
||||||
|
{
|
||||||
|
if (!is_array($first) || !is_array($second)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static::recursiveCompareArrays($first, $second);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if both arrays are equal recursively.
|
||||||
|
*
|
||||||
|
* @phpstan-ignore-next-line
|
||||||
|
* @param array $first
|
||||||
|
* @phpstan-ignore-next-line
|
||||||
|
* @param array $second
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected static function recursiveCompareArrays(array $first, array $second): bool
|
||||||
|
{
|
||||||
|
if (count($first) !== count($second)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty(array_diff(array_keys($first), array_keys($second)))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($first as $key => $value) {
|
||||||
|
if (is_array($value) && !self::recursiveCompareArrays($value, $second[$key])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value !== $second[$key]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
61
src/Comparator/RecursiveLtrArrayComparator.php
Normal file
61
src/Comparator/RecursiveLtrArrayComparator.php
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PHP 7.3
|
||||||
|
*
|
||||||
|
* @category RecursiveLtrArrayComparator
|
||||||
|
* @package Pock\Comparator
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Pock\Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class RecursiveLtrArrayComparator
|
||||||
|
*
|
||||||
|
* @category RecursiveLtrArrayComparator
|
||||||
|
* @package Pock\Comparator
|
||||||
|
*/
|
||||||
|
class RecursiveLtrArrayComparator extends RecursiveArrayComparator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function compare($first, $second): bool
|
||||||
|
{
|
||||||
|
if (!is_array($first) || !is_array($second)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static::recursiveNeedlePresentInHaystack($first, $second);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if all needle values is present in haystack.
|
||||||
|
* Works for multidimensional arrays. Internal arrays will be treated as values (e.g. will be compared recursively).
|
||||||
|
*
|
||||||
|
* @phpstan-ignore-next-line
|
||||||
|
* @param array $needle
|
||||||
|
* @phpstan-ignore-next-line
|
||||||
|
* @param array $haystack
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected static function recursiveNeedlePresentInHaystack(array $needle, array $haystack): bool
|
||||||
|
{
|
||||||
|
if (!empty(array_diff(array_keys($needle), array_keys($haystack)))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($needle as $key => $value) {
|
||||||
|
if (is_array($value) && !self::recursiveCompareArrays($value, $haystack[$key])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value !== $haystack[$key]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
47
src/Comparator/ScalarFlatArrayComparator.php
Normal file
47
src/Comparator/ScalarFlatArrayComparator.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PHP 7.3
|
||||||
|
*
|
||||||
|
* @category ScalarFlatArrayComparator
|
||||||
|
* @package Pock\Comparator
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Pock\Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ScalarFlatArrayComparator
|
||||||
|
*
|
||||||
|
* @category ScalarFlatArrayComparator
|
||||||
|
* @package Pock\Comparator
|
||||||
|
*/
|
||||||
|
class ScalarFlatArrayComparator implements ComparatorInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function compare($first, $second): bool
|
||||||
|
{
|
||||||
|
if (!is_array($first) || !is_array($second)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static::compareScalarFlatArrays($first, $second);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if two one-dimensional string arrays are equal.
|
||||||
|
*
|
||||||
|
* @phpstan-ignore-next-line
|
||||||
|
* @param array $first
|
||||||
|
* @phpstan-ignore-next-line
|
||||||
|
* @param array $second
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected static function compareScalarFlatArrays(array $first, array $second): bool
|
||||||
|
{
|
||||||
|
return count($first) === count($second) &&
|
||||||
|
array_diff($first, $second) === array_diff($second, $first);
|
||||||
|
}
|
||||||
|
}
|
@ -1,120 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PHP 7.1
|
|
||||||
*
|
|
||||||
* @category AbstractArrayPoweredComponent
|
|
||||||
* @package Pock\Matchers
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Pock\Matchers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class AbstractArrayPoweredComponent
|
|
||||||
*
|
|
||||||
* @category AbstractArrayPoweredComponent
|
|
||||||
* @package Pock\Matchers
|
|
||||||
*/
|
|
||||||
abstract class AbstractArrayPoweredComponent
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Returns true if both arrays are equal recursively.
|
|
||||||
*
|
|
||||||
* @phpstan-ignore-next-line
|
|
||||||
* @param array $first
|
|
||||||
* @phpstan-ignore-next-line
|
|
||||||
* @param array $second
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected static function recursiveCompareArrays(array $first, array $second): bool
|
|
||||||
{
|
|
||||||
if (count($first) !== count($second)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty(array_diff(array_keys($first), array_keys($second)))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($first as $key => $value) {
|
|
||||||
if (is_array($value) && !self::recursiveCompareArrays($value, $second[$key])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($value !== $second[$key]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if two one-dimensional string arrays are equal.
|
|
||||||
*
|
|
||||||
* @phpstan-ignore-next-line
|
|
||||||
* @param array $first
|
|
||||||
* @phpstan-ignore-next-line
|
|
||||||
* @param array $second
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected static function compareStringArrays(array $first, array $second): bool
|
|
||||||
{
|
|
||||||
return count($first) === count($second) &&
|
|
||||||
array_diff($first, $second) === array_diff($second, $first);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if all needle values is present in haystack.
|
|
||||||
* Doesn't work for multidimensional arrays.
|
|
||||||
*
|
|
||||||
* @phpstan-ignore-next-line
|
|
||||||
* @param array $needle
|
|
||||||
* @phpstan-ignore-next-line
|
|
||||||
* @param array $haystack
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected static function isNeedlePresentInHaystack(array $needle, array $haystack): bool
|
|
||||||
{
|
|
||||||
foreach ($needle as $value) {
|
|
||||||
if (!in_array($value, $haystack, true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if all needle values is present in haystack.
|
|
||||||
* Works for multidimensional arrays. Internal arrays will be treated as values (e.g. will be compared recursively).
|
|
||||||
*
|
|
||||||
* @phpstan-ignore-next-line
|
|
||||||
* @param array $needle
|
|
||||||
* @phpstan-ignore-next-line
|
|
||||||
* @param array $haystack
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected static function recursiveNeedlePresentInHaystack(array $needle, array $haystack): bool
|
|
||||||
{
|
|
||||||
if (!empty(array_diff(array_keys($needle), array_keys($haystack)))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($needle as $key => $value) {
|
|
||||||
if (is_array($value) && !self::recursiveCompareArrays($value, $haystack[$key])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($value !== $haystack[$key]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
namespace Pock\Matchers;
|
namespace Pock\Matchers;
|
||||||
|
|
||||||
|
use Pock\Comparator\ComparatorLocator;
|
||||||
|
use Pock\Comparator\RecursiveArrayComparator;
|
||||||
use Pock\Traits\SeekableStreamDataExtractor;
|
use Pock\Traits\SeekableStreamDataExtractor;
|
||||||
use Psr\Http\Message\RequestInterface;
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
||||||
@ -18,7 +20,7 @@ use Psr\Http\Message\RequestInterface;
|
|||||||
* @category AbstractSerializedBodyMatcher
|
* @category AbstractSerializedBodyMatcher
|
||||||
* @package Pock\Matchers
|
* @package Pock\Matchers
|
||||||
*/
|
*/
|
||||||
abstract class AbstractSerializedBodyMatcher extends AbstractArrayPoweredComponent implements RequestMatcherInterface
|
abstract class AbstractSerializedBodyMatcher implements RequestMatcherInterface
|
||||||
{
|
{
|
||||||
use SeekableStreamDataExtractor;
|
use SeekableStreamDataExtractor;
|
||||||
|
|
||||||
@ -54,7 +56,7 @@ abstract class AbstractSerializedBodyMatcher extends AbstractArrayPoweredCompone
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::recursiveCompareArrays($bodyData, $this->data);
|
return ComparatorLocator::get(RecursiveArrayComparator::class)->compare($bodyData, $this->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,17 +33,7 @@ class BodyMatcher implements RequestMatcherInterface
|
|||||||
*/
|
*/
|
||||||
public function __construct($contents)
|
public function __construct($contents)
|
||||||
{
|
{
|
||||||
if (is_string($contents)) {
|
$this->contents = static::getEntryItemData($contents);
|
||||||
$this->contents = $contents;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($contents instanceof StreamInterface) {
|
|
||||||
$this->contents = static::getStreamData($contents);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_resource($contents)) {
|
|
||||||
$this->contents = static::readAllResource($contents);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,4 +60,24 @@ class BodyMatcher implements RequestMatcherInterface
|
|||||||
fseek($resource, 0);
|
fseek($resource, 0);
|
||||||
return (string) stream_get_contents($resource);
|
return (string) stream_get_contents($resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param StreamInterface|resource|string $contents
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected static function getEntryItemData($contents): string
|
||||||
|
{
|
||||||
|
if (is_string($contents)) {
|
||||||
|
return $contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($contents instanceof StreamInterface) {
|
||||||
|
return static::getStreamData($contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_resource($contents)) {
|
||||||
|
return static::readAllResource($contents);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
namespace Pock\Matchers;
|
namespace Pock\Matchers;
|
||||||
|
|
||||||
|
use Pock\Comparator\ComparatorLocator;
|
||||||
|
use Pock\Comparator\ScalarFlatArrayComparator;
|
||||||
use Psr\Http\Message\RequestInterface;
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,6 +30,7 @@ class ExactHeaderMatcher extends HeaderMatcher
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::compareStringArrays($request->getHeader($this->header), $this->value);
|
return ComparatorLocator::get(ScalarFlatArrayComparator::class)
|
||||||
|
->compare($request->getHeader($this->header), $this->value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
namespace Pock\Matchers;
|
namespace Pock\Matchers;
|
||||||
|
|
||||||
|
use Pock\Comparator\ComparatorLocator;
|
||||||
|
use Pock\Comparator\RecursiveArrayComparator;
|
||||||
use Psr\Http\Message\RequestInterface;
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,6 +32,6 @@ class ExactQueryMatcher extends QueryMatcher
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::recursiveCompareArrays($this->query, $query);
|
return ComparatorLocator::get(RecursiveArrayComparator::class)->compare($this->query, $query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
namespace Pock\Matchers;
|
namespace Pock\Matchers;
|
||||||
|
|
||||||
|
use Pock\Comparator\ComparatorLocator;
|
||||||
|
use Pock\Comparator\LtrScalarArrayComparator;
|
||||||
use Psr\Http\Message\RequestInterface;
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -17,7 +19,7 @@ use Psr\Http\Message\RequestInterface;
|
|||||||
* @category HeaderMatcher
|
* @category HeaderMatcher
|
||||||
* @package Pock\Matchers
|
* @package Pock\Matchers
|
||||||
*/
|
*/
|
||||||
class HeaderMatcher extends AbstractArrayPoweredComponent implements RequestMatcherInterface
|
class HeaderMatcher implements RequestMatcherInterface
|
||||||
{
|
{
|
||||||
/** @var string */
|
/** @var string */
|
||||||
protected $header;
|
protected $header;
|
||||||
@ -51,6 +53,7 @@ class HeaderMatcher extends AbstractArrayPoweredComponent implements RequestMatc
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::isNeedlePresentInHaystack($this->value, $request->getHeader($this->header));
|
return ComparatorLocator::get(LtrScalarArrayComparator::class)
|
||||||
|
->compare($this->value, $request->getHeader($this->header));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
namespace Pock\Matchers;
|
namespace Pock\Matchers;
|
||||||
|
|
||||||
|
use Pock\Comparator\ComparatorLocator;
|
||||||
|
use Pock\Comparator\LtrScalarArrayComparator;
|
||||||
use Psr\Http\Message\RequestInterface;
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -17,7 +19,7 @@ use Psr\Http\Message\RequestInterface;
|
|||||||
* @category HeadersMatcher
|
* @category HeadersMatcher
|
||||||
* @package Pock\Matchers
|
* @package Pock\Matchers
|
||||||
*/
|
*/
|
||||||
class HeadersMatcher extends AbstractArrayPoweredComponent implements RequestMatcherInterface
|
class HeadersMatcher implements RequestMatcherInterface
|
||||||
{
|
{
|
||||||
/** @var array<string, string|string[]> */
|
/** @var array<string, string|string[]> */
|
||||||
protected $headers;
|
protected $headers;
|
||||||
@ -48,7 +50,7 @@ class HeadersMatcher extends AbstractArrayPoweredComponent implements RequestMat
|
|||||||
$value = [$value];
|
$value = [$value];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!static::isNeedlePresentInHaystack($value, $request->getHeader($name))) {
|
if (!ComparatorLocator::get(LtrScalarArrayComparator::class)->compare($value, $request->getHeader($name))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,9 @@
|
|||||||
|
|
||||||
namespace Pock\Matchers;
|
namespace Pock\Matchers;
|
||||||
|
|
||||||
|
use Pock\Comparator\ComparatorLocator;
|
||||||
|
use Pock\Comparator\RecursiveLtrArrayComparator;
|
||||||
use Psr\Http\Message\RequestInterface;
|
use Psr\Http\Message\RequestInterface;
|
||||||
use Psr\Http\Message\UriInterface;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class QueryMatcher
|
* Class QueryMatcher
|
||||||
@ -18,7 +19,7 @@ use Psr\Http\Message\UriInterface;
|
|||||||
* @category QueryMatcher
|
* @category QueryMatcher
|
||||||
* @package Pock\Matchers
|
* @package Pock\Matchers
|
||||||
*/
|
*/
|
||||||
class QueryMatcher extends AbstractArrayPoweredComponent implements RequestMatcherInterface
|
class QueryMatcher implements RequestMatcherInterface
|
||||||
{
|
{
|
||||||
/** @var array<string, mixed> */
|
/** @var array<string, mixed> */
|
||||||
protected $query;
|
protected $query;
|
||||||
@ -44,7 +45,7 @@ class QueryMatcher extends AbstractArrayPoweredComponent implements RequestMatch
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::recursiveNeedlePresentInHaystack($this->query, $query);
|
return ComparatorLocator::get(RecursiveLtrArrayComparator::class)->compare($this->query, $query);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
194
src/Matchers/XmlBodyMatcher.php
Normal file
194
src/Matchers/XmlBodyMatcher.php
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PHP 7.3
|
||||||
|
*
|
||||||
|
* @category XmlBodyMatcher
|
||||||
|
* @package Pock\Matchers
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Pock\Matchers;
|
||||||
|
|
||||||
|
use DOMDocument;
|
||||||
|
use Pock\Exception\XmlException;
|
||||||
|
use Throwable;
|
||||||
|
use XSLTProcessor;
|
||||||
|
use Pock\Traits\SeekableStreamDataExtractor;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class XmlBodyMatcher
|
||||||
|
*
|
||||||
|
* @category XmlBodyMatcher
|
||||||
|
* @package Pock\Matchers
|
||||||
|
*/
|
||||||
|
class XmlBodyMatcher extends BodyMatcher
|
||||||
|
{
|
||||||
|
use SeekableStreamDataExtractor;
|
||||||
|
|
||||||
|
private const TAG_SORT_XSLT = <<<EOT
|
||||||
|
<xsl:stylesheet version="1.0"
|
||||||
|
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
||||||
|
<xsl:output omit-xml-declaration="yes" indent="yes"/>
|
||||||
|
<xsl:strip-space elements="*"/>
|
||||||
|
<xsl:template match="node()|@*">
|
||||||
|
<xsl:copy>
|
||||||
|
<xsl:apply-templates select="@*">
|
||||||
|
<xsl:sort select="name()"/>
|
||||||
|
</xsl:apply-templates>
|
||||||
|
<xsl:apply-templates select="node()">
|
||||||
|
<xsl:sort select="name()"/>
|
||||||
|
</xsl:apply-templates>
|
||||||
|
</xsl:copy>
|
||||||
|
</xsl:template>
|
||||||
|
</xsl:stylesheet>
|
||||||
|
EOT;
|
||||||
|
|
||||||
|
/** @var XSLTProcessor|null */
|
||||||
|
private static $sorter;
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $useFallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XmlBodyMatcher constructor.
|
||||||
|
*
|
||||||
|
* @param DOMDocument|\Psr\Http\Message\StreamInterface|resource|string $referenceXml
|
||||||
|
*
|
||||||
|
* @throws \Pock\Exception\XmlException
|
||||||
|
*/
|
||||||
|
public function __construct($referenceXml)
|
||||||
|
{
|
||||||
|
if (!extension_loaded('xsl') || !extension_loaded('dom')) {
|
||||||
|
$this->useFallback = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!extension_loaded('xsl')) {
|
||||||
|
$this->useFallback = true;
|
||||||
|
|
||||||
|
if (extension_loaded('dom') && $referenceXml instanceof DOMDocument) {
|
||||||
|
$referenceXml = static::getDOMString($referenceXml);
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct($referenceXml); // @phpstan-ignore-line
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($referenceXml instanceof DOMDocument) {
|
||||||
|
parent::__construct(static::sortXmlTags($referenceXml));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct(static::sortXmlTags(
|
||||||
|
static::createDOMDocument(static::getEntryItemData($referenceXml))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function matches(RequestInterface $request): bool
|
||||||
|
{
|
||||||
|
if ($this->useFallback) {
|
||||||
|
return parent::matches($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 === $request->getBody()->getSize()) {
|
||||||
|
return '' === $this->contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::sortXmlTags(self::createDOMDocument(self::getStreamData($request->getBody()))) === $this->contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns new document with tags sorted alphabetically.
|
||||||
|
*
|
||||||
|
* @param \DOMDocument $document
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws \RuntimeException|\Pock\Exception\XmlException
|
||||||
|
*/
|
||||||
|
private static function sortXmlTags(DOMDocument $document): string
|
||||||
|
{
|
||||||
|
$xml = static::getSorter()->transformToXml($document);
|
||||||
|
|
||||||
|
if (false === $xml) {
|
||||||
|
throw new RuntimeException('Cannot sort XML nodes');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $xml;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns XSLTProcessor with XSLT which sorts tags alphabetically.
|
||||||
|
*
|
||||||
|
* @return \XSLTProcessor
|
||||||
|
* @throws \Pock\Exception\XmlException
|
||||||
|
*/
|
||||||
|
private static function getSorter(): XSLTProcessor
|
||||||
|
{
|
||||||
|
if (null === static::$sorter) {
|
||||||
|
static::$sorter = new XSLTProcessor();
|
||||||
|
static::$sorter->importStylesheet(static::createDOMDocument(static::TAG_SORT_XSLT));
|
||||||
|
}
|
||||||
|
|
||||||
|
return static::$sorter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create DOMDocument with provided XML string.
|
||||||
|
*
|
||||||
|
* @param string $xml
|
||||||
|
* @param string $version
|
||||||
|
* @param string $encoding
|
||||||
|
*
|
||||||
|
* @return \DOMDocument
|
||||||
|
* @throws \Pock\Exception\XmlException
|
||||||
|
*/
|
||||||
|
private static function createDOMDocument(string $xml, string $version = '1.0', string $encoding = ''): DOMDocument
|
||||||
|
{
|
||||||
|
if ('' === $xml) {
|
||||||
|
throw new XmlException('XML must not be empty.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$error = null;
|
||||||
|
$document = new DOMDocument($version, $encoding);
|
||||||
|
|
||||||
|
try {
|
||||||
|
set_error_handler(static function ($code, $message) {
|
||||||
|
throw new XmlException($message, $code);
|
||||||
|
});
|
||||||
|
$document->loadXML(trim($xml));
|
||||||
|
} catch (XmlException $exception) {
|
||||||
|
$error = $exception;
|
||||||
|
} finally {
|
||||||
|
restore_error_handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $error) {
|
||||||
|
throw $error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $document;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \DOMDocument $document
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws \Pock\Exception\XmlException
|
||||||
|
*/
|
||||||
|
private static function getDOMString(DOMDocument $document): string
|
||||||
|
{
|
||||||
|
$result = $document->saveXML();
|
||||||
|
|
||||||
|
if (false === $result) {
|
||||||
|
throw new XmlException('Cannot export XML.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
@ -10,11 +10,13 @@
|
|||||||
namespace Pock;
|
namespace Pock;
|
||||||
|
|
||||||
use Diff\ArrayComparer\StrictArrayComparer;
|
use Diff\ArrayComparer\StrictArrayComparer;
|
||||||
|
use DOMDocument;
|
||||||
use Pock\Enum\RequestMethod;
|
use Pock\Enum\RequestMethod;
|
||||||
use Pock\Enum\RequestScheme;
|
use Pock\Enum\RequestScheme;
|
||||||
use Pock\Exception\PockClientException;
|
use Pock\Exception\PockClientException;
|
||||||
use Pock\Exception\PockNetworkException;
|
use Pock\Exception\PockNetworkException;
|
||||||
use Pock\Exception\PockRequestException;
|
use Pock\Exception\PockRequestException;
|
||||||
|
use Pock\Exception\XmlException;
|
||||||
use Pock\Factory\CallbackReplyFactory;
|
use Pock\Factory\CallbackReplyFactory;
|
||||||
use Pock\Factory\ReplyFactoryInterface;
|
use Pock\Factory\ReplyFactoryInterface;
|
||||||
use Pock\Matchers\AnyRequestMatcher;
|
use Pock\Matchers\AnyRequestMatcher;
|
||||||
@ -36,6 +38,7 @@ use Pock\Matchers\QueryMatcher;
|
|||||||
use Pock\Matchers\RequestMatcherInterface;
|
use Pock\Matchers\RequestMatcherInterface;
|
||||||
use Pock\Matchers\SchemeMatcher;
|
use Pock\Matchers\SchemeMatcher;
|
||||||
use Pock\Matchers\UriMatcher;
|
use Pock\Matchers\UriMatcher;
|
||||||
|
use Pock\Matchers\XmlBodyMatcher;
|
||||||
use Pock\Traits\JsonDecoderTrait;
|
use Pock\Traits\JsonDecoderTrait;
|
||||||
use Pock\Traits\JsonSerializerAwareTrait;
|
use Pock\Traits\JsonSerializerAwareTrait;
|
||||||
use Pock\Traits\XmlSerializerAwareTrait;
|
use Pock\Traits\XmlSerializerAwareTrait;
|
||||||
@ -286,21 +289,38 @@ class PockBuilder
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Match XML request body using raw XML data.
|
||||||
|
*
|
||||||
|
* **Note:** this method will fallback to the string comparison if ext-xsl is not available.
|
||||||
|
* It also doesn't serializer values with available XML serializer.
|
||||||
|
* Use PockBuilder::matchSerializedXmlBody if you want to execute available serializer.
|
||||||
|
*
|
||||||
|
* @see \Pock\PockBuilder::matchSerializedXmlBody()
|
||||||
|
*
|
||||||
|
* @param DOMDocument|\Psr\Http\Message\StreamInterface|resource|string $data
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function matchXmlBody($data): self
|
||||||
|
{
|
||||||
|
return $this->addMatcher(new XmlBodyMatcher($data));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Match XML request body.
|
* Match XML request body.
|
||||||
*
|
*
|
||||||
* **Note:** this method will use string comparison for now. It'll be improved in future.
|
* This method will try to use available XML serializer before matching.
|
||||||
*
|
*
|
||||||
* @todo Don't use simple string comparison. Match the entire body by its DOM.
|
* @phpstan-ignore-next-line
|
||||||
*
|
* @param string|array|object $data
|
||||||
* @param mixed $data
|
|
||||||
*
|
*
|
||||||
* @return self
|
* @return self
|
||||||
* @throws \Pock\Exception\XmlException
|
* @throws \Pock\Exception\XmlException
|
||||||
*/
|
*/
|
||||||
public function matchXmlBody($data): self
|
public function matchSerializedXmlBody($data): self
|
||||||
{
|
{
|
||||||
return $this->matchBody(self::serializeXml($data) ?? '');
|
return $this->matchXmlBody(self::serializeXml($data) ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
48
tests/src/Comparator/ComparatorLocatorTest.php
Normal file
48
tests/src/Comparator/ComparatorLocatorTest.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PHP 7.3
|
||||||
|
*
|
||||||
|
* @category ComparatorLocatorTest
|
||||||
|
* @package Pock\Tests\Comparator
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Pock\Tests\Comparator;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Pock\Comparator\ComparatorLocator;
|
||||||
|
use Pock\Comparator\LtrScalarArrayComparator;
|
||||||
|
use Pock\Comparator\RecursiveArrayComparator;
|
||||||
|
use Pock\Comparator\RecursiveLtrArrayComparator;
|
||||||
|
use Pock\Comparator\ScalarFlatArrayComparator;
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ComparatorLocatorTest
|
||||||
|
*
|
||||||
|
* @category ComparatorLocatorTest
|
||||||
|
* @package Pock\Tests\Comparator
|
||||||
|
*/
|
||||||
|
class ComparatorLocatorTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testGetException(): void
|
||||||
|
{
|
||||||
|
$this->expectException(RuntimeException::class);
|
||||||
|
$this->expectExceptionMessage('Comparator random does not exist.');
|
||||||
|
|
||||||
|
ComparatorLocator::get('random');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGet(): void
|
||||||
|
{
|
||||||
|
$comparator = ComparatorLocator::get(ScalarFlatArrayComparator::class);
|
||||||
|
|
||||||
|
self::assertInstanceOf(ScalarFlatArrayComparator::class, $comparator);
|
||||||
|
self::assertTrue($comparator->compare(['1'], ['1']));
|
||||||
|
self::assertFalse($comparator->compare(['1'], ['2']));
|
||||||
|
self::assertFalse($comparator->compare(null, null));
|
||||||
|
self::assertFalse(ComparatorLocator::get(LtrScalarArrayComparator::class)->compare(null, null));
|
||||||
|
self::assertFalse(ComparatorLocator::get(RecursiveArrayComparator::class)->compare(null, null));
|
||||||
|
self::assertFalse(ComparatorLocator::get(RecursiveLtrArrayComparator::class)->compare(null, null));
|
||||||
|
}
|
||||||
|
}
|
62
tests/src/Matchers/XmlBodyMatcherTest.php
Normal file
62
tests/src/Matchers/XmlBodyMatcherTest.php
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PHP 7.3
|
||||||
|
*
|
||||||
|
* @category XmlBodyMatcherTest
|
||||||
|
* @package Pock\Tests\Matchers
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Pock\Tests\Matchers;
|
||||||
|
|
||||||
|
use Pock\Matchers\XmlBodyMatcher;
|
||||||
|
use Pock\TestUtils\PockTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class XmlBodyMatcherTest
|
||||||
|
*
|
||||||
|
* @category XmlBodyMatcherTest
|
||||||
|
* @package Pock\Tests\Matchers
|
||||||
|
*/
|
||||||
|
class XmlBodyMatcherTest extends PockTestCase
|
||||||
|
{
|
||||||
|
public function testEmptyXml(): void
|
||||||
|
{
|
||||||
|
$this->expectExceptionMessage('XML must not be empty.');
|
||||||
|
new XmlBodyMatcher('');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInvalidXml(): void
|
||||||
|
{
|
||||||
|
$brokenXml = <<<'EOF'
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<result>
|
||||||
|
<field><![CDATA[test]></field>
|
||||||
|
</result>
|
||||||
|
EOF;
|
||||||
|
|
||||||
|
$this->expectExceptionMessage('DOMDocument::loadXML(): CData section not finished');
|
||||||
|
new XmlBodyMatcher($brokenXml);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMatchXml(): void
|
||||||
|
{
|
||||||
|
$expected = <<<'EOF'
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<result>
|
||||||
|
<field key="2" id="1"><![CDATA[test]]></field>
|
||||||
|
</result>
|
||||||
|
EOF;
|
||||||
|
$actual = <<<'EOF'
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<result>
|
||||||
|
<field id="1" key="2">
|
||||||
|
<![CDATA[test]]>
|
||||||
|
</field>
|
||||||
|
</result>
|
||||||
|
EOF;
|
||||||
|
|
||||||
|
self::assertTrue((new XmlBodyMatcher($expected))->matches(static::getRequestWithBody($actual)));
|
||||||
|
}
|
||||||
|
}
|
@ -9,10 +9,11 @@
|
|||||||
|
|
||||||
namespace Pock\Tests;
|
namespace Pock\Tests;
|
||||||
|
|
||||||
|
use DOMDocument;
|
||||||
|
use phpmock\MockBuilder;
|
||||||
use Pock\Enum\RequestMethod;
|
use Pock\Enum\RequestMethod;
|
||||||
use Pock\Enum\RequestScheme;
|
use Pock\Enum\RequestScheme;
|
||||||
use Pock\Exception\UnsupportedRequestException;
|
use Pock\Exception\UnsupportedRequestException;
|
||||||
use Pock\Factory\ReplyFactoryInterface;
|
|
||||||
use Pock\PockBuilder;
|
use Pock\PockBuilder;
|
||||||
use Pock\PockResponseBuilder;
|
use Pock\PockResponseBuilder;
|
||||||
use Pock\TestUtils\PockTestCase;
|
use Pock\TestUtils\PockTestCase;
|
||||||
@ -22,7 +23,6 @@ use Psr\Http\Client\ClientExceptionInterface;
|
|||||||
use Psr\Http\Client\NetworkExceptionInterface;
|
use Psr\Http\Client\NetworkExceptionInterface;
|
||||||
use Psr\Http\Client\RequestExceptionInterface;
|
use Psr\Http\Client\RequestExceptionInterface;
|
||||||
use Psr\Http\Message\RequestInterface;
|
use Psr\Http\Message\RequestInterface;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,6 +85,23 @@ class PockBuilderTest extends PockTestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testThrowRequestExceptionGetRequest(): void
|
||||||
|
{
|
||||||
|
$builder = new PockBuilder();
|
||||||
|
$request = self::getPsr17Factory()->createRequest(RequestMethod::GET, self::TEST_URI);
|
||||||
|
|
||||||
|
$builder->matchMethod(RequestMethod::GET)
|
||||||
|
->matchScheme(RequestScheme::HTTPS)
|
||||||
|
->matchHost(self::TEST_HOST)
|
||||||
|
->throwRequestException();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$builder->getClient()->sendRequest($request);
|
||||||
|
} catch (RequestExceptionInterface $exception) {
|
||||||
|
self::assertEquals($request, $exception->getRequest());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function testMatchHeader(): void
|
public function testMatchHeader(): void
|
||||||
{
|
{
|
||||||
$builder = new PockBuilder();
|
$builder = new PockBuilder();
|
||||||
@ -305,7 +322,124 @@ class PockBuilderTest extends PockTestCase
|
|||||||
], json_decode($response->getBody()->getContents(), true));
|
], json_decode($response->getBody()->getContents(), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testXmlResponse(): void
|
public function testMatchXmlString(): void
|
||||||
|
{
|
||||||
|
$xml = <<<'EOF'
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<result>
|
||||||
|
<entry><![CDATA[Forbidden]]></entry>
|
||||||
|
</result>
|
||||||
|
|
||||||
|
EOF;
|
||||||
|
$simpleObject = <<<'EOF'
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<result>
|
||||||
|
<field><![CDATA[test]]></field>
|
||||||
|
</result>
|
||||||
|
EOF;
|
||||||
|
|
||||||
|
$builder = new PockBuilder();
|
||||||
|
$builder->matchMethod(RequestMethod::GET)
|
||||||
|
->matchScheme(RequestScheme::HTTPS)
|
||||||
|
->matchHost(self::TEST_HOST)
|
||||||
|
->matchXmlBody($simpleObject)
|
||||||
|
->repeat(2)
|
||||||
|
->reply(403)
|
||||||
|
->withHeader('Content-Type', 'text/xml')
|
||||||
|
->withXml(['error' => 'Forbidden']);
|
||||||
|
|
||||||
|
$response = $builder->getClient()->sendRequest(
|
||||||
|
self::getPsr17Factory()
|
||||||
|
->createRequest(RequestMethod::GET, self::TEST_URI)
|
||||||
|
->withBody(self::getPsr17Factory()->createStream($simpleObject))
|
||||||
|
);
|
||||||
|
|
||||||
|
self::assertEquals(403, $response->getStatusCode());
|
||||||
|
self::assertEquals(['Content-Type' => ['text/xml']], $response->getHeaders());
|
||||||
|
self::assertEquals($xml, $response->getBody()->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMatchXmlStream(): void
|
||||||
|
{
|
||||||
|
$xml = <<<'EOF'
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<result>
|
||||||
|
<entry><![CDATA[Forbidden]]></entry>
|
||||||
|
</result>
|
||||||
|
|
||||||
|
EOF;
|
||||||
|
$simpleObject = <<<'EOF'
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<result>
|
||||||
|
<field><![CDATA[test]]></field>
|
||||||
|
</result>
|
||||||
|
EOF;
|
||||||
|
|
||||||
|
$builder = new PockBuilder();
|
||||||
|
$builder->matchMethod(RequestMethod::GET)
|
||||||
|
->matchScheme(RequestScheme::HTTPS)
|
||||||
|
->matchHost(self::TEST_HOST)
|
||||||
|
->matchXmlBody(self::getPsr17Factory()->createStream($simpleObject))
|
||||||
|
->repeat(2)
|
||||||
|
->reply(403)
|
||||||
|
->withHeader('Content-Type', 'text/xml')
|
||||||
|
->withXml(['error' => 'Forbidden']);
|
||||||
|
|
||||||
|
$response = $builder->getClient()->sendRequest(
|
||||||
|
self::getPsr17Factory()
|
||||||
|
->createRequest(RequestMethod::GET, self::TEST_URI)
|
||||||
|
->withBody(self::getPsr17Factory()->createStream($simpleObject))
|
||||||
|
);
|
||||||
|
|
||||||
|
self::assertEquals(403, $response->getStatusCode());
|
||||||
|
self::assertEquals(['Content-Type' => ['text/xml']], $response->getHeaders());
|
||||||
|
self::assertEquals($xml, $response->getBody()->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMatchXmlDOMDocument(): void
|
||||||
|
{
|
||||||
|
$xml = <<<'EOF'
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<result>
|
||||||
|
<entry><![CDATA[Forbidden]]></entry>
|
||||||
|
</result>
|
||||||
|
|
||||||
|
EOF;
|
||||||
|
$simpleObject = <<<'EOF'
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<result>
|
||||||
|
<field><![CDATA[test]]></field>
|
||||||
|
</result>
|
||||||
|
EOF;
|
||||||
|
|
||||||
|
$document = new DOMDocument();
|
||||||
|
$document->loadXML($simpleObject);
|
||||||
|
|
||||||
|
$builder = new PockBuilder();
|
||||||
|
$builder->matchMethod(RequestMethod::GET)
|
||||||
|
->matchScheme(RequestScheme::HTTPS)
|
||||||
|
->matchHost(self::TEST_HOST)
|
||||||
|
->matchXmlBody($document)
|
||||||
|
->repeat(2)
|
||||||
|
->reply(403)
|
||||||
|
->withHeader('Content-Type', 'text/xml')
|
||||||
|
->withXml(['error' => 'Forbidden']);
|
||||||
|
|
||||||
|
$response = $builder->getClient()->sendRequest(
|
||||||
|
self::getPsr17Factory()
|
||||||
|
->createRequest(RequestMethod::GET, self::TEST_URI)
|
||||||
|
->withBody(self::getPsr17Factory()->createStream($simpleObject))
|
||||||
|
);
|
||||||
|
|
||||||
|
self::assertEquals(403, $response->getStatusCode());
|
||||||
|
self::assertEquals(['Content-Type' => ['text/xml']], $response->getHeaders());
|
||||||
|
self::assertEquals($xml, $response->getBody()->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider matchXmlNoXslProvider
|
||||||
|
*/
|
||||||
|
public function testMatchXmlNoXsl(string $simpleObject, bool $expectException): void
|
||||||
{
|
{
|
||||||
$xml = <<<'EOF'
|
$xml = <<<'EOF'
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
@ -315,11 +449,77 @@ class PockBuilderTest extends PockTestCase
|
|||||||
|
|
||||||
EOF;
|
EOF;
|
||||||
|
|
||||||
|
if ($expectException) {
|
||||||
|
$this->expectException(UnsupportedRequestException::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
$mock = (new MockBuilder())->setNamespace('Pock\Matchers')
|
||||||
|
->setName('extension_loaded')
|
||||||
|
->setFunction(
|
||||||
|
static function (string $extension) {
|
||||||
|
if ('xsl' === $extension) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return \extension_loaded($extension);
|
||||||
|
}
|
||||||
|
)->build();
|
||||||
|
$mock->enable();
|
||||||
|
|
||||||
|
$document = new DOMDocument();
|
||||||
|
$document->loadXML($simpleObject);
|
||||||
|
|
||||||
$builder = new PockBuilder();
|
$builder = new PockBuilder();
|
||||||
$builder->matchMethod(RequestMethod::GET)
|
$builder->matchMethod(RequestMethod::GET)
|
||||||
->matchScheme(RequestScheme::HTTPS)
|
->matchScheme(RequestScheme::HTTPS)
|
||||||
->matchHost(self::TEST_HOST)
|
->matchHost(self::TEST_HOST)
|
||||||
->matchXmlBody(new SimpleObject())
|
->matchXmlBody($document)
|
||||||
|
->repeat(2)
|
||||||
|
->reply(403)
|
||||||
|
->withHeader('Content-Type', 'text/xml')
|
||||||
|
->withXml(['error' => 'Forbidden']);
|
||||||
|
|
||||||
|
$mock->disable();
|
||||||
|
|
||||||
|
$response = $builder->getClient()->sendRequest(
|
||||||
|
self::getPsr17Factory()
|
||||||
|
->createRequest(RequestMethod::GET, self::TEST_URI)
|
||||||
|
->withBody(self::getPsr17Factory()->createStream($simpleObject))
|
||||||
|
);
|
||||||
|
|
||||||
|
self::assertEquals(403, $response->getStatusCode());
|
||||||
|
self::assertEquals(['Content-Type' => ['text/xml']], $response->getHeaders());
|
||||||
|
self::assertEquals($xml, $response->getBody()->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSerializedXmlResponse(): void
|
||||||
|
{
|
||||||
|
$xml = <<<'EOF'
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<result>
|
||||||
|
<entry><![CDATA[Forbidden]]></entry>
|
||||||
|
</result>
|
||||||
|
|
||||||
|
EOF;
|
||||||
|
$simpleObjectFreeFormXml = <<<'EOF'
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<result>
|
||||||
|
|
||||||
|
<field>
|
||||||
|
<![CDATA[test]]>
|
||||||
|
|
||||||
|
</field>
|
||||||
|
|
||||||
|
</result>
|
||||||
|
EOF;
|
||||||
|
|
||||||
|
$builder = new PockBuilder();
|
||||||
|
$builder->matchMethod(RequestMethod::GET)
|
||||||
|
->matchScheme(RequestScheme::HTTPS)
|
||||||
|
->matchHost(self::TEST_HOST)
|
||||||
|
->matchSerializedXmlBody(new SimpleObject())
|
||||||
|
->repeat(2)
|
||||||
->reply(403)
|
->reply(403)
|
||||||
->withHeader('Content-Type', 'text/xml')
|
->withHeader('Content-Type', 'text/xml')
|
||||||
->withXml(['error' => 'Forbidden']);
|
->withXml(['error' => 'Forbidden']);
|
||||||
@ -328,13 +528,23 @@ EOF;
|
|||||||
self::getPsr17Factory()
|
self::getPsr17Factory()
|
||||||
->createRequest(RequestMethod::GET, self::TEST_URI)
|
->createRequest(RequestMethod::GET, self::TEST_URI)
|
||||||
->withBody(self::getPsr17Factory()->createStream(
|
->withBody(self::getPsr17Factory()->createStream(
|
||||||
self::getXmlSerializer()->serialize(new SimpleObject())
|
PHP_EOL . self::getXmlSerializer()->serialize(new SimpleObject()) . PHP_EOL
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
self::assertEquals(403, $response->getStatusCode());
|
self::assertEquals(403, $response->getStatusCode());
|
||||||
self::assertEquals(['Content-Type' => ['text/xml']], $response->getHeaders());
|
self::assertEquals(['Content-Type' => ['text/xml']], $response->getHeaders());
|
||||||
self::assertEquals($xml, $response->getBody()->getContents());
|
self::assertEquals($xml, $response->getBody()->getContents());
|
||||||
|
|
||||||
|
$response = $builder->getClient()->sendRequest(
|
||||||
|
self::getPsr17Factory()
|
||||||
|
->createRequest(RequestMethod::GET, self::TEST_URI)
|
||||||
|
->withBody(self::getPsr17Factory()->createStream($simpleObjectFreeFormXml))
|
||||||
|
);
|
||||||
|
|
||||||
|
self::assertEquals(403, $response->getStatusCode());
|
||||||
|
self::assertEquals(['Content-Type' => ['text/xml']], $response->getHeaders());
|
||||||
|
self::assertEquals($xml, $response->getBody()->getContents());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFirstExampleApiMock(): void
|
public function testFirstExampleApiMock(): void
|
||||||
@ -649,4 +859,18 @@ EOF;
|
|||||||
self::TEST_URI
|
self::TEST_URI
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function matchXmlNoXslProvider(): array
|
||||||
|
{
|
||||||
|
$simpleObject = <<<'EOF'
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<result>
|
||||||
|
<field><![CDATA[test]]></field>
|
||||||
|
</result>
|
||||||
|
EOF;
|
||||||
|
return [
|
||||||
|
[$simpleObject, true],
|
||||||
|
[$simpleObject . "\n", false]
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,19 @@ abstract class PockTestCase extends TestCase
|
|||||||
return static::getPsr17Factory()->createRequest($method ?? static::TEST_METHOD, static::TEST_URI);
|
return static::getPsr17Factory()->createRequest($method ?? static::TEST_METHOD, static::TEST_URI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $body
|
||||||
|
*
|
||||||
|
* @return \Psr\Http\Message\RequestInterface
|
||||||
|
*/
|
||||||
|
protected static function getRequestWithBody(string $body): RequestInterface
|
||||||
|
{
|
||||||
|
return static::getPsr17Factory()->createRequest(
|
||||||
|
RequestMethod::GET,
|
||||||
|
static::TEST_URI
|
||||||
|
)->withBody(self::getPsr17Factory()->createStream($body));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return \Nyholm\Psr7\Factory\Psr17Factory
|
* @return \Nyholm\Psr7\Factory\Psr17Factory
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user