From 4d18bf27e2d2662422548e6d64e2accee7149e74 Mon Sep 17 00:00:00 2001 From: "J. King" Date: Fri, 5 Aug 2022 22:08:36 -0400 Subject: [PATCH] Adjust most uses of Diactoros to Guzzle PSR-7 --- lib/Misc/HTTP.php | 6 +- lib/REST/Fever/API.php | 3 +- lib/REST/Miniflux/V1.php | 45 +++-- lib/REST/NextcloudNews/V1_2.php | 19 +- lib/REST/NextcloudNews/Versions.php | 3 +- lib/REST/TinyTinyRSS/API.php | 9 +- tests/cases/REST/Fever/TestAPI.php | 37 ++-- tests/cases/REST/Miniflux/TestV1.php | 165 +++++++++--------- tests/cases/REST/NextcloudNews/TestV1_2.php | 35 ++-- .../cases/REST/NextcloudNews/TestVersions.php | 3 +- tests/cases/REST/TinyTinyRSS/TestAPI.php | 11 +- tests/lib/AbstractTest.php | 15 +- 12 files changed, 173 insertions(+), 178 deletions(-) diff --git a/lib/Misc/HTTP.php b/lib/Misc/HTTP.php index cd78b38..b772ad2 100644 --- a/lib/Misc/HTTP.php +++ b/lib/Misc/HTTP.php @@ -14,7 +14,11 @@ class HTTP { public static function matchType(MessageInterface $msg, string ...$type): bool { $header = $msg->getHeaderLine("Content-Type") ?? ""; foreach ($type as $t) { - $pattern = "/^".preg_quote(trim($t), "/")."\s*($|;|,)/Di"; + if (($t[0] ?? "") === "+") { + $pattern = "/^[^+;,\s]*".preg_quote(trim($t), "/")."\s*($|;|,)/Di"; + } else { + $pattern = "/^".preg_quote(trim($t), "/")."\s*($|;|,)/Di"; + } if (preg_match($pattern, $header)) { return true; } diff --git a/lib/REST/Fever/API.php b/lib/REST/Fever/API.php index 72f3e9b..aa900c9 100644 --- a/lib/REST/Fever/API.php +++ b/lib/REST/Fever/API.php @@ -14,7 +14,6 @@ use JKingWeb\Arsse\Misc\HTTP; use JKingWeb\Arsse\Db\ExceptionInput; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; -use Laminas\Diactoros\Response\JsonResponse; use Laminas\Diactoros\Response\XmlResponse; class API extends \JKingWeb\Arsse\REST\AbstractHandler { @@ -183,7 +182,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { $d->appendChild($this->makeXMLAssoc($data, $d->createElement("response"))); return new XmlResponse($d->saveXML()); } else { - return new JsonResponse($data, 200, [], \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE); + return HTTP::respJson($data, 200, [], \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE); } } diff --git a/lib/REST/Miniflux/V1.php b/lib/REST/Miniflux/V1.php index 4ed828a..bfdf678 100644 --- a/lib/REST/Miniflux/V1.php +++ b/lib/REST/Miniflux/V1.php @@ -27,7 +27,6 @@ use JKingWeb\Arsse\User\ExceptionConflict; use JKingWeb\Arsse\User\Exception as UserException; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; -use Laminas\Diactoros\Response\JsonResponse as Response; use Laminas\Diactoros\Response\TextResponse as GenericResponse; use Laminas\Diactoros\Uri; @@ -534,17 +533,17 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { // TODO: This needs to be refined once PicoFeed is replaced $out[] = ['title' => "Feed", 'type' => "rss", 'url' => $url]; } - return new Response($out); + return HTTP::respJson($out); } protected function getUsers(): ResponseInterface { $tr = Arsse::$user->begin(); - return new Response($this->listUsers(Arsse::$user->list(), false)); + return HTTP::respJson($this->listUsers(Arsse::$user->list(), false)); } protected function getUserById(array $path): ResponseInterface { try { - return new Response($this->listUsers([$path[1]], true)[0] ?? new \stdClass); + return HTTP::respJson($this->listUsers([$path[1]], true)[0] ?? new \stdClass); } catch (UserException $e) { return new ErrorResponse("404", 404); } @@ -553,14 +552,14 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { protected function getUserByNum(array $path): ResponseInterface { try { $user = Arsse::$user->lookup((int) $path[1]); - return new Response($this->listUsers([$user], true)[0] ?? new \stdClass); + return HTTP::respJson($this->listUsers([$user], true)[0] ?? new \stdClass); } catch (UserException $e) { return new ErrorResponse("404", 404); } } protected function getCurrentUser(): ResponseInterface { - return new Response($this->listUsers([Arsse::$user->id], false)[0] ?? new \stdClass); + return HTTP::respJson($this->listUsers([Arsse::$user->id], false)[0] ?? new \stdClass); } protected function createUser(array $data): ResponseInterface { @@ -582,7 +581,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { } throw $e; // @codeCoverageIgnore } - return new Response($out, 201); + return HTTP::respJson($out, 201); } protected function updateUserByNum(array $path, array $data): ResponseInterface { @@ -628,7 +627,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { } throw $e; // @codeCoverageIgnore } - return new Response($out, 201); + return HTTP::respJson($out, 201); } protected function deleteUserByNum(array $path): ResponseInterface { @@ -667,7 +666,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { // always add 1 to the ID since the root folder will always be 1 instead of 0. $out[] = ['id' => $f['id'] + 1, 'title' => $f['name'], 'user_id' => $meta['num']]; } - return new Response($out); + return HTTP::respJson($out); } protected function createCategory(array $data): ResponseInterface { @@ -681,7 +680,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { } } $meta = Arsse::$user->propertiesGet(Arsse::$user->id, false); - return new Response(['id' => $id + 1, 'title' => $data['title'], 'user_id' => $meta['num']], 201); + return HTTP::respJson(['id' => $id + 1, 'title' => $data['title'], 'user_id' => $meta['num']], 201); } protected function updateCategory(array $path, array $data): ResponseInterface { @@ -708,7 +707,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { } } $meta = Arsse::$user->propertiesGet(Arsse::$user->id, false); - return new Response(['id' => (int) $path[1], 'title' => $title, 'user_id' => $meta['num']], 201); + return HTTP::respJson(['id' => (int) $path[1], 'title' => $title, 'user_id' => $meta['num']], 201); } protected function deleteCategory(array $path): ResponseInterface { @@ -772,7 +771,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { foreach (Arsse::$db->subscriptionList(Arsse::$user->id) as $r) { $out[] = $this->transformFeed($r, $meta['num'], $meta['root'], $meta['tz']); } - return new Response($out); + return HTTP::respJson($out); } protected function getCategoryFeeds(array $path): ResponseInterface { @@ -792,7 +791,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { // the folder does not exist return new ErrorResponse("404", 404); } - return new Response($out); + return HTTP::respJson($out); } protected function getFeed(array $path): ResponseInterface { @@ -800,7 +799,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { $meta = $this->userMeta(Arsse::$user->id); try { $sub = Arsse::$db->subscriptionPropertiesGet(Arsse::$user->id, (int) $path[1]); - return new Response($this->transformFeed($sub, $meta['num'], $meta['root'], $meta['tz'])); + return HTTP::respJson($this->transformFeed($sub, $meta['num'], $meta['root'], $meta['tz'])); } catch (ExceptionInput $e) { return new ErrorResponse("404", 404); } @@ -834,7 +833,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { return new ErrorResponse("DuplicateFeed", 409); } } - return new Response(['feed_id' => $id], 201); + return HTTP::respJson(['feed_id' => $id], 201); } protected function updateFeed(array $path, array $data): ResponseInterface { @@ -881,7 +880,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { if (!$icon || !$icon['type'] || !$icon['data']) { return new ErrorResponse("404", 404); } - return new Response([ + return HTTP::respJson([ 'id' => (int) $icon['id'], 'data' => $icon['type'].";base64,".base64_encode($icon['data']), 'mime_type' => $icon['type'], @@ -1038,7 +1037,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { protected function getEntries(array $query): ResponseInterface { try { - return new Response($this->listEntries($query, new Context)); + return HTTP::respJson($this->listEntries($query, new Context)); } catch (ExceptionInput $e) { return new ErrorResponse("MissingCategory", 400); } @@ -1047,7 +1046,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { protected function getFeedEntries(array $path, array $query): ResponseInterface { $c = (new Context)->subscription((int) $path[1]); try { - return new Response($this->listEntries($query, $c)); + return HTTP::respJson($this->listEntries($query, $c)); } catch (ExceptionInput $e) { // FIXME: this should differentiate between a missing feed and a missing category, but doesn't return new ErrorResponse("404", 404); @@ -1057,7 +1056,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { protected function getCategoryEntries(array $path, array $query): ResponseInterface { $query['category_id'] = (int) $path[1]; try { - return new Response($this->listEntries($query, new Context)); + return HTTP::respJson($this->listEntries($query, new Context)); } catch (ExceptionInput $e) { return new ErrorResponse("404", 404); } @@ -1065,7 +1064,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { protected function getEntry(array $path): ResponseInterface { try { - return new Response($this->findEntry((int) $path[1])); + return HTTP::respJson($this->findEntry((int) $path[1])); } catch (ExceptionInput $e) { return new ErrorResponse("404", 404); } @@ -1074,7 +1073,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { protected function getFeedEntry(array $path): ResponseInterface { $c = (new Context)->subscription((int) $path[1]); try { - return new Response($this->findEntry((int) $path[3], $c)); + return HTTP::respJson($this->findEntry((int) $path[3], $c)); } catch (ExceptionInput $e) { return new ErrorResponse("404", 404); } @@ -1088,7 +1087,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { $c->folder((int) $path[1] - 1); } try { - return new Response($this->findEntry((int) $path[3], $c)); + return HTTP::respJson($this->findEntry((int) $path[3], $c)); } catch (ExceptionInput $e) { return new ErrorResponse("404", 404); } @@ -1200,7 +1199,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { } catch (FeedException $e) { return new ErrorResponse(["FailedImportFeed", 'url' => $e->getParams()['url'], 'code' => $e->getCode()], 502); } - return new Response(['message' => Arsse::$lang->msg("API.Miniflux.ImportSuccess")]); + return HTTP::respJson(['message' => Arsse::$lang->msg("API.Miniflux.ImportSuccess")]); } protected function opmlExport(): ResponseInterface { diff --git a/lib/REST/NextcloudNews/V1_2.php b/lib/REST/NextcloudNews/V1_2.php index 205fa73..f11eee9 100644 --- a/lib/REST/NextcloudNews/V1_2.php +++ b/lib/REST/NextcloudNews/V1_2.php @@ -17,7 +17,6 @@ use JKingWeb\Arsse\Misc\HTTP; use JKingWeb\Arsse\REST\Exception; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; -use Laminas\Diactoros\Response\JsonResponse as Response; class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { public const VERSION = "11.0.5"; @@ -283,7 +282,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { foreach (Arsse::$db->folderList(Arsse::$user->id, null, false) as $folder) { $folders[] = $this->folderTranslate($folder); } - return new Response(['folders' => $folders]); + return HTTP::respJson(['folders' => $folders]); } // create a folder @@ -302,7 +301,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { } } $folder = $this->folderTranslate(Arsse::$db->folderPropertiesGet(Arsse::$user->id, $folder)); - return new Response(['folders' => [$folder]]); + return HTTP::respJson(['folders' => [$folder]]); } // delete a folder @@ -369,7 +368,7 @@ 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' => (int) $feed, 'userId' => ""]; } - return new Response(['feeds' => $out]); + return HTTP::respJson(['feeds' => $out]); } // refresh a feed @@ -421,7 +420,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { if ($newest) { $out['newestItemId'] = $newest; } - return new Response($out); + return HTTP::respJson($out); } // return list of feeds for the logged-in user @@ -437,7 +436,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { if ($newest) { $out['newestItemId'] = $newest; } - return new Response($out); + return HTTP::respJson($out); } // delete a feed @@ -585,7 +584,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { $out[] = $this->articleTranslate($item); } $out = ['items' => $out]; - return new Response($out); + return HTTP::respJson($out); } // mark all articles as read @@ -663,7 +662,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { } protected function userStatus(array $url, array $data): ResponseInterface { - return new Response([ + return HTTP::respJson([ 'userId' => (string) Arsse::$user->id, 'displayName' => (string) Arsse::$user->id, 'lastLoginTimestamp' => time(), @@ -689,14 +688,14 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { // return the server version protected function serverVersion(array $url, array $data): ResponseInterface { - return new Response([ + return HTTP::respJson([ 'version' => self::VERSION, 'arsse_version' => Arsse::VERSION, ]); } protected function serverStatus(array $url, array $data): ResponseInterface { - return new Response([ + return HTTP::respJson([ 'version' => self::VERSION, 'arsse_version' => Arsse::VERSION, 'warnings' => [ diff --git a/lib/REST/NextcloudNews/Versions.php b/lib/REST/NextcloudNews/Versions.php index 503fef1..0a3a6f6 100644 --- a/lib/REST/NextcloudNews/Versions.php +++ b/lib/REST/NextcloudNews/Versions.php @@ -9,7 +9,6 @@ namespace JKingWeb\Arsse\REST\NextcloudNews; use JKingWeb\Arsse\Misc\HTTP; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; -use Laminas\Diactoros\Response\JsonResponse as Response; class Versions implements \JKingWeb\Arsse\REST\Handler { public function __construct() { @@ -31,7 +30,7 @@ class Versions implements \JKingWeb\Arsse\REST\Handler { 'v1-2', ], ]; - return new Response($out); + return HTTP::respJson($out); default: // if any other method was used, this is an error return HTTP::respEmpty(405, ['Allow' => "HEAD,GET"]); diff --git a/lib/REST/TinyTinyRSS/API.php b/lib/REST/TinyTinyRSS/API.php index d8fb5ca..0ec6aaf 100644 --- a/lib/REST/TinyTinyRSS/API.php +++ b/lib/REST/TinyTinyRSS/API.php @@ -21,7 +21,6 @@ use JKingWeb\Arsse\Db\ResultEmpty; use JKingWeb\Arsse\Feed\Exception as FeedException; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; -use Laminas\Diactoros\Response\JsonResponse as Response; class API extends \JKingWeb\Arsse\REST\AbstractHandler { public const LEVEL = 15; // emulated API level @@ -110,7 +109,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { // only JSON entities are allowed, but Content-Type is ignored, as is request method $data = @json_decode($data, true); if (json_last_error() !== \JSON_ERROR_NONE || !is_array($data)) { - return new Response(self::FATAL_ERR); + return HTTP::respJson(self::FATAL_ERR); } try { // normalize input @@ -136,13 +135,13 @@ 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([ + return HTTP::respJson([ 'seq' => $data['seq'], 'status' => 0, 'content' => $this->$method($data), ]); } catch (Exception $e) { - return new Response([ + return HTTP::respJson([ 'seq' => $data['seq'], 'status' => 1, 'content' => $e->getData(), @@ -152,7 +151,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { } } else { // absence of a request body indicates an error - return new Response(self::FATAL_ERR); + return HTTP::respJson(self::FATAL_ERR); } } diff --git a/tests/cases/REST/Fever/TestAPI.php b/tests/cases/REST/Fever/TestAPI.php index 59d1858..2f624da 100644 --- a/tests/cases/REST/Fever/TestAPI.php +++ b/tests/cases/REST/Fever/TestAPI.php @@ -16,7 +16,6 @@ use JKingWeb\Arsse\Db\ExceptionInput; use JKingWeb\Arsse\Db\Transaction; use JKingWeb\Arsse\REST\Fever\API; use Psr\Http\Message\ResponseInterface; -use Laminas\Diactoros\Response\JsonResponse; use Laminas\Diactoros\Response\XmlResponse; /** @covers \JKingWeb\Arsse\REST\Fever\API */ @@ -192,8 +191,8 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest { } public function provideTokenAuthenticationRequests(): iterable { - $success = new JsonResponse(['auth' => 1]); - $failure = new JsonResponse(['auth' => 0]); + $success = HTTP::respJson(['auth' => 1]); + $failure = HTTP::respJson(['auth' => 0]); $denied = HTTP::respEmpty(401); return [ [false, true, null, [], ['api' => null], $failure], @@ -255,7 +254,7 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest { ['id' => 2, 'name' => "Interesting", 'subscription' => 1], ['id' => 2, 'name' => "Interesting", 'subscription' => 3], ])); - $exp = new JsonResponse([ + $exp = HTTP::respJson([ 'groups' => [ ['id' => 1, 'title' => "Fascinating"], ['id' => 2, 'title' => "Interesting"], @@ -281,7 +280,7 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest { ['id' => 2, 'name' => "Interesting", 'subscription' => 1], ['id' => 2, 'name' => "Interesting", 'subscription' => 3], ])); - $exp = new JsonResponse([ + $exp = HTTP::respJson([ 'feeds' => [ ['id' => 1, 'favicon_id' => 42, 'title' => "Ankh-Morpork News", 'url' => "http://example.com/feed", 'site_url' => "http://example.com/", 'is_spark' => 0, 'last_updated_on_time' => strtotime("2019-01-01T21:12:00Z")], ['id' => 2, 'favicon_id' => 0, 'title' => "Ook, Ook Eek Ook!", 'url' => "http://example.net/feed", 'site_url' => "http://example.net/", 'is_spark' => 0, 'last_updated_on_time' => strtotime("1988-06-24T12:21:00Z")], @@ -301,7 +300,7 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest { $order = [$desc ? "id desc" : "id"]; $this->dbMock->articleList->returns(new Result($this->articles['db'])); $this->dbMock->articleCount->with($this->userId, (new Context)->hidden(false))->returns(1024); - $exp = new JsonResponse([ + $exp = HTTP::respJson([ 'items' => $this->articles['rest'], 'total_items' => 1024, ]); @@ -330,15 +329,15 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest { $unread = [['id' => 4],['id' => 5],['id' => 6]]; $this->dbMock->articleList->with($this->userId, (new Context)->starred(true)->hidden(false))->returns(new Result($saved)); $this->dbMock->articleList->with($this->userId, (new Context)->unread(true)->hidden(false))->returns(new Result($unread)); - $exp = new JsonResponse(['saved_item_ids' => "1,2,3"]); + $exp = HTTP::respJson(['saved_item_ids' => "1,2,3"]); $this->assertMessage($exp, $this->req("api&saved_item_ids")); - $exp = new JsonResponse(['unread_item_ids' => "4,5,6"]); + $exp = HTTP::respJson(['unread_item_ids' => "4,5,6"]); $this->assertMessage($exp, $this->req("api&unread_item_ids")); } public function testListHotLinks(): void { // hot links are not actually implemented, so an empty array should be all we get - $exp = new JsonResponse(['links' => []]); + $exp = HTTP::respJson(['links' => []]); $this->assertMessage($exp, $this->req("api&links")); } @@ -350,7 +349,7 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest { $this->dbMock->articleList->with($this->userId, (new Context)->unread(true)->hidden(false))->returns(new Result($unread)); $this->dbMock->articleMark->returns(0); $this->dbMock->articleMark->with($this->userId, $this->anything(), (new Context)->article(2112))->throws(new \JKingWeb\Arsse\Db\ExceptionInput("subjectMissing")); - $exp = new JsonResponse($out); + $exp = HTTP::respJson($out); $this->assertMessage($exp, $this->req("api", $post)); if ($c && $data) { $this->dbMock->articleMark->calledWith($this->userId, $data, $this->equalTo($c)); @@ -367,7 +366,7 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest { $this->dbMock->articleList->with($this->userId, (new Context)->unread(true)->hidden(false))->returns(new Result($unread)); $this->dbMock->articleMark->returns(0); $this->dbMock->articleMark->with($this->userId, $this->anything(), (new Context)->article(2112))->throws(new \JKingWeb\Arsse\Db\ExceptionInput("subjectMissing")); - $exp = new JsonResponse($out); + $exp = HTTP::respJson($out); $this->assertMessage($exp, $this->req("api&$get")); if ($c && $data) { $this->dbMock->articleMark->calledWith($this->userId, $data, $this->equalTo($c)); @@ -423,9 +422,9 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest { return [ 'Not an API request' => ["", "", "POST", null, HTTP::respEmpty(404)], 'Wrong method' => ["api", "", "PUT", null, HTTP::respEmpty(405, ['Allow' => "OPTIONS,POST"])], - 'Non-standard method' => ["api", "", "GET", null, new JsonResponse([])], - 'Wrong content type' => ["api", '{"api_key":"validToken"}', "POST", "application/json", new JsonResponse([])], // some clients send nonsensical content types; Fever seems to have allowed this - 'Non-standard content type' => ["api", '{"api_key":"validToken"}', "POST", "multipart/form-data; boundary=33b68964f0de4c1f-5144aa6caaa6e4a8-18bfaf416a1786c8-5c5053a45f221bc1", new JsonResponse([])], // some clients send nonsensical content types; Fever seems to have allowed this + 'Non-standard method' => ["api", "", "GET", null, HTTP::respJson([])], + 'Wrong content type' => ["api", '{"api_key":"validToken"}', "POST", "application/json", HTTP::respJson([])], // some clients send nonsensical content types; Fever seems to have allowed this + 'Non-standard content type' => ["api", '{"api_key":"validToken"}', "POST", "multipart/form-data; boundary=33b68964f0de4c1f-5144aa6caaa6e4a8-18bfaf416a1786c8-5c5053a45f221bc1", HTTP::respJson([])], // some clients send nonsensical content types; Fever seems to have allowed this ]; } @@ -433,21 +432,21 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest { $this->hMock->baseResponse->forwards(); $this->hMock->logIn->returns(true); $this->dbMock->subscriptionRefreshed->with($this->userId)->returns(new \DateTimeImmutable("2000-01-01T00:00:00Z")); - $exp = new JsonResponse([ + $exp = HTTP::respJson([ 'api_version' => API::LEVEL, 'auth' => 1, 'last_refreshed_on_time' => 946684800, ]); $this->assertMessage($exp, $this->req("api")); $this->dbMock->subscriptionRefreshed->with($this->userId)->returns(null); // no subscriptions - $exp = new JsonResponse([ + $exp = HTTP::respJson([ 'api_version' => API::LEVEL, 'auth' => 1, 'last_refreshed_on_time' => null, ]); $this->assertMessage($exp, $this->req("api")); $this->hMock->logIn->returns(false); - $exp = new JsonResponse([ + $exp = HTTP::respJson([ 'api_version' => API::LEVEL, 'auth' => 0, ]); @@ -460,7 +459,7 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest { $this->dbMock->articleList->with($this->userId, $this->equalTo((new Context)->limit(1)->hidden(false)), ["marked_date"], ["marked_date desc"])->returns(new Result([['marked_date' => "2000-01-01 00:00:00"]])); $this->dbMock->articleList->with($this->userId, $this->equalTo((new Context)->unread(true)->hidden(false)))->returns(new Result($unread)); $this->dbMock->articleMark->returns(0); - $exp = new JsonResponse($out); + $exp = HTTP::respJson($out); $this->assertMessage($exp, $this->req("api", ['unread_recently_read' => 1])); $this->dbMock->articleMark->calledWith($this->userId, ['read' => false], $this->equalTo((new Context)->unread(false)->markedRange("1999-12-31T23:59:45Z", null)->hidden(false))); $this->dbMock->articleList->with($this->userId, (new Context)->limit(1)->hidden(false), ["marked_date"], ["marked_date desc"])->returns(new Result([])); @@ -485,7 +484,7 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest { ['id' => 44, 'type' => null, 'data' => "IMAGE DATA"], ['id' => 47, 'type' => null, 'data' => null], ]))); - $exp = new JsonResponse(['favicons' => [ + $exp = HTTP::respJson(['favicons' => [ ['id' => 0, 'data' => $iconType.",".$iconData], ['id' => 42, 'data' => "image/svg+xml;base64,PHN2Zy8+"], ['id' => 44, 'data' => "application/octet-stream;base64,SU1BR0UgREFUQQ=="], diff --git a/tests/cases/REST/Miniflux/TestV1.php b/tests/cases/REST/Miniflux/TestV1.php index 8961047..b1dec74 100644 --- a/tests/cases/REST/Miniflux/TestV1.php +++ b/tests/cases/REST/Miniflux/TestV1.php @@ -26,7 +26,6 @@ use JKingWeb\Arsse\User\ExceptionConflict; use JKingWeb\Arsse\User\ExceptionInput as UserExceptionInput; use JKingWeb\Arsse\Test\Result; use Psr\Http\Message\ResponseInterface; -use Laminas\Diactoros\Response\JsonResponse as Response; use Laminas\Diactoros\Response\TextResponse; /** @covers \JKingWeb\Arsse\REST\Miniflux\V1 */ @@ -182,8 +181,8 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { ['title' => "Feed", 'type' => "rss", 'url' => "http://localhost:8000/Feed/Discovery/Missing"], ]; return [ - ["http://localhost:8000/Feed/Discovery/Valid", new Response($discovered)], - ["http://localhost:8000/Feed/Discovery/Invalid", new Response([])], + ["http://localhost:8000/Feed/Discovery/Valid", HTTP::respJson($discovered)], + ["http://localhost:8000/Feed/Discovery/Invalid", HTTP::respJson([])], ["http://localhost:8000/Feed/Discovery/Missing", new ErrorResponse("Fetch404", 502)], [1, new ErrorResponse(["InvalidInputType", 'field' => "url", 'expected' => "string", 'actual' => "integer"], 422)], ["Not a URL", new ErrorResponse(["InvalidInputValue", 'field' => "url"], 422)], @@ -226,16 +225,16 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { public function provideUserQueries(): iterable { self::clearData(); return [ - [true, "/users", new Response(self::USERS)], - [true, "/me", new Response(self::USERS[0])], - [true, "/users/john.doe@example.com", new Response(self::USERS[0])], - [true, "/users/1", new Response(self::USERS[0])], - [true, "/users/jane.doe@example.com", new Response(self::USERS[1])], - [true, "/users/2", new Response(self::USERS[1])], + [true, "/users", HTTP::respJson(self::USERS)], + [true, "/me", HTTP::respJson(self::USERS[0])], + [true, "/users/john.doe@example.com", HTTP::respJson(self::USERS[0])], + [true, "/users/1", HTTP::respJson(self::USERS[0])], + [true, "/users/jane.doe@example.com", HTTP::respJson(self::USERS[1])], + [true, "/users/2", HTTP::respJson(self::USERS[1])], [true, "/users/jack.doe@example.com", new ErrorResponse("404", 404)], [true, "/users/47", new ErrorResponse("404", 404)], [false, "/users", new ErrorResponse("403", 403)], - [false, "/me", new Response(self::USERS[1])], + [false, "/me", HTTP::respJson(self::USERS[1])], [false, "/users/john.doe@example.com", new ErrorResponse("403", 403)], [false, "/users/1", new ErrorResponse("403", 403)], [false, "/users/jane.doe@example.com", new ErrorResponse("403", 403)], @@ -310,16 +309,16 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { [false, "/users/1", ['entry_sorting_direction' => "bad"], null, null, null, null, null, null, new ErrorResponse(["InvalidInputValue", 'field' => "entry_sorting_direction"], 422)], [false, "/users/1", ['theme' => "stark"], null, null, null, null, null, null, new ErrorResponse("403", 403)], [false, "/users/2", ['is_admin' => true], null, null, null, null, null, null, new ErrorResponse("InvalidElevation", 403)], - [false, "/users/2", ['language' => "fr_CA"], null, null, null, null, ['lang' => "fr_CA"], $out1, new Response($resp1, 201)], - [false, "/users/2", ['entry_sorting_direction' => "asc"], null, null, null, null, ['sort_asc' => true], $out1, new Response($resp1, 201)], - [false, "/users/2", ['entry_sorting_direction' => "desc"], null, null, null, null, ['sort_asc' => false], $out1, new Response($resp1, 201)], + [false, "/users/2", ['language' => "fr_CA"], null, null, null, null, ['lang' => "fr_CA"], $out1, HTTP::respJson($resp1, 201)], + [false, "/users/2", ['entry_sorting_direction' => "asc"], null, null, null, null, ['sort_asc' => true], $out1, HTTP::respJson($resp1, 201)], + [false, "/users/2", ['entry_sorting_direction' => "desc"], null, null, null, null, ['sort_asc' => false], $out1, HTTP::respJson($resp1, 201)], [false, "/users/2", ['entries_per_page' => -1], null, null, null, null, ['page_size' => -1], new UserExceptionInput("invalidNonZeroInteger"), new ErrorResponse(["InvalidInputValue", 'field' => "entries_per_page"], 422)], [false, "/users/2", ['timezone' => "Ook"], null, null, null, null, ['tz' => "Ook"], new UserExceptionInput("invalidTimezone"), new ErrorResponse(["InvalidInputValue", 'field' => "timezone"], 422)], [false, "/users/2", ['username' => "j:k"], "j:k", new UserExceptionInput("invalidUsername"), null, null, null, null, new ErrorResponse(["InvalidInputValue", 'field' => "username"], 422)], [false, "/users/2", ['username' => "ook"], "ook", new ExceptionConflict("alreadyExists"), null, null, null, null, new ErrorResponse(["DuplicateUser", 'user' => "ook"], 409)], - [false, "/users/2", ['password' => "ook"], null, null, "ook", "ook", null, null, new Response(array_merge($resp1, ['password' => "ook"]), 201)], - [false, "/users/2", ['username' => "ook", 'password' => "ook"], "ook", true, "ook", "ook", null, null, new Response(array_merge($resp1, ['username' => "ook", 'password' => "ook"]), 201)], - [true, "/users/1", ['theme' => "stark"], null, null, null, null, ['theme' => "stark"], $out2, new Response($resp2, 201)], + [false, "/users/2", ['password' => "ook"], null, null, "ook", "ook", null, null, HTTP::respJson(array_merge($resp1, ['password' => "ook"]), 201)], + [false, "/users/2", ['username' => "ook", 'password' => "ook"], "ook", true, "ook", "ook", null, null, HTTP::respJson(array_merge($resp1, ['username' => "ook", 'password' => "ook"]), 201)], + [true, "/users/1", ['theme' => "stark"], null, null, null, null, ['theme' => "stark"], $out2, HTTP::respJson($resp2, 201)], [true, "/users/3", ['theme' => "stark"], null, null, null, null, null, null, new ErrorResponse("404", 404)], ]; } @@ -367,7 +366,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { [['username' => "j:k", 'password' => "eek"], ["j:k", "eek"], new UserExceptionInput("invalidUsername"), null, null, new ErrorResponse(["InvalidInputValue", 'field' => "username"], 422)], [['username' => "ook", 'password' => "eek", 'timezone' => "ook"], ["ook", "eek"], "eek", ['tz' => "ook"], new UserExceptionInput("invalidTimezone"), new ErrorResponse(["InvalidInputValue", 'field' => "timezone"], 422)], [['username' => "ook", 'password' => "eek", 'entries_per_page' => -1], ["ook", "eek"], "eek", ['page_size' => -1], new UserExceptionInput("invalidNonZeroInteger"), new ErrorResponse(["InvalidInputValue", 'field' => "entries_per_page"], 422)], - [['username' => "ook", 'password' => "eek", 'theme' => "default"], ["ook", "eek"], "eek", ['theme' => "default"], ['theme' => "default"], new Response($resp1, 201)], + [['username' => "ook", 'password' => "eek", 'theme' => "default"], ["ook", "eek"], "eek", ['theme' => "default"], ['theme' => "default"], HTTP::respJson($resp1, 201)], ]; } @@ -406,7 +405,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { ['id' => 1, 'name' => "Science"], ['id' => 20, 'name' => "Technology"], ]))); - $exp = new Response([ + $exp = HTTP::respJson([ ['id' => 1, 'title' => "All", 'user_id' => 42], ['id' => 2, 'title' => "Science", 'user_id' => 42], ['id' => 21, 'title' => "Technology", 'user_id' => 42], @@ -416,7 +415,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { // run test again with a renamed root folder Arsse::$user = $this->createMock(User::class); Arsse::$user->method("propertiesGet")->willReturn(['num' => 47, 'admin' => false, 'root_folder_name' => "Uncategorized"]); - $exp = new Response([ + $exp = HTTP::respJson([ ['id' => 1, 'title' => "Uncategorized", 'user_id' => 47], ['id' => 2, 'title' => "Science", 'user_id' => 47], ['id' => 21, 'title' => "Technology", 'user_id' => 47], @@ -440,7 +439,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { public function provideCategoryAdditions(): iterable { return [ - ["New", new Response(['id' => 2112, 'title' => "New", 'user_id' => 42], 201)], + ["New", HTTP::respJson(['id' => 2112, 'title' => "New", 'user_id' => 42], 201)], ["Duplicate", new ErrorResponse(["DuplicateCategory", 'title' => "Duplicate"], 409)], ["", new ErrorResponse(["InvalidCategory", 'title' => ""], 422)], [" ", new ErrorResponse(["InvalidCategory", 'title' => " "], 422)], @@ -467,14 +466,14 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { public function provideCategoryUpdates(): iterable { return [ [3, "New", "subjectMissing", new ErrorResponse("404", 404)], - [2, "New", true, new Response(['id' => 2, 'title' => "New", 'user_id' => 42], 201)], + [2, "New", true, HTTP::respJson(['id' => 2, 'title' => "New", 'user_id' => 42], 201)], [2, "Duplicate", "constraintViolation", new ErrorResponse(["DuplicateCategory", 'title' => "Duplicate"], 409)], [2, "", "missing", new ErrorResponse(["InvalidCategory", 'title' => ""], 422)], [2, " ", "whitespace", new ErrorResponse(["InvalidCategory", 'title' => " "], 422)], [2, null, "missing", new ErrorResponse(["MissingInputValue", 'field' => "title"], 422)], [2, false, "subjectMissing", new ErrorResponse(["InvalidInputType", 'field' => "title", 'actual' => "boolean", 'expected' => "string"], 422)], - [1, "New", true, new Response(['id' => 1, 'title' => "New", 'user_id' => 42], 201)], - [1, "Duplicate", "constraintViolation", new Response(['id' => 1, 'title' => "Duplicate", 'user_id' => 42], 201)], // This is allowed because the name of the root folder is only a duplicate in circumstances where it is used + [1, "New", true, HTTP::respJson(['id' => 1, 'title' => "New", 'user_id' => 42], 201)], + [1, "Duplicate", "constraintViolation", HTTP::respJson(['id' => 1, 'title' => "Duplicate", 'user_id' => 42], 201)], // This is allowed because the name of the root folder is only a duplicate in circumstances where it is used [1, "", "missing", new ErrorResponse(["InvalidCategory", 'title' => ""], 422)], [1, " ", "whitespace", new ErrorResponse(["InvalidCategory", 'title' => " "], 422)], [1, null, "missing", new ErrorResponse(["MissingInputValue", 'field' => "title"], 422)], @@ -510,20 +509,20 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { public function testListFeeds(): void { $this->dbMock->subscriptionList->returns(new Result($this->v(self::FEEDS))); - $exp = new Response(self::FEEDS_OUT); + $exp = HTTP::respJson(self::FEEDS_OUT); $this->assertMessage($exp, $this->req("GET", "/feeds")); } public function testListFeedsOfACategory(): void { $this->dbMock->subscriptionList->returns(new Result($this->v(self::FEEDS))); - $exp = new Response(self::FEEDS_OUT); + $exp = HTTP::respJson(self::FEEDS_OUT); $this->assertMessage($exp, $this->req("GET", "/categories/2112/feeds")); $this->dbMock->subscriptionList->calledWith(Arsse::$user->id, 2111, true); } public function testListFeedsOfTheRootCategory(): void { $this->dbMock->subscriptionList->returns(new Result($this->v(self::FEEDS))); - $exp = new Response(self::FEEDS_OUT); + $exp = HTTP::respJson(self::FEEDS_OUT); $this->assertMessage($exp, $this->req("GET", "/categories/1/feeds")); $this->dbMock->subscriptionList->calledWith(Arsse::$user->id, 0, false); } @@ -537,9 +536,9 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { public function testGetAFeed(): void { $this->dbMock->subscriptionPropertiesGet->returns($this->v(self::FEEDS[0]))->returns($this->v(self::FEEDS[1])); - $this->assertMessage(new Response(self::FEEDS_OUT[0]), $this->req("GET", "/feeds/1")); + $this->assertMessage(HTTP::respJson(self::FEEDS_OUT[0]), $this->req("GET", "/feeds/1")); $this->dbMock->subscriptionPropertiesGet->calledWith(Arsse::$user->id, 1); - $this->assertMessage(new Response(self::FEEDS_OUT[1]), $this->req("GET", "/feeds/55")); + $this->assertMessage(HTTP::respJson(self::FEEDS_OUT[1]), $this->req("GET", "/feeds/55")); $this->dbMock->subscriptionPropertiesGet->calledWith(Arsse::$user->id, 55); } @@ -634,16 +633,16 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { [['feed_url' => "http://example.com/", 'category_id' => 1], new FeedException("unsupportedFeedFormat"), null, null, null, new ErrorResponse("FetchFormat", 502)], [['feed_url' => "http://example.com/", 'category_id' => 1], 2112, new ExceptionInput("constraintViolation"), null, null, new ErrorResponse("DuplicateFeed", 409)], [['feed_url' => "http://example.com/", 'category_id' => 1], 2112, 44, new ExceptionInput("idMissing"), null, new ErrorResponse("MissingCategory", 422)], - [['feed_url' => "http://example.com/", 'category_id' => 1], 2112, 44, true, null, new Response(['feed_id' => 44], 201)], - [['feed_url' => "http://example.com/", 'category_id' => 1, 'keeplist_rules' => "^A"], 2112, 44, true, true, new Response(['feed_id' => 44], 201)], - [['feed_url' => "http://example.com/", 'category_id' => 1, 'blocklist_rules' => "A"], 2112, 44, true, true, new Response(['feed_id' => 44], 201)], + [['feed_url' => "http://example.com/", 'category_id' => 1], 2112, 44, true, null, HTTP::respJson(['feed_id' => 44], 201)], + [['feed_url' => "http://example.com/", 'category_id' => 1, 'keeplist_rules' => "^A"], 2112, 44, true, true, HTTP::respJson(['feed_id' => 44], 201)], + [['feed_url' => "http://example.com/", 'category_id' => 1, 'blocklist_rules' => "A"], 2112, 44, true, true, HTTP::respJson(['feed_id' => 44], 201)], ]; } /** @dataProvider provideFeedModifications */ public function testModifyAFeed(array $in, array $data, $out, ResponseInterface $exp): void { $this->h = $this->partialMock(V1::class); - $this->h->getFeed->returns(new Response(self::FEEDS_OUT[0])); + $this->h->getFeed->returns(HTTP::respJson(self::FEEDS_OUT[0])); if ($out instanceof \Exception) { $this->dbMock->subscriptionPropertiesSet->throws($out); } else { @@ -655,7 +654,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { public function provideFeedModifications(): iterable { self::clearData(); - $success = new Response(self::FEEDS_OUT[0], 201); + $success = HTTP::respJson(self::FEEDS_OUT[0], 201); return [ [[], [], true, $success], [[], [], new ExceptionInput("subjectMissing"), new ErrorResponse("404", 404)], @@ -672,9 +671,9 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { public function testModifyAFeedWithNoBody(): void { $this->h = $this->partialMock(V1::class); - $this->h->getFeed->returns(new Response(self::FEEDS_OUT[0])); + $this->h->getFeed->returns(HTTP::respJson(self::FEEDS_OUT[0])); $this->dbMock->subscriptionPropertiesSet->returns(true); - $this->assertMessage(new Response(self::FEEDS_OUT[0], 201), $this->req("PUT", "/feeds/2112", "")); + $this->assertMessage(HTTP::respJson(self::FEEDS_OUT[0], 201), $this->req("PUT", "/feeds/2112", "")); $this->dbMock->subscriptionPropertiesSet->calledWith(Arsse::$user->id, 2112, []); } @@ -703,7 +702,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { public function provideIcons(): iterable { return [ - [['id' => 44, 'type' => "image/svg+xml", 'data' => ""], new Response(['id' => 44, 'data' => "image/svg+xml;base64,PHN2Zy8+", 'mime_type' => "image/svg+xml"])], + [['id' => 44, 'type' => "image/svg+xml", 'data' => ""], HTTP::respJson(['id' => 44, 'data' => "image/svg+xml;base64,PHN2Zy8+", 'mime_type' => "image/svg+xml"])], [['id' => 47, 'type' => "", 'data' => ""], new ErrorResponse("404", 404)], [['id' => 47, 'type' => null, 'data' => ""], new ErrorResponse("404", 404)], [['id' => 47, 'type' => null, 'data' => null], new ErrorResponse("404", 404)], @@ -755,50 +754,50 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { ["/entries?order=false", null, null, [], false, new ErrorResponse(["InvalidInputValue", 'field' => "order"], 400)], ["/entries?starred&starred", null, null, [], false, new ErrorResponse(["DuplicateInputValue", 'field' => "starred"], 400)], ["/entries?after&after=0", null, null, [], false, new ErrorResponse(["DuplicateInputValue", 'field' => "after"], 400)], - ["/entries", $c, $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?category_id=47", (clone $c)->folder(46), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?category_id=1", (clone $c)->folderShallow(0), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?status=unread", (clone $c)->unread(true)->hidden(false), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?status=read", (clone $c)->unread(false)->hidden(false), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?status=removed", (clone $c)->hidden(true), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?status=unread&status=read", (clone $c)->hidden(false), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?status=unread&status=removed", new UnionContext((clone $c)->unread(true), (clone $c)->hidden(true)), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?status=removed&status=read", new UnionContext((clone $c)->unread(false), (clone $c)->hidden(true)), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?status=removed&status=read&status=removed", new UnionContext((clone $c)->unread(false), (clone $c)->hidden(true)), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?status=removed&status=read&status=unread", $c, $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?starred", (clone $c)->starred(true), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?starred=", (clone $c)->starred(true), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?starred=true", (clone $c)->starred(true), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?starred=false", (clone $c)->starred(true), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?after=0", (clone $c)->modifiedRange(0, null), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?before=0", $c, $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?before=1", (clone $c)->modifiedRange(null, 1), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?before=1&after=0", (clone $c)->modifiedRange(0, 1), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?after_entry_id=42", (clone $c)->articleRange(43, null), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?before_entry_id=47", (clone $c)->articleRange(null, 46), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?search=alpha%20beta", (clone $c)->searchTerms(["alpha", "beta"]), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?limit=4", (clone $c)->limit(4), $o, self::ENTRIES, true, new Response(['total' => 2112, 'entries' => self::ENTRIES_OUT])], - ["/entries?offset=20", (clone $c)->offset(20), $o, [], true, new Response(['total' => 2112, 'entries' => []])], - ["/entries?direction=asc", $c, $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?order=id", $c, ["id"], self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?order=published_at", $c, ["modified_date"], self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?order=category_id", $c, ["top_folder"], self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?order=category_title", $c, ["top_folder_name"], self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?order=status", $c, ["hidden", "unread desc"], self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?direction=desc", $c, ["modified_date desc"], self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?order=id&direction=desc", $c, ["id desc"], self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?order=published_at&direction=desc", $c, ["modified_date desc"], self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?order=category_id&direction=desc", $c, ["top_folder desc"], self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?order=category_title&direction=desc", $c, ["top_folder_name desc"], self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/entries?order=status&direction=desc", $c, ["hidden desc", "unread"], self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries", $c, $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?category_id=47", (clone $c)->folder(46), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?category_id=1", (clone $c)->folderShallow(0), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?status=unread", (clone $c)->unread(true)->hidden(false), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?status=read", (clone $c)->unread(false)->hidden(false), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?status=removed", (clone $c)->hidden(true), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?status=unread&status=read", (clone $c)->hidden(false), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?status=unread&status=removed", new UnionContext((clone $c)->unread(true), (clone $c)->hidden(true)), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?status=removed&status=read", new UnionContext((clone $c)->unread(false), (clone $c)->hidden(true)), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?status=removed&status=read&status=removed", new UnionContext((clone $c)->unread(false), (clone $c)->hidden(true)), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?status=removed&status=read&status=unread", $c, $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?starred", (clone $c)->starred(true), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?starred=", (clone $c)->starred(true), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?starred=true", (clone $c)->starred(true), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?starred=false", (clone $c)->starred(true), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?after=0", (clone $c)->modifiedRange(0, null), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?before=0", $c, $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?before=1", (clone $c)->modifiedRange(null, 1), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?before=1&after=0", (clone $c)->modifiedRange(0, 1), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?after_entry_id=42", (clone $c)->articleRange(43, null), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?before_entry_id=47", (clone $c)->articleRange(null, 46), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?search=alpha%20beta", (clone $c)->searchTerms(["alpha", "beta"]), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?limit=4", (clone $c)->limit(4), $o, self::ENTRIES, true, HTTP::respJson(['total' => 2112, 'entries' => self::ENTRIES_OUT])], + ["/entries?offset=20", (clone $c)->offset(20), $o, [], true, HTTP::respJson(['total' => 2112, 'entries' => []])], + ["/entries?direction=asc", $c, $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?order=id", $c, ["id"], self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?order=published_at", $c, ["modified_date"], self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?order=category_id", $c, ["top_folder"], self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?order=category_title", $c, ["top_folder_name"], self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?order=status", $c, ["hidden", "unread desc"], self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?direction=desc", $c, ["modified_date desc"], self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?order=id&direction=desc", $c, ["id desc"], self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?order=published_at&direction=desc", $c, ["modified_date desc"], self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?order=category_id&direction=desc", $c, ["top_folder desc"], self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?order=category_title&direction=desc", $c, ["top_folder_name desc"], self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/entries?order=status&direction=desc", $c, ["hidden desc", "unread"], self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], ["/entries?category_id=2112", (clone $c)->folder(2111), $o, new ExceptionInput("idMissing"), false, new ErrorResponse("MissingCategory")], - ["/feeds/42/entries", (clone $c)->subscription(42), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/feeds/42/entries?category_id=47", (clone $c)->subscription(42)->folder(46), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/feeds/42/entries", (clone $c)->subscription(42), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/feeds/42/entries?category_id=47", (clone $c)->subscription(42)->folder(46), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], ["/feeds/2112/entries", (clone $c)->subscription(2112), $o, new ExceptionInput("idMissing"), false, new ErrorResponse("404", 404)], - ["/categories/42/entries", (clone $c)->folder(41), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/categories/42/entries?category_id=47", (clone $c)->folder(41), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/categories/42/entries?starred", (clone $c)->folder(41)->starred(true), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], - ["/categories/1/entries", (clone $c)->folderShallow(0), $o, self::ENTRIES, false, new Response(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/categories/42/entries", (clone $c)->folder(41), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/categories/42/entries?category_id=47", (clone $c)->folder(41), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/categories/42/entries?starred", (clone $c)->folder(41)->starred(true), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], + ["/categories/1/entries", (clone $c)->folderShallow(0), $o, self::ENTRIES, false, HTTP::respJson(['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])], ["/categories/2112/entries", (clone $c)->folder(2111), $o, new ExceptionInput("idMissing"), false, new ErrorResponse("404", 404)], ]; } @@ -828,17 +827,17 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { self::clearData(); $c = new Context; return [ - ["/entries/42", (clone $c)->article(42), [self::ENTRIES[1]], new Response(self::ENTRIES_OUT[1])], + ["/entries/42", (clone $c)->article(42), [self::ENTRIES[1]], HTTP::respJson(self::ENTRIES_OUT[1])], ["/entries/2112", (clone $c)->article(2112), new ExceptionInput("subjectMissing"), new ErrorResponse("404", 404)], - ["/feeds/47/entries/42", (clone $c)->subscription(47)->article(42), [self::ENTRIES[1]], new Response(self::ENTRIES_OUT[1])], + ["/feeds/47/entries/42", (clone $c)->subscription(47)->article(42), [self::ENTRIES[1]], HTTP::respJson(self::ENTRIES_OUT[1])], ["/feeds/47/entries/44", (clone $c)->subscription(47)->article(44), [], new ErrorResponse("404", 404)], ["/feeds/47/entries/2112", (clone $c)->subscription(47)->article(2112), new ExceptionInput("subjectMissing"), new ErrorResponse("404", 404)], ["/feeds/2112/entries/47", (clone $c)->subscription(2112)->article(47), new ExceptionInput("idMissing"), new ErrorResponse("404", 404)], - ["/categories/47/entries/42", (clone $c)->folder(46)->article(42), [self::ENTRIES[1]], new Response(self::ENTRIES_OUT[1])], + ["/categories/47/entries/42", (clone $c)->folder(46)->article(42), [self::ENTRIES[1]], HTTP::respJson(self::ENTRIES_OUT[1])], ["/categories/47/entries/44", (clone $c)->folder(46)->article(44), [], new ErrorResponse("404", 404)], ["/categories/47/entries/2112", (clone $c)->folder(46)->article(2112), new ExceptionInput("subjectMissing"), new ErrorResponse("404", 404)], ["/categories/2112/entries/47", (clone $c)->folder(2111)->article(47), new ExceptionInput("idMissing"), new ErrorResponse("404", 404)], - ["/categories/1/entries/42", (clone $c)->folderShallow(0)->article(42), [self::ENTRIES[1]], new Response(self::ENTRIES_OUT[1])], + ["/categories/1/entries/42", (clone $c)->folderShallow(0)->article(42), [self::ENTRIES[1]], HTTP::respJson(self::ENTRIES_OUT[1])], ]; } @@ -970,7 +969,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { [new ImportException("invalidFolderCopy"), new ErrorResponse("DuplicateImportCategory", 422)], [new ImportException("invalidTagName"), new ErrorResponse("InvalidImportLabel", 422)], [new FeedException("invalidUrl", ['url' => "http://example.com/"]), new ErrorResponse(["FailedImportFeed", 'url' => "http://example.com/", 'code' => 10502], 502)], - [true, new Response(['message' => Arsse::$lang->msg("API.Miniflux.ImportSuccess")])], + [true, HTTP::respJson(['message' => Arsse::$lang->msg("API.Miniflux.ImportSuccess")])], ]; } diff --git a/tests/cases/REST/NextcloudNews/TestV1_2.php b/tests/cases/REST/NextcloudNews/TestV1_2.php index 08f1f70..71b5eab 100644 --- a/tests/cases/REST/NextcloudNews/TestV1_2.php +++ b/tests/cases/REST/NextcloudNews/TestV1_2.php @@ -17,7 +17,6 @@ use JKingWeb\Arsse\Db\ExceptionInput; use JKingWeb\Arsse\Db\Transaction; use JKingWeb\Arsse\REST\NextcloudNews\V1_2; use Psr\Http\Message\ResponseInterface; -use Laminas\Diactoros\Response\JsonResponse as Response; /** @covers \JKingWeb\Arsse\REST\NextcloudNews\V1_2 */ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { @@ -408,7 +407,7 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { ['id' => 12, 'name' => "Hardware"], ]; $this->dbMock->folderList->with($this->userId, null, false)->returns(new Result($this->v($list))); - $exp = new Response(['folders' => $out]); + $exp = HTTP::respJson(['folders' => $out]); $this->assertMessage($exp, $this->req("GET", "/folders")); } @@ -432,10 +431,10 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { public function provideFolderCreations(): array { return [ - [['name' => "Software"], true, 1, new Response(['folders' => [['id' => 1, 'name' => "Software"]]])], - [['name' => "Software"], false, 1, new Response(['folders' => [['id' => 1, 'name' => "Software"]]])], - [['name' => "Hardware"], true, "2", new Response(['folders' => [['id' => 2, 'name' => "Hardware"]]])], - [['name' => "Hardware"], false, "2", new Response(['folders' => [['id' => 2, 'name' => "Hardware"]]])], + [['name' => "Software"], true, 1, HTTP::respJson(['folders' => [['id' => 1, 'name' => "Software"]]])], + [['name' => "Software"], false, 1, HTTP::respJson(['folders' => [['id' => 1, 'name' => "Software"]]])], + [['name' => "Hardware"], true, "2", HTTP::respJson(['folders' => [['id' => 2, 'name' => "Hardware"]]])], + [['name' => "Hardware"], false, "2", HTTP::respJson(['folders' => [['id' => 2, 'name' => "Hardware"]]])], [['name' => "Software"], true, new ExceptionInput("constraintViolation"), HTTP::respEmpty(409)], [['name' => ""], true, new ExceptionInput("whitespace"), HTTP::respEmpty(422)], [['name' => " "], true, new ExceptionInput("whitespace"), HTTP::respEmpty(422)], @@ -477,7 +476,7 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { } public function testRetrieveServerVersion(): void { - $exp = new Response([ + $exp = HTTP::respJson([ 'version' => V1_2::VERSION, 'arsse_version' => Arsse::VERSION, ]); @@ -497,9 +496,9 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { $this->dbMock->subscriptionList->with($this->userId)->returns(new Result([]))->returns(new Result($this->v($this->feeds['db']))); $this->dbMock->articleStarred->with($this->userId)->returns($this->v(['total' => 0]))->returns($this->v(['total' => 5])); $this->dbMock->editionLatest->with($this->userId)->returns(0)->returns(4758915); - $exp = new Response($exp1); + $exp = HTTP::respJson($exp1); $this->assertMessage($exp, $this->req("GET", "/feeds")); - $exp = new Response($exp2); + $exp = HTTP::respJson($exp2); $this->assertMessage($exp, $this->req("GET", "/feeds")); } @@ -538,12 +537,12 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { public function provideNewSubscriptions(): array { $feedException = new \JKingWeb\Arsse\Feed\Exception("", [], new \PicoFeed\Reader\SubscriptionNotFoundException); return [ - [['url' => "http://example.com/news.atom", 'folderId' => 3], 2112, 0, $this->feeds['db'][0], new ExceptionInput("idMissing"), new Response(['feeds' => [$this->feeds['rest'][0]]])], - [['url' => "http://example.org/news.atom", 'folderId' => 8], 42, 4758915, $this->feeds['db'][1], true, new Response(['feeds' => [$this->feeds['rest'][1]], 'newestItemId' => 4758915])], + [['url' => "http://example.com/news.atom", 'folderId' => 3], 2112, 0, $this->feeds['db'][0], new ExceptionInput("idMissing"), HTTP::respJson(['feeds' => [$this->feeds['rest'][0]]])], + [['url' => "http://example.org/news.atom", 'folderId' => 8], 42, 4758915, $this->feeds['db'][1], true, HTTP::respJson(['feeds' => [$this->feeds['rest'][1]], 'newestItemId' => 4758915])], [['url' => "http://example.com/news.atom", 'folderId' => 3], new ExceptionInput("constraintViolation"), 0, $this->feeds['db'][0], new ExceptionInput("idMissing"), HTTP::respEmpty(409)], [['url' => "http://example.org/news.atom", 'folderId' => 8], new ExceptionInput("constraintViolation"), 4758915, $this->feeds['db'][1], true, HTTP::respEmpty(409)], [[], $feedException, 0, [], false, HTTP::respEmpty(422)], - [['url' => "http://example.net/news.atom", 'folderId' => -1], 47, 2112, $this->feeds['db'][2], new ExceptionInput("typeViolation"), new Response(['feeds' => [$this->feeds['rest'][2]], 'newestItemId' => 2112])], + [['url' => "http://example.net/news.atom", 'folderId' => -1], 47, 2112, $this->feeds['db'][2], new ExceptionInput("typeViolation"), HTTP::respJson(['feeds' => [$this->feeds['rest'][2]], 'newestItemId' => 2112])], ]; } @@ -627,7 +626,7 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { ], ]; $this->dbMock->feedListStale->returns($this->v(array_column($out, "id"))); - $exp = new Response(['feeds' => $out]); + $exp = HTTP::respJson(['feeds' => $out]); $this->assertMessage($exp, $this->req("GET", "/feeds/all")); } @@ -683,7 +682,7 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { $c = (new Context)->hidden(false); $t = Date::normalize(time()); $out = new Result($this->v($this->articles['db'])); - $r200 = new Response(['items' => $this->articles['rest']]); + $r200 = HTTP::respJson(['items' => $this->articles['rest']]); $r422 = HTTP::respEmpty(422); return [ ["/items", [], clone $c, $out, $r200], @@ -854,7 +853,7 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { ]; $arr2['warnings']['improperlyConfiguredCron'] = true; $arr2['warnings']['incorrectDbCharset'] = true; - $exp = new Response($arr1); + $exp = HTTP::respJson($arr1); $this->assertMessage($exp, $this->req("GET", "/status")); } @@ -888,10 +887,10 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { public function testQueryTheUserStatus(): void { $act = $this->req("GET", "/user"); - $exp = new Response([ + $exp = HTTP::respJson([ 'userId' => $this->userId, 'displayName' => $this->userId, - 'lastLoginTimestamp' => $this->approximateTime($act->getPayload()['lastLoginTimestamp'], new \DateTimeImmutable), + 'lastLoginTimestamp' => $this->approximateTime(json_decode((string) $act->getBody(), true)['lastLoginTimestamp'], new \DateTimeImmutable), 'avatar' => null, ]); $this->assertMessage($exp, $act); @@ -906,7 +905,7 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { $this->dbMock->folderAdd->with($this->anything(), $in)->returns(1); $this->dbMock->folderPropertiesGet->with($this->userId, 1)->returns($this->v($out1)); $this->dbMock->folderPropertiesGet->with($this->userId, 2)->returns($this->v($out2)); - $exp = new Response(['folders' => [$out1]]); + $exp = HTTP::respJson(['folders' => [$out1]]); $this->assertMessage($exp, $this->req("POST", "/folders?name=Hardware", json_encode($in))); } diff --git a/tests/cases/REST/NextcloudNews/TestVersions.php b/tests/cases/REST/NextcloudNews/TestVersions.php index a254772..472b808 100644 --- a/tests/cases/REST/NextcloudNews/TestVersions.php +++ b/tests/cases/REST/NextcloudNews/TestVersions.php @@ -9,7 +9,6 @@ namespace JKingWeb\Arsse\TestCase\REST\NextcloudNews; use JKingWeb\Arsse\Misc\HTTP; use JKingWeb\Arsse\REST\NextcloudNews\Versions; use Psr\Http\Message\ResponseInterface; -use Laminas\Diactoros\Response\JsonResponse as Response; /** @covers \JKingWeb\Arsse\REST\NextcloudNews\Versions */ class TestVersions extends \JKingWeb\Arsse\Test\AbstractTest { @@ -25,7 +24,7 @@ class TestVersions extends \JKingWeb\Arsse\Test\AbstractTest { } public function testFetchVersionList(): void { - $exp = new Response(['apiLevels' => ['v1-2']]); + $exp = HTTP::respJson(['apiLevels' => ['v1-2']]); $this->assertMessage($exp, $this->req("GET", "/")); $this->assertMessage($exp, $this->req("GET", "/")); $this->assertMessage($exp, $this->req("GET", "/")); diff --git a/tests/cases/REST/TinyTinyRSS/TestAPI.php b/tests/cases/REST/TinyTinyRSS/TestAPI.php index d643491..96d50c3 100644 --- a/tests/cases/REST/TinyTinyRSS/TestAPI.php +++ b/tests/cases/REST/TinyTinyRSS/TestAPI.php @@ -18,7 +18,6 @@ use JKingWeb\Arsse\Db\Transaction; use JKingWeb\Arsse\REST\TinyTinyRSS\API; use JKingWeb\Arsse\Feed\Exception as FeedException; use Psr\Http\Message\ResponseInterface; -use Laminas\Diactoros\Response\JsonResponse as Response; /** @covers \JKingWeb\Arsse\REST\TinyTinyRSS\API * @covers \JKingWeb\Arsse\REST\TinyTinyRSS\Exception */ @@ -166,17 +165,17 @@ LONG_STRING; return $this->req($data, "POST", "", null, $user); } - protected function respGood($content = null, $seq = 0): Response { - return new Response([ + protected function respGood($content = null, $seq = 0): ResponseInterface { + return HTTP::respJson([ 'seq' => $seq, 'status' => 0, 'content' => $content, ]); } - protected function respErr(string $msg, $content = [], $seq = 0): Response { + protected function respErr(string $msg, $content = [], $seq = 0): ResponseInterface { $err = ['error' => $msg]; - return new Response([ + return HTTP::respJson([ 'seq' => $seq, 'status' => 1, 'content' => array_merge($err, $content, $err), @@ -1815,7 +1814,7 @@ LONG_STRING; ])); } - protected function outputHeadlines(int $id): Response { + protected function outputHeadlines(int $id): ResponseInterface { return $this->respGood([ [ 'id' => $id, diff --git a/tests/lib/AbstractTest.php b/tests/lib/AbstractTest.php index 409a6e9..c155a19 100644 --- a/tests/lib/AbstractTest.php +++ b/tests/lib/AbstractTest.php @@ -19,12 +19,12 @@ use JKingWeb\Arsse\Factory; use JKingWeb\Arsse\Misc\Date; use JKingWeb\Arsse\Misc\ValueInfo; use JKingWeb\Arsse\Misc\URL; +use JKingWeb\Arsse\Misc\HTTP; use Psr\Http\Message\MessageInterface; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; use Laminas\Diactoros\ServerRequest; -use Laminas\Diactoros\Response\JsonResponse; use Laminas\Diactoros\Response\XmlResponse; /** @coversNothing */ @@ -191,12 +191,13 @@ abstract class AbstractTest extends \PHPUnit\Framework\TestCase { $this->assertSame($exp->getMethod(), $act->getMethod(), $text); $this->assertSame($exp->getRequestTarget(), $act->getRequestTarget(), $text); } - if ($exp instanceof JsonResponse) { - $this->assertInstanceOf(JsonResponse::class, $act, $text); - $this->assertEquals($exp->getPayload(), $act->getPayload(), $text); - $this->assertSame($exp->getPayload(), $act->getPayload(), $text); - } elseif ($exp instanceof XmlResponse) { - $this->assertInstanceOf(XmlResponse::class, $act, $text); + if ($exp instanceof ResponseInterface && HTTP::matchType($exp, "application/json", "text/json", "+json")) { + $expBody = json_decode((string) $exp->getBody(), true); + $actBody = json_decode((string) $act->getBody(), true); + $this->assertSame(\JSON_ERROR_NONE, json_last_error(), "Response body is not valid JSON"); + $this->assertEquals($expBody, $actBody, $text); + $this->assertSame($expBody, $actBody, $text); + } elseif ($exp instanceof ResponseInterface && HTTP::matchType($exp, "application/xml", "text/xml", "+xml")) { $this->assertXmlStringEqualsXmlString((string) $exp->getBody(), (string) $act->getBody(), $text); } else { $this->assertSame((string) $exp->getBody(), (string) $act->getBody(), $text);