From c2f60bc5d2ee1e88f7de485d81fe41e5b63fdc9d Mon Sep 17 00:00:00 2001 From: "J. King" Date: Thu, 29 Jun 2017 23:56:43 -0400 Subject: [PATCH] More article tests - Test the values returned by articleList - Test paged and reversed listing - Test marking, un/read, un/starred, and both - Nearly complete rewrite of marking queries; previous efforts simply did not work, or overzealously updated or inserted - Testing of marking contexts still required --- lib/Database.php | 84 ++++---- lib/Misc/Query.php | 5 +- tests/lib/Database/SeriesArticle.php | 312 +++++++++++++++++++++++---- 3 files changed, 324 insertions(+), 77 deletions(-) diff --git a/lib/Database.php b/lib/Database.php index 5f6e246..f083e9c 100644 --- a/lib/Database.php +++ b/lib/Database.php @@ -626,11 +626,11 @@ class Database { DATEFORMAT(?, edited) as edited, DATEFORMAT(?, max( modified, - coalesce((SELECT modified from arsse_marks join user on user is owner where article is arsse_articles.id),'') + coalesce((select modified from arsse_marks join user on user is owner where article is arsse_articles.id),'') )) as modified, - NOT (SELECT count(*) from arsse_marks join user on user is owner where article is arsse_articles.id and read is 1) as unread, - (SELECT count(*) from arsse_marks join user on user is owner where article is arsse_articles.id and starred is 1) as starred, - (SELECT max(id) from arsse_editions where article is arsse_articles.id) as edition, + NOT (select count(*) from arsse_marks join user on user is owner where article is arsse_articles.id and read is 1) as unread, + (select count(*) from arsse_marks join user on user is owner where article is arsse_articles.id and starred is 1) as starred, + (select max(id) from arsse_editions where article is arsse_articles.id) as edition, subscribed_feeds.sub as subscription, url_title_hash||':'||url_content_hash||':'||title_content_hash as fingerprint, arsse_enclosures.url as media_url, @@ -640,7 +640,7 @@ class Database { left join arsse_enclosures on arsse_enclosures.article is arsse_articles.id ", "", // WHERE clause - "edition".(!$context->reverse ? " desc" : ""), // ORDER BY clause + "edition".($context->reverse ? " desc" : ""), // ORDER BY clause $context->limit, $context->offset ); @@ -674,33 +674,31 @@ class Database { return $this->db->prepare($q, "str", "str", "str")->run($this->dateFormatDefault, $this->dateFormatDefault, $this->dateFormatDefault); } - public function articlePropertiesSet(string $user, array $data, Context $context = null): bool { + public function articleMark(string $user, array $data, Context $context = null): bool { if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if(!$context) $context = new Context; // sanitize input - $valid = [ - 'read' => "strict bool", - 'starred' => "strict bool", - ]; - list($setClause, $setTypes, $setValues) = $this->generateSet($data, $valid); - $insValues = [ - isset($data['read']) ? $data['read'] : false, - isset($data['starred']) ? $data['starred'] : false, + $values = [ + isset($data['read']) ? $data['read'] : null, + isset($data['starred']) ? $data['starred'] : null, ]; // the two queries we want to execute to make the requested changes $queries = [ - [ - 'body' => "UPDATE arsse_marks set $setClause, modified = CURRENT_TIMESTAMP", - 'where' => "owner is ? and article in (select id from target_articles and exists is 1)", - 'types' => [$setTypes, "str"], - 'values' => [$setValues, $user] - ], - [ - 'body' => "INSERT INTO arsse_marks(article,owner,read,starred) SELECT id, ?, ?, ? from target_articles", - 'where' => "exists is 0", - 'types' => ["str", "strict bool", "strict bool"], - 'values' => [$user, $insValues] - ] + "UPDATE arsse_marks + set + read = coalesce((select read from target_values),read), + starred = coalesce((select starred from target_values),starred), + modified = CURRENT_TIMESTAMP + WHERE + owner is (select user from user) + and article in (select id from target_articles where to_update is 1)", + "INSERT INTO arsse_marks(owner,article,read,starred) + select + (select user from user), + id, + coalesce((select read from target_values),0), + coalesce((select starred from target_values),0) + from target_articles where to_insert is 1" ]; $out = 0; // wrap this UPDATE and INSERT together into a transaction @@ -711,17 +709,32 @@ class Database { $q = new Query( "SELECT arsse_articles.id as id, - max(arsse_editions.id) as edition - (select count(*) from arsse_marks join user on user is owner where article is arsse_articles.id) as exists, - max(modified, - coalesce((SELECT modified from arsse_marks join user on user is owner where article is arsse_articles.id),'') - ) as modified, + (select max(id) from arsse_editions where article is arsse_articles.id) as edition, + max(arsse_articles.modified, + coalesce((select modified from arsse_marks join user on user is owner where article is arsse_articles.id),'') + ) as modified, + ( + not exists(select id from arsse_marks join user on user is owner where article is arsse_articles.id) + and exists(select * from target_values where read is 1 or starred is 1) + ) as to_insert, + exists( + select id from arsse_marks + join user on user is owner + where + article is arsse_articles.id + and ( + read is not coalesce((select read from target_values),read) + 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 - join arsse_editions on arsse_articles.id is arsse_editions.article " ); + // 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 a subscription is specified, make sure it exists $id = $this->subscriptionValidateId($user, $context->subscription)['feed']; @@ -749,13 +762,12 @@ class Database { if($context->notModifiedSince()) $q->setWhere("modified <= ?", "datetime", $context->notModifiedSince); // push the current query onto the CTE stack and execute the query we're actually interested in $q->pushCTE( - "target_articles(id, exists)", // CTE table specification + "target_articles(id,edition,modified,to_insert,to_update)", // CTE table specification [], // CTE types [], // CTE values - $query['body'], // new query body - $query['where'] // new query WHERE clause + $query // new query body ); - $out += $this->db->prepare($q, $query['types'])->run($query['values'])->changes(); + $out += $this->db->prepare($q)->run()->changes(); } // commit the transaction $tr->commit(); diff --git a/lib/Misc/Query.php b/lib/Misc/Query.php index 7f8bb0c..870aad1 100644 --- a/lib/Misc/Query.php +++ b/lib/Misc/Query.php @@ -18,7 +18,7 @@ class Query { function __construct(string $body, string $where = "", string $order = "", int $limit = 0, int $offset = 0) { if(strlen($body)) $this->body = $body; - if(strlen($where)) $this->where[] = $where; + if(strlen($where)) $this->qWhere[] = $where; if(strlen($order)) $this->order[] = $order; $this->limit = $limit; $this->offset = $offset; @@ -80,7 +80,8 @@ class Query { array_push($types, "strict int"); array_push($values, $this->offset); } - $this->setCTE($tableSpec." as (".$this->buildQueryBody().")", $types, $value); + $this->setCTE($tableSpec." as (".$this->buildQueryBody().")", $types, $values); + $this->qWhere = []; $this->tWhere = []; $this->vWhere = []; $this->order = []; diff --git a/tests/lib/Database/SeriesArticle.php b/tests/lib/Database/SeriesArticle.php index 2817e38..b635eb5 100644 --- a/tests/lib/Database/SeriesArticle.php +++ b/tests/lib/Database/SeriesArticle.php @@ -21,6 +21,8 @@ trait SeriesArticle { 'rows' => [ ["jane.doe@example.com", "", "Jane Doe", UserDriver::RIGHTS_NONE], ["john.doe@example.com", "", "John Doe", UserDriver::RIGHTS_NONE], + ["john.doe@example.org", "", "John Doe", UserDriver::RIGHTS_NONE], + ["john.doe@example.net", "", "John Doe", UserDriver::RIGHTS_NONE], ], ], 'arsse_folders' => [ @@ -34,9 +36,12 @@ trait SeriesArticle { [1, "john.doe@example.com", null, "Technology"], [2, "john.doe@example.com", 1, "Software"], [3, "john.doe@example.com", 1, "Rocketry"], - [4, "jane.doe@example.com", null, "Politics"], + [4, "jane.doe@example.com", null, "Politics"], [5, "john.doe@example.com", null, "Politics"], [6, "john.doe@example.com", 2, "Politics"], + [7, "john.doe@example.net", null, "Technology"], + [8, "john.doe@example.net", 7, "Software"], + [9, "john.doe@example.net", null, "Politics"], ] ], 'arsse_feeds' => [ @@ -58,13 +63,6 @@ trait SeriesArticle { [11,"http://example.com/11"], [12,"http://example.com/12"], [13,"http://example.com/13"], - [14,"http://example.com/14"], - [15,"http://example.com/15"], - [16,"http://example.com/16"], - [17,"http://example.com/17"], - [18,"http://example.com/18"], - [19,"http://example.com/19"], - [20,"http://example.com/20"], ] ], 'arsse_subscriptions' => [ @@ -81,33 +79,39 @@ trait SeriesArticle { [4,"john.doe@example.com",4,6], [5,"john.doe@example.com",10,5], [6,"jane.doe@example.com",1,null], - [7,"jane.doe@example.com",12,null],/* - [8,"john.doe@example.com",1,null], - [9,"john.doe@example.com",1,null], - [10,"john.doe@example.com",1,null], - [1,"john.doe@example.com",1,null], - [1,"john.doe@example.com",1,null], - [1,"john.doe@example.com",1,null], - [1,"john.doe@example.com",1,null], - [1,"john.doe@example.com",1,null], - [1,"john.doe@example.com",1,null], - [1,"john.doe@example.com",1,null], - [1,"john.doe@example.com",1,null], - [1,"john.doe@example.com",1,null], - [1,"john.doe@example.com",1,null], - [1,"john.doe@example.com",1,null],*/ + [7,"jane.doe@example.com",9,null], + [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], + [12,"john.doe@example.net",2,9], + [13,"john.doe@example.net",3,8], + [14,"john.doe@example.net",4,7], ] ], 'arsse_articles' => [ 'columns' => [ 'id' => "int", 'feed' => "int", - 'modified' => "datetime", + 'url' => "str", + 'title' => "str", + 'author' => "str", + 'published' => "datetime", + 'edited' => "datetime", + 'content' => "str", + 'guid' => "str", 'url_title_hash' => "str", 'url_content_hash' => "str", 'title_content_hash' => "str", + 'modified' => "datetime", ], - 'rows' => [] // filled by series setup + 'rows' => [ // lower IDs are filled by series setup + [101,11,'http://example.com/1','Article title 1','','2000-01-01 00:00:00','2000-01-01 00:00:01','

Article content 1

','e433653cef2e572eee4215fa299a4a5af9137b2cefd6283c85bd69a32915beda','f5cb8bfc1c7396dc9816af212a3e2ac5221585c2a00bf7ccb6aabd95dcfcd6a6','fb0bc8f8cb08913dc5a497db700e327f1d34e4987402687d494a5891f24714d4','18fdd4fa93d693128c43b004399e5c9cea6c261ddfa002518d3669f55d8c2207','2000-01-01 01:00:00'], + [102,11,'http://example.com/2','Article title 2','','2000-01-02 00:00:00','2000-01-02 00:00:02','

Article content 2

','5be8a5a46ecd52ed132191c8d27fb1af6b3d4edc00234c5d9f8f0e10562ed3b7','0e86d2de822a174fe3c44a466953e63ca1f1a58a19cbf475fce0855d4e3d5153','13075894189c47ffcfafd1dfe7fbb539f7c74a69d35a399b3abf8518952714f9','2abd0a8cba83b8214a66c8f0293ba63e467d720540e29ff8ddcdab069d4f1c9e','2000-01-02 02:00:00'], + [103,12,'http://example.com/3','Article title 3','','2000-01-03 00:00:00','2000-01-03 00:00:03','

Article content 3

','31a6594500a48b59fcc8a075ce82b946c9c3c782460d088bd7b8ef3ede97ad92','f74b06b240bd08abf4d3fdfc20dba6a6f6eb8b4f1a00e9a617efd63a87180a4b','b278380e984cefe63f0e412b88ffc9cb0befdfa06fdc00bace1da99a8daff406','ad622b31e739cd3a3f3c788991082cf4d2f7a8773773008e75f0572e58cd373b','2000-01-03 03:00:00'], + [104,12,'http://example.com/4','Article title 4','','2000-01-04 00:00:00','2000-01-04 00:00:04','

Article content 4

','804e517d623390e71497982c77cf6823180342ebcd2e7d5e32da1e55b09dd180','f3615c7f16336d3ea242d35cf3fc17dbc4ee3afb78376bf49da2dd7a5a25dec8','f11c2b4046f207579aeb9c69a8c20ca5461cef49756ccfa5ba5e2344266da3b3','ab2da63276acce431250b18d3d49b988b226a99c7faadf275c90b751aee05be9','2000-01-04 04:00:00'], + [105,13,'http://example.com/5','Article title 5','','2000-01-05 00:00:00','2000-01-05 00:00:05','

Article content 5

','db3e736c2c492f5def5c5da33ddcbea1824040e9ced2142069276b0a6e291a41','d40da96e39eea6c55948ccbe9b3d275b5f931298288dbe953990c5f496097022','834240f84501b5341d375414718204ec421561f3825d34c22bf9182203e42900','43b970ac6ec5f8a9647b2c7e4eed8b1d7f62e154a95eed748b0294c1256764ba','2000-01-05 05:00:00'], + ] ], 'arsse_enclosures' => [ 'columns' => [ @@ -115,7 +119,13 @@ trait SeriesArticle { 'url' => "str", 'type' => "str", ], - 'rows' => [] + 'rows' => [ + [102,"http://example.com/text","text/plain"], + [103,"http://example.com/video","video/webm"], + [104,"http://example.com/image","image/svg+xml"], + [105,"http://example.com/audio","audio/ogg"], + + ] ], 'arsse_editions' => [ 'columns' => [ @@ -123,37 +133,150 @@ trait SeriesArticle { 'article' => "int", ], 'rows' => [ // lower IDs are filled by series setup + [101,101], + [102,102], + [103,103], + [104,104], + [105,105], + [202,102], + [203,103], + [204,104], + [205,105], + [305,105], [1001,20], ] ], 'arsse_marks' => [ 'columns' => [ - 'owner' => "str", - 'article' => "int", - 'read' => "bool", - 'starred' => "bool", + 'owner' => "str", + 'article' => "int", + 'read' => "bool", + 'starred' => "bool", + 'modified' => "datetime" ], 'rows' => [ - ["john.doe@example.com",1,1,1], - ["john.doe@example.com",19,1,0], - ["john.doe@example.com",20,0,1], - ["jane.doe@example.com",20,1,0], + ["john.doe@example.com", 1,1,1,'2000-01-01 00:00:00'], + ["john.doe@example.com", 19,1,0,'2000-01-01 00:00:00'], + ["john.doe@example.com", 20,0,1,'2010-01-01 00:00:00'], + ["jane.doe@example.com", 20,1,0,'2010-01-01 00:00:00'], + ["john.doe@example.org",102,1,0,'2000-01-02 02:00:00'], + ["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", 3,0,1,'2017-01-01 00:00:00'], + ["john.doe@example.net", 4,1,1,'2017-01-01 00:00:00'], + ] ], ]; + protected $matches = [ + [ + 'id' => 101, + 'url' => 'http://example.com/1', + 'title' => 'Article title 1', + 'author' => '', + 'content' => '

Article content 1

', + 'guid' => 'e433653cef2e572eee4215fa299a4a5af9137b2cefd6283c85bd69a32915beda', + 'published' => 946684800, + 'edited' => 946684801, + 'modified' => 946688400, + 'unread' => 1, + 'starred' => 0, + 'edition' => 101, + 'subscription' => 8, + 'fingerprint' => 'f5cb8bfc1c7396dc9816af212a3e2ac5221585c2a00bf7ccb6aabd95dcfcd6a6:fb0bc8f8cb08913dc5a497db700e327f1d34e4987402687d494a5891f24714d4:18fdd4fa93d693128c43b004399e5c9cea6c261ddfa002518d3669f55d8c2207', + 'media_url' => null, + 'media_type' => null, + ], + [ + 'id' => 102, + 'url' => 'http://example.com/2', + 'title' => 'Article title 2', + 'author' => '', + 'content' => '

Article content 2

', + 'guid' => '5be8a5a46ecd52ed132191c8d27fb1af6b3d4edc00234c5d9f8f0e10562ed3b7', + 'published' => 946771200, + 'edited' => 946771202, + 'modified' => 946778400, + 'unread' => 0, + 'starred' => 0, + 'edition' => 202, + 'subscription' => 8, + 'fingerprint' => '0e86d2de822a174fe3c44a466953e63ca1f1a58a19cbf475fce0855d4e3d5153:13075894189c47ffcfafd1dfe7fbb539f7c74a69d35a399b3abf8518952714f9:2abd0a8cba83b8214a66c8f0293ba63e467d720540e29ff8ddcdab069d4f1c9e', + 'media_url' => "http://example.com/text", + 'media_type' => "text/plain", + ], + [ + 'id' => 103, + 'url' => 'http://example.com/3', + 'title' => 'Article title 3', + 'author' => '', + 'content' => '

Article content 3

', + 'guid' => '31a6594500a48b59fcc8a075ce82b946c9c3c782460d088bd7b8ef3ede97ad92', + 'published' => 946857600, + 'edited' => 946857603, + 'modified' => 946868400, + 'unread' => 1, + 'starred' => 1, + 'edition' => 203, + 'subscription' => 9, + 'fingerprint' => 'f74b06b240bd08abf4d3fdfc20dba6a6f6eb8b4f1a00e9a617efd63a87180a4b:b278380e984cefe63f0e412b88ffc9cb0befdfa06fdc00bace1da99a8daff406:ad622b31e739cd3a3f3c788991082cf4d2f7a8773773008e75f0572e58cd373b', + 'media_url' => "http://example.com/video", + 'media_type' => "video/webm", + ], + [ + 'id' => 104, + 'url' => 'http://example.com/4', + 'title' => 'Article title 4', + 'author' => '', + 'content' => '

Article content 4

', + 'guid' => '804e517d623390e71497982c77cf6823180342ebcd2e7d5e32da1e55b09dd180', + 'published' => 946944000, + 'edited' => 946944004, + 'modified' => 946958400, + 'unread' => 0, + 'starred' => 1, + 'edition' => 204, + 'subscription' => 9, + 'fingerprint' => 'f3615c7f16336d3ea242d35cf3fc17dbc4ee3afb78376bf49da2dd7a5a25dec8:f11c2b4046f207579aeb9c69a8c20ca5461cef49756ccfa5ba5e2344266da3b3:ab2da63276acce431250b18d3d49b988b226a99c7faadf275c90b751aee05be9', + 'media_url' => "http://example.com/image", + 'media_type' => "image/svg+xml", + ], + [ + 'id' => 105, + 'url' => 'http://example.com/5', + 'title' => 'Article title 5', + 'author' => '', + 'content' => '

Article content 5

', + 'guid' => 'db3e736c2c492f5def5c5da33ddcbea1824040e9ced2142069276b0a6e291a41', + 'published' => 947030400, + 'edited' => 947030405, + 'modified' => 947048400, + 'unread' => 1, + 'starred' => 0, + 'edition' => 305, + 'subscription' => 10, + 'fingerprint' => 'd40da96e39eea6c55948ccbe9b3d275b5f931298288dbe953990c5f496097022:834240f84501b5341d375414718204ec421561f3825d34c22bf9182203e42900:43b970ac6ec5f8a9647b2c7e4eed8b1d7f62e154a95eed748b0294c1256764ba', + 'media_url' => "http://example.com/audio", + 'media_type' => "audio/ogg", + ], + ]; function setUpSeries() { - for($a = 0, $b = 1; $b <= sizeof($this->data['arsse_feeds']['rows']); $b++) { + for($a = 0, $b = 1; $b <= 10; $b++) { // add two generic articles per feed, and matching initial editions - $this->data['arsse_articles']['rows'][] = [++$a,$b,"2000-01-01T00:00:00Z","","",""]; + $this->data['arsse_articles']['rows'][] = [++$a,$b,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"]; $this->data['arsse_editions']['rows'][] = [$a,$a]; - $this->data['arsse_articles']['rows'][] = [++$a,$b,"2010-01-01T00:00:00Z","","",""]; + $this->data['arsse_articles']['rows'][] = [++$a,$b,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"]; $this->data['arsse_editions']['rows'][] = [$a,$a]; } - $this->user = "john.doe@example.com"; + $this->user = "john.doe@example.net"; } - function testListArticlesByContext() { + function testListArticlesCheckingContext() { + $this->user = "john.doe@example.com"; // get all items for user $exp = [1,2,3,4,5,6,7,8,19,20]; $this->compareIds($exp, new Context); @@ -185,7 +308,118 @@ trait SeriesArticle { $exp = [1,3,5,7,19]; $this->compareIds($exp, (new Context)->notModifiedSince("2005-01-01T00:00:00Z")); $this->compareIds($exp, (new Context)->notModifiedSince("2000-01-01T00:00:00Z")); + // paged results + $this->compareIds([1], (new Context)->limit(1)); + $this->compareIds([2], (new Context)->limit(1)->oldestEdition(1+1)); + $this->compareIds([3], (new Context)->limit(1)->oldestEdition(2+1)); + $this->compareIds([4,5], (new Context)->limit(2)->oldestEdition(3+1)); + // reversed results + $this->compareIds([20], (new Context)->reverse(true)->limit(1)); + $this->compareIds([19], (new Context)->reverse(true)->limit(1)->latestEdition(1001-1)); + $this->compareIds([8], (new Context)->reverse(true)->limit(1)->latestEdition(19-1)); + $this->compareIds([7,6], (new Context)->reverse(true)->limit(2)->latestEdition(8-1)); + } + + function testListArticlesCheckingProperties() { + $this->user = "john.doe@example.org"; + Data::$db->dateFormatDefault("unix"); + $this->assertResult($this->matches, Data::$db->articleList($this->user)); + } + + function testMarkAllArticlesUnread() { + Data::$db->articleMark($this->user, ['read'=>false]); + $now = $this->dateTransform(time(), "sql"); + $state = $this->primeExpectations($this->data, [ + 'arsse_marks' => ["owner","article","read","starred","modified"], + ]); + $state['arsse_marks']['rows'][9][2] = 0; + $state['arsse_marks']['rows'][9][4] = $now; + $state['arsse_marks']['rows'][11][2] = 0; + $state['arsse_marks']['rows'][11][4] = $now; + $this->compareExpectations($state); + } + + function testMarkAllArticlesRead() { + Data::$db->articleMark($this->user, ['read'=>true]); + $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][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 testMarkAllArticlesUnstarred() { + Data::$db->articleMark($this->user, ['starred'=>false]); + $now = $this->dateTransform(time(), "sql"); + $state = $this->primeExpectations($this->data, [ + 'arsse_marks' => ["owner","article","read","starred","modified"], + ]); + $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; + $this->compareExpectations($state); + } + + function testMarkAllArticlesStarred() { + Data::$db->articleMark($this->user, ['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][3] = 1; + $state['arsse_marks']['rows'][9][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 testMarkAllArticlesUnreadAndUnstarred() { + Data::$db->articleMark($this->user, ['read'=>false,'starred'=>false]); + $now = $this->dateTransform(time(), "sql"); + $state = $this->primeExpectations($this->data, [ + 'arsse_marks' => ["owner","article","read","starred","modified"], + ]); + $state['arsse_marks']['rows'][9][2] = 0; + $state['arsse_marks']['rows'][9][4] = $now; + $state['arsse_marks']['rows'][10][3] = 0; + $state['arsse_marks']['rows'][10][4] = $now; + $state['arsse_marks']['rows'][11][2] = 0; + $state['arsse_marks']['rows'][11][3] = 0; + $state['arsse_marks']['rows'][11][4] = $now; + $this->compareExpectations($state); + } + function testMarkAllArticlesReadAndStarred() { + Data::$db->articleMark($this->user, ['read'=>true,'starred'=>true]); + $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][3] = 1; + $state['arsse_marks']['rows'][8][4] = $now; + $state['arsse_marks']['rows'][9][3] = 1; + $state['arsse_marks']['rows'][9][4] = $now; + $state['arsse_marks']['rows'][10][2] = 1; + $state['arsse_marks']['rows'][10][4] = $now; + $state['arsse_marks']['rows'][] = [$this->user,5,1,1,$now]; + $state['arsse_marks']['rows'][] = [$this->user,6,1,1,$now]; + $state['arsse_marks']['rows'][] = [$this->user,7,1,1,$now]; + $state['arsse_marks']['rows'][] = [$this->user,8,1,1,$now]; + $this->compareExpectations($state); } protected function compareIds(array $exp, Context $c) {