diff --git a/phpunit.xml b/phpunit.xml index 744a9611..9ba29e12 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -2,7 +2,10 @@ + bootstrap="tests/bootstrap.php" + beStrictAboutTestsThatDoNotTestAnything="true" + checkForUnintentionallyCoveredCode="true" + > src/Aviat/Ion diff --git a/src/Aviat/AnimeClient/Model/API.php b/src/Aviat/AnimeClient/Model/API.php index 10e09b0c..3ec8c30b 100644 --- a/src/Aviat/AnimeClient/Model/API.php +++ b/src/Aviat/AnimeClient/Model/API.php @@ -8,6 +8,7 @@ use GuzzleHttp\Client; use GuzzleHttp\Cookie\CookieJar; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\ResponseInterface; +use GuzzleHttp\Exception\ClientException; use Aviat\Ion\Di\ContainerInterface; use Aviat\AnimeClient\Model as BaseModel; @@ -55,10 +56,11 @@ class API extends BaseModel { $this->client = new Client([ 'base_uri' => $this->base_url, 'cookies' => TRUE, + 'http_errors' => FALSE, 'defaults' => [ 'cookies' => $this->cookieJar, 'headers' => [ - 'User-Agent' => $_SERVER['HTTP_USER_AGENT'], + 'User-Agent' => "Tim's Anime Client/2.0", 'Accept-Encoding' => 'application/json' ], 'timeout' => 5, @@ -106,16 +108,16 @@ class API extends BaseModel { */ public function authenticate($username, $password) { - $result = $this->post('https://hummingbird.me/api/v1/users/authenticate', [ - 'body' => [ + $response = $this->post('https://hummingbird.me/api/v1/users/authenticate', [ + 'form_params' => [ 'username' => $username, 'password' => $password ] ]); - if ($result->getStatusCode() === 201) + if ($response->getStatusCode() === 201) { - return json_decode($result->getBody(), TRUE); + return json_decode($response->getBody(), TRUE); } return FALSE; diff --git a/tests/AnimeClient/Auth/HummingbirdAuthTest.php b/tests/AnimeClient/Auth/HummingbirdAuthTest.php new file mode 100644 index 00000000..2418a8e9 --- /dev/null +++ b/tests/AnimeClient/Auth/HummingbirdAuthTest.php @@ -0,0 +1,81 @@ +newInstance([]); + } + + public function setUp() + { + parent::setUp(); + $auth = new HummingbirdAuth($this->container); + $friend = new Friend($auth); + $this->auth = $friend; + $this->container->set('session', self::$session); + } + + public function dataAuthenticate() + { + $testToken = 'notReallyAValidTokenButThisIsATest'; + + return [ + 'successful auth call' => [ + 'username' => 'timw4mailtest', + 'password' => 'password', + 'response_data' => [ + 'code' => 201, + 'body' => json_encode($testToken) + ], + 'session_value' => $testToken, + 'expected' => TRUE, + ], + 'unsuccessful auth call' => [ + 'username' => 'foo', + 'password' => 'foobarbaz', + 'response_data' => [ + 'code' => 401, + 'body' => '{"error":"Invalid credentials"}', + ], + 'session_value' => FALSE, + 'expected' => FALSE, + ] + ]; + } + + /** + * @dataProvider dataAuthenticate + */ + public function testAuthenticate($username, $password, $response_data, $session_value, $expected) + { + $this->container->get('config') + ->set('hummingbird_username', $username); + $model = new MockBaseApiModel($this->container); + $mock = new MockHandler([ + new Response($response_data['code'], [], $response_data['body']) + ]); + $handler = HandlerStack::create($mock); + $client = new Client([ + 'handler' => $handler, + 'http_errors' => FALSE // Don't throw an exception for 400/500 class status codes + ]); + $model->__set('client', $client); + $this->auth->__set('model', $model); + + $actual = $this->auth->authenticate($password); + $this->assertEquals($expected, $actual); + + } +} \ No newline at end of file diff --git a/tests/AnimeClient/Model/AnimeModelTest.php b/tests/AnimeClient/Model/AnimeModelTest.php index 23a6f113..feab3652 100644 --- a/tests/AnimeClient/Model/AnimeModelTest.php +++ b/tests/AnimeClient/Model/AnimeModelTest.php @@ -4,29 +4,12 @@ use Aviat\Ion\Friend; use Aviat\Ion\Di\ContainerInterface; use Aviat\AnimeClient\Model\Anime as AnimeModel; -class AnimeMock extends AnimeModel { - - protected $transformed_data_file; - - public function __construct(ContainerInterface $container) - { - parent::__construct($container); - $this->transformed_data_file = __DIR__ . "/../../test_data/anime_list/anime-completed-transformed.json"; - } - - protected function _get_list_from_api($status="all") - { - $data = json_decode(file_get_contents($this->transformed_data_file), TRUE); - return $data; - } -} - class AnimeModelTest extends AnimeClient_TestCase { public function setUp() { parent::setUp(); - $this->animeModel = new Friend(new AnimeMock($this->container)); + $this->animeModel = new Friend(new TestAnimeModel($this->container)); } protected function _pluck_anime_titles($array) diff --git a/tests/AnimeClient/Model/BaseApiModelTest.php b/tests/AnimeClient/Model/BaseApiModelTest.php index ac0639d0..bc5fd432 100644 --- a/tests/AnimeClient/Model/BaseApiModelTest.php +++ b/tests/AnimeClient/Model/BaseApiModelTest.php @@ -1,24 +1,14 @@ $key; - } -} - class BaseApiModelTest extends AnimeClient_TestCase { public function setUp() @@ -228,5 +218,53 @@ class BaseApiModelTest extends AnimeClient_TestCase { $this->assertEquals($expected, $actual); } + + public function dataAuthenticate() + { + $test_token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4YTA5ZDk4Ny1iZWQxLTQyMTktYWVmOS0wMTcxYWVjYTE3ZWUiLCJzY29wZSI6WyJhbGwiXSwic3ViIjoxMDgwMTIsImlzcyI6MTQ0NTAxNzczNSwiZXhwIjoxNDUyOTY2NTM1fQ.fpha1ZDN9dSFAuHeJesfOP9pCk5-ZnZk4uv3zumRMY0'; + + return [ + 'successful authentication' => [ + 'username' => 'timw4mailtest', + 'password' => 'password', + 'response_data' => [ + 'code' => 201, + 'body' => json_encode($test_token) + ], + 'expected' => $test_token + ], + 'failed authentication' => [ + 'username' => 'foo', + 'password' => 'foobarbaz', + 'response_data' => [ + 'code' => 401, + 'body' => '{"error":"Invalid credentials"}', + ], + 'expected' => FALSE + ] + ]; + } + + /** + * @dataProvider dataAuthenticate + */ + public function testAuthenticate($username, $password, $response_data, $expected) + { + $mock = new MockHandler([ + new Response($response_data['code'], [], $response_data['body']) + ]); + $handler = HandlerStack::create($mock); + $client = new Client([ + 'handler' => $handler, + 'http_errors' => FALSE // Don't throw an exception for 400/500 class status codes + ]); + + // Set the mock client + $this->model->__set('client', $client); + + // Check results based on mock data + $actual = $this->model->authenticate($username, $password); + $this->assertEquals($expected, $actual, "Incorrect method return value"); + } } \ No newline at end of file diff --git a/tests/AnimeClient_TestCase.php b/tests/AnimeClient_TestCase.php new file mode 100644 index 00000000..f69e8a2f --- /dev/null +++ b/tests/AnimeClient_TestCase.php @@ -0,0 +1,73 @@ + '//localhost/assets/', + 'databaase' => [], + 'routing' => [ + + ], + 'routes' => [ + 'convention' => [ + 'default_controller' => '', + 'default_method' => '', + ], + 'common' => [], + 'anime' => [], + 'manga' => [] + ] + ]; + + // Set up DI container + $di = require _dir(APP_DIR, 'bootstrap.php'); + $container = $di($config_array); + $container->set('error-handler', new MockErrorHandler()); + $container->set('session-handler', self::$session_handler); + + $this->container = $container; + } + + /** + * Set arbitrary superglobal values for testing purposes + * + * @param array $supers + * @return void + */ + public function setSuperGlobals($supers = []) + { + $default = [ + '_GET' => $_GET, + '_POST' => $_POST, + '_COOKIE' => $_COOKIE, + '_SERVER' => $_SERVER, + '_FILES' => $_FILES + ]; + $web_factory = new WebFactory(array_merge($default,$supers)); + $this->container->set('request', $web_factory->newRequest()); + $this->container->set('response', $web_factory->newResponse()); + } +} +// End of AnimeClient_TestCase.php \ No newline at end of file diff --git a/tests/Ion/EnumTest.php b/tests/Ion/EnumTest.php index 0385d545..0baada0b 100644 --- a/tests/Ion/EnumTest.php +++ b/tests/Ion/EnumTest.php @@ -2,12 +2,6 @@ use Aviat\Ion\Enum; -class TestEnum extends Enum { - const FOO = 'bar'; - const BAR = 'foo'; - const FOOBAR = 'baz'; -} - class EnumTest extends AnimeClient_TestCase { protected $expectedConstList = [ diff --git a/tests/Ion/FriendTest.php b/tests/Ion/FriendTest.php index 702ae2ae..1569eb42 100644 --- a/tests/Ion/FriendTest.php +++ b/tests/Ion/FriendTest.php @@ -2,36 +2,12 @@ use Aviat\Ion\Friend; -class GrandParentTestClass { - protected $grandParentProtected = 84; -} - -class ParentTestClass extends GrandParentTestClass { - protected $parentProtected = 47; - private $parentPrivate = 654; -} - -class TestClass extends ParentTestClass { - protected $protected = 356; - private $private = 486; - - protected function getProtected() - { - return 4; - } - - private function getPrivate() - { - return 23; - } -} - class FriendTest extends AnimeClient_TestCase { public function setUp() { parent::setUp(); - $obj = new TestClass(); + $obj = new FriendTestClass(); $this->friend = new Friend($obj); } diff --git a/tests/Ion/Transformer/AbstractTransformerTest.php b/tests/Ion/Transformer/AbstractTransformerTest.php index 56c1104d..246658ac 100644 --- a/tests/Ion/Transformer/AbstractTransformerTest.php +++ b/tests/Ion/Transformer/AbstractTransformerTest.php @@ -1,23 +1,5 @@ getProperties(); - $props = []; - - foreach($properties as $reflectProp) - { - $reflectProp->setAccessible(TRUE); - $props[$reflectProp->getName()] = $reflectProp->getValue($this); - } - - $view = new TestView($this->container); - $friend = new Friend($view); - foreach($props as $name => $val) - { - $friend->__set($name, $val); - } - - $friend->output(); - } -} - class HtmlViewTest extends ViewTest { protected $template_path; @@ -36,13 +9,12 @@ class HtmlViewTest extends ViewTest { public function setUp() { parent::setUp(); - $this->template_path = __DIR__ . "/../../test_views/"; $this->view = new TestHtmlView($this->container); } public function testRenderTemplate() { - $path = $this->template_path . 'test_view.php'; + $path = _dir(TEST_VIEW_DIR, 'test_view.php'); $expected = 'foo'; $actual = $this->view->render_template($path, [ 'var' => 'foo' diff --git a/tests/Ion/View/HttpViewTest.php b/tests/Ion/View/HttpViewTest.php index 364c8178..8e3fd2f4 100644 --- a/tests/Ion/View/HttpViewTest.php +++ b/tests/Ion/View/HttpViewTest.php @@ -3,30 +3,6 @@ include_once __DIR__ . "/../ViewTest.php"; use Aviat\Ion\Friend; -use Aviat\Ion\View\HttpView; - -class TestHttpView extends HttpView { - protected function output() { - $reflect = new ReflectionClass($this); - $properties = $reflect->getProperties(); - $props = []; - - foreach($properties as $reflectProp) - { - $reflectProp->setAccessible(TRUE); - $props[$reflectProp->getName()] = $reflectProp->getValue($this); - } - - $view = new TestView($this->container); - $friend = new Friend($view); - foreach($props as $name => $val) - { - $friend->__set($name, $val); - } - - $friend->output(); - } -} class HttpViewTest extends ViewTest { diff --git a/tests/Ion/View/JsonViewTest.php b/tests/Ion/View/JsonViewTest.php index f2800ec5..f40a9e77 100644 --- a/tests/Ion/View/JsonViewTest.php +++ b/tests/Ion/View/JsonViewTest.php @@ -1,14 +1,9 @@ save_path/$id"; + if (file_exists($file)) + { + @unlink($file); + } + $this->data[$id] = []; + return TRUE; + } + + public function gc($maxLifetime) + { + return TRUE; + } + + public function open($save_path, $name) + { + /*if ( ! array_key_exists($save_path, $this->data)) + { + $this->save_path = $save_path; + $this->data = []; + }*/ + return TRUE; + } + + public function read($id) + { + return json_decode(@file_get_contents("$this->save_path/$id"), TRUE); + } + + public function write($id, $data) + { + $file = "$this->save_path/$id"; + file_put_contents($file, json_encode($data)); + + return TRUE; + } + +} +// End of TestSessionHandler.php \ No newline at end of file diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 49ecc102..e5018489 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -2,12 +2,7 @@ /** * Global setup for unit tests */ -use Aura\Web\WebFactory; - -use Aviat\AnimeClient\Config; -use Aviat\Ion\Di\Container; -use Aviat\AnimeClient\UrlGenerator; - + // ----------------------------------------------------------------------------- // Autoloaders // ----------------------------------------------------------------------------- @@ -29,6 +24,8 @@ define('APP_DIR', _dir(ROOT_DIR, 'app')); define('CONF_DIR', _dir(APP_DIR, 'config')); define('SRC_DIR', _dir(ROOT_DIR, 'src')); define('BASE_DIR', _dir(SRC_DIR, 'Base')); +define('TEST_DATA_DIR', _dir(__DIR__, 'test_data')); +define('TEST_VIEW_DIR', _dir(__DIR__, 'test_views')); require _dir(ROOT_DIR, '/vendor/autoload.php'); require _dir(SRC_DIR, '/functions.php'); @@ -48,76 +45,27 @@ spl_autoload_register(function ($class) { return; } }); + +// ----------------------------------------------------------------------------- +// Ini Settings +// ----------------------------------------------------------------------------- +ini_set('session.use_cookies', 0); +ini_set("session.use_only_cookies",0); +ini_set("session.use_trans_sid",1); +// Start session here to supress error about headers not sent +session_start(); + +// ----------------------------------------------------------------------------- +// Load base test case and mocks +// ----------------------------------------------------------------------------- // Pre-define some superglobals $_SESSION = []; $_COOKIE = []; -// ----------------------------------------------------------------------------- -// Mock the default error handler -// ----------------------------------------------------------------------------- - -class MockErrorHandler { - public function addDataTable($name, array $values=[]) {} -} - -// ----------------------------------------------------------------------------- -// Define a base testcase class -// ----------------------------------------------------------------------------- - -/** - * Base class for TestCases - */ -class AnimeClient_TestCase extends PHPUnit_Framework_TestCase { - protected $container; - - public function setUp() - { - parent::setUp(); - - $config_array = [ - 'asset_path' => '//localhost/assets/', - 'databaase' => [], - 'routing' => [ - - ], - 'routes' => [ - 'convention' => [ - 'default_controller' => '', - 'default_method' => '', - ], - 'common' => [], - 'anime' => [], - 'manga' => [] - ] - ]; - - $di = require _dir(APP_DIR, 'bootstrap.php'); - $container = $di($config_array); - $container->set('error-handler', new MockErrorHandler()); - - $this->container = $container; - } - - /** - * Set arbitrary superglobal values for testing purposes - * - * @param array $supers - * @return void - */ - public function setSuperGlobals($supers = []) - { - $default = [ - '_GET' => $_GET, - '_POST' => $_POST, - '_COOKIE' => $_COOKIE, - '_SERVER' => $_SERVER, - '_FILES' => $_FILES - ]; - $web_factory = new WebFactory(array_merge($default,$supers)); - $this->container->set('request', $web_factory->newRequest()); - $this->container->set('response', $web_factory->newResponse()); - } -} +// Request base test case and mocks +require _dir(__DIR__, 'TestSessionHandler.php'); +require _dir(__DIR__, 'mocks.php'); +require _dir(__DIR__, 'AnimeClient_TestCase.php'); // End of bootstrap.php \ No newline at end of file diff --git a/tests/mocks.php b/tests/mocks.php new file mode 100644 index 00000000..2a52bed3 --- /dev/null +++ b/tests/mocks.php @@ -0,0 +1,152 @@ +getProperties(); + $props = []; + + foreach($properties as $reflectProp) + { + $reflectProp->setAccessible(TRUE); + $props[$reflectProp->getName()] = $reflectProp->getValue($this); + } + + $view = new TestView($this->container); + $friend = new Friend($view); + foreach($props as $name => $val) + { + $friend->__set($name, $val); + } + + $friend->output(); + } +} + +class TestHtmlView extends HtmlView { + use MockViewOutputTrait; +} + +class TestHttpView extends HttpView { + use MockViewOutputTrait; +} + +class TestJsonView extends JsonView { + public function __destruct() {} +} + +// ----------------------------------------------------------------------------- +// AnimeClient Mocks +// ----------------------------------------------------------------------------- + +class MockBaseApiModel extends BaseApiModel { + + protected $base_url = 'https://httpbin.org/'; + + public function __get($key) + { + return $this->$key; + } + + public function __set($key, $value) + { + $this->$key = $value; + return $this; + } +} + +class TestAnimeModel extends AnimeModel { + + protected $transformed_data_file; + + public function __construct(ContainerInterface $container) + { + parent::__construct($container); + $this->transformed_data_file = _dir( + TEST_DATA_DIR, 'anime_list','anime-completed-transformed.json' + ); + } + + protected function _get_list_from_api($status="all") + { + $data = json_decode(file_get_contents($this->transformed_data_file), TRUE); + return $data; + } +} +// End of mocks.php \ No newline at end of file diff --git a/tests/test_data/sessions/.gitkeep b/tests/test_data/sessions/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_data/sessions/3rhk4n2lh9cil0rhv7t0qi5na1 b/tests/test_data/sessions/3rhk4n2lh9cil0rhv7t0qi5na1 new file mode 100644 index 00000000..3cc762b5 --- /dev/null +++ b/tests/test_data/sessions/3rhk4n2lh9cil0rhv7t0qi5na1 @@ -0,0 +1 @@ +"" \ No newline at end of file diff --git a/tests/test_data/sessions/8mh03j6ffnee1ldbtihfa392e7 b/tests/test_data/sessions/8mh03j6ffnee1ldbtihfa392e7 new file mode 100644 index 00000000..3cc762b5 --- /dev/null +++ b/tests/test_data/sessions/8mh03j6ffnee1ldbtihfa392e7 @@ -0,0 +1 @@ +"" \ No newline at end of file diff --git a/tests/test_data/sessions/sess_klmd1570n1grdlj67hh2p967f4 b/tests/test_data/sessions/sess_klmd1570n1grdlj67hh2p967f4 new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_data/sessions/sess_uj9deojpbaaem1ov7v1473u3p7 b/tests/test_data/sessions/sess_uj9deojpbaaem1ov7v1473u3p7 new file mode 100644 index 00000000..e69de29b