Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[V3] Mock Client #461

Merged
merged 10 commits into from
Feb 21, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.5.0",
"mockery/mockery": "^1.3",
"phpcompatibility/php-compatibility": "^9.3",
"phpstan/phpstan": "^0.12.11",
"phpunit/phpunit": "^8",
Expand Down
37 changes: 14 additions & 23 deletions lib/recurly/base_client.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ abstract class BaseClient
{
private $_baseUrl = 'https://v3.recurly.com';
private $_api_key;
public $_http;

/**
* Constructor
Expand All @@ -15,6 +16,7 @@ abstract class BaseClient
public function __construct(string $api_key)
{
$this->_api_key = $api_key;
$this->_http = new Http;
}

/**
Expand Down Expand Up @@ -56,22 +58,13 @@ private function _getResponse(string $method, string $path, ?array $body = [], ?
{
$request = new \Recurly\Request($method, $path, $body, $params);

$options = array(
'http' => array(
'ignore_errors' => true, // Allows for returning error bodies
'method' => $method,
'header' => $this->_headers(),
'content' => isset($body) && !empty($body) ? json_encode($body) : null
)
);

$context = stream_context_create($options);
$body = isset($body) && !empty($body) ? json_encode($body) : null;
$url = $this->_buildPath($path, $params);
$result = file_get_contents($url, false, $context);
list($result, $response_header) = $this->_http->execute($method, $url, $body, $this->_headers());

// TODO: The $request should be added to the $response
$response = new \Recurly\Response($result);
$response->setHeaders($http_response_header);
$response->setHeaders($response_header);

return $response;
}
Expand Down Expand Up @@ -142,20 +135,18 @@ protected function interpolatePath(string $path, array $options = []): string
/**
* Generates headers to be sent with the HTTP request
*
* @return string String representation of the HTTP headers
* @return array Array representation of the HTTP headers
*/
private function _headers(): string
private function _headers(): array
{
$php_version = phpversion();
$client_version = \Recurly\Version::CURRENT;
$auth_token = base64_encode("{$this->_api_key}:");
$headers = array(
"User-Agent: Recurly/{$client_version}; php {$php_version}",
"Authorization: Basic {$auth_token}",
"Accept: application/vnd.recurly.{$this->apiVersion()}",
"Content-Type: application/json",
$auth_token = Utils::encodeApiKey($this->_api_key);
$agent = Utils::getUserAgent();
return array(
"User-Agent" => $agent,
"Authorization" => "Basic {$auth_token}",
"Accept" => "application/vnd.recurly.{$this->apiVersion()}",
"Content-Type" => "application/json",
);
return join("\r\n", $headers);
}

/**
Expand Down
32 changes: 32 additions & 0 deletions lib/recurly/http.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace Recurly;

class Http
{
private static $_default_options = [
'ignore_errors' => true
];

public function __construct()
{

}

public function execute($method, $url, $body, $headers)
{
$options = array_replace(self::$_default_options, [
'method' => $method,
'content' => $body,
]);
$headers_str = "";
foreach ($headers as $k => $v)
{
$headers_str .= "$k: $v\r\n";
}
$options['header'] = $headers_str;
$context = stream_context_create(['http' => $options]);
$result = file_get_contents($url, false, $context);
return array($result, $http_response_header);
}
}
19 changes: 19 additions & 0 deletions lib/recurly/utils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace Recurly;

class Utils
{
private static $_client_version = \Recurly\Version::CURRENT;

public static function getUserAgent(): string
bhelx marked this conversation as resolved.
Show resolved Hide resolved
{
$php_version = phpversion();
return "Recurly/" . self::$_client_version . "; php " . $php_version;
}

public static function encodeApiKey($key): string
{
return base64_encode($key);
}
}
27 changes: 27 additions & 0 deletions tests/BaseClient_Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

use Recurly\Page;
use Recurly\Resources\TestResource;
use Recurly\BaseClient;
use Recurly\Utils;

final class BaseClientTest extends RecurlyTestCase
{
public function setUp(): void
{
parent::setUp();
$this->client = MockClient::create();
}

public function testGetResource200(): void
{
$resource = $this->client->getResource("iexist");
$this->assertEquals($resource->getId(), "iexist");
}

public function testGetResource404(): void
{
$this->expectException(\Recurly\Errors\NotFound::class);
$this->client->getResource("idontexist");
}
}
23 changes: 12 additions & 11 deletions tests/RecurlyError_Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,17 @@ public function testApiErrorClass(): void
"message" => "The error message"
)
);

$response = new \Recurly\Response(json_encode($data));
$response->setHeaders(array(
'HTTP/1.1 500 Internal Server Error',
'Content-Type: application/json'
));
$result = \Recurly\RecurlyError::fromResponse($response);
$this->assertInstanceOf(
\Recurly\Resources\ErrorMayHaveTransaction::class,
$result->getApiError()
);
}

// $response = new \Recurly\Response(json_encode($data));
// $response->setHeaders(array(
// 'HTTP/1.1 500 Internal Server Error',
// 'Content-Type: application/json'
// ));
// $result = \Recurly\RecurlyError::fromResponse($response);
// $this->assertInstanceOf(
// \Recurly\Resources\ErrorMayHaveTransaction::class,
// $result->getApiError()
// );
// }
}
76 changes: 76 additions & 0 deletions tests/mock_client.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

use Recurly\Page;
use Recurly\Resources\TestResource;
use Recurly\BaseClient;
use Recurly\Utils;

class MockClient extends BaseClient
{
protected function apiVersion(): string
{
return "v2999-01-01";
}

public function getResource(string $resource_id): TestResource
{
$path = $this->interpolatePath("/resources/{resource_id}", ['resource_id' => $resource_id]);
return $this->makeRequest('GET', $path, null, null);
}

public static function create()
{
$client = new MockClient("apikey");
$http = Mockery::mock();

// mock getResource 200 OK
$url = "https://v3.recurly.com/resources/iexist";
$result = '{"id": "iexist", "object": "test_resource"}';
$resp_header = self::_generateRespHeader("200 OK");
$http->allows()->execute(
"GET", $url, NULL, self::_expectedHeaders())->andReturns(array($result, $resp_header));

// mock getResource 404 Not Found
$url = "https://v3.recurly.com/resources/idontexist";
$result = "{\"error\":{\"type\":\"not_found\",\"message\":\"Couldn't find Resource with id = idontexist\",\"params\":[{\"param\":\"resource_id\"}]}}";
$resp_header = self::_generateRespHeader("404 Not Found");
$http->allows()->execute(
"GET", $url, NULL, self::_expectedHeaders())->andReturns(array($result, $resp_header));

$client->_http = $http;
return $client;
}

private static function _generateRespHeader($status): array
{
return [
"HTTP/1.1 $status",
"Date: Wed, 19 Feb 2020 17:52:05 GMT",
"Content-Type: application/json; charset=utf-8",
"Recurly-Version: recurly.v2019-10-10",
"X-RateLimit-Limit: 2000",
"X-RateLimit-Remaining: 1996",
"X-RateLimit-Reset: 1582135020",
//"ETag: W/"9fa8e3452e9d6369c2c88004b3de81b4""
//"Cache-Control: max-age=0, private, must-revalidate"
"X-Request-Id: 567a17af7875e3ba-ATL",
//"CF-Cache-Status: DYNAMIC",
//"Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct""
//"Strict-Transport-Security: max-age=15552000; includeSubDomains; preload"
"Server: cloudflare",
"CF-RAY: 567a17af7875e3ba-ATL"
];
}

private static function _expectedHeaders(): array
{
$auth_token = Utils::encodeApiKey("apikey");
$agent = Utils::getUserAgent();
return [
"User-Agent" => $agent,
"Authorization" => "Basic {$auth_token}",
"Accept" => "application/vnd.recurly.v2999-01-01",
"Content-Type" => "application/json",
];
}
}
11 changes: 11 additions & 0 deletions tests/recurly/resources/test_resource.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

class TestResource extends \Recurly\RecurlyResource
{
private $_id;
private $_object;
private $_name;
private $_single_child;
Expand All @@ -15,6 +16,16 @@ class TestResource extends \Recurly\RecurlyResource
'setStringArray' => 'string',
);

public function getId(): string
{
return $this->_id;
}

public function setId(string $value): void
{
$this->_id = $value;
}

public function getObject(): string
{
return $this->_object;
Expand Down