diff options
-rw-r--r-- | composer.json | 3 | ||||
-rw-r--r-- | composer.lock | 126 | ||||
-rw-r--r-- | examples/transmission/send_transmission_all_fields.php | 2 | ||||
-rw-r--r-- | lib/SparkPost/APIResource.php | 49 | ||||
-rw-r--r-- | test/unit/APIResourceTest.php | 132 | ||||
-rw-r--r-- | test/unit/TransmissionTest.php | 225 |
6 files changed, 367 insertions, 170 deletions
diff --git a/composer.json b/composer.json index 627fbfb..bb27186 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,8 @@ }, "require-dev": { "phpunit/phpunit": "4.3.*", - "guzzlehttp/guzzle": "6.*" + "guzzlehttp/guzzle": "6.*", + "mockery/mockery": "^0.9.4" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index e62177f..7abdd48 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "c1b222200ff1ed0a0aa545592e856675", - "content-hash": "28082d79d1a6e023f128688258c25e5d", + "hash": "b0bea9fca01c22325a702a4581b2b1d3", + "content-hash": "5d7c53b1cf6f9ead8f69ba9e3d000a19", "packages": [ { "name": "egeloen/http-adapter", @@ -412,6 +412,116 @@ "time": "2015-08-15 19:32:36" }, { + "name": "hamcrest/hamcrest-php", + "version": "v1.2.2", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/b37020aa976fa52d3de9aa904aa2522dc518f79c", + "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "1.3.3", + "satooshi/php-coveralls": "dev-master" + }, + "type": "library", + "autoload": { + "classmap": [ + "hamcrest" + ], + "files": [ + "hamcrest/Hamcrest.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "time": "2015-05-11 14:41:42" + }, + { + "name": "mockery/mockery", + "version": "0.9.4", + "source": { + "type": "git", + "url": "https://github.com/padraic/mockery.git", + "reference": "70bba85e4aabc9449626651f48b9018ede04f86b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/padraic/mockery/zipball/70bba85e4aabc9449626651f48b9018ede04f86b", + "reference": "70bba85e4aabc9449626651f48b9018ede04f86b", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "~1.1", + "lib-pcre": ">=7.0", + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.9.x-dev" + } + }, + "autoload": { + "psr-0": { + "Mockery": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "http://blog.astrumfutura.com" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "http://davedevelopment.co.uk" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.", + "homepage": "http://github.com/padraic/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "time": "2015-04-02 19:54:00" + }, + { "name": "phpunit/php-code-coverage", "version": "2.2.3", "source": { @@ -1101,16 +1211,16 @@ }, { "name": "symfony/yaml", - "version": "v2.7.4", + "version": "v2.7.5", "source": { "type": "git", - "url": "https://github.com/symfony/Yaml.git", - "reference": "2dc7b06c065df96cc686c66da2705e5e18aef661" + "url": "https://github.com/symfony/yaml.git", + "reference": "31cb2ad0155c95b88ee55fe12bc7ff92232c1770" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/2dc7b06c065df96cc686c66da2705e5e18aef661", - "reference": "2dc7b06c065df96cc686c66da2705e5e18aef661", + "url": "https://api.github.com/repos/symfony/yaml/zipball/31cb2ad0155c95b88ee55fe12bc7ff92232c1770", + "reference": "31cb2ad0155c95b88ee55fe12bc7ff92232c1770", "shasum": "" }, "require": { @@ -1146,7 +1256,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2015-08-24 07:13:45" + "time": "2015-09-14 14:14:09" } ], "aliases": [], diff --git a/examples/transmission/send_transmission_all_fields.php b/examples/transmission/send_transmission_all_fields.php index 5a6cf7f..ea2a24d 100644 --- a/examples/transmission/send_transmission_all_fields.php +++ b/examples/transmission/send_transmission_all_fields.php @@ -9,8 +9,6 @@ $key = 'YOUR API KEY'; $httpAdapter = new Guzzle6HttpAdapter(new Client()); $sparky = new SparkPost($httpAdapter, ['key'=>$key]); -// TODO: update all from emails to = sandbox domain - try{ $results = $sparky->transmission->send([ "campaign"=>"my-campaign", diff --git a/lib/SparkPost/APIResource.php b/lib/SparkPost/APIResource.php index 714f1fb..6a1be7f 100644 --- a/lib/SparkPost/APIResource.php +++ b/lib/SparkPost/APIResource.php @@ -30,17 +30,17 @@ class APIResource { protected static $structure = []; /** - * TODO: Docs + * @dec connection config for making requests. */ private $config; /** - * TODO: Docs + * @desc Ivory\HttpAdapter\HttpAdapterInterface to make requests through. */ - private $httpAdapter; + protected $httpAdapter; /** - * TODO: Docs + * @desc Default config values. Passed in values will override these. */ private static $apiDefaults = [ 'host'=>'api.sparkpost.com', @@ -52,7 +52,9 @@ class APIResource { ]; /** - * @desc TODO: Docs + * @desc Initializes config and httpAdapter for use later. + * @param $httpAdapter Ivory\HttpAdapter\HttpAdapterInterface to make requests through. + * @param $config connection config for making requests. */ public function __construct($httpAdapter, $config) { //config needs to be setup before adapter because of default adapter settings @@ -84,7 +86,6 @@ class APIResource { * @desc Helper function for getting the configuration for http requests * @return \Ivory\HttpAdapter\Configuration */ - // TODO: Need to figure out how to set strictSSL private function getHttpConfig($config) { // get composer.json to extract version number $composerFile = file_get_contents(dirname(__FILE__) . "/../../composer.json"); @@ -99,7 +100,9 @@ class APIResource { } /** - * TODO: Docs + * @desc Validates and sets up the httpAdapter + * @param $httpAdapter Ivory\HttpAdapter\HttpAdapterInterface to make requests through. + * @throws \Exception */ public function setHttpAdapter($httpAdapter) { if (!$httpAdapter instanceOf HttpAdapterInterface) { @@ -133,6 +136,10 @@ class APIResource { /** * @desc Private Method helper to reference parameter mappings and set the right value for the right parameter + * + * @param array $model (pass by reference) the set of values to map + * @param string $mapKey a dot syntax path determining which value to set + * @param mixed $value value for the given path */ protected function setMappedValue (&$model, $mapKey, $value) { //get mapping @@ -160,7 +167,10 @@ class APIResource { } /** - * TODO: Docs + * @desc maps values from the passed in model to those needed for the request + * @param $requestConfig the passed in model + * @param $model the set of defaults + * @return array A model ready for the body of a request */ protected function buildRequestModel(Array $requestConfig, Array $model=[] ) { foreach($requestConfig as $key => $value) { @@ -170,14 +180,18 @@ class APIResource { } /** - * TODO: Docs + * @desc posts to the api with a supplied body + * @param body post body for the request + * @return array Result of the request */ public function create(Array $body=[]) { return $this->callResource( 'post', null, ['body'=>$body]); } /** - * TODO: Docs + * @desc Makes a put request to the api with a supplied body + * @param body Put body for the request + * @return array Result of the request */ public function update( $resourcePath, Array $body=[]) { return $this->callResource( 'put', $resourcePath, ['body'=>$body]); @@ -188,7 +202,7 @@ class APIResource { * * @param string $resourcePath (optional) string resource path of specific resource * @param array $options (optional) query string parameters - * @return array Result set of transmissions found + * @return array Result of the request */ public function get( $resourcePath=null, Array $query=[] ) { return $this->callResource( 'get', $resourcePath, ['query'=>$query] ); @@ -199,7 +213,7 @@ class APIResource { * * @param string $resourcePath (optional) string resource path of specific resource * @param array $options (optional) query string parameters - * @return array Result set of transmissions found + * @return array Result of the request */ public function delete( $resourcePath=null, Array $query=[] ) { return $this->callResource( 'delete', $resourcePath, ['query'=>$query] ); @@ -207,7 +221,10 @@ class APIResource { /** - * TODO: docs + * @desc assembles a URL for a request + * @param string $resourcePath path after the initial endpoint + * @param array options array with an optional value of query with values to build a querystring from. + * @return string the assembled URL */ private function buildUrl($resourcePath, $options) { $url = join(['/', $this->endpoint, '/']); @@ -225,7 +242,9 @@ class APIResource { /** - * TODO: Docs + * @desc Prepares a body for put and post requests + * @param array options array with an optional value of body with values to build a request body from. + * @return string|null A json encoded string or null if no body was provided */ private function buildBody($options) { $body = null; @@ -270,7 +289,7 @@ class APIResource { * Handles 4XX responses */ catch (HttpAdapterException $exception) { - $response = $exception->getBody(); + $response = $exception->getResponse(); $statusCode = $response->getStatusCode(); if($statusCode === 404) { throw new \Exception("The specified resource does not exist", 404); diff --git a/test/unit/APIResourceTest.php b/test/unit/APIResourceTest.php index 99d7c80..03d3650 100644 --- a/test/unit/APIResourceTest.php +++ b/test/unit/APIResourceTest.php @@ -2,7 +2,7 @@ namespace SparkPost\Test; use SparkPost\APIResource; use SparkPost\Test\TestUtils\ClassUtils; -use Ivory\HttpAdapter\CurlHttpAdapter; +use \Mockery; class APIResourceTest extends \PHPUnit_Framework_TestCase { @@ -10,8 +10,12 @@ class APIResourceTest extends \PHPUnit_Framework_TestCase { private $adapterMock; private $resource; - public static function setUpBeforeClass() { - + private function getExceptionMock($statusCode) { + $exception = new \Ivory\HttpAdapter\HttpAdapterException(); + $response = Mockery::mock('Ivory\HttpAdapter\Message\ResponseInterface'); + $response->shouldReceive('getStatusCode')->andReturn($statusCode); + $exception->setResponse($response); + return $exception; } /** @@ -20,14 +24,128 @@ class APIResourceTest extends \PHPUnit_Framework_TestCase { * @see PHPUnit_Framework_TestCase::setUp() */ public function setUp() { - $this->adapterMock = $this->getMockBuilder('CurlHttpAdapter')->getMock(); - - $this->resource = new APIResource(new CurlHttpAdapter(), ['key'=>'a key']); + $this->adapterMock = Mockery::mock('Ivory\HttpAdapter\HttpAdapterInterface', function($mock) { + $mock->shouldReceive('setConfiguration'); + $mock->shouldReceive('getConfiguration->getUserAgent')->andReturn('php-sparkpost/0.2.0'); + }); + $this->resource = new APIResource($this->adapterMock, ['key'=>'a key']); self::$utils = new ClassUtils($this->resource); + + self::$utils->setProperty($this->resource, 'httpAdapter', $this->adapterMock); } + public function tearDown() + { + Mockery::close(); + } + public function testConstructorSetsUpAdapterAndConfig() { - $this->assertEquals('Ivory\HttpAdapter\CurlHttpAdapter', get_class(self::$utils->getProperty($this->resource, 'httpAdapter'))); + $adapter = self::$utils->getProperty($this->resource, 'httpAdapter'); + $this->assertRegExp('/php-sparkpost.*/', $adapter->getConfiguration()->getUserAgent()); + } + + /** + * @expectedException Exception + * @expectedExceptionMessageRegExp /valid Ivory\\HttpAdapter/ + */ + public function testSetBadHTTPAdapter() { + $this->resource->setHttpAdapter(new \stdClass()); + } + + /** + * @expectedException Exception + * @expectedExceptionMessageRegExp /API key/ + */ + public function testSetBadConfig() { + $this->resource->setConfig(['not'=>'a key']); + } + + public function testCreate() { + $testInput = ['test'=>'body']; + $testBody = ["results"=>["my"=>"test"]]; + $responseMock = Mockery::mock(); + $this->adapterMock->shouldReceive('send')-> + once()-> + with(Mockery::type('string'), 'POST', Mockery::type('array'), json_encode($testInput))-> + andReturn($responseMock); + $responseMock->shouldReceive('getBody->getContents')->andReturn(json_encode($testBody)); + + $this->assertEquals($testBody, $this->resource->create($testInput)); + } + + public function testUpdate() { + $testInput = ['test'=>'body']; + $testBody = ["results"=>["my"=>"test"]]; + $responseMock = Mockery::mock(); + $this->adapterMock->shouldReceive('send')-> + once()-> + with('/.*\/test/', 'PUT', Mockery::type('array'), json_encode($testInput))-> + andReturn($responseMock); + $responseMock->shouldReceive('getBody->getContents')->andReturn(json_encode($testBody)); + + $this->assertEquals($testBody, $this->resource->update('test', $testInput)); + } + + public function testGet() { + $testBody = ["results"=>["my"=>"test"]]; + $responseMock = Mockery::mock(); + $this->adapterMock->shouldReceive('send')-> + once()-> + with('/.*\/test/', 'GET', Mockery::type('array'), null)-> + andReturn($responseMock); + $responseMock->shouldReceive('getBody->getContents')->andReturn(json_encode($testBody)); + + $this->assertEquals($testBody, $this->resource->get('test')); + } + + public function testDelete() { + $responseMock = Mockery::mock(); + $this->adapterMock->shouldReceive('send')-> + once()-> + with('/.*\/test/', 'DELETE', Mockery::type('array'), null)-> + andReturn($responseMock); + $responseMock->shouldReceive('getBody->getContents')->andReturn(''); + + $this->assertEquals(null, $this->resource->delete('test')); + } + + public function testAdapter404Exception() { + try { + $this->adapterMock->shouldReceive('send')-> + once()-> + andThrow($this->getExceptionMock(404)); + + $this->resource->get('test'); + } + catch(\Exception $e) { + $this->assertRegExp('/.*resource does not exist.*/', $e->getMessage()); + } + } + + public function testAdapter4XXException() { + try { + $this->adapterMock->shouldReceive('send')-> + once()-> + andThrow($this->getExceptionMock(400)); + + $this->resource->get('test'); + } + catch(\Exception $e) { + $this->assertRegExp('/Received bad response.*/', $e->getMessage()); + } + } + + public function testAdapter5XXException() { + try { + $this->adapterMock->shouldReceive('send')-> + once()-> + andThrow(new \Exception('Something went wrong.')); + + $this->resource->get('test'); + } + catch(\Exception $e) { + $this->assertRegExp('/Unable to contact.*API.*/', $e->getMessage()); + } } } diff --git a/test/unit/TransmissionTest.php b/test/unit/TransmissionTest.php index ca30b4a..95fabe4 100644 --- a/test/unit/TransmissionTest.php +++ b/test/unit/TransmissionTest.php @@ -2,143 +2,94 @@ namespace SparkPost\Test; use SparkPost\Transmission; -use SparkPost\SparkPost; -use Guzzle\Plugin\Mock\MockPlugin; -use Guzzle\Http\Message\Response; - +use SparkPost\Test\TestUtils\ClassUtils; +use \Mockery; class TransmissionTest extends \PHPUnit_Framework_TestCase { - - private $client = null; - - /** - * Allows access to private methods in the Transmission class - * - * This is needed to mock the GuzzleHttp\Client responses - * - * @param string $name - * @return ReflectionMethod - */ - private static function getMethod($name) { - $class = new \ReflectionClass('\SparkPost\Transmission'); - $method = $class->getMethod($name); - $method->setAccessible(true); - return $method; - } - - /** - * (non-PHPdoc) - * @before - * @see PHPUnit_Framework_TestCase::setUp() - */ - public function setUp() { - SparkPost::setConfig(array('key'=>'blah')); - $this->client = self::getMethod('getHttpClient')->invoke(null); //so we can bootstrap api responses - } - - /** - * @desc Ensures that the configuration class is not instantiable. - */ - public function testConstructorCannotBeCalled() { - $class = new \ReflectionClass('\SparkPost\Transmission'); - $this->assertFalse($class->isInstantiable()); - } - - /** - * @desc tests happy path - */ - public function testAllWithGoodResponse() { - $mock = new MockPlugin(); - $mock->addResponse(new Response(200, array(), '{"results":[{"test":"This is a test"}, {"test":"two"}]}')); - $this->client->addSubscriber($mock); - $this->assertEquals(array("results"=>array(array('test'=>'This is a test'), array('test'=>'two'))), Transmission::all()); - } - - /** - * @desc tests happy path - */ - public function testFindWithGoodResponse() { - $mock = new MockPlugin(); - $mock->addResponse(new Response(200, array(), '{"results":[{"test":"This is a test"}]}')); - $this->client->addSubscriber($mock); - - $this->assertEquals(array("results"=>array(array('test'=>'This is a test'))), Transmission::find('someId')); - } - - /** - * @desc tests 404 bad response - * @expectedException Exception - * @expectedExceptionMessage The specified resource does not exist - */ - public function testFindWith404Response() { - $mock = new MockPlugin(); - $mock->addResponse(new Response(404, array())); - $this->client->addSubscriber($mock); - Transmission::find('someId'); - } - - /** - * @desc tests unknown bad response - * @expectedException Exception - * @expectedExceptionMessage Received bad response from Transmissions API: 400 - */ - public function testFindWithOtherBadResponse() { - $mock = new MockPlugin(); - $mock->addResponse(new Response(400, array())); - $this->client->addSubscriber($mock); - Transmission::find('someId'); - } - - /** - * @desc tests bad response - * @expectedException Exception - * @expectedExceptionMessageRegExp /Unable to contact Transmissions API:.* / - */ - public function testFindForCatchAllException() { - $mock = new MockPlugin(); - $mock->addResponse(new Response(500)); - $this->client->addSubscriber($mock); - Transmission::find('someId'); - } - - /** - * @desc tests happy path - */ - public function testSuccessfulSend() { - $body = array("result"=>array("transmission_id"=>"11668787484950529"), "status"=>array("message"=> "ok","code"=> "1000")); - $mock = new MockPlugin(); - $mock->addResponse(new Response(200, array(), json_encode($body))); - $this->client->addSubscriber($mock); - - - $this->assertEquals($body, Transmission::send(array('text'=>'awesome email'))); - } - - /** - * @desc tests bad response - * @expectedException Exception - * @expectedExceptionMessage ["This is a fake error"] - */ - public function testSendFor400Exception() { - $body = array('errors'=>array('This is a fake error')); - $mock = new MockPlugin(); - $mock->addResponse(new Response(400, array(), json_encode($body))); - $this->client->addSubscriber($mock); - Transmission::send(array('text'=>'awesome email')); - } - - - /** - * @desc tests bad response - * @expectedException Exception - * @expectedExceptionMessageRegExp /Unable to contact Transmissions API:.* / - */ - public function testSendForCatchAllException() { - $mock = new MockPlugin(); - $mock->addResponse(new Response(500)); - $this->client->addSubscriber($mock); - Transmission::send(array('text'=>'awesome email')); - } - + + private static $utils; + private $adapterMock; + private $resource; + + /** + * (non-PHPdoc) + * @before + * @see PHPUnit_Framework_TestCase::setUp() + */ + public function setUp() { + $this->adapterMock = Mockery::mock('Ivory\HttpAdapter\HttpAdapterInterface', function($mock) { + $mock->shouldReceive('setConfiguration'); + $mock->shouldReceive('getConfiguration->getUserAgent')->andReturn('php-sparkpost/0.2.0'); + }); + $this->resource = new Transmission($this->adapterMock, ['key'=>'a key']); + self::$utils = new ClassUtils($this->resource); + + self::$utils->setProperty($this->resource, 'httpAdapter', $this->adapterMock); + } + + public function tearDown() + { + Mockery::close(); + } + + public function testConstructorSetsUpAdapterAndConfig() { + $adapter = self::$utils->getProperty($this->resource, 'httpAdapter'); + $this->assertRegExp('/php-sparkpost.*/', $adapter->getConfiguration()->getUserAgent()); + } + + + public function testSend() { + $responseMock = Mockery::mock(); + $body = ['text'=>'awesomesauce', 'content'=>['subject'=>'awesomeness']]; + $responseBody = ["results"=>"yay"]; + $this->adapterMock->shouldReceive('send')-> + once()-> + with('/.*\/transmissions/', 'POST', Mockery::type('array'), Mockery::type('string'))-> + andReturn($responseMock); + + $responseMock->shouldReceive('getBody->getContents')->andReturn(json_encode($responseBody)); + + $this->assertEquals($responseBody, $this->resource->send($body)); + } + + public function testAllWithFilter() { + $responseMock = Mockery::mock(); + $responseBody = ["results"=>"yay"]; + $this->adapterMock->shouldReceive('send')-> + once()-> + with('/.*transmissions.*?campaign_id=campaign&template_id=template/', 'GET', Mockery::type('array'), null)-> + andReturn($responseMock); + + $responseMock->shouldReceive('getBody->getContents')->andReturn(json_encode($responseBody)); + + $this->assertEquals($responseBody, $this->resource->all('campaign', 'template')); + } + + public function testAllWithOutFilter() { + $responseMock = Mockery::mock(); + $responseBody = ["results"=>"yay"]; + $this->adapterMock->shouldReceive('send')-> + once()-> + with('/.*\/transmissions/', 'GET', Mockery::type('array'), null)-> + andReturn($responseMock); + + $responseMock->shouldReceive('getBody->getContents')->andReturn(json_encode($responseBody)); + + $this->assertEquals($responseBody, $this->resource->all()); + } + + public function testFind() { + $responseMock = Mockery::mock(); + $responseBody = ["results"=>"yay"]; + $this->adapterMock->shouldReceive('send')-> + once()-> + with('/.*\/transmissions.*\/test/', 'GET', Mockery::type('array'), null)-> + andReturn($responseMock); + + $responseMock->shouldReceive('getBody->getContents')->andReturn(json_encode($responseBody)); + + $this->assertEquals($responseBody, $this->resource->find('test')); + } + } -?>
\ No newline at end of file +?> |