Browse Source

Replace instances of Diactoros' EmptyResponse

arch
J. King 2 years ago
parent
commit
6c0183faea
  1. 6
      lib/REST.php
  2. 11
      lib/REST/Fever/API.php
  3. 8
      lib/REST/Miniflux/Status.php
  4. 34
      lib/REST/Miniflux/V1.php
  5. 115
      lib/REST/NextcloudNews/V1_2.php
  6. 8
      lib/REST/NextcloudNews/Versions.php
  7. 10
      lib/REST/TinyTinyRSS/API.php
  8. 14
      lib/REST/TinyTinyRSS/Icon.php
  9. 10
      tests/cases/REST/Fever/TestAPI.php
  10. 12
      tests/cases/REST/Miniflux/TestStatus.php
  11. 38
      tests/cases/REST/Miniflux/TestV1.php
  12. 112
      tests/cases/REST/NextcloudNews/TestV1_2.php
  13. 8
      tests/cases/REST/NextcloudNews/TestVersions.php
  14. 40
      tests/cases/REST/TestREST.php
  15. 14
      tests/cases/REST/TinyTinyRSS/TestAPI.php
  16. 24
      tests/cases/REST/TinyTinyRSS/TestIcon.php

6
lib/REST.php

@ -7,11 +7,11 @@ declare(strict_types=1);
namespace JKingWeb\Arsse;
use JKingWeb\Arsse\Misc\URL;
use JKingWeb\Arsse\Misc\HTTP;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Laminas\Diactoros\ServerRequestFactory;
use Laminas\Diactoros\Response\EmptyResponse;
class REST {
public const API_LIST = [
@ -101,7 +101,7 @@ class REST {
$res = $drv->dispatch($req);
}
} catch (REST\Exception501 $e) {
$res = new EmptyResponse(501);
$res = HTTP::respEmpty(501);
}
// modify the response so that it has all the required metadata
return $this->normalizeResponse($res, $req);
@ -180,7 +180,7 @@ class REST {
}
// if the response is to a HEAD request, the body should be omitted
if ($req && $req->getMethod() === "HEAD") {
$res = new EmptyResponse($res->getStatusCode(), $res->getHeaders());
$res = HTTP::respEmpty($res->getStatusCode(), $res->getHeaders());
}
// if an Allow header field is present, normalize it
if ($res->hasHeader("Allow")) {

11
lib/REST/Fever/API.php

@ -10,13 +10,12 @@ use JKingWeb\Arsse\Arsse;
use JKingWeb\Arsse\Context\Context;
use JKingWeb\Arsse\Misc\ValueInfo as V;
use JKingWeb\Arsse\Misc\Date;
use JKingWeb\Arsse\Db\ExceptionInput;
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;
use Laminas\Diactoros\Response\EmptyResponse;
class API extends \JKingWeb\Arsse\REST\AbstractHandler {
public const LEVEL = 3;
@ -62,11 +61,11 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
$P = $this->normalizeInputPost($req->getParsedBody() ?? []);
if (!isset($G['api'])) {
// the original would have shown the Fever UI in the absence of the "api" parameter, but we'll return 404
return new EmptyResponse(404);
return HTTP::respEmpty(404);
}
switch ($req->getMethod()) {
case "OPTIONS":
return new EmptyResponse(204, [
return HTTP::respEmpty(204, [
'Allow' => "POST",
'Accept' => implode(", ", self::ACCEPTED_TYPES),
]);
@ -82,7 +81,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
$out['auth'] = 1;
} elseif (Arsse::$conf->userHTTPAuthRequired || Arsse::$conf->userPreAuth || $req->getAttribute("authenticationFailed", false)) {
// otherwise if HTTP authentication failed or is required, deny access at the HTTP level
return new EmptyResponse(401);
return HTTP::respEmpty(401);
}
// produce a full response if authenticated or a basic response otherwise
if ($this->logIn(strtolower($P['api_key'] ?? ""))) {
@ -93,7 +92,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
// return the result, possibly formatted as XML
return $this->formatResponse($out, ($G['api'] === "xml"));
default:
return new EmptyResponse(405, ['Allow' => "OPTIONS,POST"]);
return HTTP::respEmpty(405, ['Allow' => "OPTIONS,POST"]);
}
}

8
lib/REST/Miniflux/Status.php

@ -6,9 +6,9 @@
declare(strict_types=1);
namespace JKingWeb\Arsse\REST\Miniflux;
use JKingWeb\Arsse\Misc\HTTP;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Laminas\Diactoros\Response\EmptyResponse;
use Laminas\Diactoros\Response\TextResponse;
class Status extends \JKingWeb\Arsse\REST\AbstractHandler {
@ -18,13 +18,13 @@ class Status extends \JKingWeb\Arsse\REST\AbstractHandler {
public function dispatch(ServerRequestInterface $req): ResponseInterface {
$target = parse_url($req->getRequestTarget())['path'] ?? "";
if (!in_array($target, ["/version", "/healthcheck"])) {
return new EmptyResponse(404);
return HTTP::respEmpty(404);
}
$method = $req->getMethod();
if ($method === "OPTIONS") {
return new EmptyResponse(204, ['Allow' => "HEAD, GET"]);
return HTTP::respEmpty(204, ['Allow' => "HEAD, GET"]);
} elseif ($method !== "GET") {
return new EmptyResponse(405, ['Allow' => "HEAD, GET"]);
return HTTP::respEmpty(405, ['Allow' => "HEAD, GET"]);
}
$out = "";
if ($target === "/version") {

34
lib/REST/Miniflux/V1.php

@ -19,6 +19,7 @@ use JKingWeb\Arsse\ImportExport\OPML;
use JKingWeb\Arsse\ImportExport\Exception as ImportException;
use JKingWeb\Arsse\Misc\Date;
use JKingWeb\Arsse\Misc\URL;
use JKingWeb\Arsse\Misc\HTTP;
use JKingWeb\Arsse\Misc\ValueInfo as V;
use JKingWeb\Arsse\REST\Exception;
use JKingWeb\Arsse\Rule\Rule;
@ -26,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\EmptyResponse;
use Laminas\Diactoros\Response\JsonResponse as Response;
use Laminas\Diactoros\Response\TextResponse as GenericResponse;
use Laminas\Diactoros\Uri;
@ -295,10 +295,10 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
// @codeCoverageIgnoreStart
} catch (Exception $e) {
// if there was a REST exception return 400
return new EmptyResponse(400);
return HTTP::respEmpty(400);
} catch (AbstractException $e) {
// if there was any other Arsse exception return 500
return new EmptyResponse(500);
return HTTP::respEmpty(500);
}
// @codeCoverageIgnoreEnd
}
@ -317,11 +317,11 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
return self::CALLS[$url][$method];
} else {
// otherwise return 405
return new EmptyResponse(405, ['Allow' => implode(", ", array_keys(self::CALLS[$url]))]);
return HTTP::respEmpty(405, ['Allow' => implode(", ", array_keys(self::CALLS[$url]))]);
}
} else {
// if the path is not supported, return 404
return new EmptyResponse(404);
return HTTP::respEmpty(404);
}
}
@ -451,13 +451,13 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
if (in_array("GET", $allowed)) {
array_unshift($allowed, "HEAD");
}
return new EmptyResponse(204, [
return HTTP::respEmpty(204, [
'Allow' => implode(", ", $allowed),
'Accept' => implode(", ", $url === "/import" ? self::ACCEPTED_TYPES_OPML : self::ACCEPTED_TYPES_JSON),
]);
} else {
// if the path is not supported, return 404
return new EmptyResponse(404);
return HTTP::respEmpty(404);
}
}
@ -637,7 +637,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
} catch (ExceptionConflict $e) {
return new ErrorResponse("404", 404);
}
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
/** Returns a useful subset of user metadata
@ -728,7 +728,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
} catch (ExceptionInput $e) {
return new ErrorResponse("404", 404);
}
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
protected function transformFeed(array $sub, int $uid, string $rootName, \DateTimeZone $tz): array {
@ -866,7 +866,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
protected function deleteFeed(array $path): ResponseInterface {
try {
Arsse::$db->subscriptionRemove(Arsse::$user->id, (int) $path[1]);
return new EmptyResponse(204);
return HTTP::respEmpty(204);
} catch (ExceptionInput $e) {
return new ErrorResponse("404", 404);
}
@ -1104,7 +1104,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
}
assert(isset($in), new \Exception("Unknown status specified"));
Arsse::$db->articleMark(Arsse::$user->id, $in, (new Context)->articles($data['entry_ids']));
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
protected function massRead(Context $c): void {
@ -1118,7 +1118,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
return new ErrorResponse("403", 403);
}
$this->massRead(new Context);
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
protected function markFeed(array $path): ResponseInterface {
@ -1127,7 +1127,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
} catch (ExceptionInput $e) {
return new ErrorResponse("404", 404);
}
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
protected function markCategory(array $path): ResponseInterface {
@ -1144,7 +1144,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
} catch (ExceptionInput $e) {
return new ErrorResponse("404", 404);
}
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
protected function toggleEntryBookmark(array $path): ResponseInterface {
@ -1162,7 +1162,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
} catch (ExceptionInput $e) {
return new ErrorResponse("404", 404);
}
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
protected function refreshFeed(array $path): ResponseInterface {
@ -1172,13 +1172,13 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
} catch (ExceptionInput $e) {
return new ErrorResponse("404", 404);
}
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
protected function refreshAllFeeds(): ResponseInterface {
// NOTE: This is a no-op
// It could be implemented, but the need is considered low since we use a dynamic schedule always
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
protected function opmlImport(string $data): ResponseInterface {

115
lib/REST/NextcloudNews/V1_2.php

@ -18,7 +18,6 @@ use JKingWeb\Arsse\REST\Exception;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Laminas\Diactoros\Response\JsonResponse as Response;
use Laminas\Diactoros\Response\EmptyResponse;
class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
public const VERSION = "11.0.5";
@ -86,19 +85,19 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
if ($req->getAttribute("authenticated", false)) {
Arsse::$user->id = $req->getAttribute("authenticatedUser");
} else {
return new EmptyResponse(401);
return HTTP::respEmpty(401);
}
// normalize the input
$data = (string) $req->getBody();
if ($data) {
// if the entity body is not JSON according to content type, return "415 Unsupported Media Type"
if (!HTTP::matchType($req, "", self::ACCEPTED_TYPE)) {
return new EmptyResponse(415, ['Accept' => self::ACCEPTED_TYPE]);
return HTTP::respEmpty(415, ['Accept' => self::ACCEPTED_TYPE]);
}
$data = @json_decode($data, true);
if (json_last_error() !== \JSON_ERROR_NONE) {
// if the body could not be parsed as JSON, return "400 Bad Request"
return new EmptyResponse(400);
return HTTP::respEmpty(400);
}
} else {
$data = [];
@ -117,10 +116,10 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
// @codeCoverageIgnoreStart
} catch (Exception $e) {
// if there was a REST exception return 400
return new EmptyResponse(400);
return HTTP::respEmpty(400);
} catch (AbstractException $e) {
// if there was any other Arsse exception return 500
return new EmptyResponse(500);
return HTTP::respEmpty(500);
}
// @codeCoverageIgnoreEnd
}
@ -162,11 +161,11 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
return $this->paths[$url][$method];
} else {
// otherwise return 405
return new EmptyResponse(405, ['Allow' => implode(", ", array_keys($this->paths[$url]))]);
return HTTP::respEmpty(405, ['Allow' => implode(", ", array_keys($this->paths[$url]))]);
}
} else {
// if the path is not supported, return 404
return new EmptyResponse(404);
return HTTP::respEmpty(404);
}
}
@ -268,13 +267,13 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
if (in_array("GET", $allowed)) {
array_unshift($allowed, "HEAD");
}
return new EmptyResponse(204, [
return HTTP::respEmpty(204, [
'Allow' => implode(",", $allowed),
'Accept' => self::ACCEPTED_TYPE,
]);
} else {
// if the path is not supported, return 404
return new EmptyResponse(404);
return HTTP::respEmpty(404);
}
}
@ -294,12 +293,12 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
} catch (ExceptionInput $e) {
switch ($e->getCode()) {
// folder already exists
case 10236: return new EmptyResponse(409);
case 10236: return HTTP::respEmpty(409);
// folder name not acceptable
case 10231:
case 10232: return new EmptyResponse(422);
case 10232: return HTTP::respEmpty(422);
// other errors related to input
default: return new EmptyResponse(400); // @codeCoverageIgnore
default: return HTTP::respEmpty(400); // @codeCoverageIgnore
}
}
$folder = $this->folderTranslate(Arsse::$db->folderPropertiesGet(Arsse::$user->id, $folder));
@ -313,9 +312,9 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
Arsse::$db->folderRemove(Arsse::$user->id, (int) $url[1]);
} catch (ExceptionInput $e) {
// folder does not exist
return new EmptyResponse(404);
return HTTP::respEmpty(404);
}
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
// rename a folder (also supports moving nesting folders, but this is not a feature of the API)
@ -325,24 +324,24 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
} catch (ExceptionInput $e) {
switch ($e->getCode()) {
// folder does not exist
case 10239: return new EmptyResponse(404);
case 10239: return HTTP::respEmpty(404);
// folder already exists
case 10236: return new EmptyResponse(409);
case 10236: return HTTP::respEmpty(409);
// folder name not acceptable
case 10231:
case 10232: return new EmptyResponse(422);
case 10232: return HTTP::respEmpty(422);
// other errors related to input
default: return new EmptyResponse(400); // @codeCoverageIgnore
default: return HTTP::respEmpty(400); // @codeCoverageIgnore
}
}
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
// mark all articles associated with a folder as read
protected function folderMarkRead(array $url, array $data): ResponseInterface {
if (!ValueInfo::id($data['newestItemId'])) {
// if the item ID is invalid (i.e. not a positive integer), this is an error
return new EmptyResponse(422);
return HTTP::respEmpty(422);
}
// build the context
$c = (new Context)->hidden(false);
@ -353,15 +352,15 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
Arsse::$db->articleMark(Arsse::$user->id, ['read' => true], $c);
} catch (ExceptionInput $e) {
// folder does not exist
return new EmptyResponse(404);
return HTTP::respEmpty(404);
}
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
// return list of feeds which should be refreshed
protected function feedListStale(array $url, array $data): ResponseInterface {
if (!$this->isAdmin()) {
return new EmptyResponse(403);
return HTTP::respEmpty(403);
}
// list stale feeds which should be checked for updates
$feeds = Arsse::$db->feedListStale();
@ -376,21 +375,21 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
// refresh a feed
protected function feedUpdate(array $url, array $data): ResponseInterface {
if (!$this->isAdmin()) {
return new EmptyResponse(403);
return HTTP::respEmpty(403);
}
try {
Arsse::$db->feedUpdate($data['feedId']);
} catch (ExceptionInput $e) {
switch ($e->getCode()) {
case 10239: // feed does not exist
return new EmptyResponse(404);
return HTTP::respEmpty(404);
case 10237: // feed ID invalid
return new EmptyResponse(422);
return HTTP::respEmpty(422);
default: // other errors related to input
return new EmptyResponse(400); // @codeCoverageIgnore
return HTTP::respEmpty(400); // @codeCoverageIgnore
}
}
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
// add a new feed
@ -401,10 +400,10 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
$id = Arsse::$db->subscriptionAdd(Arsse::$user->id, (string) $data['url']);
} catch (ExceptionInput $e) {
// feed already exists
return new EmptyResponse(409);
return HTTP::respEmpty(409);
} catch (FeedException $e) {
// feed could not be retrieved
return new EmptyResponse(422);
return HTTP::respEmpty(422);
}
// if a folder was specified, move the feed to the correct folder; silently ignore errors
if ($data['folderId']) {
@ -447,9 +446,9 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
Arsse::$db->subscriptionRemove(Arsse::$user->id, (int) $url[1]);
} catch (ExceptionInput $e) {
// feed does not exist
return new EmptyResponse(404);
return HTTP::respEmpty(404);
}
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
// rename a feed
@ -459,22 +458,22 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
} catch (ExceptionInput $e) {
switch ($e->getCode()) {
// subscription does not exist
case 10239: return new EmptyResponse(404);
case 10239: return HTTP::respEmpty(404);
// name is invalid
case 10231:
case 10232: return new EmptyResponse(422);
case 10232: return HTTP::respEmpty(422);
// other errors related to input
default: return new EmptyResponse(400); // @codeCoverageIgnore
default: return HTTP::respEmpty(400); // @codeCoverageIgnore
}
}
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
// move a feed to a folder
protected function subscriptionMove(array $url, array $data): ResponseInterface {
// if no folder is specified this is an error
if (!isset($data['folderId'])) {
return new EmptyResponse(422);
return HTTP::respEmpty(422);
}
// perform the move
try {
@ -482,22 +481,22 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
} catch (ExceptionInput $e) {
switch ($e->getCode()) {
case 10239: // subscription does not exist
return new EmptyResponse(404);
return HTTP::respEmpty(404);
case 10235: // folder does not exist
case 10237: // folder ID is invalid
return new EmptyResponse(422);
return HTTP::respEmpty(422);
default: // other errors related to input
return new EmptyResponse(400); // @codeCoverageIgnore
return HTTP::respEmpty(400); // @codeCoverageIgnore
}
}
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
// mark all articles associated with a subscription as read
protected function subscriptionMarkRead(array $url, array $data): ResponseInterface {
if (!ValueInfo::id($data['newestItemId'])) {
// if the item ID is invalid (i.e. not a positive integer), this is an error
return new EmptyResponse(422);
return HTTP::respEmpty(422);
}
// build the context
$c = (new Context)->hidden(false);
@ -508,9 +507,9 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
Arsse::$db->articleMark(Arsse::$user->id, ['read' => true], $c);
} catch (ExceptionInput $e) {
// subscription does not exist
return new EmptyResponse(404);
return HTTP::respEmpty(404);
}
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
// list articles and their properties
@ -579,7 +578,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
], [$reverse ? "edition desc" : "edition"]);
} catch (ExceptionInput $e) {
// ID of subscription or folder is not valid
return new EmptyResponse(422);
return HTTP::respEmpty(422);
}
$out = [];
foreach ($items as $item) {
@ -593,14 +592,14 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
protected function articleMarkReadAll(array $url, array $data): ResponseInterface {
if (!ValueInfo::id($data['newestItemId'])) {
// if the item ID is invalid (i.e. not a positive integer), this is an error
return new EmptyResponse(422);
return HTTP::respEmpty(422);
}
// build the context
$c = (new Context)->hidden(false);
$c->editionRange(null, (int) $data['newestItemId']);
// perform the operation
Arsse::$db->articleMark(Arsse::$user->id, ['read' => true], $c);
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
// mark a single article as read
@ -614,9 +613,9 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
Arsse::$db->articleMark(Arsse::$user->id, ['read' => $set], $c);
} catch (ExceptionInput $e) {
// ID is not valid
return new EmptyResponse(404);
return HTTP::respEmpty(404);
}
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
// mark a single article as read
@ -630,9 +629,9 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
Arsse::$db->articleMark(Arsse::$user->id, ['starred' => $set], $c);
} catch (ExceptionInput $e) {
// ID is not valid
return new EmptyResponse(404);
return HTTP::respEmpty(404);
}
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
// mark an array of articles as read
@ -646,7 +645,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
Arsse::$db->articleMark(Arsse::$user->id, ['read' => $set], $c);
} catch (ExceptionInput $e) {
}
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
// mark an array of articles as starred
@ -660,7 +659,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
Arsse::$db->articleMark(Arsse::$user->id, ['starred' => $set], $c);
} catch (ExceptionInput $e) {
}
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
protected function userStatus(array $url, array $data): ResponseInterface {
@ -674,18 +673,18 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
protected function cleanupBefore(array $url, array $data): ResponseInterface {
if (!$this->isAdmin()) {
return new EmptyResponse(403);
return HTTP::respEmpty(403);
}
Service::cleanupPre();
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
protected function cleanupAfter(array $url, array $data): ResponseInterface {
if (!$this->isAdmin()) {
return new EmptyResponse(403);
return HTTP::respEmpty(403);
}
Service::cleanupPost();
return new EmptyResponse(204);
return HTTP::respEmpty(204);
}
// return the server version

8
lib/REST/NextcloudNews/Versions.php

@ -6,10 +6,10 @@
declare(strict_types=1);
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;
use Laminas\Diactoros\Response\EmptyResponse;
class Versions implements \JKingWeb\Arsse\REST\Handler {
public function __construct() {
@ -18,12 +18,12 @@ class Versions implements \JKingWeb\Arsse\REST\Handler {
public function dispatch(ServerRequestInterface $req): ResponseInterface {
if (!preg_match("<^/?$>D", $req->getRequestTarget())) {
// if the request path is more than an empty string or a slash, the client is probably trying a version we don't support
return new EmptyResponse(404);
return HTTP::respEmpty(404);
}
switch ($req->getMethod()) {
case "OPTIONS":
// if the request method is OPTIONS, respond accordingly
return new EmptyResponse(204, ['Allow' => "HEAD,GET"]);
return HTTP::respEmpty(204, ['Allow' => "HEAD,GET"]);
case "GET":
// otherwise return the supported versions
$out = [
@ -34,7 +34,7 @@ class Versions implements \JKingWeb\Arsse\REST\Handler {
return new Response($out);
default:
// if any other method was used, this is an error
return new EmptyResponse(405, ['Allow' => "HEAD,GET"]);
return HTTP::respEmpty(405, ['Allow' => "HEAD,GET"]);
}
}
}

10
lib/REST/TinyTinyRSS/API.php

@ -12,6 +12,7 @@ use JKingWeb\Arsse\Service;
use JKingWeb\Arsse\Database;
use JKingWeb\Arsse\Context\Context;
use JKingWeb\Arsse\Misc\Date;
use JKingWeb\Arsse\Misc\HTTP;
use JKingWeb\Arsse\Misc\ValueInfo as V;
use JKingWeb\Arsse\AbstractException;
use JKingWeb\Arsse\ExceptionType;
@ -21,7 +22,6 @@ use JKingWeb\Arsse\Feed\Exception as FeedException;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Laminas\Diactoros\Response\JsonResponse as Response;
use Laminas\Diactoros\Response\EmptyResponse;
class API extends \JKingWeb\Arsse\REST\AbstractHandler {
public const LEVEL = 15; // emulated API level
@ -96,11 +96,11 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
public function dispatch(ServerRequestInterface $req): ResponseInterface {
if (!preg_match("<^(?:/(?:index\.php)?)?$>D", $req->getRequestTarget())) {
// reject paths other than the index
return new EmptyResponse(404);
return HTTP::respEmpty(404);
}
if ($req->getMethod() === "OPTIONS") {
// respond to OPTIONS rquests; the response is a fib, as we technically accept any type or method
return new EmptyResponse(204, [
return HTTP::respEmpty(204, [
'Allow' => "POST",
'Accept' => implode(", ", self::ACCEPTED_TYPES),
]);
@ -125,7 +125,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
Arsse::$user->id = $req->getAttribute("authenticatedUser");
} elseif (Arsse::$conf->userHTTPAuthRequired || Arsse::$conf->userPreAuth || $req->getAttribute("authenticationFailed", false)) {
// otherwise if HTTP authentication failed or is required, deny access at the HTTP level
return new EmptyResponse(401);
return HTTP::respEmpty(401);
}
if (strtolower((string) $data['op']) !== "login") {
// unless logging in, a session identifier is required
@ -148,7 +148,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
'content' => $e->getData(),
]);
} catch (AbstractException $e) {
return new EmptyResponse(500);
return HTTP::respEmpty(500);
}
} else {
// absence of a request body indicates an error

14
lib/REST/TinyTinyRSS/Icon.php

@ -7,10 +7,10 @@ declare(strict_types=1);
namespace JKingWeb\Arsse\REST\TinyTinyRSS;
use JKingWeb\Arsse\Arsse;
use JKingWeb\Arsse\Misc\HTTP;
use JKingWeb\Arsse\Db\ExceptionInput;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Laminas\Diactoros\Response\EmptyResponse as Response;
class Icon extends \JKingWeb\Arsse\REST\AbstractHandler {
public function __construct() {
@ -22,25 +22,25 @@ class Icon extends \JKingWeb\Arsse\REST\AbstractHandler {
Arsse::$user->id = $req->getAttribute("authenticatedUser");
} elseif ($req->getAttribute("authenticationFailed", false) || Arsse::$conf->userHTTPAuthRequired) {
// otherwise if HTTP authentication failed or did not occur when it is required, deny access at the HTTP level
return new Response(401);
return HTTP::respEmpty(401);
}
if ($req->getMethod() !== "GET") {
// only GET requests are allowed
return new Response(405, ['Allow' => "GET"]);
return HTTP::respEmpty(405, ['Allow' => "GET"]);
} elseif (!preg_match("<^(\d+)\.ico$>D", $req->getRequestTarget(), $match) || !((int) $match[1])) {
return new Response(404);
return HTTP::respEmpty(404);
}
try {
$url = Arsse::$db->subscriptionIcon(Arsse::$user->id ?? null, (int) $match[1], false)['url'] ?? null;
if (!$url) {
return new Response(404);
return HTTP::respEmpty(404);
}
if (($pos = strpos($url, "\r")) !== false || ($pos = strpos($url, "\n")) !== false) {
$url = substr($url, 0, $pos);
}
return new Response(301, ['Location' => $url]);
return HTTP::respEmpty(301, ['Location' => $url]);
} catch (ExceptionInput $e) {
return new Response(404);
return HTTP::respEmpty(404);
}
}
}

10
tests/cases/REST/Fever/TestAPI.php

@ -9,6 +9,7 @@ namespace JKingWeb\Arsse\TestCase\REST\Fever;
use JKingWeb\Arsse\Arsse;
use JKingWeb\Arsse\User;
use JKingWeb\Arsse\Database;
use JKingWeb\Arsse\Misc\HTTP;
use JKingWeb\Arsse\Test\Result;
use JKingWeb\Arsse\Context\Context;
use JKingWeb\Arsse\Db\ExceptionInput;
@ -17,7 +18,6 @@ use JKingWeb\Arsse\REST\Fever\API;
use Psr\Http\Message\ResponseInterface;
use Laminas\Diactoros\Response\JsonResponse;
use Laminas\Diactoros\Response\XmlResponse;
use Laminas\Diactoros\Response\EmptyResponse;
/** @covers \JKingWeb\Arsse\REST\Fever\API<extended> */
class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
@ -194,7 +194,7 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
public function provideTokenAuthenticationRequests(): iterable {
$success = new JsonResponse(['auth' => 1]);
$failure = new JsonResponse(['auth' => 0]);
$denied = new EmptyResponse(401);
$denied = HTTP::respEmpty(401);
return [
[false, true, null, [], ['api' => null], $failure],
[false, false, null, [], ['api' => null], $failure],
@ -421,8 +421,8 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
public function provideInvalidRequests(): iterable {
return [
'Not an API request' => ["", "", "POST", null, new EmptyResponse(404)],
'Wrong method' => ["api", "", "PUT", null, new EmptyResponse(405, ['Allow' => "OPTIONS,POST"])],
'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
@ -494,7 +494,7 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
}
public function testAnswerOptionsRequest(): void {
$exp = new EmptyResponse(204, [
$exp = HTTP::respEmpty(204, [
'Allow' => "POST",
'Accept' => "application/x-www-form-urlencoded, multipart/form-data",
]);

12
tests/cases/REST/Miniflux/TestStatus.php

@ -6,10 +6,10 @@
declare(strict_types=1);
namespace JKingWeb\Arsse\TestCase\REST\Miniflux;
use JKingWeb\Arsse\Misc\HTTP;
use JKingWeb\Arsse\REST\Miniflux\Status;
use JKingWeb\Arsse\REST\Miniflux\V1;
use Psr\Http\Message\ResponseInterface;
use Laminas\Diactoros\Response\EmptyResponse;
use Laminas\Diactoros\Response\TextResponse;
/** @covers \JKingWeb\Arsse\REST\Miniflux\Status */
@ -23,12 +23,12 @@ class TestStatus extends \JKingWeb\Arsse\Test\AbstractTest {
public function provideRequests(): iterable {
return [
["/version", "GET", new TextResponse(V1::VERSION)],
["/version", "POST", new EmptyResponse(405, ['Allow' => "HEAD, GET"])],
["/version", "OPTIONS", new EmptyResponse(204, ['Allow' => "HEAD, GET"])],
["/version", "POST", HTTP::respEmpty(405, ['Allow' => "HEAD, GET"])],
["/version", "OPTIONS", HTTP::respEmpty(204, ['Allow' => "HEAD, GET"])],
["/healthcheck", "GET", new TextResponse("OK")],
["/healthcheck", "POST", new EmptyResponse(405, ['Allow' => "HEAD, GET"])],
["/healthcheck", "OPTIONS", new EmptyResponse(204, ['Allow' => "HEAD, GET"])],
["/version/", "GET", new EmptyResponse(404)],
["/healthcheck", "POST", HTTP::respEmpty(405, ['Allow' => "HEAD, GET"])],
["/healthcheck", "OPTIONS", HTTP::respEmpty(204, ['Allow' => "HEAD, GET"])],
["/version/", "GET", HTTP::respEmpty(404)],
];
}
}

38
tests/cases/REST/Miniflux/TestV1.php

@ -14,6 +14,7 @@ use JKingWeb\Arsse\Context\RootContext;
use JKingWeb\Arsse\Context\UnionContext;
use JKingWeb\Arsse\User;
use JKingWeb\Arsse\Database;
use JKingWeb\Arsse\Misc\HTTP;
use JKingWeb\Arsse\Db\Transaction;
use JKingWeb\Arsse\Db\ExceptionInput;
use JKingWeb\Arsse\REST\Miniflux\V1;
@ -26,7 +27,6 @@ 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\EmptyResponse;
use Laminas\Diactoros\Response\TextResponse;
/** @covers \JKingWeb\Arsse\REST\Miniflux\V1<extended> */
@ -102,7 +102,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
/** @dataProvider provideAuthResponses */
public function testAuthenticateAUser($token, bool $auth, bool $success): void {
$exp = $success ? new EmptyResponse(404) : new ErrorResponse("401", 401);
$exp = $success ? HTTP::respEmpty(404) : new ErrorResponse("401", 401);
$user = "john.doe@example.com";
if ($token !== null) {
$headers = ['X-Auth-Token' => $token];
@ -133,7 +133,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
/** @dataProvider provideInvalidPaths */
public function testRespondToInvalidPaths($path, $method, $code, $allow = null): void {
$exp = new EmptyResponse($code, $allow ? ['Allow' => $allow] : []);
$exp = HTTP::respEmpty($code, $allow ? ['Allow' => $allow] : []);
$this->assertMessage($exp, $this->req($method, $path));
}
@ -148,7 +148,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
/** @dataProvider provideOptionsRequests */
public function testRespondToOptionsRequests(string $url, string $allow, string $accept): void {
$exp = new EmptyResponse(204, [
$exp = HTTP::respEmpty(204, [
'Allow' => $allow,
'Accept' => $accept,
]);
@ -382,7 +382,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
Arsse::$user->method("remove")->willReturn(true);
Arsse::$user->expects($this->exactly(1))->method("lookup")->with(2112);
Arsse::$user->expects($this->exactly(1))->method("remove")->with("john.doe@example.com");
$this->assertMessage(new EmptyResponse(204), $this->req("DELETE", "/users/2112"));
$this->assertMessage(HTTP::respEmpty(204), $this->req("DELETE", "/users/2112"));
}
public function testDeleteAMissingUser(): void {
@ -484,7 +484,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
public function testDeleteARealCategory(): void {
$this->dbMock->folderRemove->returns(true)->throws(new ExceptionInput("subjectMissing"));
$this->assertMessage(new EmptyResponse(204), $this->req("DELETE", "/categories/2112"));
$this->assertMessage(HTTP::respEmpty(204), $this->req("DELETE", "/categories/2112"));
$this->dbMock->folderRemove->calledWith("john.doe@example.com", 2111);
$this->assertMessage(new ErrorResponse("404", 404), $this->req("DELETE", "/categories/47"));
$this->dbMock->folderRemove->calledWith("john.doe@example.com", 46);
@ -497,7 +497,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
['id' => 2112],
])));
$this->dbMock->subscriptionRemove->returns(true);
$this->assertMessage(new EmptyResponse(204), $this->req("DELETE", "/categories/1"));
$this->assertMessage(HTTP::respEmpty(204), $this->req("DELETE", "/categories/1"));
Phony::inOrder(
$this->dbMock->begin->calledWith(),
$this->dbMock->subscriptionList->calledWith("john.doe@example.com", null, false),
@ -680,7 +680,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
public function testDeleteAFeed(): void {
$this->dbMock->subscriptionRemove->returns(true);
$this->assertMessage(new EmptyResponse(204), $this->req("DELETE", "/feeds/2112"));
$this->assertMessage(HTTP::respEmpty(204), $this->req("DELETE", "/feeds/2112"));
$this->dbMock->subscriptionRemove->calledWith(Arsse::$user->id, 2112);
}
@ -864,9 +864,9 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
[['entry_ids' => [1], 'status' => 1], null, new ErrorResponse(["InvalidInputType", 'field' => "status", 'expected' => "string", 'actual' => "integer"], 422)],
[['entry_ids' => [0], 'status' => "read"], null, new ErrorResponse(["InvalidInputValue", 'field' => "entry_ids"], 422)],
[['entry_ids' => [1], 'status' => "reread"], null, new ErrorResponse(["InvalidInputValue", 'field' => "status"], 422)],
[['entry_ids' => [1, 2], 'status' => "read"], ['read' => true, 'hidden' => false], new EmptyResponse(204)],
[['entry_ids' => [1, 2], 'status' => "unread"], ['read' => false, 'hidden' => false], new EmptyResponse(204)],
[['entry_ids' => [1, 2], 'status' => "removed"], ['read' => true, 'hidden' => true], new EmptyResponse(204)],
[['entry_ids' => [1, 2], 'status' => "read"], ['read' => true, 'hidden' => false], HTTP::respEmpty(204)],
[['entry_ids' => [1, 2], 'status' => "unread"], ['read' => false, 'hidden' => false], HTTP::respEmpty(204)],
[['entry_ids' => [1, 2], 'status' => "removed"], ['read' => true, 'hidden' => true], HTTP::respEmpty(204)],
];
}
@ -889,13 +889,13 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
self::clearData();
$c = (new Context)->hidden(false);
return [
["/users/42/mark-all-as-read", $c, 1123, new EmptyResponse(204)],
["/users/42/mark-all-as-read", $c, 1123, HTTP::respEmpty(204)],
["/users/2112/mark-all-as-read", $c, null, new ErrorResponse("403", 403)],
["/feeds/47/mark-all-as-read", (clone $c)->subscription(47), 2112, new EmptyResponse(204)],
["/feeds/47/mark-all-as-read", (clone $c)->subscription(47), 2112, HTTP::respEmpty(204)],
["/feeds/2112/mark-all-as-read", (clone $c)->subscription(2112), new ExceptionInput("idMissing"), new ErrorResponse("404", 404)],
["/categories/47/mark-all-as-read", (clone $c)->folder(46), 1337, new EmptyResponse(204)],
["/categories/47/mark-all-as-read", (clone $c)->folder(46), 1337, HTTP::respEmpty(204)],
["/categories/2112/mark-all-as-read", (clone $c)->folder(2111), new ExceptionInput("idMissing"), new ErrorResponse("404", 404)],
["/categories/1/mark-all-as-read", (clone $c)->folderShallow(0), 6666, new EmptyResponse(204)],
["/categories/1/mark-all-as-read", (clone $c)->folderShallow(0), 6666, HTTP::respEmpty(204)],
];
}
@ -929,15 +929,15 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
public function provideBookmarkTogglings(): iterable {
self::clearData();
return [
[1, true, new EmptyResponse(204)],
[0, false, new EmptyResponse(204)],
[1, true, HTTP::respEmpty(204)],
[0, false, HTTP::respEmpty(204)],
[new ExceptionInput("subjectMissing"), null, new ErrorResponse("404", 404)],
];
}
public function testRefreshAFeed(): void {
$this->dbMock->subscriptionPropertiesGet->returns([]);
$this->assertMessage(new EmptyResponse(204), $this->req("PUT", "/feeds/47/refresh"));
$this->assertMessage(HTTP::respEmpty(204), $this->req("PUT", "/feeds/47/refresh"));
$this->dbMock->subscriptionPropertiesGet->calledWith(Arsse::$user->id, 47);
}
@ -948,7 +948,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
}
public function testRefreshAllFeeds(): void {
$this->assertMessage(new EmptyResponse(204), $this->req("PUT", "/feeds/refresh"));
$this->assertMessage(HTTP::respEmpty(204), $this->req("PUT", "/feeds/refresh"));
}
/** @dataProvider provideImports */

112
tests/cases/REST/NextcloudNews/TestV1_2.php

@ -11,13 +11,13 @@ use JKingWeb\Arsse\User;
use JKingWeb\Arsse\Database;
use JKingWeb\Arsse\Test\Result;
use JKingWeb\Arsse\Misc\Date;
use JKingWeb\Arsse\Misc\HTTP;
use JKingWeb\Arsse\Context\Context;
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;
use Laminas\Diactoros\Response\EmptyResponse;
/** @covers \JKingWeb\Arsse\REST\NextcloudNews\V1_2<extended> */
class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
@ -336,13 +336,13 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
}
public function testSendAuthenticationChallenge(): void {
$exp = new EmptyResponse(401);
$exp = HTTP::respEmpty(401);
$this->assertMessage($exp, $this->req("GET", "/", "", [], false));
}
/** @dataProvider provideInvalidPaths */
public function testRespondToInvalidPaths($path, $method, $code, $allow = null): void {
$exp = new EmptyResponse($code, $allow ? ['Allow' => $allow] : []);
$exp = HTTP::respEmpty($code, $allow ? ['Allow' => $allow] : []);
$this->assertMessage($exp, $this->req($method, $path));
}
@ -374,16 +374,16 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
}
public function testRespondToInvalidInputTypes(): void {
$exp = new EmptyResponse(415, ['Accept' => "application/json"]);
$exp = HTTP::respEmpty(415, ['Accept' => "application/json"]);
$this->assertMessage($exp, $this->req("PUT", "/folders/1", '<data/>', ['Content-Type' => "application/xml"]));
$exp = new EmptyResponse(400);
$exp = HTTP::respEmpty(400);
$this->assertMessage($exp, $this->req("PUT", "/folders/1", '<data/>'));
$this->assertMessage($exp, $this->req("PUT", "/folders/1", '<data/>', ['Content-Type' => null]));
}
/** @dataProvider provideOptionsRequests */
public function testRespondToOptionsRequests(string $url, string $allow, string $accept): void {
$exp = new EmptyResponse(204, [
$exp = HTTP::respEmpty(204, [
'Allow' => $allow,
'Accept' => $accept,
]);
@ -436,19 +436,19 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
[['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, new ExceptionInput("constraintViolation"), new EmptyResponse(409)],
[['name' => ""], true, new ExceptionInput("whitespace"), new EmptyResponse(422)],
[['name' => " "], true, new ExceptionInput("whitespace"), new EmptyResponse(422)],
[['name' => null], true, new ExceptionInput("missing"), new EmptyResponse(422)],
[['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)],
[['name' => null], true, new ExceptionInput("missing"), HTTP::respEmpty(422)],
];
}
public function testRemoveAFolder(): void {
$this->dbMock->folderRemove->with($this->userId, 1)->returns(true)->throws(new ExceptionInput("subjectMissing"));
$exp = new EmptyResponse(204);
$exp = HTTP::respEmpty(204);
$this->assertMessage($exp, $this->req("DELETE", "/folders/1"));
// fail on the second invocation because it no longer exists
$exp = new EmptyResponse(404);
$exp = HTTP::respEmpty(404);
$this->assertMessage($exp, $this->req("DELETE", "/folders/1"));
$this->dbMock->folderRemove->times(2)->calledWith($this->userId, 1);
}
@ -467,12 +467,12 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
public function provideFolderRenamings(): array {
return [
[['name' => "Software"], 1, true, new EmptyResponse(204)],
[['name' => "Software"], 2, new ExceptionInput("constraintViolation"), new EmptyResponse(409)],
[['name' => "Software"], 3, new ExceptionInput("subjectMissing"), new EmptyResponse(404)],
[['name' => ""], 2, new ExceptionInput("whitespace"), new EmptyResponse(422)],
[['name' => " "], 2, new ExceptionInput("whitespace"), new EmptyResponse(422)],
[['name' => null], 2, new ExceptionInput("missing"), new EmptyResponse(422)],
[['name' => "Software"], 1, true, HTTP::respEmpty(204)],
[['name' => "Software"], 2, new ExceptionInput("constraintViolation"), HTTP::respEmpty(409)],
[['name' => "Software"], 3, new ExceptionInput("subjectMissing"), HTTP::respEmpty(404)],
[['name' => ""], 2, new ExceptionInput("whitespace"), HTTP::respEmpty(422)],
[['name' => " "], 2, new ExceptionInput("whitespace"), HTTP::respEmpty(422)],
[['name' => null], 2, new ExceptionInput("missing"), HTTP::respEmpty(422)],
];
}
@ -540,19 +540,19 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
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], new ExceptionInput("constraintViolation"), 0, $this->feeds['db'][0], new ExceptionInput("idMissing"), new EmptyResponse(409)],
[['url' => "http://example.org/news.atom", 'folderId' => 8], new ExceptionInput("constraintViolation"), 4758915, $this->feeds['db'][1], true, new EmptyResponse(409)],
[[], $feedException, 0, [], false, new EmptyResponse(422)],
[['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])],
];
}
public function testRemoveASubscription(): void {
$this->dbMock->subscriptionRemove->with($this->userId, 1)->returns(true)->throws(new ExceptionInput("subjectMissing"));
$exp = new EmptyResponse(204);
$exp = HTTP::respEmpty(204);
$this->assertMessage($exp, $this->req("DELETE", "/feeds/1"));
// fail on the second invocation because it no longer exists
$exp = new EmptyResponse(404);
$exp = HTTP::respEmpty(404);
$this->assertMessage($exp, $this->req("DELETE", "/feeds/1"));
$this->dbMock->subscriptionRemove->times(2)->calledWith($this->userId, 1);
}
@ -571,17 +571,17 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
$this->dbMock->subscriptionPropertiesSet->with($this->userId, 1, ['folder' => 2112])->throws(new ExceptionInput("idMissing")); // folder does not exist
$this->dbMock->subscriptionPropertiesSet->with($this->userId, 1, ['folder' => -1])->throws(new ExceptionInput("typeViolation")); // folder is invalid
$this->dbMock->subscriptionPropertiesSet->with($this->userId, 42, $this->anything())->throws(new ExceptionInput("subjectMissing")); // subscription does not exist
$exp = new EmptyResponse(204);
$exp = HTTP::respEmpty(204);
$this->assertMessage($exp, $this->req("PUT", "/feeds/1/move", json_encode($in[0])));
$exp = new EmptyResponse(204);
$exp = HTTP::respEmpty(204);
$this->assertMessage($exp, $this->req("PUT", "/feeds/1/move", json_encode($in[1])));
$exp = new EmptyResponse(422);
$exp = HTTP::respEmpty(422);
$this->assertMessage($exp, $this->req("PUT", "/feeds/1/move", json_encode($in[2])));
$exp = new EmptyResponse(404);
$exp = HTTP::respEmpty(404);
$this->assertMessage($exp, $this->req("PUT", "/feeds/42/move", json_encode($in[3])));
$exp = new EmptyResponse(422);
$exp = HTTP::respEmpty(422);
$this->assertMessage($exp, $this->req("PUT", "/feeds/1/move", json_encode($in[4])));
$exp = new EmptyResponse(422);
$exp = HTTP::respEmpty(422);
$this->assertMessage($exp, $this->req("PUT", "/feeds/1/move", json_encode($in[5])));
}
@ -601,17 +601,17 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
$this->dbMock->subscriptionPropertiesSet->with($this->userId, 1, $this->identicalTo(['title' => ""]))->throws(new ExceptionInput("missing"));
$this->dbMock->subscriptionPropertiesSet->with($this->userId, 1, $this->identicalTo(['title' => false]))->throws(new ExceptionInput("missing"));
$this->dbMock->subscriptionPropertiesSet->with($this->userId, 42, $this->anything())->throws(new ExceptionInput("subjectMissing"));
$exp = new EmptyResponse(422);
$exp = HTTP::respEmpty(422);
$this->assertMessage($exp, $this->req("PUT", "/feeds/1/rename", json_encode($in[0])));
$exp = new EmptyResponse(204);
$exp = HTTP::respEmpty(204);
$this->assertMessage($exp, $this->req("PUT", "/feeds/1/rename", json_encode($in[1])));
$exp = new EmptyResponse(422);
$exp = HTTP::respEmpty(422);
$this->assertMessage($exp, $this->req("PUT", "/feeds/1/rename", json_encode($in[2])));
$exp = new EmptyResponse(422);
$exp = HTTP::respEmpty(422);
$this->assertMessage($exp, $this->req("PUT", "/feeds/1/rename", json_encode($in[3])));
$exp = new EmptyResponse(404);
$exp = HTTP::respEmpty(404);
$this->assertMessage($exp, $this->req("PUT", "/feeds/42/rename", json_encode($in[4])));
$exp = new EmptyResponse(422);
$exp = HTTP::respEmpty(422);
$this->assertMessage($exp, $this->req("PUT", "/feeds/1/rename", json_encode($in[6])));
}
@ -633,7 +633,7 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
public function testListStaleFeedsWithoutAuthority(): void {
$this->userMock->propertiesGet->returns(['admin' => false]);
$exp = new EmptyResponse(403);
$exp = HTTP::respEmpty(403);
$this->assertMessage($exp, $this->req("GET", "/feeds/all"));
$this->dbMock->feedListStale->never()->called();
}
@ -649,11 +649,11 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
$this->dbMock->feedUpdate->with(42)->returns(true);
$this->dbMock->feedUpdate->with(2112)->throws(new ExceptionInput("subjectMissing"));
$this->dbMock->feedUpdate->with($this->lessThan(1))->throws(new ExceptionInput("typeViolation"));
$exp = new EmptyResponse(204);
$exp = HTTP::respEmpty(204);
$this->assertMessage($exp, $this->req("GET", "/feeds/update", json_encode($in[0])));
$exp = new EmptyResponse(404);
$exp = HTTP::respEmpty(404);
$this->assertMessage($exp, $this->req("GET", "/feeds/update", json_encode($in[1])));
$exp = new EmptyResponse(422);
$exp = HTTP::respEmpty(422);
$this->assertMessage($exp, $this->req("GET", "/feeds/update", json_encode($in[2])));
$this->assertMessage($exp, $this->req("GET", "/feeds/update", json_encode($in[3])));
$this->assertMessage($exp, $this->req("GET", "/feeds/update", json_encode($in[4])));
@ -661,7 +661,7 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
public function testUpdateAFeedWithoutAuthority(): void {
$this->userMock->propertiesGet->returns(['admin' => false]);
$exp = new EmptyResponse(403);
$exp = HTTP::respEmpty(403);
$this->assertMessage($exp, $this->req("GET", "/feeds/update", ['feedId' => 42]));
$this->dbMock->feedUpdate->never()->called();
}
@ -684,7 +684,7 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
$t = Date::normalize(time());
$out = new Result($this->v($this->articles['db']));
$r200 = new Response(['items' => $this->articles['rest']]);
$r422 = new EmptyResponse(422);
$r422 = HTTP::respEmpty(422);
return [
["/items", [], clone $c, $out, $r200],
["/items", ['type' => 0, 'id' => 42], (clone $c)->subscription(42), new ExceptionInput("idMissing"), $r422],
@ -720,13 +720,13 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
$in = json_encode(['newestItemId' => 2112]);
$this->dbMock->articleMark->with($this->userId, $read, $this->equalTo((new Context)->folder(1)->editionRange(null, 2112)->hidden(false)))->returns(42);
$this->dbMock->articleMark->with($this->userId, $read, $this->equalTo((new Context)->folder(42)->editionRange(null, 2112)->hidden(false)))->throws(new ExceptionInput("idMissing")); // folder doesn't exist
$exp = new EmptyResponse(204);
$exp = HTTP::respEmpty(204);
$this->assertMessage($exp, $this->req("PUT", "/folders/1/read", $in));
$this->assertMessage($exp, $this->req("PUT", "/folders/1/read?newestItemId=2112"));
$exp = new EmptyResponse(422);
$exp = HTTP::respEmpty(422);
$this->assertMessage($exp, $this->req("PUT", "/folders/1/read"));
$this->assertMessage($exp, $this->req("PUT", "/folders/1/read?newestItemId=ook"));
$exp = new EmptyResponse(404);
$exp = HTTP::respEmpty(404);
$this->assertMessage($exp, $this->req("PUT", "/folders/42/read", $in));
}
@ -735,13 +735,13 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
$in = json_encode(['newestItemId' => 2112]);
$this->dbMock->articleMark->with($this->userId, $read, $this->equalTo((new Context)->subscription(1)->editionRange(null, 2112)->hidden(false)))->returns(42);
$this->dbMock->articleMark->with($this->userId, $read, $this->equalTo((new Context)->subscription(42)->editionRange(null, 2112)->hidden(false)))->throws(new ExceptionInput("idMissing")); // subscription doesn't exist
$exp = new EmptyResponse(204);
$exp = HTTP::respEmpty(204);
$this->assertMessage($exp, $this->req("PUT", "/feeds/1/read", $in));
$this->assertMessage($exp, $this->req("PUT", "/feeds/1/read?newestItemId=2112"));
$exp = new EmptyResponse(422);
$exp = HTTP::respEmpty(422);
$this->assertMessage($exp, $this->req("PUT", "/feeds/1/read"));
$this->assertMessage($exp, $this->req("PUT", "/feeds/1/read?newestItemId=ook"));
$exp = new EmptyResponse(404);
$exp = HTTP::respEmpty(404);
$this->assertMessage($exp, $this->req("PUT", "/feeds/42/read", $in));
}
@ -749,10 +749,10 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
$read = ['read' => true];
$in = json_encode(['newestItemId' => 2112]);
$this->dbMock->articleMark->with($this->userId, $read, $this->equalTo((new Context)->editionRange(null, 2112)))->returns(42);
$exp = new EmptyResponse(204);
$exp = HTTP::respEmpty(204);
$this->assertMessage($exp, $this->req("PUT", "/items/read", $in));
$this->assertMessage($exp, $this->req("PUT", "/items/read?newestItemId=2112"));
$exp = new EmptyResponse(422);
$exp = HTTP::respEmpty(422);
$this->assertMessage($exp, $this->req("PUT", "/items/read"));
$this->assertMessage($exp, $this->req("PUT", "/items/read?newestItemId=ook"));
}
@ -770,12 +770,12 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
$this->dbMock->articleMark->with($this->userId, $star, $this->equalTo((new Context)->article(2112)))->throws(new ExceptionInput("subjectMissing")); // article doesn't exist doesn't exist
$this->dbMock->articleMark->with($this->userId, $unstar, $this->equalTo((new Context)->article(4)))->returns(42);
$this->dbMock->articleMark->with($this->userId, $unstar, $this->equalTo((new Context)->article(1337)))->throws(new ExceptionInput("subjectMissing")); // article doesn't exist doesn't exist
$exp = new EmptyResponse(204);
$exp = HTTP::respEmpty(204);
$this->assertMessage($exp, $this->req("PUT", "/items/1/read"));
$this->assertMessage($exp, $this->req("PUT", "/items/2/unread"));
$this->assertMessage($exp, $this->req("PUT", "/items/1/3/star"));
$this->assertMessage($exp, $this->req("PUT", "/items/4400/4/unstar"));
$exp = new EmptyResponse(404);
$exp = HTTP::respEmpty(404);
$this->assertMessage($exp, $this->req("PUT", "/items/42/read"));
$this->assertMessage($exp, $this->req("PUT", "/items/47/unread"));
$this->assertMessage($exp, $this->req("PUT", "/items/1/2112/star"));
@ -801,7 +801,7 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
$this->dbMock->articleMark->with($this->userId, $this->anything(), $this->anything())->returns(42);
$this->dbMock->articleMark->with($this->userId, $this->anything(), $this->equalTo((new Context)->editions([])))->throws(new ExceptionInput("tooShort")); // data model function requires one valid integer for multiples
$this->dbMock->articleMark->with($this->userId, $this->anything(), $this->equalTo((new Context)->articles([])))->throws(new ExceptionInput("tooShort")); // data model function requires one valid integer for multiples
$exp = new EmptyResponse(204);
$exp = HTTP::respEmpty(204);
$this->assertMessage($exp, $this->req("PUT", "/items/read/multiple"));
$this->assertMessage($exp, $this->req("PUT", "/items/unread/multiple"));
$this->assertMessage($exp, $this->req("PUT", "/items/star/multiple"));
@ -860,28 +860,28 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
public function testCleanUpBeforeUpdate(): void {
$this->dbMock->feedCleanup->with()->returns(true);
$exp = new EmptyResponse(204);
$exp = HTTP::respEmpty(204);
$this->assertMessage($exp, $this->req("GET", "/cleanup/before-update"));
$this->dbMock->feedCleanup->calledWith();
}
public function testCleanUpBeforeUpdateWithoutAuthority(): void {
$this->userMock->propertiesGet->returns(['admin' => false]);
$exp = new EmptyResponse(403);
$exp = HTTP::respEmpty(403);
$this->assertMessage($exp, $this->req("GET", "/cleanup/before-update"));
$this->dbMock->feedCleanup->never()->called();
}
public function testCleanUpAfterUpdate(): void {
$this->dbMock->articleCleanup->with()->returns(true);
$exp = new EmptyResponse(204);
$exp = HTTP::respEmpty(204);
$this->assertMessage($exp, $this->req("GET", "/cleanup/after-update"));
$this->dbMock->articleCleanup->calledWith();
}
public function testCleanUpAfterUpdateWithoutAuthority(): void {
$this->userMock->propertiesGet->returns(['admin' => false]);
$exp = new EmptyResponse(403);
$exp = HTTP::respEmpty(403);
$this->assertMessage($exp, $this->req("GET", "/cleanup/after-update"));
$this->dbMock->feedCleanup->never()->called();
}

8
tests/cases/REST/NextcloudNews/TestVersions.php

@ -6,10 +6,10 @@
declare(strict_types=1);
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;
use Laminas\Diactoros\Response\EmptyResponse;
/** @covers \JKingWeb\Arsse\REST\NextcloudNews\Versions */
class TestVersions extends \JKingWeb\Arsse\Test\AbstractTest {
@ -32,17 +32,17 @@ class TestVersions extends \JKingWeb\Arsse\Test\AbstractTest {
}
public function testRespondToOptionsRequest(): void {
$exp = new EmptyResponse(204, ['Allow' => "HEAD,GET"]);
$exp = HTTP::respEmpty(204, ['Allow' => "HEAD,GET"]);
$this->assertMessage($exp, $this->req("OPTIONS", "/"));
}
public function testUseIncorrectMethod(): void {
$exp = new EmptyResponse(405, ['Allow' => "HEAD,GET"]);
$exp = HTTP::respEmpty(405, ['Allow' => "HEAD,GET"]);
$this->assertMessage($exp, $this->req("POST", "/"));
}
public function testUseIncorrectPath(): void {
$exp = new EmptyResponse(404);
$exp = HTTP::respEmpty(404);
$this->assertMessage($exp, $this->req("GET", "/ook"));
$this->assertMessage($exp, $this->req("OPTIONS", "/ook"));
}

40
tests/cases/REST/TestREST.php

@ -12,13 +12,13 @@ use JKingWeb\Arsse\REST;
use JKingWeb\Arsse\REST\Exception501;
use JKingWeb\Arsse\REST\NextcloudNews\V1_2 as NCN;
use JKingWeb\Arsse\REST\TinyTinyRSS\API as TTRSS;
use JKingWeb\Arsse\Misc\HTTP;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Laminas\Diactoros\Request;
use Laminas\Diactoros\Response;
use Laminas\Diactoros\ServerRequest;
use Laminas\Diactoros\Response\TextResponse;
use Laminas\Diactoros\Response\EmptyResponse;
/** @covers \JKingWeb\Arsse\REST */
class TestREST extends \JKingWeb\Arsse\Test\AbstractTest {
@ -96,7 +96,7 @@ class TestREST extends \JKingWeb\Arsse\Test\AbstractTest {
public function testSendAuthenticationChallenges(): void {
self::setConf();
$r = new REST();
$in = new EmptyResponse(401);
$in = HTTP::respEmpty(401);
$exp = $in->withHeader("WWW-Authenticate", 'Basic realm="OOK", charset="UTF-8"');
$act = $r->challenge($in, "OOK");
$this->assertMessage($exp, $act);
@ -190,8 +190,8 @@ class TestREST extends \JKingWeb\Arsse\Test\AbstractTest {
public function testAddCorsHeaders(string $reqMethod, array $reqHeaders, array $resHeaders, array $expHeaders): void {
$r = new REST();
$req = new Request("", $reqMethod, "php://memory", $reqHeaders);
$res = new EmptyResponse(204, $resHeaders);
$exp = new EmptyResponse(204, $expHeaders);
$res = HTTP::respEmpty(204, $resHeaders);
$exp = HTTP::respEmpty(204, $expHeaders);
$act = $r->corsApply($res, $req);
$this->assertMessage($exp, $act);
}
@ -268,21 +268,21 @@ class TestREST extends \JKingWeb\Arsse\Test\AbstractTest {
$stream = fopen("php://memory", "w+b");
fwrite($stream, "ook");
return [
[new EmptyResponse(204), new EmptyResponse(204)],
[new EmptyResponse(401), new EmptyResponse(401, ['WWW-Authenticate' => "Fake Value"])],
[new EmptyResponse(204, ['Allow' => "PUT"]), new EmptyResponse(204, ['Allow' => "PUT, OPTIONS"])],
[new EmptyResponse(204, ['Allow' => "PUT, OPTIONS"]), new EmptyResponse(204, ['Allow' => "PUT, OPTIONS"])],
[new EmptyResponse(204, ['Allow' => "PUT,OPTIONS"]), new EmptyResponse(204, ['Allow' => "PUT, OPTIONS"])],
[new EmptyResponse(204, ['Allow' => ["PUT", "OPTIONS"]]), new EmptyResponse(204, ['Allow' => "PUT, OPTIONS"])],
[new EmptyResponse(204, ['Allow' => ["PUT, DELETE", "OPTIONS"]]), new EmptyResponse(204, ['Allow' => "PUT, DELETE, OPTIONS"])],
[new EmptyResponse(204, ['Allow' => "HEAD,GET"]), new EmptyResponse(204, ['Allow' => "HEAD, GET, OPTIONS"])],
[new EmptyResponse(204, ['Allow' => "GET"]), new EmptyResponse(204, ['Allow' => "GET, HEAD, OPTIONS"])],
[new TextResponse("ook", 200), new TextResponse("ook", 200, ['Content-Length' => "3"])],
[new TextResponse("", 200), new TextResponse("", 200, ['Content-Length' => "0"])],
[new TextResponse("ook", 404), new TextResponse("ook", 404, ['Content-Length' => "3"])],
[new TextResponse("", 404), new TextResponse("", 404)],
[new Response($stream, 200), new Response($stream, 200, ['Content-Length' => "3"]), new Request("", "GET")],
[new Response($stream, 200), new EmptyResponse(200, ['Content-Length' => "3"]), new Request("", "HEAD")],
[HTTP::respEmpty(204), HTTP::respEmpty(204)],
[HTTP::respEmpty(401), HTTP::respEmpty(401, ['WWW-Authenticate' => "Fake Value"])],
[HTTP::respEmpty(204, ['Allow' => "PUT"]), HTTP::respEmpty(204, ['Allow' => "PUT, OPTIONS"])],
[HTTP::respEmpty(204, ['Allow' => "PUT, OPTIONS"]), HTTP::respEmpty(204, ['Allow' => "PUT, OPTIONS"])],
[HTTP::respEmpty(204, ['Allow' => "PUT,OPTIONS"]), HTTP::respEmpty(204, ['Allow' => "PUT, OPTIONS"])],
[HTTP::respEmpty(204, ['Allow' => ["PUT", "OPTIONS"]]), HTTP::respEmpty(204, ['Allow' => "PUT, OPTIONS"])],
[HTTP::respEmpty(204, ['Allow' => ["PUT, DELETE", "OPTIONS"]]), HTTP::respEmpty(204, ['Allow' => "PUT, DELETE, OPTIONS"])],
[HTTP::respEmpty(204, ['Allow' => "HEAD,GET"]), HTTP::respEmpty(204, ['Allow' => "HEAD, GET, OPTIONS"])],
[HTTP::respEmpty(204, ['Allow' => "GET"]), HTTP::respEmpty(204, ['Allow' => "GET, HEAD, OPTIONS"])],
[new TextResponse("ook", 200), new TextResponse("ook", 200, ['Content-Length' => "3"])],
[new TextResponse("", 200), new TextResponse("", 200, ['Content-Length' => "0"])],
[new TextResponse("ook", 404), new TextResponse("ook", 404, ['Content-Length' => "3"])],
[new TextResponse("", 404), new TextResponse("", 404)],
[new Response($stream, 200), new Response($stream, 200, ['Content-Length' => "3"]), new Request("", "GET")],
[new Response($stream, 200), HTTP::respEmpty(200, ['Content-Length' => "3"]), new Request("", "HEAD")],
];
}
@ -297,7 +297,7 @@ class TestREST extends \JKingWeb\Arsse\Test\AbstractTest {
});
if ($called) {
$hMock = $this->mock($class);
$hMock->dispatch->returns(new EmptyResponse(204));
$hMock->dispatch->returns(HTTP::respEmpty(204));
$this->objMock->get->with($class)->returns($hMock);
Arsse::$obj = $this->objMock->get();
}

14
tests/cases/REST/TinyTinyRSS/TestAPI.php

@ -11,6 +11,7 @@ use JKingWeb\Arsse\User;
use JKingWeb\Arsse\Database;
use JKingWeb\Arsse\Test\Result;
use JKingWeb\Arsse\Misc\Date;
use JKingWeb\Arsse\Misc\HTTP;
use JKingWeb\Arsse\Context\Context;
use JKingWeb\Arsse\Db\ExceptionInput;
use JKingWeb\Arsse\Db\Transaction;
@ -18,7 +19,6 @@ 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;
use Laminas\Diactoros\Response\EmptyResponse;
/** @covers \JKingWeb\Arsse\REST\TinyTinyRSS\API<extended>
* @covers \JKingWeb\Arsse\REST\TinyTinyRSS\Exception */
@ -188,12 +188,12 @@ LONG_STRING;
$this->assertMessage($exp, $this->req(null, "POST", "", ""));
$this->assertMessage($exp, $this->req(null, "POST", "/", ""));
$this->assertMessage($exp, $this->req(null, "POST", "/index.php", ""));
$exp = new EmptyResponse(404);
$exp = HTTP::respEmpty(404);
$this->assertMessage($exp, $this->req(null, "POST", "/bad/path", ""));
}
public function testHandleOptionsRequest(): void {
$exp = new EmptyResponse(204, [
$exp = HTTP::respEmpty(204, [
'Allow' => "POST",
'Accept' => "application/json, text/json",
]);
@ -215,7 +215,7 @@ LONG_STRING;
$this->userMock->auth->with("jane.doe@example.com", "superman")->returns(true);
$this->dbMock->sessionCreate->with("john.doe@example.com")->returns("PriestsOfSyrinx", "SolarFederation");
$this->dbMock->sessionCreate->with("jane.doe@example.com")->returns("ClockworkAngels", "SevenCitiesOfGold");
if ($sessions instanceof EmptyResponse) {
if ($sessions instanceof ResponseInterface) {
$exp1 = $sessions;
$exp2 = $sessions;
} elseif ($sessions) {
@ -260,7 +260,7 @@ LONG_STRING;
'op' => "isLoggedIn",
'sid' => $data,
];
if ($result instanceof EmptyResponse) {
if ($result instanceof ResponseInterface) {
$exp1 = $result;
$exp2 = null;
} elseif ($result) {
@ -333,7 +333,7 @@ LONG_STRING;
'userHTTPAuthRequired' => true,
'userSessionEnforced' => false,
];
$http401 = new EmptyResponse(401);
$http401 = HTTP::respEmpty(401);
if ($type === "login") {
return [
// conf, user, data, result
@ -532,7 +532,7 @@ LONG_STRING;
'user' => $this->userId,
'password' => "secret",
];
$exp = new EmptyResponse(500);
$exp = HTTP::respEmpty(500);
$this->assertMessage($exp, $this->req($data));
}

24
tests/cases/REST/TinyTinyRSS/TestIcon.php

@ -9,10 +9,10 @@ namespace JKingWeb\Arsse\TestCase\REST\TinyTinyRSS;
use JKingWeb\Arsse\Arsse;
use JKingWeb\Arsse\User;
use JKingWeb\Arsse\Database;
use JKingWeb\Arsse\Misc\HTTP;
use JKingWeb\Arsse\Db\ExceptionInput;
use JKingWeb\Arsse\REST\TinyTinyRSS\Icon;
use Psr\Http\Message\ResponseInterface;
use Laminas\Diactoros\Response\EmptyResponse as Response;
/** @covers \JKingWeb\Arsse\REST\TinyTinyRSS\Icon<extended> */
class TestIcon extends \JKingWeb\Arsse\Test\AbstractTest {
@ -51,21 +51,21 @@ class TestIcon extends \JKingWeb\Arsse\Test\AbstractTest {
$this->dbMock->subscriptionIcon->with($this->anything(), 2112, false)->returns(['url' => "http://example.net/logo.png"]);
$this->dbMock->subscriptionIcon->with($this->anything(), 1337, false)->returns(['url' => "http://example.org/icon.gif\r\nLocation: http://bad.example.com/"]);
// these requests should succeed
$exp = new Response(301, ['Location' => "http://example.com/favicon.ico"]);
$exp = HTTP::respEmpty(301, ['Location' => "http://example.com/favicon.ico"]);
$this->assertMessage($exp, $this->req("42.ico"));
$exp = new Response(301, ['Location' => "http://example.net/logo.png"]);
$exp = HTTP::respEmpty(301, ['Location' => "http://example.net/logo.png"]);
$this->assertMessage($exp, $this->req("2112.ico"));
$exp = new Response(301, ['Location' => "http://example.org/icon.gif"]);
$exp = HTTP::respEmpty(301, ['Location' => "http://example.org/icon.gif"]);
$this->assertMessage($exp, $this->req("1337.ico"));
// these requests should fail
$exp = new Response(404);
$exp = HTTP::respEmpty(404);
$this->assertMessage($exp, $this->req("ook.ico"));
$this->assertMessage($exp, $this->req("ook"));
$this->assertMessage($exp, $this->req("47.ico"));
$this->assertMessage($exp, $this->req("2112.png"));
$this->assertMessage($exp, $this->req("1123.ico"));
// only GET is allowed
$exp = new Response(405, ['Allow' => "GET"]);
$exp = HTTP::respEmpty(405, ['Allow' => "GET"]);
$this->assertMessage($exp, $this->req("2112.ico", "PUT"));
}
@ -79,32 +79,32 @@ class TestIcon extends \JKingWeb\Arsse\Test\AbstractTest {
$this->dbMock->subscriptionIcon->with(null, 2112, false)->returns($url);
$this->dbMock->subscriptionIcon->with(null, 1337, false)->returns($url);
// these requests should succeed
$exp = new Response(301, ['Location' => "http://example.org/icon.gif"]);
$exp = HTTP::respEmpty(301, ['Location' => "http://example.org/icon.gif"]);
$this->assertMessage($exp, $this->req("42.ico"));
$this->assertMessage($exp, $this->req("2112.ico"));
$this->assertMessage($exp, $this->req("1337.ico"));
$this->assertMessage($exp, $this->reqAuth("42.ico"));
$this->assertMessage($exp, $this->reqAuth("1337.ico"));
// these requests should fail
$exp = new Response(404);
$exp = HTTP::respEmpty(404);
$this->assertMessage($exp, $this->reqAuth("2112.ico"));
$exp = new Response(401);
$exp = HTTP::respEmpty(401);
$this->assertMessage($exp, $this->reqAuthFailed("42.ico"));
$this->assertMessage($exp, $this->reqAuthFailed("1337.ico"));
// with HTTP auth required, only authenticated requests should succeed
self::setConf(['userHTTPAuthRequired' => true]);
$exp = new Response(301, ['Location' => "http://example.org/icon.gif"]);
$exp = HTTP::respEmpty(301, ['Location' => "http://example.org/icon.gif"]);
$this->assertMessage($exp, $this->reqAuth("42.ico"));
$this->assertMessage($exp, $this->reqAuth("1337.ico"));
// anything else should fail
$exp = new Response(401);
$exp = HTTP::respEmpty(401);
$this->assertMessage($exp, $this->req("42.ico"));
$this->assertMessage($exp, $this->req("2112.ico"));
$this->assertMessage($exp, $this->req("1337.ico"));
$this->assertMessage($exp, $this->reqAuthFailed("42.ico"));
$this->assertMessage($exp, $this->reqAuthFailed("1337.ico"));
// resources for the wrtong user should still fail, too
$exp = new Response(404);
$exp = HTTP::respEmpty(404);
$this->assertMessage($exp, $this->reqAuth("2112.ico"));
}
}

Loading…
Cancel
Save