diff --git a/lib/REST/AbstractHandler.php b/lib/REST/AbstractHandler.php
index 273e61a4..756ebe7d 100644
--- a/lib/REST/AbstractHandler.php
+++ b/lib/REST/AbstractHandler.php
@@ -8,10 +8,12 @@ namespace JKingWeb\Arsse\REST;
use JKingWeb\Arsse\Misc\Date;
use JKingWeb\Arsse\Misc\ValueInfo;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\ResponseInterface;
abstract class AbstractHandler implements Handler {
abstract public function __construct();
- abstract public function dispatch(Request $req): \Psr\Http\Message\ResponseInterface;
+ abstract public function dispatch(ServerRequestInterface $req): ResponseInterface;
protected function fieldMapNames(array $data, array $map): array {
$out = [];
diff --git a/lib/REST/Handler.php b/lib/REST/Handler.php
index 4d4904a3..3b2c88e6 100644
--- a/lib/REST/Handler.php
+++ b/lib/REST/Handler.php
@@ -6,7 +6,10 @@
declare(strict_types=1);
namespace JKingWeb\Arsse\REST;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
interface Handler {
public function __construct();
- public function dispatch(Request $req): \Psr\Http\Message\ResponseInterface;
+ public function dispatch(ServerRequestInterface $req): ResponseInterface;
}
diff --git a/lib/REST/NextCloudNews/V1_2.php b/lib/REST/NextCloudNews/V1_2.php
index bf395512..fe4b974e 100644
--- a/lib/REST/NextCloudNews/V1_2.php
+++ b/lib/REST/NextCloudNews/V1_2.php
@@ -15,7 +15,9 @@ use JKingWeb\Arsse\Misc\ValueInfo;
use JKingWeb\Arsse\AbstractException;
use JKingWeb\Arsse\Db\ExceptionInput;
use JKingWeb\Arsse\Feed\Exception as FeedException;
-use \Psr\Http\Message\ResponseInterface;
+use JKingWeb\Arsse\REST\Target;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Response\JsonResponse as Response;
use Zend\Diactoros\Response\EmptyResponse;
@@ -43,53 +45,61 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
'items' => ValueInfo::T_MIXED | ValueInfo::M_ARRAY,
];
protected $paths = [
- 'folders' => ['GET' => "folderList", 'POST' => "folderAdd"],
- 'folders/1' => ['PUT' => "folderRename", 'DELETE' => "folderRemove"],
- 'folders/1/read' => ['PUT' => "folderMarkRead"],
- 'feeds' => ['GET' => "subscriptionList", 'POST' => "subscriptionAdd"],
- 'feeds/1' => ['DELETE' => "subscriptionRemove"],
- 'feeds/1/move' => ['PUT' => "subscriptionMove"],
- 'feeds/1/rename' => ['PUT' => "subscriptionRename"],
- 'feeds/1/read' => ['PUT' => "subscriptionMarkRead"],
- 'feeds/all' => ['GET' => "feedListStale"],
- 'feeds/update' => ['GET' => "feedUpdate"],
- 'items' => ['GET' => "articleList"],
- 'items/updated' => ['GET' => "articleList"],
- 'items/read' => ['PUT' => "articleMarkReadAll"],
- 'items/1/read' => ['PUT' => "articleMarkRead"],
- 'items/1/unread' => ['PUT' => "articleMarkRead"],
- 'items/read/multiple' => ['PUT' => "articleMarkReadMulti"],
- 'items/unread/multiple' => ['PUT' => "articleMarkReadMulti"],
- 'items/1/1/star' => ['PUT' => "articleMarkStarred"],
- 'items/1/1/unstar' => ['PUT' => "articleMarkStarred"],
- 'items/star/multiple' => ['PUT' => "articleMarkStarredMulti"],
- 'items/unstar/multiple' => ['PUT' => "articleMarkStarredMulti"],
- 'cleanup/before-update' => ['GET' => "cleanupBefore"],
- 'cleanup/after-update' => ['GET' => "cleanupAfter"],
- 'version' => ['GET' => "serverVersion"],
- 'status' => ['GET' => "serverStatus"],
- 'user' => ['GET' => "userStatus"],
+ '/folders' => ['GET' => "folderList", 'POST' => "folderAdd"],
+ '/folders/1' => ['PUT' => "folderRename", 'DELETE' => "folderRemove"],
+ '/folders/1/read' => ['PUT' => "folderMarkRead"],
+ '/feeds' => ['GET' => "subscriptionList", 'POST' => "subscriptionAdd"],
+ '/feeds/1' => ['DELETE' => "subscriptionRemove"],
+ '/feeds/1/move' => ['PUT' => "subscriptionMove"],
+ '/feeds/1/rename' => ['PUT' => "subscriptionRename"],
+ '/feeds/1/read' => ['PUT' => "subscriptionMarkRead"],
+ '/feeds/all' => ['GET' => "feedListStale"],
+ '/feeds/update' => ['GET' => "feedUpdate"],
+ '/items' => ['GET' => "articleList"],
+ '/items/updated' => ['GET' => "articleList"],
+ '/items/read' => ['PUT' => "articleMarkReadAll"],
+ '/items/1/read' => ['PUT' => "articleMarkRead"],
+ '/items/1/unread' => ['PUT' => "articleMarkRead"],
+ '/items/read/multiple' => ['PUT' => "articleMarkReadMulti"],
+ '/items/unread/multiple' => ['PUT' => "articleMarkReadMulti"],
+ '/items/1/1/star' => ['PUT' => "articleMarkStarred"],
+ '/items/1/1/unstar' => ['PUT' => "articleMarkStarred"],
+ '/items/star/multiple' => ['PUT' => "articleMarkStarredMulti"],
+ '/items/unstar/multiple' => ['PUT' => "articleMarkStarredMulti"],
+ '/cleanup/before-update' => ['GET' => "cleanupBefore"],
+ '/cleanup/after-update' => ['GET' => "cleanupAfter"],
+ '/version' => ['GET' => "serverVersion"],
+ '/status' => ['GET' => "serverStatus"],
+ '/user' => ['GET' => "userStatus"],
];
public function __construct() {
}
- public function dispatch(\JKingWeb\Arsse\REST\Request $req): ResponseInterface {
+ public function dispatch(ServerRequestInterface $req): ResponseInterface {
// try to authenticate
if (!Arsse::$user->authHTTP()) {
return new EmptyResponse(401, ['WWW-Authenticate' => 'Basic realm="'.self::REALM.'"']);
}
+ // explode and normalize the URL path
+ $target = new Target($req->getRequestTarget());
// handle HTTP OPTIONS requests
- if ($req->method=="OPTIONS") {
- return $this->handleHTTPOptions($req->paths);
+ if ($req->getMethod()=="OPTIONS") {
+ return $this->handleHTTPOptions((string) $target);
}
// normalize the input
- if ($req->body) {
+ $data = (string) $req->getBody();
+ $type = "";
+ if ($req->hasHeader("Content-Type")) {
+ $type = $req->getHeader("Content-Type");
+ $type = array_pop($type);
+ }
+ if ($data) {
// if the entity body is not JSON according to content type, return "415 Unsupported Media Type"
- if (!preg_match("<^application/json\b|^$>", $req->type)) {
+ if (!preg_match("<^application/json\b|^$>", $type)) {
return new EmptyResponse(415, ['Accept' => "application/json"]);
}
- $data = @json_decode($req->body, true);
+ $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);
@@ -98,10 +108,10 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
$data = [];
}
// FIXME: Do query parameters take precedence in NextCloud? Is there a conflict error when values differ?
- $data = $this->normalizeInput(array_merge($data, $req->query), $this->validInput, "unix");
+ $data = $this->normalizeInput(array_merge($data, $req->getQueryParams()), $this->validInput, "unix");
// check to make sure the requested function is implemented
try {
- $func = $this->chooseCall($req->paths, $req->method);
+ $func = $this->chooseCall((string) $target, $req->getMethod());
} catch (Exception404 $e) {
return new EmptyResponse(404);
} catch (Exception405 $e) {
@@ -112,7 +122,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
}
// dispatch
try {
- return $this->$func($req->paths, $data);
+ return $this->$func($target->path, $data);
// @codeCoverageIgnoreStart
} catch (Exception $e) {
// if there was a REST exception return 400
@@ -124,19 +134,24 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
// @codeCoverageIgnoreEnd
}
- protected function normalizePath(array $url): string {
- // any URL components which are database IDs (integers greater than zero) should be replaced with "1", for easier comparison (we don't care about the specific ID)
- for ($a = 0; $a < sizeof($url); $a++) {
- if (ValueInfo::id($url[$a])) {
- $url[$a] = "1";
+ protected function normalizePathIds(string $url): string {
+ // first parse the URL and perform syntactic normalization
+ $target = new Target($url);
+ // any path components which are database IDs (integers greater than zero) should be replaced with "1", for easier comparison (we don't care about the specific ID)
+ for ($a = 0; $a < sizeof($target->path); $a++) {
+ if (ValueInfo::id($target->path[$a])) {
+ $target->path[$a] = "1";
}
}
- return implode("/", $url);
+ // discard any fragment ID (there shouldn't be any) and query string (the query is available in the request itself)
+ $target->fragment = "";
+ $target->query = "";
+ return (string) $target;
}
- protected function chooseCall(array $url, string $method): string {
- // normalize the URL path
- $url = $this->normalizePath($url);
+ protected function chooseCall(string $url, string $method): string {
+ // // normalize the URL path: change any IDs to 1 for easier comparison
+ $url = $this->normalizePathIds($url);
// normalize the HTTP method to uppercase
$method = strtoupper($method);
// we now evaluate the supplied URL against every supported path for the selected scope
@@ -244,9 +259,9 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
return $article;
}
- protected function handleHTTPOptions(array $url): ResponseInterface {
- // normalize the URL path
- $url = $this->normalizePath($url);
+ protected function handleHTTPOptions(string $url): ResponseInterface {
+ // normalize the URL path: change any IDs to 1 for easier comparison
+ $url = $this->normalizePathIDs($url);
if (isset($this->paths[$url])) {
// if the path is supported, respond with the allowed methods and other metadata
$allowed = array_keys($this->paths[$url]);
diff --git a/lib/REST/NextCloudNews/Versions.php b/lib/REST/NextCloudNews/Versions.php
index 1f97e77d..77924bd4 100644
--- a/lib/REST/NextCloudNews/Versions.php
+++ b/lib/REST/NextCloudNews/Versions.php
@@ -6,6 +6,8 @@
declare(strict_types=1);
namespace JKingWeb\Arsse\REST\NextCloudNews;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Response\JsonResponse as Response;
use Zend\Diactoros\Response\EmptyResponse;
@@ -13,24 +15,26 @@ class Versions implements \JKingWeb\Arsse\REST\Handler {
public function __construct() {
}
- public function dispatch(\JKingWeb\Arsse\REST\Request $req): \Psr\Http\Message\ResponseInterface {
- if (!preg_match("<^/?$>", $req->path)) {
- // if the request path is an empty string or just a slash, the client is probably trying a version we don't support
+ public function dispatch(ServerRequestInterface $req): ResponseInterface {
+ if (!preg_match("<^/?$>", $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);
- } elseif ($req->method=="OPTIONS") {
- // if the request method is OPTIONS, respond accordingly
- return new EmptyResponse(204, ['Allow' => "HEAD,GET"]);
- } elseif ($req->method != "GET") {
- // if a method other than GET was used, this is an error
- return new EmptyResponse(405, ['Allow' => "HEAD,GET"]);
- } else {
- // otherwise return the supported versions
- $out = [
- 'apiLevels' => [
- 'v1-2',
- ]
- ];
- return new Response($out);
+ }
+ switch ($req->getMethod()) {
+ case "OPTIONS":
+ // if the request method is OPTIONS, respond accordingly
+ return new EmptyResponse(204, ['Allow' => "HEAD,GET"]);
+ case "GET":
+ // otherwise return the supported versions
+ $out = [
+ 'apiLevels' => [
+ 'v1-2',
+ ]
+ ];
+ return new Response($out);
+ default:
+ // if any other method was used, this is an error
+ return new EmptyResponse(405, ['Allow' => "HEAD,GET"]);
}
}
}
diff --git a/lib/REST/Request.php b/lib/REST/Request.php
deleted file mode 100644
index 157027a1..00000000
--- a/lib/REST/Request.php
+++ /dev/null
@@ -1,89 +0,0 @@
-method = strtoupper($method);
- $this->url = $url;
- $this->body = $body;
- $this->type = $contentType;
- if ($this->method=="HEAD") {
- $this->head = true;
- $this->method = "GET";
- }
- $this->refreshURL();
- }
-
- public function refreshURL() {
- $url = $this->parseURL($this->url);
- $this->path = $url['path'];
- $this->paths = $url['paths'];
- $this->query = $url['query'];
- }
-
- protected function parseURL(string $url): array {
- // split the query string from the path
- $parts = explode("?", $url);
- $out = ['path' => $parts[0], 'paths' => [''], 'query' => []];
- // if there is a query string, parse it
- if (isset($parts[1])) {
- // split along & to get key-value pairs
- $query = explode("&", $parts[1]);
- for ($a = 0; $a < sizeof($query); $a++) {
- // split each pair, into no more than two parts
- $data = explode("=", $query[$a], 2);
- // decode the key
- $key = rawurldecode($data[0]);
- // decode the value if there is one
- $value = "";
- if (isset($data[1])) {
- $value = rawurldecode($data[1]);
- }
- // add the pair to the query output, overwriting earlier values for the same key, is present
- $out['query'][$key] = $value;
- }
- }
- // also include the path as a set of decoded elements
- // if the path is an empty string or just / nothing needs be done
- if (!in_array($out['path'], ["/",""])) {
- $paths = explode("/", $out['path']);
- // remove the first and last empty elements, if present (they are artefacts of the splitting; others should remain)
- if (!strlen($paths[0])) {
- array_shift($paths);
- }
- if (!strlen($paths[sizeof($paths)-1])) {
- array_pop($paths);
- }
- // %-decode each path element
- $paths = array_map(function ($v) {
- return rawurldecode($v);
- }, $paths);
- $out['paths'] = $paths;
- }
- return $out;
- }
-}
diff --git a/lib/REST/TinyTinyRSS/API.php b/lib/REST/TinyTinyRSS/API.php
index 24649545..6f94bb4b 100644
--- a/lib/REST/TinyTinyRSS/API.php
+++ b/lib/REST/TinyTinyRSS/API.php
@@ -19,6 +19,8 @@ use JKingWeb\Arsse\ExceptionType;
use JKingWeb\Arsse\Db\ExceptionInput;
use JKingWeb\Arsse\Db\ResultEmpty;
use JKingWeb\Arsse\Feed\Exception as FeedException;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Response\JsonResponse as Response;
use Zend\Diactoros\Response\EmptyResponse;
@@ -89,21 +91,22 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
public function __construct() {
}
- public function dispatch(\JKingWeb\Arsse\REST\Request $req): \Psr\Http\Message\ResponseInterface {
- if (!preg_match("<^(?:/(?:index\.php)?)?$>", $req->path)) {
+ public function dispatch(ServerRequestInterface $req): ResponseInterface {
+ if (!preg_match("<^(?:/(?:index\.php)?)?$>", $req->getRequestTarget())) {
// reject paths other than the index
return new EmptyResponse(404);
}
- if ($req->method=="OPTIONS") {
+ 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, [
'Allow' => "POST",
'Accept' => "application/json, text/json",
]);
}
- if ($req->body) {
+ $data = (string) $req->getBody();
+ if ($data) {
// only JSON entities are allowed, but Content-Type is ignored, as is request method
- $data = @json_decode($req->body, true);
+ $data = @json_decode($data, true);
if (json_last_error() != \JSON_ERROR_NONE || !is_array($data)) {
return new Response(self::FATAL_ERR);
}
diff --git a/lib/REST/TinyTinyRSS/Icon.php b/lib/REST/TinyTinyRSS/Icon.php
index 3641f17c..ef2d0c07 100644
--- a/lib/REST/TinyTinyRSS/Icon.php
+++ b/lib/REST/TinyTinyRSS/Icon.php
@@ -7,17 +7,19 @@ declare(strict_types=1);
namespace JKingWeb\Arsse\REST\TinyTinyRSS;
use JKingWeb\Arsse\Arsse;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Response\EmptyResponse as Response;
class Icon extends \JKingWeb\Arsse\REST\AbstractHandler {
public function __construct() {
}
- public function dispatch(\JKingWeb\Arsse\REST\Request $req): \Psr\Http\Message\ResponseInterface {
- if ($req->method != "GET") {
+ public function dispatch(ServerRequestInterface $req): ResponseInterface {
+ if ($req->getMethod() != "GET") {
// only GET requests are allowed
return new Response(405, ['Allow' => "GET"]);
- } elseif (!preg_match("<^(\d+)\.ico$>", $req->url, $match) || !((int) $match[1])) {
+ } elseif (!preg_match("<^(\d+)\.ico$>", $req->getRequestTarget(), $match) || !((int) $match[1])) {
return new Response(404);
}
$url = Arsse::$db->subscriptionFavicon((int) $match[1]);
diff --git a/tests/cases/REST/NextCloudNews/TestV1_2.php b/tests/cases/REST/NextCloudNews/TestV1_2.php
index d16e1011..fd201606 100644
--- a/tests/cases/REST/NextCloudNews/TestV1_2.php
+++ b/tests/cases/REST/NextCloudNews/TestV1_2.php
@@ -11,13 +11,14 @@ use JKingWeb\Arsse\Conf;
use JKingWeb\Arsse\User;
use JKingWeb\Arsse\Database;
use JKingWeb\Arsse\Service;
-use JKingWeb\Arsse\REST\Request;
use JKingWeb\Arsse\Test\Result;
use JKingWeb\Arsse\Misc\Date;
use JKingWeb\Arsse\Misc\Context;
use JKingWeb\Arsse\Db\ExceptionInput;
use JKingWeb\Arsse\Db\Transaction;
use JKingWeb\Arsse\REST\NextCloudNews\V1_2;
+use Psr\Http\Message\ResponseInterface;
+use Zend\Diactoros\ServerRequest;
use Zend\Diactoros\Response\JsonResponse as Response;
use Zend\Diactoros\Response\EmptyResponse;
use Phake;
@@ -300,6 +301,40 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
],
];
+ protected function req(string $method, string $target, string $data = "", array $headers = []): ResponseInterface {
+ $url = "/index.php/apps/news/api/v1-2".$target;
+ $server = [
+ 'REQUEST_METHOD' => $method,
+ 'REQUEST_URI' => $url,
+ 'PHP_AUTH_USER' => "john.doe@example.com",
+ 'PHP_AUTH_PW' => "secret",
+ 'REMOTE_USER' => "john.doe@example.com",
+ ];
+ if (strlen($data)) {
+ $server['HTTP_CONTENT_TYPE'] = "application/json";
+ }
+ $req = new ServerRequest($server, [], $url, $method, "php://memory");
+ foreach($headers as $key => $value) {
+ if (!is_null($value)) {
+ $req = $req->withHeader($key, $value);
+ } else {
+ $req = $req->withoutHeader($key);
+ }
+ }
+ if (strlen($data)) {
+ $body = $req->getBody();
+ $body->write($data);
+ $req = $req->withBody($body);
+ }
+ $q = $req->getUri()->getQuery();
+ if (strlen($q)) {
+ parse_str($q, $q);
+ $req = $req->withQueryParams($q);
+ }
+ $req = $req->withRequestTarget($target);
+ return $this->h->dispatch($req);
+ }
+
public function setUp() {
$this->clearData();
Arsse::$conf = new Conf();
@@ -321,7 +356,7 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
public function testSendAuthenticationChallenge() {
Phake::when(Arsse::$user)->authHTTP->thenReturn(false);
$exp = new EmptyResponse(401, ['WWW-Authenticate' => 'Basic realm="'.V1_2::REALM.'"']);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/")));
+ $this->assertResponse($exp, $this->req("GET", "/"));
}
public function testRespondToInvalidPaths() {
@@ -359,22 +394,23 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
foreach ($errs[404] as $req) {
$exp = new EmptyResponse(404);
list($method, $path) = $req;
- $this->assertResponse($exp, $this->h->dispatch(new Request($method, $path)), "$method call to $path did not return 404.");
+ $this->assertResponse($exp, $this->req($method, $path), "$method call to $path did not return 404.");
}
foreach ($errs[405] as $allow => $cases) {
$exp = new EmptyResponse(405, ['Allow' => $allow]);
foreach ($cases as $req) {
list($method, $path) = $req;
- $this->assertResponse($exp, $this->h->dispatch(new Request($method, $path)), "$method call to $path did not return 405.");
+ $this->assertResponse($exp, $this->req($method, $path), "$method call to $path did not return 405.");
}
}
}
public function testRespondToInvalidInputTypes() {
$exp = new EmptyResponse(415, ['Accept' => "application/json"]);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", '', 'application/xml')));
+ $this->assertResponse($exp, $this->req("PUT", "/folders/1", '', ['Content-Type' => "application/xml"]));
$exp = new EmptyResponse(400);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", '', 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/folders/1", ''));
+ $this->assertResponse($exp, $this->req("PUT", "/folders/1", '', ['Content-Type' => null]));
}
public function testRespondToOptionsRequests() {
@@ -382,19 +418,19 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
'Allow' => "HEAD,GET,POST",
'Accept' => "application/json",
]);
- $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", "/feeds")));
+ $this->assertResponse($exp, $this->req("OPTIONS", "/feeds"));
$exp = new EmptyResponse(204, [
'Allow' => "DELETE",
'Accept' => "application/json",
]);
- $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", "/feeds/2112")));
+ $this->assertResponse($exp, $this->req("OPTIONS", "/feeds/2112"));
$exp = new EmptyResponse(204, [
'Allow' => "HEAD,GET",
'Accept' => "application/json",
]);
- $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", "/user")));
+ $this->assertResponse($exp, $this->req("OPTIONS", "/user"));
$exp = new EmptyResponse(404);
- $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", "/invalid/path")));
+ $this->assertResponse($exp, $this->req("OPTIONS", "/invalid/path"));
}
public function testListFolders() {
@@ -408,9 +444,9 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
];
Phake::when(Arsse::$db)->folderList(Arsse::$user->id, null, false)->thenReturn(new Result([]))->thenReturn(new Result($list));
$exp = new Response(['folders' => []]);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/folders")));
+ $this->assertResponse($exp, $this->req("GET", "/folders"));
$exp = new Response(['folders' => $out]);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/folders")));
+ $this->assertResponse($exp, $this->req("GET", "/folders"));
}
public function testAddAFolder() {
@@ -438,33 +474,33 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, ['name' => " "])->thenThrow(new ExceptionInput("whitespace"));
// correctly add two folders, using different means
$exp = new Response(['folders' => [$out[0]]]);
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", json_encode($in[0]), 'application/json')));
+ $this->assertResponse($exp, $this->req("POST", "/folders", json_encode($in[0])));
$exp = new Response(['folders' => [$out[1]]]);
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders?name=Hardware")));
+ $this->assertResponse($exp, $this->req("POST", "/folders?name=Hardware"));
Phake::verify(Arsse::$db)->folderAdd(Arsse::$user->id, $in[0]);
Phake::verify(Arsse::$db)->folderAdd(Arsse::$user->id, $in[1]);
Phake::verify(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 1);
Phake::verify(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 2);
// test bad folder names
$exp = new EmptyResponse(422);
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", '{"name":""}', 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", '{"name":" "}', 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", '{"name":{}}', 'application/json')));
+ $this->assertResponse($exp, $this->req("POST", "/folders"));
+ $this->assertResponse($exp, $this->req("POST", "/folders", '{"name":""}'));
+ $this->assertResponse($exp, $this->req("POST", "/folders", '{"name":" "}'));
+ $this->assertResponse($exp, $this->req("POST", "/folders", '{"name":{}}'));
// try adding the same two folders again
$exp = new EmptyResponse(409);
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders?name=Software")));
+ $this->assertResponse($exp, $this->req("POST", "/folders?name=Software"));
$exp = new EmptyResponse(409);
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", json_encode($in[1]), 'application/json')));
+ $this->assertResponse($exp, $this->req("POST", "/folders", json_encode($in[1])));
}
public function testRemoveAFolder() {
Phake::when(Arsse::$db)->folderRemove(Arsse::$user->id, 1)->thenReturn(true)->thenThrow(new ExceptionInput("subjectMissing"));
$exp = new EmptyResponse(204);
- $this->assertResponse($exp, $this->h->dispatch(new Request("DELETE", "/folders/1")));
+ $this->assertResponse($exp, $this->req("DELETE", "/folders/1"));
// fail on the second invocation because it no longer exists
$exp = new EmptyResponse(404);
- $this->assertResponse($exp, $this->h->dispatch(new Request("DELETE", "/folders/1")));
+ $this->assertResponse($exp, $this->req("DELETE", "/folders/1"));
Phake::verify(Arsse::$db, Phake::times(2))->folderRemove(Arsse::$user->id, 1);
}
@@ -483,17 +519,17 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
Phake::when(Arsse::$db)->folderPropertiesSet(Arsse::$user->id, 1, $in[4])->thenReturn(true); // this should be stopped by the handler before the request gets to the database
Phake::when(Arsse::$db)->folderPropertiesSet(Arsse::$user->id, 3, $this->anything())->thenThrow(new ExceptionInput("subjectMissing")); // folder ID 3 does not exist
$exp = new EmptyResponse(204);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[0]), 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/folders/1", json_encode($in[0])));
$exp = new EmptyResponse(409);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/2", json_encode($in[1]), 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/folders/2", json_encode($in[1])));
$exp = new EmptyResponse(422);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[2]), 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/folders/1", json_encode($in[2])));
$exp = new EmptyResponse(422);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[3]), 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/folders/1", json_encode($in[3])));
$exp = new EmptyResponse(422);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[4]), 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/folders/1", json_encode($in[4])));
$exp = new EmptyResponse(404);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/3", json_encode($in[0]), 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/folders/3", json_encode($in[0])));
}
public function testRetrieveServerVersion() {
@@ -501,7 +537,7 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
'version' => V1_2::VERSION,
'arsse_version' => Arsse::VERSION,
]);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/version")));
+ $this->assertResponse($exp, $this->req("GET", "/version"));
}
public function testListSubscriptions() {
@@ -518,9 +554,9 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
Phake::when(Arsse::$db)->articleStarred(Arsse::$user->id)->thenReturn(['total' => 0])->thenReturn(['total' => 5]);
Phake::when(Arsse::$db)->editionLatest(Arsse::$user->id)->thenReturn(0)->thenReturn(4758915);
$exp = new Response($exp1);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds")));
+ $this->assertResponse($exp, $this->req("GET", "/feeds"));
$exp = new Response($exp2);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds")));
+ $this->assertResponse($exp, $this->req("GET", "/feeds"));
}
public function testAddASubscription() {
@@ -553,31 +589,31 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
Phake::when(Arsse::$db)->subscriptionAdd(Arsse::$user->id, "http://example.net/news.atom")->thenThrow(new \JKingWeb\Arsse\Feed\Exception("http://example.net/news.atom", new \PicoFeed\Client\InvalidUrlException()))->thenReturn(47);
// add the subscriptions
$exp = new Response($out[0]);
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[0]), 'application/json')));
+ $this->assertResponse($exp, $this->req("POST", "/feeds", json_encode($in[0])));
$exp = new Response($out[1]);
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[1]), 'application/json')));
+ $this->assertResponse($exp, $this->req("POST", "/feeds", json_encode($in[1])));
// try to add them a second time
$exp = new EmptyResponse(409);
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[0]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[1]), 'application/json')));
+ $this->assertResponse($exp, $this->req("POST", "/feeds", json_encode($in[0])));
+ $this->assertResponse($exp, $this->req("POST", "/feeds", json_encode($in[1])));
// try to add a bad feed
$exp = new EmptyResponse(422);
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[2]), 'application/json')));
+ $this->assertResponse($exp, $this->req("POST", "/feeds", json_encode($in[2])));
// try again (this will succeed), with an invalid folder ID
$exp = new Response($out[2]);
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[3]), 'application/json')));
+ $this->assertResponse($exp, $this->req("POST", "/feeds", json_encode($in[3])));
// try to add no feed
$exp = new EmptyResponse(422);
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[4]), 'application/json')));
+ $this->assertResponse($exp, $this->req("POST", "/feeds", json_encode($in[4])));
}
public function testRemoveASubscription() {
Phake::when(Arsse::$db)->subscriptionRemove(Arsse::$user->id, 1)->thenReturn(true)->thenThrow(new ExceptionInput("subjectMissing"));
$exp = new EmptyResponse(204);
- $this->assertResponse($exp, $this->h->dispatch(new Request("DELETE", "/feeds/1")));
+ $this->assertResponse($exp, $this->req("DELETE", "/feeds/1"));
// fail on the second invocation because it no longer exists
$exp = new EmptyResponse(404);
- $this->assertResponse($exp, $this->h->dispatch(new Request("DELETE", "/feeds/1")));
+ $this->assertResponse($exp, $this->req("DELETE", "/feeds/1"));
Phake::verify(Arsse::$db, Phake::times(2))->subscriptionRemove(Arsse::$user->id, 1);
}
@@ -596,17 +632,17 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 1, ['folder' => -1])->thenThrow(new ExceptionInput("typeViolation")); // folder is invalid
Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 42, $this->anything())->thenThrow(new ExceptionInput("subjectMissing")); // subscription does not exist
$exp = new EmptyResponse(204);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[0]), 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/feeds/1/move", json_encode($in[0])));
$exp = new EmptyResponse(204);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[1]), 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/feeds/1/move", json_encode($in[1])));
$exp = new EmptyResponse(422);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[2]), 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/feeds/1/move", json_encode($in[2])));
$exp = new EmptyResponse(404);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/42/move", json_encode($in[3]), 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/feeds/42/move", json_encode($in[3])));
$exp = new EmptyResponse(422);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[4]), 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/feeds/1/move", json_encode($in[4])));
$exp = new EmptyResponse(422);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[5]), 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/feeds/1/move", json_encode($in[5])));
}
public function testRenameASubscription() {
@@ -626,17 +662,17 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 1, $this->identicalTo(['title' => false]))->thenThrow(new ExceptionInput("missing"));
Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 42, $this->anything())->thenThrow(new ExceptionInput("subjectMissing"));
$exp = new EmptyResponse(422);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[0]), 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/feeds/1/rename", json_encode($in[0])));
$exp = new EmptyResponse(204);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[1]), 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/feeds/1/rename", json_encode($in[1])));
$exp = new EmptyResponse(422);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[2]), 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/feeds/1/rename", json_encode($in[2])));
$exp = new EmptyResponse(422);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[3]), 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/feeds/1/rename", json_encode($in[3])));
$exp = new EmptyResponse(404);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/42/rename", json_encode($in[4]), 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/feeds/42/rename", json_encode($in[4])));
$exp = new EmptyResponse(422);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[6]), 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/feeds/1/rename", json_encode($in[6])));
}
public function testListStaleFeeds() {
@@ -652,11 +688,11 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
];
Phake::when(Arsse::$db)->feedListStale->thenReturn(array_column($out, "id"));
$exp = new Response(['feeds' => $out]);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/all")));
+ $this->assertResponse($exp, $this->req("GET", "/feeds/all"));
// retrieving the list when not an admin fails
Phake::when(Arsse::$user)->rightsGet->thenReturn(0);
$exp = new EmptyResponse(403);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/all")));
+ $this->assertResponse($exp, $this->req("GET", "/feeds/all"));
}
public function testUpdateAFeed() {
@@ -671,17 +707,17 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
Phake::when(Arsse::$db)->feedUpdate(2112)->thenThrow(new ExceptionInput("subjectMissing"));
Phake::when(Arsse::$db)->feedUpdate($this->lessThan(1))->thenThrow(new ExceptionInput("typeViolation"));
$exp = new EmptyResponse(204);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[0]), 'application/json')));
+ $this->assertResponse($exp, $this->req("GET", "/feeds/update", json_encode($in[0])));
$exp = new EmptyResponse(404);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[1]), 'application/json')));
+ $this->assertResponse($exp, $this->req("GET", "/feeds/update", json_encode($in[1])));
$exp = new EmptyResponse(422);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[2]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[3]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[4]), 'application/json')));
+ $this->assertResponse($exp, $this->req("GET", "/feeds/update", json_encode($in[2])));
+ $this->assertResponse($exp, $this->req("GET", "/feeds/update", json_encode($in[3])));
+ $this->assertResponse($exp, $this->req("GET", "/feeds/update", json_encode($in[4])));
// updating a feed when not an admin fails
Phake::when(Arsse::$user)->rightsGet->thenReturn(0);
$exp = new EmptyResponse(403);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[0]), 'application/json')));
+ $this->assertResponse($exp, $this->req("GET", "/feeds/update", json_encode($in[0])));
}
public function testListArticles() {
@@ -708,23 +744,23 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->reverse(true)->folder(-1), Database::LIST_TYPICAL)->thenThrow(new ExceptionInput("typeViolation"));
$exp = new Response(['items' => $this->articles['rest']]);
// check the contents of the response
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items"))); // first instance of base context
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items/updated"))); // second instance of base context
+ $this->assertResponse($exp, $this->req("GET", "/items")); // first instance of base context
+ $this->assertResponse($exp, $this->req("GET", "/items/updated")); // second instance of base context
// check error conditions
$exp = new EmptyResponse(422);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items", json_encode($in[0]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items", json_encode($in[1]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items", json_encode($in[2]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items", json_encode($in[3]), 'application/json')));
+ $this->assertResponse($exp, $this->req("GET", "/items", json_encode($in[0])));
+ $this->assertResponse($exp, $this->req("GET", "/items", json_encode($in[1])));
+ $this->assertResponse($exp, $this->req("GET", "/items", json_encode($in[2])));
+ $this->assertResponse($exp, $this->req("GET", "/items", json_encode($in[3])));
// simply run through the remainder of the input for later method verification
- $this->h->dispatch(new Request("GET", "/items", json_encode($in[4]), 'application/json'));
- $this->h->dispatch(new Request("GET", "/items", json_encode($in[5]), 'application/json')); // third instance of base context
- $this->h->dispatch(new Request("GET", "/items", json_encode($in[6]), 'application/json'));
- $this->h->dispatch(new Request("GET", "/items", json_encode($in[7]), 'application/json'));
- $this->h->dispatch(new Request("GET", "/items", json_encode($in[8]), 'application/json')); // fourth instance of base context
- $this->h->dispatch(new Request("GET", "/items", json_encode($in[9]), 'application/json'));
- $this->h->dispatch(new Request("GET", "/items", json_encode($in[10]), 'application/json'));
- $this->h->dispatch(new Request("GET", "/items", json_encode($in[11]), 'application/json'));
+ $this->req("GET", "/items", json_encode($in[4]));
+ $this->req("GET", "/items", json_encode($in[5])); // third instance of base context
+ $this->req("GET", "/items", json_encode($in[6]));
+ $this->req("GET", "/items", json_encode($in[7]));
+ $this->req("GET", "/items", json_encode($in[8])); // fourth instance of base context
+ $this->req("GET", "/items", json_encode($in[9]));
+ $this->req("GET", "/items", json_encode($in[10]));
+ $this->req("GET", "/items", json_encode($in[11]));
// perform method verifications
Phake::verify(Arsse::$db, Phake::times(4))->articleList(Arsse::$user->id, (new Context)->reverse(true), Database::LIST_TYPICAL);
Phake::verify(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->reverse(true)->subscription(42), Database::LIST_TYPICAL);
@@ -745,13 +781,13 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->folder(1)->latestEdition(2112))->thenReturn(42);
Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->folder(42)->latestEdition(2112))->thenThrow(new ExceptionInput("idMissing")); // folder doesn't exist
$exp = new EmptyResponse(204);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read", $in, 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read?newestItemId=2112")));
+ $this->assertResponse($exp, $this->req("PUT", "/folders/1/read", $in));
+ $this->assertResponse($exp, $this->req("PUT", "/folders/1/read?newestItemId=2112"));
$exp = new EmptyResponse(422);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read?newestItemId=ook")));
+ $this->assertResponse($exp, $this->req("PUT", "/folders/1/read"));
+ $this->assertResponse($exp, $this->req("PUT", "/folders/1/read?newestItemId=ook"));
$exp = new EmptyResponse(404);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/42/read", $in, 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/folders/42/read", $in));
}
public function testMarkASubscriptionRead() {
@@ -760,13 +796,13 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->subscription(1)->latestEdition(2112))->thenReturn(42);
Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->subscription(42)->latestEdition(2112))->thenThrow(new ExceptionInput("idMissing")); // subscription doesn't exist
$exp = new EmptyResponse(204);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read", $in, 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read?newestItemId=2112")));
+ $this->assertResponse($exp, $this->req("PUT", "/feeds/1/read", $in));
+ $this->assertResponse($exp, $this->req("PUT", "/feeds/1/read?newestItemId=2112"));
$exp = new EmptyResponse(422);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read?newestItemId=ook")));
+ $this->assertResponse($exp, $this->req("PUT", "/feeds/1/read"));
+ $this->assertResponse($exp, $this->req("PUT", "/feeds/1/read?newestItemId=ook"));
$exp = new EmptyResponse(404);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/42/read", $in, 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/feeds/42/read", $in));
}
public function testMarkAllItemsRead() {
@@ -774,11 +810,11 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
$in = json_encode(['newestItemId' => 2112]);
Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->latestEdition(2112))->thenReturn(42);
$exp = new EmptyResponse(204);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read", $in, 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read?newestItemId=2112")));
+ $this->assertResponse($exp, $this->req("PUT", "/items/read", $in));
+ $this->assertResponse($exp, $this->req("PUT", "/items/read?newestItemId=2112"));
$exp = new EmptyResponse(422);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read?newestItemId=ook")));
+ $this->assertResponse($exp, $this->req("PUT", "/items/read"));
+ $this->assertResponse($exp, $this->req("PUT", "/items/read?newestItemId=ook"));
}
public function testChangeMarksOfASingleArticle() {
@@ -795,15 +831,15 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $unstar, (new Context)->article(4))->thenReturn(42);
Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $unstar, (new Context)->article(1337))->thenThrow(new ExceptionInput("subjectMissing")); // article doesn't exist doesn't exist
$exp = new EmptyResponse(204);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/1/read")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/2/unread")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/1/3/star")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/4400/4/unstar")));
+ $this->assertResponse($exp, $this->req("PUT", "/items/1/read"));
+ $this->assertResponse($exp, $this->req("PUT", "/items/2/unread"));
+ $this->assertResponse($exp, $this->req("PUT", "/items/1/3/star"));
+ $this->assertResponse($exp, $this->req("PUT", "/items/4400/4/unstar"));
$exp = new EmptyResponse(404);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/42/read")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/47/unread")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/1/2112/star")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/4400/1337/unstar")));
+ $this->assertResponse($exp, $this->req("PUT", "/items/42/read"));
+ $this->assertResponse($exp, $this->req("PUT", "/items/47/unread"));
+ $this->assertResponse($exp, $this->req("PUT", "/items/1/2112/star"));
+ $this->assertResponse($exp, $this->req("PUT", "/items/4400/1337/unstar"));
Phake::verify(Arsse::$db, Phake::times(8))->articleMark(Arsse::$user->id, $this->anything(), $this->anything());
}
@@ -826,26 +862,26 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->editions([]))->thenThrow(new ExceptionInput("tooShort")); // data model function requires one valid integer for multiples
Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->articles([]))->thenThrow(new ExceptionInput("tooShort")); // data model function requires one valid integer for multiples
$exp = new EmptyResponse(204);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/unstar/multiple")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple", json_encode(['items' => "ook"]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple", json_encode(['items' => "ook"]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple", json_encode(['items' => "ook"]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/unstar/multiple", json_encode(['items' => "ook"]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple", json_encode(['items' => []]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple", json_encode(['items' => []]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple", json_encode(['items' => $in[0]]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple", json_encode(['items' => $in[0]]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple", json_encode(['items' => $in[1]]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple", json_encode(['items' => $in[1]]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple", json_encode(['items' => []]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/unstar/multiple", json_encode(['items' => []]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple", json_encode(['items' => $inStar[0]]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/unstar/multiple", json_encode(['items' => $inStar[0]]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple", json_encode(['items' => $inStar[1]]), 'application/json')));
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/unstar/multiple", json_encode(['items' => $inStar[1]]), 'application/json')));
+ $this->assertResponse($exp, $this->req("PUT", "/items/read/multiple"));
+ $this->assertResponse($exp, $this->req("PUT", "/items/unread/multiple"));
+ $this->assertResponse($exp, $this->req("PUT", "/items/star/multiple"));
+ $this->assertResponse($exp, $this->req("PUT", "/items/unstar/multiple"));
+ $this->assertResponse($exp, $this->req("PUT", "/items/read/multiple", json_encode(['items' => "ook"])));
+ $this->assertResponse($exp, $this->req("PUT", "/items/unread/multiple", json_encode(['items' => "ook"])));
+ $this->assertResponse($exp, $this->req("PUT", "/items/star/multiple", json_encode(['items' => "ook"])));
+ $this->assertResponse($exp, $this->req("PUT", "/items/unstar/multiple", json_encode(['items' => "ook"])));
+ $this->assertResponse($exp, $this->req("PUT", "/items/read/multiple", json_encode(['items' => []])));
+ $this->assertResponse($exp, $this->req("PUT", "/items/unread/multiple", json_encode(['items' => []])));
+ $this->assertResponse($exp, $this->req("PUT", "/items/read/multiple", json_encode(['items' => $in[0]])));
+ $this->assertResponse($exp, $this->req("PUT", "/items/unread/multiple", json_encode(['items' => $in[0]])));
+ $this->assertResponse($exp, $this->req("PUT", "/items/read/multiple", json_encode(['items' => $in[1]])));
+ $this->assertResponse($exp, $this->req("PUT", "/items/unread/multiple", json_encode(['items' => $in[1]])));
+ $this->assertResponse($exp, $this->req("PUT", "/items/star/multiple", json_encode(['items' => []])));
+ $this->assertResponse($exp, $this->req("PUT", "/items/unstar/multiple", json_encode(['items' => []])));
+ $this->assertResponse($exp, $this->req("PUT", "/items/star/multiple", json_encode(['items' => $inStar[0]])));
+ $this->assertResponse($exp, $this->req("PUT", "/items/unstar/multiple", json_encode(['items' => $inStar[0]])));
+ $this->assertResponse($exp, $this->req("PUT", "/items/star/multiple", json_encode(['items' => $inStar[1]])));
+ $this->assertResponse($exp, $this->req("PUT", "/items/unstar/multiple", json_encode(['items' => $inStar[1]])));
// ensure the data model was queried appropriately for read/unread
Phake::verify(Arsse::$db, Phake::atLeast(1))->articleMark(Arsse::$user->id, $read, (new Context)->editions([]));
Phake::verify(Arsse::$db, Phake::atLeast(1))->articleMark(Arsse::$user->id, $read, (new Context)->editions($in[0]));
@@ -879,28 +915,28 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
$arr2['warnings']['improperlyConfiguredCron'] = true;
$arr2['warnings']['incorrectDbCharset'] = true;
$exp = new Response($arr1);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/status")));
+ $this->assertResponse($exp, $this->req("GET", "/status"));
}
public function testCleanUpBeforeUpdate() {
Phake::when(Arsse::$db)->feedCleanup()->thenReturn(true);
$exp = new EmptyResponse(204);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/cleanup/before-update")));
+ $this->assertResponse($exp, $this->req("GET", "/cleanup/before-update"));
Phake::verify(Arsse::$db)->feedCleanup();
// performing a cleanup when not an admin fails
Phake::when(Arsse::$user)->rightsGet->thenReturn(0);
$exp = new EmptyResponse(403);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/cleanup/before-update")));
+ $this->assertResponse($exp, $this->req("GET", "/cleanup/before-update"));
}
public function testCleanUpAfterUpdate() {
Phake::when(Arsse::$db)->articleCleanup()->thenReturn(true);
$exp = new EmptyResponse(204);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/cleanup/after-update")));
+ $this->assertResponse($exp, $this->req("GET", "/cleanup/after-update"));
Phake::verify(Arsse::$db)->articleCleanup();
// performing a cleanup when not an admin fails
Phake::when(Arsse::$user)->rightsGet->thenReturn(0);
$exp = new EmptyResponse(403);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/cleanup/after-update")));
+ $this->assertResponse($exp, $this->req("GET", "/cleanup/after-update"));
}
}
diff --git a/tests/cases/REST/NextCloudNews/TestVersions.php b/tests/cases/REST/NextCloudNews/TestVersions.php
index 6904d3ef..3f66b42b 100644
--- a/tests/cases/REST/NextCloudNews/TestVersions.php
+++ b/tests/cases/REST/NextCloudNews/TestVersions.php
@@ -7,7 +7,8 @@ declare(strict_types=1);
namespace JKingWeb\Arsse\TestCase\REST\NextCloudNews;
use JKingWeb\Arsse\REST\NextCloudNews\Versions;
-use JKingWeb\Arsse\REST\Request;
+use Psr\Http\Message\ResponseInterface;
+use Zend\Diactoros\ServerRequest;
use Zend\Diactoros\Response\JsonResponse as Response;
use Zend\Diactoros\Response\EmptyResponse;
@@ -17,44 +18,37 @@ class TestVersions extends \JKingWeb\Arsse\Test\AbstractTest {
$this->clearData();
}
+ protected function req(string $method, string $target): ResponseInterface {
+ $url = "/index.php/apps/news/api".$target;
+ $server = [
+ 'REQUEST_METHOD' => $method,
+ 'REQUEST_URI' => $url,
+ ];
+ $req = new ServerRequest($server, [], $url, $method, "php://memory");
+ $req = $req->withRequestTarget($target);
+ return (new Versions)->dispatch($req);
+ }
+
public function testFetchVersionList() {
$exp = new Response(['apiLevels' => ['v1-2']]);
- $h = new Versions;
- $req = new Request("GET", "/");
- $res = $h->dispatch($req);
- $this->assertResponse($exp, $res);
- $req = new Request("GET", "");
- $res = $h->dispatch($req);
- $this->assertResponse($exp, $res);
- $req = new Request("GET", "/?id=1827");
- $res = $h->dispatch($req);
- $this->assertResponse($exp, $res);
+ $this->assertResponse($exp, $this->req("GET", "/"));
+ $this->assertResponse($exp, $this->req("GET", "/"));
+ $this->assertResponse($exp, $this->req("GET", "/"));
}
public function testRespondToOptionsRequest() {
$exp = new EmptyResponse(204, ['Allow' => "HEAD,GET"]);
- $h = new Versions;
- $req = new Request("OPTIONS", "/");
- $res = $h->dispatch($req);
- $this->assertResponse($exp, $res);
+ $this->assertResponse($exp, $this->req("OPTIONS", "/"));
}
public function testUseIncorrectMethod() {
$exp = new EmptyResponse(405, ['Allow' => "HEAD,GET"]);
- $h = new Versions;
- $req = new Request("POST", "/");
- $res = $h->dispatch($req);
- $this->assertResponse($exp, $res);
+ $this->assertResponse($exp, $this->req("POST", "/"));
}
public function testUseIncorrectPath() {
$exp = new EmptyResponse(404);
- $h = new Versions;
- $req = new Request("GET", "/ook");
- $res = $h->dispatch($req);
- $this->assertResponse($exp, $res);
- $req = new Request("OPTIONS", "/ook");
- $res = $h->dispatch($req);
- $this->assertResponse($exp, $res);
+ $this->assertResponse($exp, $this->req("GET", "/ook"));
+ $this->assertResponse($exp, $this->req("OPTIONS", "/ook"));
}
}
diff --git a/tests/cases/REST/TinyTinyRSS/TestAPI.php b/tests/cases/REST/TinyTinyRSS/TestAPI.php
index d2f1ff36..988c2db8 100644
--- a/tests/cases/REST/TinyTinyRSS/TestAPI.php
+++ b/tests/cases/REST/TinyTinyRSS/TestAPI.php
@@ -19,6 +19,7 @@ use JKingWeb\Arsse\Db\ExceptionInput;
use JKingWeb\Arsse\Db\Transaction;
use JKingWeb\Arsse\REST\TinyTinyRSS\API;
use Psr\Http\Message\ResponseInterface;
+use Zend\Diactoros\ServerRequest;
use Zend\Diactoros\Response\JsonResponse as Response;
use Zend\Diactoros\Response\EmptyResponse;
use Phake;
@@ -124,8 +125,22 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
LONG_STRING;
- protected function req($data): ResponseInterface {
- return $this->h->dispatch(new Request("POST", "", json_encode($data)));
+ protected function req($data, string $method = "POST", string $target = "", string $strData = null): ResponseInterface {
+ $url = "/tt-rss/api".$target;
+ $server = [
+ 'REQUEST_METHOD' => $method,
+ 'REQUEST_URI' => $url,
+ 'HTTP_CONTENT_TYPE' => "application/x-www-form-urlencoded",
+ ];
+ $req = new ServerRequest($server, [], $url, $method, "php://memory");
+ $body = $req->getBody();
+ if (!is_null($strData)) {
+ $body->write($strData);
+ } else {
+ $body->write(json_encode($data));
+ }
+ $req = $req->withBody($body)->withRequestTarget($target);
+ return $this->h->dispatch($req);
}
protected function respGood($content = null, $seq = 0): Response {
@@ -172,11 +187,11 @@ LONG_STRING;
public function testHandleInvalidPaths() {
$exp = $this->respErr("MALFORMED_INPUT", [], null);
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "", "")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/", "")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/index.php", "")));
+ $this->assertResponse($exp, $this->req(null, "POST", "", ""));
+ $this->assertResponse($exp, $this->req(null, "POST", "/", ""));
+ $this->assertResponse($exp, $this->req(null, "POST", "/index.php", ""));
$exp = new EmptyResponse(404);
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/bad/path", "")));
+ $this->assertResponse($exp, $this->req(null, "POST", "/bad/path", ""));
}
public function testHandleOptionsRequest() {
@@ -184,13 +199,13 @@ LONG_STRING;
'Allow' => "POST",
'Accept' => "application/json, text/json",
]);
- $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", "")));
+ $this->assertResponse($exp, $this->req(null, "OPTIONS", "", ""));
}
public function testHandleInvalidData() {
$exp = $this->respErr("MALFORMED_INPUT", [], null);
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "", "This is not valid JSON data")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "", ""))); // lack of data is also an error
+ $this->assertResponse($exp, $this->req(null, "POST", "", "This is not valid JSON data"));
+ $this->assertResponse($exp, $this->req(null, "POST", "", "")); // lack of data is also an error
}
public function testLogIn() {
diff --git a/tests/cases/REST/TinyTinyRSS/TestIcon.php b/tests/cases/REST/TinyTinyRSS/TestIcon.php
index c5c67bc4..548ab502 100644
--- a/tests/cases/REST/TinyTinyRSS/TestIcon.php
+++ b/tests/cases/REST/TinyTinyRSS/TestIcon.php
@@ -12,6 +12,8 @@ use JKingWeb\Arsse\User;
use JKingWeb\Arsse\Database;
use JKingWeb\Arsse\REST\TinyTinyRSS\Icon;
use JKingWeb\Arsse\REST\Request;
+use Psr\Http\Message\ResponseInterface;
+use Zend\Diactoros\ServerRequest;
use Zend\Diactoros\Response\EmptyResponse as Response;
use Phake;
@@ -32,6 +34,17 @@ class TestIcon extends \JKingWeb\Arsse\Test\AbstractTest {
$this->clearData();
}
+ protected function req(string $target, $method = "GET"): ResponseInterface {
+ $url = "/tt-rss/feed-icons/".$target;
+ $server = [
+ 'REQUEST_METHOD' => $method,
+ 'REQUEST_URI' => $url,
+ ];
+ $req = new ServerRequest($server, [], $url, $method, "php://memory");
+ $req = $req->withRequestTarget($target);
+ return $this->h->dispatch($req);
+ }
+
public function testRetrieveFavion() {
Phake::when(Arsse::$db)->subscriptionFavicon->thenReturn("");
Phake::when(Arsse::$db)->subscriptionFavicon(42)->thenReturn("http://example.com/favicon.ico");
@@ -39,19 +52,19 @@ class TestIcon extends \JKingWeb\Arsse\Test\AbstractTest {
Phake::when(Arsse::$db)->subscriptionFavicon(1337)->thenReturn("http://example.org/icon.gif\r\nLocation: http://bad.example.com/");
// these requests should succeed
$exp = new Response(301, ['Location' => "http://example.com/favicon.ico"]);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "42.ico")));
+ $this->assertResponse($exp, $this->req("42.ico"));
$exp = new Response(301, ['Location' => "http://example.net/logo.png"]);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "2112.ico")));
+ $this->assertResponse($exp, $this->req("2112.ico"));
$exp = new Response(301, ['Location' => "http://example.org/icon.gif"]);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "1337.ico")));
+ $this->assertResponse($exp, $this->req("1337.ico"));
// these requests should fail
$exp = new Response(404);
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "ook.ico")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "ook")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "47.ico")));
- $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "2112.png")));
+ $this->assertResponse($exp, $this->req("ook.ico"));
+ $this->assertResponse($exp, $this->req("ook"));
+ $this->assertResponse($exp, $this->req("47.ico"));
+ $this->assertResponse($exp, $this->req("2112.png"));
// only GET is allowed
$exp = new Response(405, ['Allow' => "GET"]);
- $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "2112.ico")));
+ $this->assertResponse($exp, $this->req("2112.ico", "PUT"));
}
}
diff --git a/tests/lib/AbstractTest.php b/tests/lib/AbstractTest.php
index 762e991c..c3188a6b 100644
--- a/tests/lib/AbstractTest.php
+++ b/tests/lib/AbstractTest.php
@@ -33,13 +33,13 @@ abstract class AbstractTest extends \PHPUnit\Framework\TestCase {
}
protected function assertResponse(ResponseInterface $exp, ResponseInterface $act, string $text = null) {
- $this->assertEquals($exp->getHeaders(), $act->getHeaders(), $text);
$this->assertEquals($exp->getStatusCode(), $act->getStatusCode(), $text);
$this->assertInstanceOf(get_class($exp), $act);
if ($exp instanceof JsonResponse) {
$this->assertEquals($exp->getPayload(), $act->getPayload(), $text);
$this->assertSame($exp->getPayload(), $act->getPayload(), $text);
}
+ $this->assertEquals($exp->getHeaders(), $act->getHeaders(), $text);
}
public function approximateTime($exp, $act) {