@ -26,7 +26,6 @@ use JKingWeb\Arsse\User\ExceptionConflict;
use JKingWeb\Arsse\User\ExceptionInput as UserExceptionInput;
use JKingWeb\Arsse\Test\Result;
use Psr\Http\Message\ResponseInterface;
use Laminas\Diactoros\Response\JsonResponse as Response;
use Laminas\Diactoros\Response\TextResponse;
/** @covers \JKingWeb\Arsse\REST\Miniflux\V1< extended > */
@ -182,8 +181,8 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
['title' => "Feed", 'type' => "rss", 'url' => "http://localhost:8000/Feed/Discovery/Missing"],
];
return [
["http://localhost:8000/Feed/Discovery/Valid", new Response ($discovered)],
["http://localhost:8000/Feed/Discovery/Invalid", new Response ([])],
["http://localhost:8000/Feed/Discovery/Valid", HTTP::respJson ($discovered)],
["http://localhost:8000/Feed/Discovery/Invalid", HTTP::respJson ([])],
["http://localhost:8000/Feed/Discovery/Missing", new ErrorResponse("Fetch404", 502)],
[1, new ErrorResponse(["InvalidInputType", 'field' => "url", 'expected' => "string", 'actual' => "integer"], 422)],
["Not a URL", new ErrorResponse(["InvalidInputValue", 'field' => "url"], 422)],
@ -226,16 +225,16 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
public function provideUserQueries(): iterable {
self::clearData();
return [
[true, "/users", new Response (self::USERS)],
[true, "/me", new Response (self::USERS[0])],
[true, "/users/john.doe@example.com", new Response (self::USERS[0])],
[true, "/users/1", new Response (self::USERS[0])],
[true, "/users/jane.doe@example.com", new Response (self::USERS[1])],
[true, "/users/2", new Response (self::USERS[1])],
[true, "/users", HTTP::respJson (self::USERS)],
[true, "/me", HTTP::respJson (self::USERS[0])],
[true, "/users/john.doe@example.com", HTTP::respJson (self::USERS[0])],
[true, "/users/1", HTTP::respJson (self::USERS[0])],
[true, "/users/jane.doe@example.com", HTTP::respJson (self::USERS[1])],
[true, "/users/2", HTTP::respJson (self::USERS[1])],
[true, "/users/jack.doe@example.com", new ErrorResponse("404", 404)],
[true, "/users/47", new ErrorResponse("404", 404)],
[false, "/users", new ErrorResponse("403", 403)],
[false, "/me", new Response (self::USERS[1])],
[false, "/me", HTTP::respJson (self::USERS[1])],
[false, "/users/john.doe@example.com", new ErrorResponse("403", 403)],
[false, "/users/1", new ErrorResponse("403", 403)],
[false, "/users/jane.doe@example.com", new ErrorResponse("403", 403)],
@ -310,16 +309,16 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
[false, "/users/1", ['entry_sorting_direction' => "bad"], null, null, null, null, null, null, new ErrorResponse(["InvalidInputValue", 'field' => "entry_sorting_direction"], 422)],
[false, "/users/1", ['theme' => "stark"], null, null, null, null, null, null, new ErrorResponse("403", 403)],
[false, "/users/2", ['is_admin' => true], null, null, null, null, null, null, new ErrorResponse("InvalidElevation", 403)],
[false, "/users/2", ['language' => "fr_CA"], null, null, null, null, ['lang' => "fr_CA"], $out1, new Response ($resp1, 201)],
[false, "/users/2", ['entry_sorting_direction' => "asc"], null, null, null, null, ['sort_asc' => true], $out1, new Response ($resp1, 201)],
[false, "/users/2", ['entry_sorting_direction' => "desc"], null, null, null, null, ['sort_asc' => false], $out1, new Response ($resp1, 201)],
[false, "/users/2", ['language' => "fr_CA"], null, null, null, null, ['lang' => "fr_CA"], $out1, HTTP::respJson ($resp1, 201)],
[false, "/users/2", ['entry_sorting_direction' => "asc"], null, null, null, null, ['sort_asc' => true], $out1, HTTP::respJson ($resp1, 201)],
[false, "/users/2", ['entry_sorting_direction' => "desc"], null, null, null, null, ['sort_asc' => false], $out1, HTTP::respJson ($resp1, 201)],
[false, "/users/2", ['entries_per_page' => -1], null, null, null, null, ['page_size' => -1], new UserExceptionInput("invalidNonZeroInteger"), new ErrorResponse(["InvalidInputValue", 'field' => "entries_per_page"], 422)],
[false, "/users/2", ['timezone' => "Ook"], null, null, null, null, ['tz' => "Ook"], new UserExceptionInput("invalidTimezone"), new ErrorResponse(["InvalidInputValue", 'field' => "timezone"], 422)],
[false, "/users/2", ['username' => "j:k"], "j:k", new UserExceptionInput("invalidUsername"), null, null, null, null, new ErrorResponse(["InvalidInputValue", 'field' => "username"], 422)],
[false, "/users/2", ['username' => "ook"], "ook", new ExceptionConflict("alreadyExists"), null, null, null, null, new ErrorResponse(["DuplicateUser", 'user' => "ook"], 409)],
[false, "/users/2", ['password' => "ook"], null, null, "ook", "ook", null, null, new Response (array_merge($resp1, ['password' => "ook"]), 201)],
[false, "/users/2", ['username' => "ook", 'password' => "ook"], "ook", true, "ook", "ook", null, null, new Response (array_merge($resp1, ['username' => "ook", 'password' => "ook"]), 201)],
[true, "/users/1", ['theme' => "stark"], null, null, null, null, ['theme' => "stark"], $out2, new Response ($resp2, 201)],
[false, "/users/2", ['password' => "ook"], null, null, "ook", "ook", null, null, HTTP::respJson (array_merge($resp1, ['password' => "ook"]), 201)],
[false, "/users/2", ['username' => "ook", 'password' => "ook"], "ook", true, "ook", "ook", null, null, HTTP::respJson (array_merge($resp1, ['username' => "ook", 'password' => "ook"]), 201)],
[true, "/users/1", ['theme' => "stark"], null, null, null, null, ['theme' => "stark"], $out2, HTTP::respJson ($resp2, 201)],
[true, "/users/3", ['theme' => "stark"], null, null, null, null, null, null, new ErrorResponse("404", 404)],
];
}
@ -367,7 +366,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
[['username' => "j:k", 'password' => "eek"], ["j:k", "eek"], new UserExceptionInput("invalidUsername"), null, null, new ErrorResponse(["InvalidInputValue", 'field' => "username"], 422)],
[['username' => "ook", 'password' => "eek", 'timezone' => "ook"], ["ook", "eek"], "eek", ['tz' => "ook"], new UserExceptionInput("invalidTimezone"), new ErrorResponse(["InvalidInputValue", 'field' => "timezone"], 422)],
[['username' => "ook", 'password' => "eek", 'entries_per_page' => -1], ["ook", "eek"], "eek", ['page_size' => -1], new UserExceptionInput("invalidNonZeroInteger"), new ErrorResponse(["InvalidInputValue", 'field' => "entries_per_page"], 422)],
[['username' => "ook", 'password' => "eek", 'theme' => "default"], ["ook", "eek"], "eek", ['theme' => "default"], ['theme' => "default"], new Response ($resp1, 201)],
[['username' => "ook", 'password' => "eek", 'theme' => "default"], ["ook", "eek"], "eek", ['theme' => "default"], ['theme' => "default"], HTTP::respJson ($resp1, 201)],
];
}
@ -406,7 +405,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
['id' => 1, 'name' => "Science"],
['id' => 20, 'name' => "Technology"],
])));
$exp = new Response ([
$exp = HTTP::respJson ([
['id' => 1, 'title' => "All", 'user_id' => 42],
['id' => 2, 'title' => "Science", 'user_id' => 42],
['id' => 21, 'title' => "Technology", 'user_id' => 42],
@ -416,7 +415,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
// run test again with a renamed root folder
Arsse::$user = $this->createMock(User::class);
Arsse::$user->method("propertiesGet")->willReturn(['num' => 47, 'admin' => false, 'root_folder_name' => "Uncategorized"]);
$exp = new Response ([
$exp = HTTP::respJson ([
['id' => 1, 'title' => "Uncategorized", 'user_id' => 47],
['id' => 2, 'title' => "Science", 'user_id' => 47],
['id' => 21, 'title' => "Technology", 'user_id' => 47],
@ -440,7 +439,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
public function provideCategoryAdditions(): iterable {
return [
["New", new Response (['id' => 2112, 'title' => "New", 'user_id' => 42], 201)],
["New", HTTP::respJson (['id' => 2112, 'title' => "New", 'user_id' => 42], 201)],
["Duplicate", new ErrorResponse(["DuplicateCategory", 'title' => "Duplicate"], 409)],
["", new ErrorResponse(["InvalidCategory", 'title' => ""], 422)],
[" ", new ErrorResponse(["InvalidCategory", 'title' => " "], 422)],
@ -467,14 +466,14 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
public function provideCategoryUpdates(): iterable {
return [
[3, "New", "subjectMissing", new ErrorResponse("404", 404)],
[2, "New", true, new Response (['id' => 2, 'title' => "New", 'user_id' => 42], 201)],
[2, "New", true, HTTP::respJson (['id' => 2, 'title' => "New", 'user_id' => 42], 201)],
[2, "Duplicate", "constraintViolation", new ErrorResponse(["DuplicateCategory", 'title' => "Duplicate"], 409)],
[2, "", "missing", new ErrorResponse(["InvalidCategory", 'title' => ""], 422)],
[2, " ", "whitespace", new ErrorResponse(["InvalidCategory", 'title' => " "], 422)],
[2, null, "missing", new ErrorResponse(["MissingInputValue", 'field' => "title"], 422)],
[2, false, "subjectMissing", new ErrorResponse(["InvalidInputType", 'field' => "title", 'actual' => "boolean", 'expected' => "string"], 422)],
[1, "New", true, new Response (['id' => 1, 'title' => "New", 'user_id' => 42], 201)],
[1, "Duplicate", "constraintViolation", new Response (['id' => 1, 'title' => "Duplicate", 'user_id' => 42], 201)], // This is allowed because the name of the root folder is only a duplicate in circumstances where it is used
[1, "New", true, HTTP::respJson (['id' => 1, 'title' => "New", 'user_id' => 42], 201)],
[1, "Duplicate", "constraintViolation", HTTP::respJson (['id' => 1, 'title' => "Duplicate", 'user_id' => 42], 201)], // This is allowed because the name of the root folder is only a duplicate in circumstances where it is used
[1, "", "missing", new ErrorResponse(["InvalidCategory", 'title' => ""], 422)],
[1, " ", "whitespace", new ErrorResponse(["InvalidCategory", 'title' => " "], 422)],
[1, null, "missing", new ErrorResponse(["MissingInputValue", 'field' => "title"], 422)],
@ -510,20 +509,20 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
public function testListFeeds(): void {
$this->dbMock->subscriptionList->returns(new Result($this->v(self::FEEDS)));
$exp = new Response (self::FEEDS_OUT);
$exp = HTTP::respJson (self::FEEDS_OUT);
$this->assertMessage($exp, $this->req("GET", "/feeds"));
}
public function testListFeedsOfACategory(): void {
$this->dbMock->subscriptionList->returns(new Result($this->v(self::FEEDS)));
$exp = new Response (self::FEEDS_OUT);
$exp = HTTP::respJson (self::FEEDS_OUT);
$this->assertMessage($exp, $this->req("GET", "/categories/2112/feeds"));
$this->dbMock->subscriptionList->calledWith(Arsse::$user->id, 2111, true);
}
public function testListFeedsOfTheRootCategory(): void {
$this->dbMock->subscriptionList->returns(new Result($this->v(self::FEEDS)));
$exp = new Response (self::FEEDS_OUT);
$exp = HTTP::respJson (self::FEEDS_OUT);
$this->assertMessage($exp, $this->req("GET", "/categories/1/feeds"));
$this->dbMock->subscriptionList->calledWith(Arsse::$user->id, 0, false);
}
@ -537,9 +536,9 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
public function testGetAFeed(): void {
$this->dbMock->subscriptionPropertiesGet->returns($this->v(self::FEEDS[0]))->returns($this->v(self::FEEDS[1]));
$this->assertMessage(new Response (self::FEEDS_OUT[0]), $this->req("GET", "/feeds/1"));
$this->assertMessage(HTTP::respJson (self::FEEDS_OUT[0]), $this->req("GET", "/feeds/1"));
$this->dbMock->subscriptionPropertiesGet->calledWith(Arsse::$user->id, 1);
$this->assertMessage(new Response (self::FEEDS_OUT[1]), $this->req("GET", "/feeds/55"));
$this->assertMessage(HTTP::respJson (self::FEEDS_OUT[1]), $this->req("GET", "/feeds/55"));
$this->dbMock->subscriptionPropertiesGet->calledWith(Arsse::$user->id, 55);
}
@ -634,16 +633,16 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
[['feed_url' => "http://example.com/", 'category_id' => 1], new FeedException("unsupportedFeedFormat"), null, null, null, new ErrorResponse("FetchFormat", 502)],
[['feed_url' => "http://example.com/", 'category_id' => 1], 2112, new ExceptionInput("constraintViolation"), null, null, new ErrorResponse("DuplicateFeed", 409)],
[['feed_url' => "http://example.com/", 'category_id' => 1], 2112, 44, new ExceptionInput("idMissing"), null, new ErrorResponse("MissingCategory", 422)],
[['feed_url' => "http://example.com/", 'category_id' => 1], 2112, 44, true, null, new Response (['feed_id' => 44], 201)],
[['feed_url' => "http://example.com/", 'category_id' => 1, 'keeplist_rules' => "^A"], 2112, 44, true, true, new Response (['feed_id' => 44], 201)],
[['feed_url' => "http://example.com/", 'category_id' => 1, 'blocklist_rules' => "A"], 2112, 44, true, true, new Response (['feed_id' => 44], 201)],
[['feed_url' => "http://example.com/", 'category_id' => 1], 2112, 44, true, null, HTTP::respJson (['feed_id' => 44], 201)],
[['feed_url' => "http://example.com/", 'category_id' => 1, 'keeplist_rules' => "^A"], 2112, 44, true, true, HTTP::respJson (['feed_id' => 44], 201)],
[['feed_url' => "http://example.com/", 'category_id' => 1, 'blocklist_rules' => "A"], 2112, 44, true, true, HTTP::respJson (['feed_id' => 44], 201)],
];
}
/** @dataProvider provideFeedModifications */
public function testModifyAFeed(array $in, array $data, $out, ResponseInterface $exp): void {
$this->h = $this->partialMock(V1::class);
$this->h->getFeed->returns(new Response (self::FEEDS_OUT[0]));
$this->h->getFeed->returns(HTTP::respJson (self::FEEDS_OUT[0]));
if ($out instanceof \Exception) {
$this->dbMock->subscriptionPropertiesSet->throws($out);
} else {
@ -655,7 +654,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
public function provideFeedModifications(): iterable {
self::clearData();
$success = new Response (self::FEEDS_OUT[0], 201);
$success = HTTP::respJson (self::FEEDS_OUT[0], 201);
return [
[[], [], true, $success],
[[], [], new ExceptionInput("subjectMissing"), new ErrorResponse("404", 404)],
@ -672,9 +671,9 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
public function testModifyAFeedWithNoBody(): void {
$this->h = $this->partialMock(V1::class);
$this->h->getFeed->returns(new Response (self::FEEDS_OUT[0]));
$this->h->getFeed->returns(HTTP::respJson (self::FEEDS_OUT[0]));
$this->dbMock->subscriptionPropertiesSet->returns(true);
$this->assertMessage(new Response (self::FEEDS_OUT[0], 201), $this->req("PUT", "/feeds/2112", ""));
$this->assertMessage(HTTP::respJson (self::FEEDS_OUT[0], 201), $this->req("PUT", "/feeds/2112", ""));
$this->dbMock->subscriptionPropertiesSet->calledWith(Arsse::$user->id, 2112, []);
}
@ -703,7 +702,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
public function provideIcons(): iterable {
return [
[['id' => 44, 'type' => "image/svg+xml", 'data' => "< svg / > "], new Response (['id' => 44, 'data' => "image/svg+xml;base64,PHN2Zy8+", 'mime_type' => "image/svg+xml"])],
[['id' => 44, 'type' => "image/svg+xml", 'data' => "< svg / > "], HTTP::respJson (['id' => 44, 'data' => "image/svg+xml;base64,PHN2Zy8+", 'mime_type' => "image/svg+xml"])],
[['id' => 47, 'type' => "", 'data' => "< svg / > "], new ErrorResponse("404", 404)],
[['id' => 47, 'type' => null, 'data' => "< svg / > "], new ErrorResponse("404", 404)],
[['id' => 47, 'type' => null, 'data' => null], new ErrorResponse("404", 404)],
@ -755,50 +754,50 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
["/entries?order=false", null, null, [], false, new ErrorResponse(["InvalidInputValue", 'field' => "order"], 400)],
["/entries?starred& starred", null, null, [], false, new ErrorResponse(["DuplicateInputValue", 'field' => "starred"], 400)],
["/entries?after& after=0", null, null, [], false, new ErrorResponse(["DuplicateInputValue", 'field' => "after"], 400)],
["/entries", $c, $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?category_id=47", (clone $c)->folder(46), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?category_id=1", (clone $c)->folderShallow(0), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?status=unread", (clone $c)->unread(true)->hidden(false), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?status=read", (clone $c)->unread(false)->hidden(false), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?status=removed", (clone $c)->hidden(true), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?status=unread& status=read", (clone $c)->hidden(false), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?status=unread& status=removed", new UnionContext((clone $c)->unread(true), (clone $c)->hidden(true)), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?status=removed& status=read", new UnionContext((clone $c)->unread(false), (clone $c)->hidden(true)), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?status=removed& status=read& status=removed", new UnionContext((clone $c)->unread(false), (clone $c)->hidden(true)), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?status=removed& status=read& status=unread", $c, $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?starred", (clone $c)->starred(true), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?starred=", (clone $c)->starred(true), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?starred=true", (clone $c)->starred(true), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?starred=false", (clone $c)->starred(true), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?after=0", (clone $c)->modifiedRange(0, null), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?before=0", $c, $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?before=1", (clone $c)->modifiedRange(null, 1), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?before=1& after=0", (clone $c)->modifiedRange(0, 1), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?after_entry_id=42", (clone $c)->articleRange(43, null), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?before_entry_id=47", (clone $c)->articleRange(null, 46), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?search=alpha%20beta", (clone $c)->searchTerms(["alpha", "beta"]), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?limit=4", (clone $c)->limit(4), $o, self::ENTRIES, true, new Response (['total' => 2112, 'entries' => self::ENTRIES_OUT])],
["/entries?offset=20", (clone $c)->offset(20), $o, [], true, new Response (['total' => 2112, 'entries' => []])],
["/entries?direction=asc", $c, $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=id", $c, ["id"], self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=published_at", $c, ["modified_date"], self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=category_id", $c, ["top_folder"], self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=category_title", $c, ["top_folder_name"], self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=status", $c, ["hidden", "unread desc"], self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?direction=desc", $c, ["modified_date desc"], self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=id& direction=desc", $c, ["id desc"], self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=published_at& direction=desc", $c, ["modified_date desc"], self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=category_id& direction=desc", $c, ["top_folder desc"], self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=category_title& direction=desc", $c, ["top_folder_name desc"], self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=status& direction=desc", $c, ["hidden desc", "unread"], self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries", $c, $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?category_id=47", (clone $c)->folder(46), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?category_id=1", (clone $c)->folderShallow(0), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?status=unread", (clone $c)->unread(true)->hidden(false), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?status=read", (clone $c)->unread(false)->hidden(false), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?status=removed", (clone $c)->hidden(true), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?status=unread& status=read", (clone $c)->hidden(false), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?status=unread& status=removed", new UnionContext((clone $c)->unread(true), (clone $c)->hidden(true)), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?status=removed& status=read", new UnionContext((clone $c)->unread(false), (clone $c)->hidden(true)), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?status=removed& status=read& status=removed", new UnionContext((clone $c)->unread(false), (clone $c)->hidden(true)), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?status=removed& status=read& status=unread", $c, $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?starred", (clone $c)->starred(true), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?starred=", (clone $c)->starred(true), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?starred=true", (clone $c)->starred(true), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?starred=false", (clone $c)->starred(true), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?after=0", (clone $c)->modifiedRange(0, null), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?before=0", $c, $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?before=1", (clone $c)->modifiedRange(null, 1), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?before=1& after=0", (clone $c)->modifiedRange(0, 1), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?after_entry_id=42", (clone $c)->articleRange(43, null), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?before_entry_id=47", (clone $c)->articleRange(null, 46), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?search=alpha%20beta", (clone $c)->searchTerms(["alpha", "beta"]), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?limit=4", (clone $c)->limit(4), $o, self::ENTRIES, true, HTTP::respJson (['total' => 2112, 'entries' => self::ENTRIES_OUT])],
["/entries?offset=20", (clone $c)->offset(20), $o, [], true, HTTP::respJson (['total' => 2112, 'entries' => []])],
["/entries?direction=asc", $c, $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=id", $c, ["id"], self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=published_at", $c, ["modified_date"], self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=category_id", $c, ["top_folder"], self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=category_title", $c, ["top_folder_name"], self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=status", $c, ["hidden", "unread desc"], self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?direction=desc", $c, ["modified_date desc"], self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=id& direction=desc", $c, ["id desc"], self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=published_at& direction=desc", $c, ["modified_date desc"], self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=category_id& direction=desc", $c, ["top_folder desc"], self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=category_title& direction=desc", $c, ["top_folder_name desc"], self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?order=status& direction=desc", $c, ["hidden desc", "unread"], self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/entries?category_id=2112", (clone $c)->folder(2111), $o, new ExceptionInput("idMissing"), false, new ErrorResponse("MissingCategory")],
["/feeds/42/entries", (clone $c)->subscription(42), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/feeds/42/entries?category_id=47", (clone $c)->subscription(42)->folder(46), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/feeds/42/entries", (clone $c)->subscription(42), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/feeds/42/entries?category_id=47", (clone $c)->subscription(42)->folder(46), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/feeds/2112/entries", (clone $c)->subscription(2112), $o, new ExceptionInput("idMissing"), false, new ErrorResponse("404", 404)],
["/categories/42/entries", (clone $c)->folder(41), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/categories/42/entries?category_id=47", (clone $c)->folder(41), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/categories/42/entries?starred", (clone $c)->folder(41)->starred(true), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/categories/1/entries", (clone $c)->folderShallow(0), $o, self::ENTRIES, false, new Response (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/categories/42/entries", (clone $c)->folder(41), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/categories/42/entries?category_id=47", (clone $c)->folder(41), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/categories/42/entries?starred", (clone $c)->folder(41)->starred(true), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/categories/1/entries", (clone $c)->folderShallow(0), $o, self::ENTRIES, false, HTTP::respJson (['total' => sizeof(self::ENTRIES_OUT), 'entries' => self::ENTRIES_OUT])],
["/categories/2112/entries", (clone $c)->folder(2111), $o, new ExceptionInput("idMissing"), false, new ErrorResponse("404", 404)],
];
}
@ -828,17 +827,17 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
self::clearData();
$c = new Context;
return [
["/entries/42", (clone $c)->article(42), [self::ENTRIES[1]], new Response (self::ENTRIES_OUT[1])],
["/entries/42", (clone $c)->article(42), [self::ENTRIES[1]], HTTP::respJson (self::ENTRIES_OUT[1])],
["/entries/2112", (clone $c)->article(2112), new ExceptionInput("subjectMissing"), new ErrorResponse("404", 404)],
["/feeds/47/entries/42", (clone $c)->subscription(47)->article(42), [self::ENTRIES[1]], new Response (self::ENTRIES_OUT[1])],
["/feeds/47/entries/42", (clone $c)->subscription(47)->article(42), [self::ENTRIES[1]], HTTP::respJson (self::ENTRIES_OUT[1])],
["/feeds/47/entries/44", (clone $c)->subscription(47)->article(44), [], new ErrorResponse("404", 404)],
["/feeds/47/entries/2112", (clone $c)->subscription(47)->article(2112), new ExceptionInput("subjectMissing"), new ErrorResponse("404", 404)],
["/feeds/2112/entries/47", (clone $c)->subscription(2112)->article(47), new ExceptionInput("idMissing"), new ErrorResponse("404", 404)],
["/categories/47/entries/42", (clone $c)->folder(46)->article(42), [self::ENTRIES[1]], new Response (self::ENTRIES_OUT[1])],
["/categories/47/entries/42", (clone $c)->folder(46)->article(42), [self::ENTRIES[1]], HTTP::respJson (self::ENTRIES_OUT[1])],
["/categories/47/entries/44", (clone $c)->folder(46)->article(44), [], new ErrorResponse("404", 404)],
["/categories/47/entries/2112", (clone $c)->folder(46)->article(2112), new ExceptionInput("subjectMissing"), new ErrorResponse("404", 404)],
["/categories/2112/entries/47", (clone $c)->folder(2111)->article(47), new ExceptionInput("idMissing"), new ErrorResponse("404", 404)],
["/categories/1/entries/42", (clone $c)->folderShallow(0)->article(42), [self::ENTRIES[1]], new Response (self::ENTRIES_OUT[1])],
["/categories/1/entries/42", (clone $c)->folderShallow(0)->article(42), [self::ENTRIES[1]], HTTP::respJson (self::ENTRIES_OUT[1])],
];
}
@ -970,7 +969,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
[new ImportException("invalidFolderCopy"), new ErrorResponse("DuplicateImportCategory", 422)],
[new ImportException("invalidTagName"), new ErrorResponse("InvalidImportLabel", 422)],
[new FeedException("invalidUrl", ['url' => "http://example.com/"]), new ErrorResponse(["FailedImportFeed", 'url' => "http://example.com/", 'code' => 10502], 502)],
[true, new Response (['message' => Arsse::$lang->msg("API.Miniflux.ImportSuccess")])],
[true, HTTP::respJson (['message' => Arsse::$lang->msg("API.Miniflux.ImportSuccess")])],
];
}