mirror of
https://github.com/Neur0toxine/pock.git
synced 2025-04-07 06:33:31 +03:00
Compare commits
38 commits
Author | SHA1 | Date | |
---|---|---|---|
a9a9c7a2b8 | |||
1de2133b12 | |||
a5f7f280f2 | |||
eef38bf466 | |||
bbfe55bc94 | |||
f455ba52d5 | |||
5a92b28b5f | |||
f38395734a | |||
aec5a4c4a0 | |||
dcc642a0ea | |||
2170930dc5 | |||
f7832865a1 | |||
d892514e21 | |||
cea2f0a19e | |||
d5b1044efa | |||
dd441f54db | |||
b7f47bb6b9 | |||
064b07940c | |||
f09304f129 | |||
66e6e3ab2a | |||
285d06a01d | |||
2a124cfdd8 | |||
b4142304ab | |||
321650388e | |||
e7a17599ad | |||
b99fb4f255 | |||
e352c310d7 | |||
b08ba6f6cd | |||
60003ef9cc | |||
26d707000c | |||
21021e5d70 | |||
03fc4e4c2a | |||
b0f0c48815 | |||
70eef2abfa | |||
7c62c096de | |||
b68c11976a | |||
285136a674 | |||
d8c69ad11f |
66 changed files with 3113 additions and 313 deletions
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['7.2', '7.3', '7.4', '8.0']
|
||||
php-version: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2']
|
||||
steps:
|
||||
- name: Check out code into the workspace
|
||||
uses: actions/checkout@v2
|
||||
|
|
38
README.md
38
README.md
|
@ -1,14 +1,17 @@
|
|||
[](https://github.com/Neur0toxine/pock/actions)
|
||||
[](https://codecov.io/gh/Neur0toxine/pock)
|
||||
[](https://packagist.org/packages/neur0toxine/pock)
|
||||
[](https://packagist.org/packages/neur0toxine/pock)
|
||||

|
||||
[](https://codecov.io/gh/Neur0toxine/pock)
|
||||
[](https://packagist.org/packages/neur0toxine/pock)
|
||||
[](https://packagist.org/packages/neur0toxine/pock)
|
||||

|
||||
|
||||
# pock
|
||||
|
||||
Easy to use HTTP mocking solution, compatible with PSR-18 and HTTPlug.
|
||||
|
||||
Project is still in its early development stage. API can change over time, but I'll try to not introduce breaking changes.
|
||||
You can find autogenerated documentation [here](neur0toxine.github.io/pock/) or look at the examples.
|
||||
You can find autogenerated documentation [here](https://neur0toxine.github.io/pock/) or look at the examples. API for the mock building can be found
|
||||
[here](https://neur0toxine.github.io/pock/classes/Pock-PockBuilder.html) and API for the response building (returned from `PockBuilder::reply` call)
|
||||
can be found [here](https://neur0toxine.github.io/pock/classes/Pock-PockResponseBuilder.html).
|
||||
|
||||
# Examples
|
||||
|
||||
|
@ -88,13 +91,13 @@ pock supports JMS serializer and Symfony serializer out of the box. Available se
|
|||
It will be used to serialize requests and responses in mocks which means you actually can pass an entire DTO
|
||||
into the corresponding methods (for example, `matchJsonBody` as an assertion or `withJsonBody` to generate a response body).
|
||||
|
||||
By default JMS serializer has more priority than the Symfony serializer. You can use methods below before running tests (`bootstrap.php`)
|
||||
By default, JMS serializer has more priority than the Symfony serializer. You can use methods below before running tests (`bootstrap.php`)
|
||||
if you want to override default behavior.
|
||||
|
||||
```php
|
||||
use Pock\Factory\JsonSerializerFactory;
|
||||
use Pock\Factory\XmlSerializerFactory;
|
||||
use Pock\Serializer\SymfonySerializerDecorator;
|
||||
use Pock\Serializer\SymfonySerializerAdapter;
|
||||
use Symfony\Component\Serializer\Encoder\JsonEncoder;
|
||||
use Symfony\Component\Serializer\Encoder\XmlEncoder;
|
||||
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
||||
|
@ -102,10 +105,27 @@ use Symfony\Component\Serializer\Serializer;
|
|||
|
||||
$encoders = [new XmlEncoder(), new JsonEncoder()];
|
||||
$normalizers = [new ObjectNormalizer()];
|
||||
$serializer = new SymfonySerializerDecorator(new Serializer($normalizers, $encoders));
|
||||
$serializer = new SymfonySerializerAdapter(new Serializer($normalizers, $encoders));
|
||||
|
||||
JsonSerializerFactory::setSerializer($serializer);
|
||||
XmlSerializerFactory::setSerializer($serializer);
|
||||
```
|
||||
|
||||
In order to use unsupported serializer you should create a decorator which implements `Pock\Serializer\SerializerInterface`.
|
||||
In order to use unsupported serializer you should create an adapter which implements `Pock\Serializer\SerializerInterface`.
|
||||
|
||||
# Roadmap to stable
|
||||
|
||||
- [x] `at(N)` - execute mock only at Nth call.
|
||||
- [x] `always()` - always execute this mock (removes mock expiration).
|
||||
- [x] Separate `UniversalMockException` into several exceptions (`PockClientException`, `PockNetworkException`, etc).
|
||||
- [x] Add methods for easier throwing of exceptions listed in previous entry.
|
||||
- [x] `replyWithCallback` - reply using specified callback.
|
||||
- [x] `replyWithFactory` - reply using specified response factory (provide corresponding interface).
|
||||
- [x] Compare XML bodies using `DOMDocument`, fallback to text comparison in case of problems.
|
||||
- [x] Regexp matchers for body, query, URI and path.
|
||||
- [x] Form Data body matcher (partial & exact)
|
||||
- [x] Multipart form body matcher (just like callback matcher but parses the body as a multipart form data)
|
||||
- [x] **BREAKING CHANGE:** Rename serializer decorators to serializer adapters.
|
||||
- [x] Real network response for mocked requests.
|
||||
- [ ] `symfony/http-client` support.
|
||||
- [ ] Document everything (with examples if it’s feasible).
|
||||
|
|
|
@ -3,6 +3,16 @@
|
|||
"description": "PSR-18 compatible HTTP mock library",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/Neur0toxine/pock",
|
||||
"keywords": [
|
||||
"php-http",
|
||||
"http",
|
||||
"symfony",
|
||||
"mock",
|
||||
"psr-7",
|
||||
"psr-18",
|
||||
"mock"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Neur0toxine",
|
||||
|
@ -24,21 +34,23 @@
|
|||
"php": ">=7.2.0",
|
||||
"ext-json": "*",
|
||||
"psr/http-client": "^1.0",
|
||||
"psr/http-message": "^1.0",
|
||||
"psr/http-message": "^1.0 || ^2.0",
|
||||
"php-http/httplug": "^1.0 || ^2.0",
|
||||
"nyholm/psr7": "^1.4"
|
||||
"nyholm/psr7": "^1.4",
|
||||
"riverline/multipart-parser": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"squizlabs/php_codesniffer": "^3.6",
|
||||
"phpmd/phpmd": "^2.10",
|
||||
"phpmd/phpmd": "^2.12",
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.1",
|
||||
"phpcompatibility/php-compatibility": "^9.3",
|
||||
"phpstan/phpstan": "^0.12.87",
|
||||
"jms/serializer": "^2 | ^3.12",
|
||||
"phpstan/phpstan": "^1.5",
|
||||
"jms/serializer": "^2 | ^3.17",
|
||||
"symfony/phpunit-bridge": "^5.2",
|
||||
"symfony/var-dumper": "^5.2",
|
||||
"symfony/serializer": "^5.2",
|
||||
"symfony/property-access": "^5.2"
|
||||
"symfony/property-access": "^5.2",
|
||||
"php-http/multipart-stream-builder": "^1.2",
|
||||
"symfony/http-client": "^5.3"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-client-implementation": "1.0",
|
||||
|
@ -61,5 +73,11 @@
|
|||
"@lint",
|
||||
"@phpunit"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true,
|
||||
"php-http/discovery": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
10
phpmd.xml
10
phpmd.xml
|
@ -10,7 +10,15 @@
|
|||
<rule ref="rulesets/design.xml" />
|
||||
<rule ref="rulesets/cleancode.xml" />
|
||||
<rule ref="rulesets/codesize.xml" />
|
||||
<rule ref="rulesets/naming.xml" />
|
||||
<rule ref="rulesets/naming.xml">
|
||||
<exclude name="ShortMethodName" />
|
||||
</rule>
|
||||
|
||||
<rule ref="rulesets/naming.xml/ShortMethodName">
|
||||
<properties>
|
||||
<property name="minimum" value="2" />
|
||||
</properties>
|
||||
</rule>
|
||||
|
||||
<exclude-pattern>tests/*</exclude-pattern>
|
||||
</ruleset>
|
||||
|
|
111
phpstan-baseline.neon
Normal file
111
phpstan-baseline.neon
Normal file
|
@ -0,0 +1,111 @@
|
|||
parameters:
|
||||
ignoreErrors:
|
||||
-
|
||||
message: "#^Method Pock\\\\Client\\:\\:doSendRequest\\(\\) should return Psr\\\\Http\\\\Message\\\\ResponseInterface but returns mixed\\.$#"
|
||||
count: 1
|
||||
path: src/Client.php
|
||||
|
||||
-
|
||||
message: "#^Static property Pock\\\\Comparator\\\\ComparatorLocator\\:\\:\\$comparators \\(array\\<Pock\\\\Comparator\\\\ComparatorInterface\\>\\) does not accept array\\<object\\>\\.$#"
|
||||
count: 1
|
||||
path: src/Comparator/ComparatorLocator.php
|
||||
|
||||
-
|
||||
message: "#^Unsafe access to private property Pock\\\\Comparator\\\\ComparatorLocator\\:\\:\\$comparators through static\\:\\:\\.$#"
|
||||
count: 3
|
||||
path: src/Comparator/ComparatorLocator.php
|
||||
|
||||
-
|
||||
message: "#^Unsafe access to private property Pock\\\\Factory\\\\JsonSerializerFactory\\:\\:\\$mainSerializer through static\\:\\:\\.$#"
|
||||
count: 2
|
||||
path: src/Factory/JsonSerializerFactory.php
|
||||
|
||||
-
|
||||
message: "#^Unsafe access to private property Pock\\\\Factory\\\\XmlSerializerFactory\\:\\:\\$mainSerializer through static\\:\\:\\.$#"
|
||||
count: 2
|
||||
path: src/Factory/XmlSerializerFactory.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#4 \\$flags of function preg_match expects TFlags of 0\\|256\\|512\\|768, int given\\.$#"
|
||||
count: 1
|
||||
path: src/Matchers/AbstractRegExpMatcher.php
|
||||
|
||||
-
|
||||
message: "#^Unsafe call to private method Pock\\\\Matchers\\\\ExactHeadersMatcher\\:\\:headerValuesEqual\\(\\) through static\\:\\:\\.$#"
|
||||
count: 2
|
||||
path: src/Matchers/ExactHeadersMatcher.php
|
||||
|
||||
-
|
||||
message: "#^Method Pock\\\\Matchers\\\\JsonBodyMatcher\\:\\:deserialize\\(\\) should return array\\|null but returns mixed\\.$#"
|
||||
count: 1
|
||||
path: src/Matchers/JsonBodyMatcher.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$depth of function json_decode expects int\\<1, max\\>, int given\\.$#"
|
||||
count: 1
|
||||
path: src/Matchers/JsonBodyMatcher.php
|
||||
|
||||
-
|
||||
message: "#^Method Pock\\\\Matchers\\\\QueryMatcher\\:\\:parseQuery\\(\\) should return array\\<string, mixed\\> but returns array\\<int\\|string, array\\|string\\>\\.$#"
|
||||
count: 1
|
||||
path: src/Matchers/QueryMatcher.php
|
||||
|
||||
-
|
||||
message: "#^Method Pock\\\\Matchers\\\\XmlBodyMatcher\\:\\:sortXmlTags\\(\\) should return string but returns string\\|null\\.$#"
|
||||
count: 1
|
||||
path: src/Matchers/XmlBodyMatcher.php
|
||||
|
||||
-
|
||||
message: "#^Unsafe access to private constant Pock\\\\Matchers\\\\XmlBodyMatcher\\:\\:TAG_SORT_XSLT through static\\:\\:\\.$#"
|
||||
count: 1
|
||||
path: src/Matchers/XmlBodyMatcher.php
|
||||
|
||||
-
|
||||
message: "#^Unsafe access to private property Pock\\\\Matchers\\\\XmlBodyMatcher\\:\\:\\$sorter through static\\:\\:\\.$#"
|
||||
count: 4
|
||||
path: src/Matchers/XmlBodyMatcher.php
|
||||
|
||||
-
|
||||
message: "#^Unsafe call to private method Pock\\\\Matchers\\\\XmlBodyMatcher\\:\\:createDOMDocument\\(\\) through static\\:\\:\\.$#"
|
||||
count: 2
|
||||
path: src/Matchers/XmlBodyMatcher.php
|
||||
|
||||
-
|
||||
message: "#^Unsafe call to private method Pock\\\\Matchers\\\\XmlBodyMatcher\\:\\:getDOMString\\(\\) through static\\:\\:\\.$#"
|
||||
count: 1
|
||||
path: src/Matchers/XmlBodyMatcher.php
|
||||
|
||||
-
|
||||
message: "#^Unsafe call to private method Pock\\\\Matchers\\\\XmlBodyMatcher\\:\\:getSorter\\(\\) through static\\:\\:\\.$#"
|
||||
count: 1
|
||||
path: src/Matchers/XmlBodyMatcher.php
|
||||
|
||||
-
|
||||
message: "#^Unsafe call to private method Pock\\\\Matchers\\\\XmlBodyMatcher\\:\\:hasExtension\\(\\) through static\\:\\:\\.$#"
|
||||
count: 4
|
||||
path: src/Matchers/XmlBodyMatcher.php
|
||||
|
||||
-
|
||||
message: "#^Unsafe call to private method Pock\\\\Matchers\\\\XmlBodyMatcher\\:\\:sortXmlTags\\(\\) through static\\:\\:\\.$#"
|
||||
count: 2
|
||||
path: src/Matchers/XmlBodyMatcher.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$data of class Pock\\\\Matchers\\\\JsonBodyMatcher constructor expects array, mixed given\\.$#"
|
||||
count: 2
|
||||
path: src/PockBuilder.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$depth of function json_decode expects int\\<1, max\\>, int given\\.$#"
|
||||
count: 1
|
||||
path: src/PockBuilder.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$depth of function json_encode expects int\\<1, max\\>, int given\\.$#"
|
||||
count: 1
|
||||
path: src/PockBuilder.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$depth of function json_encode expects int\\<1, max\\>, int given\\.$#"
|
||||
count: 1
|
||||
path: src/PockResponseBuilder.php
|
|
@ -1,3 +1,6 @@
|
|||
includes:
|
||||
- phpstan-baseline.neon # TODO: Configure new phpstan
|
||||
|
||||
parameters:
|
||||
level: max
|
||||
paths:
|
||||
|
|
|
@ -74,17 +74,31 @@ class Client implements ClientInterface, HttpClient, HttpAsyncClient
|
|||
continue;
|
||||
}
|
||||
|
||||
if ($mock->getMatcher()->matches($request)) {
|
||||
if ($mock->matches($request)) {
|
||||
if (null !== $mock->getResponse()) {
|
||||
$mock->registerHit();
|
||||
|
||||
return new HttpFulfilledPromise($mock->getResponse());
|
||||
}
|
||||
|
||||
if (null !== $mock->getThrowable()) {
|
||||
if (null !== $mock->getReplyFactory()) {
|
||||
$mock->registerHit();
|
||||
|
||||
return new HttpRejectedPromise($mock->getThrowable());
|
||||
try {
|
||||
return new HttpFulfilledPromise(
|
||||
$mock->getReplyFactory()->createReply($request, new PockResponseBuilder())
|
||||
);
|
||||
} catch (Throwable $throwable) {
|
||||
return new HttpRejectedPromise($throwable);
|
||||
}
|
||||
}
|
||||
|
||||
$throwable = $mock->getThrowable($request);
|
||||
|
||||
if (null !== $throwable) {
|
||||
$mock->registerHit();
|
||||
|
||||
return new HttpRejectedPromise($throwable);
|
||||
}
|
||||
|
||||
throw new IncompleteMockException($mock);
|
||||
|
@ -92,13 +106,23 @@ class Client implements ClientInterface, HttpClient, HttpAsyncClient
|
|||
}
|
||||
|
||||
if (null !== $this->fallbackClient) {
|
||||
try {
|
||||
return new HttpFulfilledPromise($this->fallbackClient->sendRequest($request));
|
||||
} catch (Throwable $throwable) {
|
||||
return new HttpRejectedPromise($throwable);
|
||||
}
|
||||
return $this->replyWithFallbackClient($request);
|
||||
}
|
||||
|
||||
throw new UnsupportedRequestException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Http\Message\RequestInterface $request
|
||||
*
|
||||
* @return \Http\Promise\Promise
|
||||
*/
|
||||
protected function replyWithFallbackClient(RequestInterface $request): Promise
|
||||
{
|
||||
try {
|
||||
return new HttpFulfilledPromise($this->fallbackClient->sendRequest($request)); // @phpstan-ignore-line
|
||||
} catch (Throwable $throwable) {
|
||||
return new HttpRejectedPromise($throwable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
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;
|
||||
}
|
||||
}
|
68
src/Comparator/RecursiveArrayComparator.php
Normal file
68
src/Comparator/RecursiveArrayComparator.php
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?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)) {
|
||||
if (!is_array($second[$key]) || !self::recursiveCompareArrays($value, $second[$key])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($value !== $second[$key]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
65
src/Comparator/RecursiveLtrArrayComparator.php
Normal file
65
src/Comparator/RecursiveLtrArrayComparator.php
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?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)) {
|
||||
if (!is_array($haystack[$key]) || !self::recursiveCompareArrays($value, $haystack[$key])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@
|
|||
namespace Pock\Creator;
|
||||
|
||||
use Throwable;
|
||||
use Pock\Serializer\JmsSerializerDecorator;
|
||||
use Pock\Serializer\JmsSerializerAdapter;
|
||||
use Pock\Serializer\SerializerInterface;
|
||||
|
||||
/**
|
||||
|
@ -30,13 +30,13 @@ abstract class AbstractJmsSerializerCreator implements SerializerCreatorInterfac
|
|||
{
|
||||
if (
|
||||
class_exists(self::BUILDER_CLASS) &&
|
||||
method_exists(self::BUILDER_CLASS, 'create')
|
||||
method_exists(self::BUILDER_CLASS, 'create') // @phpstan-ignore-line
|
||||
) {
|
||||
try {
|
||||
$builder = call_user_func([self::BUILDER_CLASS, 'create']);
|
||||
$builder = call_user_func([self::BUILDER_CLASS, 'create']); // @phpstan-ignore-line
|
||||
|
||||
if (null !== $builder && method_exists($builder, 'build')) {
|
||||
return new JmsSerializerDecorator($builder->build(), static::getFormat()); // @phpstan-ignore-line
|
||||
if (null !== $builder && method_exists($builder, 'build')) { // @phpstan-ignore-line
|
||||
return new JmsSerializerAdapter($builder->build(), static::getFormat()); // @phpstan-ignore-line
|
||||
}
|
||||
} catch (Throwable $throwable) {
|
||||
return null;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
namespace Pock\Creator;
|
||||
|
||||
use Pock\Serializer\SerializerInterface;
|
||||
use Pock\Serializer\SymfonySerializerDecorator;
|
||||
use Pock\Serializer\SymfonySerializerAdapter;
|
||||
|
||||
/**
|
||||
* Class AbstractSymfonySerializerCreator
|
||||
|
@ -33,8 +33,8 @@ abstract class AbstractSymfonySerializerCreator implements SerializerCreatorInte
|
|||
$normalizer = self::OBJECT_NORMALIZER_CLASS;
|
||||
$encoder = static::getEncoderClass();
|
||||
|
||||
return new SymfonySerializerDecorator(
|
||||
new $serializer([new $normalizer()], [new $encoder()]),
|
||||
return new SymfonySerializerAdapter(
|
||||
new $serializer([new $normalizer()], [new $encoder()]), // @phpstan-ignore-line
|
||||
static::getFormat()
|
||||
);
|
||||
}
|
||||
|
|
45
src/Exception/AbstractRequestAwareException.php
Normal file
45
src/Exception/AbstractRequestAwareException.php
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category AbstractRequestAwareException
|
||||
* @package Pock\Exception
|
||||
*/
|
||||
|
||||
namespace Pock\Exception;
|
||||
|
||||
use Exception;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Class AbstractRequestAwareException
|
||||
*
|
||||
* @category AbstractRequestAwareException
|
||||
* @package Pock\Exception
|
||||
*/
|
||||
class AbstractRequestAwareException extends Exception
|
||||
{
|
||||
/** @var RequestInterface */
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* @param \Psr\Http\Message\RequestInterface $request
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setRequest(RequestInterface $request): self
|
||||
{
|
||||
$instance = new static($this->message, $this->code, $this->getPrevious()); // @phpstan-ignore-line
|
||||
$instance->request = $request;
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Psr\Http\Message\RequestInterface
|
||||
*/
|
||||
public function getRequest(): RequestInterface
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
}
|
23
src/Exception/PockClientException.php
Normal file
23
src/Exception/PockClientException.php
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category PockClientException
|
||||
* @package Pock\Exception
|
||||
*/
|
||||
|
||||
namespace Pock\Exception;
|
||||
|
||||
use Exception;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
|
||||
/**
|
||||
* Class PockClientException
|
||||
*
|
||||
* @category PockClientException
|
||||
* @package Pock\Exception
|
||||
*/
|
||||
class PockClientException extends Exception implements ClientExceptionInterface
|
||||
{
|
||||
}
|
22
src/Exception/PockNetworkException.php
Normal file
22
src/Exception/PockNetworkException.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category PockNetworkException
|
||||
* @package Pock\Exception
|
||||
*/
|
||||
|
||||
namespace Pock\Exception;
|
||||
|
||||
use Psr\Http\Client\NetworkExceptionInterface;
|
||||
|
||||
/**
|
||||
* Class PockNetworkException
|
||||
*
|
||||
* @category PockNetworkException
|
||||
* @package Pock\Exception
|
||||
*/
|
||||
class PockNetworkException extends AbstractRequestAwareException implements NetworkExceptionInterface
|
||||
{
|
||||
}
|
22
src/Exception/PockRequestException.php
Normal file
22
src/Exception/PockRequestException.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category PockRequestException
|
||||
* @package Pock\Exception
|
||||
*/
|
||||
|
||||
namespace Pock\Exception;
|
||||
|
||||
use Psr\Http\Client\RequestExceptionInterface;
|
||||
|
||||
/**
|
||||
* Class PockRequestException
|
||||
*
|
||||
* @category PockRequestException
|
||||
* @package Pock\Exception
|
||||
*/
|
||||
class PockRequestException extends AbstractRequestAwareException implements RequestExceptionInterface
|
||||
{
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.2
|
||||
*
|
||||
* @category UniversalMockException
|
||||
* @package Pock\Exception
|
||||
*/
|
||||
|
||||
namespace Pock\Exception;
|
||||
|
||||
use Exception;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Psr\Http\Client\NetworkExceptionInterface;
|
||||
use Psr\Http\Client\RequestExceptionInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Class UniversalMockException
|
||||
*
|
||||
* @category UniversalMockException
|
||||
* @package Pock\Exception
|
||||
*/
|
||||
class UniversalMockException extends Exception implements
|
||||
ClientExceptionInterface,
|
||||
NetworkExceptionInterface,
|
||||
RequestExceptionInterface
|
||||
{
|
||||
/** @var mixed */
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* UniversalMockException constructor.
|
||||
*
|
||||
* @param mixed $request
|
||||
*/
|
||||
public function __construct($request)
|
||||
{
|
||||
parent::__construct('Default mock exception');
|
||||
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getRequest(): RequestInterface
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
}
|
44
src/Factory/CallbackReplyFactory.php
Normal file
44
src/Factory/CallbackReplyFactory.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category CallbackReplyFactory
|
||||
* @package Pock\Factory
|
||||
*/
|
||||
|
||||
namespace Pock\Factory;
|
||||
|
||||
use Pock\PockResponseBuilder;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Class CallbackReplyFactory
|
||||
*
|
||||
* @category CallbackReplyFactory
|
||||
* @package Pock\Factory
|
||||
*/
|
||||
class CallbackReplyFactory implements ReplyFactoryInterface
|
||||
{
|
||||
/** @var callable */
|
||||
private $callback;
|
||||
|
||||
/**
|
||||
* CallbackReplyFactory constructor.
|
||||
*
|
||||
* @param callable $callback
|
||||
*/
|
||||
public function __construct(callable $callback)
|
||||
{
|
||||
$this->callback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function createReply(RequestInterface $request, PockResponseBuilder $responseBuilder): ResponseInterface
|
||||
{
|
||||
return call_user_func($this->callback, $request, $responseBuilder);
|
||||
}
|
||||
}
|
38
src/Factory/ReplyFactoryInterface.php
Normal file
38
src/Factory/ReplyFactoryInterface.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category ReplyFactoryInterface
|
||||
* @package Pock\Factory
|
||||
*/
|
||||
|
||||
namespace Pock\Factory;
|
||||
|
||||
use Pock\PockResponseBuilder;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Interface ReplyFactoryInterface
|
||||
*
|
||||
* @category ReplyFactoryInterface
|
||||
* @package Pock\Factory
|
||||
*/
|
||||
interface ReplyFactoryInterface
|
||||
{
|
||||
/**
|
||||
* Reply to the specified request.
|
||||
*
|
||||
* If this method throws any exception, it will be treated as with the `PockBuilder::throwException call`.
|
||||
*
|
||||
* @see \Pock\PockBuilder::throwException()
|
||||
*
|
||||
* @param \Psr\Http\Message\RequestInterface $request
|
||||
* @param \Pock\PockResponseBuilder $responseBuilder
|
||||
*
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function createReply(RequestInterface $request, PockResponseBuilder $responseBuilder): ResponseInterface;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
41
src/Matchers/AbstractRegExpMatcher.php
Normal file
41
src/Matchers/AbstractRegExpMatcher.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category AbstractRegExpMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Matchers;
|
||||
|
||||
/**
|
||||
* Class AbstractRegExpMatcher
|
||||
*
|
||||
* @category AbstractRegExpMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
abstract class AbstractRegExpMatcher implements RequestMatcherInterface
|
||||
{
|
||||
/** @var string */
|
||||
protected $expression;
|
||||
|
||||
/** @var int */
|
||||
protected $flags = 0;
|
||||
|
||||
/**
|
||||
* @param string $expression
|
||||
* @param int $flags
|
||||
*/
|
||||
public function __construct(string $expression, int $flags = 0)
|
||||
{
|
||||
$this->expression = $expression;
|
||||
$this->flags = $flags;
|
||||
}
|
||||
|
||||
protected function matchRegExp(string $content): bool
|
||||
{
|
||||
$matches = [];
|
||||
return 1 === preg_match($this->expression, $content, $matches, $this->flags);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
namespace Pock\Matchers;
|
||||
|
||||
use Pock\Comparator\ComparatorLocator;
|
||||
use Pock\Comparator\RecursiveArrayComparator;
|
||||
use Pock\Traits\SeekableStreamDataExtractor;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
|
@ -18,7 +20,7 @@ use Psr\Http\Message\RequestInterface;
|
|||
* @category AbstractSerializedBodyMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
abstract class AbstractSerializedBodyMatcher extends AbstractArrayPoweredComponent implements RequestMatcherInterface
|
||||
abstract class AbstractSerializedBodyMatcher implements RequestMatcherInterface
|
||||
{
|
||||
use SeekableStreamDataExtractor;
|
||||
|
||||
|
@ -54,7 +56,7 @@ abstract class AbstractSerializedBodyMatcher extends AbstractArrayPoweredCompone
|
|||
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)
|
||||
{
|
||||
if (is_string($contents)) {
|
||||
$this->contents = $contents;
|
||||
}
|
||||
|
||||
if ($contents instanceof StreamInterface) {
|
||||
$this->contents = static::getStreamData($contents);
|
||||
}
|
||||
|
||||
if (is_resource($contents)) {
|
||||
$this->contents = static::readAllResource($contents);
|
||||
}
|
||||
$this->contents = static::getEntryItemData($contents);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -70,4 +60,26 @@ class BodyMatcher implements RequestMatcherInterface
|
|||
fseek($resource, 0);
|
||||
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);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
|
40
src/Matchers/ExactFormDataMatcher.php
Normal file
40
src/Matchers/ExactFormDataMatcher.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category ExactFormDataMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Matchers;
|
||||
|
||||
use Pock\Comparator\ComparatorLocator;
|
||||
use Pock\Comparator\RecursiveArrayComparator;
|
||||
use Pock\Traits\SeekableStreamDataExtractor;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Class ExactFormDataMatcher
|
||||
*
|
||||
* @category ExactFormDataMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
class ExactFormDataMatcher extends QueryMatcher
|
||||
{
|
||||
use SeekableStreamDataExtractor;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function matches(RequestInterface $request): bool
|
||||
{
|
||||
$query = static::parseQuery(static::getStreamData($request->getBody()));
|
||||
|
||||
if (empty($query)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ComparatorLocator::get(RecursiveArrayComparator::class)->compare($this->query, $query);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
namespace Pock\Matchers;
|
||||
|
||||
use Pock\Comparator\ComparatorLocator;
|
||||
use Pock\Comparator\ScalarFlatArrayComparator;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
|
@ -28,6 +30,7 @@ class ExactHeaderMatcher extends HeaderMatcher
|
|||
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;
|
||||
|
||||
use Pock\Comparator\ComparatorLocator;
|
||||
use Pock\Comparator\RecursiveArrayComparator;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
|
@ -30,6 +32,6 @@ class ExactQueryMatcher extends QueryMatcher
|
|||
return false;
|
||||
}
|
||||
|
||||
return self::recursiveCompareArrays($this->query, $query);
|
||||
return ComparatorLocator::get(RecursiveArrayComparator::class)->compare($this->query, $query);
|
||||
}
|
||||
}
|
||||
|
|
40
src/Matchers/FormDataMatcher.php
Normal file
40
src/Matchers/FormDataMatcher.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category FormDataMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Matchers;
|
||||
|
||||
use Pock\Comparator\ComparatorLocator;
|
||||
use Pock\Comparator\RecursiveArrayComparator;
|
||||
use Pock\Traits\SeekableStreamDataExtractor;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Class FormDataMatcher
|
||||
*
|
||||
* @category FormDataMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
class FormDataMatcher extends QueryMatcher
|
||||
{
|
||||
use SeekableStreamDataExtractor;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function matches(RequestInterface $request): bool
|
||||
{
|
||||
$query = static::parseQuery(static::getStreamData($request->getBody()));
|
||||
|
||||
if (empty($query)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ComparatorLocator::get(RecursiveArrayComparator::class)->compare($this->query, $query);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
namespace Pock\Matchers;
|
||||
|
||||
use Pock\Comparator\ComparatorLocator;
|
||||
use Pock\Comparator\LtrScalarArrayComparator;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
|
@ -17,7 +19,7 @@ use Psr\Http\Message\RequestInterface;
|
|||
* @category HeaderMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
class HeaderMatcher extends AbstractArrayPoweredComponent implements RequestMatcherInterface
|
||||
class HeaderMatcher implements RequestMatcherInterface
|
||||
{
|
||||
/** @var string */
|
||||
protected $header;
|
||||
|
@ -51,6 +53,7 @@ class HeaderMatcher extends AbstractArrayPoweredComponent implements RequestMatc
|
|||
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;
|
||||
|
||||
use Pock\Comparator\ComparatorLocator;
|
||||
use Pock\Comparator\LtrScalarArrayComparator;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
|
@ -17,7 +19,7 @@ use Psr\Http\Message\RequestInterface;
|
|||
* @category HeadersMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
class HeadersMatcher extends AbstractArrayPoweredComponent implements RequestMatcherInterface
|
||||
class HeadersMatcher implements RequestMatcherInterface
|
||||
{
|
||||
/** @var array<string, string|string[]> */
|
||||
protected $headers;
|
||||
|
@ -48,7 +50,7 @@ class HeadersMatcher extends AbstractArrayPoweredComponent implements RequestMat
|
|||
$value = [$value];
|
||||
}
|
||||
|
||||
if (!static::isNeedlePresentInHaystack($value, $request->getHeader($name))) {
|
||||
if (!ComparatorLocator::get(LtrScalarArrayComparator::class)->compare($value, $request->getHeader($name))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
57
src/Matchers/MultipartFormDataMatcher.php
Normal file
57
src/Matchers/MultipartFormDataMatcher.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP version 7.1
|
||||
*
|
||||
* @category MultipartFormDataMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Matchers;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Pock\Traits\SeekableStreamDataExtractor;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Riverline\MultiPartParser\StreamedPart;
|
||||
use Riverline\MultiPartParser\Converters\PSR7;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Class MultipartFormDataMatcher
|
||||
*
|
||||
* @category MultipartFormDataMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
class MultipartFormDataMatcher implements RequestMatcherInterface
|
||||
{
|
||||
use SeekableStreamDataExtractor;
|
||||
|
||||
/** @var callable */
|
||||
private $callback;
|
||||
|
||||
/**
|
||||
* MultipartFormDataMatcher constructor.
|
||||
*
|
||||
* @param callable $callback Accepts Riverline\MultiPartParser\StreamedPart as an argument, returns true if matched.
|
||||
*/
|
||||
public function __construct(callable $callback)
|
||||
{
|
||||
$this->callback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @SuppressWarnings(PHPMD.StaticAccess)
|
||||
*/
|
||||
public function matches(RequestInterface $request): bool
|
||||
{
|
||||
try {
|
||||
$part = PSR7::convert($request);
|
||||
$request->getBody()->rewind();
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return call_user_func($this->callback, $part);
|
||||
}
|
||||
}
|
56
src/Matchers/PortMatcher.php
Normal file
56
src/Matchers/PortMatcher.php
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category PortMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Matchers;
|
||||
|
||||
use Pock\Enum\RequestScheme;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Class PortMatcher
|
||||
*
|
||||
* @category PortMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
class PortMatcher implements RequestMatcherInterface
|
||||
{
|
||||
/** @var int */
|
||||
protected $port;
|
||||
|
||||
/**
|
||||
* PortMatcher constructor.
|
||||
*
|
||||
* @param int $port
|
||||
*/
|
||||
public function __construct(int $port)
|
||||
{
|
||||
$this->port = $port;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function matches(RequestInterface $request): bool
|
||||
{
|
||||
$port = $request->getUri()->getPort();
|
||||
|
||||
if (null === $port) {
|
||||
switch ($request->getUri()->getScheme()) {
|
||||
case RequestScheme::HTTP:
|
||||
return 80 === $this->port;
|
||||
case RequestScheme::HTTPS:
|
||||
return 443 === $this->port;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $port === $this->port;
|
||||
}
|
||||
}
|
|
@ -9,8 +9,9 @@
|
|||
|
||||
namespace Pock\Matchers;
|
||||
|
||||
use Pock\Comparator\ComparatorLocator;
|
||||
use Pock\Comparator\RecursiveLtrArrayComparator;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
/**
|
||||
* Class QueryMatcher
|
||||
|
@ -18,7 +19,7 @@ use Psr\Http\Message\UriInterface;
|
|||
* @category QueryMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
class QueryMatcher extends AbstractArrayPoweredComponent implements RequestMatcherInterface
|
||||
class QueryMatcher implements RequestMatcherInterface
|
||||
{
|
||||
/** @var array<string, mixed> */
|
||||
protected $query;
|
||||
|
@ -44,7 +45,7 @@ class QueryMatcher extends AbstractArrayPoweredComponent implements RequestMatch
|
|||
return false;
|
||||
}
|
||||
|
||||
return self::isNeedlePresentInHaystack($this->query, $query);
|
||||
return ComparatorLocator::get(RecursiveLtrArrayComparator::class)->compare($this->query, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
32
src/Matchers/RegExpBodyMatcher.php
Normal file
32
src/Matchers/RegExpBodyMatcher.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category RegExpBodyMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Matchers;
|
||||
|
||||
use Pock\Traits\SeekableStreamDataExtractor;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Class RegExpBodyMatcher
|
||||
*
|
||||
* @category RegExpBodyMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
class RegExpBodyMatcher extends AbstractRegExpMatcher
|
||||
{
|
||||
use SeekableStreamDataExtractor;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function matches(RequestInterface $request): bool
|
||||
{
|
||||
return $this->matchRegExp(static::getStreamData($request->getBody()));
|
||||
}
|
||||
}
|
29
src/Matchers/RegExpPathMatcher.php
Normal file
29
src/Matchers/RegExpPathMatcher.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category RegExpPathMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Matchers;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Class RegExpPathMatcher
|
||||
*
|
||||
* @category RegExpPathMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
class RegExpPathMatcher extends AbstractRegExpMatcher
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function matches(RequestInterface $request): bool
|
||||
{
|
||||
return $this->matchRegExp($request->getUri()->getPath());
|
||||
}
|
||||
}
|
29
src/Matchers/RegExpQueryMatcher.php
Normal file
29
src/Matchers/RegExpQueryMatcher.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category RegExpQueryMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Matchers;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Class RegExpQueryMatcher
|
||||
*
|
||||
* @category RegExpQueryMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
class RegExpQueryMatcher extends AbstractRegExpMatcher
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function matches(RequestInterface $request): bool
|
||||
{
|
||||
return $this->matchRegExp($request->getUri()->getQuery());
|
||||
}
|
||||
}
|
29
src/Matchers/RegExpUriMatcher.php
Normal file
29
src/Matchers/RegExpUriMatcher.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category RegExpUriMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Matchers;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Class RegExpUriMatcher
|
||||
*
|
||||
* @category RegExpUriMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
class RegExpUriMatcher extends AbstractRegExpMatcher
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function matches(RequestInterface $request): bool
|
||||
{
|
||||
return $this->matchRegExp((string) $request->getUri());
|
||||
}
|
||||
}
|
210
src/Matchers/XmlBodyMatcher.php
Normal file
210
src/Matchers/XmlBodyMatcher.php
Normal file
|
@ -0,0 +1,210 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category XmlBodyMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Matchers;
|
||||
|
||||
use DOMDocument;
|
||||
use Pock\Exception\XmlException;
|
||||
use Pock\Traits\SeekableStreamDataExtractor;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use RuntimeException;
|
||||
use XSLTProcessor;
|
||||
|
||||
/**
|
||||
* 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 bool */
|
||||
public static $forceTextComparison = false;
|
||||
|
||||
/** @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 (!static::hasExtension('xsl') || !static::hasExtension('dom')) {
|
||||
$this->useFallback = true;
|
||||
}
|
||||
|
||||
if (!static::hasExtension('xsl')) {
|
||||
$this->useFallback = true;
|
||||
|
||||
if (static::hasExtension('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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $extension
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function hasExtension(string $extension): bool
|
||||
{
|
||||
if (static::$forceTextComparison && 'xsl' === $extension) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return extension_loaded($extension);
|
||||
}
|
||||
}
|
61
src/Mock.php
61
src/Mock.php
|
@ -9,7 +9,13 @@
|
|||
|
||||
namespace Pock;
|
||||
|
||||
use Pock\Exception\PockNetworkException;
|
||||
use Pock\Exception\PockRequestException;
|
||||
use Pock\Factory\ReplyFactoryInterface;
|
||||
use Pock\Matchers\RequestMatcherInterface;
|
||||
use Psr\Http\Client\NetworkExceptionInterface;
|
||||
use Psr\Http\Client\RequestExceptionInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Throwable;
|
||||
|
||||
|
@ -24,6 +30,9 @@ class Mock implements MockInterface
|
|||
/** @var \Pock\Matchers\RequestMatcherInterface */
|
||||
private $matcher;
|
||||
|
||||
/** @var \Pock\Factory\ReplyFactoryInterface|null */
|
||||
private $replyFactory;
|
||||
|
||||
/** @var \Psr\Http\Message\ResponseInterface|null */
|
||||
private $response;
|
||||
|
||||
|
@ -36,25 +45,38 @@ class Mock implements MockInterface
|
|||
/** @var int */
|
||||
private $maxHits;
|
||||
|
||||
/** @var int */
|
||||
private $matchAt;
|
||||
|
||||
/**
|
||||
* Mock constructor.
|
||||
*
|
||||
* @param \Pock\Matchers\RequestMatcherInterface $matcher
|
||||
* @param \Pock\Factory\ReplyFactoryInterface|null $replyFactory
|
||||
* @param \Psr\Http\Message\ResponseInterface|null $response
|
||||
* @param \Throwable|null $throwable
|
||||
* @param int $maxHits
|
||||
* @param int $matchAt
|
||||
*/
|
||||
public function __construct(
|
||||
RequestMatcherInterface $matcher,
|
||||
?ReplyFactoryInterface $replyFactory,
|
||||
?ResponseInterface $response,
|
||||
?Throwable $throwable,
|
||||
int $maxHits
|
||||
int $maxHits,
|
||||
int $matchAt
|
||||
) {
|
||||
$this->matcher = $matcher;
|
||||
$this->replyFactory = $replyFactory;
|
||||
$this->response = $response;
|
||||
$this->throwable = $throwable;
|
||||
$this->matchAt = $matchAt;
|
||||
$this->maxHits = $maxHits;
|
||||
$this->hits = 0;
|
||||
|
||||
if ($this->maxHits < ($matchAt + 1) && -1 !== $this->maxHits) {
|
||||
$this->maxHits = $matchAt + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -62,7 +84,10 @@ class Mock implements MockInterface
|
|||
*/
|
||||
public function registerHit(): MockInterface
|
||||
{
|
||||
++$this->hits;
|
||||
if (-1 !== $this->maxHits) {
|
||||
++$this->hits;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -71,15 +96,27 @@ class Mock implements MockInterface
|
|||
*/
|
||||
public function available(): bool
|
||||
{
|
||||
return $this->hits < $this->maxHits;
|
||||
return -1 === $this->maxHits || $this->hits < $this->maxHits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getMatcher(): RequestMatcherInterface
|
||||
public function matches(RequestInterface $request): bool
|
||||
{
|
||||
return $this->matcher;
|
||||
if ($this->matcher->matches($request)) {
|
||||
if ($this->matchAt <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->matchAt === $this->hits) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->registerHit();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,8 +138,20 @@ class Mock implements MockInterface
|
|||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getThrowable(): ?Throwable
|
||||
public function getReplyFactory(): ?ReplyFactoryInterface
|
||||
{
|
||||
return $this->replyFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getThrowable(RequestInterface $request): ?Throwable
|
||||
{
|
||||
if ($this->throwable instanceof PockRequestException || $this->throwable instanceof PockNetworkException) {
|
||||
return $this->throwable->setRequest($request);
|
||||
}
|
||||
|
||||
return $this->throwable;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,9 @@
|
|||
|
||||
namespace Pock;
|
||||
|
||||
use Pock\Factory\ReplyFactoryInterface;
|
||||
use Pock\Matchers\RequestMatcherInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Throwable;
|
||||
|
||||
|
@ -36,11 +38,14 @@ interface MockInterface
|
|||
public function available(): bool;
|
||||
|
||||
/**
|
||||
* Returns matcher for the request.
|
||||
* Returns true if underlying matcher has matched provided request.
|
||||
* It also returns false if matcher has matched request but hits condition is not met yet.
|
||||
*
|
||||
* @return \Pock\Matchers\RequestMatcherInterface
|
||||
* @param \Psr\Http\Message\RequestInterface $request
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getMatcher(): RequestMatcherInterface;
|
||||
public function matches(RequestInterface $request): bool;
|
||||
|
||||
/**
|
||||
* Returns response which should be used as mock data.
|
||||
|
@ -49,10 +54,19 @@ interface MockInterface
|
|||
*/
|
||||
public function getResponse(): ?ResponseInterface;
|
||||
|
||||
/**
|
||||
* Returns reply factory which should be used to form the mocked response.
|
||||
*
|
||||
* @return \Pock\Factory\ReplyFactoryInterface|null
|
||||
*/
|
||||
public function getReplyFactory(): ?ReplyFactoryInterface;
|
||||
|
||||
/**
|
||||
* Returns the throwable which will be thrown as mock data.
|
||||
*
|
||||
* @param \Psr\Http\Message\RequestInterface $request This request may be set into exception if possible
|
||||
*
|
||||
* @return \Throwable|null
|
||||
*/
|
||||
public function getThrowable(): ?Throwable;
|
||||
public function getThrowable(RequestInterface $request): ?Throwable;
|
||||
}
|
||||
|
|
|
@ -9,15 +9,20 @@
|
|||
|
||||
namespace Pock;
|
||||
|
||||
use Diff\ArrayComparer\StrictArrayComparer;
|
||||
use Pock\Enum\RequestMethod;
|
||||
use Pock\Enum\RequestScheme;
|
||||
use DOMDocument;
|
||||
use Pock\Exception\PockClientException;
|
||||
use Pock\Exception\PockNetworkException;
|
||||
use Pock\Exception\PockRequestException;
|
||||
use Pock\Factory\CallbackReplyFactory;
|
||||
use Pock\Factory\ReplyFactoryInterface;
|
||||
use Pock\Matchers\AnyRequestMatcher;
|
||||
use Pock\Matchers\BodyMatcher;
|
||||
use Pock\Matchers\CallbackRequestMatcher;
|
||||
use Pock\Matchers\ExactFormDataMatcher;
|
||||
use Pock\Matchers\ExactHeaderMatcher;
|
||||
use Pock\Matchers\ExactHeadersMatcher;
|
||||
use Pock\Matchers\ExactQueryMatcher;
|
||||
use Pock\Matchers\FormDataMatcher;
|
||||
use Pock\Matchers\HeaderLineMatcher;
|
||||
use Pock\Matchers\HeaderLineRegexpMatcher;
|
||||
use Pock\Matchers\HeaderMatcher;
|
||||
|
@ -25,15 +30,28 @@ use Pock\Matchers\HeadersMatcher;
|
|||
use Pock\Matchers\HostMatcher;
|
||||
use Pock\Matchers\JsonBodyMatcher;
|
||||
use Pock\Matchers\MethodMatcher;
|
||||
use Pock\Matchers\MultipartFormDataMatcher;
|
||||
use Pock\Matchers\MultipleMatcher;
|
||||
use Pock\Matchers\PathMatcher;
|
||||
use Pock\Matchers\PortMatcher;
|
||||
use Pock\Matchers\QueryMatcher;
|
||||
use Pock\Matchers\RegExpBodyMatcher;
|
||||
use Pock\Matchers\RegExpPathMatcher;
|
||||
use Pock\Matchers\RegExpQueryMatcher;
|
||||
use Pock\Matchers\RegExpUriMatcher;
|
||||
use Pock\Matchers\RequestMatcherInterface;
|
||||
use Pock\Matchers\SchemeMatcher;
|
||||
use Pock\Matchers\UriMatcher;
|
||||
use Pock\Matchers\XmlBodyMatcher;
|
||||
use Pock\Traits\JsonDecoderTrait;
|
||||
use Pock\Traits\JsonSerializerAwareTrait;
|
||||
use Pock\Traits\XmlSerializerAwareTrait;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\HttpClient\Psr18Client;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
|
@ -44,11 +62,15 @@ use Throwable;
|
|||
*
|
||||
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
|
||||
* @SuppressWarnings(PHPMD.TooManyPublicMethods)
|
||||
* @SuppressWarnings(PHPMD.TooManyMethods)
|
||||
* @SuppressWarnings(PHPMD.ExcessivePublicCount)
|
||||
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
|
||||
*/
|
||||
class PockBuilder
|
||||
{
|
||||
use JsonDecoderTrait;
|
||||
use JsonSerializerAwareTrait;
|
||||
use XmlSerializerAwareTrait;
|
||||
|
||||
/** @var \Pock\Matchers\MultipleMatcher */
|
||||
private $matcher;
|
||||
|
@ -56,12 +78,18 @@ class PockBuilder
|
|||
/** @var \Pock\PockResponseBuilder|null */
|
||||
private $responseBuilder;
|
||||
|
||||
/** @var ReplyFactoryInterface|null */
|
||||
private $replyFactory;
|
||||
|
||||
/** @var \Throwable|null */
|
||||
private $throwable;
|
||||
|
||||
/** @var int */
|
||||
private $maxHits;
|
||||
|
||||
/** @var int */
|
||||
private $matchAt;
|
||||
|
||||
/** @var \Pock\MockInterface[] */
|
||||
private $mocks;
|
||||
|
||||
|
@ -112,6 +140,49 @@ class PockBuilder
|
|||
return $this->addMatcher(new HostMatcher($host));
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches request by the port.
|
||||
*
|
||||
* @param int $port
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function matchPort(int $port): self
|
||||
{
|
||||
return $this->addMatcher(new PortMatcher($port));
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches request by origin.
|
||||
*
|
||||
* @param string $origin
|
||||
*
|
||||
* @return self
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function matchOrigin(string $origin): self
|
||||
{
|
||||
$parsed = parse_url($origin);
|
||||
|
||||
if (!is_array($parsed)) {
|
||||
throw new RuntimeException('Malformed origin: ' . $origin);
|
||||
}
|
||||
|
||||
if (array_key_exists('scheme', $parsed) && !empty($parsed['scheme'])) {
|
||||
$this->matchScheme($parsed['scheme']);
|
||||
}
|
||||
|
||||
if (array_key_exists('host', $parsed) && !empty($parsed['host'])) {
|
||||
$this->matchHost($parsed['host']);
|
||||
}
|
||||
|
||||
if (array_key_exists('port', $parsed) && is_int($parsed['port']) && $parsed['port'] > 0) {
|
||||
$this->matchPort($parsed['port']);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches request by the whole URI.
|
||||
*
|
||||
|
@ -124,6 +195,19 @@ class PockBuilder
|
|||
return $this->addMatcher(new UriMatcher($uri));
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches request by the whole URI using regular expression.
|
||||
*
|
||||
* @param string $expression
|
||||
* @param int $flags
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function matchUriRegExp(string $expression, int $flags = 0): self
|
||||
{
|
||||
return $this->addMatcher(new RegExpUriMatcher($expression, $flags));
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches request by header value or several values. Header can have other values which are not specified here.
|
||||
* @see PockBuilder::matchExactHeader() if you want to match exact header values.
|
||||
|
@ -217,6 +301,20 @@ class PockBuilder
|
|||
return $this->addMatcher(new PathMatcher($path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Match request by its path using regular expression. This matcher doesn't care about prefix slash
|
||||
* since it's pretty easy to do it using regular expression.
|
||||
*
|
||||
* @param string $expression
|
||||
* @param int $flags
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function matchPathRegExp(string $expression, int $flags = 0): self
|
||||
{
|
||||
return $this->addMatcher(new RegExpPathMatcher($expression, $flags));
|
||||
}
|
||||
|
||||
/**
|
||||
* Match request by its query. Request can contain other query variables.
|
||||
* @see PockBuilder::matchExactQuery() if you want to match an entire query string.
|
||||
|
@ -230,6 +328,19 @@ class PockBuilder
|
|||
return $this->addMatcher(new QueryMatcher($query));
|
||||
}
|
||||
|
||||
/**
|
||||
* Match request by its query using regular expression.
|
||||
*
|
||||
* @param string $expression
|
||||
* @param int $flags
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function matchQueryRegExp(string $expression, int $flags = 0): self
|
||||
{
|
||||
return $this->addMatcher(new RegExpQueryMatcher($expression, $flags));
|
||||
}
|
||||
|
||||
/**
|
||||
* Match request by its query. Additional query parameters aren't allowed.
|
||||
*
|
||||
|
@ -242,6 +353,44 @@ class PockBuilder
|
|||
return $this->addMatcher(new ExactQueryMatcher($query));
|
||||
}
|
||||
|
||||
/**
|
||||
* Match request with form-data.
|
||||
*
|
||||
* @param array<string, mixed> $formFields
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function matchFormData(array $formFields): self
|
||||
{
|
||||
return $this->addMatcher(new FormDataMatcher($formFields));
|
||||
}
|
||||
|
||||
/**
|
||||
* Match request with form-data. Additional fields aren't allowed.
|
||||
*
|
||||
* @param array<string, mixed> $formFields
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function matchExactFormData(array $formFields): self
|
||||
{
|
||||
return $this->addMatcher(new ExactFormDataMatcher($formFields));
|
||||
}
|
||||
|
||||
/**
|
||||
* Match request multipart form data. Will not match the request if body is not multipart.
|
||||
* Uses third-party library to parse the data.
|
||||
*
|
||||
* @param callable $callback Accepts Riverline\MultiPartParser\StreamedPart as an argument, returns true if matched.
|
||||
*
|
||||
* @return self
|
||||
* @see https://github.com/Riverline/multipart-parser#usage
|
||||
*/
|
||||
public function matchMultipartFormData(callable $callback): self
|
||||
{
|
||||
return $this->addMatcher(new MultipartFormDataMatcher($callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* Match entire request body.
|
||||
*
|
||||
|
@ -254,6 +403,19 @@ class PockBuilder
|
|||
return $this->addMatcher(new BodyMatcher($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Match entire request body using provided regular expression.
|
||||
*
|
||||
* @param string $expression
|
||||
* @param int $flags
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function matchBodyRegExp(string $expression, int $flags = 0): self
|
||||
{
|
||||
return $this->addMatcher(new RegExpBodyMatcher($expression, $flags));
|
||||
}
|
||||
|
||||
/**
|
||||
* Match JSON request body.
|
||||
*
|
||||
|
@ -272,11 +434,63 @@ class PockBuilder
|
|||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Match JSON request body against JSON string or array with data.
|
||||
*
|
||||
* @param array<int|string, mixed>|string $data
|
||||
*
|
||||
* @return self
|
||||
* @throws \Pock\Exception\JsonException
|
||||
*/
|
||||
public function matchSerializedJsonBody($data): self
|
||||
{
|
||||
if (is_string($data)) {
|
||||
$data = self::jsonDecode($data, true);
|
||||
}
|
||||
|
||||
return $this->addMatcher(new JsonBodyMatcher($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param DOMDocument|\Psr\Http\Message\StreamInterface|resource|string $data
|
||||
*
|
||||
* @return self
|
||||
* @throws \Pock\Exception\XmlException
|
||||
* @see \Pock\PockBuilder::matchSerializedXmlBody()
|
||||
*
|
||||
*/
|
||||
public function matchXmlBody($data): self
|
||||
{
|
||||
return $this->addMatcher(new XmlBodyMatcher($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Match XML request body.
|
||||
*
|
||||
* This method will try to use available XML serializer before matching.
|
||||
*
|
||||
* @phpstan-ignore-next-line
|
||||
* @param string|array|object $data
|
||||
*
|
||||
* @return self
|
||||
* @throws \Pock\Exception\XmlException
|
||||
*/
|
||||
public function matchSerializedXmlBody($data): self
|
||||
{
|
||||
return $this->matchXmlBody(self::serializeXml($data) ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Match request using provided callback. Callback should receive RequestInterface and return boolean.
|
||||
* If returned value is true then request is matched.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param callable $callback Callable that accepts PSR-7 RequestInterface as it's first argument.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
|
@ -310,6 +524,8 @@ class PockBuilder
|
|||
*/
|
||||
public function repeat(int $hits): self
|
||||
{
|
||||
$this->closePrevious();
|
||||
|
||||
if ($hits > 0) {
|
||||
$this->maxHits = $hits;
|
||||
}
|
||||
|
@ -317,6 +533,49 @@ class PockBuilder
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Always execute this mock if matched. Mock with this call will not be expired ever.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function always(): self
|
||||
{
|
||||
$this->closePrevious();
|
||||
$this->maxHits = -1;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Match request only at Nth hit. Previous matches will not be executed.
|
||||
*
|
||||
* **Note:** There IS a catch if you use this with the equal mocks. The test Client will not register hit
|
||||
* for the second mock and the second mock will be executed at N+1 time.
|
||||
*
|
||||
* For example, if you try to send 5 requests with this mocks and log response codes:
|
||||
* ```php
|
||||
* $builder = new PockBuilder();
|
||||
*
|
||||
* $builder->matchHost('example.com')->at(2)->reply(200);
|
||||
* $builder->matchHost('example.com')->at(4)->reply(201);
|
||||
* $builder->always()->reply(400);
|
||||
* ```
|
||||
*
|
||||
* You will get this: 400, 400, 200, 400, 400, 201
|
||||
* Instead of this: 400, 400, 200, 400, 201, 400
|
||||
*
|
||||
* @param int $hit
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function at(int $hit): self
|
||||
{
|
||||
$this->closePrevious();
|
||||
$this->matchAt = $hit - 1;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw an exception when request is being sent.
|
||||
*
|
||||
|
@ -331,6 +590,42 @@ class PockBuilder
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw an ClientExceptionInterface instance with specified message
|
||||
*
|
||||
* @param string $message
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function throwClientException(string $message = 'Pock ClientException'): self
|
||||
{
|
||||
return $this->throwException(new PockClientException($message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw an NetworkExceptionInterface instance with specified message
|
||||
*
|
||||
* @param string $message
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function throwNetworkException(string $message = 'Pock NetworkException'): self
|
||||
{
|
||||
return $this->throwException(new PockNetworkException($message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw an RequestExceptionInterface instance with specified message
|
||||
*
|
||||
* @param string $message
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function throwRequestException(string $message = 'Pock RequestException'): self
|
||||
{
|
||||
return $this->throwException(new PockRequestException($message));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $statusCode
|
||||
*
|
||||
|
@ -347,6 +642,47 @@ class PockBuilder
|
|||
return $this->responseBuilder->withStatusCode($statusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the response during request execution using provided ReplytFactoryInterface implementation.
|
||||
*
|
||||
* @param \Pock\Factory\ReplyFactoryInterface $factory
|
||||
* @see ReplyFactoryInterface
|
||||
*/
|
||||
public function replyWithFactory(ReplyFactoryInterface $factory): void
|
||||
{
|
||||
$this->replyFactory = $factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the response during request execution using provided callback.
|
||||
*
|
||||
* Callback should receive the same parameters as in the `ReplyFactoryInterface::createReply` method.
|
||||
*
|
||||
* @see ReplyFactoryInterface::createReply()
|
||||
*
|
||||
* @param callable $callback
|
||||
*/
|
||||
public function replyWithCallback(callable $callback): void
|
||||
{
|
||||
$this->replyWithFactory(new CallbackReplyFactory($callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reply to the request using provided client. Can be used to send real network request.
|
||||
*
|
||||
* @param \Psr\Http\Client\ClientInterface $client
|
||||
* @SuppressWarnings(unused)
|
||||
*/
|
||||
public function replyWithClient(ClientInterface $client): void
|
||||
{
|
||||
$this->replyWithCallback(function (
|
||||
RequestInterface $request,
|
||||
PockResponseBuilder $responseBuilder
|
||||
) use ($client): ResponseInterface {
|
||||
return $client->sendRequest($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the builder.
|
||||
*
|
||||
|
@ -355,9 +691,11 @@ class PockBuilder
|
|||
public function reset(): self
|
||||
{
|
||||
$this->matcher = new MultipleMatcher();
|
||||
$this->replyFactory = null;
|
||||
$this->responseBuilder = null;
|
||||
$this->throwable = null;
|
||||
$this->maxHits = 1;
|
||||
$this->matchAt = -1;
|
||||
$this->mocks = [];
|
||||
|
||||
return $this;
|
||||
|
@ -387,7 +725,7 @@ class PockBuilder
|
|||
|
||||
private function closePrevious(): void
|
||||
{
|
||||
if (null !== $this->responseBuilder || null !== $this->throwable) {
|
||||
if (null !== $this->responseBuilder || null !== $this->replyFactory || null !== $this->throwable) {
|
||||
if (0 === count($this->matcher)) {
|
||||
$this->matcher->addMatcher(new AnyRequestMatcher());
|
||||
}
|
||||
|
@ -400,14 +738,18 @@ class PockBuilder
|
|||
|
||||
$this->mocks[] = new Mock(
|
||||
$this->matcher,
|
||||
$this->replyFactory,
|
||||
$response,
|
||||
$this->throwable,
|
||||
$this->maxHits
|
||||
$this->maxHits,
|
||||
$this->matchAt
|
||||
);
|
||||
$this->matcher = new MultipleMatcher();
|
||||
$this->replyFactory = null;
|
||||
$this->responseBuilder = null;
|
||||
$this->throwable = null;
|
||||
$this->maxHits = 1;
|
||||
$this->matchAt = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
/**
|
||||
* PHP 7.1
|
||||
*
|
||||
* @category CallbackSerializerDecorator
|
||||
* @category CallbackSerializerAdapter
|
||||
* @package Pock\Serializer
|
||||
*/
|
||||
|
||||
|
@ -12,18 +12,18 @@ namespace Pock\Serializer;
|
|||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Class CallbackSerializerDecorator
|
||||
* Class CallbackSerializerAdapter
|
||||
*
|
||||
* @category CallbackSerializerDecorator
|
||||
* @category CallbackSerializerAdapter
|
||||
* @package Pock\Serializer
|
||||
*/
|
||||
class CallbackSerializerDecorator implements SerializerInterface
|
||||
class CallbackSerializerAdapter implements SerializerInterface
|
||||
{
|
||||
/** @var callable */
|
||||
private $callback;
|
||||
|
||||
/**
|
||||
* CallbackSerializerDecorator constructor.
|
||||
* CallbackSerializerAdapter constructor.
|
||||
*
|
||||
* @param callable $callback
|
||||
*/
|
|
@ -3,19 +3,19 @@
|
|||
/**
|
||||
* PHP 7.2
|
||||
*
|
||||
* @category JmsSerializerDecorator
|
||||
* @category JmsSerializerAdapter
|
||||
* @package Pock\Serializer
|
||||
*/
|
||||
|
||||
namespace Pock\Serializer;
|
||||
|
||||
/**
|
||||
* Class JmsSerializerDecorator
|
||||
* Class JmsSerializerAdapter
|
||||
*
|
||||
* @category JmsSerializerDecorator
|
||||
* @category JmsSerializerAdapter
|
||||
* @package Pock\Serializer
|
||||
*/
|
||||
class JmsSerializerDecorator implements SerializerInterface
|
||||
class JmsSerializerAdapter implements SerializerInterface
|
||||
{
|
||||
/** @var object */
|
||||
private $serializer;
|
||||
|
@ -24,7 +24,7 @@ class JmsSerializerDecorator implements SerializerInterface
|
|||
private $format;
|
||||
|
||||
/**
|
||||
* JmsSerializerDecorator constructor.
|
||||
* JmsSerializerAdapter constructor.
|
||||
*
|
||||
* @param object $serializer
|
||||
*/
|
20
src/Serializer/SymfonySerializerAdapter.php
Normal file
20
src/Serializer/SymfonySerializerAdapter.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.1
|
||||
*
|
||||
* @category SymfonySerializerAdapter
|
||||
* @package Pock\Serializer
|
||||
*/
|
||||
|
||||
namespace Pock\Serializer;
|
||||
|
||||
/**
|
||||
* Class SymfonySerializerAdapter
|
||||
*
|
||||
* @category SymfonySerializerAdapter
|
||||
* @package Pock\Serializer
|
||||
*/
|
||||
class SymfonySerializerAdapter extends JmsSerializerAdapter
|
||||
{
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.1
|
||||
*
|
||||
* @category SymfonySerializerDecorator
|
||||
* @package Pock\Serializer
|
||||
*/
|
||||
|
||||
namespace Pock\Serializer;
|
||||
|
||||
/**
|
||||
* Class SymfonySerializerDecorator
|
||||
*
|
||||
* @category SymfonySerializerDecorator
|
||||
* @package Pock\Serializer
|
||||
*/
|
||||
class SymfonySerializerDecorator extends JmsSerializerDecorator
|
||||
{
|
||||
}
|
|
@ -22,11 +22,11 @@ trait JsonDecoderTrait
|
|||
/**
|
||||
* json_decode which throws exception on error.
|
||||
*
|
||||
* @param string $json
|
||||
* @param bool|null $associative
|
||||
* @param int $depth
|
||||
* @param string $json
|
||||
* @param bool $associative
|
||||
* @param int $depth
|
||||
*
|
||||
* @param int $flags
|
||||
* @param int $flags
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \Pock\Exception\JsonException
|
||||
|
@ -35,7 +35,7 @@ trait JsonDecoderTrait
|
|||
*/
|
||||
public static function jsonDecode(
|
||||
string $json,
|
||||
?bool $associative = false,
|
||||
bool $associative = false,
|
||||
int $depth = 512,
|
||||
int $flags = 0
|
||||
) {
|
||||
|
|
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));
|
||||
}
|
||||
}
|
75
tests/src/Comparator/RecursiveArrayComparatorTest.php
Normal file
75
tests/src/Comparator/RecursiveArrayComparatorTest.php
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category RecursiveArrayComparatorTest
|
||||
* @package Pock\Tests\Comparator
|
||||
*/
|
||||
|
||||
namespace Pock\Tests\Comparator;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Pock\Comparator\ComparatorLocator;
|
||||
use Pock\Comparator\RecursiveArrayComparator;
|
||||
|
||||
/**
|
||||
* Class RecursiveArrayComparatorTest
|
||||
*
|
||||
* @category RecursiveArrayComparatorTest
|
||||
* @package Pock\Tests\Comparator
|
||||
*/
|
||||
class RecursiveArrayComparatorTest extends TestCase
|
||||
{
|
||||
public function testMatches(): void
|
||||
{
|
||||
$needle = [
|
||||
'filter' => [
|
||||
'createdAtFrom' => '2020-01-01 00:00:00',
|
||||
'createdAtTo' => '2021-08-01 00:00:00',
|
||||
],
|
||||
'test' => ''
|
||||
];
|
||||
$haystack = [
|
||||
'filter' => [
|
||||
'createdAtFrom' => '2020-01-01 00:00:00',
|
||||
'createdAtTo' => '2021-08-01 00:00:00',
|
||||
],
|
||||
'test' => ''
|
||||
];
|
||||
|
||||
self::assertTrue(ComparatorLocator::get(RecursiveArrayComparator::class)->compare($needle, $haystack));
|
||||
}
|
||||
|
||||
public function testNotMatches(): void
|
||||
{
|
||||
$needle = [
|
||||
'filter' => [
|
||||
'createdAtFrom' => '2020-01-01 00:00:00',
|
||||
'createdAtTo' => '2021-08-01 00:00:00',
|
||||
],
|
||||
'test2' => [1]
|
||||
];
|
||||
$haystack = [
|
||||
'filter' => [
|
||||
'createdAtFrom' => '2020-01-01 00:00:00',
|
||||
'createdAtTo' => '2021-08-01 00:00:00',
|
||||
],
|
||||
'test2' => 1
|
||||
];
|
||||
|
||||
self::assertFalse(ComparatorLocator::get(RecursiveArrayComparator::class)->compare($needle, $haystack));
|
||||
}
|
||||
|
||||
public function testNotMatchesKeyPositions(): void
|
||||
{
|
||||
$needle = [
|
||||
'source' => json_decode('{"medium":"tiktok","source":"Test Ad","campaign":"Test Campaign"}', true)
|
||||
];
|
||||
$haystack = [
|
||||
'source' => json_decode('{"source":"Test Ad","medium":"tiktok","campaign":"Test Campaign"}', true)
|
||||
];
|
||||
|
||||
self::assertTrue(ComparatorLocator::get(RecursiveArrayComparator::class)->compare($needle, $haystack));
|
||||
}
|
||||
}
|
75
tests/src/Comparator/RecursiveArrayLtrComparatorTest.php
Normal file
75
tests/src/Comparator/RecursiveArrayLtrComparatorTest.php
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category RecursiveArrayLtrComparatorTest
|
||||
* @package Pock\Tests\Comparator
|
||||
*/
|
||||
|
||||
namespace Pock\Tests\Comparator;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Pock\Comparator\ComparatorLocator;
|
||||
use Pock\Comparator\RecursiveLtrArrayComparator;
|
||||
|
||||
/**
|
||||
* Class RecursiveArrayLtrComparatorTest
|
||||
*
|
||||
* @category RecursiveArrayLtrComparatorTest
|
||||
* @package Pock\Tests\Comparator
|
||||
*/
|
||||
class RecursiveArrayLtrComparatorTest extends TestCase
|
||||
{
|
||||
public function testMatches(): void
|
||||
{
|
||||
$needle = [
|
||||
'filter' => [
|
||||
'createdAtFrom' => '2020-01-01 00:00:00',
|
||||
'createdAtTo' => '2021-08-01 00:00:00',
|
||||
]
|
||||
];
|
||||
$haystack = [
|
||||
'filter' => [
|
||||
'createdAtFrom' => '2020-01-01 00:00:00',
|
||||
'createdAtTo' => '2021-08-01 00:00:00',
|
||||
],
|
||||
'test' => ''
|
||||
];
|
||||
|
||||
self::assertTrue(ComparatorLocator::get(RecursiveLtrArrayComparator::class)->compare($needle, $haystack));
|
||||
}
|
||||
|
||||
public function testNotMatches(): void
|
||||
{
|
||||
$needle = [
|
||||
'filter' => [
|
||||
'createdAtFrom' => '2020-01-01 00:00:00',
|
||||
'createdAtTo' => '2021-08-01 00:00:00',
|
||||
],
|
||||
'test2' => [1]
|
||||
];
|
||||
$haystack = [
|
||||
'filter' => [
|
||||
'createdAtFrom' => '2020-01-01 00:00:00',
|
||||
'createdAtTo' => '2021-08-01 00:00:00',
|
||||
],
|
||||
'test2' => 1,
|
||||
'test' => ''
|
||||
];
|
||||
|
||||
self::assertFalse(ComparatorLocator::get(RecursiveLtrArrayComparator::class)->compare($needle, $haystack));
|
||||
}
|
||||
|
||||
public function testNotMatchesKeyPositions(): void
|
||||
{
|
||||
$needle = [
|
||||
'source' => json_decode('{"medium":"tiktok","source":"Test Ad","campaign":"Test Campaign"}', true)
|
||||
];
|
||||
$haystack = [
|
||||
'source' => json_decode('{"source":"Test Ad","medium":"tiktok","campaign":"Test Campaign"}', true)
|
||||
];
|
||||
|
||||
self::assertTrue(ComparatorLocator::get(RecursiveLtrArrayComparator::class)->compare($needle, $haystack));
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.1
|
||||
*
|
||||
* @category CallbackSerializerDecoratorTest
|
||||
* @package Pock\Tests\Decorator
|
||||
*/
|
||||
|
||||
namespace Pock\Tests\Decorator;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Pock\Serializer\CallbackSerializerDecorator;
|
||||
|
||||
/**
|
||||
* Class CallbackSerializerDecoratorTest
|
||||
*
|
||||
* @category CallbackSerializerDecoratorTest
|
||||
* @package Pock\Tests\Decorator
|
||||
*/
|
||||
class CallbackSerializerDecoratorTest extends TestCase
|
||||
{
|
||||
public function testSerialize(): void
|
||||
{
|
||||
self::assertEquals('{}', (new CallbackSerializerDecorator(function ($data) {
|
||||
return $data;
|
||||
}))->serialize('{}'));
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ namespace Pock\Tests\Factory;
|
|||
use PHPUnit\Framework\TestCase;
|
||||
use Pock\Factory\JsonSerializerFactory;
|
||||
use Pock\Factory\XmlSerializerFactory;
|
||||
use Pock\Serializer\CallbackSerializerDecorator;
|
||||
use Pock\Serializer\CallbackSerializerAdapter;
|
||||
use Pock\Serializer\SerializerInterface;
|
||||
use Pock\TestUtils\EmptyJsonSerializerDecorator;
|
||||
use Pock\TestUtils\EmptyXmlSerializerDecorator;
|
||||
|
|
62
tests/src/Matchers/ExactFormDataMatcherTest.php
Normal file
62
tests/src/Matchers/ExactFormDataMatcherTest.php
Normal file
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category ExactFormDataMatcherTest
|
||||
* @package Pock\Tests\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Tests\Matchers;
|
||||
|
||||
use Pock\Matchers\ExactFormDataMatcher;
|
||||
use Pock\TestUtils\PockTestCase;
|
||||
|
||||
/**
|
||||
* Class ExactFormDataMatcherTest
|
||||
*
|
||||
* @category ExactFormDataMatcherTest
|
||||
* @package Pock\Tests\Matchers
|
||||
*/
|
||||
class ExactFormDataMatcherTest extends PockTestCase
|
||||
{
|
||||
public function testInvalidData(): void
|
||||
{
|
||||
$matcher = new ExactFormDataMatcher(['field3' => 'value3']);
|
||||
$request = self::getRequestWithBody('doesn\'t look like form-data at all');
|
||||
|
||||
self::assertFalse($matcher->matches($request));
|
||||
}
|
||||
|
||||
public function testNoMatches(): void
|
||||
{
|
||||
$matcher = new ExactFormDataMatcher(['field3' => 'value3']);
|
||||
$request = self::getRequestWithBody('field1=value1&field2=value2');
|
||||
|
||||
self::assertFalse($matcher->matches($request));
|
||||
}
|
||||
|
||||
public function testNoMatchesByValue(): void
|
||||
{
|
||||
$matcher = new ExactFormDataMatcher(['field1' => 'value2']);
|
||||
$request = self::getRequestWithBody('field1=value1&field2=value2');
|
||||
|
||||
self::assertFalse($matcher->matches($request));
|
||||
}
|
||||
|
||||
public function testNoMatchesRedundantParam(): void
|
||||
{
|
||||
$matcher = new ExactFormDataMatcher(['field2' => 'value2']);
|
||||
$request = self::getRequestWithBody('field1=value1&field2=value2');
|
||||
|
||||
self::assertFalse($matcher->matches($request));
|
||||
}
|
||||
|
||||
public function testMatches(): void
|
||||
{
|
||||
$matcher = new ExactFormDataMatcher(['field1' => 'value1', 'field2' => 'value2']);
|
||||
$request = self::getRequestWithBody('field1=value1&field2=value2');
|
||||
|
||||
self::assertTrue($matcher->matches($request));
|
||||
}
|
||||
}
|
62
tests/src/Matchers/FormDataMatcherTest.php
Normal file
62
tests/src/Matchers/FormDataMatcherTest.php
Normal file
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category FormDataMatcherTest
|
||||
* @package Pock\Tests\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Tests\Matchers;
|
||||
|
||||
use Pock\Matchers\FormDataMatcher;
|
||||
use Pock\TestUtils\PockTestCase;
|
||||
|
||||
/**
|
||||
* Class FormDataMatcherTest
|
||||
*
|
||||
* @category FormDataMatcherTest
|
||||
* @package Pock\Tests\Matchers
|
||||
*/
|
||||
class FormDataMatcherTest extends PockTestCase
|
||||
{
|
||||
public function testInvalidData(): void
|
||||
{
|
||||
$matcher = new FormDataMatcher(['field3' => 'value3']);
|
||||
$request = self::getRequestWithBody('doesn\'t look like form-data at all');
|
||||
|
||||
self::assertFalse($matcher->matches($request));
|
||||
}
|
||||
|
||||
public function testNoMatches(): void
|
||||
{
|
||||
$matcher = new FormDataMatcher(['field3' => 'value3']);
|
||||
$request = self::getRequestWithBody('field1=value1&field2=value2');
|
||||
|
||||
self::assertFalse($matcher->matches($request));
|
||||
}
|
||||
|
||||
public function testNoMatchesByValue(): void
|
||||
{
|
||||
$matcher = new FormDataMatcher(['field1' => 'value2']);
|
||||
$request = self::getRequestWithBody('field1=value1&field2=value2');
|
||||
|
||||
self::assertFalse($matcher->matches($request));
|
||||
}
|
||||
|
||||
public function testNoMatchesExtraValues(): void
|
||||
{
|
||||
$matcher = new FormDataMatcher(['field2' => 'value2']);
|
||||
$request = self::getRequestWithBody('field1=value1&field2=value2');
|
||||
|
||||
self::assertFalse($matcher->matches($request));
|
||||
}
|
||||
|
||||
public function testMatches(): void
|
||||
{
|
||||
$matcher = new FormDataMatcher(['field1' => 'value1', 'field2' => 'value2']);
|
||||
$request = self::getRequestWithBody('field1=value1&field2=value2');
|
||||
|
||||
self::assertTrue($matcher->matches($request));
|
||||
}
|
||||
}
|
59
tests/src/Matchers/MultipartFormDataMatcherTest.php
Normal file
59
tests/src/Matchers/MultipartFormDataMatcherTest.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category MultipartFormDataMatcherTest
|
||||
* @package Pock\Tests\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Tests\Matchers;
|
||||
|
||||
use Http\Message\MultipartStream\MultipartStreamBuilder;
|
||||
use Pock\Matchers\MultipartFormDataMatcher;
|
||||
use Pock\TestUtils\PockTestCase;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Riverline\MultiPartParser\StreamedPart;
|
||||
|
||||
/**
|
||||
* Class MultipartFormDataMatcherTest
|
||||
*
|
||||
* @category MultipartFormDataMatcherTest
|
||||
* @package Pock\Tests\Matchers
|
||||
*/
|
||||
class MultipartFormDataMatcherTest extends PockTestCase
|
||||
{
|
||||
public function testNoMatchesNotMultipart(): void
|
||||
{
|
||||
$matcher = new MultipartFormDataMatcher(function (StreamedPart $part) {
|
||||
return $part->isMultiPart();
|
||||
});
|
||||
|
||||
self::assertFalse($matcher->matches(self::getTestRequest()));
|
||||
self::assertFalse($matcher->matches(self::getRequestWithBody('param=value¶m2=value')));
|
||||
}
|
||||
|
||||
public function testMatches(): void
|
||||
{
|
||||
$matcher = new MultipartFormDataMatcher(function (StreamedPart $part) {
|
||||
return $part->isMultiPart() &&
|
||||
1 === count($part->getPartsByName('param1')) &&
|
||||
1 === count($part->getPartsByName('param2')) &&
|
||||
'value1' === $part->getPartsByName('param1')[0]->getBody() &&
|
||||
'value2' === $part->getPartsByName('param2')[0]->getBody() &&
|
||||
'text/plain' === $part->getPartsByName('param1')[0]->getHeader('Content-Type');
|
||||
});
|
||||
$builder = new MultipartStreamBuilder(self::getPsr17Factory());
|
||||
$builder->addResource('param1', 'value1', ['headers' => ['Content-Type' => 'text/plain']])
|
||||
->addResource('param2', 'value2');
|
||||
|
||||
self::assertTrue($matcher->matches(self::getMultipartRequest($builder)));
|
||||
}
|
||||
|
||||
private static function getMultipartRequest(MultipartStreamBuilder $builder): RequestInterface
|
||||
{
|
||||
return self::getPsr17Factory()->createRequest('POST', 'https://example.com')
|
||||
->withHeader('Content-Type', 'multipart/form-data; boundary="' . $builder->getBoundary() . '"')
|
||||
->withBody($builder->build());
|
||||
}
|
||||
}
|
60
tests/src/Matchers/PortMatcherTest.php
Normal file
60
tests/src/Matchers/PortMatcherTest.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category PortMatcherTest
|
||||
* @package Pock\Tests\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Tests\Matchers;
|
||||
|
||||
use Nyholm\Psr7\Uri;
|
||||
use Pock\Enum\RequestScheme;
|
||||
use Pock\Matchers\PortMatcher;
|
||||
use Pock\TestUtils\PockTestCase;
|
||||
|
||||
/**
|
||||
* Class PortMatcherTest
|
||||
*
|
||||
* @category PortMatcherTest
|
||||
* @package Pock\Tests\Matchers
|
||||
*/
|
||||
class PortMatcherTest extends PockTestCase
|
||||
{
|
||||
public function testNotMatches(): void
|
||||
{
|
||||
self::assertFalse((new PortMatcher(80))->matches(static::getTestRequest()));
|
||||
}
|
||||
|
||||
public function testMatches(): void
|
||||
{
|
||||
self::assertTrue((new PortMatcher(443))->matches(static::getTestRequest()));
|
||||
}
|
||||
|
||||
public function testMatchesWithoutProto(): void
|
||||
{
|
||||
self::assertTrue((new PortMatcher(80))->matches(static::getTestRequest()->withUri(new class extends Uri {
|
||||
public function getScheme(): string
|
||||
{
|
||||
return RequestScheme::HTTP;
|
||||
}
|
||||
|
||||
public function getPort(): ?int
|
||||
{
|
||||
return null;
|
||||
}
|
||||
})));
|
||||
self::assertTrue((new PortMatcher(443))->matches(static::getTestRequest()->withUri(new class extends Uri {
|
||||
public function getScheme(): string
|
||||
{
|
||||
return RequestScheme::HTTPS;
|
||||
}
|
||||
|
||||
public function getPort(): ?int
|
||||
{
|
||||
return null;
|
||||
}
|
||||
})));
|
||||
}
|
||||
}
|
38
tests/src/Matchers/RegExpBodyMatcherTest.php
Normal file
38
tests/src/Matchers/RegExpBodyMatcherTest.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category RegExpBodyMatcherTest
|
||||
* @package Pock\Tests\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Tests\Matchers;
|
||||
|
||||
use Pock\Matchers\RegExpBodyMatcher;
|
||||
use Pock\TestUtils\PockTestCase;
|
||||
|
||||
/**
|
||||
* Class RegExpBodyMatcherTest
|
||||
*
|
||||
* @category RegExpBodyMatcherTest
|
||||
* @package Pock\Tests\Matchers
|
||||
*/
|
||||
class RegExpBodyMatcherTest extends PockTestCase
|
||||
{
|
||||
public function testNoMatches(): void
|
||||
{
|
||||
$matcher = new RegExpBodyMatcher('/\d+-\d+/m');
|
||||
$request = static::getRequestWithBody('test unmatchable request');
|
||||
|
||||
self::assertFalse($matcher->matches($request));
|
||||
}
|
||||
|
||||
public function testMatches(): void
|
||||
{
|
||||
$matcher = new RegExpBodyMatcher('/\d+-\d+/m', PREG_UNMATCHED_AS_NULL);
|
||||
$request = static::getRequestWithBody('23-900');
|
||||
|
||||
self::assertTrue($matcher->matches($request));
|
||||
}
|
||||
}
|
38
tests/src/Matchers/RegExpPathMatcherTest.php
Normal file
38
tests/src/Matchers/RegExpPathMatcherTest.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category RegExpPathMatcherTest
|
||||
* @package Pock\Tests\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Tests\Matchers;
|
||||
|
||||
use Pock\Matchers\RegExpPathMatcher;
|
||||
use Pock\TestUtils\PockTestCase;
|
||||
|
||||
/**
|
||||
* Class RegExpPathMatcherTest
|
||||
*
|
||||
* @category RegExpPathMatcherTest
|
||||
* @package Pock\Tests\Matchers
|
||||
*/
|
||||
class RegExpPathMatcherTest extends PockTestCase
|
||||
{
|
||||
public function testNoMatches(): void
|
||||
{
|
||||
$matcher = new RegExpPathMatcher('/\/?\d+-\d+/m');
|
||||
$request = static::getTestRequest()->withUri(static::getPsr17Factory()->createUri('https://test.com/test'));
|
||||
|
||||
self::assertFalse($matcher->matches($request));
|
||||
}
|
||||
|
||||
public function testMatches(): void
|
||||
{
|
||||
$matcher = new RegExpPathMatcher('/\d+-\d+/m', PREG_UNMATCHED_AS_NULL);
|
||||
$request = static::getTestRequest()->withUri(static::getPsr17Factory()->createUri('https://test.com/23-900'));
|
||||
|
||||
self::assertTrue($matcher->matches($request));
|
||||
}
|
||||
}
|
44
tests/src/Matchers/RegExpQueryMatcherTest.php
Normal file
44
tests/src/Matchers/RegExpQueryMatcherTest.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category RegExpQueryMatcherTest
|
||||
* @package Pock\Tests\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Tests\Matchers;
|
||||
|
||||
use Pock\Matchers\RegExpQueryMatcher;
|
||||
use Pock\TestUtils\PockTestCase;
|
||||
|
||||
/**
|
||||
* Class RegExpQueryMatcherTest
|
||||
*
|
||||
* @category RegExpQueryMatcherTest
|
||||
* @package Pock\Tests\Matchers
|
||||
*/
|
||||
class RegExpQueryMatcherTest extends PockTestCase
|
||||
{
|
||||
public function testNoMatches(): void
|
||||
{
|
||||
$matcher = new RegExpQueryMatcher('/\d+-\d+/m');
|
||||
$request = static::getTestRequest()->withUri(
|
||||
static::getPsr17Factory()->createUri('https://test.com')
|
||||
->withQuery('param=value')
|
||||
);
|
||||
|
||||
self::assertFalse($matcher->matches($request));
|
||||
}
|
||||
|
||||
public function testMatches(): void
|
||||
{
|
||||
$matcher = new RegExpQueryMatcher('/\d+-\d+/m', PREG_UNMATCHED_AS_NULL);
|
||||
$request = static::getTestRequest()->withUri(
|
||||
static::getPsr17Factory()->createUri('https://test.com')
|
||||
->withQuery('param=23-900')
|
||||
);
|
||||
|
||||
self::assertTrue($matcher->matches($request));
|
||||
}
|
||||
}
|
41
tests/src/Matchers/RegExpUriMatcherTest.php
Normal file
41
tests/src/Matchers/RegExpUriMatcherTest.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @category RegExpUriMatcherTest
|
||||
* @package Pock\Tests\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Tests\Matchers;
|
||||
|
||||
use Pock\Matchers\RegExpUriMatcher;
|
||||
use Pock\TestUtils\PockTestCase;
|
||||
|
||||
/**
|
||||
* Class RegExpUriMatcherTest
|
||||
*
|
||||
* @category RegExpUriMatcher
|
||||
* @package Pock\Tests\Matchers
|
||||
*/
|
||||
class RegExpUriMatcherTest extends PockTestCase
|
||||
{
|
||||
public function testNoMatches(): void
|
||||
{
|
||||
$matcher = new RegExpUriMatcher('/https\:\/\/\w+\.com\/\d+-\d+\?param=\d+-\d+/m');
|
||||
$request = static::getTestRequest();
|
||||
|
||||
self::assertFalse($matcher->matches($request));
|
||||
}
|
||||
|
||||
public function testMatches(): void
|
||||
{
|
||||
$matcher = new RegExpUriMatcher('/https\:\/\/\w+\.com\/\d+-\d+\?param=\d+-\d+/m', PREG_UNMATCHED_AS_NULL);
|
||||
$request = static::getTestRequest()->withUri(
|
||||
static::getPsr17Factory()->createUri('https://example.com/23-900')
|
||||
->withQuery('param=23-900')
|
||||
);
|
||||
|
||||
self::assertTrue($matcher->matches($request));
|
||||
}
|
||||
}
|
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,15 +9,22 @@
|
|||
|
||||
namespace Pock\Tests;
|
||||
|
||||
use DOMDocument;
|
||||
use Http\Message\MultipartStream\MultipartStreamBuilder;
|
||||
use Pock\Enum\RequestMethod;
|
||||
use Pock\Enum\RequestScheme;
|
||||
use Pock\Exception\UniversalMockException;
|
||||
use Pock\Exception\UnsupportedRequestException;
|
||||
use Pock\Matchers\XmlBodyMatcher;
|
||||
use Pock\PockBuilder;
|
||||
use Pock\PockResponseBuilder;
|
||||
use Pock\TestUtils\PockTestCase;
|
||||
use Pock\TestUtils\SimpleObject;
|
||||
use Pock\TestUtils\TestReplyFactory;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Psr\Http\Client\NetworkExceptionInterface;
|
||||
use Psr\Http\Client\RequestExceptionInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Riverline\MultiPartParser\StreamedPart;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
|
@ -28,6 +35,11 @@ use RuntimeException;
|
|||
*/
|
||||
class PockBuilderTest extends PockTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
XmlBodyMatcher::$forceTextComparison = false;
|
||||
}
|
||||
|
||||
public function testNoHit(): void
|
||||
{
|
||||
$this->expectException(UnsupportedRequestException::class);
|
||||
|
@ -35,7 +47,7 @@ class PockBuilderTest extends PockTestCase
|
|||
->createRequest(RequestMethod::GET, self::TEST_URI));
|
||||
}
|
||||
|
||||
public function testThrowException(): void
|
||||
public function testThrowClientException(): void
|
||||
{
|
||||
$this->expectException(ClientExceptionInterface::class);
|
||||
|
||||
|
@ -43,13 +55,60 @@ class PockBuilderTest extends PockTestCase
|
|||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchScheme(RequestScheme::HTTPS)
|
||||
->matchHost(self::TEST_HOST)
|
||||
->throwException(new UniversalMockException('Boom!'));
|
||||
->throwClientException();
|
||||
|
||||
$builder->getClient()->sendRequest(
|
||||
self::getPsr17Factory()->createRequest(RequestMethod::GET, self::TEST_URI)
|
||||
);
|
||||
}
|
||||
|
||||
public function testThrowNetworkException(): void
|
||||
{
|
||||
$this->expectException(NetworkExceptionInterface::class);
|
||||
|
||||
$builder = new PockBuilder();
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchScheme(RequestScheme::HTTPS)
|
||||
->matchHost(self::TEST_HOST)
|
||||
->throwNetworkException();
|
||||
|
||||
$builder->getClient()->sendRequest(
|
||||
self::getPsr17Factory()->createRequest(RequestMethod::GET, self::TEST_URI)
|
||||
);
|
||||
}
|
||||
|
||||
public function testThrowRequestException(): void
|
||||
{
|
||||
$this->expectException(RequestExceptionInterface::class);
|
||||
|
||||
$builder = new PockBuilder();
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchScheme(RequestScheme::HTTPS)
|
||||
->matchHost(self::TEST_HOST)
|
||||
->throwRequestException();
|
||||
|
||||
$builder->getClient()->sendRequest(
|
||||
self::getPsr17Factory()->createRequest(RequestMethod::GET, self::TEST_URI)
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
$builder = new PockBuilder();
|
||||
|
@ -94,6 +153,46 @@ class PockBuilderTest extends PockTestCase
|
|||
self::assertEquals('Successful', $response->getBody()->getContents());
|
||||
}
|
||||
|
||||
public function testMatchOriginFailure(): void
|
||||
{
|
||||
$incorrectOrigin = RequestScheme::HTTPS . ':///' . self::TEST_HOST;
|
||||
|
||||
$this->expectExceptionMessage($incorrectOrigin);
|
||||
|
||||
$builder = new PockBuilder();
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchOrigin($incorrectOrigin)
|
||||
->reply(200)
|
||||
->withHeader('Content-Type', 'text/plain')
|
||||
->withBody('Successful');
|
||||
|
||||
$builder->getClient()->sendRequest(
|
||||
self::getPsr17Factory()
|
||||
->createRequest(RequestMethod::GET, 'https://another-example.com')
|
||||
);
|
||||
}
|
||||
|
||||
public function testMatchOrigin(): void
|
||||
{
|
||||
$origin = RequestScheme::HTTPS . '://' . self::TEST_HOST . ':443';
|
||||
$builder = new PockBuilder();
|
||||
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchOrigin($origin)
|
||||
->reply(200)
|
||||
->withHeader('Content-Type', 'text/plain')
|
||||
->withBody('Successful');
|
||||
|
||||
$response = $builder->getClient()->sendRequest(
|
||||
self::getPsr17Factory()
|
||||
->createRequest(RequestMethod::GET, $origin)
|
||||
);
|
||||
|
||||
self::assertEquals(200, $response->getStatusCode());
|
||||
self::assertEquals(['Content-Type' => ['text/plain']], $response->getHeaders());
|
||||
self::assertEquals('Successful', $response->getBody()->getContents());
|
||||
}
|
||||
|
||||
public function testMatchExactHeader(): void
|
||||
{
|
||||
$builder = new PockBuilder();
|
||||
|
@ -270,7 +369,124 @@ class PockBuilderTest extends PockTestCase
|
|||
], 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 version="1.0" encoding="UTF-8"?>
|
||||
|
@ -280,17 +496,29 @@ class PockBuilderTest extends PockTestCase
|
|||
|
||||
EOF;
|
||||
|
||||
if ($expectException) {
|
||||
$this->expectException(UnsupportedRequestException::class);
|
||||
}
|
||||
|
||||
$document = new DOMDocument();
|
||||
$document->loadXML($simpleObject);
|
||||
|
||||
XmlBodyMatcher::$forceTextComparison = true;
|
||||
|
||||
$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)
|
||||
self::getPsr17Factory()
|
||||
->createRequest(RequestMethod::GET, self::TEST_URI)
|
||||
->withBody(self::getPsr17Factory()->createStream($simpleObject))
|
||||
);
|
||||
|
||||
self::assertEquals(403, $response->getStatusCode());
|
||||
|
@ -298,6 +526,182 @@ EOF;
|
|||
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)
|
||||
->withHeader('Content-Type', 'text/xml')
|
||||
->withXml(['error' => 'Forbidden']);
|
||||
|
||||
$response = $builder->getClient()->sendRequest(
|
||||
self::getPsr17Factory()
|
||||
->createRequest(RequestMethod::GET, self::TEST_URI)
|
||||
->withBody(self::getPsr17Factory()->createStream(
|
||||
PHP_EOL . self::getXmlSerializer()->serialize(new SimpleObject()) . PHP_EOL
|
||||
))
|
||||
);
|
||||
|
||||
self::assertEquals(403, $response->getStatusCode());
|
||||
self::assertEquals(['Content-Type' => ['text/xml']], $response->getHeaders());
|
||||
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 testMultipartFormDataMock(): void
|
||||
{
|
||||
$builder = new PockBuilder();
|
||||
$builder->matchMethod(RequestMethod::POST)
|
||||
->matchScheme(RequestScheme::HTTPS)
|
||||
->matchHost(self::TEST_HOST)
|
||||
->matchMultipartFormData(function (StreamedPart $part) {
|
||||
return $part->isMultiPart() &&
|
||||
1 === count($part->getPartsByName('param1')) &&
|
||||
1 === count($part->getPartsByName('param2')) &&
|
||||
'value1' === $part->getPartsByName('param1')[0]->getBody() &&
|
||||
'value2' === $part->getPartsByName('param2')[0]->getBody() &&
|
||||
'text/plain' === $part->getPartsByName('param1')[0]->getHeader('Content-Type');
|
||||
})->reply(200)
|
||||
->withHeader('Content-Type', 'text/plain')
|
||||
->withBody('ok');
|
||||
|
||||
$streamBuilder = (new MultipartStreamBuilder(self::getPsr17Factory()))
|
||||
->addResource('param1', 'value1', ['headers' => ['Content-Type' => 'text/plain']])
|
||||
->addResource('param2', 'value2');
|
||||
$response = $builder->getClient()->sendRequest(
|
||||
self::getPsr17Factory()
|
||||
->createRequest(RequestMethod::POST, self::TEST_URI)
|
||||
->withHeader('Content-Type', 'multipart/form-data; boundary="' . $streamBuilder->getBoundary() . '"')
|
||||
->withBody($streamBuilder->build())
|
||||
);
|
||||
|
||||
self::assertEquals(200, $response->getStatusCode());
|
||||
self::assertEquals(['Content-Type' => ['text/plain']], $response->getHeaders());
|
||||
self::assertEquals('ok', $response->getBody()->getContents());
|
||||
}
|
||||
|
||||
public function testMatchBodyRegExp(): void
|
||||
{
|
||||
$builder = new PockBuilder();
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchUri(self::TEST_URI)
|
||||
->matchBodyRegExp('/\d+-\d+/')
|
||||
->reply(200);
|
||||
|
||||
$response = $builder->getClient()->sendRequest(static::getRequestWithBody('test matchable 23-900'));
|
||||
|
||||
self::assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testMatchPathRegExp(): void
|
||||
{
|
||||
$builder = new PockBuilder();
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchOrigin(self::TEST_HOST)
|
||||
->matchPathRegExp('/^\/?test$/')
|
||||
->reply(200);
|
||||
|
||||
$response = $builder->getClient()->sendRequest(
|
||||
static::getTestRequest()->withUri(static::getPsr17Factory()->createUri('https://test.com/test'))
|
||||
);
|
||||
|
||||
self::assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testMatchQueryRegExp(): void
|
||||
{
|
||||
$builder = new PockBuilder();
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchOrigin(self::TEST_HOST)
|
||||
->matchQueryRegExp('/\d+-\d+/')
|
||||
->reply(200);
|
||||
|
||||
$response = $builder->getClient()->sendRequest(
|
||||
static::getTestRequest()->withUri(
|
||||
static::getPsr17Factory()->createUri(self::TEST_URI)
|
||||
->withQuery('param=23-900')
|
||||
)
|
||||
);
|
||||
|
||||
self::assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testMatchUriRegExp(): void
|
||||
{
|
||||
$builder = new PockBuilder();
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchUriRegExp('/https\:\/\/\w+\.com\/\d+-\d+\?param=\d+-\d+/')
|
||||
->reply(200);
|
||||
|
||||
$response = $builder->getClient()->sendRequest(
|
||||
static::getTestRequest()->withUri(
|
||||
static::getPsr17Factory()->createUri('https://example.com/23-900')
|
||||
->withQuery('param=23-900')
|
||||
)
|
||||
);
|
||||
|
||||
self::assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testMatchFormData(): void
|
||||
{
|
||||
$builder = new PockBuilder();
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchUri(self::TEST_URI)
|
||||
->matchFormData(['field1' => 'value1', 'field2' => 'value2'])
|
||||
->reply(200);
|
||||
|
||||
$response = $builder->getClient()->sendRequest(self::getRequestWithBody('field1=value1&field2=value2'));
|
||||
|
||||
self::assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testMatchExactFormData(): void
|
||||
{
|
||||
$builder = new PockBuilder();
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchUri(self::TEST_URI)
|
||||
->matchExactFormData(['field2' => 'value2'])
|
||||
->reply(200);
|
||||
|
||||
$response = $builder->getClient()->sendRequest(self::getRequestWithBody('field2=value2'));
|
||||
|
||||
self::assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
public function testFirstExampleApiMock(): void
|
||||
{
|
||||
$data = [
|
||||
|
@ -430,6 +834,19 @@ EOF;
|
|||
->withHeader('Content-Type', 'text/plain')
|
||||
->withBody('Second token (post json)');
|
||||
|
||||
$builder->matchMethod(RequestMethod::POST)
|
||||
->matchScheme(RequestScheme::HTTPS)
|
||||
->matchHost(self::TEST_HOST)
|
||||
->matchPath('/ping')
|
||||
->matchHeaders([
|
||||
'Authorization' => 'Token token_3',
|
||||
'Content-Type' => 'application/json'
|
||||
])
|
||||
->matchSerializedJsonBody('{"field": "value3"}')
|
||||
->reply(200)
|
||||
->withHeader('Content-Type', 'text/plain')
|
||||
->withBody('Third token (post json with match against serialized data)');
|
||||
|
||||
$builder->matchMethod(RequestMethod::POST)
|
||||
->matchScheme(RequestScheme::HTTPS)
|
||||
->matchHost(self::TEST_HOST)
|
||||
|
@ -498,6 +915,19 @@ EOF;
|
|||
);
|
||||
self::assertEquals('Second token (post json)', $response->getBody()->getContents());
|
||||
|
||||
$response = $client->sendRequest(
|
||||
self::getPsr17Factory()
|
||||
->createRequest(RequestMethod::POST, self::TEST_URI)
|
||||
->withHeader('Authorization', 'Token token_3')
|
||||
->withHeader('Content-Type', 'application/json')
|
||||
->withUri(self::getPsr17Factory()->createUri(self::TEST_URI . 'ping'))
|
||||
->withBody(self::getPsr17Factory()->createStream('{"field": "value3"}'))
|
||||
);
|
||||
self::assertEquals(
|
||||
'Third token (post json with match against serialized data)',
|
||||
$response->getBody()->getContents()
|
||||
);
|
||||
|
||||
$response = $client->sendRequest(
|
||||
self::getPsr17Factory()
|
||||
->createRequest(RequestMethod::POST, self::TEST_URI)
|
||||
|
@ -507,4 +937,141 @@ EOF;
|
|||
);
|
||||
self::assertEquals('Second token (post)', $response->getBody()->getContents());
|
||||
}
|
||||
|
||||
public function testAlways(): void
|
||||
{
|
||||
$builder = new PockBuilder();
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchUri(self::TEST_URI)
|
||||
->always()
|
||||
->reply(200)
|
||||
->withHeader('Content-Type', 'text/plain')
|
||||
->withBody('Successful');
|
||||
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$response = $builder->getClient()->sendRequest(
|
||||
self::getPsr17Factory()->createRequest(RequestMethod::GET, self::TEST_URI)
|
||||
);
|
||||
|
||||
self::assertEquals(200, $response->getStatusCode());
|
||||
self::assertEquals(['Content-Type' => ['text/plain']], $response->getHeaders());
|
||||
self::assertEquals('Successful', $response->getBody()->getContents());
|
||||
}
|
||||
}
|
||||
|
||||
public function testAt(): void
|
||||
{
|
||||
$builder = new PockBuilder();
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchUri(self::TEST_URI)
|
||||
->at(2)
|
||||
->reply(200);
|
||||
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchUri(self::TEST_URI)
|
||||
->at(4)
|
||||
->reply(201);
|
||||
|
||||
$builder->always()->reply(400);
|
||||
$builder->getClient();
|
||||
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$response = $builder->getClient()->sendRequest(
|
||||
self::getPsr17Factory()->createRequest(RequestMethod::GET, self::TEST_URI)
|
||||
);
|
||||
|
||||
self::assertEquals(1 === $i ? 200 : (4 === $i ? 201 : 400), $response->getStatusCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function testReplyWithFactory(): void
|
||||
{
|
||||
$builder = new PockBuilder();
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchUri(self::TEST_URI)
|
||||
->always()
|
||||
->replyWithFactory(new TestReplyFactory());
|
||||
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$response = $builder->getClient()->sendRequest(
|
||||
self::getPsr17Factory()->createRequest(RequestMethod::GET, self::TEST_URI)
|
||||
);
|
||||
|
||||
self::assertEquals(200, $response->getStatusCode());
|
||||
self::assertEquals('Request #' . ($i + 1), $response->getBody()->getContents());
|
||||
}
|
||||
}
|
||||
|
||||
public function testReplyWithCallback(): void
|
||||
{
|
||||
$builder = new PockBuilder();
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchUri(self::TEST_URI)
|
||||
->always()
|
||||
->replyWithCallback(static function (RequestInterface $request, PockResponseBuilder $responseBuilder) {
|
||||
return $responseBuilder->withStatusCode(200)
|
||||
->withBody(self::TEST_URI)
|
||||
->getResponse();
|
||||
});
|
||||
|
||||
$response = $builder->getClient()->sendRequest(
|
||||
self::getPsr17Factory()->createRequest(RequestMethod::GET, self::TEST_URI)
|
||||
);
|
||||
|
||||
self::assertEquals(200, $response->getStatusCode());
|
||||
self::assertEquals(self::TEST_URI, $response->getBody()->getContents());
|
||||
}
|
||||
|
||||
public function testReplyWithCallbackException(): void
|
||||
{
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectExceptionMessage('Exception from the callback');
|
||||
|
||||
$builder = new PockBuilder();
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchUri(self::TEST_URI)
|
||||
->always()
|
||||
->replyWithCallback(static function (RequestInterface $request, PockResponseBuilder $responseBuilder) {
|
||||
throw new RuntimeException('Exception from the callback');
|
||||
});
|
||||
|
||||
$builder->getClient()->sendRequest(self::getPsr17Factory()->createRequest(
|
||||
RequestMethod::GET,
|
||||
self::TEST_URI
|
||||
));
|
||||
}
|
||||
|
||||
public function testReplyWithClient(): void
|
||||
{
|
||||
$inlined = new PockBuilder();
|
||||
$inlined->reply(429);
|
||||
|
||||
$builder = new PockBuilder();
|
||||
$builder->matchMethod(RequestMethod::GET)
|
||||
->matchUri(self::TEST_URI)
|
||||
->always()
|
||||
->replyWithClient($inlined->getClient());
|
||||
|
||||
$response = $builder->getClient()->sendRequest(self::getPsr17Factory()->createRequest(
|
||||
RequestMethod::GET,
|
||||
self::TEST_URI
|
||||
));
|
||||
|
||||
self::assertEquals(429, $response->getStatusCode());
|
||||
}
|
||||
|
||||
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]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
29
tests/src/Serializer/CallbackSerializerAdapterTest.php
Normal file
29
tests/src/Serializer/CallbackSerializerAdapterTest.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.1
|
||||
*
|
||||
* @category CallbackSerializerAdapterTest
|
||||
* @package Pock\Tests\Serializer
|
||||
*/
|
||||
|
||||
namespace Pock\Tests\Serializer;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Pock\Serializer\CallbackSerializerAdapter;
|
||||
|
||||
/**
|
||||
* Class CallbackSerializerAdapterTest
|
||||
*
|
||||
* @category CallbackSerializerAdapterTest
|
||||
* @package Pock\Tests\Serializer
|
||||
*/
|
||||
class CallbackSerializerAdapterTest extends TestCase
|
||||
{
|
||||
public function testSerialize(): void
|
||||
{
|
||||
self::assertEquals('{}', (new CallbackSerializerAdapter(function ($data) {
|
||||
return $data;
|
||||
}))->serialize('{}'));
|
||||
}
|
||||
}
|
|
@ -13,6 +13,9 @@ use Nyholm\Psr7\Factory\Psr17Factory;
|
|||
use PHPUnit\Framework\TestCase;
|
||||
use Pock\Enum\RequestMethod;
|
||||
use Pock\Enum\RequestScheme;
|
||||
use Pock\Factory\JsonSerializerFactory;
|
||||
use Pock\Factory\XmlSerializerFactory;
|
||||
use Pock\Serializer\SerializerInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
|
@ -41,6 +44,19 @@ abstract class PockTestCase extends TestCase
|
|||
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
|
||||
*/
|
||||
|
@ -52,4 +68,14 @@ abstract class PockTestCase extends TestCase
|
|||
|
||||
return static::$psr17Factory;
|
||||
}
|
||||
|
||||
protected static function getJsonSerializer(): SerializerInterface
|
||||
{
|
||||
return JsonSerializerFactory::create();
|
||||
}
|
||||
|
||||
protected static function getXmlSerializer(): SerializerInterface
|
||||
{
|
||||
return XmlSerializerFactory::create();
|
||||
}
|
||||
}
|
||||
|
|
37
tests/utils/TestReplyFactory.php
Normal file
37
tests/utils/TestReplyFactory.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category TestReplyFactory
|
||||
* @package Pock\TestUtils
|
||||
*/
|
||||
|
||||
namespace Pock\TestUtils;
|
||||
|
||||
use Pock\Factory\ReplyFactoryInterface;
|
||||
use Pock\PockResponseBuilder;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Class TestReplyFactory
|
||||
*
|
||||
* @category TestReplyFactory
|
||||
* @package Pock\TestUtils
|
||||
*/
|
||||
class TestReplyFactory implements ReplyFactoryInterface
|
||||
{
|
||||
/** @var int */
|
||||
private $requestNumber = 0;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function createReply(RequestInterface $request, PockResponseBuilder $responseBuilder): ResponseInterface
|
||||
{
|
||||
return $responseBuilder->withStatusCode(200)
|
||||
->withBody('Request #' . ++$this->requestNumber)
|
||||
->getResponse();
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue