mirror of
https://github.com/Neur0toxine/pock.git
synced 2024-12-01 09:26:02 +03:00
first code
This commit is contained in:
parent
5fb3636afc
commit
2429eab7f8
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
composer.lock
|
||||
vendor/*
|
||||
.idea/*
|
5
README.md
Normal file
5
README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# pock
|
||||
|
||||
Easy to use HTTP mocking solution, compatible with PSR-18 and HTTPlug. Should resemble [gock](https://github.com/h2non/gock) in the end.
|
||||
|
||||
Project in its early development stage. Stay tuned!
|
28
composer.json
Normal file
28
composer.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "neur0toxine/pock",
|
||||
"description": "PSR-18 compatible HTTP mock library",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Neur0toxine",
|
||||
"email": "pashok9825@gmail.com"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Pock\\": "src/"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1.0",
|
||||
"psr/http-client": "^1.0",
|
||||
"psr/http-message": "^1.0",
|
||||
"php-http/httplug": "^1.0 || ^2.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-client-implementation": "1.0",
|
||||
"php-http/client-implementation": "1.0",
|
||||
"php-http/async-client-implementation": "1.0"
|
||||
}
|
||||
}
|
91
src/Client.php
Normal file
91
src/Client.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category Client
|
||||
* @package Pock
|
||||
*/
|
||||
|
||||
namespace Pock;
|
||||
|
||||
use Http\Client\HttpAsyncClient;
|
||||
use Http\Client\HttpClient;
|
||||
use Http\Client\Promise\HttpFulfilledPromise;
|
||||
use Http\Promise\Promise;
|
||||
use Pock\Exception\BrokenMockException;
|
||||
use Pock\Exception\UnsupportedRequestException;
|
||||
use Pock\Promise\HttpRejectedPromise;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Class Client
|
||||
*
|
||||
* @category Client
|
||||
* @package Pock
|
||||
*/
|
||||
class Client implements ClientInterface, HttpClient, HttpAsyncClient
|
||||
{
|
||||
/** @var \Pock\MockInterface[] */
|
||||
private $mocks;
|
||||
|
||||
/**
|
||||
* Client constructor.
|
||||
*
|
||||
* @param \Pock\MockInterface[] $mocks
|
||||
*/
|
||||
public function __construct(array $mocks)
|
||||
{
|
||||
$this->mocks = $mocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function doSendRequest(RequestInterface $request): ResponseInterface
|
||||
{
|
||||
return $this->sendAsyncRequest($request)->wait();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function sendRequest(RequestInterface $request): ResponseInterface
|
||||
{
|
||||
return $this->doSendRequest($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function sendAsyncRequest(RequestInterface $request): Promise
|
||||
{
|
||||
foreach ($this->mocks as $mock) {
|
||||
if ($mock->isFired()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($mock->getMatcher()->matches($request)) {
|
||||
if (null !== $mock->getResponse()) {
|
||||
$mock->markAsFired();
|
||||
|
||||
return new HttpFulfilledPromise($mock->getResponse());
|
||||
}
|
||||
|
||||
if (null !== $mock->getThrowable()) {
|
||||
$mock->markAsFired();
|
||||
|
||||
return new HttpRejectedPromise($mock->getThrowable());
|
||||
}
|
||||
|
||||
throw new BrokenMockException($mock);
|
||||
}
|
||||
}
|
||||
|
||||
throw new UnsupportedRequestException();
|
||||
}
|
||||
}
|
45
src/Exception/BrokenMockException.php
Normal file
45
src/Exception/BrokenMockException.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category BrokenMockException
|
||||
* @package Pock\Exception
|
||||
*/
|
||||
|
||||
namespace Pock\Exception;
|
||||
|
||||
use Exception;
|
||||
use Pock\MockInterface;
|
||||
|
||||
/**
|
||||
* Class BrokenMockException
|
||||
*
|
||||
* @category BrokenMockException
|
||||
* @package Pock\Exception
|
||||
*/
|
||||
class BrokenMockException extends Exception
|
||||
{
|
||||
/** @var \Pock\MockInterface */
|
||||
private $mock;
|
||||
|
||||
/**
|
||||
* AbstractMockException constructor.
|
||||
*
|
||||
* @param \Pock\MockInterface $mock
|
||||
*/
|
||||
public function __construct(MockInterface $mock)
|
||||
{
|
||||
parent::__construct('Neither response nor the throwable is set in the mock.');
|
||||
|
||||
$this->mock = $mock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Pock\MockInterface
|
||||
*/
|
||||
public function getMock(): MockInterface
|
||||
{
|
||||
return $this->mock;
|
||||
}
|
||||
}
|
50
src/Exception/UniversalMockException.php
Normal file
50
src/Exception/UniversalMockException.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @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
|
||||
{
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* UniversalMockException constructor.
|
||||
*
|
||||
* @param $request
|
||||
*/
|
||||
public function __construct($request)
|
||||
{
|
||||
parent::__construct('Default mock exception');
|
||||
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getRequest(): RequestInterface
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
}
|
26
src/Exception/UnsupportedRequestException.php
Normal file
26
src/Exception/UnsupportedRequestException.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category UnsupportedRequestException
|
||||
* @package Pock\Exception
|
||||
*/
|
||||
|
||||
namespace Pock\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class UnsupportedRequestException
|
||||
*
|
||||
* @category UnsupportedRequestException
|
||||
* @package Pock\Exception
|
||||
*/
|
||||
class UnsupportedRequestException extends Exception
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('Cannot match request with any available matchers');
|
||||
}
|
||||
}
|
29
src/Matchers/AnyRequestMatcher.php
Normal file
29
src/Matchers/AnyRequestMatcher.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category AnyRequestMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Matchers;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Class AnyRequestMatcher
|
||||
*
|
||||
* @category AnyRequestMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
class AnyRequestMatcher implements RequestMatcherInterface
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function matches(RequestInterface $request): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
42
src/Matchers/HostMatcher.php
Normal file
42
src/Matchers/HostMatcher.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category HostMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Matchers;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Class HostMatcher
|
||||
*
|
||||
* @category HostMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
class HostMatcher implements RequestMatcherInterface
|
||||
{
|
||||
/** @var string */
|
||||
private $host;
|
||||
|
||||
/**
|
||||
* HostMatcher constructor.
|
||||
*
|
||||
* @param string $host
|
||||
*/
|
||||
public function __construct(string $host)
|
||||
{
|
||||
$this->host = $host;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function matches(RequestInterface $request): bool
|
||||
{
|
||||
return $request->getUri()->getHost() === $this->host;
|
||||
}
|
||||
}
|
70
src/Matchers/MultipleMatcher.php
Normal file
70
src/Matchers/MultipleMatcher.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category MultipleMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Matchers;
|
||||
|
||||
use Countable;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Class MultipleMatcher
|
||||
*
|
||||
* @category MultipleMatcher
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
class MultipleMatcher implements RequestMatcherInterface, Countable
|
||||
{
|
||||
/** @var \Pock\Matchers\RequestMatcherInterface[] */
|
||||
public $matchers;
|
||||
|
||||
/**
|
||||
* MultipleMatcher constructor.
|
||||
*
|
||||
* @param \Pock\Matchers\RequestMatcherInterface[] $matchers
|
||||
*/
|
||||
public function __construct(array $matchers = [])
|
||||
{
|
||||
$this->matchers = $matchers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Pock\Matchers\RequestMatcherInterface $matcher
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addMatcher(RequestMatcherInterface $matcher): self
|
||||
{
|
||||
$this->matchers[] = $matcher;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->matchers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Http\Message\RequestInterface $request
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function matches(RequestInterface $request): bool
|
||||
{
|
||||
foreach ($this->matchers as $matcher) {
|
||||
if (!$matcher->matches($request)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
30
src/Matchers/RequestMatcherInterface.php
Normal file
30
src/Matchers/RequestMatcherInterface.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category RequestMatcherInterface
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
|
||||
namespace Pock\Matchers;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Interface RequestMatcherInterface
|
||||
*
|
||||
* @category RequestMatcherInterface
|
||||
* @package Pock\Matchers
|
||||
*/
|
||||
interface RequestMatcherInterface
|
||||
{
|
||||
/**
|
||||
* Returns true if request is matched by this matcher.
|
||||
*
|
||||
* @param \Psr\Http\Message\RequestInterface $request
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function matches(RequestInterface $request): bool;
|
||||
}
|
87
src/Mock.php
Normal file
87
src/Mock.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category Mock
|
||||
* @package Pock
|
||||
*/
|
||||
|
||||
namespace Pock;
|
||||
|
||||
use Pock\Matchers\RequestMatcherInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Class Mock
|
||||
*
|
||||
* @category Mock
|
||||
* @package Pock
|
||||
*/
|
||||
class Mock implements MockInterface
|
||||
{
|
||||
/** @var \Pock\Matchers\RequestMatcherInterface */
|
||||
private $matcher;
|
||||
|
||||
/** @var \Psr\Http\Message\ResponseInterface|null */
|
||||
private $response;
|
||||
|
||||
/** @var \Throwable|null */
|
||||
private $throwable;
|
||||
|
||||
/** @var bool */
|
||||
private $fired = false;
|
||||
|
||||
/**
|
||||
* Mock constructor.
|
||||
*
|
||||
* @param \Pock\Matchers\RequestMatcherInterface $matcher
|
||||
* @param \Psr\Http\Message\ResponseInterface|null $response
|
||||
* @param \Throwable|null $throwable
|
||||
*/
|
||||
public function __construct(RequestMatcherInterface $matcher, ?ResponseInterface $response, ?Throwable $throwable)
|
||||
{
|
||||
$this->matcher = $matcher;
|
||||
$this->response = $response;
|
||||
$this->throwable = $throwable;
|
||||
}
|
||||
|
||||
public function markAsFired(): MockInterface
|
||||
{
|
||||
$this->fired = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isFired(): bool
|
||||
{
|
||||
return $this->fired;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Pock\Matchers\RequestMatcherInterface
|
||||
*/
|
||||
public function getMatcher(): RequestMatcherInterface
|
||||
{
|
||||
return $this->matcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Psr\Http\Message\ResponseInterface|null
|
||||
*/
|
||||
public function getResponse(): ?ResponseInterface
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Throwable|null
|
||||
*/
|
||||
public function getThrowable(): ?Throwable
|
||||
{
|
||||
return $this->throwable;
|
||||
}
|
||||
}
|
58
src/MockInterface.php
Normal file
58
src/MockInterface.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category MockInterface
|
||||
* @package Pock
|
||||
*/
|
||||
|
||||
namespace Pock;
|
||||
|
||||
use Pock\Matchers\RequestMatcherInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Interface MockInterface
|
||||
*
|
||||
* @category MockInterface
|
||||
* @package Pock
|
||||
*/
|
||||
interface MockInterface
|
||||
{
|
||||
/**
|
||||
* Marks mock as already used.
|
||||
*
|
||||
* @return \Pock\MockInterface
|
||||
*/
|
||||
public function markAsFired(): MockInterface;
|
||||
|
||||
/**
|
||||
* Returns true if mock was not used yet.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFired(): bool;
|
||||
|
||||
/**
|
||||
* Returns matcher for the request.
|
||||
*
|
||||
* @return \Pock\Matchers\RequestMatcherInterface
|
||||
*/
|
||||
public function getMatcher(): RequestMatcherInterface;
|
||||
|
||||
/**
|
||||
* Returns response which should be used as mock data.
|
||||
*
|
||||
* @return \Psr\Http\Message\ResponseInterface|null
|
||||
*/
|
||||
public function getResponse(): ?ResponseInterface;
|
||||
|
||||
/**
|
||||
* Returns the throwable which will be thrown as mock data.
|
||||
*
|
||||
* @return \Throwable|null
|
||||
*/
|
||||
public function getThrowable(): ?Throwable;
|
||||
}
|
89
src/PockBuilder.php
Normal file
89
src/PockBuilder.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category PockBuilder
|
||||
* @package Pock
|
||||
*/
|
||||
|
||||
namespace Pock;
|
||||
|
||||
use Pock\Matchers\AnyRequestMatcher;
|
||||
use Pock\Matchers\HostMatcher;
|
||||
use Pock\Matchers\MultipleMatcher;
|
||||
|
||||
/**
|
||||
* Class PockBuilder
|
||||
*
|
||||
* @category PockBuilder
|
||||
* @package Pock
|
||||
*/
|
||||
class PockBuilder
|
||||
{
|
||||
/** @var \Pock\Matchers\MultipleMatcher */
|
||||
private $matcher;
|
||||
|
||||
/** @var \Psr\Http\Message\ResponseInterface|null */
|
||||
private $response;
|
||||
|
||||
/** @var \Throwable|null */
|
||||
private $throwable;
|
||||
|
||||
/** @var \Pock\MockInterface[] */
|
||||
private $mocks;
|
||||
|
||||
/**
|
||||
* PockBuilder constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches request by hostname.
|
||||
*
|
||||
* @param string $host
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function host(string $host): PockBuilder
|
||||
{
|
||||
$this->closePrevious();
|
||||
$this->matcher->addMatcher(new HostMatcher($host));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the builder.
|
||||
*
|
||||
* @return \Pock\PockBuilder
|
||||
*/
|
||||
public function reset(): PockBuilder
|
||||
{
|
||||
$this->matcher = new MultipleMatcher();
|
||||
$this->response = null;
|
||||
$this->throwable = null;
|
||||
$this->mocks = [];
|
||||
}
|
||||
|
||||
public function getClient(): Client
|
||||
{
|
||||
return new Client($this->mocks);
|
||||
}
|
||||
|
||||
private function closePrevious(): void
|
||||
{
|
||||
if (null !== $this->response || null !== $this->throwable) {
|
||||
if (0 === count($this->matcher)) {
|
||||
$this->matcher->addMatcher(new AnyRequestMatcher());
|
||||
}
|
||||
|
||||
$this->mocks[] = new Mock($this->matcher, $this->response, $this->throwable);
|
||||
$this->response = null;
|
||||
$this->throwable = null;
|
||||
}
|
||||
}
|
||||
}
|
72
src/Promise/HttpRejectedPromise.php
Normal file
72
src/Promise/HttpRejectedPromise.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP 7.3
|
||||
*
|
||||
* @category HttpRejectedPromise
|
||||
* @package Pock\Promise
|
||||
*/
|
||||
|
||||
namespace Pock\Promise;
|
||||
|
||||
use Http\Client\Exception;
|
||||
use Http\Client\Promise\HttpFulfilledPromise;
|
||||
use Http\Promise\Promise;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Class HttpRejectedPromise
|
||||
*
|
||||
* @category HttpRejectedPromise
|
||||
* @package Pock\Promise
|
||||
*/
|
||||
class HttpRejectedPromise implements Promise
|
||||
{
|
||||
/** @var \Throwable */
|
||||
private $throwable;
|
||||
|
||||
/**
|
||||
* HttpRejectedPromise constructor.
|
||||
*
|
||||
* @param \Throwable $throwable
|
||||
*/
|
||||
public function __construct(Throwable $throwable)
|
||||
{
|
||||
$this->throwable = $throwable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function then(callable $onFulfilled = null, callable $onRejected = null)
|
||||
{
|
||||
if (null === $onRejected) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
try {
|
||||
return new HttpFulfilledPromise($onRejected($this->throwable));
|
||||
} catch (Exception $e) {
|
||||
return new self($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getState(): string
|
||||
{
|
||||
return Promise::REJECTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function wait($unwrap = true): void
|
||||
{
|
||||
if ($unwrap) {
|
||||
throw $this->throwable;
|
||||
}
|
||||
}
|
||||
}
|
0
tests/.gitkeep
Normal file
0
tests/.gitkeep
Normal file
Loading…
Reference in New Issue
Block a user