diff --git a/lib/REST/TinyTinyRSS/API.php b/lib/REST/TinyTinyRSS/API.php index f0648de..e34081d 100644 --- a/lib/REST/TinyTinyRSS/API.php +++ b/lib/REST/TinyTinyRSS/API.php @@ -130,4 +130,33 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { // session validity is already checked by the dispatcher, so we need only return true return ['status' => true]; } + + public function opAddCategory(array $data) { + $in = [ + 'name' => isset($data['caption']) ? $data['caption'] : "", + 'parent' => isset($data['parent_id']) ? $data['parent_id'] : null, + ]; + if (!$in['parent']) { + $in['parent'] = null; + } + try { + return Arsse::$db->folderAdd(Arsse::$user->id, $in); + } catch (ExceptionInput $e) { + switch ($e->getCode()) { + // folder already exists + case 10236: + // retrieve the ID of the existing folder; duplicating a category silently returns the existing one + $folders = Arsse::$db->folderList(Arsse::$user->id, $in['parent'], false); + foreach ($folders as $folder) { + if ($folder['name']==$in['name']) { + return (int) $folder['id']; + } + } + // parent folder does not exist; this returns false as an ID + case 10235: return false; + // other errors related to input + default: throw new Exception("INCORRECT_USAGE"); + } + } + } } \ No newline at end of file diff --git a/tests/REST/TinyTinyRSS/TestTinyTinyAPI.php b/tests/REST/TinyTinyRSS/TestTinyTinyAPI.php index f18adea..ebb42b8 100644 --- a/tests/REST/TinyTinyRSS/TestTinyTinyAPI.php +++ b/tests/REST/TinyTinyRSS/TestTinyTinyAPI.php @@ -143,7 +143,7 @@ class TestTinyTinyAPI extends Test\AbstractTest { ] ]; - protected function respGood(array $content, $seq = 0): Response { + protected function respGood($content = null, $seq = 0): Response { return new Response(200, [ 'seq' => $seq, 'status' => 0, @@ -151,7 +151,7 @@ class TestTinyTinyAPI extends Test\AbstractTest { ]); } - protected function respErr(string $msg, array $content = [], $seq = 0): Response { + protected function respErr(string $msg, $content = [], $seq = 0): Response { $err = ['error' => $msg]; return new Response(200, [ 'seq' => $seq, @@ -248,4 +248,49 @@ class TestTinyTinyAPI extends Test\AbstractTest { $exp = $this->respGood(['level' => \JKingWeb\Arsse\REST\TinyTinyRSS\API::LEVEL]); $this->assertEquals($exp, $this->h->dispatch(new Request("POST", "", json_encode($data)))); } + + public function testAddACategory() { + $in = [ + ['op' => "addCategory", 'sid' => "PriestsOfSyrinx", 'caption' => "Software"], + ['op' => "addCategory", 'sid' => "PriestsOfSyrinx", 'caption' => "Hardware", 'parent_id' => 1], + ['op' => "addCategory", 'sid' => "PriestsOfSyrinx"], + ['op' => "addCategory", 'sid' => "PriestsOfSyrinx", 'caption' => ""], + ['op' => "addCategory", 'sid' => "PriestsOfSyrinx", 'caption' => " "], + ]; + $db = [ + ['name' => "Software", 'parent' => null], + ['name' => "Hardware", 'parent' => 1], + ]; + $out = [ + ['id' => 2, 'name' => "Software", 'parent' => null], + ['id' => 3, 'name' => "Hardware", 'parent' => 1], + ['id' => 1, 'name' => "Politics", 'parent' => null], + ]; + // set of various mocks for testing + Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, $db[0])->thenReturn(2)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call + Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, $db[1])->thenReturn(3)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call + Phake::when(Arsse::$db)->folderList(Arsse::$user->id, null, false)->thenReturn(new Result([$out[0], $out[2]])); + Phake::when(Arsse::$db)->folderList(Arsse::$user->id, 1, false)->thenReturn(new Result([$out[1]])); + // set up mocks that produce errors + Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, [])->thenThrow(new ExceptionInput("missing")); + Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, ['name' => "", 'parent' => null])->thenThrow(new ExceptionInput("missing")); + Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, ['name' => " ", 'parent' => null])->thenThrow(new ExceptionInput("whitespace")); + // correctly add two folders + $exp = $this->respGood(2); + $this->assertEquals($exp, $this->h->dispatch(new Request("POST", "", json_encode($in[0])))); + $exp = $this->respGood(3); + $this->assertEquals($exp, $this->h->dispatch(new Request("POST", "", json_encode($in[1])))); + // attempt to add the two folders again + $exp = $this->respGood(2); + $this->assertEquals($exp, $this->h->dispatch(new Request("POST", "", json_encode($in[0])))); + $exp = $this->respGood(3); + $this->assertEquals($exp, $this->h->dispatch(new Request("POST", "", json_encode($in[1])))); + Phake::verify(Arsse::$db)->folderList(Arsse::$user->id, null, false); + Phake::verify(Arsse::$db)->folderList(Arsse::$user->id, 1, false); + // add some invalid folders + $exp = $this->respErr("INCORRECT_USAGE"); + $this->assertEquals($exp, $this->h->dispatch(new Request("POST", "", json_encode($in[2])))); + $this->assertEquals($exp, $this->h->dispatch(new Request("POST", "", json_encode($in[3])))); + $this->assertEquals($exp, $this->h->dispatch(new Request("POST", "", json_encode($in[4])))); + } } \ No newline at end of file