-
Notifications
You must be signed in to change notification settings - Fork 93
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #461 from recurly/v3-mock-client
[V3] Mock Client
- Loading branch information
Showing
6 changed files
with
302 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<?php | ||
/** | ||
* This class abstracts away all the PHP-level HTTP | ||
* code. This allows us to easily mock out the HTTP | ||
* calls in BaseClient by injecting a mocked version of | ||
* this adapter. | ||
*/ | ||
|
||
namespace Recurly; | ||
|
||
class HttpAdapter | ||
{ | ||
private static $_default_options = [ | ||
'ignore_errors' => true | ||
]; | ||
|
||
/** | ||
* Performs HTTP request | ||
* | ||
* @param string $method HTTP method to use | ||
* @param string $url Fully qualified URL | ||
* @param array $body The request body | ||
* @param array $headers HTTP headers | ||
* | ||
* @return array The API response as a string and the headers as an array | ||
*/ | ||
public function execute($method, $url, $body, $headers): array | ||
{ | ||
$body = empty($body) ? null : json_encode($body); | ||
$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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
<?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 = new MockClient(); | ||
} | ||
|
||
public function tearDown(): void | ||
{ | ||
$this->client->clearScenarios(); | ||
} | ||
|
||
public function testGetResource200(): void | ||
{ | ||
$url = "https://v3.recurly.com/resources/iexist"; | ||
$result = '{"id": "iexist", "object": "test_resource"}'; | ||
$this->client->addScenario("GET", $url, NULL, $result, "200 OK"); | ||
|
||
$resource = $this->client->getResource("iexist"); | ||
$this->assertEquals($resource->getId(), "iexist"); | ||
} | ||
|
||
public function testGetResource404(): void | ||
{ | ||
$url = "https://v3.recurly.com/resources/idontexist"; | ||
$result = "{\"error\":{\"type\":\"not_found\",\"message\":\"Couldn't find Resource with id = idontexist\",\"params\":[{\"param\":\"resource_id\"}]}}"; | ||
$this->client->addScenario("GET", $url, NULL, $result, "404 Not Found"); | ||
|
||
$this->expectException(\Recurly\Errors\NotFound::class); | ||
$this->client->getResource("idontexist"); | ||
} | ||
|
||
public function testCreateResource201(): void | ||
{ | ||
$url = "https://v3.recurly.com/resources/"; | ||
$result = '{"id": "created", "object": "test_resource", "name": "valid"}'; | ||
$body = [ "name" => "valid" ]; | ||
$this->client->addScenario("POST", $url, $body, $result, "201 Created"); | ||
$resource = $this->client->createResource([ "name" => "valid" ]); | ||
$this->assertEquals($resource->getId(), "created"); | ||
} | ||
|
||
public function testCreateResource422(): void | ||
{ | ||
$url = "https://v3.recurly.com/resources/"; | ||
$result = "{\"error\":{\"type\":\"validation\",\"message\":\"Name is invalid\",\"params\":[{\"param\":\"name\",\"message\":\"is invalid\"}]}}"; | ||
$body = [ "name" => "invalid" ]; | ||
$this->client->addScenario("POST", $url, $body, $result, "422 Unprocessable Entity"); | ||
|
||
$this->expectException(\Recurly\Errors\Validation::class); | ||
$resource = $this->client->createResource([ "name" => "invalid" ]); | ||
} | ||
|
||
public function testDeleteResource204(): void | ||
{ | ||
$url = "https://v3.recurly.com/resources/iexist"; | ||
$result = ""; | ||
$this->client->addScenario("DELETE", $url, NULL, $result, "204 No Content"); | ||
$empty = $this->client->deleteResource("iexist"); | ||
} | ||
|
||
public function testUpdateResource200(): void | ||
{ | ||
$url = "https://v3.recurly.com/resources/iexist"; | ||
$result = '{"id": "iexist", "object": "test_resource", "name": "newname"}'; | ||
$body = [ "name" => "newname" ]; | ||
$this->client->addScenario("PUT", $url, $body, $result, "200 OK"); | ||
|
||
$resource = $this->client->updateResource("iexist", $body); | ||
$this->assertEquals($resource->getName(), "newname"); | ||
} | ||
|
||
public function testListResources200(): void | ||
{ | ||
$url = "https://v3.recurly.com/resources"; | ||
$result = '{ "object": "list", "has_more": false, "next": null, "data": [{"id": "iexist", "object": "test_resource", "name": "newname"}]}'; | ||
$this->client->addScenario("GET", $url, NULL, $result, "200 OK"); | ||
|
||
$resources = $this->client->listResources(); | ||
$count = 0; | ||
foreach($resources as $resource) { | ||
$count = $count + 1; | ||
$this->assertEquals($resource->getId(), "iexist"); | ||
} | ||
$this->assertEquals($count, 1); | ||
} | ||
|
||
public function testListResourcesWithParams200(): void | ||
{ | ||
$url = "https://v3.recurly.com/resources?limit=1"; | ||
$result = '{ "object": "list", "has_more": false, "next": null, "data": [{"id": "iexist", "object": "test_resource", "name": "newname"}]}'; | ||
$this->client->addScenario("GET", $url, NULL, $result, "200 OK"); | ||
|
||
$resources = $this->client->listResources([ "limit" => 1 ]); | ||
$count = 0; | ||
foreach($resources as $resource) { | ||
$count = $count + 1; | ||
$this->assertEquals($resource->getId(), "iexist"); | ||
} | ||
$this->assertEquals($count, 1); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
<?php | ||
|
||
use Recurly\Page; | ||
use Recurly\Resources\TestResource; | ||
use Recurly\BaseClient; | ||
use Recurly\Utils; | ||
use PHPUnit\Framework\MockObject\Generator; | ||
use Recurly\HttpAdapter; | ||
|
||
class MockClient extends BaseClient | ||
{ | ||
use Recurly\RecurlyTraits; | ||
|
||
public function __construct() | ||
{ | ||
parent::__construct("apikey"); | ||
$this->http = (new Generator())->getMock(HttpAdapter::class); | ||
} | ||
|
||
protected function apiVersion(): string | ||
{ | ||
return "v2999-01-01"; | ||
} | ||
|
||
public function listResources(array $options = []): \Recurly\Pager | ||
{ | ||
$path = $this->interpolatePath("/resources", []); | ||
return new \Recurly\Pager($this, $path, $options); | ||
} | ||
|
||
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 function createResource(array $body): TestResource | ||
{ | ||
$path = $this->interpolatePath("/resources/", []); | ||
return $this->makeRequest('POST', $path, $body, null); | ||
} | ||
|
||
public function updateResource(string $resource_id, array $body): TestResource | ||
{ | ||
$path = $this->interpolatePath("/resources/{resource_id}", ['resource_id' => $resource_id]); | ||
return $this->makeRequest('PUT', $path, $body, null); | ||
} | ||
|
||
public function deleteResource(string $resource_id): \Recurly\EmptyResource | ||
{ | ||
$path = $this->interpolatePath("/resources/{resource_id}", ['resource_id' => $resource_id]); | ||
return $this->makeRequest('DELETE', $path, null, null); | ||
} | ||
|
||
public function addScenario($method, $url, $body, $result, $status): void | ||
{ | ||
$resp_header = self::_generateRespHeader($status); | ||
$this->http->method('execute')->with( | ||
$method, | ||
$url, | ||
$body, | ||
self::_expectedHeaders() | ||
)->willReturn(array($result, $resp_header)); | ||
} | ||
|
||
public function clearScenarios(): void | ||
{ | ||
$this->http = (new Generator())->getMock(HttpAdapter::class); | ||
} | ||
|
||
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.v2999-01-01", | ||
"X-RateLimit-Limit: 2000", | ||
"X-RateLimit-Remaining: 1996", | ||
"X-RateLimit-Reset: 1582135020", | ||
"X-Request-Id: 567a17af7875e3ba-ATL", | ||
"Server: cloudflare", | ||
"CF-RAY: 567a17af7875e3ba-ATL" | ||
]; | ||
} | ||
|
||
private static function _expectedHeaders(): array | ||
{ | ||
$auth_token = self::encodeApiKey("apikey"); | ||
$agent = self::getUserAgent(); | ||
return [ | ||
"User-Agent" => $agent, | ||
"Authorization" => "Basic {$auth_token}", | ||
"Accept" => "application/vnd.recurly.v2999-01-01", | ||
"Content-Type" => "application/json", | ||
]; | ||
} | ||
} |
Oops, something went wrong.