mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
Implement NCN API v1-2 feed handling: tests
- Fixes #8 - Fixes #9 - Fixes #10 - Fixes #11 - Fixes #12 - Differentiated between a missing ID which is the subject of an action (e.g. the subscription when moving a subscription) and other missing IDs (e.g. the folder when moving a subscription) - Enforced same rules for subscription titles as for folder names, save that null is valid for subscriptions
This commit is contained in:
parent
054200dfc0
commit
88c0f4986c
7 changed files with 259 additions and 48 deletions
|
@ -43,6 +43,7 @@ abstract class AbstractException extends \Exception {
|
||||||
"Db/ExceptionInput.constraintViolation" => 10236,
|
"Db/ExceptionInput.constraintViolation" => 10236,
|
||||||
"Db/ExceptionInput.typeViolation" => 10237,
|
"Db/ExceptionInput.typeViolation" => 10237,
|
||||||
"Db/ExceptionInput.circularDependence" => 10238,
|
"Db/ExceptionInput.circularDependence" => 10238,
|
||||||
|
"Db/ExceptionInput.subjectMissing" => 10239,
|
||||||
"Db/ExceptionTimeout.general" => 10241,
|
"Db/ExceptionTimeout.general" => 10241,
|
||||||
"Conf/Exception.fileMissing" => 10301,
|
"Conf/Exception.fileMissing" => 10301,
|
||||||
"Conf/Exception.fileUnusable" => 10302,
|
"Conf/Exception.fileUnusable" => 10302,
|
||||||
|
|
|
@ -344,25 +344,23 @@ class Database {
|
||||||
public function folderRemove(string $user, int $id): bool {
|
public function folderRemove(string $user, int $id): bool {
|
||||||
if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||||
$changes = $this->db->prepare("DELETE FROM arsse_folders where owner is ? and id is ?", "str", "int")->run($user, $id)->changes();
|
$changes = $this->db->prepare("DELETE FROM arsse_folders where owner is ? and id is ?", "str", "int")->run($user, $id)->changes();
|
||||||
if(!$changes) throw new Db\ExceptionInput("idMissing", ["action" => __FUNCTION__, "field" => "folder", 'id' => $id]);
|
if(!$changes) throw new Db\ExceptionInput("subjectMissing", ["action" => __FUNCTION__, "field" => "folder", 'id' => $id]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function folderPropertiesGet(string $user, int $id): array {
|
public function folderPropertiesGet(string $user, int $id): array {
|
||||||
if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||||
$props = $this->db->prepare("SELECT id,name,parent from arsse_folders where owner is ? and id is ?", "str", "int")->run($user, $id)->getRow();
|
$props = $this->db->prepare("SELECT id,name,parent from arsse_folders where owner is ? and id is ?", "str", "int")->run($user, $id)->getRow();
|
||||||
if(!$props) throw new Db\ExceptionInput("idMissing", ["action" => __FUNCTION__, "field" => "folder", 'id' => $id]);
|
if(!$props) throw new Db\ExceptionInput("subjectMissing", ["action" => __FUNCTION__, "field" => "folder", 'id' => $id]);
|
||||||
return $props;
|
return $props;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function folderPropertiesSet(string $user, int $id, array $data): bool {
|
public function folderPropertiesSet(string $user, int $id, array $data): bool {
|
||||||
if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||||
// validate the folder ID and, if specified, the parent to move it to
|
// validate the folder ID and, if specified, the parent to move it to
|
||||||
if(array_key_exists("parent", $data)) {
|
$parent = null;
|
||||||
$f = $this->folderValidateId($user, $id, $data['parent']);
|
if(array_key_exists("parent", $data)) $parent = $data['parent'];
|
||||||
} else {
|
$f = $this->folderValidateId($user, $id, $parent, true);
|
||||||
$f = $this->folderValidateId($user, $id);
|
|
||||||
}
|
|
||||||
// if a new name is specified, validate it
|
// if a new name is specified, validate it
|
||||||
if(array_key_exists("name", $data)) {
|
if(array_key_exists("name", $data)) {
|
||||||
$this->folderValidateName($data['name']);
|
$this->folderValidateName($data['name']);
|
||||||
|
@ -381,7 +379,7 @@ class Database {
|
||||||
return (bool) $this->db->prepare("UPDATE arsse_folders set $setClause where owner is ? and id is ?", $setTypes, "str", "int")->run($setValues, $user, $id)->changes();
|
return (bool) $this->db->prepare("UPDATE arsse_folders set $setClause where owner is ? and id is ?", $setTypes, "str", "int")->run($setValues, $user, $id)->changes();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function folderValidateId(string $user, int $id = null, int $parent = null): array {
|
protected function folderValidateId(string $user, int $id = null, int $parent = null, bool $subject = false): array {
|
||||||
if(is_null($id)) {
|
if(is_null($id)) {
|
||||||
// if no ID is specified this is a no-op, unless a parent is specified, which is always a circular dependence
|
// if no ID is specified this is a no-op, unless a parent is specified, which is always a circular dependence
|
||||||
if(!is_null($parent)) {
|
if(!is_null($parent)) {
|
||||||
|
@ -391,7 +389,7 @@ class Database {
|
||||||
}
|
}
|
||||||
// check whether the folder exists and is owned by the user
|
// check whether the folder exists and is owned by the user
|
||||||
$f = $this->db->prepare("SELECT name,parent from arsse_folders where owner is ? and id is ?", "str", "int")->run($user, $id)->getRow();
|
$f = $this->db->prepare("SELECT name,parent from arsse_folders where owner is ? and id is ?", "str", "int")->run($user, $id)->getRow();
|
||||||
if(!$f) throw new Db\ExceptionInput("idMissing", ["action" => $this->caller(), "field" => "folder", 'id' => $parent]);
|
if(!$f) throw new Db\ExceptionInput($subject ? "subjectMissing" : "idMissing", ["action" => $this->caller(), "field" => "folder", 'id' => $parent]);
|
||||||
// if we're moving a folder to a new parent, check that the parent is valid
|
// if we're moving a folder to a new parent, check that the parent is valid
|
||||||
if(!is_null($parent)) {
|
if(!is_null($parent)) {
|
||||||
// make sure both that the parent exists, and that the parent is not either the folder itself or one of its children (a circular dependence)
|
// make sure both that the parent exists, and that the parent is not either the folder itself or one of its children (a circular dependence)
|
||||||
|
@ -413,7 +411,7 @@ class Database {
|
||||||
|
|
||||||
protected function folderValidateName($name): bool {
|
protected function folderValidateName($name): bool {
|
||||||
$name = (string) $name;
|
$name = (string) $name;
|
||||||
if($name=="") {
|
if(!strlen($name)) {
|
||||||
throw new Db\ExceptionInput("missing", ["action" => $this->caller(), "field" => "name"]);
|
throw new Db\ExceptionInput("missing", ["action" => $this->caller(), "field" => "name"]);
|
||||||
} else if(!strlen(trim($name))) {
|
} else if(!strlen(trim($name))) {
|
||||||
throw new Db\ExceptionInput("whitespace", ["action" => $this->caller(), "field" => "name"]);
|
throw new Db\ExceptionInput("whitespace", ["action" => $this->caller(), "field" => "name"]);
|
||||||
|
@ -473,7 +471,7 @@ class Database {
|
||||||
public function subscriptionRemove(string $user, int $id): bool {
|
public function subscriptionRemove(string $user, int $id): bool {
|
||||||
if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||||
$changes = $this->db->prepare("DELETE from arsse_subscriptions where owner is ? and id is ?", "str", "int")->run($user, $id)->changes();
|
$changes = $this->db->prepare("DELETE from arsse_subscriptions where owner is ? and id is ?", "str", "int")->run($user, $id)->changes();
|
||||||
if(!$changes) throw new Db\ExceptionInput("idMissing", ["action" => __FUNCTION__, "field" => "folder", 'id' => $id]);
|
if(!$changes) throw new Db\ExceptionInput("subjectMissing", ["action" => __FUNCTION__, "field" => "folder", 'id' => $id]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -483,7 +481,7 @@ class Database {
|
||||||
Data::$user->authorizationEnabled(false);
|
Data::$user->authorizationEnabled(false);
|
||||||
$sub = $this->subscriptionList($user, null, $id)->getRow();
|
$sub = $this->subscriptionList($user, null, $id)->getRow();
|
||||||
Data::$user->authorizationEnabled(true);
|
Data::$user->authorizationEnabled(true);
|
||||||
if(!$sub) throw new Db\ExceptionInput("idMissing", ["action" => __FUNCTION__, "field" => "feed", 'id' => $id]);
|
if(!$sub) throw new Db\ExceptionInput("subjectMissing", ["action" => __FUNCTION__, "field" => "feed", 'id' => $id]);
|
||||||
return $sub;
|
return $sub;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,19 +490,21 @@ class Database {
|
||||||
$tr = $this->db->begin();
|
$tr = $this->db->begin();
|
||||||
if(!$this->db->prepare("SELECT count(*) from arsse_subscriptions where owner is ? and id is ?", "str", "int")->run($user, $id)->getValue()) {
|
if(!$this->db->prepare("SELECT count(*) from arsse_subscriptions where owner is ? and id is ?", "str", "int")->run($user, $id)->getValue()) {
|
||||||
// if the ID doesn't exist or doesn't belong to the user, throw an exception
|
// if the ID doesn't exist or doesn't belong to the user, throw an exception
|
||||||
throw new Db\ExceptionInput("idMissing", ["action" => __FUNCTION__, "field" => "feed", 'id' => $id]);
|
throw new Db\ExceptionInput("subjectMissing", ["action" => __FUNCTION__, "field" => "feed", 'id' => $id]);
|
||||||
}
|
}
|
||||||
if(array_key_exists("folder", $data)) {
|
if(array_key_exists("folder", $data)) {
|
||||||
// ensure the target folder exists and belong to the user
|
// ensure the target folder exists and belong to the user
|
||||||
$this->folderValidateId($user, $data['folder']);
|
$this->folderValidateId($user, $data['folder']);
|
||||||
}
|
}
|
||||||
if(array_key_exists("title", $data)) {
|
if(array_key_exists("title", $data)) {
|
||||||
// if the title is effectively an empty string, change it to null so that the feed title is used instead
|
// if the title is null, this signals intended use of the default title; otherwise make sure it's not effectively an empty string
|
||||||
|
if(!is_null($data['title'])) {
|
||||||
$title = (string) $data['title'];
|
$title = (string) $data['title'];
|
||||||
$title = trim($title);
|
if(!strlen($title)) throw new Db\ExceptionInput("missing", ["action" => __FUNCTION__, "field" => "title"]);
|
||||||
if($title==="") $title = null;
|
if(!strlen(trim($title))) throw new Db\ExceptionInput("whitespace", ["action" => __FUNCTION__, "field" => "title"]);
|
||||||
$data['title'] = $title;
|
$data['title'] = $title;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
$valid = [
|
$valid = [
|
||||||
'title' => "str",
|
'title' => "str",
|
||||||
'folder' => "int",
|
'folder' => "int",
|
||||||
|
@ -558,7 +558,7 @@ class Database {
|
||||||
$tr = $this->db->begin();
|
$tr = $this->db->begin();
|
||||||
// check to make sure the feed exists
|
// check to make sure the feed exists
|
||||||
$f = $this->db->prepare('SELECT url, username, password, DATEFORMAT("http", modified) AS lastmodified, etag, err_count FROM arsse_feeds where id is ?', "int")->run($feedID)->getRow();
|
$f = $this->db->prepare('SELECT url, username, password, DATEFORMAT("http", modified) AS lastmodified, etag, err_count FROM arsse_feeds where id is ?', "int")->run($feedID)->getRow();
|
||||||
if(!$f) throw new Db\ExceptionInput("idMissing", ["action" => __FUNCTION__, "field" => "feed", 'id' => $feedID]);
|
if(!$f) throw new Db\ExceptionInput("subjectMissing", ["action" => __FUNCTION__, "field" => "feed", 'id' => $feedID]);
|
||||||
// the Feed object throws an exception when there are problems, but that isn't ideal
|
// the Feed object throws an exception when there are problems, but that isn't ideal
|
||||||
// here. When an exception is thrown it should update the database with the
|
// here. When an exception is thrown it should update the database with the
|
||||||
// error instead of failing; if other exceptions are thrown, we should simply roll back
|
// error instead of failing; if other exceptions are thrown, we should simply roll back
|
||||||
|
|
|
@ -159,7 +159,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
} catch(ExceptionInput $e) {
|
} catch(ExceptionInput $e) {
|
||||||
switch($e->getCode()) {
|
switch($e->getCode()) {
|
||||||
// folder does not exist
|
// folder does not exist
|
||||||
case 10235: return new Response(404);
|
case 10239: return new Response(404);
|
||||||
// folder already exists
|
// folder already exists
|
||||||
case 10236: return new Response(409);
|
case 10236: return new Response(409);
|
||||||
// folder name not acceptable
|
// folder name not acceptable
|
||||||
|
@ -206,13 +206,12 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
}
|
}
|
||||||
$out = ['feeds' => $out];
|
$out = ['feeds' => $out];
|
||||||
$out['starredCount'] = Data::$db->articleStarredCount(Data::$user->id);
|
$out['starredCount'] = Data::$db->articleStarredCount(Data::$user->id);
|
||||||
$newest = Data::$db->editionLatest(Data::$user->id, ['subscription' => $id]);
|
$newest = Data::$db->editionLatest(Data::$user->id);
|
||||||
if($newest) $out['newestItemId'] = $newest;
|
if($newest) $out['newestItemId'] = $newest;
|
||||||
return new Response(200, $out);
|
return new Response(200, $out);
|
||||||
}
|
}
|
||||||
|
|
||||||
// return list of feeds which should be refreshed
|
// return list of feeds which should be refreshed
|
||||||
// refresh a feed
|
|
||||||
protected function feedListStale(array $url, array $data): Response {
|
protected function feedListStale(array $url, array $data): Response {
|
||||||
// function requires admin rights per spec
|
// function requires admin rights per spec
|
||||||
if(Data::$user->rightsGet(Data::$user->id)==User::RIGHTS_NONE) return new Response(403);
|
if(Data::$user->rightsGet(Data::$user->id)==User::RIGHTS_NONE) return new Response(403);
|
||||||
|
@ -305,7 +304,15 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
try {
|
try {
|
||||||
Data::$db->subscriptionPropertiesSet(Data::$user->id, (int) $url[1], $in);
|
Data::$db->subscriptionPropertiesSet(Data::$user->id, (int) $url[1], $in);
|
||||||
} catch(ExceptionInput $e) {
|
} catch(ExceptionInput $e) {
|
||||||
return new Response(404);
|
switch($e->getCode()) {
|
||||||
|
// subscription does not exist
|
||||||
|
case 10239: return new Response(404);
|
||||||
|
// name is invalid
|
||||||
|
case 10231:
|
||||||
|
case 10232: return new Response(422);
|
||||||
|
// other errors related to input
|
||||||
|
default: return new Response(400);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return new Response(204);
|
return new Response(204);
|
||||||
}
|
}
|
||||||
|
@ -326,7 +333,14 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
try {
|
try {
|
||||||
Data::$db->subscriptionPropertiesSet(Data::$user->id, (int) $url[1], $in);
|
Data::$db->subscriptionPropertiesSet(Data::$user->id, (int) $url[1], $in);
|
||||||
} catch(ExceptionInput $e) {
|
} catch(ExceptionInput $e) {
|
||||||
return new Response(404);
|
switch($e->getCode()) {
|
||||||
|
// subscription does not exist
|
||||||
|
case 10239: return new Response(404);
|
||||||
|
// folder does not exist
|
||||||
|
case 10235: return new Response(422);
|
||||||
|
// other errors related to input
|
||||||
|
default: return new Response(400);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return new Response(204);
|
return new Response(204);
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,7 @@ return [
|
||||||
'Exception.JKingWeb/Arsse/Db/ExceptionInput.whitespace' => 'Required field "{field}" of action "{action}" may not contain only whitespace',
|
'Exception.JKingWeb/Arsse/Db/ExceptionInput.whitespace' => 'Required field "{field}" of action "{action}" may not contain only whitespace',
|
||||||
'Exception.JKingWeb/Arsse/Db/ExceptionInput.tooLong' => 'Required field "{field}" of action "{action}" has a maximum length of {max}',
|
'Exception.JKingWeb/Arsse/Db/ExceptionInput.tooLong' => 'Required field "{field}" of action "{action}" has a maximum length of {max}',
|
||||||
'Exception.JKingWeb/Arsse/Db/ExceptionInput.tooShort' => 'Required field "{field}" of action "{action}" has a minimum length of {min}',
|
'Exception.JKingWeb/Arsse/Db/ExceptionInput.tooShort' => 'Required field "{field}" of action "{action}" has a minimum length of {min}',
|
||||||
|
'Exception.JKingWeb/Arsse/Db/ExceptionInput.subjectMissing' => 'Referenced ID ({id}) in field "{field}" does not exist',
|
||||||
'Exception.JKingWeb/Arsse/Db/ExceptionInput.idMissing' => 'Referenced ID ({id}) in field "{field}" does not exist',
|
'Exception.JKingWeb/Arsse/Db/ExceptionInput.idMissing' => 'Referenced ID ({id}) in field "{field}" does not exist',
|
||||||
'Exception.JKingWeb/Arsse/Db/ExceptionInput.circularDependence' => 'Referenced ID ({id}) in field "{field}" creates a circular dependence',
|
'Exception.JKingWeb/Arsse/Db/ExceptionInput.circularDependence' => 'Referenced ID ({id}) in field "{field}" creates a circular dependence',
|
||||||
'Exception.JKingWeb/Arsse/Db/ExceptionInput.constraintViolation' => '{0}',
|
'Exception.JKingWeb/Arsse/Db/ExceptionInput.constraintViolation' => '{0}',
|
||||||
|
|
|
@ -11,6 +11,68 @@ class TestNCNV1_2 extends \PHPUnit\Framework\TestCase {
|
||||||
use Test\Tools;
|
use Test\Tools;
|
||||||
|
|
||||||
protected $h;
|
protected $h;
|
||||||
|
protected $feeds = [ // expected sample output of a feed list from the database, and the resultant expected transformation by the REST handler
|
||||||
|
'db' => [
|
||||||
|
[
|
||||||
|
'id' => 2112,
|
||||||
|
'url' => 'http://example.com/news.atom',
|
||||||
|
'favicon' => 'http://example.com/favicon.png',
|
||||||
|
'source' => 'http://example.com/',
|
||||||
|
'folder' => NULL,
|
||||||
|
'pinned' => 0,
|
||||||
|
'err_count' => 0,
|
||||||
|
'err_msg' => '',
|
||||||
|
'order_type' => 0,
|
||||||
|
'added' => 1495287354,
|
||||||
|
'title' => 'First example feed',
|
||||||
|
'unread' => 50048,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 42,
|
||||||
|
'url' => 'http://example.org/news.atom',
|
||||||
|
'favicon' => 'http://example.org/favicon.png',
|
||||||
|
'source' => 'http://example.org/',
|
||||||
|
'folder' => 8,
|
||||||
|
'pinned' => 1,
|
||||||
|
'err_count' => 0,
|
||||||
|
'err_msg' => '',
|
||||||
|
'order_type' => 2,
|
||||||
|
'added' => 1495287354,
|
||||||
|
'title' => 'Second example feed',
|
||||||
|
'unread' => 23,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'rest' => [
|
||||||
|
[
|
||||||
|
'id' => 2112,
|
||||||
|
'url' => 'http://example.com/news.atom',
|
||||||
|
'faviconLink' => 'http://example.com/favicon.png',
|
||||||
|
'link' => 'http://example.com/',
|
||||||
|
'folderId' => 0,
|
||||||
|
'pinned' => false,
|
||||||
|
'updateErrorCount' => 0,
|
||||||
|
'lastUpdateError' => '',
|
||||||
|
'ordering' => 0,
|
||||||
|
'added' => 1495287354,
|
||||||
|
'title' => 'First example feed',
|
||||||
|
'unreadCount' => 50048,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 42,
|
||||||
|
'url' => 'http://example.org/news.atom',
|
||||||
|
'faviconLink' => 'http://example.org/favicon.png',
|
||||||
|
'link' => 'http://example.org/',
|
||||||
|
'folderId' => 8,
|
||||||
|
'pinned' => true,
|
||||||
|
'updateErrorCount' => 0,
|
||||||
|
'lastUpdateError' => '',
|
||||||
|
'ordering' => 2,
|
||||||
|
'added' => 1495287354,
|
||||||
|
'title' => 'Second example feed',
|
||||||
|
'unreadCount' => 23,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
function setUp() {
|
function setUp() {
|
||||||
$this->clearData();
|
$this->clearData();
|
||||||
|
@ -78,7 +140,9 @@ class TestNCNV1_2 extends \PHPUnit\Framework\TestCase {
|
||||||
['id' => 1, 'name' => "Software", 'parent' => null],
|
['id' => 1, 'name' => "Software", 'parent' => null],
|
||||||
['id' => 12, 'name' => "Hardware", 'parent' => null],
|
['id' => 12, 'name' => "Hardware", 'parent' => null],
|
||||||
];
|
];
|
||||||
Phake::when(Data::$db)->folderList(Data::$user->id, null, false)->thenReturn(new Result($list));
|
Phake::when(Data::$db)->folderList(Data::$user->id, null, false)->thenReturn(new Result([]))->thenReturn(new Result($list));
|
||||||
|
$exp = new Response(200, ['folders' => []]);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/folders")));
|
||||||
$exp = new Response(200, ['folders' => $list]);
|
$exp = new Response(200, ['folders' => $list]);
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/folders")));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/folders")));
|
||||||
}
|
}
|
||||||
|
@ -123,7 +187,7 @@ class TestNCNV1_2 extends \PHPUnit\Framework\TestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
function testRemoveAFolder() {
|
function testRemoveAFolder() {
|
||||||
Phake::when(Data::$db)->folderRemove(Data::$user->id, 1)->thenReturn(true)->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("idMissing"));
|
Phake::when(Data::$db)->folderRemove(Data::$user->id, 1)->thenReturn(true)->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("subjectMissing"));
|
||||||
$exp = new Response(204);
|
$exp = new Response(204);
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("DELETE", "/folders/1")));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("DELETE", "/folders/1")));
|
||||||
// fail on the second invocation because it no longer exists
|
// fail on the second invocation because it no longer exists
|
||||||
|
@ -145,7 +209,7 @@ class TestNCNV1_2 extends \PHPUnit\Framework\TestCase {
|
||||||
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[2])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("missing"));
|
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[2])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("missing"));
|
||||||
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[3])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("whitespace"));
|
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[3])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("whitespace"));
|
||||||
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[4])->thenReturn(true); // this should be stopped by the handler before the request gets to the database
|
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[4])->thenReturn(true); // this should be stopped by the handler before the request gets to the database
|
||||||
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 3, $this->anything())->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("idMissing")); // folder ID 3 does not exist
|
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 3, $this->anything())->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("subjectMissing")); // folder ID 3 does not exist
|
||||||
$exp = new Response(204);
|
$exp = new Response(204);
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[0]), 'application/json')));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[0]), 'application/json')));
|
||||||
$exp = new Response(409);
|
$exp = new Response(409);
|
||||||
|
@ -164,4 +228,122 @@ class TestNCNV1_2 extends \PHPUnit\Framework\TestCase {
|
||||||
$exp = new Response(200, ['version' => \JKingWeb\Arsse\VERSION]);
|
$exp = new Response(200, ['version' => \JKingWeb\Arsse\VERSION]);
|
||||||
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/version")));
|
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/version")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testListSubscriptions() {
|
||||||
|
$exp1 = [
|
||||||
|
'feeds' => [],
|
||||||
|
'starredCount' => 0,
|
||||||
|
];
|
||||||
|
$exp2 = [
|
||||||
|
'feeds' => $this->feeds['rest'],
|
||||||
|
'starredCount' => 5,
|
||||||
|
'newestItemId' => 4758915,
|
||||||
|
];
|
||||||
|
Phake::when(Data::$db)->subscriptionList(Data::$user->id)->thenReturn(new Result([]))->thenReturn(new Result($this->feeds['db']));
|
||||||
|
Phake::when(Data::$db)->articleStarredCount(Data::$user->id)->thenReturn(0)->thenReturn(5);
|
||||||
|
Phake::when(Data::$db)->editionLatest(Data::$user->id)->thenReturn(0)->thenReturn(4758915);
|
||||||
|
$exp = new Response(200, $exp1);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/feeds")));
|
||||||
|
$exp = new Response(200, $exp2);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/feeds")));
|
||||||
|
// make sure the correct date format is actually requested
|
||||||
|
Phake::verify(Data::$db, Phake::atLeast(1))->dateFormatDefault("unix");
|
||||||
|
}
|
||||||
|
|
||||||
|
function testAddASubscription() {
|
||||||
|
$in = [
|
||||||
|
['url' => "http://example.com/news.atom", 'folderId' => 3],
|
||||||
|
['url' => "http://example.org/news.atom", 'folderId' => 8],
|
||||||
|
['url' => "http://example.net/news.atom", 'folderId' => 0],
|
||||||
|
];
|
||||||
|
$out = [
|
||||||
|
['feeds' => [$this->feeds['rest'][0]]],
|
||||||
|
['feeds' => [$this->feeds['rest'][1]], 'newestItemId' => 4758915],
|
||||||
|
[],
|
||||||
|
];
|
||||||
|
// set up the necessary mocks
|
||||||
|
Phake::when(Data::$db)->subscriptionAdd(Data::$user->id, "http://example.com/news.atom")->thenReturn(2112)->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("constraintViolation")); // error on the second call
|
||||||
|
Phake::when(Data::$db)->subscriptionAdd(Data::$user->id, "http://example.org/news.atom")->thenReturn( 42 )->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("constraintViolation")); // error on the second call
|
||||||
|
Phake::when(Data::$db)->subscriptionPropertiesGet(Data::$user->id, 2112)->thenReturn($this->feeds['db'][0]);
|
||||||
|
Phake::when(Data::$db)->subscriptionPropertiesGet(Data::$user->id, 42)->thenReturn($this->feeds['db'][1]);
|
||||||
|
Phake::when(Data::$db)->editionLatest(Data::$user->id, ['subscription' => 2112])->thenReturn(0);
|
||||||
|
Phake::when(Data::$db)->editionLatest(Data::$user->id, ['subscription' => 42])->thenReturn(4758915);
|
||||||
|
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 2112, ['folder' => 3])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("idMissing")); // folder ID 3 does not exist
|
||||||
|
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 42, ['folder' => 8])->thenReturn(true);
|
||||||
|
// set up a mock for a bad feed
|
||||||
|
Phake::when(Data::$db)->subscriptionAdd(Data::$user->id, "http://example.net/news.atom")->thenThrow(new \JKingWeb\Arsse\Feed\Exception("http://example.net/news.atom", new \PicoFeed\Client\InvalidUrlException()));
|
||||||
|
// add the subscriptions
|
||||||
|
$exp = new Response(200, $out[0]);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[0]), 'application/json')));
|
||||||
|
$exp = new Response(200, $out[1]);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[1]), 'application/json')));
|
||||||
|
// try to add them a second time
|
||||||
|
$exp = new Response(409);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[0]), 'application/json')));
|
||||||
|
$exp = new Response(409);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[1]), 'application/json')));
|
||||||
|
// try to add a bad feed
|
||||||
|
$exp = new Response(422);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[2]), 'application/json')));
|
||||||
|
// make sure the correct date format is actually requested
|
||||||
|
Phake::verify(Data::$db, Phake::atLeast(1))->dateFormatDefault("unix");
|
||||||
|
}
|
||||||
|
|
||||||
|
function testRemoveASubscription() {
|
||||||
|
Phake::when(Data::$db)->subscriptionRemove(Data::$user->id, 1)->thenReturn(true)->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("subjectMissing"));
|
||||||
|
$exp = new Response(204);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("DELETE", "/feeds/1")));
|
||||||
|
// fail on the second invocation because it no longer exists
|
||||||
|
$exp = new Response(404);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("DELETE", "/feeds/1")));
|
||||||
|
Phake::verify(Data::$db, Phake::times(2))->subscriptionRemove(Data::$user->id, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testMoveASubscription() {
|
||||||
|
$in = [
|
||||||
|
['folderId' => 0],
|
||||||
|
['folderId' => 42],
|
||||||
|
['folderId' => 2112],
|
||||||
|
['folderId' => 42],
|
||||||
|
];
|
||||||
|
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, ['folder' => 42])->thenReturn(true);
|
||||||
|
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, ['folder' => null])->thenReturn(true);
|
||||||
|
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, ['folder' => 2112])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("idMissing")); // folder does not exist
|
||||||
|
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 42, $this->anything())->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("subjectMissing")); // subscription does not exist
|
||||||
|
$exp = new Response(204);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[0]), 'application/json')));
|
||||||
|
$exp = new Response(204);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[1]), 'application/json')));
|
||||||
|
$exp = new Response(422);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[2]), 'application/json')));
|
||||||
|
$exp = new Response(404);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/42/move", json_encode($in[3]), 'application/json')));
|
||||||
|
}
|
||||||
|
|
||||||
|
function testRenameASubscription() {
|
||||||
|
$in = [
|
||||||
|
['feedTitle' => null],
|
||||||
|
['feedTitle' => "Ook"],
|
||||||
|
['feedTitle' => " "],
|
||||||
|
['feedTitle' => ""],
|
||||||
|
['feedTitle' => false],
|
||||||
|
['feedTitle' => "Feed does not exist"],
|
||||||
|
];
|
||||||
|
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, $this->identicalTo(['title' => null]))->thenReturn(true);
|
||||||
|
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, $this->identicalTo(['title' => "Ook"]))->thenReturn(true);
|
||||||
|
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, $this->identicalTo(['title' => " "]))->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("whitespace"));
|
||||||
|
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, $this->identicalTo(['title' => ""]))->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("missing"));
|
||||||
|
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 1, $this->identicalTo(['title' => false]))->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("missing"));
|
||||||
|
Phake::when(Data::$db)->subscriptionPropertiesSet(Data::$user->id, 42, $this->anything())->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("subjectMissing"));
|
||||||
|
$exp = new Response(204);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[0]), 'application/json')));
|
||||||
|
$exp = new Response(204);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[1]), 'application/json')));
|
||||||
|
$exp = new Response(422);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[2]), 'application/json')));
|
||||||
|
$exp = new Response(422);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[3]), 'application/json')));
|
||||||
|
$exp = new Response(404);
|
||||||
|
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/feeds/42/rename", json_encode($in[4]), 'application/json')));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -139,12 +139,12 @@ trait SeriesFolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
function testRemoveAMissingFolder() {
|
function testRemoveAMissingFolder() {
|
||||||
$this->assertException("idMissing", "Db", "ExceptionInput");
|
$this->assertException("subjectMissing", "Db", "ExceptionInput");
|
||||||
Data::$db->folderRemove("john.doe@example.com", 2112);
|
Data::$db->folderRemove("john.doe@example.com", 2112);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testRemoveAFolderOfTheWrongOwner() {
|
function testRemoveAFolderOfTheWrongOwner() {
|
||||||
$this->assertException("idMissing", "Db", "ExceptionInput");
|
$this->assertException("subjectMissing", "Db", "ExceptionInput");
|
||||||
Data::$db->folderRemove("john.doe@example.com", 4); // folder ID 4 belongs to Jane
|
Data::$db->folderRemove("john.doe@example.com", 4); // folder ID 4 belongs to Jane
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,12 +165,12 @@ trait SeriesFolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
function testGetThePropertiesOfAMissingFolder() {
|
function testGetThePropertiesOfAMissingFolder() {
|
||||||
$this->assertException("idMissing", "Db", "ExceptionInput");
|
$this->assertException("subjectMissing", "Db", "ExceptionInput");
|
||||||
Data::$db->folderPropertiesGet("john.doe@example.com", 2112);
|
Data::$db->folderPropertiesGet("john.doe@example.com", 2112);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testGetThePropertiesOfAFolderOfTheWrongOwner() {
|
function testGetThePropertiesOfAFolderOfTheWrongOwner() {
|
||||||
$this->assertException("idMissing", "Db", "ExceptionInput");
|
$this->assertException("subjectMissing", "Db", "ExceptionInput");
|
||||||
Data::$db->folderPropertiesGet("john.doe@example.com", 4); // folder ID 4 belongs to Jane
|
Data::$db->folderPropertiesGet("john.doe@example.com", 4); // folder ID 4 belongs to Jane
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,12 +217,12 @@ trait SeriesFolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
function testSetThePropertiesOfAMissingFolder() {
|
function testSetThePropertiesOfAMissingFolder() {
|
||||||
$this->assertException("idMissing", "Db", "ExceptionInput");
|
$this->assertException("subjectMissing", "Db", "ExceptionInput");
|
||||||
Data::$db->folderPropertiesSet("john.doe@example.com", 2112, ['parent' => null]);
|
Data::$db->folderPropertiesSet("john.doe@example.com", 2112, ['parent' => null]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testSetThePropertiesOfAFolderOfTheWrongOwner() {
|
function testSetThePropertiesOfAFolderForTheWrongOwner() {
|
||||||
$this->assertException("idMissing", "Db", "ExceptionInput");
|
$this->assertException("subjectMissing", "Db", "ExceptionInput");
|
||||||
Data::$db->folderPropertiesSet("john.doe@example.com", 4, ['parent' => null]); // folder ID 4 belongs to Jane
|
Data::$db->folderPropertiesSet("john.doe@example.com", 4, ['parent' => null]); // folder ID 4 belongs to Jane
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -167,13 +167,13 @@ trait SeriesSubscription {
|
||||||
}
|
}
|
||||||
|
|
||||||
function testRemoveAMissingSubscription() {
|
function testRemoveAMissingSubscription() {
|
||||||
$this->assertException("idMissing", "Db", "ExceptionInput");
|
$this->assertException("subjectMissing", "Db", "ExceptionInput");
|
||||||
Data::$db->subscriptionRemove($this->user, 2112);
|
Data::$db->subscriptionRemove($this->user, 2112);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testRemoveASubscriptionForTheWrongOwner() {
|
function testRemoveASubscriptionForTheWrongOwner() {
|
||||||
$this->user = "jane.doe@example.com";
|
$this->user = "jane.doe@example.com";
|
||||||
$this->assertException("idMissing", "Db", "ExceptionInput");
|
$this->assertException("subjectMissing", "Db", "ExceptionInput");
|
||||||
Data::$db->subscriptionRemove($this->user, 1);
|
Data::$db->subscriptionRemove($this->user, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,31 +257,44 @@ trait SeriesSubscription {
|
||||||
$state['arsse_subscriptions']['rows'][0] = [1,"john.doe@example.com",2,"Ook Ook",3,0,0];
|
$state['arsse_subscriptions']['rows'][0] = [1,"john.doe@example.com",2,"Ook Ook",3,0,0];
|
||||||
$this->compareExpectations($state);
|
$this->compareExpectations($state);
|
||||||
Data::$db->subscriptionPropertiesSet($this->user, 1,[
|
Data::$db->subscriptionPropertiesSet($this->user, 1,[
|
||||||
'title' => " ",
|
'title' => null,
|
||||||
]);
|
]);
|
||||||
$state['arsse_subscriptions']['rows'][0] = [1,"john.doe@example.com",2,null,3,0,0];
|
$state['arsse_subscriptions']['rows'][0] = [1,"john.doe@example.com",2,null,3,0,0];
|
||||||
$this->compareExpectations($state);
|
$this->compareExpectations($state);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testMoveSubscriptionToAMissingFolder() {
|
function testMoveASubscriptionToAMissingFolder() {
|
||||||
$this->assertException("idMissing", "Db", "ExceptionInput");
|
$this->assertException("idMissing", "Db", "ExceptionInput");
|
||||||
Data::$db->subscriptionPropertiesSet($this->user, 1,[
|
Data::$db->subscriptionPropertiesSet($this->user, 1, ['folder' => 4]);
|
||||||
'folder' => 4,
|
}
|
||||||
]);
|
|
||||||
|
function testRenameASubscriptionToABlankTitle() {
|
||||||
|
$this->assertException("missing", "Db", "ExceptionInput");
|
||||||
|
Data::$db->subscriptionPropertiesSet($this->user, 1, ['title' => ""]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testRenameASubscriptionToAWhitespaceTitle() {
|
||||||
|
$this->assertException("whitespace", "Db", "ExceptionInput");
|
||||||
|
Data::$db->subscriptionPropertiesSet($this->user, 1, ['title' => " "]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testRenameASubscriptionToFalse() {
|
||||||
|
$this->assertException("missing", "Db", "ExceptionInput");
|
||||||
|
Data::$db->subscriptionPropertiesSet($this->user, 1, ['title' => false]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testRenameASubscriptionToZero() {
|
||||||
|
$this->assertTrue(Data::$db->subscriptionPropertiesSet($this->user, 1, ['title' => 0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function testSetThePropertiesOfAMissingSubscription() {
|
function testSetThePropertiesOfAMissingSubscription() {
|
||||||
$this->assertException("idMissing", "Db", "ExceptionInput");
|
$this->assertException("subjectMissing", "Db", "ExceptionInput");
|
||||||
Data::$db->subscriptionPropertiesSet($this->user, 2112,[
|
Data::$db->subscriptionPropertiesSet($this->user, 2112, ['folder' => null]);
|
||||||
'folder' => null,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function testSetThePropertiesOfASubscriptionWithoutAuthority() {
|
function testSetThePropertiesOfASubscriptionWithoutAuthority() {
|
||||||
Phake::when(Data::$user)->authorize->thenReturn(false);
|
Phake::when(Data::$user)->authorize->thenReturn(false);
|
||||||
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
|
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
|
||||||
Data::$db->subscriptionPropertiesSet($this->user, 1,[
|
Data::$db->subscriptionPropertiesSet($this->user, 1, ['folder' => null]);
|
||||||
'folder' => null,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue