mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
More article marking tests, some context tests
Various fixes as a result
This commit is contained in:
parent
c2f60bc5d2
commit
5742d949cc
3 changed files with 182 additions and 14 deletions
|
@ -703,6 +703,16 @@ class Database {
|
|||
$out = 0;
|
||||
// wrap this UPDATE and INSERT together into a transaction
|
||||
$tr = $this->begin();
|
||||
// if an edition context is specified, make sure it's valid
|
||||
if($context->edition()) {
|
||||
// make sure the edition exists
|
||||
$edition = $this->articleValidateEdition($user, $context->edition);
|
||||
// if the edition is not the latest, make no marks and return
|
||||
if(!$edition['current']) return false;
|
||||
} else if($context->article()) {
|
||||
// otherwise if an article context is specified, make sure it's valid
|
||||
$this->articleValidateId($user, $context->article);
|
||||
}
|
||||
// execute each query in sequence
|
||||
foreach($queries as $query) {
|
||||
// first build the query which will select the target articles; we will later turn this into a CTE for the actual query that manipulates the articles
|
||||
|
@ -727,33 +737,32 @@ class Database {
|
|||
or starred is not coalesce((select starred from target_values),starred)
|
||||
)
|
||||
) as to_update
|
||||
FROM arsse_articles
|
||||
join subscribed_feeds on feed is subscribed_feeds.id
|
||||
"
|
||||
FROM arsse_articles"
|
||||
);
|
||||
// common table expression for the affected user
|
||||
$q->setCTE("user(user) as (SELECT ?)", "str", $user);
|
||||
// common table expression with the values to set
|
||||
$q->setCTE("target_values(read,starred) as (select ?,?)", ["bool","bool"], $values);
|
||||
if($context->subscription()) {
|
||||
if($context->edition()) {
|
||||
$q->setWhere("arsse_articles.id is ?", "int", $edition['article']);
|
||||
} else if($context->article()) {
|
||||
$q->setWhere("arsse_articles.id is ?", "int", $context->article);
|
||||
} else if($context->subscription()) {
|
||||
// if a subscription is specified, make sure it exists
|
||||
$id = $this->subscriptionValidateId($user, $context->subscription)['feed'];
|
||||
// add a basic CTE that will join in only the requested subscription
|
||||
$q->setCTE("subscribed_feeds(id,sub) as (SELECT ?,?)", ["int","int"], [$id,$context->subscription]);
|
||||
$q->setCTE("subscribed_feeds(id,sub) as (SELECT ?,?)", ["int","int"], [$id,$context->subscription], "join subscribed_feeds on feed is subscribed_feeds.id");
|
||||
} else if($context->folder()) {
|
||||
// if a folder is specified, make sure it exists
|
||||
$this->folderValidateId($user, $context->folder);
|
||||
// if it does exist, add a common table expression to list it and its children so that we select from the entire subtree
|
||||
$q->setCTE("folders(folder) as (SELECT ? union select id from arsse_folders join folders on parent is folder)", "int", $context->folder);
|
||||
// add another CTE for the subscriptions within the folder
|
||||
$q->setCTE("subscribed_feeds(id,sub) as (SELECT feed,id from arsse_subscriptions join user on user is owner join folders on arsse_subscriptions.folder is folders.folder)");
|
||||
$q->setCTE("subscribed_feeds(id,sub) as (SELECT feed,id from arsse_subscriptions join user on user is owner join folders on arsse_subscriptions.folder is folders.folder)", [], [], "join subscribed_feeds on feed is subscribed_feeds.id");
|
||||
} else {
|
||||
// otherwise add a CTE for all the user's subscriptions
|
||||
$q->setCTE("subscribed_feeds(id,sub) as (SELECT feed,id from arsse_subscriptions join user on user is owner)");
|
||||
$q->setCTE("subscribed_feeds(id,sub) as (SELECT feed,id from arsse_subscriptions join user on user is owner)", [], [], "join subscribed_feeds on feed is subscribed_feeds.id");
|
||||
}
|
||||
// filter for specific article or edition
|
||||
if($context->article()) $q->setWhere("arsse_article.id is ?", "int", $context->article);
|
||||
if($context->edition()) $q->setWhere("arsse_article.id is (SELECT article from arsse_editions where id is ?)", "int", $context->edition);
|
||||
// filter based on edition offset
|
||||
if($context->oldestEdition()) $q->setWhere("edition >= ?", "int", $context->oldestEdition);
|
||||
if($context->latestEdition()) $q->setWhere("edition <= ?", "int", $context->latestEdition);
|
||||
|
@ -773,4 +782,38 @@ class Database {
|
|||
$tr->commit();
|
||||
return (bool) $out;
|
||||
}
|
||||
|
||||
public function articleValidateId(string $user, int $id): array {
|
||||
$out = $this->db->prepare(
|
||||
"SELECT
|
||||
arsse_articles.id as article,
|
||||
(select max(id) from arsse_editions where article is arsse_articles.id) as edition
|
||||
FROM arsse_articles
|
||||
join arsse_feeds on arsse_feeds.id is arsse_articles.feed
|
||||
join arsse_subscriptions on arsse_subscriptions.feed is arsse_feeds.id
|
||||
WHERE
|
||||
arsse_articles.id is ? and arsse_subscriptions.owner is ?",
|
||||
"int", "str"
|
||||
)->run($id, $user)->getRow();
|
||||
if(!$out) throw new Db\ExceptionInput("idMissing", ["action" => $this->caller(), "field" => "article", 'id' => $id]);
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function articleValidateEdition(string $user, int $id): array {
|
||||
$out = $this->db->prepare(
|
||||
"SELECT
|
||||
arsse_editions.id as edition,
|
||||
arsse_editions.article as article,
|
||||
(arsse_editions.id is (select max(id) from arsse_editions where article is arsse_editions.article)) as current
|
||||
FROM arsse_editions
|
||||
join arsse_articles on arsse_editions.article is arsse_articles.id
|
||||
join arsse_feeds on arsse_feeds.id is arsse_articles.feed
|
||||
join arsse_subscriptions on arsse_subscriptions.feed is arsse_feeds.id
|
||||
WHERE
|
||||
edition is ? and arsse_subscriptions.owner is ?",
|
||||
"int", "str"
|
||||
)->run($id, $user)->getRow();
|
||||
if(!$out) throw new Db\ExceptionInput("idMissing", ["action" => $this->caller(), "field" => "edition", 'id' => $id]);
|
||||
return $out;
|
||||
}
|
||||
}
|
|
@ -81,6 +81,7 @@ class Query {
|
|||
array_push($values, $this->offset);
|
||||
}
|
||||
$this->setCTE($tableSpec." as (".$this->buildQueryBody().")", $types, $values);
|
||||
$this->jCTE = [];
|
||||
$this->qWhere = [];
|
||||
$this->tWhere = [];
|
||||
$this->vWhere = [];
|
||||
|
|
|
@ -83,7 +83,7 @@ trait SeriesArticle {
|
|||
[8,"john.doe@example.org",11,null],
|
||||
[9,"john.doe@example.org",12,null],
|
||||
[10,"john.doe@example.org",13,null],
|
||||
[11,"john.doe@example.net",1,null],
|
||||
[11,"john.doe@example.net",10,null],
|
||||
[12,"john.doe@example.net",2,9],
|
||||
[13,"john.doe@example.net",3,8],
|
||||
[14,"john.doe@example.net",4,7],
|
||||
|
@ -163,11 +163,10 @@ trait SeriesArticle {
|
|||
["john.doe@example.org",103,0,1,'2000-01-03 03:00:00'],
|
||||
["john.doe@example.org",104,1,1,'2000-01-04 04:00:00'],
|
||||
["john.doe@example.org",105,0,0,'2000-01-05 05:00:00'],
|
||||
["john.doe@example.net", 1,0,0,'2017-01-01 00:00:00'],
|
||||
["john.doe@example.net", 2,1,0,'2017-01-01 00:00:00'],
|
||||
["john.doe@example.net", 19,0,0,'2017-01-01 00:00:00'],
|
||||
["john.doe@example.net", 20,1,0,'2017-01-01 00:00:00'],
|
||||
["john.doe@example.net", 3,0,1,'2017-01-01 00:00:00'],
|
||||
["john.doe@example.net", 4,1,1,'2017-01-01 00:00:00'],
|
||||
|
||||
]
|
||||
],
|
||||
];
|
||||
|
@ -422,6 +421,131 @@ trait SeriesArticle {
|
|||
$this->compareExpectations($state);
|
||||
}
|
||||
|
||||
function testMarkAllArticlesUnreadAndStarred() {
|
||||
Data::$db->articleMark($this->user, ['read'=>false,'starred'=>true]);
|
||||
$now = $this->dateTransform(time(), "sql");
|
||||
$state = $this->primeExpectations($this->data, [
|
||||
'arsse_marks' => ["owner","article","read","starred","modified"],
|
||||
]);
|
||||
$state['arsse_marks']['rows'][8][3] = 1;
|
||||
$state['arsse_marks']['rows'][8][4] = $now;
|
||||
$state['arsse_marks']['rows'][9][2] = 0;
|
||||
$state['arsse_marks']['rows'][9][3] = 1;
|
||||
$state['arsse_marks']['rows'][9][4] = $now;
|
||||
$state['arsse_marks']['rows'][11][2] = 0;
|
||||
$state['arsse_marks']['rows'][11][4] = $now;
|
||||
$state['arsse_marks']['rows'][] = [$this->user,5,0,1,$now];
|
||||
$state['arsse_marks']['rows'][] = [$this->user,6,0,1,$now];
|
||||
$state['arsse_marks']['rows'][] = [$this->user,7,0,1,$now];
|
||||
$state['arsse_marks']['rows'][] = [$this->user,8,0,1,$now];
|
||||
$this->compareExpectations($state);
|
||||
}
|
||||
|
||||
function testMarkAllArticlesReadAndUnstarred() {
|
||||
Data::$db->articleMark($this->user, ['read'=>true,'starred'=>false]);
|
||||
$now = $this->dateTransform(time(), "sql");
|
||||
$state = $this->primeExpectations($this->data, [
|
||||
'arsse_marks' => ["owner","article","read","starred","modified"],
|
||||
]);
|
||||
$state['arsse_marks']['rows'][8][2] = 1;
|
||||
$state['arsse_marks']['rows'][8][4] = $now;
|
||||
$state['arsse_marks']['rows'][10][2] = 1;
|
||||
$state['arsse_marks']['rows'][10][3] = 0;
|
||||
$state['arsse_marks']['rows'][10][4] = $now;
|
||||
$state['arsse_marks']['rows'][11][3] = 0;
|
||||
$state['arsse_marks']['rows'][11][4] = $now;
|
||||
$state['arsse_marks']['rows'][] = [$this->user,5,1,0,$now];
|
||||
$state['arsse_marks']['rows'][] = [$this->user,6,1,0,$now];
|
||||
$state['arsse_marks']['rows'][] = [$this->user,7,1,0,$now];
|
||||
$state['arsse_marks']['rows'][] = [$this->user,8,1,0,$now];
|
||||
$this->compareExpectations($state);
|
||||
}
|
||||
|
||||
function testMarkATreeFolder() {
|
||||
Data::$db->articleMark($this->user, ['read'=>true], (new Context)->folder(7));
|
||||
$now = $this->dateTransform(time(), "sql");
|
||||
$state = $this->primeExpectations($this->data, [
|
||||
'arsse_marks' => ["owner","article","read","starred","modified"],
|
||||
]);
|
||||
$state['arsse_marks']['rows'][] = [$this->user,5,1,0,$now];
|
||||
$state['arsse_marks']['rows'][] = [$this->user,6,1,0,$now];
|
||||
$state['arsse_marks']['rows'][] = [$this->user,7,1,0,$now];
|
||||
$state['arsse_marks']['rows'][] = [$this->user,8,1,0,$now];
|
||||
$this->compareExpectations($state);
|
||||
}
|
||||
|
||||
function testMarkALeafFolder() {
|
||||
Data::$db->articleMark($this->user, ['read'=>true], (new Context)->folder(8));
|
||||
$now = $this->dateTransform(time(), "sql");
|
||||
$state = $this->primeExpectations($this->data, [
|
||||
'arsse_marks' => ["owner","article","read","starred","modified"],
|
||||
]);
|
||||
$state['arsse_marks']['rows'][] = [$this->user,5,1,0,$now];
|
||||
$state['arsse_marks']['rows'][] = [$this->user,6,1,0,$now];
|
||||
$this->compareExpectations($state);
|
||||
}
|
||||
|
||||
function testMarkAMissingFolder() {
|
||||
$this->assertException("idMissing", "Db", "ExceptionInput");
|
||||
Data::$db->articleMark($this->user, ['read'=>true], (new Context)->folder(42));
|
||||
}
|
||||
|
||||
function testMarkASubscription() {
|
||||
Data::$db->articleMark($this->user, ['read'=>true], (new Context)->subscription(13));
|
||||
$now = $this->dateTransform(time(), "sql");
|
||||
$state = $this->primeExpectations($this->data, [
|
||||
'arsse_marks' => ["owner","article","read","starred","modified"],
|
||||
]);
|
||||
$state['arsse_marks']['rows'][] = [$this->user,5,1,0,$now];
|
||||
$state['arsse_marks']['rows'][] = [$this->user,6,1,0,$now];
|
||||
$this->compareExpectations($state);
|
||||
}
|
||||
|
||||
function testMarkAMissingSubscription() {
|
||||
$this->assertException("idMissing", "Db", "ExceptionInput");
|
||||
Data::$db->articleMark($this->user, ['read'=>true], (new Context)->folder(2112));
|
||||
}
|
||||
|
||||
function testMarkAnArticle() {
|
||||
Data::$db->articleMark($this->user, ['starred'=>true], (new Context)->article(20));
|
||||
$now = $this->dateTransform(time(), "sql");
|
||||
$state = $this->primeExpectations($this->data, [
|
||||
'arsse_marks' => ["owner","article","read","starred","modified"],
|
||||
]);
|
||||
$state['arsse_marks']['rows'][9][3] = 1;
|
||||
$state['arsse_marks']['rows'][9][4] = $now;
|
||||
$this->compareExpectations($state);
|
||||
}
|
||||
|
||||
function testMarkAMissingArticle() {
|
||||
$this->assertException("idMissing", "Db", "ExceptionInput");
|
||||
Data::$db->articleMark($this->user, ['starred'=>true], (new Context)->article(1));
|
||||
}
|
||||
|
||||
function testMarkAnEdition() {
|
||||
Data::$db->articleMark($this->user, ['starred'=>true], (new Context)->edition(1001));
|
||||
$now = $this->dateTransform(time(), "sql");
|
||||
$state = $this->primeExpectations($this->data, [
|
||||
'arsse_marks' => ["owner","article","read","starred","modified"],
|
||||
]);
|
||||
$state['arsse_marks']['rows'][9][3] = 1;
|
||||
$state['arsse_marks']['rows'][9][4] = $now;
|
||||
$this->compareExpectations($state);
|
||||
}
|
||||
|
||||
function testMarkAStaleEdition() {
|
||||
Data::$db->articleMark($this->user, ['starred'=>true], (new Context)->edition(20)); // no changes occur
|
||||
$state = $this->primeExpectations($this->data, [
|
||||
'arsse_marks' => ["owner","article","read","starred","modified"],
|
||||
]);
|
||||
$this->compareExpectations($state);
|
||||
}
|
||||
|
||||
function testMarkAMissingEdition() {
|
||||
$this->assertException("idMissing", "Db", "ExceptionInput");
|
||||
Data::$db->articleMark($this->user, ['starred'=>true], (new Context)->edition(2));
|
||||
}
|
||||
|
||||
protected function compareIds(array $exp, Context $c) {
|
||||
$ids = array_column($ids = Data::$db->articleList($this->user, $c)->getAll(), "id");
|
||||
sort($ids);
|
||||
|
|
Loading…
Reference in a new issue