mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
Implement TTRSS operation getHeadlines; fixe #82
This commit is contained in:
parent
faf00d63ba
commit
c669273792
3 changed files with 495 additions and 40 deletions
|
@ -41,14 +41,19 @@ Protocol difference so far:
|
||||||
- IDs for enclosures are ommitted as we don't give them IDs
|
- IDs for enclosures are ommitted as we don't give them IDs
|
||||||
- Searching in getHeadlines is not yet implemented
|
- Searching in getHeadlines is not yet implemented
|
||||||
- Category -3 (all non-special feeds) is handled correctly in getHeadlines; TT-RSS returns results for feed -3 (Fresh)
|
- Category -3 (all non-special feeds) is handled correctly in getHeadlines; TT-RSS returns results for feed -3 (Fresh)
|
||||||
|
- Sorting of headlines does not match TT-RSS: special feeds are not sorted specially like they should be
|
||||||
|
- The 'sanitize', 'force_update', and 'has_sandbox' parameters of getHeadlines are ignored
|
||||||
|
- The 'always_display_attachments' key of articles in getHeadlines is omitted, as the user cannot express a preference
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
const LEVEL = 14;
|
const LEVEL = 14; // emulated API level
|
||||||
const VERSION = "17.4";
|
const VERSION = "17.4"; // emulated TT-RSS version
|
||||||
const LABEL_OFFSET = 1024;
|
const LABEL_OFFSET = 1024; // offset below zero at which labels begin, counting down
|
||||||
|
const LIMIT_ARTICLES = 200; // maximum number of articles returned by getHeadlines
|
||||||
|
const LIMIT_EXCERPT = 100; // maximum length of excerpts in getHeadlines, counted in grapheme units
|
||||||
// special feeds
|
// special feeds
|
||||||
const FEED_ARCHIVED = 0;
|
const FEED_ARCHIVED = 0;
|
||||||
const FEED_STARRED = -1;
|
const FEED_STARRED = -1;
|
||||||
|
@ -91,18 +96,14 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
'show_excerpt' => ValueInfo::T_BOOL | ValueInfo::M_DROP, // whether to include article excerpts in `getHeadlines`
|
'show_excerpt' => ValueInfo::T_BOOL | ValueInfo::M_DROP, // whether to include article excerpts in `getHeadlines`
|
||||||
'show_content' => ValueInfo::T_BOOL | ValueInfo::M_DROP, // whether to include article content in `getHeadlines`
|
'show_content' => ValueInfo::T_BOOL | ValueInfo::M_DROP, // whether to include article content in `getHeadlines`
|
||||||
'include_attachments' => ValueInfo::T_BOOL | ValueInfo::M_DROP, // whether to include article enclosures in `getHeadlines`
|
'include_attachments' => ValueInfo::T_BOOL | ValueInfo::M_DROP, // whether to include article enclosures in `getHeadlines`
|
||||||
'view_mode' => ValueInfo::T_STRING,
|
'view_mode' => ValueInfo::T_STRING, // various filters for `getHeadlines`
|
||||||
'since_id' => ValueInfo::T_INT,
|
'since_id' => ValueInfo::T_INT, // cut-off article ID for `getHeadlines` and `getCompactHeadlines; returns only higher article IDs when specified
|
||||||
'order_by' => ValueInfo::T_STRING,
|
'order_by' => ValueInfo::T_STRING, // sort order for `getHeadlines`
|
||||||
'sanitize' => ValueInfo::T_BOOL | ValueInfo::M_DROP,
|
'include_header' => ValueInfo::T_BOOL | ValueInfo::M_DROP, // whether to attach a header to the results of `getHeadlines`
|
||||||
'force_update' => ValueInfo::T_BOOL | ValueInfo::M_DROP,
|
'search' => ValueInfo::T_STRING, // search string for `getHeadlines` (not yet implemented)
|
||||||
'has_sandbox' => ValueInfo::T_BOOL | ValueInfo::M_DROP,
|
|
||||||
'include_header' => ValueInfo::T_BOOL | ValueInfo::M_DROP,
|
|
||||||
'search' => ValueInfo::T_STRING,
|
|
||||||
'field' => ValueInfo::T_INT, // which state to change in `updateArticle`
|
'field' => ValueInfo::T_INT, // which state to change in `updateArticle`
|
||||||
'mode' => ValueInfo::T_INT, // whether to set, clear, or toggle the selected state in `updateArticle`
|
'mode' => ValueInfo::T_INT, // whether to set, clear, or toggle the selected state in `updateArticle`
|
||||||
'data' => ValueInfo::T_STRING, // note text in `updateArticle` if setting a note
|
'data' => ValueInfo::T_STRING, // note text in `updateArticle` if setting a note
|
||||||
'pref_name' => ValueInfo::T_STRING, // preference identifier in `getPref`
|
|
||||||
];
|
];
|
||||||
// generic error construct
|
// generic error construct
|
||||||
const FATAL_ERR = [
|
const FATAL_ERR = [
|
||||||
|
@ -1232,8 +1233,101 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
$data = $this->normalizeInput($data, self::VALID_INPUT, "unix");
|
$data = $this->normalizeInput($data, self::VALID_INPUT, "unix");
|
||||||
// fetch the list of IDs
|
// fetch the list of IDs
|
||||||
$out = [];
|
$out = [];
|
||||||
foreach ($this->fetchArticles($data, Database::LIST_MINIMAL) as $row) {
|
try {
|
||||||
$out[] = ['id' => $row['id']];
|
foreach ($this->fetchArticles($data, Database::LIST_MINIMAL) as $row) {
|
||||||
|
$out[] = ['id' => $row['id']];
|
||||||
|
}
|
||||||
|
} catch (ExceptionInput $e) {
|
||||||
|
// ignore database errors (feeds/categories that don't exist)
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function opGetHeadlines(array $data): array {
|
||||||
|
// normalize input
|
||||||
|
$data['limit'] = max(min(!$data['limit'] ? 200 : $data['limit'], 200), 0); // at most 200; not specified/zero yields 200; negative values yield no limit
|
||||||
|
$tr = Arsse::$db->begin();
|
||||||
|
// retrieve the list of label names for the user
|
||||||
|
$labels = [];
|
||||||
|
foreach (Arsse::$db->labelList(Arsse::$user->id, false) as $label) {
|
||||||
|
$labels[$label['id']] = $label['name'];
|
||||||
|
}
|
||||||
|
// retrieve the requested articles
|
||||||
|
$out = [];
|
||||||
|
try {
|
||||||
|
foreach ($this->fetchArticles($data, Database::LIST_FULL) as $article) {
|
||||||
|
$row = [
|
||||||
|
'id' => $article['id'],
|
||||||
|
'guid' => $article['guid'] ? "SHA256:".$article['guid'] : null,
|
||||||
|
'title' => $article['title'],
|
||||||
|
'link' => $article['url'],
|
||||||
|
'labels' => $this->articleLabelList($labels, $article['id']),
|
||||||
|
'unread' => (bool) $article['unread'],
|
||||||
|
'marked' => (bool) $article['starred'],
|
||||||
|
'published' => false, // TODO: if the Published feed is implemented, the getHeadlines operation should be amended accordingly
|
||||||
|
'author' => $article['author'],
|
||||||
|
'updated' => Date::transform($article['edited_date'], "unix", "sql"),
|
||||||
|
'is_updated' => ($article['published_date'] < $article['edited_date']),
|
||||||
|
'feed_id' => $article['subscription'],
|
||||||
|
'feed_title' => $article['subscription_title'],
|
||||||
|
'score' => 0, // score is not implemented as it is not modifiable from the TTRSS API
|
||||||
|
'note' => strlen($article['note']) ? $article['note'] : null,
|
||||||
|
'lang' => "", // FIXME: picoFeed should be able to retrieve this information
|
||||||
|
'tags' => Arsse::$db->articleCategoriesGet(Arsse::$user->id, $article['id']),
|
||||||
|
'comments_count' => 0,
|
||||||
|
'comments_link' => "",
|
||||||
|
];
|
||||||
|
if ($data['show_content']) {
|
||||||
|
$row['content'] = $article['content'];
|
||||||
|
}
|
||||||
|
if ($data['show_excerpt']) {
|
||||||
|
// prepare an excerpt from the content
|
||||||
|
$text = strip_tags($article['content']); // get rid of all tags; elements with problematic content (e.g. script, style) should already be gone thanks to sanitization
|
||||||
|
$text = html_entity_decode($text, \ENT_QUOTES | \ENT_HTML5, "UTF-8");
|
||||||
|
$text = trim($text); // trim whitespace at ends
|
||||||
|
$text = preg_replace("<\s+>s", " ", $text); // replace runs of whitespace with a single space
|
||||||
|
$row['excerpt'] = grapheme_substr($text, 0, self::LIMIT_EXCERPT).(grapheme_strlen($text) > self::LIMIT_EXCERPT ? "…" : ""); // add an ellipsis if the string is longer than N characters
|
||||||
|
}
|
||||||
|
if ($data['include_attachments']) {
|
||||||
|
$row['attachments'] = $article['media_url'] ? [[
|
||||||
|
'content_url' => $article['media_url'],
|
||||||
|
'content_type' => $article['media_type'],
|
||||||
|
'title' => "",
|
||||||
|
'duration' => "",
|
||||||
|
'width' => "",
|
||||||
|
'height' => "",
|
||||||
|
'post_id' => $article['id'],
|
||||||
|
]] : []; // TODO: We need to support multiple enclosures
|
||||||
|
}
|
||||||
|
$out[] = $row;
|
||||||
|
}
|
||||||
|
} catch (ExceptionInput $e) {
|
||||||
|
// ignore database errors (feeds/categories that don't exist)
|
||||||
|
// ensure that if using a header the database is not needlessly queried again
|
||||||
|
$data['skip'] = null;
|
||||||
|
}
|
||||||
|
if ($data['include_header']) {
|
||||||
|
if ($data['skip'] > 0 && $data['order_by'] != "date_reverse") {
|
||||||
|
// when paginating the header returns the latest ("first") item ID in the full list; we get this ID here
|
||||||
|
$data['skip'] = 0;
|
||||||
|
$data['limit'] = 1;
|
||||||
|
$firstID = ($this->fetchArticles($data, Database::LIST_MINIMAL)->getRow() ?? ['id' => 0])['id'];
|
||||||
|
} elseif ($data['order_by']=="date_reverse") {
|
||||||
|
// the "date_reverse" sort order doesn't get a first ID because it's meaningless for ascending-order pagination (pages doesn't go stale)
|
||||||
|
$firstID = 0;
|
||||||
|
} else {
|
||||||
|
// otherwise just use the ID of the first item in the list we've already computed
|
||||||
|
$firstID = ($out) ? $out[0]['id'] : 0;
|
||||||
|
}
|
||||||
|
// wrap the output with (but after) the header
|
||||||
|
$out = [
|
||||||
|
[
|
||||||
|
'id' => $data['feed_id'],
|
||||||
|
'is_cat' => $data['is_cat'] ?? false,
|
||||||
|
'first_id' => $firstID,
|
||||||
|
],
|
||||||
|
$out,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
|
@ -1340,6 +1434,21 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
throw new \JKingWeb\Arsse\Exception("constantUnknown", $viewMode); // @codeCoverageIgnore
|
throw new \JKingWeb\Arsse\Exception("constantUnknown", $viewMode); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
// TODO: implement searching
|
// TODO: implement searching
|
||||||
|
// handle sorting
|
||||||
|
switch ($data['order_by']) {
|
||||||
|
case "date_reverse":
|
||||||
|
// sort oldest first
|
||||||
|
$c->reverse(false);
|
||||||
|
break;
|
||||||
|
case "feed_dates":
|
||||||
|
// sort newest first
|
||||||
|
$c->reverse(true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// in TT-RSS the default sort order is unusual for some of the special feeds; we do not implement this
|
||||||
|
$c->reverse(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
// set the limit and offset
|
// set the limit and offset
|
||||||
if ($data['limit'] > 0) {
|
if ($data['limit'] > 0) {
|
||||||
$c->limit($data['limit']);
|
$c->limit($data['limit']);
|
||||||
|
@ -1352,11 +1461,6 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
$c->oldestArticle($data['since_id'] + 1);
|
$c->oldestArticle($data['since_id'] + 1);
|
||||||
}
|
}
|
||||||
// return results
|
// return results
|
||||||
try {
|
return Arsse::$db->articleList(Arsse::$user->id, $c, $fields);
|
||||||
return Arsse::$db->articleList(Arsse::$user->id, $c, $fields);
|
|
||||||
} catch (ExceptionInput $e) {
|
|
||||||
// if a category/feed does not exist
|
|
||||||
return new ResultEmpty;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,6 +93,29 @@ class TestTinyTinyAPI extends Test\AbstractTest {
|
||||||
'note' => "Note 2",
|
'note' => "Note 2",
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
// text from https://corrigeur.fr/lorem-ipsum-traduction-origine.php
|
||||||
|
protected $richContent = <<<LONG_STRING
|
||||||
|
<section>
|
||||||
|
<p>
|
||||||
|
<b>Pour</b> vous faire mieux
|
||||||
|
connaitre d’ou\u{300} vient
|
||||||
|
l’erreur de ceux qui
|
||||||
|
bla\u{302}ment la
|
||||||
|
volupte\u{301}, et qui louent
|
||||||
|
en quelque sorte la douleur,
|
||||||
|
je vais entrer dans une
|
||||||
|
explication plus
|
||||||
|
e\u{301}tendue, et vous faire
|
||||||
|
voir tout ce qui a
|
||||||
|
e\u{301}te\u{301} dit
|
||||||
|
la\u{300}-dessus par
|
||||||
|
l’inventeur de la
|
||||||
|
ve\u{301}rite\u{301}, et, pour
|
||||||
|
ainsi dire, par l’architecte
|
||||||
|
de la vie heureuse.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
LONG_STRING;
|
||||||
|
|
||||||
protected function respGood($content = null, $seq = 0): Response {
|
protected function respGood($content = null, $seq = 0): Response {
|
||||||
return new Response(200, [
|
return new Response(200, [
|
||||||
|
@ -1301,7 +1324,7 @@ class TestTinyTinyAPI extends Test\AbstractTest {
|
||||||
$this->assertEquals($this->respGood([$exp[0]]), $this->h->dispatch(new Request("POST", "", json_encode($in[5]))));
|
$this->assertEquals($this->respGood([$exp[0]]), $this->h->dispatch(new Request("POST", "", json_encode($in[5]))));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetCompactHeadlines() {
|
public function testRetrieveCompactHeadlines() {
|
||||||
$in1 = [
|
$in1 = [
|
||||||
// erroneous input
|
// erroneous input
|
||||||
['op' => "getCompactHeadlines", 'sid' => "PriestsOfSyrinx"],
|
['op' => "getCompactHeadlines", 'sid' => "PriestsOfSyrinx"],
|
||||||
|
@ -1336,18 +1359,19 @@ class TestTinyTinyAPI extends Test\AbstractTest {
|
||||||
Phake::when(Arsse::$db)->articleList->thenReturn(new Result([['id' => 0]]));
|
Phake::when(Arsse::$db)->articleList->thenReturn(new Result([['id' => 0]]));
|
||||||
Phake::when(Arsse::$db)->articleCount->thenReturn(0);
|
Phake::when(Arsse::$db)->articleCount->thenReturn(0);
|
||||||
Phake::when(Arsse::$db)->articleCount($this->anything(), (new Context)->unread(true))->thenReturn(1);
|
Phake::when(Arsse::$db)->articleCount($this->anything(), (new Context)->unread(true))->thenReturn(1);
|
||||||
Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->subscription(2112), Database::LIST_MINIMAL)->thenThrow(new ExceptionInput("subjectMissing"));
|
$c = (new Context)->reverse(true);
|
||||||
Phake::when(Arsse::$db)->articleList($this->anything(), new Context, Database::LIST_MINIMAL)->thenReturn(new Result($this->articles));
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->subscription(2112), Database::LIST_MINIMAL)->thenThrow(new ExceptionInput("subjectMissing"));
|
||||||
Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->starred(true), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 1]]));
|
Phake::when(Arsse::$db)->articleList($this->anything(), $c, Database::LIST_MINIMAL)->thenReturn(new Result($this->articles));
|
||||||
Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->label(1088), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 2]]));
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->starred(true), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 1]]));
|
||||||
Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->unread(true), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 3]]));
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->label(1088), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 2]]));
|
||||||
Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->label(1088)->unread(true), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 4]]));
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->unread(true), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 3]]));
|
||||||
Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->subscription(42)->starred(true), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 5]]));
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->label(1088)->unread(true), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 4]]));
|
||||||
Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->subscription(42)->annotated(true), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 6]]));
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->subscription(42)->starred(true), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 5]]));
|
||||||
Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->limit(5), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 7]]));
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->subscription(42)->annotated(true), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 6]]));
|
||||||
Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->offset(2), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 8]]));
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->limit(5), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 7]]));
|
||||||
Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->limit(5)->offset(2), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 9]]));
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->offset(2), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 8]]));
|
||||||
Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->oldestArticle(48), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 10]]));
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->limit(5)->offset(2), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 9]]));
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->oldestArticle(48), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 10]]));
|
||||||
$out1 = [
|
$out1 = [
|
||||||
$this->respErr("INCORRECT_USAGE"),
|
$this->respErr("INCORRECT_USAGE"),
|
||||||
$this->respGood([]),
|
$this->respGood([]),
|
||||||
|
@ -1379,10 +1403,333 @@ class TestTinyTinyAPI extends Test\AbstractTest {
|
||||||
$this->assertEquals($out1[$a], $this->h->dispatch(new Request("POST", "", json_encode($in1[$a]))), "Test $a failed");
|
$this->assertEquals($out1[$a], $this->h->dispatch(new Request("POST", "", json_encode($in1[$a]))), "Test $a failed");
|
||||||
}
|
}
|
||||||
for ($a = 0; $a < sizeof($in2); $a++) {
|
for ($a = 0; $a < sizeof($in2); $a++) {
|
||||||
Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->unread(false)->markedSince(Date::sub("PT24H")), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 1001]]));
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->unread(false)->markedSince(Date::sub("PT24H")), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 1001]]));
|
||||||
Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->unread(true)->modifiedSince(Date::sub("PT24H")), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 1002]]));
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->unread(true)->modifiedSince(Date::sub("PT24H")), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 1002]]));
|
||||||
Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->unread(true)->modifiedSince(Date::sub("PT24H"))->starred(true), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 1003]]));
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->unread(true)->modifiedSince(Date::sub("PT24H"))->starred(true), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 1003]]));
|
||||||
$this->assertEquals($out2[$a], $this->h->dispatch(new Request("POST", "", json_encode($in2[$a]))), "Test $a failed");
|
$this->assertEquals($out2[$a], $this->h->dispatch(new Request("POST", "", json_encode($in2[$a]))), "Test $a failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testRetrieveFullHeadlines() {
|
||||||
|
$in1 = [
|
||||||
|
// empty results
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => 0],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -2],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -1, 'is_cat' => true],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4, 'view_mode' => "published"],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -6, 'view_mode' => "unread"],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => 2112],
|
||||||
|
];
|
||||||
|
$in2 = [
|
||||||
|
// simple context tests
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx"],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -1],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -2112],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4, 'view_mode' => "adaptive"],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -2112, 'view_mode' => "adaptive"],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -2112, 'view_mode' => "unread"],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => 42, 'view_mode' => "marked"],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => 42, 'view_mode' => "has_note"],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4, 'limit' => 5],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4, 'skip' => 2],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4, 'limit' => 5, 'skip' => 2],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4, 'since_id' => 47],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -3, 'is_cat' => true],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4, 'is_cat' => true],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -2, 'is_cat' => true],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => 0, 'is_cat' => true],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => 42, 'is_cat' => true],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => 42, 'is_cat' => true, 'include_nested' => true],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4, 'order_by' => "feed_dates"],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4, 'order_by' => "date_reverse"],
|
||||||
|
];
|
||||||
|
$in3 = [
|
||||||
|
// time-based context tests
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -6],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -6, 'view_mode' => "adaptive"],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -3],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -3, 'view_mode' => "marked"],
|
||||||
|
];
|
||||||
|
Phake::when(Arsse::$db)->labelList($this->anything())->thenReturn(new Result($this->labels));
|
||||||
|
Phake::when(Arsse::$db)->labelList($this->anything(), false)->thenReturn(new Result($this->usedLabels));
|
||||||
|
Phake::when(Arsse::$db)->articleLabelsGet->thenReturn([]);
|
||||||
|
Phake::when(Arsse::$db)->articleLabelsGet($this->anything(), 2112)->thenReturn([1,3]);
|
||||||
|
Phake::when(Arsse::$db)->articleCategoriesGet->thenReturn([]);
|
||||||
|
Phake::when(Arsse::$db)->articleCategoriesGet($this->anything(), 2112)->thenReturn(["Boring","Illogical"]);
|
||||||
|
Phake::when(Arsse::$db)->articleList->thenReturn($this->generateHeadlines(0));
|
||||||
|
Phake::when(Arsse::$db)->articleCount->thenReturn(0);
|
||||||
|
Phake::when(Arsse::$db)->articleCount($this->anything(), (new Context)->unread(true))->thenReturn(1);
|
||||||
|
$c = (new Context)->limit(200)->reverse(true);
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->subscription(2112), Database::LIST_FULL)->thenThrow(new ExceptionInput("subjectMissing"));
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->starred(true), Database::LIST_FULL)->thenReturn($this->generateHeadlines(1));
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->label(1088), Database::LIST_FULL)->thenReturn($this->generateHeadlines(2));
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->unread(true), Database::LIST_FULL)->thenReturn($this->generateHeadlines(3));
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->label(1088)->unread(true), Database::LIST_FULL)->thenReturn($this->generateHeadlines(4));
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->subscription(42)->starred(true), Database::LIST_FULL)->thenReturn($this->generateHeadlines(5));
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->subscription(42)->annotated(true), Database::LIST_FULL)->thenReturn($this->generateHeadlines(6));
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->limit(5), Database::LIST_FULL)->thenReturn($this->generateHeadlines(7));
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->offset(2), Database::LIST_FULL)->thenReturn($this->generateHeadlines(8));
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->limit(5)->offset(2), Database::LIST_FULL)->thenReturn($this->generateHeadlines(9));
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->oldestArticle(48), Database::LIST_FULL)->thenReturn($this->generateHeadlines(10));
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c), Database::LIST_FULL)->thenReturn($this->generateHeadlines(11));
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->labelled(true), Database::LIST_FULL)->thenReturn($this->generateHeadlines(12));
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->folderShallow(0), Database::LIST_FULL)->thenReturn($this->generateHeadlines(13));
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->folderShallow(42), Database::LIST_FULL)->thenReturn($this->generateHeadlines(14));
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->folder(42), Database::LIST_FULL)->thenReturn($this->generateHeadlines(15));
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->reverse(false), Database::LIST_FULL)->thenReturn($this->generateHeadlines(16));
|
||||||
|
$out2 = [
|
||||||
|
$this->respErr("INCORRECT_USAGE"),
|
||||||
|
$this->outputHeadlines(11),
|
||||||
|
$this->outputHeadlines(1),
|
||||||
|
$this->outputHeadlines(2),
|
||||||
|
$this->outputHeadlines(3),
|
||||||
|
$this->outputHeadlines(2), // the result is 2 rather than 4 because there are no unread, so the unread context is not used
|
||||||
|
$this->outputHeadlines(4),
|
||||||
|
$this->outputHeadlines(5),
|
||||||
|
$this->outputHeadlines(6),
|
||||||
|
$this->outputHeadlines(7),
|
||||||
|
$this->outputHeadlines(8),
|
||||||
|
$this->outputHeadlines(9),
|
||||||
|
$this->outputHeadlines(10),
|
||||||
|
$this->outputHeadlines(11),
|
||||||
|
$this->outputHeadlines(11),
|
||||||
|
$this->outputHeadlines(12),
|
||||||
|
$this->outputHeadlines(13),
|
||||||
|
$this->outputHeadlines(14),
|
||||||
|
$this->outputHeadlines(15),
|
||||||
|
$this->outputHeadlines(11), // defaulting sorting is not fully implemented
|
||||||
|
$this->outputHeadlines(16),
|
||||||
|
];
|
||||||
|
$out3 = [
|
||||||
|
$this->outputHeadlines(1001),
|
||||||
|
$this->outputHeadlines(1001),
|
||||||
|
$this->outputHeadlines(1002),
|
||||||
|
$this->outputHeadlines(1003),
|
||||||
|
];
|
||||||
|
for ($a = 0; $a < sizeof($in1); $a++) {
|
||||||
|
$this->assertResponse($this->respGood([]), $this->h->dispatch(new Request("POST", "", json_encode($in1[$a]))), "Test $a failed");
|
||||||
|
}
|
||||||
|
for ($a = 0; $a < sizeof($in2); $a++) {
|
||||||
|
$this->assertEquals($out2[$a], $this->h->dispatch(new Request("POST", "", json_encode($in2[$a]))), "Test $a failed");
|
||||||
|
}
|
||||||
|
for ($a = 0; $a < sizeof($in3); $a++) {
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->unread(false)->markedSince(Date::sub("PT24H")), Database::LIST_FULL)->thenReturn($this->generateHeadlines(1001));
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->unread(true)->modifiedSince(Date::sub("PT24H")), Database::LIST_FULL)->thenReturn($this->generateHeadlines(1002));
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->unread(true)->modifiedSince(Date::sub("PT24H"))->starred(true), Database::LIST_FULL)->thenReturn($this->generateHeadlines(1003));
|
||||||
|
$this->assertEquals($out3[$a], $this->h->dispatch(new Request("POST", "", json_encode($in3[$a]))), "Test $a failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRetrieveFullHeadlinesCheckingExtraFields() {
|
||||||
|
$in = [
|
||||||
|
// empty results
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4, 'show_content' => true],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4, 'include_attachments' => true],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4, 'include_header' => true],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -3, 'is_cat' => true, 'include_header' => true],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -1, 'is_cat' => true, 'include_header' => true],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => 2112, 'include_header' => true],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4, 'include_header' => true, 'order_by' => "date_reverse"],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => 42, 'skip' => 47, 'include_header' => true],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => 42, 'skip' => 47, 'include_header' => true, 'order_by' => "date_reverse"],
|
||||||
|
['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4, 'show_excerpt' => true],
|
||||||
|
];
|
||||||
|
Phake::when(Arsse::$db)->labelList($this->anything())->thenReturn(new Result($this->labels));
|
||||||
|
Phake::when(Arsse::$db)->labelList($this->anything(), false)->thenReturn(new Result($this->usedLabels));
|
||||||
|
Phake::when(Arsse::$db)->articleLabelsGet->thenReturn([]);
|
||||||
|
Phake::when(Arsse::$db)->articleLabelsGet($this->anything(), 2112)->thenReturn([1,3]);
|
||||||
|
Phake::when(Arsse::$db)->articleCategoriesGet->thenReturn([]);
|
||||||
|
Phake::when(Arsse::$db)->articleCategoriesGet($this->anything(), 2112)->thenReturn(["Boring","Illogical"]);
|
||||||
|
Phake::when(Arsse::$db)->articleList->thenReturn($this->generateHeadlines(1));
|
||||||
|
Phake::when(Arsse::$db)->articleCount->thenReturn(0);
|
||||||
|
Phake::when(Arsse::$db)->articleCount($this->anything(), (new Context)->unread(true))->thenReturn(1);
|
||||||
|
// sanity check; this makes sure extra fields are not included in default situations
|
||||||
|
$test = $this->h->dispatch(new Request("POST", "", json_encode($in[0])));
|
||||||
|
$this->assertEquals($this->outputHeadlines(1), $test);
|
||||||
|
// test 'show_content'
|
||||||
|
$test = $this->h->dispatch(new Request("POST", "", json_encode($in[1])));
|
||||||
|
$this->assertArrayHasKey("content", $test->payload['content'][0]);
|
||||||
|
$this->assertArrayHasKey("content", $test->payload['content'][1]);
|
||||||
|
foreach ($this->generateHeadlines(1) as $key => $row) {
|
||||||
|
$this->assertSame($row['content'], $test->payload['content'][$key]['content']);
|
||||||
|
}
|
||||||
|
// test 'include_attachments'
|
||||||
|
$test = $this->h->dispatch(new Request("POST", "", json_encode($in[2])));
|
||||||
|
$exp = [
|
||||||
|
[
|
||||||
|
'content_url' => "http://example.com/text",
|
||||||
|
'content_type' => "text/plain",
|
||||||
|
'title' => "",
|
||||||
|
'duration' => "",
|
||||||
|
'width' => "",
|
||||||
|
'height' => "",
|
||||||
|
'post_id' => 2112,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$this->assertArrayHasKey("attachments", $test->payload['content'][0]);
|
||||||
|
$this->assertArrayHasKey("attachments", $test->payload['content'][1]);
|
||||||
|
$this->assertSame([], $test->payload['content'][0]['attachments']);
|
||||||
|
$this->assertSame($exp, $test->payload['content'][1]['attachments']);
|
||||||
|
// test 'include_header'
|
||||||
|
$test = $this->h->dispatch(new Request("POST", "", json_encode($in[3])));
|
||||||
|
$exp = $this->outputHeadlines(1);
|
||||||
|
$exp->payload['content'] = [
|
||||||
|
['id' => -4, 'is_cat' => false, 'first_id' => 1],
|
||||||
|
$exp->payload['content'],
|
||||||
|
];
|
||||||
|
$this->assertEquals($exp, $test);
|
||||||
|
// test 'include_header' with a category
|
||||||
|
$test = $this->h->dispatch(new Request("POST", "", json_encode($in[4])));
|
||||||
|
$exp = $this->outputHeadlines(1);
|
||||||
|
$exp->payload['content'] = [
|
||||||
|
['id' => -3, 'is_cat' => true, 'first_id' => 1],
|
||||||
|
$exp->payload['content'],
|
||||||
|
];
|
||||||
|
$this->assertEquals($exp, $test);
|
||||||
|
// test 'include_header' with an empty result
|
||||||
|
$test = $this->h->dispatch(new Request("POST", "", json_encode($in[5])));
|
||||||
|
$exp = $this->respGood([
|
||||||
|
['id' => -1, 'is_cat' => true, 'first_id' => 0],
|
||||||
|
[],
|
||||||
|
]);
|
||||||
|
$this->assertEquals($exp, $test);
|
||||||
|
// test 'include_header' with an erroneous result
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->limit(200)->reverse(true)->subscription(2112), $this->anything())->thenThrow(new ExceptionInput("subjectMissing"));
|
||||||
|
$test = $this->h->dispatch(new Request("POST", "", json_encode($in[6])));
|
||||||
|
$exp = $this->respGood([
|
||||||
|
['id' => 2112, 'is_cat' => false, 'first_id' => 0],
|
||||||
|
[],
|
||||||
|
]);
|
||||||
|
$this->assertEquals($exp, $test);
|
||||||
|
// test 'include_header' with ascending order
|
||||||
|
$test = $this->h->dispatch(new Request("POST", "", json_encode($in[7])));
|
||||||
|
$exp = $this->outputHeadlines(1);
|
||||||
|
$exp->payload['content'] = [
|
||||||
|
['id' => -4, 'is_cat' => false, 'first_id' => 0],
|
||||||
|
$exp->payload['content'],
|
||||||
|
];
|
||||||
|
$this->assertEquals($exp, $test);
|
||||||
|
// test 'include_header' with skip
|
||||||
|
Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->reverse(true)->limit(1)->subscription(42), Database::LIST_MINIMAL)->thenReturn($this->generateHeadlines(1867));
|
||||||
|
$test = $this->h->dispatch(new Request("POST", "", json_encode($in[8])));
|
||||||
|
$exp = $this->outputHeadlines(1);
|
||||||
|
$exp->payload['content'] = [
|
||||||
|
['id' => 42, 'is_cat' => false, 'first_id' => 1867],
|
||||||
|
$exp->payload['content'],
|
||||||
|
];
|
||||||
|
$this->assertEquals($exp, $test);
|
||||||
|
// test 'include_header' with skip and ascending order
|
||||||
|
$test = $this->h->dispatch(new Request("POST", "", json_encode($in[9])));
|
||||||
|
$exp = $this->outputHeadlines(1);
|
||||||
|
$exp->payload['content'] = [
|
||||||
|
['id' => 42, 'is_cat' => false, 'first_id' => 0],
|
||||||
|
$exp->payload['content'],
|
||||||
|
];
|
||||||
|
$this->assertEquals($exp, $test);
|
||||||
|
// test 'show_excerpt'
|
||||||
|
$exp1 = "“This & that, you know‽”";
|
||||||
|
$exp2 = "Pour vous faire mieux connaitre d’ou\u{300} vient l’erreur de ceux qui bla\u{302}ment la volupte\u{301}, et qui louent en…";
|
||||||
|
$test = $this->h->dispatch(new Request("POST", "", json_encode($in[10])));
|
||||||
|
$this->assertArrayHasKey("excerpt", $test->payload['content'][0]);
|
||||||
|
$this->assertArrayHasKey("excerpt", $test->payload['content'][1]);
|
||||||
|
$this->assertSame($exp1, $test->payload['content'][0]['excerpt']);
|
||||||
|
$this->assertSame($exp2, $test->payload['content'][1]['excerpt']);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function generateHeadlines(int $id): Result {
|
||||||
|
return new Result([
|
||||||
|
[
|
||||||
|
'id' => $id,
|
||||||
|
'url' => 'http://example.com/1',
|
||||||
|
'title' => 'Article title 1',
|
||||||
|
'subscription_title' => "Feed 2112",
|
||||||
|
'author' => '',
|
||||||
|
'content' => '<p>“This & that, you know‽”</p>',
|
||||||
|
'guid' => '',
|
||||||
|
'published_date' => '2000-01-01 00:00:00',
|
||||||
|
'edited_date' => '2000-01-01 00:00:00',
|
||||||
|
'modified_date' => '2000-01-01 01:00:00',
|
||||||
|
'unread' => 0,
|
||||||
|
'starred' => 0,
|
||||||
|
'edition' => 101,
|
||||||
|
'subscription' => 12,
|
||||||
|
'fingerprint' => 'f5cb8bfc1c7396dc9816af212a3e2ac5221585c2a00bf7ccb6aabd95dcfcd6a6:fb0bc8f8cb08913dc5a497db700e327f1d34e4987402687d494a5891f24714d4:18fdd4fa93d693128c43b004399e5c9cea6c261ddfa002518d3669f55d8c2207',
|
||||||
|
'media_url' => null,
|
||||||
|
'media_type' => null,
|
||||||
|
'note' => "",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 2112,
|
||||||
|
'url' => 'http://example.com/2',
|
||||||
|
'title' => 'Article title 2',
|
||||||
|
'subscription_title' => "Feed 11",
|
||||||
|
'author' => 'J. King',
|
||||||
|
'content' => $this->richContent,
|
||||||
|
'guid' => '5be8a5a46ecd52ed132191c8d27fb1af6b3d4edc00234c5d9f8f0e10562ed3b7',
|
||||||
|
'published_date' => '2000-01-02 00:00:00',
|
||||||
|
'edited_date' => '2000-01-02 00:00:02',
|
||||||
|
'modified_date' => '2000-01-02 02:00:00',
|
||||||
|
'unread' => 1,
|
||||||
|
'starred' => 1,
|
||||||
|
'edition' => 202,
|
||||||
|
'subscription' => 8,
|
||||||
|
'fingerprint' => '0e86d2de822a174fe3c44a466953e63ca1f1a58a19cbf475fce0855d4e3d5153:13075894189c47ffcfafd1dfe7fbb539f7c74a69d35a399b3abf8518952714f9:2abd0a8cba83b8214a66c8f0293ba63e467d720540e29ff8ddcdab069d4f1c9e',
|
||||||
|
'media_url' => "http://example.com/text",
|
||||||
|
'media_type' => "text/plain",
|
||||||
|
'note' => "Note 2",
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function outputHeadlines(int $id): Response {
|
||||||
|
return $this->respGood([
|
||||||
|
[
|
||||||
|
'id' => $id,
|
||||||
|
'guid' => null,
|
||||||
|
'title' => 'Article title 1',
|
||||||
|
'link' => 'http://example.com/1',
|
||||||
|
'labels' => [],
|
||||||
|
'unread' => false,
|
||||||
|
'marked' => false,
|
||||||
|
'published' => false,
|
||||||
|
'author' => '',
|
||||||
|
'updated' => strtotime('2000-01-01 00:00:00'),
|
||||||
|
'is_updated' => false,
|
||||||
|
'feed_id' => 12,
|
||||||
|
'feed_title' => "Feed 2112",
|
||||||
|
'score' => 0,
|
||||||
|
'note' => null,
|
||||||
|
'lang' => "",
|
||||||
|
'tags' => [],
|
||||||
|
'comments_count' => 0,
|
||||||
|
'comments_link' => "",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 2112,
|
||||||
|
'guid' => "SHA256:5be8a5a46ecd52ed132191c8d27fb1af6b3d4edc00234c5d9f8f0e10562ed3b7",
|
||||||
|
'title' => 'Article title 2',
|
||||||
|
'link' => 'http://example.com/2',
|
||||||
|
'labels' => [
|
||||||
|
[-1025, "Logical", "", ""],
|
||||||
|
[-1027, "Fascinating", "", ""],
|
||||||
|
],
|
||||||
|
'unread' => true,
|
||||||
|
'marked' => true,
|
||||||
|
'published' => false,
|
||||||
|
'author' => "J. King",
|
||||||
|
'updated' => strtotime('2000-01-02 00:00:02'),
|
||||||
|
'is_updated' => true,
|
||||||
|
'feed_id' => 8,
|
||||||
|
'feed_title' => "Feed 11",
|
||||||
|
'score' => 0,
|
||||||
|
'note' => "Note 2",
|
||||||
|
'lang' => "",
|
||||||
|
'tags' => ["Boring", "Illogical"],
|
||||||
|
'comments_count' => 0,
|
||||||
|
'comments_link' => "",
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,10 +76,14 @@
|
||||||
<file>Db/SQLite3/Database/TestDatabaseCleanupSQLite3.php</file>
|
<file>Db/SQLite3/Database/TestDatabaseCleanupSQLite3.php</file>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
<testsuite name="Controllers">
|
<testsuite name="Controllers">
|
||||||
<file>REST/NextCloudNews/TestNCNVersionDiscovery.php</file>
|
<testsuite name="NCNv1">
|
||||||
<file>REST/NextCloudNews/TestNCNV1_2.php</file>
|
<file>REST/NextCloudNews/TestNCNVersionDiscovery.php</file>
|
||||||
<file>REST/TinyTinyRSS/TestTinyTinyAPI.php</file>
|
<file>REST/NextCloudNews/TestNCNV1_2.php</file>
|
||||||
<file>REST/TinyTinyRSS/TestTinyTinyIcon.php</file>
|
</testsuite>
|
||||||
|
<testsuite name="TTRSS">
|
||||||
|
<file>REST/TinyTinyRSS/TestTinyTinyAPI.php</file>
|
||||||
|
<file>REST/TinyTinyRSS/TestTinyTinyIcon.php</file>
|
||||||
|
</testsuite>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
<testsuite name="Refresh service">
|
<testsuite name="Refresh service">
|
||||||
<file>Service/TestService.php</file>
|
<file>Service/TestService.php</file>
|
||||||
|
|
Loading…
Reference in a new issue