From dccd4caedefbb9fd5d38d108d53aab983e7b433c Mon Sep 17 00:00:00 2001 From: "J. King" Date: Sat, 24 Nov 2018 23:18:17 -0500 Subject: [PATCH] Convert one database function test series (articles) to a common harness Also revert the dropping of tables in the schema files. This was for the convenience of tests, but the risk of data loss is too great --- sql/PostgreSQL/0.sql | 12 - sql/PostgreSQL/1.sql | 4 - sql/PostgreSQL/2.sql | 1 - sql/SQLite3/0.sql | 13 - sql/SQLite3/1.sql | 5 - tests/cases/Database/Base.php | 95 ++- tests/cases/Database/SeriesArticle.php | 823 +++++++++++---------- tests/cases/Db/SQLite3/TestDatabase.php | 19 + tests/cases/Db/SQLite3PDO/TestDatabase.php | 19 + tests/lib/DatabaseInformation.php | 82 +- 10 files changed, 602 insertions(+), 471 deletions(-) create mode 100644 tests/cases/Db/SQLite3/TestDatabase.php create mode 100644 tests/cases/Db/SQLite3PDO/TestDatabase.php diff --git a/sql/PostgreSQL/0.sql b/sql/PostgreSQL/0.sql index c76f614..6e6b2f1 100644 --- a/sql/PostgreSQL/0.sql +++ b/sql/PostgreSQL/0.sql @@ -4,18 +4,6 @@ -- Please consult the SQLite 3 schemata for commented version -drop table if exists arsse_meta cascade; -drop table if exists arsse_users cascade; -drop table if exists arsse_users_meta cascade; -drop table if exists arsse_folders cascade; -drop table if exists arsse_feeds cascade; -drop table if exists arsse_subscriptions cascade; -drop table if exists arsse_articles cascade; -drop table if exists arsse_enclosures cascade; -drop table if exists arsse_marks cascade; -drop table if exists arsse_editions cascade; -drop table if exists arsse_categories cascade; - create table arsse_meta( key text primary key, value text diff --git a/sql/PostgreSQL/1.sql b/sql/PostgreSQL/1.sql index 5c35d6b..086c7e3 100644 --- a/sql/PostgreSQL/1.sql +++ b/sql/PostgreSQL/1.sql @@ -4,10 +4,6 @@ -- Please consult the SQLite 3 schemata for commented version -drop table if exists arsse_sessions cascade; -drop table if exists arsse_labels cascade; -drop table if exists arsse_label_members cascade; - create table arsse_sessions ( id text primary key, created timestamp(0) with time zone not null default CURRENT_TIMESTAMP, diff --git a/sql/PostgreSQL/2.sql b/sql/PostgreSQL/2.sql index cd1fbf6..021d3cd 100644 --- a/sql/PostgreSQL/2.sql +++ b/sql/PostgreSQL/2.sql @@ -7,7 +7,6 @@ -- create a case-insensitive generic collation sequence -- this collation is Unicode-aware, whereas SQLite's built-in nocase -- collation is ASCII-only -drop collation if exists nocase cascade; create collation nocase( provider = icu, locale = '@kf=false' diff --git a/sql/SQLite3/0.sql b/sql/SQLite3/0.sql index c8ae67f..7a9dea6 100644 --- a/sql/SQLite3/0.sql +++ b/sql/SQLite3/0.sql @@ -5,19 +5,6 @@ -- Make the database WAL-journalled; this is persitent PRAGMA journal_mode = wal; --- drop any existing tables, just in case -drop table if exists arsse_meta; -drop table if exists arsse_users; -drop table if exists arsse_users_meta; -drop table if exists arsse_folders; -drop table if exists arsse_feeds; -drop table if exists arsse_subscriptions; -drop table if exists arsse_articles; -drop table if exists arsse_enclosures; -drop table if exists arsse_marks; -drop table if exists arsse_editions; -drop table if exists arsse_categories; - create table arsse_meta( -- application metadata key text primary key not null, -- metadata key diff --git a/sql/SQLite3/1.sql b/sql/SQLite3/1.sql index 1859ea8..b96bd79 100644 --- a/sql/SQLite3/1.sql +++ b/sql/SQLite3/1.sql @@ -2,11 +2,6 @@ -- Copyright 2017 J. King, Dustin Wilson et al. -- See LICENSE and AUTHORS files for details --- drop any existing tables, just in case -drop table if exists arsse_sessions; -drop table if exists arsse_labels; -drop table if exists arsse_label_members; - create table arsse_sessions ( -- sessions for Tiny Tiny RSS (and possibly others) id text primary key, -- UUID of session diff --git a/tests/cases/Database/Base.php b/tests/cases/Database/Base.php index 8f6f7b6..8efcf3b 100644 --- a/tests/cases/Database/Base.php +++ b/tests/cases/Database/Base.php @@ -6,37 +6,72 @@ declare(strict_types=1); namespace JKingWeb\Arsse\TestCase\Database; -use JKingWeb\Arsse\User\Driver as UserDriver; +use JKingWeb\Arsse\Test\Database; use JKingWeb\Arsse\Arsse; use JKingWeb\Arsse\Conf; use JKingWeb\Arsse\User; use JKingWeb\Arsse\Misc\ValueInfo; -use JKingWeb\Arsse\Database; use JKingWeb\Arsse\Db\Result; +use JKingWeb\Arsse\Test\DatabaseInformation; use Phake; -abstract class Base { - protected $drv; +abstract class Base extends \JKingWeb\Arsse\Test\AbstractTest{ + use SeriesArticle; + + /** @var \JKingWeb\Arsse\Test\DatabaseInformation */ + protected static $dbInfo; + /** @var \JKingWeb\Arsse\Db\Driver */ + protected static $drv; + protected static $failureReason = ""; protected $primed = false; protected abstract function nextID(string $table): int; - public function setUp() { + protected function findTraitOfTest(string $test): string { + $class = new \ReflectionClass(self::class); + foreach ($class->getTraits() as $trait) { + if ($trait->hasMethod($test)) { + return $trait->getShortName(); + } + } + return $class->getShortName(); + } + + public static function setUpBeforeClass() { // establish a clean baseline - self::clearData(); - self::setConf(); - // configure and create the relevant database driver - $this->setUpDriver(); - // create the database interface with the suitable driver - Arsse::$db = new Database; + static::clearData(); + // perform an initial connection to the database to reset its version to zero + // in the case of SQLite this will always be the case (we use a memory database), + // but other engines should clean up from potentially interrupted prior tests + static::$dbInfo = new DatabaseInformation(static::$implementation); + static::setConf(); + try { + static::$drv = new static::$dbInfo->driverClass; + } catch (\JKingWeb\Arsse\Db\Exception $e) { + static::$failureReason = $e->getMessage(); + return; + } + // wipe the database absolutely clean + (static::$dbInfo->razeFunction)(static::$drv); + // create the database interface with the suitable driver and apply the latest schema + Arsse::$db = new Database(static::$drv); Arsse::$db->driverSchemaUpdate(); + } + + public function setUp() { + // get the name of the test's test series + $this->series = $this->findTraitofTest($this->getName()); + static::clearData(); + if (strlen(static::$failureReason)) { + $this->markTestSkipped(static::$failureReason); + } + Arsse::$db = new Database(static::$drv); // create a mock user manager Arsse::$user = Phake::mock(User::class); Phake::when(Arsse::$user)->authorize->thenReturn(true); - // call the additional setup method if it exists - if (method_exists($this, "setUpSeries")) { - $this->setUpSeries(); - } + // call the series-specific setup method + $setUp = "setUp".$this->series; + $this->$setUp(); // prime the database with series data if it hasn't already been done if (!$this->primed && isset($this->data)) { $this->primeDatabase($this->data); @@ -44,18 +79,30 @@ abstract class Base { } public function tearDown() { - // call the additional teardiwn method if it exists - if (method_exists($this, "tearDownSeries")) { - $this->tearDownSeries(); - } + // call the series-specific teardown method + $this->series = $this->findTraitofTest($this->getName()); + $tearDown = "tearDown".$this->series; + $this->$tearDown(); // clean up $this->primed = false; - $this->drv = null; - self::clearData(); + // call the database-specific table cleanup function + (static::$dbInfo->truncateFunction)(static::$drv); + // clear state + static::clearData(); + } + + public static function tearDownAfterClass() { + // wipe the database absolutely clean + (static::$dbInfo->razeFunction)(static::$drv); + // clean up + static::$drv = null; + static::$dbInfo = null; + static::$failureReason = ""; + static::clearData(); } - public function primeDatabase(array $data, \JKingWeb\Arsse\Db\Driver $drv = null): bool { - $drv = $drv ?? $this->drv; + public function primeDatabase(array $data): bool { + $drv = static::$drv; $tr = $drv->begin(); foreach ($data as $table => $info) { $cols = implode(",", array_keys($info['columns'])); @@ -75,7 +122,7 @@ abstract class Base { foreach ($expected as $table => $info) { $cols = implode(",", array_keys($info['columns'])); $types = $info['columns']; - $data = $this->drv->prepare("SELECT $cols from $table")->run()->getAll(); + $data = static::$drv->prepare("SELECT $cols from $table")->run()->getAll(); $cols = array_keys($info['columns']); foreach ($info['rows'] as $index => $row) { $this->assertCount(sizeof($cols), $row, "The number of values for array index $index does not match the number of fields"); diff --git a/tests/cases/Database/SeriesArticle.php b/tests/cases/Database/SeriesArticle.php index 695fe3c..79d1461 100644 --- a/tests/cases/Database/SeriesArticle.php +++ b/tests/cases/Database/SeriesArticle.php @@ -13,463 +13,464 @@ use JKingWeb\Arsse\Misc\Date; use Phake; trait SeriesArticle { - protected $data = [ - 'arsse_users' => [ - 'columns' => [ - 'id' => 'str', - 'password' => 'str', - 'name' => 'str', + protected function setUpSeriesArticle() { + $this->data = [ + 'arsse_users' => [ + 'columns' => [ + 'id' => 'str', + 'password' => 'str', + 'name' => 'str', + ], + 'rows' => [ + ["jane.doe@example.com", "", "Jane Doe"], + ["john.doe@example.com", "", "John Doe"], + ["john.doe@example.org", "", "John Doe"], + ["john.doe@example.net", "", "John Doe"], + ], ], - 'rows' => [ - ["jane.doe@example.com", "", "Jane Doe"], - ["john.doe@example.com", "", "John Doe"], - ["john.doe@example.org", "", "John Doe"], - ["john.doe@example.net", "", "John Doe"], + 'arsse_folders' => [ + 'columns' => [ + 'id' => "int", + 'owner' => "str", + 'parent' => "int", + 'name' => "str", + ], + 'rows' => [ + [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"], + [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_folders' => [ - 'columns' => [ - 'id' => "int", - 'owner' => "str", - 'parent' => "int", - 'name' => "str", + 'arsse_feeds' => [ + 'columns' => [ + 'id' => "int", + 'url' => "str", + 'title' => "str", + ], + 'rows' => [ + [1,"http://example.com/1", "Feed 1"], + [2,"http://example.com/2", "Feed 2"], + [3,"http://example.com/3", "Feed 3"], + [4,"http://example.com/4", "Feed 4"], + [5,"http://example.com/5", "Feed 5"], + [6,"http://example.com/6", "Feed 6"], + [7,"http://example.com/7", "Feed 7"], + [8,"http://example.com/8", "Feed 8"], + [9,"http://example.com/9", "Feed 9"], + [10,"http://example.com/10", "Feed 10"], + [11,"http://example.com/11", "Feed 11"], + [12,"http://example.com/12", "Feed 12"], + [13,"http://example.com/13", "Feed 13"], + ] ], - 'rows' => [ - [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"], - [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' => [ - 'columns' => [ - 'id' => "int", - 'url' => "str", - 'title' => "str", + 'arsse_subscriptions' => [ + 'columns' => [ + 'id' => "int", + 'owner' => "str", + 'feed' => "int", + 'folder' => "int", + 'title' => "str", + ], + 'rows' => [ + [1, "john.doe@example.com",1, null,"Subscription 1"], + [2, "john.doe@example.com",2, null,null], + [3, "john.doe@example.com",3, 1,"Subscription 3"], + [4, "john.doe@example.com",4, 6,null], + [5, "john.doe@example.com",10, 5,"Subscription 5"], + [6, "jane.doe@example.com",1, null,null], + [7, "jane.doe@example.com",10,null,"Subscription 7"], + [8, "john.doe@example.org",11,null,null], + [9, "john.doe@example.org",12,null,"Subscription 9"], + [10,"john.doe@example.org",13,null,null], + [11,"john.doe@example.net",10,null,"Subscription 11"], + [12,"john.doe@example.net",2, 9,null], + [13,"john.doe@example.net",3, 8,"Subscription 13"], + [14,"john.doe@example.net",4, 7,null], + ] ], - 'rows' => [ - [1,"http://example.com/1", "Feed 1"], - [2,"http://example.com/2", "Feed 2"], - [3,"http://example.com/3", "Feed 3"], - [4,"http://example.com/4", "Feed 4"], - [5,"http://example.com/5", "Feed 5"], - [6,"http://example.com/6", "Feed 6"], - [7,"http://example.com/7", "Feed 7"], - [8,"http://example.com/8", "Feed 8"], - [9,"http://example.com/9", "Feed 9"], - [10,"http://example.com/10", "Feed 10"], - [11,"http://example.com/11", "Feed 11"], - [12,"http://example.com/12", "Feed 12"], - [13,"http://example.com/13", "Feed 13"], - ] - ], - 'arsse_subscriptions' => [ - 'columns' => [ - 'id' => "int", - 'owner' => "str", - 'feed' => "int", - 'folder' => "int", - 'title' => "str", + 'arsse_articles' => [ + 'columns' => [ + 'id' => "int", + 'feed' => "int", + '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' => [ + [1,1,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], + [2,1,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], + [3,2,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], + [4,2,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], + [5,3,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], + [6,3,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], + [7,4,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], + [8,4,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], + [9,5,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], + [10,5,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], + [11,6,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], + [12,6,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], + [13,7,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], + [14,7,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], + [15,8,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], + [16,8,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], + [17,9,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], + [18,9,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], + [19,10,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], + [20,10,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], + [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'], + ] ], - 'rows' => [ - [1, "john.doe@example.com",1, null,"Subscription 1"], - [2, "john.doe@example.com",2, null,null], - [3, "john.doe@example.com",3, 1,"Subscription 3"], - [4, "john.doe@example.com",4, 6,null], - [5, "john.doe@example.com",10, 5,"Subscription 5"], - [6, "jane.doe@example.com",1, null,null], - [7, "jane.doe@example.com",10,null,"Subscription 7"], - [8, "john.doe@example.org",11,null,null], - [9, "john.doe@example.org",12,null,"Subscription 9"], - [10,"john.doe@example.org",13,null,null], - [11,"john.doe@example.net",10,null,"Subscription 11"], - [12,"john.doe@example.net",2, 9,null], - [13,"john.doe@example.net",3, 8,"Subscription 13"], - [14,"john.doe@example.net",4, 7,null], - ] - ], - 'arsse_articles' => [ - 'columns' => [ - 'id' => "int", - 'feed' => "int", - '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", + 'arsse_enclosures' => [ + 'columns' => [ + 'article' => "int", + 'url' => "str", + 'type' => "str", + ], + '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"], + + ] ], - 'rows' => [ - [1,1,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], - [2,1,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], - [3,2,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], - [4,2,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], - [5,3,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], - [6,3,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], - [7,4,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], - [8,4,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], - [9,5,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], - [10,5,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], - [11,6,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], - [12,6,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], - [13,7,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], - [14,7,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], - [15,8,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], - [16,8,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], - [17,9,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], - [18,9,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], - [19,10,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"], - [20,10,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"], - [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' => [ - 'article' => "int", - 'url' => "str", - 'type' => "str", + 'arsse_editions' => [ + 'columns' => [ + 'id' => "int", + 'article' => "int", + ], + 'rows' => [ + [1,1], + [2,2], + [3,3], + [4,4], + [5,5], + [6,6], + [7,7], + [8,8], + [9,9], + [10,10], + [11,11], + [12,12], + [13,13], + [14,14], + [15,15], + [16,16], + [17,17], + [18,18], + [19,19], + [20,20], + [101,101], + [102,102], + [103,103], + [104,104], + [105,105], + [202,102], + [203,103], + [204,104], + [205,105], + [305,105], + [1001,20], + ] ], - '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' => [ - 'id' => "int", - 'article' => "int", + 'arsse_marks' => [ + 'columns' => [ + 'subscription' => "int", + 'article' => "int", + 'read' => "bool", + 'starred' => "bool", + 'modified' => "datetime", + 'note' => "str", + ], + 'rows' => [ + [1, 1,1,1,'2000-01-01 00:00:00',''], + [5, 19,1,0,'2016-01-01 00:00:00',''], + [5, 20,0,1,'2005-01-01 00:00:00',''], + [7, 20,1,0,'2010-01-01 00:00:00',''], + [8, 102,1,0,'2000-01-02 02:00:00','Note 2'], + [9, 103,0,1,'2000-01-03 03:00:00','Note 3'], + [9, 104,1,1,'2000-01-04 04:00:00','Note 4'], + [10,105,0,0,'2000-01-05 05:00:00',''], + [11, 19,0,0,'2017-01-01 00:00:00','ook'], + [11, 20,1,0,'2017-01-01 00:00:00','eek'], + [12, 3,0,1,'2017-01-01 00:00:00','ack'], + [12, 4,1,1,'2017-01-01 00:00:00','ach'], + [1, 2,0,0,'2010-01-01 00:00:00','Some Note'], + ] ], - 'rows' => [ - [1,1], - [2,2], - [3,3], - [4,4], - [5,5], - [6,6], - [7,7], - [8,8], - [9,9], - [10,10], - [11,11], - [12,12], - [13,13], - [14,14], - [15,15], - [16,16], - [17,17], - [18,18], - [19,19], - [20,20], - [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' => [ - 'subscription' => "int", - 'article' => "int", - 'read' => "bool", - 'starred' => "bool", - 'modified' => "datetime", - 'note' => "str", + 'arsse_categories' => [ // author-supplied categories + 'columns' => [ + 'article' => "int", + 'name' => "str", + ], + 'rows' => [ + [19,"Fascinating"], + [19,"Logical"], + [20,"Interesting"], + [20,"Logical"], + ], ], - 'rows' => [ - [1, 1,1,1,'2000-01-01 00:00:00',''], - [5, 19,1,0,'2016-01-01 00:00:00',''], - [5, 20,0,1,'2005-01-01 00:00:00',''], - [7, 20,1,0,'2010-01-01 00:00:00',''], - [8, 102,1,0,'2000-01-02 02:00:00','Note 2'], - [9, 103,0,1,'2000-01-03 03:00:00','Note 3'], - [9, 104,1,1,'2000-01-04 04:00:00','Note 4'], - [10,105,0,0,'2000-01-05 05:00:00',''], - [11, 19,0,0,'2017-01-01 00:00:00','ook'], - [11, 20,1,0,'2017-01-01 00:00:00','eek'], - [12, 3,0,1,'2017-01-01 00:00:00','ack'], - [12, 4,1,1,'2017-01-01 00:00:00','ach'], - [1, 2,0,0,'2010-01-01 00:00:00','Some Note'], - ] - ], - 'arsse_categories' => [ // author-supplied categories - 'columns' => [ - 'article' => "int", - 'name' => "str", + 'arsse_labels' => [ + 'columns' => [ + 'id' => "int", + 'owner' => "str", + 'name' => "str", + ], + 'rows' => [ + [1,"john.doe@example.com","Interesting"], + [2,"john.doe@example.com","Fascinating"], + [3,"jane.doe@example.com","Boring"], + [4,"john.doe@example.com","Lonely"], + ], ], - 'rows' => [ - [19,"Fascinating"], - [19,"Logical"], - [20,"Interesting"], - [20,"Logical"], + 'arsse_label_members' => [ + 'columns' => [ + 'label' => "int", + 'article' => "int", + 'subscription' => "int", + 'assigned' => "bool", + 'modified' => "datetime", + ], + 'rows' => [ + [1, 1,1,1,'2000-01-01 00:00:00'], + [2, 1,1,1,'2000-01-01 00:00:00'], + [1,19,5,1,'2000-01-01 00:00:00'], + [2,20,5,1,'2000-01-01 00:00:00'], + [1, 5,3,0,'2000-01-01 00:00:00'], + [2, 5,3,1,'2000-01-01 00:00:00'], + [4, 7,4,0,'2000-01-01 00:00:00'], + [4, 8,4,1,'2015-01-01 00:00:00'], + ], ], - ], - 'arsse_labels' => [ - 'columns' => [ - 'id' => "int", - 'owner' => "str", - 'name' => "str", + ]; + $this->matches = [ + [ + 'id' => 101, + 'url' => 'http://example.com/1', + 'title' => 'Article title 1', + 'subscription_title' => "Feed 11", + 'author' => '', + 'content' => '

Article content 1

', + 'guid' => 'e433653cef2e572eee4215fa299a4a5af9137b2cefd6283c85bd69a32915beda', + 'published_date' => '2000-01-01 00:00:00', + 'edited_date' => '2000-01-01 00:00:01', + 'modified_date' => '2000-01-01 01:00:00', + 'unread' => 1, + 'starred' => 0, + 'edition' => 101, + 'subscription' => 8, + 'fingerprint' => 'f5cb8bfc1c7396dc9816af212a3e2ac5221585c2a00bf7ccb6aabd95dcfcd6a6:fb0bc8f8cb08913dc5a497db700e327f1d34e4987402687d494a5891f24714d4:18fdd4fa93d693128c43b004399e5c9cea6c261ddfa002518d3669f55d8c2207', + 'media_url' => null, + 'media_type' => null, + 'note' => "", ], - 'rows' => [ - [1,"john.doe@example.com","Interesting"], - [2,"john.doe@example.com","Fascinating"], - [3,"jane.doe@example.com","Boring"], - [4,"john.doe@example.com","Lonely"], + [ + 'id' => 102, + 'url' => 'http://example.com/2', + 'title' => 'Article title 2', + 'subscription_title' => "Feed 11", + 'author' => '', + 'content' => '

Article content 2

', + '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' => 0, + 'starred' => 0, + 'edition' => 202, + 'subscription' => 8, + 'fingerprint' => '0e86d2de822a174fe3c44a466953e63ca1f1a58a19cbf475fce0855d4e3d5153:13075894189c47ffcfafd1dfe7fbb539f7c74a69d35a399b3abf8518952714f9:2abd0a8cba83b8214a66c8f0293ba63e467d720540e29ff8ddcdab069d4f1c9e', + 'media_url' => "http://example.com/text", + 'media_type' => "text/plain", + 'note' => "Note 2", ], - ], - 'arsse_label_members' => [ - 'columns' => [ - 'label' => "int", - 'article' => "int", - 'subscription' => "int", - 'assigned' => "bool", - 'modified' => "datetime", + [ + 'id' => 103, + 'url' => 'http://example.com/3', + 'title' => 'Article title 3', + 'subscription_title' => "Subscription 9", + 'author' => '', + 'content' => '

Article content 3

', + 'guid' => '31a6594500a48b59fcc8a075ce82b946c9c3c782460d088bd7b8ef3ede97ad92', + 'published_date' => '2000-01-03 00:00:00', + 'edited_date' => '2000-01-03 00:00:03', + 'modified_date' => '2000-01-03 03:00:00', + 'unread' => 1, + 'starred' => 1, + 'edition' => 203, + 'subscription' => 9, + 'fingerprint' => 'f74b06b240bd08abf4d3fdfc20dba6a6f6eb8b4f1a00e9a617efd63a87180a4b:b278380e984cefe63f0e412b88ffc9cb0befdfa06fdc00bace1da99a8daff406:ad622b31e739cd3a3f3c788991082cf4d2f7a8773773008e75f0572e58cd373b', + 'media_url' => "http://example.com/video", + 'media_type' => "video/webm", + 'note' => "Note 3", ], - 'rows' => [ - [1, 1,1,1,'2000-01-01 00:00:00'], - [2, 1,1,1,'2000-01-01 00:00:00'], - [1,19,5,1,'2000-01-01 00:00:00'], - [2,20,5,1,'2000-01-01 00:00:00'], - [1, 5,3,0,'2000-01-01 00:00:00'], - [2, 5,3,1,'2000-01-01 00:00:00'], - [4, 7,4,0,'2000-01-01 00:00:00'], - [4, 8,4,1,'2015-01-01 00:00:00'], + [ + 'id' => 104, + 'url' => 'http://example.com/4', + 'title' => 'Article title 4', + 'subscription_title' => "Subscription 9", + 'author' => '', + 'content' => '

Article content 4

', + 'guid' => '804e517d623390e71497982c77cf6823180342ebcd2e7d5e32da1e55b09dd180', + 'published_date' => '2000-01-04 00:00:00', + 'edited_date' => '2000-01-04 00:00:04', + 'modified_date' => '2000-01-04 04:00:00', + 'unread' => 0, + 'starred' => 1, + 'edition' => 204, + 'subscription' => 9, + 'fingerprint' => 'f3615c7f16336d3ea242d35cf3fc17dbc4ee3afb78376bf49da2dd7a5a25dec8:f11c2b4046f207579aeb9c69a8c20ca5461cef49756ccfa5ba5e2344266da3b3:ab2da63276acce431250b18d3d49b988b226a99c7faadf275c90b751aee05be9', + 'media_url' => "http://example.com/image", + 'media_type' => "image/svg+xml", + 'note' => "Note 4", ], - ], - ]; - protected $matches = [ - [ - 'id' => 101, - 'url' => 'http://example.com/1', - 'title' => 'Article title 1', - 'subscription_title' => "Feed 11", - 'author' => '', - 'content' => '

Article content 1

', - 'guid' => 'e433653cef2e572eee4215fa299a4a5af9137b2cefd6283c85bd69a32915beda', - 'published_date' => '2000-01-01 00:00:00', - 'edited_date' => '2000-01-01 00:00:01', - 'modified_date' => '2000-01-01 01:00:00', - 'unread' => 1, - 'starred' => 0, - 'edition' => 101, - 'subscription' => 8, - 'fingerprint' => 'f5cb8bfc1c7396dc9816af212a3e2ac5221585c2a00bf7ccb6aabd95dcfcd6a6:fb0bc8f8cb08913dc5a497db700e327f1d34e4987402687d494a5891f24714d4:18fdd4fa93d693128c43b004399e5c9cea6c261ddfa002518d3669f55d8c2207', - 'media_url' => null, - 'media_type' => null, - 'note' => "", - ], - [ - 'id' => 102, - 'url' => 'http://example.com/2', - 'title' => 'Article title 2', - 'subscription_title' => "Feed 11", - 'author' => '', - 'content' => '

Article content 2

', - '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' => 0, - 'starred' => 0, - 'edition' => 202, - 'subscription' => 8, - 'fingerprint' => '0e86d2de822a174fe3c44a466953e63ca1f1a58a19cbf475fce0855d4e3d5153:13075894189c47ffcfafd1dfe7fbb539f7c74a69d35a399b3abf8518952714f9:2abd0a8cba83b8214a66c8f0293ba63e467d720540e29ff8ddcdab069d4f1c9e', - 'media_url' => "http://example.com/text", - 'media_type' => "text/plain", - 'note' => "Note 2", - ], - [ - 'id' => 103, - 'url' => 'http://example.com/3', - 'title' => 'Article title 3', - 'subscription_title' => "Subscription 9", - 'author' => '', - 'content' => '

Article content 3

', - 'guid' => '31a6594500a48b59fcc8a075ce82b946c9c3c782460d088bd7b8ef3ede97ad92', - 'published_date' => '2000-01-03 00:00:00', - 'edited_date' => '2000-01-03 00:00:03', - 'modified_date' => '2000-01-03 03:00:00', - 'unread' => 1, - 'starred' => 1, - 'edition' => 203, - 'subscription' => 9, - 'fingerprint' => 'f74b06b240bd08abf4d3fdfc20dba6a6f6eb8b4f1a00e9a617efd63a87180a4b:b278380e984cefe63f0e412b88ffc9cb0befdfa06fdc00bace1da99a8daff406:ad622b31e739cd3a3f3c788991082cf4d2f7a8773773008e75f0572e58cd373b', - 'media_url' => "http://example.com/video", - 'media_type' => "video/webm", - 'note' => "Note 3", - ], - [ - 'id' => 104, - 'url' => 'http://example.com/4', - 'title' => 'Article title 4', - 'subscription_title' => "Subscription 9", - 'author' => '', - 'content' => '

Article content 4

', - 'guid' => '804e517d623390e71497982c77cf6823180342ebcd2e7d5e32da1e55b09dd180', - 'published_date' => '2000-01-04 00:00:00', - 'edited_date' => '2000-01-04 00:00:04', - 'modified_date' => '2000-01-04 04:00:00', - 'unread' => 0, - 'starred' => 1, - 'edition' => 204, - 'subscription' => 9, - 'fingerprint' => 'f3615c7f16336d3ea242d35cf3fc17dbc4ee3afb78376bf49da2dd7a5a25dec8:f11c2b4046f207579aeb9c69a8c20ca5461cef49756ccfa5ba5e2344266da3b3:ab2da63276acce431250b18d3d49b988b226a99c7faadf275c90b751aee05be9', - 'media_url' => "http://example.com/image", - 'media_type' => "image/svg+xml", - 'note' => "Note 4", - ], - [ - 'id' => 105, - 'url' => 'http://example.com/5', - 'title' => 'Article title 5', - 'subscription_title' => "Feed 13", - 'author' => '', - 'content' => '

Article content 5

', - 'guid' => 'db3e736c2c492f5def5c5da33ddcbea1824040e9ced2142069276b0a6e291a41', - 'published_date' => '2000-01-05 00:00:00', - 'edited_date' => '2000-01-05 00:00:05', - 'modified_date' => '2000-01-05 05:00:00', - 'unread' => 1, - 'starred' => 0, - 'edition' => 305, - 'subscription' => 10, - 'fingerprint' => 'd40da96e39eea6c55948ccbe9b3d275b5f931298288dbe953990c5f496097022:834240f84501b5341d375414718204ec421561f3825d34c22bf9182203e42900:43b970ac6ec5f8a9647b2c7e4eed8b1d7f62e154a95eed748b0294c1256764ba', - 'media_url' => "http://example.com/audio", - 'media_type' => "audio/ogg", - 'note' => "", - ], - ]; - protected $fields = [ - Database::LIST_MINIMAL => [ - "id", "subscription", "feed", "modified_date", "marked_date", "unread", "starred", "edition", "edited_date", - ], - Database::LIST_CONSERVATIVE => [ - "id", "subscription", "feed", "modified_date", "marked_date", "unread", "starred", "edition", "edited_date", - "url", "title", "subscription_title", "author", "guid", "published_date", "fingerprint", - ], - Database::LIST_TYPICAL => [ - "id", "subscription", "feed", "modified_date", "marked_date", "unread", "starred", "edition", "edited_date", - "url", "title", "subscription_title", "author", "guid", "published_date", "fingerprint", - "content", "media_url", "media_type", - ], - Database::LIST_FULL => [ - "id", "subscription", "feed", "modified_date", "marked_date", "unread", "starred", "edition", "edited_date", - "url", "title", "subscription_title", "author", "guid", "published_date", "fingerprint", - "content", "media_url", "media_type", - "note", - ], - ]; - - public function setUpSeries() { + [ + 'id' => 105, + 'url' => 'http://example.com/5', + 'title' => 'Article title 5', + 'subscription_title' => "Feed 13", + 'author' => '', + 'content' => '

Article content 5

', + 'guid' => 'db3e736c2c492f5def5c5da33ddcbea1824040e9ced2142069276b0a6e291a41', + 'published_date' => '2000-01-05 00:00:00', + 'edited_date' => '2000-01-05 00:00:05', + 'modified_date' => '2000-01-05 05:00:00', + 'unread' => 1, + 'starred' => 0, + 'edition' => 305, + 'subscription' => 10, + 'fingerprint' => 'd40da96e39eea6c55948ccbe9b3d275b5f931298288dbe953990c5f496097022:834240f84501b5341d375414718204ec421561f3825d34c22bf9182203e42900:43b970ac6ec5f8a9647b2c7e4eed8b1d7f62e154a95eed748b0294c1256764ba', + 'media_url' => "http://example.com/audio", + 'media_type' => "audio/ogg", + 'note' => "", + ], + ]; + $this->fields = [ + Database::LIST_MINIMAL => [ + "id", "subscription", "feed", "modified_date", "marked_date", "unread", "starred", "edition", "edited_date", + ], + Database::LIST_CONSERVATIVE => [ + "id", "subscription", "feed", "modified_date", "marked_date", "unread", "starred", "edition", "edited_date", + "url", "title", "subscription_title", "author", "guid", "published_date", "fingerprint", + ], + Database::LIST_TYPICAL => [ + "id", "subscription", "feed", "modified_date", "marked_date", "unread", "starred", "edition", "edited_date", + "url", "title", "subscription_title", "author", "guid", "published_date", "fingerprint", + "content", "media_url", "media_type", + ], + Database::LIST_FULL => [ + "id", "subscription", "feed", "modified_date", "marked_date", "unread", "starred", "edition", "edited_date", + "url", "title", "subscription_title", "author", "guid", "published_date", "fingerprint", + "content", "media_url", "media_type", + "note", + ], + ]; $this->checkTables = ['arsse_marks' => ["subscription","article","read","starred","modified","note"],]; $this->user = "john.doe@example.net"; } - protected function compareIds(array $exp, Context $c) { - $ids = array_column($ids = Arsse::$db->articleList($this->user, $c)->getAll(), "id"); - sort($ids); - sort($exp); - $this->assertEquals($exp, $ids); + protected function tearDownSeriesArticle() { + unset($this->data, $this->matches, $this->fields, $this->checkTables, $this->user); } public function testListArticlesCheckingContext() { - $this->user = "john.doe@example.com"; + $compareIds = function(array $exp, Context $c) { + $ids = array_column($ids = Arsse::$db->articleList("john.doe@example.com", $c)->getAll(), "id"); + sort($ids); + sort($exp); + $this->assertEquals($exp, $ids); + }; // get all items for user $exp = [1,2,3,4,5,6,7,8,19,20]; - $this->compareIds($exp, new Context); - $this->compareIds($exp, (new Context)->articles(range(1, Database::LIMIT_ARTICLES * 3))); + $compareIds($exp, new Context); + $compareIds($exp, (new Context)->articles(range(1, Database::LIMIT_ARTICLES * 3))); // get items from a folder tree - $this->compareIds([5,6,7,8], (new Context)->folder(1)); + $compareIds([5,6,7,8], (new Context)->folder(1)); // get items from a leaf folder - $this->compareIds([7,8], (new Context)->folder(6)); + $compareIds([7,8], (new Context)->folder(6)); // get items from a non-leaf folder without descending - $this->compareIds([1,2,3,4], (new Context)->folderShallow(0)); - $this->compareIds([5,6], (new Context)->folderShallow(1)); + $compareIds([1,2,3,4], (new Context)->folderShallow(0)); + $compareIds([5,6], (new Context)->folderShallow(1)); // get items from a single subscription $exp = [19,20]; - $this->compareIds($exp, (new Context)->subscription(5)); + $compareIds($exp, (new Context)->subscription(5)); // get un/read items from a single subscription - $this->compareIds([20], (new Context)->subscription(5)->unread(true)); - $this->compareIds([19], (new Context)->subscription(5)->unread(false)); + $compareIds([20], (new Context)->subscription(5)->unread(true)); + $compareIds([19], (new Context)->subscription(5)->unread(false)); // get starred articles - $this->compareIds([1,20], (new Context)->starred(true)); - $this->compareIds([2,3,4,5,6,7,8,19], (new Context)->starred(false)); - $this->compareIds([1], (new Context)->starred(true)->unread(false)); - $this->compareIds([], (new Context)->starred(true)->unread(false)->subscription(5)); + $compareIds([1,20], (new Context)->starred(true)); + $compareIds([2,3,4,5,6,7,8,19], (new Context)->starred(false)); + $compareIds([1], (new Context)->starred(true)->unread(false)); + $compareIds([], (new Context)->starred(true)->unread(false)->subscription(5)); // get items relative to edition - $this->compareIds([19], (new Context)->subscription(5)->latestEdition(999)); - $this->compareIds([19], (new Context)->subscription(5)->latestEdition(19)); - $this->compareIds([20], (new Context)->subscription(5)->oldestEdition(999)); - $this->compareIds([20], (new Context)->subscription(5)->oldestEdition(1001)); + $compareIds([19], (new Context)->subscription(5)->latestEdition(999)); + $compareIds([19], (new Context)->subscription(5)->latestEdition(19)); + $compareIds([20], (new Context)->subscription(5)->oldestEdition(999)); + $compareIds([20], (new Context)->subscription(5)->oldestEdition(1001)); // get items relative to article ID - $this->compareIds([1,2,3], (new Context)->latestArticle(3)); - $this->compareIds([19,20], (new Context)->oldestArticle(19)); + $compareIds([1,2,3], (new Context)->latestArticle(3)); + $compareIds([19,20], (new Context)->oldestArticle(19)); // get items relative to (feed) modification date $exp = [2,4,6,8,20]; - $this->compareIds($exp, (new Context)->modifiedSince("2005-01-01T00:00:00Z")); - $this->compareIds($exp, (new Context)->modifiedSince("2010-01-01T00:00:00Z")); + $compareIds($exp, (new Context)->modifiedSince("2005-01-01T00:00:00Z")); + $compareIds($exp, (new Context)->modifiedSince("2010-01-01T00:00:00Z")); $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")); + $compareIds($exp, (new Context)->notModifiedSince("2005-01-01T00:00:00Z")); + $compareIds($exp, (new Context)->notModifiedSince("2000-01-01T00:00:00Z")); // get items relative to (user) modification date (both marks and labels apply) - $this->compareIds([8,19], (new Context)->markedSince("2014-01-01T00:00:00Z")); - $this->compareIds([2,4,6,8,19,20], (new Context)->markedSince("2010-01-01T00:00:00Z")); - $this->compareIds([1,2,3,4,5,6,7,20], (new Context)->notMarkedSince("2014-01-01T00:00:00Z")); - $this->compareIds([1,3,5,7], (new Context)->notMarkedSince("2005-01-01T00:00:00Z")); + $compareIds([8,19], (new Context)->markedSince("2014-01-01T00:00:00Z")); + $compareIds([2,4,6,8,19,20], (new Context)->markedSince("2010-01-01T00:00:00Z")); + $compareIds([1,2,3,4,5,6,7,20], (new Context)->notMarkedSince("2014-01-01T00:00:00Z")); + $compareIds([1,3,5,7], (new Context)->notMarkedSince("2005-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)); + $compareIds([1], (new Context)->limit(1)); + $compareIds([2], (new Context)->limit(1)->oldestEdition(1+1)); + $compareIds([3], (new Context)->limit(1)->oldestEdition(2+1)); + $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)); + $compareIds([20], (new Context)->reverse(true)->limit(1)); + $compareIds([19], (new Context)->reverse(true)->limit(1)->latestEdition(1001-1)); + $compareIds([8], (new Context)->reverse(true)->limit(1)->latestEdition(19-1)); + $compareIds([7,6], (new Context)->reverse(true)->limit(2)->latestEdition(8-1)); // get articles by label ID - $this->compareIds([1,19], (new Context)->label(1)); - $this->compareIds([1,5,20], (new Context)->label(2)); + $compareIds([1,19], (new Context)->label(1)); + $compareIds([1,5,20], (new Context)->label(2)); // get articles by label name - $this->compareIds([1,19], (new Context)->labelName("Interesting")); - $this->compareIds([1,5,20], (new Context)->labelName("Fascinating")); + $compareIds([1,19], (new Context)->labelName("Interesting")); + $compareIds([1,5,20], (new Context)->labelName("Fascinating")); // get articles with any or no label - $this->compareIds([1,5,8,19,20], (new Context)->labelled(true)); - $this->compareIds([2,3,4,6,7], (new Context)->labelled(false)); + $compareIds([1,5,8,19,20], (new Context)->labelled(true)); + $compareIds([2,3,4,6,7], (new Context)->labelled(false)); // get a specific article or edition - $this->compareIds([20], (new Context)->article(20)); - $this->compareIds([20], (new Context)->edition(1001)); + $compareIds([20], (new Context)->article(20)); + $compareIds([20], (new Context)->edition(1001)); // get multiple specific articles or editions - $this->compareIds([1,20], (new Context)->articles([1,20,50])); - $this->compareIds([1,20], (new Context)->editions([1,1001,50])); + $compareIds([1,20], (new Context)->articles([1,20,50])); + $compareIds([1,20], (new Context)->editions([1,1001,50])); // get articles base on whether or not they have notes - $this->compareIds([1,3,4,5,6,7,8,19,20], (new Context)->annotated(false)); - $this->compareIds([2], (new Context)->annotated(true)); + $compareIds([1,3,4,5,6,7,8,19,20], (new Context)->annotated(false)); + $compareIds([2], (new Context)->annotated(true)); // get specific starred articles - $this->compareIds([1], (new Context)->articles([1,2,3])->starred(true)); - $this->compareIds([2,3], (new Context)->articles([1,2,3])->starred(false)); + $compareIds([1], (new Context)->articles([1,2,3])->starred(true)); + $compareIds([2,3], (new Context)->articles([1,2,3])->starred(false)); } public function testListArticlesOfAMissingFolder() { diff --git a/tests/cases/Db/SQLite3/TestDatabase.php b/tests/cases/Db/SQLite3/TestDatabase.php new file mode 100644 index 0000000..d40dd88 --- /dev/null +++ b/tests/cases/Db/SQLite3/TestDatabase.php @@ -0,0 +1,19 @@ + + * @covers \JKingWeb\Arsse\Misc\Query + */ +class TestDatabase extends \JKingWeb\Arsse\TestCase\Database\Base { + protected static $implementation = "SQLite 3"; + + protected function nextID(string $table): int { + return static::$drv->query("SELECT (case when max(id) then max(id) else 0 end)+1 from $table")->getValue(); + } +} diff --git a/tests/cases/Db/SQLite3PDO/TestDatabase.php b/tests/cases/Db/SQLite3PDO/TestDatabase.php new file mode 100644 index 0000000..1dca8b7 --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/TestDatabase.php @@ -0,0 +1,19 @@ + + * @covers \JKingWeb\Arsse\Misc\Query + */ +class TestDatabase extends \JKingWeb\Arsse\TestCase\Database\Base { + protected static $implementation = "PDO SQLite 3"; + + protected function nextID(string $table): int { + return (int) static::$drv->query("SELECT (case when max(id) then max(id) else 0 end)+1 from $table")->getValue(); + } +} diff --git a/tests/lib/DatabaseInformation.php b/tests/lib/DatabaseInformation.php index d22a618..4b1d950 100644 --- a/tests/lib/DatabaseInformation.php +++ b/tests/lib/DatabaseInformation.php @@ -7,6 +7,7 @@ declare(strict_types=1); namespace JKingWeb\Arsse\Test; use JKingWeb\Arsse\Arsse; +use JKingWeb\Arsse\Db\Driver; class DatabaseInformation { public $name; @@ -17,6 +18,8 @@ class DatabaseInformation { public $driverClass; public $stringOutput; public $interfaceConstructor; + public $truncateFunction; + public $razeFunction; protected static $data; @@ -50,6 +53,57 @@ class DatabaseInformation { } protected static function getData() { + $sqlite3TableList = function($db): array { + $listTables = "SELECT name from sqlite_master where type = 'table' and name like 'arsse_%'"; + if ($db instanceof Driver) { + $tables = $db->query($listTables)->getAll(); + $tables = sizeof($tables) ? array_column($tables, "name") : []; + } elseif ($db instanceof \PDO) { + $tables = $db->query($listTables)->fetchAll(\PDO::FETCH_ASSOC); + $tables = sizeof($tables) ? array_column($tables, "name") : []; + } else { + $tables = []; + $result = $db->query($listTables); + while ($r = $result->fetchArray(\SQLITE3_ASSOC)) { + $tables[] = $r['name']; + } + $result->finalize(); + } + return $tables; + }; + $sqlite3TruncateFunction = function($db, array $afterStatements = []) use ($sqlite3TableList) { + foreach ($sqlite3TableList($db) as $table) { + if ($table == "arsse_meta") { + $db->exec("DELETE FROM $table where key <> 'schema_version'"); + } else { + $db->exec("DELETE FROM $table"); + } + } + foreach ($afterStatements as $st) { + $db->exec($st); + } + }; + $sqlite3RazeFunction = function($db, array $afterStatements = []) use ($sqlite3TableList) { + $db->exec("PRAGMA foreign_keys=0"); + foreach ($sqlite3TableList($db) as $table) { + $db->exec("DROP TABLE IF EXISTS $table"); + } + $db->exec("PRAGMA user_version=0"); + $db->exec("PRAGMA foreign_keys=1"); + foreach ($afterStatements as $st) { + $db->exec($st); + } + }; + $pgObjectList = function($db): array { + $listObjects = "SELECT table_name as name, 'TABLE' as type from information_schema.tables where table_schema = current_schema() and table_name like 'arsse_%' union SELECT collation_name as name, 'COLLATION' as type from information_schema.collations where collation_schema = current_schema()"; + if ($db instanceof Driver) { + return $db->query($listObjects)->getAll(); + } elseif ($db instanceof \PDO) { + return $db->query($listObjects)->fetchAll(\PDO::FETCH_ASSOC); + } else { + throw \Exception("Native PostgreSQL interface not implemented"); + } + }; return [ 'SQLite 3' => [ 'pdo' => false, @@ -67,7 +121,8 @@ class DatabaseInformation { $d->enableExceptions(true); return $d; }, - + 'truncateFunction' => $sqlite3TruncateFunction, + 'razeFunction' => $sqlite3RazeFunction, ], 'PDO SQLite 3' => [ 'pdo' => true, @@ -85,6 +140,8 @@ class DatabaseInformation { return; } }, + 'truncateFunction' => $sqlite3TruncateFunction, + 'razeFunction' => $sqlite3RazeFunction, ], 'PDO PostgreSQL' => [ 'pdo' => true, @@ -105,6 +162,29 @@ class DatabaseInformation { } return $d; }, + 'truncateFunction' => function($db, array $afterStatements = []) use ($pgObjectList) { + foreach ($objectList($db) as $obj) { + if ($obj['type'] != "TABLE") { + continue; + } elseif ($obj['name'] == "arsse_meta") { + $db->exec("DELETE FROM {$obj['name']} where key <> 'schema_version'"); + } else { + $db->exec("TRUNCATE TABLE {$obj['name']} restart identity cascade"); + } + } + foreach ($afterStatements as $st) { + $db->exec($st); + } + }, + 'razeFunction' => function($db, array $afterStatements = []) use ($pgObjectList) { + foreach ($objectList($db) as $obj) { + $db->exec("DROP {$obj['type']} {$obj['name']} IF EXISTS cascade"); + + } + foreach ($afterStatements as $st) { + $db->exec($st); + } + }, ], ]; }