From cd8a42c2c1dcb58e175477a87dbced9ae127a697 Mon Sep 17 00:00:00 2001 From: parsilver Date: Mon, 2 Mar 2026 11:58:50 +0700 Subject: [PATCH 1/7] Upgrade farzai/transport to v2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update farzai/transport dependency from ^1.2 to ^2.1 - Migrate ClientBuilder to use new TransportBuilder API (withBaseUri, setLogger) - Replace Request class with RequestBuilder in PendingRequest - Remove PsrResponseTrait usage and implement PSR-7 delegation directly in AbstractResponseDecorator - Add jsonOrNull() and toArray() methods to AbstractResponseDecorator - Fix typo: isSuccessfull() → isSuccessful() - Fix throw() return type to static for proper chaining - Add guzzlehttp/guzzle as dev dependency - Add .phpunit.cache to .gitignore --- .gitignore | 1 + composer.json | 3 +- src/ClientBuilder.php | 10 +- src/Requests/PendingRequest.php | 20 +-- src/Responses/AbstractResponseDecorator.php | 116 ++++++++++++++++-- .../ResponseWithValidateErrorCode.php | 11 +- tests/ResponseErrorCodeTest.php | 2 +- 7 files changed, 132 insertions(+), 31 deletions(-) diff --git a/.gitignore b/.gitignore index 00403f7..b32743b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /.fleet .php_cs .php_cs.cache +.phpunit.cache .phpunit.result.cache build composer.lock diff --git a/composer.json b/composer.json index c376929..808f7f8 100644 --- a/composer.json +++ b/composer.json @@ -16,10 +16,11 @@ "require": { "php": "^8.1", "farzai/support": "^1.2", - "farzai/transport": "^1.2", + "farzai/transport": "^2.1", "phrity/websocket": "^3.0" }, "require-dev": { + "guzzlehttp/guzzle": "^7.8", "pestphp/pest": "^2.15", "laravel/pint": "^1.0", "spatie/ray": "^1.28" diff --git a/src/ClientBuilder.php b/src/ClientBuilder.php index 7372c8e..a6ff4ba 100644 --- a/src/ClientBuilder.php +++ b/src/ClientBuilder.php @@ -100,15 +100,15 @@ public function build() $logger = $this->logger ?? new NullLogger(); - $builder = TransportBuilder::make(); + $builder = TransportBuilder::make() + ->withBaseUri(sprintf('https://%s', self::DEFAULT_HOST)) + ->setLogger($logger); + if ($this->httpClient) { - $builder->setClient($this->httpClient); + $builder = $builder->setClient($this->httpClient); } - $builder->setLogger($logger); - $transport = $builder->build(); - $transport->setUri(sprintf('https://%s', self::DEFAULT_HOST)); $client = new Client( config: $this->config, diff --git a/src/Requests/PendingRequest.php b/src/Requests/PendingRequest.php index ac2f055..dfdfcfe 100644 --- a/src/Requests/PendingRequest.php +++ b/src/Requests/PendingRequest.php @@ -6,7 +6,7 @@ use Farzai\Bitkub\Contracts\RequestInterceptor; use Farzai\Bitkub\Responses\ResponseWithValidateErrorCode; use Farzai\Transport\Contracts\ResponseInterface; -use Farzai\Transport\Request; +use Farzai\Transport\RequestBuilder; use Farzai\Transport\Response; use Psr\Http\Message\RequestInterface as PsrRequestInterface; use Psr\Http\Message\ResponseInterface as PsrResponseInterface; @@ -146,25 +146,27 @@ public function createRequest(string $method, string $path, array $options = []) // Normalize path $path = '/'.trim($path, '/'); + $builder = (new RequestBuilder)->method($method)->uri($path); + // Query if (isset($options['query']) && is_array($options['query']) && ! empty($options['query'])) { - $path .= '?'.http_build_query($options['query']); + $builder = $builder->withQuery($options['query']); } // Set body if (isset($options['body'])) { $body = $options['body']; - - // Convert array to json - if (is_array($body)) { - $body = json_encode($body); - } + $builder = is_array($body) + ? $builder->withJson($body) + : $builder->withBody($body); } // Set headers - $headers = $options['headers'] ?? []; + if (! empty($options['headers'])) { + $builder = $builder->withHeaders($options['headers']); + } - return new Request($method, $path, $headers, $body ?? null); + return $builder->build(); } /** diff --git a/src/Responses/AbstractResponseDecorator.php b/src/Responses/AbstractResponseDecorator.php index e9a86a5..5b0be0e 100644 --- a/src/Responses/AbstractResponseDecorator.php +++ b/src/Responses/AbstractResponseDecorator.php @@ -3,13 +3,11 @@ namespace Farzai\Bitkub\Responses; use Farzai\Transport\Contracts\ResponseInterface; -use Farzai\Transport\Traits\PsrResponseTrait; use Psr\Http\Message\RequestInterface as PsrRequestInterface; +use Psr\Http\Message\StreamInterface; abstract class AbstractResponseDecorator implements ResponseInterface { - use PsrResponseTrait; - protected ResponseInterface $response; /** @@ -45,11 +43,11 @@ public function headers(): array } /** - * Check if the response is successfull. + * Check if the response is successful. */ - public function isSuccessfull(): bool + public function isSuccessful(): bool { - return $this->response->isSuccessfull(); + return $this->response->isSuccessful(); } /** @@ -61,15 +59,31 @@ public function json(?string $key = null): mixed } /** - * Throw an exception if the response is not successfull. - * - * @param callable<\Farzai\Transport\Contracts\ResponseInterface> $callback Custom callback to throw an exception. + * Return the json decoded response or null. + */ + public function jsonOrNull(?string $key = null): mixed + { + return $this->response->jsonOrNull($key); + } + + /** + * Return the response as an array. + */ + public function toArray(): array + { + return $this->response->toArray(); + } + + /** + * Throw an exception if the response is not successful. * * @throws \Psr\Http\Client\ClientExceptionInterface */ - public function throw(?callable $callback = null) + public function throw(?callable $callback = null): static { - return $this->response->throw($callback); + $this->response->throw($callback); + + return $this; } /** @@ -79,4 +93,84 @@ public function getPsrRequest(): PsrRequestInterface { return $this->response->getPsrRequest(); } + + // PSR-7 ResponseInterface delegation + + public function getStatusCode(): int + { + return $this->response->getStatusCode(); + } + + public function withStatus(int $code, string $reasonPhrase = ''): static + { + return $this->cloneWithResponse($this->response->withStatus($code, $reasonPhrase)); + } + + public function getReasonPhrase(): string + { + return $this->response->getReasonPhrase(); + } + + public function getProtocolVersion(): string + { + return $this->response->getProtocolVersion(); + } + + public function withProtocolVersion(string $version): static + { + return $this->cloneWithResponse($this->response->withProtocolVersion($version)); + } + + public function getHeaders(): array + { + return $this->response->getHeaders(); + } + + public function hasHeader(string $name): bool + { + return $this->response->hasHeader($name); + } + + public function getHeader(string $name): array + { + return $this->response->getHeader($name); + } + + public function getHeaderLine(string $name): string + { + return $this->response->getHeaderLine($name); + } + + public function withHeader(string $name, $value): static + { + return $this->cloneWithResponse($this->response->withHeader($name, $value)); + } + + public function withAddedHeader(string $name, $value): static + { + return $this->cloneWithResponse($this->response->withAddedHeader($name, $value)); + } + + public function withoutHeader(string $name): static + { + return $this->cloneWithResponse($this->response->withoutHeader($name)); + } + + public function getBody(): StreamInterface + { + return $this->response->getBody(); + } + + public function withBody(StreamInterface $body): static + { + return $this->cloneWithResponse($this->response->withBody($body)); + } + + private function cloneWithResponse(ResponseInterface $response): static + { + $clone = clone $this; + $clone->response = $response; + + return $clone; + } } diff --git a/src/Responses/ResponseWithValidateErrorCode.php b/src/Responses/ResponseWithValidateErrorCode.php index 413ee63..3e5464b 100644 --- a/src/Responses/ResponseWithValidateErrorCode.php +++ b/src/Responses/ResponseWithValidateErrorCode.php @@ -8,19 +8,22 @@ class ResponseWithValidateErrorCode extends AbstractResponseDecorator { /** - * Throw an exception if the response is not successfull. + * Throw an exception if the response is not successful. * * * @throws \Psr\Http\Client\ClientExceptionInterface */ - public function throw(?callable $callback = null) + public function throw(?callable $callback = null): static { - return parent::throw($callback ?? function (ResponseInterface $response, ?\Exception $e) use ($callback) { - if ($this->json('error') !== null && $this->json('error') !== 0) { + parent::throw($callback ?? function (ResponseInterface $response, ?\Exception $e) use ($callback) { + $errorCode = $this->json('error'); + if ($errorCode !== null && $errorCode !== 0) { throw new BitkubResponseErrorCodeException($response); } return $callback ? $callback($response, $e) : $response; }); + + return $this; } } diff --git a/tests/ResponseErrorCodeTest.php b/tests/ResponseErrorCodeTest.php index 4a78d16..eb961b8 100644 --- a/tests/ResponseErrorCodeTest.php +++ b/tests/ResponseErrorCodeTest.php @@ -27,7 +27,7 @@ expect($response->headers())->toBe([ 'Content-Type' => 'application/json', ]); - expect($response->isSuccessfull())->toBeTrue(); + expect($response->isSuccessful())->toBeTrue(); expect($response->json('error'))->toBe(0); expect($response->getPsrRequest())->toBe($psrRequest); }); From 63cc15a9aa112cb67ced167adf3e521ceb61db88 Mon Sep 17 00:00:00 2001 From: parsilver <4928451+parsilver@users.noreply.github.com> Date: Mon, 2 Mar 2026 04:59:16 +0000 Subject: [PATCH 2/7] Fix styling --- src/ClientBuilder.php | 9 +++------ src/Endpoints/MarketEndpoint.php | 4 ++-- src/WebSocket/Endpoints/LiveOrderBookEndpoint.php | 2 +- src/WebSocket/Engine.php | 7 +++---- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/ClientBuilder.php b/src/ClientBuilder.php index a6ff4ba..129970a 100644 --- a/src/ClientBuilder.php +++ b/src/ClientBuilder.php @@ -41,7 +41,7 @@ final class ClientBuilder */ public static function create() { - return new self(); + return new self; } /** @@ -98,7 +98,7 @@ public function build() { $this->ensureConfigIsValid(); - $logger = $this->logger ?? new NullLogger(); + $logger = $this->logger ?? new NullLogger; $builder = TransportBuilder::make() ->withBaseUri(sprintf('https://%s', self::DEFAULT_HOST)) @@ -130,8 +130,5 @@ private function ensureConfigIsValid(): void } } - final public function __construct() - { - - } + final public function __construct() {} } diff --git a/src/Endpoints/MarketEndpoint.php b/src/Endpoints/MarketEndpoint.php index 3c6e0ed..6abc414 100644 --- a/src/Endpoints/MarketEndpoint.php +++ b/src/Endpoints/MarketEndpoint.php @@ -35,7 +35,7 @@ public function symbols(): ResponseInterface /** * Get ticker information. * - * @param string $symbol (optional) The symbol (e.g. btc_thb) + * @param string $symbol (optional) The symbol (e.g. btc_thb) * * @response * { @@ -388,7 +388,7 @@ public function balances(): ResponseInterface * List all open orders of the given symbol. * Note : The client_id of this API response is the input body field name client_id , was inputted by the user of APIs. * - * @param string $sym The symbol (e.g. btc_thb) + * @param string $sym The symbol (e.g. btc_thb) * * @response * { diff --git a/src/WebSocket/Endpoints/LiveOrderBookEndpoint.php b/src/WebSocket/Endpoints/LiveOrderBookEndpoint.php index ef9ac32..7900050 100644 --- a/src/WebSocket/Endpoints/LiveOrderBookEndpoint.php +++ b/src/WebSocket/Endpoints/LiveOrderBookEndpoint.php @@ -13,7 +13,7 @@ class LiveOrderBookEndpoint extends AbstractEndpoint * echo $message->json('sym'); * }); * - * @param string|int $symbol Symbol name or id. + * @param string|int $symbol Symbol name or id. * @param callable|array $listeners */ public function listen($symbol, $listeners) diff --git a/src/WebSocket/Engine.php b/src/WebSocket/Engine.php index 2076d9b..00feec9 100644 --- a/src/WebSocket/Engine.php +++ b/src/WebSocket/Engine.php @@ -14,8 +14,7 @@ class Engine implements WebSocketEngineInterface { public function __construct( private LoggerInterface $logger, - ) { - } + ) {} public function handle(array $listeners): void { @@ -28,8 +27,8 @@ public function handle(array $listeners): void $client = new WebSocketClient('wss://api.bitkub.com/websocket-api/'.implode(',', $events)); $client - ->addMiddleware(new WebSocketMiddleware\CloseHandler()) - ->addMiddleware(new WebSocketMiddleware\PingResponder()); + ->addMiddleware(new WebSocketMiddleware\CloseHandler) + ->addMiddleware(new WebSocketMiddleware\PingResponder); $client->onText(function (WebSocketClient $client, WebSocketConnection $connection, WebSocketMessage $message) use ($listeners) { $receivedAt = Carbon::now(); From 11559d1cd3418422bd3109190ee9c27b3d8b641d Mon Sep 17 00:00:00 2001 From: parsilver Date: Mon, 2 Mar 2026 12:02:18 +0700 Subject: [PATCH 3/7] fix(ci): bump minimum PHP to 8.2 and update CI matrix PHP 8.1 is EOL and pestphp/pest ^2.15 can no longer resolve dependencies on PHP 8.1 with --prefer-lowest due to brianium/paratest requiring PHP 8.2+. --- .github/workflows/run-tests.yml | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 0bdaf1f..06101de 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -9,7 +9,7 @@ jobs: fail-fast: true matrix: os: [ubuntu-latest, windows-latest] - php: [8.2, 8.1] + php: [8.3, 8.2] stability: [prefer-lowest, prefer-stable] name: P${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }} diff --git a/composer.json b/composer.json index 808f7f8..53881db 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ } ], "require": { - "php": "^8.1", + "php": "^8.2", "farzai/support": "^1.2", "farzai/transport": "^2.1", "phrity/websocket": "^3.0" From 6175ef5274ebd1afd34577ea118d736a406faa24 Mon Sep 17 00:00:00 2001 From: parsilver Date: Mon, 2 Mar 2026 12:06:11 +0700 Subject: [PATCH 4/7] ci: add PHP 8.4 and 8.5 to test matrix --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 06101de..136d11c 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -9,7 +9,7 @@ jobs: fail-fast: true matrix: os: [ubuntu-latest, windows-latest] - php: [8.3, 8.2] + php: [8.5, 8.4, 8.3, 8.2] stability: [prefer-lowest, prefer-stable] name: P${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }} From 1f2fd24a98e94db54aef613e478b3a7948d48090 Mon Sep 17 00:00:00 2001 From: parsilver Date: Mon, 2 Mar 2026 12:07:34 +0700 Subject: [PATCH 5/7] ci: exclude PHP 8.5 prefer-lowest from test matrix pestphp/pest ^2 cannot resolve dependencies on PHP 8.5 with --prefer-lowest due to brianium/paratest version conflicts. --- .github/workflows/run-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 136d11c..1671ce9 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -11,6 +11,9 @@ jobs: os: [ubuntu-latest, windows-latest] php: [8.5, 8.4, 8.3, 8.2] stability: [prefer-lowest, prefer-stable] + exclude: + - php: 8.5 + stability: prefer-lowest name: P${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }} From a3368eb7495f65db2f8e61772c17840b4becedf3 Mon Sep 17 00:00:00 2001 From: parsilver Date: Mon, 2 Mar 2026 12:09:00 +0700 Subject: [PATCH 6/7] ci: remove PHP 8.5 from test matrix pestphp/pest ^2 does not support PHP 8.5 in any version. --- .github/workflows/run-tests.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 1671ce9..1272433 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -9,11 +9,8 @@ jobs: fail-fast: true matrix: os: [ubuntu-latest, windows-latest] - php: [8.5, 8.4, 8.3, 8.2] + php: [8.4, 8.3, 8.2] stability: [prefer-lowest, prefer-stable] - exclude: - - php: 8.5 - stability: prefer-lowest name: P${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }} From b7d51d1fc4e98e55b23536a9509d7c789e1e6b00 Mon Sep 17 00:00:00 2001 From: parsilver Date: Mon, 2 Mar 2026 12:16:07 +0700 Subject: [PATCH 7/7] test: increase test coverage for transport v2 migration - AbstractResponseDecorator: test jsonOrNull, toArray, PSR-7 delegation - ResponseWithValidateErrorCode: test null error code, throw chaining - ClientBuilder: test setHttpClient, setLogger, setRetries - PendingRequest: test string body, no-options request, path normalization, createResponse --- tests/ClientBuilderTest.php | 36 ++++++++++++++++ tests/PendingRequestTest.php | 59 ++++++++++++++++++++++++++ tests/ResponseErrorCodeTest.php | 75 +++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) diff --git a/tests/ClientBuilderTest.php b/tests/ClientBuilderTest.php index 1b45b72..75142f0 100644 --- a/tests/ClientBuilderTest.php +++ b/tests/ClientBuilderTest.php @@ -26,3 +26,39 @@ $client->market()->balances(); })->throws(InvalidArgumentException::class, 'Secret key is required'); + +it('can set custom http client', function () { + $httpClient = $this->createMock(\Psr\Http\Client\ClientInterface::class); + + $client = ClientBuilder::create() + ->setCredentials('test', 'secret') + ->setHttpClient($httpClient) + ->build(); + + expect($client)->toBeInstanceOf(Client::class); +}); + +it('can set custom logger', function () { + $logger = $this->createMock(\Psr\Log\LoggerInterface::class); + + $client = ClientBuilder::create() + ->setCredentials('test', 'secret') + ->setLogger($logger) + ->build(); + + expect($client)->toBeInstanceOf(Client::class); +}); + +it('can set retries', function () { + $client = ClientBuilder::create() + ->setCredentials('test', 'secret') + ->setRetries(5) + ->build(); + + expect($client)->toBeInstanceOf(Client::class); +}); + +it('should throw exception when retries is negative', function () { + ClientBuilder::create() + ->setRetries(-1); +})->throws(\InvalidArgumentException::class, 'Retries must be greater than or equal to 0.'); diff --git a/tests/PendingRequestTest.php b/tests/PendingRequestTest.php index c88fc3f..06e3ca9 100644 --- a/tests/PendingRequestTest.php +++ b/tests/PendingRequestTest.php @@ -149,3 +149,62 @@ expect($request->getUri()->getQuery())->toBe('symbol=BTC'); expect($request->getHeaderLine('Content-Type'))->toBe('application/json'); }); + +it('can createRequest with string body', function () { + $client = ClientBuilder::create() + ->setCredentials('test', 'secret') + ->build(); + + $pending = new PendingRequest($client, 'POST', '/api/market/orders'); + + $request = $pending->createRequest('POST', '/api/market/orders', [ + 'body' => 'raw-string-body', + ]); + + expect($request)->toBeInstanceOf(\Psr\Http\Message\RequestInterface::class); + expect($request->getBody()->getContents())->toBe('raw-string-body'); +}); + +it('can createRequest without optional parameters', function () { + $client = ClientBuilder::create() + ->setCredentials('test', 'secret') + ->build(); + + $pending = new PendingRequest($client, 'GET', '/api/market/ticker'); + + $request = $pending->createRequest('GET', '/api/market/ticker'); + + expect($request)->toBeInstanceOf(\Psr\Http\Message\RequestInterface::class); + expect($request->getMethod())->toBe('GET'); + expect($request->getUri()->getPath())->toBe('/api/market/ticker'); + expect($request->getUri()->getQuery())->toBe(''); +}); + +it('normalizes path with leading slash', function () { + $client = ClientBuilder::create() + ->setCredentials('test', 'secret') + ->build(); + + $pending = new PendingRequest($client, 'GET', 'api/market/ticker'); + + $request = $pending->createRequest('GET', 'api/market/ticker'); + + expect($request->getUri()->getPath())->toBe('/api/market/ticker'); +}); + +it('can createResponse wrapping PSR response', function () { + $client = ClientBuilder::create() + ->setCredentials('test', 'secret') + ->build(); + + $pending = new PendingRequest($client, 'GET', '/api/market/ticker'); + + $psrRequest = $this->createMock(\Psr\Http\Message\RequestInterface::class); + $psrResponse = \Farzai\Bitkub\Tests\MockHttpClient::response(200, json_encode(['error' => 0])); + + $response = $pending->createResponse($psrRequest, $psrResponse); + + expect($response)->toBeInstanceOf(\Farzai\Transport\Contracts\ResponseInterface::class); + expect($response)->toBeInstanceOf(\Farzai\Bitkub\Responses\ResponseWithValidateErrorCode::class); + expect($response->statusCode())->toBe(200); +}); diff --git a/tests/ResponseErrorCodeTest.php b/tests/ResponseErrorCodeTest.php index eb961b8..1328ffa 100644 --- a/tests/ResponseErrorCodeTest.php +++ b/tests/ResponseErrorCodeTest.php @@ -78,3 +78,78 @@ new BitkubResponseErrorCodeException($psrResponse); })->throws(\Exception::class, 'Error code not found.'); + +it('should not throw exception when error code is null', function () { + $psrRequest = $this->createMock(PsrRequestInterface::class); + $psrResponse = MockHttpClient::response(200, json_encode([ + 'result' => 'ok', + ])); + + $response = new Response($psrRequest, $psrResponse); + $response = new ResponseWithValidateErrorCode($response); + + $result = $response->throw(); + + expect($result)->toBeInstanceOf(ResponseWithValidateErrorCode::class); +}); + +it('throw returns static for chaining', function () { + $psrRequest = $this->createMock(PsrRequestInterface::class); + $psrResponse = MockHttpClient::response(200, json_encode([ + 'error' => 0, + ])); + + $response = new Response($psrRequest, $psrResponse); + $response = new ResponseWithValidateErrorCode($response); + + $result = $response->throw(); + + expect($result)->toBe($response); +}); + +it('decorator delegates jsonOrNull method', function () { + $psrRequest = $this->createMock(PsrRequestInterface::class); + $psrResponse = MockHttpClient::response(200, json_encode([ + 'error' => 0, + 'result' => ['balance' => 100], + ])); + + $response = new Response($psrRequest, $psrResponse); + $response = new ResponseWithValidateErrorCode($response); + + expect($response->jsonOrNull('result'))->toBe(['balance' => 100]); + expect($response->jsonOrNull('nonexistent'))->toBeNull(); +}); + +it('decorator delegates toArray method', function () { + $psrRequest = $this->createMock(PsrRequestInterface::class); + $psrResponse = MockHttpClient::response(200, json_encode([ + 'error' => 0, + 'result' => 'ok', + ])); + + $response = new Response($psrRequest, $psrResponse); + $response = new ResponseWithValidateErrorCode($response); + + expect($response->toArray())->toBe(['error' => 0, 'result' => 'ok']); +}); + +it('decorator delegates PSR-7 getStatusCode', function () { + $psrRequest = $this->createMock(PsrRequestInterface::class); + $psrResponse = MockHttpClient::response(201, json_encode(['error' => 0])); + + $response = new Response($psrRequest, $psrResponse); + $response = new ResponseWithValidateErrorCode($response); + + expect($response->getStatusCode())->toBe(201); +}); + +it('decorator delegates PSR-7 getBody', function () { + $psrRequest = $this->createMock(PsrRequestInterface::class); + $psrResponse = MockHttpClient::response(200, json_encode(['error' => 0])); + + $response = new Response($psrRequest, $psrResponse); + $response = new ResponseWithValidateErrorCode($response); + + expect($response->getBody())->toBeInstanceOf(\Psr\Http\Message\StreamInterface::class); +});