Browse Source

Adjust most uses of Diactoros to Guzzle PSR-7

arch
J. King 2 years ago
parent
commit
4d18bf27e2
  1. 6
      lib/Misc/HTTP.php
  2. 3
      lib/REST/Fever/API.php
  3. 45
      lib/REST/Miniflux/V1.php
  4. 19
      lib/REST/NextcloudNews/V1_2.php
  5. 3
      lib/REST/NextcloudNews/Versions.php
  6. 9
      lib/REST/TinyTinyRSS/API.php
  7. 37
      tests/cases/REST/Fever/TestAPI.php
  8. 165
      tests/cases/REST/Miniflux/TestV1.php
  9. 35
      tests/cases/REST/NextcloudNews/TestV1_2.php
  10. 3
      tests/cases/REST/NextcloudNews/TestVersions.php
  11. 11
      tests/cases/REST/TinyTinyRSS/TestAPI.php
  12. 15
      tests/lib/AbstractTest.php

6
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;
}

3
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);
}
}

45
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 {

19
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' => [

3
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"]);

9
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);
}
}

37
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<extended> */
@ -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=="],

165
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<extended> */
@ -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' => "<svg/>"], new Response(['id' => 44, 'data' => "image/svg+xml;base64,PHN2Zy8+", 'mime_type' => "image/svg+xml"])],
[['id' => 44, 'type' => "image/svg+xml", 'data' => "<svg/>"], HTTP::respJson(['id' => 44, 'data' => "image/svg+xml;base64,PHN2Zy8+", 'mime_type' => "image/svg+xml"])],
[['id' => 47, 'type' => "", 'data' => "<svg/>"], new ErrorResponse("404", 404)],
[['id' => 47, 'type' => null, 'data' => "<svg/>"], 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")])],
];
}

35
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<extended> */
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)));
}

3
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", "/"));

11
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<extended>
* @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,

15
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);

Loading…
Cancel
Save