diff --git a/docs/en/030_Supported_Protocols/005_Miniflux.md b/docs/en/030_Supported_Protocols/005_Miniflux.md index 0d5792e..84e7d28 100644 --- a/docs/en/030_Supported_Protocols/005_Miniflux.md +++ b/docs/en/030_Supported_Protocols/005_Miniflux.md @@ -29,6 +29,7 @@ Miniflux version 2.0.26 is emulated, though not all features are implemented # Differences - Various error messages differ due to significant implementation differences +- `PUT` requests which return a body respond with `200 OK` rather than `201 Created` - Only the URL should be considered reliable in feed discovery results - The "All" category is treated specially (see below for details) - Category names consisting only of whitespace are rejected along with the empty string diff --git a/lib/REST/Miniflux/V1.php b/lib/REST/Miniflux/V1.php index fd6baa9..d84a7e9 100644 --- a/lib/REST/Miniflux/V1.php +++ b/lib/REST/Miniflux/V1.php @@ -331,10 +331,12 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { } protected function updateCategory(array $path, array $query, array $data): ResponseInterface { + // category IDs in Miniflux are always greater than 1; we have folder 0, so we decrement category IDs by 1 to get the folder ID $folder = $path[1] - 1; $title = $data['title'] ?? ""; try { if ($folder === 0) { + // folder 0 doesn't actually exist in the database, so its name is kept as user metadata if (!strlen(trim($title))) { throw new ExceptionInput("whitespace"); } @@ -345,7 +347,7 @@ class V1 extends \JKingWeb\Arsse\REST\AbstractHandler { } catch (ExceptionInput $e) { if ($e->getCode() === 10236) { return new ErrorResponse(["DuplicateCategory", 'title' => $title], 500); - } elseif ($e->getCode === 10239) { + } elseif ($e->getCode() === 10239) { return new ErrorResponse("404", 404); } else { return new ErrorResponse(["InvalidCategory", 'title' => $title], 500); diff --git a/tests/cases/REST/Miniflux/TestV1.php b/tests/cases/REST/Miniflux/TestV1.php index 0259948..f9bb423 100644 --- a/tests/cases/REST/Miniflux/TestV1.php +++ b/tests/cases/REST/Miniflux/TestV1.php @@ -283,4 +283,44 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest { [false, new ErrorResponse(["InvalidInputType", 'field' => "title", 'actual' => "boolean", 'expected' => "string"], 400)], ]; } + + /** @dataProvider provideCategoryUpdates */ + public function testRenameACategory(int $id, $title, ResponseInterface $exp): void { + Arsse::$user->method("propertiesSet")->willReturn(['root_folder_name' => $title]); + if (!in_array($id, [1,2])) { + \Phake::when(Arsse::$db)->folderPropertiesSet->thenThrow(new ExceptionInput("subjectMissing")); + } elseif (!strlen((string) $title)) { + \Phake::when(Arsse::$db)->folderPropertiesSet->thenThrow(new ExceptionInput("missing")); + } elseif (!strlen(trim((string) $title))) { + \Phake::when(Arsse::$db)->folderPropertiesSet->thenThrow(new ExceptionInput("whitespace")); + } elseif ($title === "Duplicate") { + \Phake::when(Arsse::$db)->folderPropertiesSet->thenThrow(new ExceptionInput("constraintViolation")); + } else { + \Phake::when(Arsse::$db)->folderPropertiesSet->thenReturn(true); + } + if ($id === 1) { + $times = (int) (is_string($title) && strlen(trim($title))); + Arsse::$user->expects($this->exactly($times))->method("propertiesSet")->with("john.doe@example.com", ['root_folder_name' => $title]); + } + $this->assertMessage($exp, $this->req("PUT", "/categories/$id", ['title' => $title])); + if ($id !== 1 && is_string($title)) { + \Phake::verify(Arsse::$db)->folderPropertiesSet("john.doe@example.com", $id - 1, ['name' => $title]); + } + } + + public function provideCategoryUpdates(): iterable { + return [ + [3, "New", new ErrorResponse("404", 404)], + [2, "New", new Response(['id' => 2, 'title' => "New", 'user_id' => 42])], + [2, "Duplicate", new ErrorResponse(["DuplicateCategory", 'title' => "Duplicate"], 500)], + [2, "", new ErrorResponse(["InvalidCategory", 'title' => ""], 500)], + [2, " ", new ErrorResponse(["InvalidCategory", 'title' => " "], 500)], + [2, false, new ErrorResponse(["InvalidInputType", 'field' => "title", 'actual' => "boolean", 'expected' => "string"], 400)], + [1, "New", new Response(['id' => 1, 'title' => "New", 'user_id' => 42])], + [1, "Duplicate", new Response(['id' => 1, 'title' => "Duplicate", 'user_id' => 42])], // This is allowed because the name of the root folder is only a duplicate in circumstances where it is used + [1, "", new ErrorResponse(["InvalidCategory", 'title' => ""], 500)], + [1, " ", new ErrorResponse(["InvalidCategory", 'title' => " "], 500)], + [1, false, new ErrorResponse(["InvalidInputType", 'field' => "title", 'actual' => "boolean", 'expected' => "string"], 400)], + ]; + } }