mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
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
This commit is contained in:
parent
36c5984c47
commit
dccd4caede
10 changed files with 603 additions and 472 deletions
|
@ -4,18 +4,6 @@
|
||||||
|
|
||||||
-- Please consult the SQLite 3 schemata for commented version
|
-- 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(
|
create table arsse_meta(
|
||||||
key text primary key,
|
key text primary key,
|
||||||
value text
|
value text
|
||||||
|
|
|
@ -4,10 +4,6 @@
|
||||||
|
|
||||||
-- Please consult the SQLite 3 schemata for commented version
|
-- 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 (
|
create table arsse_sessions (
|
||||||
id text primary key,
|
id text primary key,
|
||||||
created timestamp(0) with time zone not null default CURRENT_TIMESTAMP,
|
created timestamp(0) with time zone not null default CURRENT_TIMESTAMP,
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
-- create a case-insensitive generic collation sequence
|
-- create a case-insensitive generic collation sequence
|
||||||
-- this collation is Unicode-aware, whereas SQLite's built-in nocase
|
-- this collation is Unicode-aware, whereas SQLite's built-in nocase
|
||||||
-- collation is ASCII-only
|
-- collation is ASCII-only
|
||||||
drop collation if exists nocase cascade;
|
|
||||||
create collation nocase(
|
create collation nocase(
|
||||||
provider = icu,
|
provider = icu,
|
||||||
locale = '@kf=false'
|
locale = '@kf=false'
|
||||||
|
|
|
@ -5,19 +5,6 @@
|
||||||
-- Make the database WAL-journalled; this is persitent
|
-- Make the database WAL-journalled; this is persitent
|
||||||
PRAGMA journal_mode = wal;
|
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(
|
create table arsse_meta(
|
||||||
-- application metadata
|
-- application metadata
|
||||||
key text primary key not null, -- metadata key
|
key text primary key not null, -- metadata key
|
||||||
|
|
|
@ -2,11 +2,6 @@
|
||||||
-- Copyright 2017 J. King, Dustin Wilson et al.
|
-- Copyright 2017 J. King, Dustin Wilson et al.
|
||||||
-- See LICENSE and AUTHORS files for details
|
-- 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 (
|
create table arsse_sessions (
|
||||||
-- sessions for Tiny Tiny RSS (and possibly others)
|
-- sessions for Tiny Tiny RSS (and possibly others)
|
||||||
id text primary key, -- UUID of session
|
id text primary key, -- UUID of session
|
||||||
|
|
|
@ -6,37 +6,72 @@
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
namespace JKingWeb\Arsse\TestCase\Database;
|
namespace JKingWeb\Arsse\TestCase\Database;
|
||||||
|
|
||||||
use JKingWeb\Arsse\User\Driver as UserDriver;
|
use JKingWeb\Arsse\Test\Database;
|
||||||
use JKingWeb\Arsse\Arsse;
|
use JKingWeb\Arsse\Arsse;
|
||||||
use JKingWeb\Arsse\Conf;
|
use JKingWeb\Arsse\Conf;
|
||||||
use JKingWeb\Arsse\User;
|
use JKingWeb\Arsse\User;
|
||||||
use JKingWeb\Arsse\Misc\ValueInfo;
|
use JKingWeb\Arsse\Misc\ValueInfo;
|
||||||
use JKingWeb\Arsse\Database;
|
|
||||||
use JKingWeb\Arsse\Db\Result;
|
use JKingWeb\Arsse\Db\Result;
|
||||||
|
use JKingWeb\Arsse\Test\DatabaseInformation;
|
||||||
use Phake;
|
use Phake;
|
||||||
|
|
||||||
abstract class Base {
|
abstract class Base extends \JKingWeb\Arsse\Test\AbstractTest{
|
||||||
protected $drv;
|
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 $primed = false;
|
||||||
|
|
||||||
protected abstract function nextID(string $table): int;
|
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
|
// establish a clean baseline
|
||||||
self::clearData();
|
static::clearData();
|
||||||
self::setConf();
|
// perform an initial connection to the database to reset its version to zero
|
||||||
// configure and create the relevant database driver
|
// in the case of SQLite this will always be the case (we use a memory database),
|
||||||
$this->setUpDriver();
|
// but other engines should clean up from potentially interrupted prior tests
|
||||||
// create the database interface with the suitable driver
|
static::$dbInfo = new DatabaseInformation(static::$implementation);
|
||||||
Arsse::$db = new Database;
|
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();
|
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
|
// create a mock user manager
|
||||||
Arsse::$user = Phake::mock(User::class);
|
Arsse::$user = Phake::mock(User::class);
|
||||||
Phake::when(Arsse::$user)->authorize->thenReturn(true);
|
Phake::when(Arsse::$user)->authorize->thenReturn(true);
|
||||||
// call the additional setup method if it exists
|
// call the series-specific setup method
|
||||||
if (method_exists($this, "setUpSeries")) {
|
$setUp = "setUp".$this->series;
|
||||||
$this->setUpSeries();
|
$this->$setUp();
|
||||||
}
|
|
||||||
// prime the database with series data if it hasn't already been done
|
// prime the database with series data if it hasn't already been done
|
||||||
if (!$this->primed && isset($this->data)) {
|
if (!$this->primed && isset($this->data)) {
|
||||||
$this->primeDatabase($this->data);
|
$this->primeDatabase($this->data);
|
||||||
|
@ -44,18 +79,30 @@ abstract class Base {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tearDown() {
|
public function tearDown() {
|
||||||
// call the additional teardiwn method if it exists
|
// call the series-specific teardown method
|
||||||
if (method_exists($this, "tearDownSeries")) {
|
$this->series = $this->findTraitofTest($this->getName());
|
||||||
$this->tearDownSeries();
|
$tearDown = "tearDown".$this->series;
|
||||||
}
|
$this->$tearDown();
|
||||||
// clean up
|
// clean up
|
||||||
$this->primed = false;
|
$this->primed = false;
|
||||||
$this->drv = null;
|
// call the database-specific table cleanup function
|
||||||
self::clearData();
|
(static::$dbInfo->truncateFunction)(static::$drv);
|
||||||
|
// clear state
|
||||||
|
static::clearData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function primeDatabase(array $data, \JKingWeb\Arsse\Db\Driver $drv = null): bool {
|
public static function tearDownAfterClass() {
|
||||||
$drv = $drv ?? $this->drv;
|
// 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): bool {
|
||||||
|
$drv = static::$drv;
|
||||||
$tr = $drv->begin();
|
$tr = $drv->begin();
|
||||||
foreach ($data as $table => $info) {
|
foreach ($data as $table => $info) {
|
||||||
$cols = implode(",", array_keys($info['columns']));
|
$cols = implode(",", array_keys($info['columns']));
|
||||||
|
@ -75,7 +122,7 @@ abstract class Base {
|
||||||
foreach ($expected as $table => $info) {
|
foreach ($expected as $table => $info) {
|
||||||
$cols = implode(",", array_keys($info['columns']));
|
$cols = implode(",", array_keys($info['columns']));
|
||||||
$types = $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']);
|
$cols = array_keys($info['columns']);
|
||||||
foreach ($info['rows'] as $index => $row) {
|
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");
|
$this->assertCount(sizeof($cols), $row, "The number of values for array index $index does not match the number of fields");
|
||||||
|
|
|
@ -13,7 +13,8 @@ use JKingWeb\Arsse\Misc\Date;
|
||||||
use Phake;
|
use Phake;
|
||||||
|
|
||||||
trait SeriesArticle {
|
trait SeriesArticle {
|
||||||
protected $data = [
|
protected function setUpSeriesArticle() {
|
||||||
|
$this->data = [
|
||||||
'arsse_users' => [
|
'arsse_users' => [
|
||||||
'columns' => [
|
'columns' => [
|
||||||
'id' => 'str',
|
'id' => 'str',
|
||||||
|
@ -260,7 +261,7 @@ trait SeriesArticle {
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
protected $matches = [
|
$this->matches = [
|
||||||
[
|
[
|
||||||
'id' => 101,
|
'id' => 101,
|
||||||
'url' => 'http://example.com/1',
|
'url' => 'http://example.com/1',
|
||||||
|
@ -362,7 +363,7 @@ trait SeriesArticle {
|
||||||
'note' => "",
|
'note' => "",
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
protected $fields = [
|
$this->fields = [
|
||||||
Database::LIST_MINIMAL => [
|
Database::LIST_MINIMAL => [
|
||||||
"id", "subscription", "feed", "modified_date", "marked_date", "unread", "starred", "edition", "edited_date",
|
"id", "subscription", "feed", "modified_date", "marked_date", "unread", "starred", "edition", "edited_date",
|
||||||
],
|
],
|
||||||
|
@ -382,94 +383,94 @@ trait SeriesArticle {
|
||||||
"note",
|
"note",
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
public function setUpSeries() {
|
|
||||||
$this->checkTables = ['arsse_marks' => ["subscription","article","read","starred","modified","note"],];
|
$this->checkTables = ['arsse_marks' => ["subscription","article","read","starred","modified","note"],];
|
||||||
$this->user = "john.doe@example.net";
|
$this->user = "john.doe@example.net";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function compareIds(array $exp, Context $c) {
|
protected function tearDownSeriesArticle() {
|
||||||
$ids = array_column($ids = Arsse::$db->articleList($this->user, $c)->getAll(), "id");
|
unset($this->data, $this->matches, $this->fields, $this->checkTables, $this->user);
|
||||||
sort($ids);
|
|
||||||
sort($exp);
|
|
||||||
$this->assertEquals($exp, $ids);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testListArticlesCheckingContext() {
|
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
|
// get all items for user
|
||||||
$exp = [1,2,3,4,5,6,7,8,19,20];
|
$exp = [1,2,3,4,5,6,7,8,19,20];
|
||||||
$this->compareIds($exp, new Context);
|
$compareIds($exp, new Context);
|
||||||
$this->compareIds($exp, (new Context)->articles(range(1, Database::LIMIT_ARTICLES * 3)));
|
$compareIds($exp, (new Context)->articles(range(1, Database::LIMIT_ARTICLES * 3)));
|
||||||
// get items from a folder tree
|
// 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
|
// 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
|
// get items from a non-leaf folder without descending
|
||||||
$this->compareIds([1,2,3,4], (new Context)->folderShallow(0));
|
$compareIds([1,2,3,4], (new Context)->folderShallow(0));
|
||||||
$this->compareIds([5,6], (new Context)->folderShallow(1));
|
$compareIds([5,6], (new Context)->folderShallow(1));
|
||||||
// get items from a single subscription
|
// get items from a single subscription
|
||||||
$exp = [19,20];
|
$exp = [19,20];
|
||||||
$this->compareIds($exp, (new Context)->subscription(5));
|
$compareIds($exp, (new Context)->subscription(5));
|
||||||
// get un/read items from a single subscription
|
// get un/read items from a single subscription
|
||||||
$this->compareIds([20], (new Context)->subscription(5)->unread(true));
|
$compareIds([20], (new Context)->subscription(5)->unread(true));
|
||||||
$this->compareIds([19], (new Context)->subscription(5)->unread(false));
|
$compareIds([19], (new Context)->subscription(5)->unread(false));
|
||||||
// get starred articles
|
// get starred articles
|
||||||
$this->compareIds([1,20], (new Context)->starred(true));
|
$compareIds([1,20], (new Context)->starred(true));
|
||||||
$this->compareIds([2,3,4,5,6,7,8,19], (new Context)->starred(false));
|
$compareIds([2,3,4,5,6,7,8,19], (new Context)->starred(false));
|
||||||
$this->compareIds([1], (new Context)->starred(true)->unread(false));
|
$compareIds([1], (new Context)->starred(true)->unread(false));
|
||||||
$this->compareIds([], (new Context)->starred(true)->unread(false)->subscription(5));
|
$compareIds([], (new Context)->starred(true)->unread(false)->subscription(5));
|
||||||
// get items relative to edition
|
// get items relative to edition
|
||||||
$this->compareIds([19], (new Context)->subscription(5)->latestEdition(999));
|
$compareIds([19], (new Context)->subscription(5)->latestEdition(999));
|
||||||
$this->compareIds([19], (new Context)->subscription(5)->latestEdition(19));
|
$compareIds([19], (new Context)->subscription(5)->latestEdition(19));
|
||||||
$this->compareIds([20], (new Context)->subscription(5)->oldestEdition(999));
|
$compareIds([20], (new Context)->subscription(5)->oldestEdition(999));
|
||||||
$this->compareIds([20], (new Context)->subscription(5)->oldestEdition(1001));
|
$compareIds([20], (new Context)->subscription(5)->oldestEdition(1001));
|
||||||
// get items relative to article ID
|
// get items relative to article ID
|
||||||
$this->compareIds([1,2,3], (new Context)->latestArticle(3));
|
$compareIds([1,2,3], (new Context)->latestArticle(3));
|
||||||
$this->compareIds([19,20], (new Context)->oldestArticle(19));
|
$compareIds([19,20], (new Context)->oldestArticle(19));
|
||||||
// get items relative to (feed) modification date
|
// get items relative to (feed) modification date
|
||||||
$exp = [2,4,6,8,20];
|
$exp = [2,4,6,8,20];
|
||||||
$this->compareIds($exp, (new Context)->modifiedSince("2005-01-01T00:00:00Z"));
|
$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("2010-01-01T00:00:00Z"));
|
||||||
$exp = [1,3,5,7,19];
|
$exp = [1,3,5,7,19];
|
||||||
$this->compareIds($exp, (new Context)->notModifiedSince("2005-01-01T00:00:00Z"));
|
$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("2000-01-01T00:00:00Z"));
|
||||||
// get items relative to (user) modification date (both marks and labels apply)
|
// get items relative to (user) modification date (both marks and labels apply)
|
||||||
$this->compareIds([8,19], (new Context)->markedSince("2014-01-01T00:00:00Z"));
|
$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"));
|
$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"));
|
$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([1,3,5,7], (new Context)->notMarkedSince("2005-01-01T00:00:00Z"));
|
||||||
// paged results
|
// paged results
|
||||||
$this->compareIds([1], (new Context)->limit(1));
|
$compareIds([1], (new Context)->limit(1));
|
||||||
$this->compareIds([2], (new Context)->limit(1)->oldestEdition(1+1));
|
$compareIds([2], (new Context)->limit(1)->oldestEdition(1+1));
|
||||||
$this->compareIds([3], (new Context)->limit(1)->oldestEdition(2+1));
|
$compareIds([3], (new Context)->limit(1)->oldestEdition(2+1));
|
||||||
$this->compareIds([4,5], (new Context)->limit(2)->oldestEdition(3+1));
|
$compareIds([4,5], (new Context)->limit(2)->oldestEdition(3+1));
|
||||||
// reversed results
|
// reversed results
|
||||||
$this->compareIds([20], (new Context)->reverse(true)->limit(1));
|
$compareIds([20], (new Context)->reverse(true)->limit(1));
|
||||||
$this->compareIds([19], (new Context)->reverse(true)->limit(1)->latestEdition(1001-1));
|
$compareIds([19], (new Context)->reverse(true)->limit(1)->latestEdition(1001-1));
|
||||||
$this->compareIds([8], (new Context)->reverse(true)->limit(1)->latestEdition(19-1));
|
$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([7,6], (new Context)->reverse(true)->limit(2)->latestEdition(8-1));
|
||||||
// get articles by label ID
|
// get articles by label ID
|
||||||
$this->compareIds([1,19], (new Context)->label(1));
|
$compareIds([1,19], (new Context)->label(1));
|
||||||
$this->compareIds([1,5,20], (new Context)->label(2));
|
$compareIds([1,5,20], (new Context)->label(2));
|
||||||
// get articles by label name
|
// get articles by label name
|
||||||
$this->compareIds([1,19], (new Context)->labelName("Interesting"));
|
$compareIds([1,19], (new Context)->labelName("Interesting"));
|
||||||
$this->compareIds([1,5,20], (new Context)->labelName("Fascinating"));
|
$compareIds([1,5,20], (new Context)->labelName("Fascinating"));
|
||||||
// get articles with any or no label
|
// get articles with any or no label
|
||||||
$this->compareIds([1,5,8,19,20], (new Context)->labelled(true));
|
$compareIds([1,5,8,19,20], (new Context)->labelled(true));
|
||||||
$this->compareIds([2,3,4,6,7], (new Context)->labelled(false));
|
$compareIds([2,3,4,6,7], (new Context)->labelled(false));
|
||||||
// get a specific article or edition
|
// get a specific article or edition
|
||||||
$this->compareIds([20], (new Context)->article(20));
|
$compareIds([20], (new Context)->article(20));
|
||||||
$this->compareIds([20], (new Context)->edition(1001));
|
$compareIds([20], (new Context)->edition(1001));
|
||||||
// get multiple specific articles or editions
|
// get multiple specific articles or editions
|
||||||
$this->compareIds([1,20], (new Context)->articles([1,20,50]));
|
$compareIds([1,20], (new Context)->articles([1,20,50]));
|
||||||
$this->compareIds([1,20], (new Context)->editions([1,1001,50]));
|
$compareIds([1,20], (new Context)->editions([1,1001,50]));
|
||||||
// get articles base on whether or not they have notes
|
// 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));
|
$compareIds([1,3,4,5,6,7,8,19,20], (new Context)->annotated(false));
|
||||||
$this->compareIds([2], (new Context)->annotated(true));
|
$compareIds([2], (new Context)->annotated(true));
|
||||||
// get specific starred articles
|
// get specific starred articles
|
||||||
$this->compareIds([1], (new Context)->articles([1,2,3])->starred(true));
|
$compareIds([1], (new Context)->articles([1,2,3])->starred(true));
|
||||||
$this->compareIds([2,3], (new Context)->articles([1,2,3])->starred(false));
|
$compareIds([2,3], (new Context)->articles([1,2,3])->starred(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testListArticlesOfAMissingFolder() {
|
public function testListArticlesOfAMissingFolder() {
|
||||||
|
|
19
tests/cases/Db/SQLite3/TestDatabase.php
Normal file
19
tests/cases/Db/SQLite3/TestDatabase.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
/** @license MIT
|
||||||
|
* Copyright 2017 J. King, Dustin Wilson et al.
|
||||||
|
* See LICENSE and AUTHORS files for details */
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\Arsse\TestCase\Db\SQLite3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \JKingWeb\Arsse\Database<extended>
|
||||||
|
* @covers \JKingWeb\Arsse\Misc\Query<extended>
|
||||||
|
*/
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
19
tests/cases/Db/SQLite3PDO/TestDatabase.php
Normal file
19
tests/cases/Db/SQLite3PDO/TestDatabase.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
/** @license MIT
|
||||||
|
* Copyright 2017 J. King, Dustin Wilson et al.
|
||||||
|
* See LICENSE and AUTHORS files for details */
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\Arsse\TestCase\Db\SQLite3PDO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \JKingWeb\Arsse\Database<extended>
|
||||||
|
* @covers \JKingWeb\Arsse\Misc\Query<extended>
|
||||||
|
*/
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ declare(strict_types=1);
|
||||||
namespace JKingWeb\Arsse\Test;
|
namespace JKingWeb\Arsse\Test;
|
||||||
|
|
||||||
use JKingWeb\Arsse\Arsse;
|
use JKingWeb\Arsse\Arsse;
|
||||||
|
use JKingWeb\Arsse\Db\Driver;
|
||||||
|
|
||||||
class DatabaseInformation {
|
class DatabaseInformation {
|
||||||
public $name;
|
public $name;
|
||||||
|
@ -17,6 +18,8 @@ class DatabaseInformation {
|
||||||
public $driverClass;
|
public $driverClass;
|
||||||
public $stringOutput;
|
public $stringOutput;
|
||||||
public $interfaceConstructor;
|
public $interfaceConstructor;
|
||||||
|
public $truncateFunction;
|
||||||
|
public $razeFunction;
|
||||||
|
|
||||||
protected static $data;
|
protected static $data;
|
||||||
|
|
||||||
|
@ -50,6 +53,57 @@ class DatabaseInformation {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function getData() {
|
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 [
|
return [
|
||||||
'SQLite 3' => [
|
'SQLite 3' => [
|
||||||
'pdo' => false,
|
'pdo' => false,
|
||||||
|
@ -67,7 +121,8 @@ class DatabaseInformation {
|
||||||
$d->enableExceptions(true);
|
$d->enableExceptions(true);
|
||||||
return $d;
|
return $d;
|
||||||
},
|
},
|
||||||
|
'truncateFunction' => $sqlite3TruncateFunction,
|
||||||
|
'razeFunction' => $sqlite3RazeFunction,
|
||||||
],
|
],
|
||||||
'PDO SQLite 3' => [
|
'PDO SQLite 3' => [
|
||||||
'pdo' => true,
|
'pdo' => true,
|
||||||
|
@ -85,6 +140,8 @@ class DatabaseInformation {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
'truncateFunction' => $sqlite3TruncateFunction,
|
||||||
|
'razeFunction' => $sqlite3RazeFunction,
|
||||||
],
|
],
|
||||||
'PDO PostgreSQL' => [
|
'PDO PostgreSQL' => [
|
||||||
'pdo' => true,
|
'pdo' => true,
|
||||||
|
@ -105,6 +162,29 @@ class DatabaseInformation {
|
||||||
}
|
}
|
||||||
return $d;
|
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);
|
||||||
|
}
|
||||||
|
},
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue