Browse Source

More article marking tests, some context tests

Various fixes as a result
microsub
J. King 7 years ago
parent
commit
5742d949cc
  1. 63
      lib/Database.php
  2. 1
      lib/Misc/Query.php
  3. 132
      tests/lib/Database/SeriesArticle.php

63
lib/Database.php

@ -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;
}
}

1
lib/Misc/Query.php

@ -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 = [];

132
tests/lib/Database/SeriesArticle.php

@ -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…
Cancel
Save