From 9eadd602bd22fe5fd21e376f9aa0764990efb6b6 Mon Sep 17 00:00:00 2001 From: "J. King" Date: Wed, 3 Jan 2018 23:13:08 -0500 Subject: [PATCH] Replace Response objects with PSR-7 response messages; improves #53 While the test suite passes, this commit yields a broken server: replacing ad hoc request objectss with PSR-7 ones is still required, as is emission of PSR-7 responses. Both will come in subsequent commits, with tests Diactoros was chosen specifically because it includes facilities for emitting responses, something which is awkward to test. The end of this refactoring should see both the Response and Request classes disappear, and the general REST class fully covered (as well as any speculative additions to AbstractHanlder). --- composer.json | 3 +- composer.lock | 104 +++++++++- lib/REST.php | 2 +- lib/REST/AbstractHandler.php | 2 +- lib/REST/Handler.php | 2 +- lib/REST/NextCloudNews/V1_2.php | 194 +++++++++--------- lib/REST/NextCloudNews/Versions.php | 13 +- lib/REST/TinyTinyRSS/API.php | 23 ++- lib/REST/TinyTinyRSS/Icon.php | 8 +- tests/cases/REST/NextCloudNews/TestV1_2.php | 162 +++++++-------- .../cases/REST/NextCloudNews/TestVersions.php | 25 +-- tests/cases/REST/TinyTinyRSS/TestAPI.php | 82 ++++---- tests/cases/REST/TinyTinyRSS/TestIcon.php | 26 +-- tests/lib/AbstractTest.php | 13 ++ vendor-bin/phpunit/composer.lock | 20 +- vendor-bin/robo/composer.lock | 76 +++++-- 16 files changed, 458 insertions(+), 297 deletions(-) diff --git a/composer.json b/composer.json index 02949a3b..aa4ac4a8 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,8 @@ "fguillot/picofeed": ">=0.1.31", "hosteurope/password-generator": "^1.0", "docopt/docopt": "^1.0", - "jkingweb/druuid": "^3.0" + "jkingweb/druuid": "^3.0", + "zendframework/zend-diactoros": "^1.6" }, "require-dev": { "bamarni/composer-bin-plugin": "*" diff --git a/composer.lock b/composer.lock index 8a76cbdf..3c1262ab 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "8a3c7ff23f125a5fa3dac2e6a7244a90", + "content-hash": "7d381fa958169b7079c1d3c5b911f3bd", "packages": [ { "name": "docopt/docopt", @@ -190,6 +190,108 @@ ], "time": "2017-02-09T14:17:01+00:00" }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06T14:39:51+00:00" + }, + { + "name": "zendframework/zend-diactoros", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-diactoros.git", + "reference": "c8664b92a6d5bc229e48b0923486c097e45a7877" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/c8664b92a6d5bc229e48b0923486c097e45a7877", + "reference": "c8664b92a6d5bc229e48b0923486c097e45a7877", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0", + "psr/http-message": "^1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "ext-dom": "*", + "ext-libxml": "*", + "phpunit/phpunit": "^5.7.16 || ^6.0.8", + "zendframework/zend-coding-standard": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6-dev", + "dev-develop": "1.7-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Diactoros\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "description": "PSR HTTP Message implementations", + "homepage": "https://github.com/zendframework/zend-diactoros", + "keywords": [ + "http", + "psr", + "psr-7" + ], + "time": "2017-10-12T15:24:51+00:00" + }, { "name": "zendframework/zendxml", "version": "1.0.2", diff --git a/lib/REST.php b/lib/REST.php index d79c0034..ea8c87dc 100644 --- a/lib/REST.php +++ b/lib/REST.php @@ -48,7 +48,7 @@ class REST { public function __construct() { } - public function dispatch(REST\Request $req = null): REST\Response { + public function dispatch(REST\Request $req = null): \Psr\Http\Message\ResponseInterface { if ($req===null) { $req = new REST\Request(); } diff --git a/lib/REST/AbstractHandler.php b/lib/REST/AbstractHandler.php index d471525b..273e61a4 100644 --- a/lib/REST/AbstractHandler.php +++ b/lib/REST/AbstractHandler.php @@ -11,7 +11,7 @@ use JKingWeb\Arsse\Misc\ValueInfo; abstract class AbstractHandler implements Handler { abstract public function __construct(); - abstract public function dispatch(Request $req): Response; + abstract public function dispatch(Request $req): \Psr\Http\Message\ResponseInterface; protected function fieldMapNames(array $data, array $map): array { $out = []; diff --git a/lib/REST/Handler.php b/lib/REST/Handler.php index 05ddafff..4d4904a3 100644 --- a/lib/REST/Handler.php +++ b/lib/REST/Handler.php @@ -8,5 +8,5 @@ namespace JKingWeb\Arsse\REST; interface Handler { public function __construct(); - public function dispatch(Request $req): Response; + public function dispatch(Request $req): \Psr\Http\Message\ResponseInterface; } diff --git a/lib/REST/NextCloudNews/V1_2.php b/lib/REST/NextCloudNews/V1_2.php index 814ee570..bf395512 100644 --- a/lib/REST/NextCloudNews/V1_2.php +++ b/lib/REST/NextCloudNews/V1_2.php @@ -15,7 +15,9 @@ use JKingWeb\Arsse\Misc\ValueInfo; use JKingWeb\Arsse\AbstractException; use JKingWeb\Arsse\Db\ExceptionInput; use JKingWeb\Arsse\Feed\Exception as FeedException; -use JKingWeb\Arsse\REST\Response; +use \Psr\Http\Message\ResponseInterface; +use Zend\Diactoros\Response\JsonResponse as Response; +use Zend\Diactoros\Response\EmptyResponse; class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { const REALM = "NextCloud News API v1-2"; @@ -72,10 +74,10 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { public function __construct() { } - public function dispatch(\JKingWeb\Arsse\REST\Request $req): Response { + public function dispatch(\JKingWeb\Arsse\REST\Request $req): ResponseInterface { // try to authenticate if (!Arsse::$user->authHTTP()) { - return new Response(401, "", "", ['WWW-Authenticate: Basic realm="'.self::REALM.'"']); + return new EmptyResponse(401, ['WWW-Authenticate' => 'Basic realm="'.self::REALM.'"']); } // handle HTTP OPTIONS requests if ($req->method=="OPTIONS") { @@ -85,12 +87,12 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { if ($req->body) { // if the entity body is not JSON according to content type, return "415 Unsupported Media Type" if (!preg_match("<^application/json\b|^$>", $req->type)) { - return new Response(415, "", "", ['Accept: application/json']); + return new EmptyResponse(415, ['Accept' => "application/json"]); } $data = @json_decode($req->body, true); if (json_last_error() != \JSON_ERROR_NONE) { // if the body could not be parsed as JSON, return "400 Bad Request" - return new Response(400); + return new EmptyResponse(400); } } else { $data = []; @@ -101,12 +103,12 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { try { $func = $this->chooseCall($req->paths, $req->method); } catch (Exception404 $e) { - return new Response(404); + return new EmptyResponse(404); } catch (Exception405 $e) { - return new Response(405, "", "", ["Allow: ".$e->getMessage()]); + return new EmptyResponse(405, ['Allow' => $e->getMessage()]); } if (!method_exists($this, $func)) { - return new Response(501); // @codeCoverageIgnore + return new EmptyResponse(501); // @codeCoverageIgnore } // dispatch try { @@ -114,10 +116,10 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { // @codeCoverageIgnoreStart } catch (Exception $e) { // if there was a REST exception return 400 - return new Response(400); + return new EmptyResponse(400); } catch (AbstractException $e) { // if there was any other Arsse exception return 500 - return new Response(500); + return new EmptyResponse(500); } // @codeCoverageIgnoreEnd } @@ -242,7 +244,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { return $article; } - protected function handleHTTPOptions(array $url): Response { + protected function handleHTTPOptions(array $url): ResponseInterface { // normalize the URL path $url = $this->normalizePath($url); if (isset($this->paths[$url])) { @@ -252,81 +254,81 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { if (in_array("GET", $allowed)) { array_unshift($allowed, "HEAD"); } - return new Response(204, "", "", [ - "Allow: ".implode(",", $allowed), - "Accept: application/json", + return new EmptyResponse(204, [ + 'Allow' => implode(",", $allowed), + 'Accept' => "application/json", ]); } else { // if the path is not supported, return 404 - return new Response(404); + return new EmptyResponse(404); } } // list folders - protected function folderList(array $url, array $data): Response { + protected function folderList(array $url, array $data): ResponseInterface { $folders = []; foreach (Arsse::$db->folderList(Arsse::$user->id, null, false) as $folder) { $folders[] = $this->folderTranslate($folder); } - return new Response(200, ['folders' => $folders]); + return new Response(['folders' => $folders]); } // create a folder - protected function folderAdd(array $url, array $data): Response { + protected function folderAdd(array $url, array $data): ResponseInterface { try { $folder = Arsse::$db->folderAdd(Arsse::$user->id, ['name' => $data['name']]); } catch (ExceptionInput $e) { switch ($e->getCode()) { // folder already exists - case 10236: return new Response(409); + case 10236: return new EmptyResponse(409); // folder name not acceptable case 10231: - case 10232: return new Response(422); + case 10232: return new EmptyResponse(422); // other errors related to input - default: return new Response(400); // @codeCoverageIgnore + default: return new EmptyResponse(400); // @codeCoverageIgnore } } $folder = $this->folderTranslate(Arsse::$db->folderPropertiesGet(Arsse::$user->id, $folder)); - return new Response(200, ['folders' => [$folder]]); + return new Response(['folders' => [$folder]]); } // delete a folder - protected function folderRemove(array $url, array $data): Response { + protected function folderRemove(array $url, array $data): ResponseInterface { // perform the deletion try { Arsse::$db->folderRemove(Arsse::$user->id, (int) $url[1]); } catch (ExceptionInput $e) { // folder does not exist - return new Response(404); + return new EmptyResponse(404); } - return new Response(204); + return new EmptyResponse(204); } // rename a folder (also supports moving nesting folders, but this is not a feature of the API) - protected function folderRename(array $url, array $data): Response { + protected function folderRename(array $url, array $data): ResponseInterface { try { Arsse::$db->folderPropertiesSet(Arsse::$user->id, (int) $url[1], ['name' => $data['name']]); } catch (ExceptionInput $e) { switch ($e->getCode()) { // folder does not exist - case 10239: return new Response(404); + case 10239: return new EmptyResponse(404); // folder already exists - case 10236: return new Response(409); + case 10236: return new EmptyResponse(409); // folder name not acceptable case 10231: - case 10232: return new Response(422); + case 10232: return new EmptyResponse(422); // other errors related to input - default: return new Response(400); // @codeCoverageIgnore + default: return new EmptyResponse(400); // @codeCoverageIgnore } } - return new Response(204); + return new EmptyResponse(204); } // mark all articles associated with a folder as read - protected function folderMarkRead(array $url, array $data): Response { + protected function folderMarkRead(array $url, array $data): ResponseInterface { if (!ValueInfo::id($data['newestItemId'])) { // if the item ID is invalid (i.e. not a positive integer), this is an error - return new Response(422); + return new EmptyResponse(422); } // build the context $c = new Context; @@ -337,16 +339,16 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { Arsse::$db->articleMark(Arsse::$user->id, ['read' => true], $c); } catch (ExceptionInput $e) { // folder does not exist - return new Response(404); + return new EmptyResponse(404); } - return new Response(204); + return new EmptyResponse(204); } // return list of feeds which should be refreshed - protected function feedListStale(array $url, array $data): Response { + protected function feedListStale(array $url, array $data): ResponseInterface { // function requires admin rights per spec if (Arsse::$user->rightsGet(Arsse::$user->id)==User::RIGHTS_NONE) { - return new Response(403); + return new EmptyResponse(403); } // list stale feeds which should be checked for updates $feeds = Arsse::$db->feedListStale(); @@ -355,42 +357,42 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { // since in our implementation feeds don't belong the users, the 'userId' field will always be an empty string $out[] = ['id' => $feed, 'userId' => ""]; } - return new Response(200, ['feeds' => $out]); + return new Response(['feeds' => $out]); } // refresh a feed - protected function feedUpdate(array $url, array $data): Response { + protected function feedUpdate(array $url, array $data): ResponseInterface { // function requires admin rights per spec if (Arsse::$user->rightsGet(Arsse::$user->id)==User::RIGHTS_NONE) { - return new Response(403); + return new EmptyResponse(403); } try { Arsse::$db->feedUpdate($data['feedId']); } catch (ExceptionInput $e) { switch ($e->getCode()) { case 10239: // feed does not exist - return new Response(404); + return new EmptyResponse(404); case 10237: // feed ID invalid - return new Response(422); + return new EmptyResponse(422); default: // other errors related to input - return new Response(400); // @codeCoverageIgnore + return new EmptyResponse(400); // @codeCoverageIgnore } } - return new Response(204); + return new EmptyResponse(204); } // add a new feed - protected function subscriptionAdd(array $url, array $data): Response { + protected function subscriptionAdd(array $url, array $data): ResponseInterface { // try to add the feed $tr = Arsse::$db->begin(); try { $id = Arsse::$db->subscriptionAdd(Arsse::$user->id, (string) $data['url']); } catch (ExceptionInput $e) { // feed already exists - return new Response(409); + return new EmptyResponse(409); } catch (FeedException $e) { // feed could not be retrieved - return new Response(422); + return new EmptyResponse(422); } // if a folder was specified, move the feed to the correct folder; silently ignore errors if ($data['folderId']) { @@ -408,11 +410,11 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { if ($newest) { $out['newestItemId'] = $newest; } - return new Response(200, $out); + return new Response($out); } // return list of feeds for the logged-in user - protected function subscriptionList(array $url, array $data): Response { + protected function subscriptionList(array $url, array $data): ResponseInterface { $subs = Arsse::$db->subscriptionList(Arsse::$user->id); $out = []; foreach ($subs as $sub) { @@ -424,43 +426,43 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { if ($newest) { $out['newestItemId'] = $newest; } - return new Response(200, $out); + return new Response($out); } // delete a feed - protected function subscriptionRemove(array $url, array $data): Response { + protected function subscriptionRemove(array $url, array $data): ResponseInterface { try { Arsse::$db->subscriptionRemove(Arsse::$user->id, (int) $url[1]); } catch (ExceptionInput $e) { // feed does not exist - return new Response(404); + return new EmptyResponse(404); } - return new Response(204); + return new EmptyResponse(204); } // rename a feed - protected function subscriptionRename(array $url, array $data): Response { + protected function subscriptionRename(array $url, array $data): ResponseInterface { try { Arsse::$db->subscriptionPropertiesSet(Arsse::$user->id, (int) $url[1], ['title' => (string) $data['feedTitle']]); } catch (ExceptionInput $e) { switch ($e->getCode()) { // subscription does not exist - case 10239: return new Response(404); + case 10239: return new EmptyResponse(404); // name is invalid case 10231: - case 10232: return new Response(422); + case 10232: return new EmptyResponse(422); // other errors related to input - default: return new Response(400); // @codeCoverageIgnore + default: return new EmptyResponse(400); // @codeCoverageIgnore } } - return new Response(204); + return new EmptyResponse(204); } // move a feed to a folder - protected function subscriptionMove(array $url, array $data): Response { + protected function subscriptionMove(array $url, array $data): ResponseInterface { // if no folder is specified this is an error if (!isset($data['folderId'])) { - return new Response(422); + return new EmptyResponse(422); } // perform the move try { @@ -468,22 +470,22 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { } catch (ExceptionInput $e) { switch ($e->getCode()) { case 10239: // subscription does not exist - return new Response(404); + return new EmptyResponse(404); case 10235: // folder does not exist case 10237: // folder ID is invalid - return new Response(422); + return new EmptyResponse(422); default: // other errors related to input - return new Response(400); // @codeCoverageIgnore + return new EmptyResponse(400); // @codeCoverageIgnore } } - return new Response(204); + return new EmptyResponse(204); } // mark all articles associated with a subscription as read - protected function subscriptionMarkRead(array $url, array $data): Response { + protected function subscriptionMarkRead(array $url, array $data): ResponseInterface { if (!ValueInfo::id($data['newestItemId'])) { // if the item ID is invalid (i.e. not a positive integer), this is an error - return new Response(422); + return new EmptyResponse(422); } // build the context $c = new Context; @@ -494,13 +496,13 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { Arsse::$db->articleMark(Arsse::$user->id, ['read' => true], $c); } catch (ExceptionInput $e) { // subscription does not exist - return new Response(404); + return new EmptyResponse(404); } - return new Response(204); + return new EmptyResponse(204); } // list articles and their properties - protected function articleList(array $url, array $data): Response { + protected function articleList(array $url, array $data): ResponseInterface { // set the context options supplied by the client $c = new Context; // set the batch size @@ -553,32 +555,32 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { $items = Arsse::$db->articleList(Arsse::$user->id, $c, Database::LIST_TYPICAL); } catch (ExceptionInput $e) { // ID of subscription or folder is not valid - return new Response(422); + return new EmptyResponse(422); } $out = []; foreach ($items as $item) { $out[] = $this->articleTranslate($item); } $out = ['items' => $out]; - return new Response(200, $out); + return new Response($out); } // mark all articles as read - protected function articleMarkReadAll(array $url, array $data): Response { + protected function articleMarkReadAll(array $url, array $data): ResponseInterface { if (!ValueInfo::id($data['newestItemId'])) { // if the item ID is invalid (i.e. not a positive integer), this is an error - return new Response(422); + return new EmptyResponse(422); } // build the context $c = new Context; $c->latestEdition((int) $data['newestItemId']); // perform the operation Arsse::$db->articleMark(Arsse::$user->id, ['read' => true], $c); - return new Response(204); + return new EmptyResponse(204); } // mark a single article as read - protected function articleMarkRead(array $url, array $data): Response { + protected function articleMarkRead(array $url, array $data): ResponseInterface { // initialize the matching context $c = new Context; $c->edition((int) $url[1]); @@ -588,13 +590,13 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { Arsse::$db->articleMark(Arsse::$user->id, ['read' => $set], $c); } catch (ExceptionInput $e) { // ID is not valid - return new Response(404); + return new EmptyResponse(404); } - return new Response(204); + return new EmptyResponse(204); } // mark a single article as read - protected function articleMarkStarred(array $url, array $data): Response { + protected function articleMarkStarred(array $url, array $data): ResponseInterface { // initialize the matching context $c = new Context; $c->article((int) $url[2]); @@ -604,13 +606,13 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { Arsse::$db->articleMark(Arsse::$user->id, ['starred' => $set], $c); } catch (ExceptionInput $e) { // ID is not valid - return new Response(404); + return new EmptyResponse(404); } - return new Response(204); + return new EmptyResponse(204); } // mark an array of articles as read - protected function articleMarkReadMulti(array $url, array $data): Response { + protected function articleMarkReadMulti(array $url, array $data): ResponseInterface { // determine whether to mark read or unread $set = ($url[1]=="read"); // initialize the matching context @@ -620,11 +622,11 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { Arsse::$db->articleMark(Arsse::$user->id, ['read' => $set], $c); } catch (ExceptionInput $e) { } - return new Response(204); + return new EmptyResponse(204); } // mark an array of articles as starred - protected function articleMarkStarredMulti(array $url, array $data): Response { + protected function articleMarkStarredMulti(array $url, array $data): ResponseInterface { // determine whether to mark starred or unstarred $set = ($url[1]=="star"); // initialize the matching context @@ -634,10 +636,10 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { Arsse::$db->articleMark(Arsse::$user->id, ['starred' => $set], $c); } catch (ExceptionInput $e) { } - return new Response(204); + return new EmptyResponse(204); } - protected function userStatus(array $url, array $data): Response { + protected function userStatus(array $url, array $data): ResponseInterface { $data = Arsse::$user->propertiesGet(Arsse::$user->id, true); // construct the avatar structure, if an image is available if (isset($data['avatar'])) { @@ -655,37 +657,37 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { 'lastLoginTimestamp' => time(), 'avatar' => $avatar, ]; - return new Response(200, $out); + return new Response($out); } - protected function cleanupBefore(array $url, array $data): Response { + protected function cleanupBefore(array $url, array $data): ResponseInterface { // function requires admin rights per spec if (Arsse::$user->rightsGet(Arsse::$user->id)==User::RIGHTS_NONE) { - return new Response(403); + return new EmptyResponse(403); } Service::cleanupPre(); - return new Response(204); + return new EmptyResponse(204); } - protected function cleanupAfter(array $url, array $data): Response { + protected function cleanupAfter(array $url, array $data): ResponseInterface { // function requires admin rights per spec if (Arsse::$user->rightsGet(Arsse::$user->id)==User::RIGHTS_NONE) { - return new Response(403); + return new EmptyResponse(403); } Service::cleanupPost(); - return new Response(204); + return new EmptyResponse(204); } // return the server version - protected function serverVersion(array $url, array $data): Response { - return new Response(200, [ + protected function serverVersion(array $url, array $data): ResponseInterface { + return new Response([ 'version' => self::VERSION, 'arsse_version' => Arsse::VERSION, ]); } - protected function serverStatus(array $url, array $data): Response { - return new Response(200, [ + protected function serverStatus(array $url, array $data): ResponseInterface { + return new Response([ 'version' => self::VERSION, 'arsse_version' => Arsse::VERSION, 'warnings' => [ diff --git a/lib/REST/NextCloudNews/Versions.php b/lib/REST/NextCloudNews/Versions.php index b51773fb..1f97e77d 100644 --- a/lib/REST/NextCloudNews/Versions.php +++ b/lib/REST/NextCloudNews/Versions.php @@ -6,22 +6,23 @@ declare(strict_types=1); namespace JKingWeb\Arsse\REST\NextCloudNews; -use JKingWeb\Arsse\REST\Response; +use Zend\Diactoros\Response\JsonResponse as Response; +use Zend\Diactoros\Response\EmptyResponse; class Versions implements \JKingWeb\Arsse\REST\Handler { public function __construct() { } - public function dispatch(\JKingWeb\Arsse\REST\Request $req): Response { + public function dispatch(\JKingWeb\Arsse\REST\Request $req): \Psr\Http\Message\ResponseInterface { if (!preg_match("<^/?$>", $req->path)) { // if the request path is an empty string or just a slash, the client is probably trying a version we don't support - return new Response(404); + return new EmptyResponse(404); } elseif ($req->method=="OPTIONS") { // if the request method is OPTIONS, respond accordingly - return new Response(204, "", "", ["Allow: HEAD,GET"]); + return new EmptyResponse(204, ['Allow' => "HEAD,GET"]); } elseif ($req->method != "GET") { // if a method other than GET was used, this is an error - return new Response(405, "", "", ["Allow: HEAD,GET"]); + return new EmptyResponse(405, ['Allow' => "HEAD,GET"]); } else { // otherwise return the supported versions $out = [ @@ -29,7 +30,7 @@ class Versions implements \JKingWeb\Arsse\REST\Handler { 'v1-2', ] ]; - return new Response(200, $out); + return new Response($out); } } } diff --git a/lib/REST/TinyTinyRSS/API.php b/lib/REST/TinyTinyRSS/API.php index 7435c80d..24649545 100644 --- a/lib/REST/TinyTinyRSS/API.php +++ b/lib/REST/TinyTinyRSS/API.php @@ -19,7 +19,8 @@ use JKingWeb\Arsse\ExceptionType; use JKingWeb\Arsse\Db\ExceptionInput; use JKingWeb\Arsse\Db\ResultEmpty; use JKingWeb\Arsse\Feed\Exception as FeedException; -use JKingWeb\Arsse\REST\Response; +use Zend\Diactoros\Response\JsonResponse as Response; +use Zend\Diactoros\Response\EmptyResponse; class API extends \JKingWeb\Arsse\REST\AbstractHandler { const LEVEL = 14; // emulated API level @@ -88,23 +89,23 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { public function __construct() { } - public function dispatch(\JKingWeb\Arsse\REST\Request $req): Response { + public function dispatch(\JKingWeb\Arsse\REST\Request $req): \Psr\Http\Message\ResponseInterface { if (!preg_match("<^(?:/(?:index\.php)?)?$>", $req->path)) { // reject paths other than the index - return new Response(404); + return new EmptyResponse(404); } if ($req->method=="OPTIONS") { // respond to OPTIONS rquests; the response is a fib, as we technically accept any type or method - return new Response(204, "", "", [ - "Allow: POST", - "Accept: application/json, text/json", + return new EmptyResponse(204, [ + 'Allow' => "POST", + 'Accept' => "application/json, text/json", ]); } if ($req->body) { // only JSON entities are allowed, but Content-Type is ignored, as is request method $data = @json_decode($req->body, true); if (json_last_error() != \JSON_ERROR_NONE || !is_array($data)) { - return new Response(200, self::FATAL_ERR); + return new Response(self::FATAL_ERR); } try { // normalize input @@ -123,23 +124,23 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { // TT-RSS operations are case-insensitive by dint of PHP method names being case-insensitive; this will only trigger if the method really doesn't exist throw new Exception("UNKNOWN_METHOD", ['method' => $data['op']]); } - return new Response(200, [ + return new Response([ 'seq' => $data['seq'], 'status' => 0, 'content' => $this->$method($data), ]); } catch (Exception $e) { - return new Response(200, [ + return new Response([ 'seq' => $data['seq'], 'status' => 1, 'content' => $e->getData(), ]); } catch (AbstractException $e) { - return new Response(500); + return new EmptyResponse(500); } } else { // absence of a request body indicates an error - return new Response(200, self::FATAL_ERR); + return new Response(self::FATAL_ERR); } } diff --git a/lib/REST/TinyTinyRSS/Icon.php b/lib/REST/TinyTinyRSS/Icon.php index aabb2c28..3641f17c 100644 --- a/lib/REST/TinyTinyRSS/Icon.php +++ b/lib/REST/TinyTinyRSS/Icon.php @@ -7,16 +7,16 @@ declare(strict_types=1); namespace JKingWeb\Arsse\REST\TinyTinyRSS; use JKingWeb\Arsse\Arsse; -use JKingWeb\Arsse\REST\Response; +use Zend\Diactoros\Response\EmptyResponse as Response; class Icon extends \JKingWeb\Arsse\REST\AbstractHandler { public function __construct() { } - public function dispatch(\JKingWeb\Arsse\REST\Request $req): Response { + public function dispatch(\JKingWeb\Arsse\REST\Request $req): \Psr\Http\Message\ResponseInterface { if ($req->method != "GET") { // only GET requests are allowed - return new Response(405, "", "", ["Allow: GET"]); + return new Response(405, ['Allow' => "GET"]); } elseif (!preg_match("<^(\d+)\.ico$>", $req->url, $match) || !((int) $match[1])) { return new Response(404); } @@ -26,7 +26,7 @@ class Icon extends \JKingWeb\Arsse\REST\AbstractHandler { if (($pos = strpos($url, "\r")) !== false || ($pos = strpos($url, "\n")) !== false) { $url = substr($url, 0, $pos); } - return new Response(301, "", "", ["Location: $url"]); + return new Response(301, ['Location' => $url]); } else { return new Response(404); } diff --git a/tests/cases/REST/NextCloudNews/TestV1_2.php b/tests/cases/REST/NextCloudNews/TestV1_2.php index 17c4679d..d16e1011 100644 --- a/tests/cases/REST/NextCloudNews/TestV1_2.php +++ b/tests/cases/REST/NextCloudNews/TestV1_2.php @@ -12,13 +12,14 @@ use JKingWeb\Arsse\User; use JKingWeb\Arsse\Database; use JKingWeb\Arsse\Service; use JKingWeb\Arsse\REST\Request; -use JKingWeb\Arsse\REST\Response; use JKingWeb\Arsse\Test\Result; use JKingWeb\Arsse\Misc\Date; use JKingWeb\Arsse\Misc\Context; use JKingWeb\Arsse\Db\ExceptionInput; use JKingWeb\Arsse\Db\Transaction; use JKingWeb\Arsse\REST\NextCloudNews\V1_2; +use Zend\Diactoros\Response\JsonResponse as Response; +use Zend\Diactoros\Response\EmptyResponse; use Phake; /** @covers \JKingWeb\Arsse\REST\NextCloudNews\V1_2 */ @@ -317,14 +318,9 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { $this->clearData(); } - protected function assertResponse(Response $exp, Response $act, string $text = null) { - $this->assertEquals($exp, $act, $text); - $this->assertSame($exp->payload, $act->payload, $text); - } - public function testSendAuthenticationChallenge() { Phake::when(Arsse::$user)->authHTTP->thenReturn(false); - $exp = new Response(401, "", "", ['WWW-Authenticate: Basic realm="'.V1_2::REALM.'"']); + $exp = new EmptyResponse(401, ['WWW-Authenticate' => 'Basic realm="'.V1_2::REALM.'"']); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/"))); } @@ -361,12 +357,12 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { ], ]; foreach ($errs[404] as $req) { - $exp = new Response(404); + $exp = new EmptyResponse(404); list($method, $path) = $req; $this->assertResponse($exp, $this->h->dispatch(new Request($method, $path)), "$method call to $path did not return 404."); } foreach ($errs[405] as $allow => $cases) { - $exp = new Response(405, "", "", ['Allow: '.$allow]); + $exp = new EmptyResponse(405, ['Allow' => $allow]); foreach ($cases as $req) { list($method, $path) = $req; $this->assertResponse($exp, $this->h->dispatch(new Request($method, $path)), "$method call to $path did not return 405."); @@ -375,29 +371,29 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { } public function testRespondToInvalidInputTypes() { - $exp = new Response(415, "", "", ['Accept: application/json']); + $exp = new EmptyResponse(415, ['Accept' => "application/json"]); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", '', 'application/xml'))); - $exp = new Response(400); + $exp = new EmptyResponse(400); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", '', 'application/json'))); } public function testRespondToOptionsRequests() { - $exp = new Response(204, "", "", [ - "Allow: HEAD,GET,POST", - "Accept: application/json", + $exp = new EmptyResponse(204, [ + 'Allow' => "HEAD,GET,POST", + 'Accept' => "application/json", ]); $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", "/feeds"))); - $exp = new Response(204, "", "", [ - "Allow: DELETE", - "Accept: application/json", + $exp = new EmptyResponse(204, [ + 'Allow' => "DELETE", + 'Accept' => "application/json", ]); $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", "/feeds/2112"))); - $exp = new Response(204, "", "", [ - "Allow: HEAD,GET", - "Accept: application/json", + $exp = new EmptyResponse(204, [ + 'Allow' => "HEAD,GET", + 'Accept' => "application/json", ]); $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", "/user"))); - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", "/invalid/path"))); } @@ -411,9 +407,9 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { ['id' => 12, 'name' => "Hardware"], ]; Phake::when(Arsse::$db)->folderList(Arsse::$user->id, null, false)->thenReturn(new Result([]))->thenReturn(new Result($list)); - $exp = new Response(200, ['folders' => []]); + $exp = new Response(['folders' => []]); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/folders"))); - $exp = new Response(200, ['folders' => $out]); + $exp = new Response(['folders' => $out]); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/folders"))); } @@ -441,33 +437,33 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, ['name' => ""])->thenThrow(new ExceptionInput("missing")); Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, ['name' => " "])->thenThrow(new ExceptionInput("whitespace")); // correctly add two folders, using different means - $exp = new Response(200, ['folders' => [$out[0]]]); + $exp = new Response(['folders' => [$out[0]]]); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", json_encode($in[0]), 'application/json'))); - $exp = new Response(200, ['folders' => [$out[1]]]); + $exp = new Response(['folders' => [$out[1]]]); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders?name=Hardware"))); Phake::verify(Arsse::$db)->folderAdd(Arsse::$user->id, $in[0]); Phake::verify(Arsse::$db)->folderAdd(Arsse::$user->id, $in[1]); Phake::verify(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 1); Phake::verify(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 2); // test bad folder names - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders"))); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", '{"name":""}', 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", '{"name":" "}', 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", '{"name":{}}', 'application/json'))); // try adding the same two folders again - $exp = new Response(409); + $exp = new EmptyResponse(409); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders?name=Software"))); - $exp = new Response(409); + $exp = new EmptyResponse(409); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", json_encode($in[1]), 'application/json'))); } public function testRemoveAFolder() { Phake::when(Arsse::$db)->folderRemove(Arsse::$user->id, 1)->thenReturn(true)->thenThrow(new ExceptionInput("subjectMissing")); - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("DELETE", "/folders/1"))); // fail on the second invocation because it no longer exists - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("DELETE", "/folders/1"))); Phake::verify(Arsse::$db, Phake::times(2))->folderRemove(Arsse::$user->id, 1); } @@ -486,22 +482,22 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->folderPropertiesSet(Arsse::$user->id, 1, $in[3])->thenThrow(new ExceptionInput("whitespace")); Phake::when(Arsse::$db)->folderPropertiesSet(Arsse::$user->id, 1, $in[4])->thenReturn(true); // this should be stopped by the handler before the request gets to the database Phake::when(Arsse::$db)->folderPropertiesSet(Arsse::$user->id, 3, $this->anything())->thenThrow(new ExceptionInput("subjectMissing")); // folder ID 3 does not exist - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[0]), 'application/json'))); - $exp = new Response(409); + $exp = new EmptyResponse(409); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/2", json_encode($in[1]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[2]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[3]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[4]), 'application/json'))); - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/3", json_encode($in[0]), 'application/json'))); } public function testRetrieveServerVersion() { - $exp = new Response(200, [ + $exp = new Response([ 'version' => V1_2::VERSION, 'arsse_version' => Arsse::VERSION, ]); @@ -521,9 +517,9 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->subscriptionList(Arsse::$user->id)->thenReturn(new Result([]))->thenReturn(new Result($this->feeds['db'])); Phake::when(Arsse::$db)->articleStarred(Arsse::$user->id)->thenReturn(['total' => 0])->thenReturn(['total' => 5]); Phake::when(Arsse::$db)->editionLatest(Arsse::$user->id)->thenReturn(0)->thenReturn(4758915); - $exp = new Response(200, $exp1); + $exp = new Response($exp1); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds"))); - $exp = new Response(200, $exp2); + $exp = new Response($exp2); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds"))); } @@ -556,31 +552,31 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { // set up a mock for a bad feed which succeeds the second time Phake::when(Arsse::$db)->subscriptionAdd(Arsse::$user->id, "http://example.net/news.atom")->thenThrow(new \JKingWeb\Arsse\Feed\Exception("http://example.net/news.atom", new \PicoFeed\Client\InvalidUrlException()))->thenReturn(47); // add the subscriptions - $exp = new Response(200, $out[0]); + $exp = new Response($out[0]); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[0]), 'application/json'))); - $exp = new Response(200, $out[1]); + $exp = new Response($out[1]); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[1]), 'application/json'))); // try to add them a second time - $exp = new Response(409); + $exp = new EmptyResponse(409); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[0]), 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[1]), 'application/json'))); // try to add a bad feed - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[2]), 'application/json'))); // try again (this will succeed), with an invalid folder ID - $exp = new Response(200, $out[2]); + $exp = new Response($out[2]); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[3]), 'application/json'))); // try to add no feed - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[4]), 'application/json'))); } public function testRemoveASubscription() { Phake::when(Arsse::$db)->subscriptionRemove(Arsse::$user->id, 1)->thenReturn(true)->thenThrow(new ExceptionInput("subjectMissing")); - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("DELETE", "/feeds/1"))); // fail on the second invocation because it no longer exists - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("DELETE", "/feeds/1"))); Phake::verify(Arsse::$db, Phake::times(2))->subscriptionRemove(Arsse::$user->id, 1); } @@ -599,17 +595,17 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 1, ['folder' => 2112])->thenThrow(new ExceptionInput("idMissing")); // folder does not exist Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 1, ['folder' => -1])->thenThrow(new ExceptionInput("typeViolation")); // folder is invalid Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 42, $this->anything())->thenThrow(new ExceptionInput("subjectMissing")); // subscription does not exist - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[0]), 'application/json'))); - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[1]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[2]), 'application/json'))); - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/42/move", json_encode($in[3]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[4]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[5]), 'application/json'))); } @@ -629,17 +625,17 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 1, $this->identicalTo(['title' => ""]))->thenThrow(new ExceptionInput("missing")); Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 1, $this->identicalTo(['title' => false]))->thenThrow(new ExceptionInput("missing")); Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 42, $this->anything())->thenThrow(new ExceptionInput("subjectMissing")); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[0]), 'application/json'))); - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[1]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[2]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[3]), 'application/json'))); - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/42/rename", json_encode($in[4]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[6]), 'application/json'))); } @@ -655,11 +651,11 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { ], ]; Phake::when(Arsse::$db)->feedListStale->thenReturn(array_column($out, "id")); - $exp = new Response(200, ['feeds' => $out]); + $exp = new Response(['feeds' => $out]); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/all"))); // retrieving the list when not an admin fails Phake::when(Arsse::$user)->rightsGet->thenReturn(0); - $exp = new Response(403); + $exp = new EmptyResponse(403); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/all"))); } @@ -674,17 +670,17 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->feedUpdate(42)->thenReturn(true); Phake::when(Arsse::$db)->feedUpdate(2112)->thenThrow(new ExceptionInput("subjectMissing")); Phake::when(Arsse::$db)->feedUpdate($this->lessThan(1))->thenThrow(new ExceptionInput("typeViolation")); - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[0]), 'application/json'))); - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[1]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[2]), 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[3]), 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[4]), 'application/json'))); // updating a feed when not an admin fails Phake::when(Arsse::$user)->rightsGet->thenReturn(0); - $exp = new Response(403); + $exp = new EmptyResponse(403); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[0]), 'application/json'))); } @@ -710,12 +706,12 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->reverse(true)->folder(2112), Database::LIST_TYPICAL)->thenThrow(new ExceptionInput("idMissing")); Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->reverse(true)->subscription(-1), Database::LIST_TYPICAL)->thenThrow(new ExceptionInput("typeViolation")); Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->reverse(true)->folder(-1), Database::LIST_TYPICAL)->thenThrow(new ExceptionInput("typeViolation")); - $exp = new Response(200, ['items' => $this->articles['rest']]); + $exp = new Response(['items' => $this->articles['rest']]); // check the contents of the response $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items"))); // first instance of base context $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items/updated"))); // second instance of base context // check error conditions - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items", json_encode($in[0]), 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items", json_encode($in[1]), 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items", json_encode($in[2]), 'application/json'))); @@ -748,13 +744,13 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { $in = json_encode(['newestItemId' => 2112]); Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->folder(1)->latestEdition(2112))->thenReturn(42); Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->folder(42)->latestEdition(2112))->thenThrow(new ExceptionInput("idMissing")); // folder doesn't exist - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read", $in, 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read?newestItemId=2112"))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read?newestItemId=ook"))); - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/42/read", $in, 'application/json'))); } @@ -763,13 +759,13 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { $in = json_encode(['newestItemId' => 2112]); Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->subscription(1)->latestEdition(2112))->thenReturn(42); Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->subscription(42)->latestEdition(2112))->thenThrow(new ExceptionInput("idMissing")); // subscription doesn't exist - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read", $in, 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read?newestItemId=2112"))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read?newestItemId=ook"))); - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/42/read", $in, 'application/json'))); } @@ -777,10 +773,10 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { $read = ['read' => true]; $in = json_encode(['newestItemId' => 2112]); Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->latestEdition(2112))->thenReturn(42); - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read", $in, 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read?newestItemId=2112"))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read?newestItemId=ook"))); } @@ -798,12 +794,12 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $star, (new Context)->article(2112))->thenThrow(new ExceptionInput("subjectMissing")); // article doesn't exist doesn't exist Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $unstar, (new Context)->article(4))->thenReturn(42); Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $unstar, (new Context)->article(1337))->thenThrow(new ExceptionInput("subjectMissing")); // article doesn't exist doesn't exist - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/1/read"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/2/unread"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/1/3/star"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/4400/4/unstar"))); - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/42/read"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/47/unread"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/1/2112/star"))); @@ -829,7 +825,7 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), $this->anything())->thenReturn(42); Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->editions([]))->thenThrow(new ExceptionInput("tooShort")); // data model function requires one valid integer for multiples Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->articles([]))->thenThrow(new ExceptionInput("tooShort")); // data model function requires one valid integer for multiples - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple"))); @@ -882,29 +878,29 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { ]; $arr2['warnings']['improperlyConfiguredCron'] = true; $arr2['warnings']['incorrectDbCharset'] = true; - $exp = new Response(200, $arr1); + $exp = new Response($arr1); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/status"))); } public function testCleanUpBeforeUpdate() { Phake::when(Arsse::$db)->feedCleanup()->thenReturn(true); - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/cleanup/before-update"))); Phake::verify(Arsse::$db)->feedCleanup(); // performing a cleanup when not an admin fails Phake::when(Arsse::$user)->rightsGet->thenReturn(0); - $exp = new Response(403); + $exp = new EmptyResponse(403); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/cleanup/before-update"))); } public function testCleanUpAfterUpdate() { Phake::when(Arsse::$db)->articleCleanup()->thenReturn(true); - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/cleanup/after-update"))); Phake::verify(Arsse::$db)->articleCleanup(); // performing a cleanup when not an admin fails Phake::when(Arsse::$user)->rightsGet->thenReturn(0); - $exp = new Response(403); + $exp = new EmptyResponse(403); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/cleanup/after-update"))); } } diff --git a/tests/cases/REST/NextCloudNews/TestVersions.php b/tests/cases/REST/NextCloudNews/TestVersions.php index 3081d57a..6904d3ef 100644 --- a/tests/cases/REST/NextCloudNews/TestVersions.php +++ b/tests/cases/REST/NextCloudNews/TestVersions.php @@ -8,7 +8,8 @@ namespace JKingWeb\Arsse\TestCase\REST\NextCloudNews; use JKingWeb\Arsse\REST\NextCloudNews\Versions; use JKingWeb\Arsse\REST\Request; -use JKingWeb\Arsse\REST\Response; +use Zend\Diactoros\Response\JsonResponse as Response; +use Zend\Diactoros\Response\EmptyResponse; /** @covers \JKingWeb\Arsse\REST\NextCloudNews\Versions */ class TestVersions extends \JKingWeb\Arsse\Test\AbstractTest { @@ -17,43 +18,43 @@ class TestVersions extends \JKingWeb\Arsse\Test\AbstractTest { } public function testFetchVersionList() { - $exp = new Response(200, ['apiLevels' => ['v1-2']]); + $exp = new Response(['apiLevels' => ['v1-2']]); $h = new Versions; $req = new Request("GET", "/"); $res = $h->dispatch($req); - $this->assertEquals($exp, $res); + $this->assertResponse($exp, $res); $req = new Request("GET", ""); $res = $h->dispatch($req); - $this->assertEquals($exp, $res); + $this->assertResponse($exp, $res); $req = new Request("GET", "/?id=1827"); $res = $h->dispatch($req); - $this->assertEquals($exp, $res); + $this->assertResponse($exp, $res); } public function testRespondToOptionsRequest() { - $exp = new Response(204, "", "", ["Allow: HEAD,GET"]); + $exp = new EmptyResponse(204, ['Allow' => "HEAD,GET"]); $h = new Versions; $req = new Request("OPTIONS", "/"); $res = $h->dispatch($req); - $this->assertEquals($exp, $res); + $this->assertResponse($exp, $res); } public function testUseIncorrectMethod() { - $exp = new Response(405, "", "", ["Allow: HEAD,GET"]); + $exp = new EmptyResponse(405, ['Allow' => "HEAD,GET"]); $h = new Versions; $req = new Request("POST", "/"); $res = $h->dispatch($req); - $this->assertEquals($exp, $res); + $this->assertResponse($exp, $res); } public function testUseIncorrectPath() { - $exp = new Response(404); + $exp = new EmptyResponse(404); $h = new Versions; $req = new Request("GET", "/ook"); $res = $h->dispatch($req); - $this->assertEquals($exp, $res); + $this->assertResponse($exp, $res); $req = new Request("OPTIONS", "/ook"); $res = $h->dispatch($req); - $this->assertEquals($exp, $res); + $this->assertResponse($exp, $res); } } diff --git a/tests/cases/REST/TinyTinyRSS/TestAPI.php b/tests/cases/REST/TinyTinyRSS/TestAPI.php index 6844e9a7..d2f1ff36 100644 --- a/tests/cases/REST/TinyTinyRSS/TestAPI.php +++ b/tests/cases/REST/TinyTinyRSS/TestAPI.php @@ -12,13 +12,15 @@ use JKingWeb\Arsse\User; use JKingWeb\Arsse\Database; use JKingWeb\Arsse\Service; use JKingWeb\Arsse\REST\Request; -use JKingWeb\Arsse\REST\Response; use JKingWeb\Arsse\Test\Result; use JKingWeb\Arsse\Misc\Date; use JKingWeb\Arsse\Misc\Context; use JKingWeb\Arsse\Db\ExceptionInput; use JKingWeb\Arsse\Db\Transaction; use JKingWeb\Arsse\REST\TinyTinyRSS\API; +use Psr\Http\Message\ResponseInterface; +use Zend\Diactoros\Response\JsonResponse as Response; +use Zend\Diactoros\Response\EmptyResponse; use Phake; /** @covers \JKingWeb\Arsse\REST\TinyTinyRSS\API @@ -122,12 +124,12 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest { LONG_STRING; - protected function req($data) : Response { + protected function req($data): ResponseInterface { return $this->h->dispatch(new Request("POST", "", json_encode($data))); } protected function respGood($content = null, $seq = 0): Response { - return new Response(200, [ + return new Response([ 'seq' => $seq, 'status' => 0, 'content' => $content, @@ -136,18 +138,13 @@ LONG_STRING; protected function respErr(string $msg, $content = [], $seq = 0): Response { $err = ['error' => $msg]; - return new Response(200, [ + return new Response([ 'seq' => $seq, 'status' => 1, 'content' => array_merge($err, $content, $err), ]); } - protected function assertResponse(Response $exp, Response $act, string $text = null) { - $this->assertEquals($exp, $act, $text); - $this->assertSame($exp->payload, $act->payload, $text); - } - public function setUp() { $this->clearData(); Arsse::$conf = new Conf(); @@ -178,14 +175,14 @@ LONG_STRING; $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "", ""))); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/", ""))); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/index.php", ""))); - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/bad/path", ""))); } public function testHandleOptionsRequest() { - $exp = new Response(204, "", "", [ - "Allow: POST", - "Accept: application/json, text/json", + $exp = new EmptyResponse(204, [ + 'Allow' => "POST", + 'Accept' => "application/json, text/json", ]); $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", ""))); } @@ -226,7 +223,7 @@ LONG_STRING; 'user' => Arsse::$user->id, 'password' => "secret", ]; - $exp = new Response(500); + $exp = new EmptyResponse(500); $this->assertResponse($exp, $this->req($data)); } @@ -1630,10 +1627,10 @@ LONG_STRING; $this->assertResponse($this->outputHeadlines(1), $test); // test 'show_content' $test = $this->req($in[1]); - $this->assertArrayHasKey("content", $test->payload['content'][0]); - $this->assertArrayHasKey("content", $test->payload['content'][1]); + $this->assertArrayHasKey("content", $test->getPayload()['content'][0]); + $this->assertArrayHasKey("content", $test->getPayload()['content'][1]); foreach ($this->generateHeadlines(1) as $key => $row) { - $this->assertSame($row['content'], $test->payload['content'][$key]['content']); + $this->assertSame($row['content'], $test->getPayload()['content'][$key]['content']); } // test 'include_attachments' $test = $this->req($in[2]); @@ -1649,25 +1646,23 @@ LONG_STRING; 'post_id' => "2112", ], ]; - $this->assertArrayHasKey("attachments", $test->payload['content'][0]); - $this->assertArrayHasKey("attachments", $test->payload['content'][1]); - $this->assertSame([], $test->payload['content'][0]['attachments']); - $this->assertSame($exp, $test->payload['content'][1]['attachments']); + $this->assertArrayHasKey("attachments", $test->getPayload()['content'][0]); + $this->assertArrayHasKey("attachments", $test->getPayload()['content'][1]); + $this->assertSame([], $test->getPayload()['content'][0]['attachments']); + $this->assertSame($exp, $test->getPayload()['content'][1]['attachments']); // test 'include_header' $test = $this->req($in[3]); - $exp = $this->outputHeadlines(1); - $exp->payload['content'] = [ + $exp = $this->respGood([ ['id' => -4, 'is_cat' => false, 'first_id' => 1], - $exp->payload['content'], - ]; + $this->outputHeadlines(1)->getPayload()['content'], + ]); $this->assertResponse($exp, $test); // test 'include_header' with a category $test = $this->req($in[4]); - $exp = $this->outputHeadlines(1); - $exp->payload['content'] = [ + $exp = $this->respGood([ ['id' => -3, 'is_cat' => true, 'first_id' => 1], - $exp->payload['content'], - ]; + $this->outputHeadlines(1)->getPayload()['content'], + ]); $this->assertResponse($exp, $test); // test 'include_header' with an empty result $test = $this->req($in[5]); @@ -1686,37 +1681,34 @@ LONG_STRING; $this->assertResponse($exp, $test); // test 'include_header' with ascending order $test = $this->req($in[7]); - $exp = $this->outputHeadlines(1); - $exp->payload['content'] = [ + $exp = $this->respGood([ ['id' => -4, 'is_cat' => false, 'first_id' => 0], - $exp->payload['content'], - ]; + $this->outputHeadlines(1)->getPayload()['content'], + ]); $this->assertResponse($exp, $test); // test 'include_header' with skip Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->reverse(true)->limit(1)->subscription(42), Database::LIST_MINIMAL)->thenReturn($this->generateHeadlines(1867)); $test = $this->req($in[8]); - $exp = $this->outputHeadlines(1); - $exp->payload['content'] = [ + $exp = $this->respGood([ ['id' => 42, 'is_cat' => false, 'first_id' => 1867], - $exp->payload['content'], - ]; + $this->outputHeadlines(1)->getPayload()['content'], + ]); $this->assertResponse($exp, $test); // test 'include_header' with skip and ascending order $test = $this->req($in[9]); - $exp = $this->outputHeadlines(1); - $exp->payload['content'] = [ + $exp = $this->respGood([ ['id' => 42, 'is_cat' => false, 'first_id' => 0], - $exp->payload['content'], - ]; + $this->outputHeadlines(1)->getPayload()['content'], + ]); $this->assertResponse($exp, $test); // test 'show_excerpt' $exp1 = "“This & that, you know‽”"; $exp2 = "Pour vous faire mieux connaitre d’ou\u{300} vient l’erreur de ceux qui bla\u{302}ment la volupte\u{301}, et qui louent en…"; $test = $this->req($in[10]); - $this->assertArrayHasKey("excerpt", $test->payload['content'][0]); - $this->assertArrayHasKey("excerpt", $test->payload['content'][1]); - $this->assertSame($exp1, $test->payload['content'][0]['excerpt']); - $this->assertSame($exp2, $test->payload['content'][1]['excerpt']); + $this->assertArrayHasKey("excerpt", $test->getPayload()['content'][0]); + $this->assertArrayHasKey("excerpt", $test->getPayload()['content'][1]); + $this->assertSame($exp1, $test->getPayload()['content'][0]['excerpt']); + $this->assertSame($exp2, $test->getPayload()['content'][1]['excerpt']); } protected function generateHeadlines(int $id): Result { diff --git a/tests/cases/REST/TinyTinyRSS/TestIcon.php b/tests/cases/REST/TinyTinyRSS/TestIcon.php index 64f33588..c5c67bc4 100644 --- a/tests/cases/REST/TinyTinyRSS/TestIcon.php +++ b/tests/cases/REST/TinyTinyRSS/TestIcon.php @@ -12,7 +12,7 @@ use JKingWeb\Arsse\User; use JKingWeb\Arsse\Database; use JKingWeb\Arsse\REST\TinyTinyRSS\Icon; use JKingWeb\Arsse\REST\Request; -use JKingWeb\Arsse\REST\Response; +use Zend\Diactoros\Response\EmptyResponse as Response; use Phake; /** @covers \JKingWeb\Arsse\REST\TinyTinyRSS\Icon */ @@ -38,20 +38,20 @@ class TestIcon extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->subscriptionFavicon(2112)->thenReturn("http://example.net/logo.png"); Phake::when(Arsse::$db)->subscriptionFavicon(1337)->thenReturn("http://example.org/icon.gif\r\nLocation: http://bad.example.com/"); // these requests should succeed - $exp = new Response(301, "", "", ["Location: http://example.com/favicon.ico"]); - $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "42.ico"))); - $exp = new Response(301, "", "", ["Location: http://example.net/logo.png"]); - $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "2112.ico"))); - $exp = new Response(301, "", "", ["Location: http://example.org/icon.gif"]); - $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "1337.ico"))); + $exp = new Response(301, ['Location' => "http://example.com/favicon.ico"]); + $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "42.ico"))); + $exp = new Response(301, ['Location' => "http://example.net/logo.png"]); + $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "2112.ico"))); + $exp = new Response(301, ['Location' => "http://example.org/icon.gif"]); + $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "1337.ico"))); // these requests should fail $exp = new Response(404); - $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "ook.ico"))); - $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "ook"))); - $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "47.ico"))); - $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "2112.png"))); + $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "ook.ico"))); + $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "ook"))); + $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "47.ico"))); + $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "2112.png"))); // only GET is allowed - $exp = new Response(405, "", "", ["Allow: GET"]); - $this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "2112.ico"))); + $exp = new Response(405, ['Allow' => "GET"]); + $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "2112.ico"))); } } diff --git a/tests/lib/AbstractTest.php b/tests/lib/AbstractTest.php index 85bb0eb9..762e991c 100644 --- a/tests/lib/AbstractTest.php +++ b/tests/lib/AbstractTest.php @@ -9,6 +9,9 @@ namespace JKingWeb\Arsse\Test; use JKingWeb\Arsse\Exception; use JKingWeb\Arsse\Arsse; use JKingWeb\Arsse\Misc\Date; +use Psr\Http\Message\ResponseInterface; +use Zend\Diactoros\Response\JsonResponse; +use Zend\Diactoros\Response\EmptyResponse; /** @coversNothing */ abstract class AbstractTest extends \PHPUnit\Framework\TestCase { @@ -29,6 +32,16 @@ abstract class AbstractTest extends \PHPUnit\Framework\TestCase { } } + protected function assertResponse(ResponseInterface $exp, ResponseInterface $act, string $text = null) { + $this->assertEquals($exp->getHeaders(), $act->getHeaders(), $text); + $this->assertEquals($exp->getStatusCode(), $act->getStatusCode(), $text); + $this->assertInstanceOf(get_class($exp), $act); + if ($exp instanceof JsonResponse) { + $this->assertEquals($exp->getPayload(), $act->getPayload(), $text); + $this->assertSame($exp->getPayload(), $act->getPayload(), $text); + } + } + public function approximateTime($exp, $act) { if (is_null($act)) { return null; diff --git a/vendor-bin/phpunit/composer.lock b/vendor-bin/phpunit/composer.lock index bb028642..26b133e0 100644 --- a/vendor-bin/phpunit/composer.lock +++ b/vendor-bin/phpunit/composer.lock @@ -777,16 +777,16 @@ }, { "name": "phpunit/phpunit", - "version": "6.5.4", + "version": "6.5.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "1b2f933d5775f9237369deaa2d2bfbf9d652be4c" + "reference": "83d27937a310f2984fd575686138597147bdc7df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1b2f933d5775f9237369deaa2d2bfbf9d652be4c", - "reference": "1b2f933d5775f9237369deaa2d2bfbf9d652be4c", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/83d27937a310f2984fd575686138597147bdc7df", + "reference": "83d27937a310f2984fd575686138597147bdc7df", "shasum": "" }, "require": { @@ -857,7 +857,7 @@ "testing", "xunit" ], - "time": "2017-12-10T08:06:19+00:00" + "time": "2017-12-17T06:31:19+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -965,16 +965,16 @@ }, { "name": "sebastian/comparator", - "version": "2.1.0", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1174d9018191e93cb9d719edec01257fc05f8158" + "reference": "b11c729f95109b56a0fe9650c6a63a0fcd8c439f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1174d9018191e93cb9d719edec01257fc05f8158", - "reference": "1174d9018191e93cb9d719edec01257fc05f8158", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/b11c729f95109b56a0fe9650c6a63a0fcd8c439f", + "reference": "b11c729f95109b56a0fe9650c6a63a0fcd8c439f", "shasum": "" }, "require": { @@ -1025,7 +1025,7 @@ "compare", "equality" ], - "time": "2017-11-03T07:16:52+00:00" + "time": "2017-12-22T14:50:35+00:00" }, { "name": "sebastian/diff", diff --git a/vendor-bin/robo/composer.lock b/vendor-bin/robo/composer.lock index ee2d3df9..3f95a030 100644 --- a/vendor-bin/robo/composer.lock +++ b/vendor-bin/robo/composer.lock @@ -59,28 +59,33 @@ }, { "name": "consolidation/config", - "version": "1.0.7", + "version": "1.0.9", "source": { "type": "git", "url": "https://github.com/consolidation/config.git", - "reference": "b59a3b9ea750c21397f26a68fd2e04d9580af42e" + "reference": "34ca8d7c1ee60a7b591b10617114cf1210a2e92c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/config/zipball/b59a3b9ea750c21397f26a68fd2e04d9580af42e", - "reference": "b59a3b9ea750c21397f26a68fd2e04d9580af42e", + "url": "https://api.github.com/repos/consolidation/config/zipball/34ca8d7c1ee60a7b591b10617114cf1210a2e92c", + "reference": "34ca8d7c1ee60a7b591b10617114cf1210a2e92c", "shasum": "" }, "require": { "dflydev/dot-access-data": "^1.1.0", - "grasmash/yaml-expander": "^1.1", + "grasmash/expander": "^1", "php": ">=5.4.0" }, "require-dev": { + "greg-1-anderson/composer-test-scenarios": "^1", "phpunit/phpunit": "^4", "satooshi/php-coveralls": "^1.0", "squizlabs/php_codesniffer": "2.*", - "symfony/console": "^2.5|^3" + "symfony/console": "^2.5|^3|^4", + "symfony/yaml": "^2.8.11|^3|^4" + }, + "suggest": { + "symfony/yaml": "Required to use Consolidation\\Config\\Loader\\YamlConfigLoader" }, "type": "library", "extra": { @@ -104,7 +109,7 @@ } ], "description": "Provide configuration services for a commandline tool.", - "time": "2017-10-25T05:50:10+00:00" + "time": "2017-12-22T17:28:19+00:00" }, { "name": "consolidation/log", @@ -205,16 +210,16 @@ }, { "name": "consolidation/robo", - "version": "1.2.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/consolidation/Robo.git", - "reference": "c46c13de3eca55e6b3635f363688ce85e845adf0" + "reference": "b6296f1cf1088f1a11b0b819f9e42ef6f00b79a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/c46c13de3eca55e6b3635f363688ce85e845adf0", - "reference": "c46c13de3eca55e6b3635f363688ce85e845adf0", + "url": "https://api.github.com/repos/consolidation/Robo/zipball/b6296f1cf1088f1a11b0b819f9e42ef6f00b79a9", + "reference": "b6296f1cf1088f1a11b0b819f9e42ef6f00b79a9", "shasum": "" }, "require": { @@ -278,7 +283,7 @@ } ], "description": "Modern task runner", - "time": "2017-12-13T02:10:49+00:00" + "time": "2017-12-29T06:48:35+00:00" }, { "name": "container-interop/container-interop", @@ -370,6 +375,53 @@ ], "time": "2017-01-20T21:14:22+00:00" }, + { + "name": "grasmash/expander", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/grasmash/expander.git", + "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/grasmash/expander/zipball/95d6037344a4be1dd5f8e0b0b2571a28c397578f", + "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^1.1.0", + "php": ">=5.4" + }, + "require-dev": { + "greg-1-anderson/composer-test-scenarios": "^1", + "phpunit/phpunit": "^4|^5.5.4", + "satooshi/php-coveralls": "^1.0.2|dev-master", + "squizlabs/php_codesniffer": "^2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Grasmash\\Expander\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Grasmick" + } + ], + "description": "Expands internal property references in PHP arrays file.", + "time": "2017-12-21T22:14:55+00:00" + }, { "name": "grasmash/yaml-expander", "version": "1.4.0",