diff --git a/lib/AbstractException.php b/lib/AbstractException.php index 433d5f4..b8fa079 100644 --- a/lib/AbstractException.php +++ b/lib/AbstractException.php @@ -43,6 +43,7 @@ abstract class AbstractException extends \Exception { "Db/ExceptionInput.constraintViolation" => 10236, "Db/ExceptionInput.typeViolation" => 10237, "Db/ExceptionInput.circularDependence" => 10238, + "Db/ExceptionInput.subjectMissing" => 10239, "Db/ExceptionTimeout.general" => 10241, "Conf/Exception.fileMissing" => 10301, "Conf/Exception.fileUnusable" => 10302, diff --git a/lib/Database.php b/lib/Database.php index 17e8ad7..16fe21a 100644 --- a/lib/Database.php +++ b/lib/Database.php @@ -344,25 +344,23 @@ class Database { public function folderRemove(string $user, int $id): bool { 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(); - 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; } public function folderPropertiesGet(string $user, int $id): array { 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(); - 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; } 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]); // validate the folder ID and, if specified, the parent to move it to - if(array_key_exists("parent", $data)) { - $f = $this->folderValidateId($user, $id, $data['parent']); - } else { - $f = $this->folderValidateId($user, $id); - } + $parent = null; + if(array_key_exists("parent", $data)) $parent = $data['parent']; + $f = $this->folderValidateId($user, $id, $parent, true); // if a new name is specified, validate it if(array_key_exists("name", $data)) { $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(); } - 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 no ID is specified this is a no-op, unless a parent is specified, which is always a circular dependence if(!is_null($parent)) { @@ -391,7 +389,7 @@ class Database { } // 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(); - 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(!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) @@ -413,7 +411,7 @@ class Database { protected function folderValidateName($name): bool { $name = (string) $name; - if($name=="") { + if(!strlen($name)) { throw new Db\ExceptionInput("missing", ["action" => $this->caller(), "field" => "name"]); } else if(!strlen(trim($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 { 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(); - 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; } @@ -483,7 +481,7 @@ class Database { Data::$user->authorizationEnabled(false); $sub = $this->subscriptionList($user, null, $id)->getRow(); 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; } @@ -492,18 +490,20 @@ class Database { $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 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)) { // ensure the target folder exists and belong to the user $this->folderValidateId($user, $data['folder']); } 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 - $title = (string) $data['title']; - $title = trim($title); - if($title==="") $title = null; - $data['title'] = $title; + // 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']; + if(!strlen($title)) throw new Db\ExceptionInput("missing", ["action" => __FUNCTION__, "field" => "title"]); + if(!strlen(trim($title))) throw new Db\ExceptionInput("whitespace", ["action" => __FUNCTION__, "field" => "title"]); + $data['title'] = $title; + } } $valid = [ 'title' => "str", @@ -558,7 +558,7 @@ class Database { $tr = $this->db->begin(); // 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(); - 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 // 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 diff --git a/lib/REST/NextCloudNews/V1_2.php b/lib/REST/NextCloudNews/V1_2.php index 59bdd0b..9061e5a 100644 --- a/lib/REST/NextCloudNews/V1_2.php +++ b/lib/REST/NextCloudNews/V1_2.php @@ -159,7 +159,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { } catch(ExceptionInput $e) { switch($e->getCode()) { // folder does not exist - case 10235: return new Response(404); + case 10239: return new Response(404); // folder already exists case 10236: return new Response(409); // folder name not acceptable @@ -206,13 +206,12 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { } $out = ['feeds' => $out]; $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; return new Response(200, $out); } // return list of feeds which should be refreshed - // refresh a feed protected function feedListStale(array $url, array $data): Response { // function requires admin rights per spec 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 { Data::$db->subscriptionPropertiesSet(Data::$user->id, (int) $url[1], $in); } 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); } @@ -326,7 +333,14 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { try { Data::$db->subscriptionPropertiesSet(Data::$user->id, (int) $url[1], $in); } 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); } diff --git a/locale/en.php b/locale/en.php index 97d8c78..137af98 100644 --- a/locale/en.php +++ b/locale/en.php @@ -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.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.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.circularDependence' => 'Referenced ID ({id}) in field "{field}" creates a circular dependence', 'Exception.JKingWeb/Arsse/Db/ExceptionInput.constraintViolation' => '{0}', diff --git a/tests/REST/NextCloudNews/TestNCNV1_2.php b/tests/REST/NextCloudNews/TestNCNV1_2.php index c88c273..d5f6782 100644 --- a/tests/REST/NextCloudNews/TestNCNV1_2.php +++ b/tests/REST/NextCloudNews/TestNCNV1_2.php @@ -11,6 +11,68 @@ class TestNCNV1_2 extends \PHPUnit\Framework\TestCase { use Test\Tools; 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() { $this->clearData(); @@ -78,7 +140,9 @@ class TestNCNV1_2 extends \PHPUnit\Framework\TestCase { ['id' => 1, 'name' => "Software", '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]); $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/folders"))); } @@ -123,7 +187,7 @@ class TestNCNV1_2 extends \PHPUnit\Framework\TestCase { } 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); $this->assertEquals($exp, $this->h->dispatch(new Request("DELETE", "/folders/1"))); // 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[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, 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); $this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[0]), 'application/json'))); $exp = new Response(409); @@ -164,4 +228,122 @@ class TestNCNV1_2 extends \PHPUnit\Framework\TestCase { $exp = new Response(200, ['version' => \JKingWeb\Arsse\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'))); + } } \ No newline at end of file diff --git a/tests/lib/Database/SeriesFolder.php b/tests/lib/Database/SeriesFolder.php index 2042c49..9311071 100644 --- a/tests/lib/Database/SeriesFolder.php +++ b/tests/lib/Database/SeriesFolder.php @@ -139,12 +139,12 @@ trait SeriesFolder { } function testRemoveAMissingFolder() { - $this->assertException("idMissing", "Db", "ExceptionInput"); + $this->assertException("subjectMissing", "Db", "ExceptionInput"); Data::$db->folderRemove("john.doe@example.com", 2112); } 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 } @@ -165,12 +165,12 @@ trait SeriesFolder { } function testGetThePropertiesOfAMissingFolder() { - $this->assertException("idMissing", "Db", "ExceptionInput"); + $this->assertException("subjectMissing", "Db", "ExceptionInput"); Data::$db->folderPropertiesGet("john.doe@example.com", 2112); } 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 } @@ -217,12 +217,12 @@ trait SeriesFolder { } function testSetThePropertiesOfAMissingFolder() { - $this->assertException("idMissing", "Db", "ExceptionInput"); + $this->assertException("subjectMissing", "Db", "ExceptionInput"); Data::$db->folderPropertiesSet("john.doe@example.com", 2112, ['parent' => null]); } - function testSetThePropertiesOfAFolderOfTheWrongOwner() { - $this->assertException("idMissing", "Db", "ExceptionInput"); + function testSetThePropertiesOfAFolderForTheWrongOwner() { + $this->assertException("subjectMissing", "Db", "ExceptionInput"); Data::$db->folderPropertiesSet("john.doe@example.com", 4, ['parent' => null]); // folder ID 4 belongs to Jane } diff --git a/tests/lib/Database/SeriesSubscription.php b/tests/lib/Database/SeriesSubscription.php index 0b72547..691ef9e 100644 --- a/tests/lib/Database/SeriesSubscription.php +++ b/tests/lib/Database/SeriesSubscription.php @@ -167,13 +167,13 @@ trait SeriesSubscription { } function testRemoveAMissingSubscription() { - $this->assertException("idMissing", "Db", "ExceptionInput"); + $this->assertException("subjectMissing", "Db", "ExceptionInput"); Data::$db->subscriptionRemove($this->user, 2112); } function testRemoveASubscriptionForTheWrongOwner() { $this->user = "jane.doe@example.com"; - $this->assertException("idMissing", "Db", "ExceptionInput"); + $this->assertException("subjectMissing", "Db", "ExceptionInput"); 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]; $this->compareExpectations($state); Data::$db->subscriptionPropertiesSet($this->user, 1,[ - 'title' => " ", + 'title' => null, ]); $state['arsse_subscriptions']['rows'][0] = [1,"john.doe@example.com",2,null,3,0,0]; $this->compareExpectations($state); } - function testMoveSubscriptionToAMissingFolder() { + function testMoveASubscriptionToAMissingFolder() { $this->assertException("idMissing", "Db", "ExceptionInput"); - Data::$db->subscriptionPropertiesSet($this->user, 1,[ - 'folder' => 4, - ]); + Data::$db->subscriptionPropertiesSet($this->user, 1, ['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() { - $this->assertException("idMissing", "Db", "ExceptionInput"); - Data::$db->subscriptionPropertiesSet($this->user, 2112,[ - 'folder' => null, - ]); + $this->assertException("subjectMissing", "Db", "ExceptionInput"); + Data::$db->subscriptionPropertiesSet($this->user, 2112, ['folder' => null]); } function testSetThePropertiesOfASubscriptionWithoutAuthority() { Phake::when(Data::$user)->authorize->thenReturn(false); $this->assertException("notAuthorized", "User", "ExceptionAuthz"); - Data::$db->subscriptionPropertiesSet($this->user, 1,[ - 'folder' => null, - ]); + Data::$db->subscriptionPropertiesSet($this->user, 1, ['folder' => null]); } } \ No newline at end of file