From 7ca0f4e877f61973083fcb543f9c07ec9e5d9a3b Mon Sep 17 00:00:00 2001 From: "J. King" Date: Mon, 18 Dec 2017 18:29:32 -0500 Subject: [PATCH 01/16] Make the SQLite3 driver more generic The changes in this commit should make it more practical to: - Allow the driver to decide for itself whether to try creating a PDO object if its own requirements are not met - Have any driver use a generic schema update procedure - Use the same constructor for native and PDO SQLite --- lib/Database.php | 2 +- lib/Db/AbstractDriver.php | 52 ++++++++++++++++++++++++-- lib/Db/Driver.php | 4 +- lib/Db/SQLite3/Driver.php | 77 ++++++++++++++++----------------------- 4 files changed, 83 insertions(+), 52 deletions(-) diff --git a/lib/Database.php b/lib/Database.php index fdede55..0072cd8 100644 --- a/lib/Database.php +++ b/lib/Database.php @@ -27,7 +27,7 @@ class Database { public function __construct($initialize = true) { $driver = Arsse::$conf->dbDriver; - $this->db = new $driver(); + $this->db = $driver::create(); $ver = $this->db->schemaVersion(); if ($initialize && $ver < self::SCHEMA_VERSION) { $this->db->schemaUpdate(self::SCHEMA_VERSION); diff --git a/lib/Db/AbstractDriver.php b/lib/Db/AbstractDriver.php index 74fc25d..1f106f9 100644 --- a/lib/Db/AbstractDriver.php +++ b/lib/Db/AbstractDriver.php @@ -6,15 +6,13 @@ declare(strict_types=1); namespace JKingWeb\Arsse\Db; +use JKingWeb\Arsse\Arsse; + abstract class AbstractDriver implements Driver { protected $locked = false; protected $transDepth = 0; protected $transStatus = []; - abstract public function prepareArray(string $query, array $paramTypes): Statement; - abstract protected function lock(): bool; - abstract protected function unlock(bool $rollback = false) : bool; - /** @codeCoverageIgnore */ public function schemaVersion(): int { // FIXME: generic schemaVersion() will need to be covered for database engines other than SQLite @@ -25,6 +23,52 @@ abstract class AbstractDriver implements Driver { } } + public function schemaUpdate(int $to, string $basePath = null): bool { + $ver = $this->schemaVersion(); + if (!Arsse::$conf->dbAutoUpdate) { + throw new Exception("updateManual", ['version' => $ver, 'driver_name' => $this->driverName()]); + } elseif ($ver >= $to) { + throw new Exception("updateTooNew", ['difference' => ($ver - $to), 'current' => $ver, 'target' => $to, 'driver_name' => $this->driverName()]); + } + $sep = \DIRECTORY_SEPARATOR; + $path = ($basePath ?? \JKingWeb\Arsse\BASE."sql").$sep.static::schemaID().$sep; + // lock the database + $this->savepointCreate(true); + for ($a = $this->schemaVersion(); $a < $to; $a++) { + $this->savepointCreate(); + try { + $file = $path.$a.".sql"; + if (!file_exists($file)) { + throw new Exception("updateFileMissing", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a]); + } elseif (!is_readable($file)) { + throw new Exception("updateFileUnreadable", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a]); + } + $sql = @file_get_contents($file); + if ($sql===false) { + throw new Exception("updateFileUnusable", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a]); // @codeCoverageIgnore + } + try { + $this->exec($sql); + } catch (\Throwable $e) { + throw new Exception("updateFileError", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a, 'message' => $this->getError()]); + } + if ($this->schemaVersion() != $a+1) { + throw new Exception("updateFileIncomplete", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a]); + } + } catch (\Throwable $e) { + // undo any partial changes from the failed update + $this->savepointUndo(); + // commit any successful updates if updating by more than one version + $this->savepointRelease(); + // throw the error received + throw $e; + } + $this->savepointRelease(); + } + $this->savepointRelease(); + return true; + } + public function begin(bool $lock = false): Transaction { return new Transaction($this, $lock); } diff --git a/lib/Db/Driver.php b/lib/Db/Driver.php index 5485458..bf3bb28 100644 --- a/lib/Db/Driver.php +++ b/lib/Db/Driver.php @@ -13,11 +13,13 @@ interface Driver { const TR_PEND_COMMIT = -1; const TR_PEND_ROLLBACK = -2; - public function __construct(); + public static function create(): Driver; // returns a human-friendly name for the driver (for display in installer, for example) public static function driverName(): string; // returns the version of the scheme of the opened database; if uninitialized should return 0 public function schemaVersion(): int; + // returns the schema set to be used for database set-up + public static function schemaID(): string; // return a Transaction object public function begin(bool $lock = false): Transaction; // manually begin a real or synthetic transactions, with real or synthetic nesting diff --git a/lib/Db/SQLite3/Driver.php b/lib/Db/SQLite3/Driver.php index 7c78c78..a2eace4 100644 --- a/lib/Db/SQLite3/Driver.php +++ b/lib/Db/SQLite3/Driver.php @@ -22,7 +22,7 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver { public function __construct(string $dbFile = null) { // check to make sure required extension is loaded - if (!class_exists("SQLite3")) { + if (!self::requirementsMet()) { throw new Exception("extMissing", self::driverName()); // @codeCoverageIgnore } // if no database file is specified in the configuration, use a suitable default @@ -30,9 +30,7 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver { $mode = \SQLITE3_OPEN_READWRITE | \SQLITE3_OPEN_CREATE; $timeout = Arsse::$conf->dbSQLite3Timeout * 1000; try { - $this->db = $this->makeConnection($dbFile, $mode, Arsse::$conf->dbSQLite3Key); - // enable exceptions - $this->db->enableExceptions(true); + $this->makeConnection($dbFile, $mode, Arsse::$conf->dbSQLite3Key); // set the timeout; parameters are not allowed for pragmas, but this usage should be safe $this->exec("PRAGMA busy_timeout = $timeout"); // set other initial options @@ -60,8 +58,14 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver { } } + public static function requirementsMet(): bool { + return class_exists("SQLite3"); + } + protected function makeConnection(string $file, int $opts, string $key) { - return new \SQLite3($file, $opts, $key); + $this->db = new \SQLite3($file, $opts, $key); + // enable exceptions + $this->db->enableExceptions(true); } public function __destruct() { @@ -72,60 +76,41 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver { unset($this->db); } + public static function create(): \JKingWeb\Arsse\Db\Driver { + if (self::requirementsMet()) { + return new self; + } elseif (PDODriver::requirementsMet()) { + return new PDODriver; + } else { + throw new Exception("extMissing", self::driverName()); + } + } + public static function driverName(): string { return Arsse::$lang->msg("Driver.Db.SQLite3.Name"); } + public static function schemaID(): string { + return "SQLite3"; + } + public function schemaVersion(): int { return $this->query("PRAGMA user_version")->getValue(); } public function schemaUpdate(int $to, string $basePath = null): bool { - $ver = $this->schemaVersion(); - if (!Arsse::$conf->dbAutoUpdate) { - throw new Exception("updateManual", ['version' => $ver, 'driver_name' => $this->driverName()]); - } elseif ($ver >= $to) { - throw new Exception("updateTooNew", ['difference' => ($ver - $to), 'current' => $ver, 'target' => $to, 'driver_name' => $this->driverName()]); - } - $sep = \DIRECTORY_SEPARATOR; - $path = ($basePath ?? \JKingWeb\Arsse\BASE."sql").$sep."SQLite3".$sep; // turn off foreign keys $this->exec("PRAGMA foreign_keys = no"); - // lock the database - $this->savepointCreate(true); - for ($a = $this->schemaVersion(); $a < $to; $a++) { - $this->savepointCreate(); - try { - $file = $path.$a.".sql"; - if (!file_exists($file)) { - throw new Exception("updateFileMissing", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a]); - } elseif (!is_readable($file)) { - throw new Exception("updateFileUnreadable", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a]); - } - $sql = @file_get_contents($file); - if ($sql===false) { - throw new Exception("updateFileUnusable", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a]); // @codeCoverageIgnore - } - try { - $this->exec($sql); - } catch (\Throwable $e) { - throw new Exception("updateFileError", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a, 'message' => $this->getError()]); - } - if ($this->schemaVersion() != $a+1) { - throw new Exception("updateFileIncomplete", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a]); - } - } catch (\Throwable $e) { - // undo any partial changes from the failed update - $this->savepointUndo(); - // commit any successful updates if updating by more than one version - $this->savepointRelease(); - // throw the error received - throw $e; - } - $this->savepointRelease(); + // run the generic updater + try { + parent::schemaUpdate($to, $basePath); + } catch (\Throwable $e) { + // turn foreign keys back on + $this->exec("PRAGMA foreign_keys = yes"); + // pass the exception up + throw $e; } - $this->savepointRelease(); // turn foreign keys back on $this->exec("PRAGMA foreign_keys = yes"); return true; From ef75b5e9ab845f0cb1b7fa3343654b8713738638 Mon Sep 17 00:00:00 2001 From: "J. King" Date: Tue, 19 Dec 2017 12:11:49 -0500 Subject: [PATCH 02/16] SQLite driver tweaks --- lib/Db/AbstractDriver.php | 4 +++ lib/Db/SQLite3/Driver.php | 10 +++---- .../SQLite3/TestDbDriverCreationSQLite3.php | 30 +++++++++---------- .../cases/Db/SQLite3/TestDbDriverSQLite3.php | 2 +- .../cases/Db/SQLite3/TestDbResultSQLite3.php | 4 ++- .../cases/Db/SQLite3/TestDbUpdateSQLite3.php | 8 ++++- 6 files changed, 35 insertions(+), 23 deletions(-) diff --git a/lib/Db/AbstractDriver.php b/lib/Db/AbstractDriver.php index 1f106f9..624074c 100644 --- a/lib/Db/AbstractDriver.php +++ b/lib/Db/AbstractDriver.php @@ -13,6 +13,8 @@ abstract class AbstractDriver implements Driver { protected $transDepth = 0; protected $transStatus = []; + protected abstract function getError(): string; + /** @codeCoverageIgnore */ public function schemaVersion(): int { // FIXME: generic schemaVersion() will need to be covered for database engines other than SQLite @@ -46,6 +48,8 @@ abstract class AbstractDriver implements Driver { $sql = @file_get_contents($file); if ($sql===false) { throw new Exception("updateFileUnusable", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a]); // @codeCoverageIgnore + } elseif ($sql==="") { + throw new Exception("updateFileIncomplete", ['file' => $file, 'driver_name' => $this->driverName(), 'current' => $a]); } try { $this->exec($sql); diff --git a/lib/Db/SQLite3/Driver.php b/lib/Db/SQLite3/Driver.php index a2eace4..7c7beea 100644 --- a/lib/Db/SQLite3/Driver.php +++ b/lib/Db/SQLite3/Driver.php @@ -27,10 +27,9 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver { } // if no database file is specified in the configuration, use a suitable default $dbFile = $dbFile ?? Arsse::$conf->dbSQLite3File ?? \JKingWeb\Arsse\BASE."arsse.db"; - $mode = \SQLITE3_OPEN_READWRITE | \SQLITE3_OPEN_CREATE; $timeout = Arsse::$conf->dbSQLite3Timeout * 1000; try { - $this->makeConnection($dbFile, $mode, Arsse::$conf->dbSQLite3Key); + $this->makeConnection($dbFile, Arsse::$conf->dbSQLite3Key); // set the timeout; parameters are not allowed for pragmas, but this usage should be safe $this->exec("PRAGMA busy_timeout = $timeout"); // set other initial options @@ -62,8 +61,8 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver { return class_exists("SQLite3"); } - protected function makeConnection(string $file, int $opts, string $key) { - $this->db = new \SQLite3($file, $opts, $key); + protected function makeConnection(string $file, string $key) { + $this->db = new \SQLite3($file, \SQLITE3_OPEN_READWRITE | \SQLITE3_OPEN_CREATE, $key); // enable exceptions $this->db->enableExceptions(true); } @@ -76,6 +75,7 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver { unset($this->db); } + /** @codeCoverageIgnore */ public static function create(): \JKingWeb\Arsse\Db\Driver { if (self::requirementsMet()) { return new self; @@ -96,7 +96,7 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver { } public function schemaVersion(): int { - return $this->query("PRAGMA user_version")->getValue(); + return (int) $this->query("PRAGMA user_version")->getValue(); } public function schemaUpdate(int $to, string $basePath = null): bool { diff --git a/tests/cases/Db/SQLite3/TestDbDriverCreationSQLite3.php b/tests/cases/Db/SQLite3/TestDbDriverCreationSQLite3.php index 402542a..ebd9249 100644 --- a/tests/cases/Db/SQLite3/TestDbDriverCreationSQLite3.php +++ b/tests/cases/Db/SQLite3/TestDbDriverCreationSQLite3.php @@ -7,6 +7,7 @@ declare(strict_types=1); namespace JKingWeb\Arsse; use JKingWeb\Arsse\Arsse; +use JKingWeb\Arsse\Db\SQLite3\Driver; use org\bovigo\vfs\vfsStream; use Phake; @@ -19,7 +20,7 @@ class TestDbDriverCreationSQLite3 extends Test\AbstractTest { protected $ch; public function setUp() { - if (!extension_loaded("sqlite3")) { + if (!Driver::requirementsMet()) { $this->markTestSkipped("SQLite extension not loaded"); } $this->clearData(); @@ -107,7 +108,6 @@ class TestDbDriverCreationSQLite3 extends Test\AbstractTest { // set up configuration Arsse::$conf = new Conf(); Arsse::$conf->dbSQLite3File = ":memory:"; - // set up database shim } public function tearDown() { @@ -117,78 +117,78 @@ class TestDbDriverCreationSQLite3 extends Test\AbstractTest { public function testFailToCreateDatabase() { Arsse::$conf->dbSQLite3File = $this->path."Cmain/arsse.db"; $this->assertException("fileUncreatable", "Db"); - new Db\SQLite3\Driver; + new Driver; } public function testFailToCreateJournal() { Arsse::$conf->dbSQLite3File = $this->path."Cwal/arsse.db"; $this->assertException("fileUncreatable", "Db"); - new Db\SQLite3\Driver; + new Driver; } public function testFailToCreateSharedMmeory() { Arsse::$conf->dbSQLite3File = $this->path."Cshm/arsse.db"; $this->assertException("fileUncreatable", "Db"); - new Db\SQLite3\Driver; + new Driver; } public function testFailToReadDatabase() { Arsse::$conf->dbSQLite3File = $this->path."Rmain/arsse.db"; $this->assertException("fileUnreadable", "Db"); - new Db\SQLite3\Driver; + new Driver; } public function testFailToReadJournal() { Arsse::$conf->dbSQLite3File = $this->path."Rwal/arsse.db"; $this->assertException("fileUnreadable", "Db"); - new Db\SQLite3\Driver; + new Driver; } public function testFailToReadSharedMmeory() { Arsse::$conf->dbSQLite3File = $this->path."Rshm/arsse.db"; $this->assertException("fileUnreadable", "Db"); - new Db\SQLite3\Driver; + new Driver; } public function testFailToWriteToDatabase() { Arsse::$conf->dbSQLite3File = $this->path."Wmain/arsse.db"; $this->assertException("fileUnwritable", "Db"); - new Db\SQLite3\Driver; + new Driver; } public function testFailToWriteToJournal() { Arsse::$conf->dbSQLite3File = $this->path."Wwal/arsse.db"; $this->assertException("fileUnwritable", "Db"); - new Db\SQLite3\Driver; + new Driver; } public function testFailToWriteToSharedMmeory() { Arsse::$conf->dbSQLite3File = $this->path."Wshm/arsse.db"; $this->assertException("fileUnwritable", "Db"); - new Db\SQLite3\Driver; + new Driver; } public function testFailToAccessDatabase() { Arsse::$conf->dbSQLite3File = $this->path."Amain/arsse.db"; $this->assertException("fileUnusable", "Db"); - new Db\SQLite3\Driver; + new Driver; } public function testFailToAccessJournal() { Arsse::$conf->dbSQLite3File = $this->path."Awal/arsse.db"; $this->assertException("fileUnusable", "Db"); - new Db\SQLite3\Driver; + new Driver; } public function testFailToAccessSharedMmeory() { Arsse::$conf->dbSQLite3File = $this->path."Ashm/arsse.db"; $this->assertException("fileUnusable", "Db"); - new Db\SQLite3\Driver; + new Driver; } public function testAssumeDatabaseCorruption() { Arsse::$conf->dbSQLite3File = $this->path."corrupt/arsse.db"; $this->assertException("fileCorrupt", "Db"); - new Db\SQLite3\Driver; + new Driver; } } diff --git a/tests/cases/Db/SQLite3/TestDbDriverSQLite3.php b/tests/cases/Db/SQLite3/TestDbDriverSQLite3.php index 051ebae..224f2cc 100644 --- a/tests/cases/Db/SQLite3/TestDbDriverSQLite3.php +++ b/tests/cases/Db/SQLite3/TestDbDriverSQLite3.php @@ -15,7 +15,7 @@ class TestDbDriverSQLite3 extends Test\AbstractTest { protected $ch; public function setUp() { - if (!extension_loaded("sqlite3")) { + if (!Db\SQLite3\Driver::requirementsMet()) { $this->markTestSkipped("SQLite extension not loaded"); } $this->clearData(); diff --git a/tests/cases/Db/SQLite3/TestDbResultSQLite3.php b/tests/cases/Db/SQLite3/TestDbResultSQLite3.php index 19ae8db..31b5bca 100644 --- a/tests/cases/Db/SQLite3/TestDbResultSQLite3.php +++ b/tests/cases/Db/SQLite3/TestDbResultSQLite3.php @@ -11,7 +11,8 @@ class TestDbResultSQLite3 extends Test\AbstractTest { protected $c; public function setUp() { - if (!extension_loaded("sqlite3")) { + $this->clearData(); + if (!Db\SQLite3\Driver::requirementsMet()) { $this->markTestSkipped("SQLite extension not loaded"); } $c = new \SQLite3(":memory:"); @@ -22,6 +23,7 @@ class TestDbResultSQLite3 extends Test\AbstractTest { public function tearDown() { $this->c->close(); unset($this->c); + $this->clearData(); } public function testConstructResult() { diff --git a/tests/cases/Db/SQLite3/TestDbUpdateSQLite3.php b/tests/cases/Db/SQLite3/TestDbUpdateSQLite3.php index 61e5ced..7c48d2f 100644 --- a/tests/cases/Db/SQLite3/TestDbUpdateSQLite3.php +++ b/tests/cases/Db/SQLite3/TestDbUpdateSQLite3.php @@ -68,6 +68,12 @@ class TestDbUpdateSQLite3 extends Test\AbstractTest { $this->drv->schemaUpdate(1, $this->base); } + public function testLoadEmptyFile() { + file_put_contents($this->path."0.sql", ""); + $this->assertException("updateFileIncomplete", "Db"); + $this->drv->schemaUpdate(1, $this->base); + } + public function testLoadCorrectFile() { file_put_contents($this->path."0.sql", self::MINIMAL1); $this->drv->schemaUpdate(1, $this->base); @@ -76,7 +82,7 @@ class TestDbUpdateSQLite3 extends Test\AbstractTest { public function testPerformPartialUpdate() { file_put_contents($this->path."0.sql", self::MINIMAL1); - file_put_contents($this->path."1.sql", ""); + file_put_contents($this->path."1.sql", " "); $this->assertException("updateFileIncomplete", "Db"); try { $this->drv->schemaUpdate(2, $this->base); From ad6a09ffa127843c8de34d3a01cf7b7baa4da7e8 Mon Sep 17 00:00:00 2001 From: "J. King" Date: Tue, 19 Dec 2017 17:15:05 -0500 Subject: [PATCH 03/16] Partially tested pdo_sqlite driver; improves #72 --- lib/Db/PDODriver.php | 47 +++ lib/Db/PDOError.php | 42 +++ lib/Db/PDOResult.php | 49 +++ lib/Db/PDOStatement.php | 85 +++++ lib/Db/SQLite3/PDODriver.php | 46 +++ locale/en.php | 1 + .../TestDbDriverCreationSQLite3PDO.php | 195 ++++++++++ .../Db/SQLite3PDO/TestDbDriverSQLite3PDO.php | 337 ++++++++++++++++++ .../Db/SQLite3PDO/TestDbResultSQLite3PDO.php | 104 ++++++ .../SQLite3PDO/TestDbStatementSQLite3PDO.php | 105 ++++++ .../Db/SQLite3PDO/TestDbUpdateSQLite3PDO.php | 120 +++++++ tests/phpunit.xml | 8 +- 12 files changed, 1138 insertions(+), 1 deletion(-) create mode 100644 lib/Db/PDODriver.php create mode 100644 lib/Db/PDOError.php create mode 100644 lib/Db/PDOResult.php create mode 100644 lib/Db/PDOStatement.php create mode 100644 lib/Db/SQLite3/PDODriver.php create mode 100644 tests/cases/Db/SQLite3PDO/TestDbDriverCreationSQLite3PDO.php create mode 100644 tests/cases/Db/SQLite3PDO/TestDbDriverSQLite3PDO.php create mode 100644 tests/cases/Db/SQLite3PDO/TestDbResultSQLite3PDO.php create mode 100644 tests/cases/Db/SQLite3PDO/TestDbStatementSQLite3PDO.php create mode 100644 tests/cases/Db/SQLite3PDO/TestDbUpdateSQLite3PDO.php diff --git a/lib/Db/PDODriver.php b/lib/Db/PDODriver.php new file mode 100644 index 0000000..99dfe67 --- /dev/null +++ b/lib/Db/PDODriver.php @@ -0,0 +1,47 @@ +db->exec($query); + return true; + } catch (\PDOException $e) { + list($excClass, $excMsg, $excData) = $this->exceptionBuild(); + throw new $excClass($excMsg, $excData); + } + } + + public function query(string $query): Result { + try { + $r = $this->db->query($query); + } catch (\PDOException $e) { + list($excClass, $excMsg, $excData) = $this->exceptionBuild(); + throw new $excClass($excMsg, $excData); + } + $changes = $r->rowCount(); + try { + $lastId = 0; + $lastId = $this->db->lastInsertId(); + } catch (\PDOException $e) { // @codeCoverageIgnore + } + return new PDOResult($r, [$changes, $lastId]); + } + + public function prepareArray(string $query, array $paramTypes): Statement { + try { + $s = $this->db->prepare($query); + } catch (\PDOException $e) { + list($excClass, $excMsg, $excData) = $this->exceptionBuild(); + throw new $excClass($excMsg, $excData); + } + return new PDOStatement($this->db, $s, $paramTypes); + } +} \ No newline at end of file diff --git a/lib/Db/PDOError.php b/lib/Db/PDOError.php new file mode 100644 index 0000000..929fe1e --- /dev/null +++ b/lib/Db/PDOError.php @@ -0,0 +1,42 @@ +st->errorInfo(); + } else { + $err = $this->db->errorInfo(); + } + switch ($err[0]) { + case "23000": + return [ExceptionInput::class, "constraintViolation", $err[2]]; + case "HY000": + // engine-specific errors + switch ($this->db->getAttribute(\PDO::ATTR_DRIVER_NAME)) { + case "sqlite": + switch ($err[1]) { + case \JKingWeb\Arsse\Db\SQLite3\Driver::SQLITE_BUSY: + return [ExceptionTimeout::class, 'general', $err[2]]; + case \JKingWeb\Arsse\Db\SQLite3\Driver::SQLITE_MISMATCH: + return [ExceptionInput::class, 'engineTypeViolation', $err[2]]; + default: + return [Exception::class, "engineErrorGeneral", $err[1]." - ".$err[2]]; + } + default: + return [Exception::class, "engineErrorGeneral", $err[2]]; // @codeCoverageIgnore + } + default: + return [Exception::class, "engineErrorGeneral", $err[0].": ".$err[2]]; // @codeCoverageIgnore + } + } + + public function getError(): string { + return (string) $this->db->errorInfo()[2]; + } +} \ No newline at end of file diff --git a/lib/Db/PDOResult.php b/lib/Db/PDOResult.php new file mode 100644 index 0000000..32400e9 --- /dev/null +++ b/lib/Db/PDOResult.php @@ -0,0 +1,49 @@ +rows; + } + + public function lastId() { + return $this->id; + } + + // constructor/destructor + + public function __construct(\PDOStatement $result, array $changes = [0,0]) { + $this->set = $result; + $this->rows = (int) $changes[0]; + $this->id = (int) $changes[1]; + } + + public function __destruct() { + try { + $this->set->closeCursor(); + } catch (\PDOException $e) { // @codeCoverageIgnore + } + unset($this->set); + } + + // PHP iterator methods + + public function valid() { + $this->cur = $this->set->fetch(\PDO::FETCH_ASSOC); + return ($this->cur !== false); + } +} diff --git a/lib/Db/PDOStatement.php b/lib/Db/PDOStatement.php new file mode 100644 index 0000000..a8d459e --- /dev/null +++ b/lib/Db/PDOStatement.php @@ -0,0 +1,85 @@ + \PDO::PARAM_NULL, + "integer" => \PDO::PARAM_INT, + "float" => \PDO::PARAM_STR, + "date" => \PDO::PARAM_STR, + "time" => \PDO::PARAM_STR, + "datetime" => \PDO::PARAM_STR, + "binary" => \PDO::PARAM_LOB, + "string" => \PDO::PARAM_STR, + "boolean" => \PDO::PARAM_BOOL, + ]; + + protected $st; + protected $db; + + public function __construct(\PDO $db, \PDOStatement $st, array $bindings = []) { + $this->db = $db; + $this->st = $st; + $this->rebindArray($bindings); + } + + public function __destruct() { + unset($this->st); + } + + public function runArray(array $values = []): \JKingWeb\Arsse\Db\Result { + $this->st->closeCursor(); + $this->bindValues($values); + try { + $this->st->execute(); + } catch (\PDOException $e) { + list($excClass, $excMsg, $excData) = $this->exceptionBuild(); + throw new $excClass($excMsg, $excData); + } + $changes = $this->st->rowCount(); + try { + $lastId = 0; + $lastId = $this->db->lastInsertId(); + } catch (\PDOException $e) { // @codeCoverageIgnore + } + return new PDOResult($this->st, [$changes, $lastId]); + } + + protected function bindValues(array $values, int $offset = 0): int { + $a = $offset; + foreach ($values as $value) { + if (is_array($value)) { + // recursively flatten any arrays, which may be provided for SET or IN() clauses + $a += $this->bindValues($value, $a); + } elseif (array_key_exists($a, $this->types)) { + // if the parameter type is something other than the known values, this is an error + assert(array_key_exists($this->types[$a], self::BINDINGS), new Exception("paramTypeUnknown", $this->types[$a])); + // if the parameter type is null or the value is null (and the type is nullable), just bind null + if ($this->types[$a]=="null" || ($this->isNullable[$a] && is_null($value))) { + $this->st->bindValue($a+1, null, \PDO::PARAM_NULL); + } else { + // otherwise cast the value to the right type and bind the result + $type = self::BINDINGS[$this->types[$a]]; + $value = $this->cast($value, $this->types[$a], $this->isNullable[$a]); + // re-adjust for null casts + if ($value===null) { + $type = \PDO::PARAM_NULL; + } + // perform binding + $this->st->bindValue($a+1, $value, $type); + } + $a++; + } else { + throw new Exception("paramTypeMissing", $a+1); + } + } + return $a - $offset; + } +} diff --git a/lib/Db/SQLite3/PDODriver.php b/lib/Db/SQLite3/PDODriver.php new file mode 100644 index 0000000..a78dc24 --- /dev/null +++ b/lib/Db/SQLite3/PDODriver.php @@ -0,0 +1,46 @@ +db = new \PDO("sqlite:".$file, "", "", [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]); + } + + public function __destruct() { + unset($this->db); + } + + /** @codeCoverageIgnore */ + public static function create(): \JKingWeb\Arsse\Db\Driver { + if (self::requirementsMet()) { + return new self; + } elseif (Driver::requirementsMet()) { + return new Driver; + } else { + throw new Exception("extMissing", self::driverName()); + } + } + + + public static function driverName(): string { + return Arsse::$lang->msg("Driver.Db.SQLite3PDO.Name"); + } +} diff --git a/locale/en.php b/locale/en.php index e284847..a6d1698 100644 --- a/locale/en.php +++ b/locale/en.php @@ -16,6 +16,7 @@ return [ 'API.TTRSS.FeedCount' => '{0, select, 1 {(1 feed)} other {({0} feeds)}}', 'Driver.Db.SQLite3.Name' => 'SQLite 3', + 'Driver.Db.SQLite3PDO.Name' => 'SQLite 3 (PDO)', 'Driver.Service.Curl.Name' => 'HTTP (curl)', 'Driver.Service.Internal.Name' => 'Internal', 'Driver.User.Internal.Name' => 'Internal', diff --git a/tests/cases/Db/SQLite3PDO/TestDbDriverCreationSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/TestDbDriverCreationSQLite3PDO.php new file mode 100644 index 0000000..436d4e2 --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/TestDbDriverCreationSQLite3PDO.php @@ -0,0 +1,195 @@ + + * @covers \JKingWeb\Arsse\Db\PDODriver + * @covers \JKingWeb\Arsse\Db\PDOError */ +class TestDbDriverCreationSQLite3PDO extends Test\AbstractTest { + protected $data; + protected $drv; + protected $ch; + + public function setUp() { + if (!Driver::requirementsMet()) { + $this->markTestSkipped("PDO-SQLite extension not loaded"); + } + $this->clearData(); + // test files + $this->files = [ + // cannot create files + 'Cmain' => [], + 'Cshm' => [ + 'arsse.db' => "", + 'arsse.db-wal' => "", + ], + 'Cwal' => [ + 'arsse.db' => "", + ], + // cannot write to files + 'Wmain' => [ + 'arsse.db' => "", + 'arsse.db-wal' => "", + 'arsse.db-shm' => "", + ], + 'Wwal' => [ + 'arsse.db' => "", + 'arsse.db-wal' => "", + 'arsse.db-shm' => "", + ], + 'Wshm' => [ + 'arsse.db' => "", + 'arsse.db-wal' => "", + 'arsse.db-shm' => "", + ], + // cannot read from files + 'Rmain' => [ + 'arsse.db' => "", + 'arsse.db-wal' => "", + 'arsse.db-shm' => "", + ], + 'Rwal' => [ + 'arsse.db' => "", + 'arsse.db-wal' => "", + 'arsse.db-shm' => "", + ], + 'Rshm' => [ + 'arsse.db' => "", + 'arsse.db-wal' => "", + 'arsse.db-shm' => "", + ], + // can neither read from or write to files + 'Amain' => [ + 'arsse.db' => "", + 'arsse.db-wal' => "", + 'arsse.db-shm' => "", + ], + 'Awal' => [ + 'arsse.db' => "", + 'arsse.db-wal' => "", + 'arsse.db-shm' => "", + ], + 'Ashm' => [ + 'arsse.db' => "", + 'arsse.db-wal' => "", + 'arsse.db-shm' => "", + ], + // non-filesystem errors + 'corrupt' => [ + 'arsse.db' => "", + 'arsse.db-wal' => "", + 'arsse.db-shm' => "", + ], + ]; + $vfs = vfsStream::setup("dbtest", 0777, $this->files); + $this->path = $path = $vfs->url()."/"; + // set up access blocks + chmod($path."Cmain", 0555); + chmod($path."Cwal", 0555); + chmod($path."Cshm", 0555); + chmod($path."Rmain/arsse.db", 0333); + chmod($path."Rwal/arsse.db-wal", 0333); + chmod($path."Rshm/arsse.db-shm", 0333); + chmod($path."Wmain/arsse.db", 0555); + chmod($path."Wwal/arsse.db-wal", 0555); + chmod($path."Wshm/arsse.db-shm", 0555); + chmod($path."Amain/arsse.db", 0111); + chmod($path."Awal/arsse.db-wal", 0111); + chmod($path."Ashm/arsse.db-shm", 0111); + // set up configuration + Arsse::$conf = new Conf(); + Arsse::$conf->dbSQLite3File = ":memory:"; + } + + public function tearDown() { + $this->clearData(); + } + + public function testFailToCreateDatabase() { + Arsse::$conf->dbSQLite3File = $this->path."Cmain/arsse.db"; + $this->assertException("fileUncreatable", "Db"); + new Driver; + } + + public function testFailToCreateJournal() { + Arsse::$conf->dbSQLite3File = $this->path."Cwal/arsse.db"; + $this->assertException("fileUncreatable", "Db"); + new Driver; + } + + public function testFailToCreateSharedMmeory() { + Arsse::$conf->dbSQLite3File = $this->path."Cshm/arsse.db"; + $this->assertException("fileUncreatable", "Db"); + new Driver; + } + + public function testFailToReadDatabase() { + Arsse::$conf->dbSQLite3File = $this->path."Rmain/arsse.db"; + $this->assertException("fileUnreadable", "Db"); + new Driver; + } + + public function testFailToReadJournal() { + Arsse::$conf->dbSQLite3File = $this->path."Rwal/arsse.db"; + $this->assertException("fileUnreadable", "Db"); + new Driver; + } + + public function testFailToReadSharedMmeory() { + Arsse::$conf->dbSQLite3File = $this->path."Rshm/arsse.db"; + $this->assertException("fileUnreadable", "Db"); + new Driver; + } + + public function testFailToWriteToDatabase() { + Arsse::$conf->dbSQLite3File = $this->path."Wmain/arsse.db"; + $this->assertException("fileUnwritable", "Db"); + new Driver; + } + + public function testFailToWriteToJournal() { + Arsse::$conf->dbSQLite3File = $this->path."Wwal/arsse.db"; + $this->assertException("fileUnwritable", "Db"); + new Driver; + } + + public function testFailToWriteToSharedMmeory() { + Arsse::$conf->dbSQLite3File = $this->path."Wshm/arsse.db"; + $this->assertException("fileUnwritable", "Db"); + new Driver; + } + + public function testFailToAccessDatabase() { + Arsse::$conf->dbSQLite3File = $this->path."Amain/arsse.db"; + $this->assertException("fileUnusable", "Db"); + new Driver; + } + + public function testFailToAccessJournal() { + Arsse::$conf->dbSQLite3File = $this->path."Awal/arsse.db"; + $this->assertException("fileUnusable", "Db"); + new Driver; + } + + public function testFailToAccessSharedMmeory() { + Arsse::$conf->dbSQLite3File = $this->path."Ashm/arsse.db"; + $this->assertException("fileUnusable", "Db"); + new Driver; + } + + public function testAssumeDatabaseCorruption() { + Arsse::$conf->dbSQLite3File = $this->path."corrupt/arsse.db"; + $this->assertException("fileCorrupt", "Db"); + new Driver; + } +} diff --git a/tests/cases/Db/SQLite3PDO/TestDbDriverSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/TestDbDriverSQLite3PDO.php new file mode 100644 index 0000000..bb96112 --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/TestDbDriverSQLite3PDO.php @@ -0,0 +1,337 @@ + + * @covers \JKingWeb\Arsse\Db\PDODriver + * @covers \JKingWeb\Arsse\Db\PDOError */ +class TestDbDriverSQLite3PDO extends Test\AbstractTest { + protected $data; + protected $drv; + protected $ch; + + public function setUp() { + if (!Db\SQLite3\PDODriver::requirementsMet()) { + $this->markTestSkipped("PDO-SQLite extension not loaded"); + } + $this->clearData(); + $conf = new Conf(); + Arsse::$conf = $conf; + $conf->dbDriver = Db\SQLite3\PDODriver::class; + $conf->dbSQLite3Timeout = 0; + $conf->dbSQLite3File = tempnam(sys_get_temp_dir(), 'ook'); + $this->drv = new Db\SQLite3\PDODriver(); + $this->ch = new \PDO("sqlite:".Arsse::$conf->dbSQLite3File, "", "", [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]); + } + + public function tearDown() { + unset($this->drv); + unset($this->ch); + if (isset(Arsse::$conf)) { + unlink(Arsse::$conf->dbSQLite3File); + } + $this->clearData(); + } + + public function testFetchDriverName() { + $class = Arsse::$conf->dbDriver; + $this->assertTrue(strlen($class::driverName()) > 0); + } + + public function testCheckCharacterSetAcceptability() { + $this->assertTrue($this->drv->charsetAcceptable()); + } + + public function testExecAValidStatement() { + $this->assertTrue($this->drv->exec("CREATE TABLE test(id integer primary key)")); + } + + public function testExecAnInvalidStatement() { + $this->assertException("engineErrorGeneral", "Db"); + $this->drv->exec("And the meek shall inherit the earth..."); + } + + public function testExecMultipleStatements() { + $this->assertTrue($this->drv->exec("CREATE TABLE test(id integer primary key); INSERT INTO test(id) values(2112)")); + $this->assertEquals(2112, $this->ch->query("SELECT id from test")->fetchColumn()); + } + + public function testExecTimeout() { + $this->ch->exec("BEGIN EXCLUSIVE TRANSACTION"); + $this->assertException("general", "Db", "ExceptionTimeout"); + $this->drv->exec("CREATE TABLE test(id integer primary key)"); + } + + public function testExecConstraintViolation() { + $this->drv->exec("CREATE TABLE test(id integer not null)"); + $this->assertException("constraintViolation", "Db", "ExceptionInput"); + $this->drv->exec("INSERT INTO test(id) values(null)"); + } + + public function testExecTypeViolation() { + $this->drv->exec("CREATE TABLE test(id integer primary key)"); + $this->assertException("typeViolation", "Db", "ExceptionInput"); + $this->drv->exec("INSERT INTO test(id) values('ook')"); + } + + public function testMakeAValidQuery() { + $this->assertInstanceOf(Db\Result::class, $this->drv->query("SELECT 1")); + } + + public function testMakeAnInvalidQuery() { + $this->assertException("engineErrorGeneral", "Db"); + $this->drv->query("Apollo was astonished; Dionysus thought me mad"); + } + + public function testQueryTimeout() { + $this->ch->exec("BEGIN EXCLUSIVE TRANSACTION"); + $this->assertException("general", "Db", "ExceptionTimeout"); + $this->drv->query("CREATE TABLE test(id integer primary key)"); + } + + public function testQueryConstraintViolation() { + $this->drv->exec("CREATE TABLE test(id integer not null)"); + $this->assertException("constraintViolation", "Db", "ExceptionInput"); + $this->drv->query("INSERT INTO test(id) values(null)"); + } + + public function testQueryTypeViolation() { + $this->drv->exec("CREATE TABLE test(id integer primary key)"); + $this->assertException("typeViolation", "Db", "ExceptionInput"); + $this->drv->query("INSERT INTO test(id) values('ook')"); + } + + public function testPrepareAValidQuery() { + $s = $this->drv->prepare("SELECT ?, ?", "int", "int"); + $this->assertInstanceOf(Db\Statement::class, $s); + } + + public function testPrepareAnInvalidQuery() { + $this->assertException("engineErrorGeneral", "Db"); + $s = $this->drv->prepare("This is an invalid query", "int", "int"); + } + + public function testCreateASavepoint() { + $this->assertEquals(1, $this->drv->savepointCreate()); + $this->assertEquals(2, $this->drv->savepointCreate()); + $this->assertEquals(3, $this->drv->savepointCreate()); + } + + public function testReleaseASavepoint() { + $this->assertEquals(1, $this->drv->savepointCreate()); + $this->assertEquals(true, $this->drv->savepointRelease()); + $this->assertException("savepointInvalid", "Db"); + $this->drv->savepointRelease(); + } + + public function testUndoASavepoint() { + $this->assertEquals(1, $this->drv->savepointCreate()); + $this->assertEquals(true, $this->drv->savepointUndo()); + $this->assertException("savepointInvalid", "Db"); + $this->drv->savepointUndo(); + } + + public function testManipulateSavepoints() { + $this->assertEquals(1, $this->drv->savepointCreate()); + $this->assertEquals(2, $this->drv->savepointCreate()); + $this->assertEquals(3, $this->drv->savepointCreate()); + $this->assertEquals(4, $this->drv->savepointCreate()); + $this->assertEquals(5, $this->drv->savepointCreate()); + $this->assertTrue($this->drv->savepointUndo(3)); + $this->assertFalse($this->drv->savepointRelease(4)); + $this->assertEquals(6, $this->drv->savepointCreate()); + $this->assertFalse($this->drv->savepointRelease(5)); + $this->assertTrue($this->drv->savepointRelease(6)); + $this->assertEquals(3, $this->drv->savepointCreate()); + $this->assertTrue($this->drv->savepointRelease(2)); + $this->assertException("savepointStale", "Db"); + $this->drv->savepointRelease(2); + } + + public function testManipulateSavepointsSomeMore() { + $this->assertEquals(1, $this->drv->savepointCreate()); + $this->assertEquals(2, $this->drv->savepointCreate()); + $this->assertEquals(3, $this->drv->savepointCreate()); + $this->assertEquals(4, $this->drv->savepointCreate()); + $this->assertTrue($this->drv->savepointRelease(2)); + $this->assertFalse($this->drv->savepointUndo(3)); + $this->assertException("savepointStale", "Db"); + $this->drv->savepointUndo(2); + } + + public function testBeginATransaction() { + $select = "SELECT count(*) FROM test"; + $insert = "INSERT INTO test(id) values(null)"; + $this->drv->exec("CREATE TABLE test(id integer primary key)"); + $tr = $this->drv->begin(); + $this->drv->query($insert); + $this->assertEquals(1, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + $this->drv->query($insert); + $this->assertEquals(2, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + } + + public function testCommitATransaction() { + $select = "SELECT count(*) FROM test"; + $insert = "INSERT INTO test(id) values(null)"; + $this->drv->exec("CREATE TABLE test(id integer primary key)"); + $tr = $this->drv->begin(); + $this->drv->query($insert); + $this->assertEquals(1, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + $tr->commit(); + $this->assertEquals(1, $this->drv->query($select)->getValue()); + $this->assertEquals(1, $this->ch->query($select)->fetchColumn()); + } + + public function testRollbackATransaction() { + $select = "SELECT count(*) FROM test"; + $insert = "INSERT INTO test(id) values(null)"; + $this->drv->exec("CREATE TABLE test(id integer primary key)"); + $tr = $this->drv->begin(); + $this->drv->query($insert); + $this->assertEquals(1, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + $tr->rollback(); + $this->assertEquals(0, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + } + + public function testBeginChainedTransactions() { + $select = "SELECT count(*) FROM test"; + $insert = "INSERT INTO test(id) values(null)"; + $this->drv->exec("CREATE TABLE test(id integer primary key)"); + $tr1 = $this->drv->begin(); + $this->drv->query($insert); + $this->assertEquals(1, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + $tr2 = $this->drv->begin(); + $this->drv->query($insert); + $this->assertEquals(2, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + } + + public function testCommitChainedTransactions() { + $select = "SELECT count(*) FROM test"; + $insert = "INSERT INTO test(id) values(null)"; + $this->drv->exec("CREATE TABLE test(id integer primary key)"); + $tr1 = $this->drv->begin(); + $this->drv->query($insert); + $this->assertEquals(1, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + $tr2 = $this->drv->begin(); + $this->drv->query($insert); + $this->assertEquals(2, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + $tr2->commit(); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + $tr1->commit(); + $this->assertEquals(2, $this->ch->query($select)->fetchColumn()); + } + + public function testCommitChainedTransactionsOutOfOrder() { + $select = "SELECT count(*) FROM test"; + $insert = "INSERT INTO test(id) values(null)"; + $this->drv->exec("CREATE TABLE test(id integer primary key)"); + $tr1 = $this->drv->begin(); + $this->drv->query($insert); + $this->assertEquals(1, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + $tr2 = $this->drv->begin(); + $this->drv->query($insert); + $this->assertEquals(2, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + $tr1->commit(); + $this->assertEquals(2, $this->ch->query($select)->fetchColumn()); + $tr2->commit(); + } + + public function testRollbackChainedTransactions() { + $select = "SELECT count(*) FROM test"; + $insert = "INSERT INTO test(id) values(null)"; + $this->drv->exec("CREATE TABLE test(id integer primary key)"); + $tr1 = $this->drv->begin(); + $this->drv->query($insert); + $this->assertEquals(1, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + $tr2 = $this->drv->begin(); + $this->drv->query($insert); + $this->assertEquals(2, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + $tr2->rollback(); + $this->assertEquals(1, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + $tr1->rollback(); + $this->assertEquals(0, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + } + + public function testRollbackChainedTransactionsOutOfOrder() { + $select = "SELECT count(*) FROM test"; + $insert = "INSERT INTO test(id) values(null)"; + $this->drv->exec("CREATE TABLE test(id integer primary key)"); + $tr1 = $this->drv->begin(); + $this->drv->query($insert); + $this->assertEquals(1, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + $tr2 = $this->drv->begin(); + $this->drv->query($insert); + $this->assertEquals(2, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + $tr1->rollback(); + $this->assertEquals(0, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + $tr2->rollback(); + $this->assertEquals(0, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + } + + public function testPartiallyRollbackChainedTransactions() { + $select = "SELECT count(*) FROM test"; + $insert = "INSERT INTO test(id) values(null)"; + $this->drv->exec("CREATE TABLE test(id integer primary key)"); + $tr1 = $this->drv->begin(); + $this->drv->query($insert); + $this->assertEquals(1, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + $tr2 = $this->drv->begin(); + $this->drv->query($insert); + $this->assertEquals(2, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + $tr2->rollback(); + $this->assertEquals(1, $this->drv->query($select)->getValue()); + $this->assertEquals(0, $this->ch->query($select)->fetchColumn()); + $tr1->commit(); + $this->assertEquals(1, $this->drv->query($select)->getValue()); + $this->assertEquals(1, $this->ch->query($select)->fetchColumn()); + } + + public function testFetchSchemaVersion() { + $this->assertSame(0, $this->drv->schemaVersion()); + $this->drv->exec("PRAGMA user_version=1"); + $this->assertSame(1, $this->drv->schemaVersion()); + $this->drv->exec("PRAGMA user_version=2"); + $this->assertSame(2, $this->drv->schemaVersion()); + } + + public function testLockTheDatabase() { + $this->drv->savepointCreate(true); + $this->ch->exec("PRAGMA busy_timeout = 0"); + $this->assertException(); + $this->ch->exec("CREATE TABLE test(id integer primary key)"); + } + + public function testUnlockTheDatabase() { + $this->drv->savepointCreate(true); + $this->drv->savepointRelease(); + $this->drv->savepointCreate(true); + $this->drv->savepointUndo(); + $this->assertSame(0, $this->ch->exec("CREATE TABLE test(id integer primary key)")); + } +} diff --git a/tests/cases/Db/SQLite3PDO/TestDbResultSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/TestDbResultSQLite3PDO.php new file mode 100644 index 0000000..fbc745f --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/TestDbResultSQLite3PDO.php @@ -0,0 +1,104 @@ + */ +class TestDbResultSQLite3PDO extends Test\AbstractTest { + protected $c; + + public function setUp() { + $this->clearData(); + if (!Db\SQLite3\PDODriver::requirementsMet()) { + $this->markTestSkipped("PDO-SQLite extension not loaded"); + } + $c = new \PDO("sqlite::memory:", "", "", [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]); + $this->c = $c; + } + + public function tearDown() { + unset($this->c); + $this->clearData(); + } + + public function testConstructResult() { + $set = $this->c->query("SELECT 1"); + $this->assertInstanceOf(Db\Result::class, new Db\PDOResult($set)); + } + + public function testGetChangeCountAndLastInsertId() { + $this->c->query("CREATE TABLE test(col)"); + $set = $this->c->query("INSERT INTO test(col) values(1)"); + $rows = $set->rowCount(); + $id = $this->c->lastInsertID(); + $r = new Db\PDOResult($set, [$rows,$id]); + $this->assertSame((int) $rows, $r->changes()); + $this->assertSame((int) $id, $r->lastId()); + } + + public function testIterateOverResults() { + $set = $this->c->query("SELECT 1 as col union select 2 as col union select 3 as col"); + $rows = []; + foreach (new Db\PDOResult($set) as $index => $row) { + $rows[$index] = $row['col']; + } + $this->assertSame([0 => "1", 1 => "2", 2 => "3"], $rows); + } + + public function testIterateOverResultsTwice() { + $set = $this->c->query("SELECT 1 as col union select 2 as col union select 3 as col"); + $rows = []; + $test = new Db\PDOResult($set); + foreach ($test as $row) { + $rows[] = $row['col']; + } + $this->assertSame(["1","2","3"], $rows); + $this->assertException("resultReused", "Db"); + foreach ($test as $row) { + $rows[] = $row['col']; + } + } + + public function testGetSingleValues() { + $set = $this->c->query("SELECT 1867 as year union select 1970 as year union select 2112 as year"); + $test = new Db\PDOResult($set); + $this->assertEquals(1867, $test->getValue()); + $this->assertEquals(1970, $test->getValue()); + $this->assertEquals(2112, $test->getValue()); + $this->assertSame(null, $test->getValue()); + } + + public function testGetFirstValuesOnly() { + $set = $this->c->query("SELECT 1867 as year, 19 as century union select 1970 as year, 20 as century union select 2112 as year, 22 as century"); + $test = new Db\PDOResult($set); + $this->assertEquals(1867, $test->getValue()); + $this->assertEquals(1970, $test->getValue()); + $this->assertEquals(2112, $test->getValue()); + $this->assertSame(null, $test->getValue()); + } + + public function testGetRows() { + $set = $this->c->query("SELECT '2112' as album, '2112' as track union select 'Clockwork Angels' as album, 'The Wreckers' as track"); + $rows = [ + ['album' => '2112', 'track' => '2112'], + ['album' => 'Clockwork Angels', 'track' => 'The Wreckers'], + ]; + $test = new Db\PDOResult($set); + $this->assertEquals($rows[0], $test->getRow()); + $this->assertEquals($rows[1], $test->getRow()); + $this->assertSame(null, $test->getRow()); + } + + public function testGetAllRows() { + $set = $this->c->query("SELECT '2112' as album, '2112' as track union select 'Clockwork Angels' as album, 'The Wreckers' as track"); + $rows = [ + ['album' => '2112', 'track' => '2112'], + ['album' => 'Clockwork Angels', 'track' => 'The Wreckers'], + ]; + $test = new Db\PDOResult($set); + $this->assertEquals($rows, $test->getAll()); + } +} diff --git a/tests/cases/Db/SQLite3PDO/TestDbStatementSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/TestDbStatementSQLite3PDO.php new file mode 100644 index 0000000..76ca24a --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/TestDbStatementSQLite3PDO.php @@ -0,0 +1,105 @@ + + * @covers \JKingWeb\Arsse\Db\PDOError */ +class TestDbStatementSQLite3PDO extends Test\AbstractTest { + + protected $c; + protected static $imp = Db\PDOStatement::class; + + public function setUp() { + $this->clearData(); + if (!Db\SQLite3\PDODriver::requirementsMet()) { + $this->markTestSkipped("PDO-SQLite extension not loaded"); + } + $c = new \PDO("sqlite::memory:", "", "", [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]); + $this->c = $c; + } + + public function tearDown() { + unset($this->c); + $this->clearData(); + } + + protected function checkBinding($input, array $expectations, bool $strict = false) { + $nativeStatement = $this->c->prepare("SELECT ? as value"); + $s = new self::$imp($this->c, $nativeStatement); + $types = array_unique(Statement::TYPES); + foreach ($types as $type) { + $s->rebindArray([$strict ? "strict $type" : $type]); + $val = $s->runArray([$input])->getRow()['value']; + $this->assertSame($expectations[$type], $val, "Binding from type $type failed comparison."); + $s->rebind(...[$strict ? "strict $type" : $type]); + $val = $s->run(...[$input])->getRow()['value']; + $this->assertSame($expectations[$type], $val, "Binding from type $type failed comparison."); + } + } + + public function testConstructStatement() { + $nativeStatement = $this->c->prepare("SELECT ? as value"); + $this->assertInstanceOf(Statement::class, new Db\PDOStatement($this->c, $nativeStatement)); + } + + public function testBindMissingValue() { + $nativeStatement = $this->c->prepare("SELECT ? as value"); + $s = new self::$imp($this->c, $nativeStatement); + $val = $s->runArray()->getRow()['value']; + $this->assertSame(null, $val); + } + + public function testBindMultipleValues() { + $exp = [ + 'one' => "1", + 'two' => "2", + ]; + $nativeStatement = $this->c->prepare("SELECT ? as one, ? as two"); + $s = new self::$imp($this->c, $nativeStatement, ["int", "int"]); + $val = $s->runArray([1,2])->getRow(); + $this->assertSame($exp, $val); + } + + public function testBindRecursively() { + $exp = [ + 'one' => "1", + 'two' => "2", + 'three' => "3", + 'four' => "4", + ]; + $nativeStatement = $this->c->prepare("SELECT ? as one, ? as two, ? as three, ? as four"); + $s = new self::$imp($this->c, $nativeStatement, ["int", ["int", "int"], "int"]); + $val = $s->runArray([1, [2, 3], 4])->getRow(); + $this->assertSame($exp, $val); + } + + public function testBindWithoutType() { + $nativeStatement = $this->c->prepare("SELECT ? as value"); + $this->assertException("paramTypeMissing", "Db"); + $s = new self::$imp($this->c, $nativeStatement, []); + $s->runArray([1]); + } + + public function testViolateConstraint() { + $this->c->exec("CREATE TABLE test(id integer not null)"); + $nativeStatement = $this->c->prepare("INSERT INTO test(id) values(?)"); + $s = new self::$imp($this->c, $nativeStatement, ["int"]); + $this->assertException("constraintViolation", "Db", "ExceptionInput"); + $s->runArray([null]); + } + + public function testMismatchTypes() { + $this->c->exec("CREATE TABLE test(id integer primary key)"); + $nativeStatement = $this->c->prepare("INSERT INTO test(id) values(?)"); + $s = new self::$imp($this->c, $nativeStatement, ["str"]); + $this->assertException("typeViolation", "Db", "ExceptionInput"); + $s->runArray(['ook']); + } +} diff --git a/tests/cases/Db/SQLite3PDO/TestDbUpdateSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/TestDbUpdateSQLite3PDO.php new file mode 100644 index 0000000..b75ba7d --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/TestDbUpdateSQLite3PDO.php @@ -0,0 +1,120 @@ + + * @covers \JKingWeb\Arsse\Db\PDOError */ +class TestDbUpdateSQLite3PDO extends Test\AbstractTest { + protected $data; + protected $drv; + protected $vfs; + protected $base; + + const MINIMAL1 = "create table arsse_meta(key text primary key not null, value text); pragma user_version=1"; + const MINIMAL2 = "pragma user_version=2"; + + public function setUp(Conf $conf = null) { + if (!Db\SQLite3\PDODriver::requirementsMet()) { + $this->markTestSkipped("PDO-SQLite extension not loaded"); + } + $this->clearData(); + $this->vfs = vfsStream::setup("schemata", null, ['SQLite3' => []]); + if (!$conf) { + $conf = new Conf(); + } + $conf->dbDriver = Db\SQLite3\PDODriver::class; + $conf->dbSQLite3File = ":memory:"; + Arsse::$conf = $conf; + $this->base = $this->vfs->url(); + $this->path = $this->base."/SQLite3/"; + $this->drv = new Db\SQLite3\PDODriver(); + } + + public function tearDown() { + unset($this->drv); + unset($this->data); + unset($this->vfs); + $this->clearData(); + } + + public function testLoadMissingFile() { + $this->assertException("updateFileMissing", "Db"); + $this->drv->schemaUpdate(1, $this->base); + } + + public function testLoadUnreadableFile() { + touch($this->path."0.sql"); + chmod($this->path."0.sql", 0000); + $this->assertException("updateFileUnreadable", "Db"); + $this->drv->schemaUpdate(1, $this->base); + } + + public function testLoadCorruptFile() { + file_put_contents($this->path."0.sql", "This is a corrupt file"); + $this->assertException("updateFileError", "Db"); + $this->drv->schemaUpdate(1, $this->base); + } + + public function testLoadIncompleteFile() { + file_put_contents($this->path."0.sql", "create table arsse_meta(key text primary key not null, value text);"); + $this->assertException("updateFileIncomplete", "Db"); + $this->drv->schemaUpdate(1, $this->base); + } + + public function testLoadEmptyFile() { + file_put_contents($this->path."0.sql", ""); + $this->assertException("updateFileIncomplete", "Db"); + $this->drv->schemaUpdate(1, $this->base); + } + + public function testLoadCorrectFile() { + file_put_contents($this->path."0.sql", self::MINIMAL1); + $this->drv->schemaUpdate(1, $this->base); + $this->assertEquals(1, $this->drv->schemaVersion()); + } + + public function testPerformPartialUpdate() { + file_put_contents($this->path."0.sql", self::MINIMAL1); + file_put_contents($this->path."1.sql", " "); + $this->assertException("updateFileIncomplete", "Db"); + try { + $this->drv->schemaUpdate(2, $this->base); + } catch (Exception $e) { + $this->assertEquals(1, $this->drv->schemaVersion()); + throw $e; + } + } + + public function testPerformSequentialUpdate() { + file_put_contents($this->path."0.sql", self::MINIMAL1); + file_put_contents($this->path."1.sql", self::MINIMAL2); + $this->drv->schemaUpdate(2, $this->base); + $this->assertEquals(2, $this->drv->schemaVersion()); + } + + public function testPerformActualUpdate() { + $this->drv->schemaUpdate(Database::SCHEMA_VERSION); + $this->assertEquals(Database::SCHEMA_VERSION, $this->drv->schemaVersion()); + } + + public function testDeclineManualUpdate() { + // turn auto-updating off + $conf = new Conf(); + $conf->dbAutoUpdate = false; + $this->setUp($conf); + $this->assertException("updateManual", "Db"); + $this->drv->schemaUpdate(Database::SCHEMA_VERSION); + } + + public function testDeclineDowngrade() { + $this->assertException("updateTooNew", "Db"); + $this->drv->schemaUpdate(-1, $this->base); + } +} diff --git a/tests/phpunit.xml b/tests/phpunit.xml index 76d41e9..c15db12 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -51,8 +51,14 @@ cases/Db/SQLite3/TestDbDriverCreationSQLite3.php cases/Db/SQLite3/TestDbDriverSQLite3.php cases/Db/SQLite3/TestDbUpdateSQLite3.php + + cases/Db/SQLite3PDO/TestDbResultSQLite3PDO.php + cases/Db/SQLite3PDO/TestDbStatementSQLite3PDO.php + cases/Db/SQLite3PDO/TestDbDriverCreationSQLite3PDO.php + cases/Db/SQLite3PDO/TestDbDriverSQLite3PDO.php + cases/Db/SQLite3PDO/TestDbUpdateSQLite3PDO.php - + cases/Db/SQLite3/Database/TestDatabaseMiscellanySQLite3.php cases/Db/SQLite3/Database/TestDatabaseMetaSQLite3.php cases/Db/SQLite3/Database/TestDatabaseUserSQLite3.php From 4b46d654bb5df2fa71b5917c666cb3ca3a559437 Mon Sep 17 00:00:00 2001 From: "J. King" Date: Tue, 19 Dec 2017 17:19:05 -0500 Subject: [PATCH 04/16] Replace SQLite `is` with SQL = for consistency --- sql/SQLite3/1.sql | 2 +- sql/SQLite3/2.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/SQLite3/1.sql b/sql/SQLite3/1.sql index fe36942..8f273e6 100644 --- a/sql/SQLite3/1.sql +++ b/sql/SQLite3/1.sql @@ -45,4 +45,4 @@ drop table arsse_marks_old; -- set version marker pragma user_version = 2; -update arsse_meta set value = '2' where key is 'schema_version'; \ No newline at end of file +update arsse_meta set value = '2' where key = 'schema_version'; \ No newline at end of file diff --git a/sql/SQLite3/2.sql b/sql/SQLite3/2.sql index 12a269a..87f21ef 100644 --- a/sql/SQLite3/2.sql +++ b/sql/SQLite3/2.sql @@ -108,4 +108,4 @@ drop table arsse_labels_old; -- set version marker pragma user_version = 3; -update arsse_meta set value = '3' where key is 'schema_version'; \ No newline at end of file +update arsse_meta set value = '3' where key = 'schema_version'; \ No newline at end of file From 4bada691e957342342c7217cac4840789c19d402 Mon Sep 17 00:00:00 2001 From: "J. King" Date: Tue, 19 Dec 2017 19:08:08 -0500 Subject: [PATCH 05/16] PDO tests and fixes; improves #72 --- RoboFile.php | 2 +- lib/Database.php | 8 +++---- .../TestDatabaseArticleSQLite3PDO.php | 16 +++++++++++++ .../TestDatabaseCleanupSQLite3PDO.php | 16 +++++++++++++ .../Database/TestDatabaseFeedSQLite3PDO.php | 16 +++++++++++++ .../Database/TestDatabaseFolderSQLite3PDO.php | 16 +++++++++++++ .../Database/TestDatabaseLabelSQLite3PDO.php | 12 ++++++++++ .../Database/TestDatabaseMetaSQLite3PDO.php | 16 +++++++++++++ .../TestDatabaseMiscellanySQLite3PDO.php | 16 +++++++++++++ .../TestDatabaseSessionSQLite3PDO.php | 12 ++++++++++ .../TestDatabaseSubscriptionSQLite3PDO.php | 16 +++++++++++++ .../Database/TestDatabaseUserSQLite3PDO.php | 16 +++++++++++++ tests/lib/AbstractTest.php | 2 ++ tests/lib/Database/DriverSQLite3PDO.php | 24 +++++++++++++++++++ tests/lib/Database/SeriesArticle.php | 4 ++-- tests/lib/Database/SeriesFeed.php | 4 ++-- tests/lib/Database/Setup.php | 16 +++++++++++-- tests/phpunit.xml | 12 +++++++++- 18 files changed, 212 insertions(+), 12 deletions(-) create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseArticleSQLite3PDO.php create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseCleanupSQLite3PDO.php create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseFeedSQLite3PDO.php create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseFolderSQLite3PDO.php create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseLabelSQLite3PDO.php create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseMetaSQLite3PDO.php create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseMiscellanySQLite3PDO.php create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseSessionSQLite3PDO.php create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseSubscriptionSQLite3PDO.php create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseUserSQLite3PDO.php create mode 100644 tests/lib/Database/DriverSQLite3PDO.php diff --git a/RoboFile.php b/RoboFile.php index 4653e4c..02ae1c4 100644 --- a/RoboFile.php +++ b/RoboFile.php @@ -45,7 +45,7 @@ class RoboFile extends \Robo\Tasks { * See help for the "test" task for more details. */ public function testQuick(array $args): Result { - return $this->test(array_merge(["--exclude-group","slow"], $args)); + return $this->test(array_merge(["--exclude-group", "slow,optional"], $args)); } /** Produces a code coverage report diff --git a/lib/Database.php b/lib/Database.php index 0072cd8..6489e30 100644 --- a/lib/Database.php +++ b/lib/Database.php @@ -415,7 +415,7 @@ class Database { return $f; } - protected function folderValidateMove(string $user, int $id = null, $parent = null, string $name = null) { + protected function folderValidateMove(string $user, $id = null, $parent = null, string $name = null) { $errData = ["action" => $this->caller(), "field" => "parent", 'id' => $parent]; if (!$id) { // the root cannot be moved @@ -467,7 +467,7 @@ class Database { return $parent; } - protected function folderValidateName($name, bool $checkDuplicates = false, int $parent = null): bool { + protected function folderValidateName($name, bool $checkDuplicates = false, $parent = null): bool { $info = ValueInfo::str($name); if ($info & (ValueInfo::NULL | ValueInfo::EMPTY)) { throw new Db\ExceptionInput("missing", ["action" => $this->caller(), "field" => "name"]); @@ -572,7 +572,7 @@ class Database { // add a suitable WHERE condition $q->setWhere("folder in (select folder from folders)"); } - return $this->db->prepare($q->getQuery(), $q->getTypes())->run($q->getValues())->getValue(); + return (int) $this->db->prepare($q->getQuery(), $q->getTypes())->run($q->getValues())->getValue(); } public function subscriptionRemove(string $user, $id): bool { @@ -1102,7 +1102,7 @@ class Database { $q = $this->articleQuery($user, $context); $q->pushCTE("selected_articles"); $q->setBody("SELECT count(*) from selected_articles"); - return $this->db->prepare($q->getQuery(), $q->getTypes())->run($q->getValues())->getValue(); + return (int) $this->db->prepare($q->getQuery(), $q->getTypes())->run($q->getValues())->getValue(); } } diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseArticleSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseArticleSQLite3PDO.php new file mode 100644 index 0000000..a904781 --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseArticleSQLite3PDO.php @@ -0,0 +1,16 @@ + + * @group optional */ +class TestDatabaseArticleSQLite3PDO extends Test\AbstractTest { + use Test\Database\Setup; + use Test\Database\DriverSQLite3PDO; + use Test\Database\SeriesArticle; +} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseCleanupSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseCleanupSQLite3PDO.php new file mode 100644 index 0000000..8eb5ef7 --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseCleanupSQLite3PDO.php @@ -0,0 +1,16 @@ + + * @group optional */ +class TestDatabaseCleanupSQLite3PDO extends Test\AbstractTest { + use Test\Database\Setup; + use Test\Database\DriverSQLite3PDO; + use Test\Database\SeriesCleanup; +} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseFeedSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseFeedSQLite3PDO.php new file mode 100644 index 0000000..07ed2ab --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseFeedSQLite3PDO.php @@ -0,0 +1,16 @@ + + * @group optional */ +class TestDatabaseFeedSQLite3PDO extends Test\AbstractTest { + use Test\Database\Setup; + use Test\Database\DriverSQLite3PDO; + use Test\Database\SeriesFeed; +} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseFolderSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseFolderSQLite3PDO.php new file mode 100644 index 0000000..d348de9 --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseFolderSQLite3PDO.php @@ -0,0 +1,16 @@ + + * @group optional */ +class TestDatabaseFolderSQLite3PDO extends Test\AbstractTest { + use Test\Database\Setup; + use Test\Database\DriverSQLite3PDO; + use Test\Database\SeriesFolder; +} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseLabelSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseLabelSQLite3PDO.php new file mode 100644 index 0000000..3d367af --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseLabelSQLite3PDO.php @@ -0,0 +1,12 @@ + + * @group optional */ +class TestDatabaseLabelSQLite3PDO extends Test\AbstractTest { + use Test\Database\Setup; + use Test\Database\DriverSQLite3PDO; + use Test\Database\SeriesLabel; +} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseMetaSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseMetaSQLite3PDO.php new file mode 100644 index 0000000..8f8ad5e --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseMetaSQLite3PDO.php @@ -0,0 +1,16 @@ + + * @group optional */ +class TestDatabaseMetaSQLite3PDO extends Test\AbstractTest { + use Test\Database\Setup; + use Test\Database\DriverSQLite3PDO; + use Test\Database\SeriesMeta; +} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseMiscellanySQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseMiscellanySQLite3PDO.php new file mode 100644 index 0000000..2e1d01e --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseMiscellanySQLite3PDO.php @@ -0,0 +1,16 @@ + + * @group optional */ +class TestDatabaseMiscellanySQLite3PDO extends Test\AbstractTest { + use Test\Database\Setup; + use Test\Database\DriverSQLite3PDO; + use Test\Database\SeriesMiscellany; +} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseSessionSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseSessionSQLite3PDO.php new file mode 100644 index 0000000..bd0a857 --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseSessionSQLite3PDO.php @@ -0,0 +1,12 @@ + + * @group optional */ +class TestDatabaseSessionSQLite3PDO extends Test\AbstractTest { + use Test\Database\Setup; + use Test\Database\DriverSQLite3PDO; + use Test\Database\SeriesSession; +} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseSubscriptionSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseSubscriptionSQLite3PDO.php new file mode 100644 index 0000000..99ec86c --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseSubscriptionSQLite3PDO.php @@ -0,0 +1,16 @@ + + * @group optional */ +class TestDatabaseSubscriptionSQLite3PDO extends Test\AbstractTest { + use Test\Database\Setup; + use Test\Database\DriverSQLite3PDO; + use Test\Database\SeriesSubscription; +} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseUserSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseUserSQLite3PDO.php new file mode 100644 index 0000000..ef5ec44 --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseUserSQLite3PDO.php @@ -0,0 +1,16 @@ + + * @group optional */ +class TestDatabaseUserSQLite3PDO extends Test\AbstractTest { + use Test\Database\Setup; + use Test\Database\DriverSQLite3PDO; + use Test\Database\SeriesUser; +} diff --git a/tests/lib/AbstractTest.php b/tests/lib/AbstractTest.php index 85bb0eb..dd63b4d 100644 --- a/tests/lib/AbstractTest.php +++ b/tests/lib/AbstractTest.php @@ -32,6 +32,8 @@ abstract class AbstractTest extends \PHPUnit\Framework\TestCase { public function approximateTime($exp, $act) { if (is_null($act)) { return null; + } elseif (is_null($exp)) { + return $act; } $target = Date::normalize($exp)->getTimeStamp(); $value = Date::normalize($act)->getTimeStamp(); diff --git a/tests/lib/Database/DriverSQLite3PDO.php b/tests/lib/Database/DriverSQLite3PDO.php new file mode 100644 index 0000000..9c52bd8 --- /dev/null +++ b/tests/lib/Database/DriverSQLite3PDO.php @@ -0,0 +1,24 @@ +markTestSkipped("PDO-SQLite extension not loaded"); + } + Arsse::$conf->dbSQLite3File = ":memory:"; + $this->drv = new PDODriver(); + } + + public function nextID(string $table): int { + return (int) $this->drv->query("SELECT (case when max(id) then max(id) else 0 end)+1 from $table")->getValue(); + } +} diff --git a/tests/lib/Database/SeriesArticle.php b/tests/lib/Database/SeriesArticle.php index 8226711..7ffae2d 100644 --- a/tests/lib/Database/SeriesArticle.php +++ b/tests/lib/Database/SeriesArticle.php @@ -888,8 +888,8 @@ trait SeriesArticle { public function testFetchStarredCounts() { $exp1 = ['total' => 2, 'unread' => 1, 'read' => 1]; $exp2 = ['total' => 0, 'unread' => 0, 'read' => 0]; - $this->assertSame($exp1, Arsse::$db->articleStarred("john.doe@example.com")); - $this->assertSame($exp2, Arsse::$db->articleStarred("jane.doe@example.com")); + $this->assertEquals($exp1, Arsse::$db->articleStarred("john.doe@example.com")); + $this->assertEquals($exp2, Arsse::$db->articleStarred("jane.doe@example.com")); } public function testFetchStarredCountsWithoutAuthority() { diff --git a/tests/lib/Database/SeriesFeed.php b/tests/lib/Database/SeriesFeed.php index e605bd4..fcfaf6b 100644 --- a/tests/lib/Database/SeriesFeed.php +++ b/tests/lib/Database/SeriesFeed.php @@ -256,9 +256,9 @@ trait SeriesFeed { } public function testListStaleFeeds() { - $this->assertSame([1,3,4], Arsse::$db->feedListStale()); + $this->assertEquals([1,3,4], Arsse::$db->feedListStale()); Arsse::$db->feedUpdate(3); Arsse::$db->feedUpdate(4); - $this->assertSame([1], Arsse::$db->feedListStale()); + $this->assertEquals([1], Arsse::$db->feedListStale()); } } diff --git a/tests/lib/Database/Setup.php b/tests/lib/Database/Setup.php index 0adb9d3..4142818 100644 --- a/tests/lib/Database/Setup.php +++ b/tests/lib/Database/Setup.php @@ -10,6 +10,7 @@ use JKingWeb\Arsse\User\Driver as UserDriver; use JKingWeb\Arsse\Arsse; use JKingWeb\Arsse\Conf; use JKingWeb\Arsse\User; +use JKingWeb\Arsse\Misc\ValueInfo; use JKingWeb\Arsse\Test\Database; use JKingWeb\Arsse\Db\Result; use Phake; @@ -90,8 +91,19 @@ trait Setup { $row = array_combine($cols, $row); foreach($data as $index => $test) { foreach ($test as $col => $value) { - if ($types[$col]=="datetime") { - $test[$col] = $this->approximateTime($row[$col], $value); + switch ($types[$col]) { + case "datetime": + $test[$col] = $this->approximateTime($row[$col], $value); + break; + case "int": + $test[$col] = ValueInfo::normalize($value, ValueInfo::T_INT | ValueInfo::M_DROP | valueInfo::M_NULL); + break; + case "float": + $test[$col] = ValueInfo::normalize($value, ValueInfo::T_FLOAT | ValueInfo::M_DROP | valueInfo::M_NULL); + break; + case "bool": + $test[$col] = (int) ValueInfo::normalize($value, ValueInfo::T_BOOL | ValueInfo::M_DROP | valueInfo::M_NULL); + break; } } if($row===$test) { diff --git a/tests/phpunit.xml b/tests/phpunit.xml index c15db12..37e5bbe 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -58,7 +58,7 @@ cases/Db/SQLite3PDO/TestDbDriverSQLite3PDO.php cases/Db/SQLite3PDO/TestDbUpdateSQLite3PDO.php - + cases/Db/SQLite3/Database/TestDatabaseMiscellanySQLite3.php cases/Db/SQLite3/Database/TestDatabaseMetaSQLite3.php cases/Db/SQLite3/Database/TestDatabaseUserSQLite3.php @@ -69,6 +69,16 @@ cases/Db/SQLite3/Database/TestDatabaseArticleSQLite3.php cases/Db/SQLite3/Database/TestDatabaseLabelSQLite3.php cases/Db/SQLite3/Database/TestDatabaseCleanupSQLite3.php + + cases/Db/SQLite3PDO/Database/TestDatabaseMiscellanySQLite3PDO.php + cases/Db/SQLite3PDO/Database/TestDatabaseUserSQLite3PDO.php + cases/Db/SQLite3PDO/Database/TestDatabaseSessionSQLite3PDO.php + cases/Db/SQLite3PDO/Database/TestDatabaseFolderSQLite3PDO.php + cases/Db/SQLite3PDO/Database/TestDatabaseFeedSQLite3PDO.php + cases/Db/SQLite3PDO/Database/TestDatabaseSubscriptionSQLite3PDO.php + cases/Db/SQLite3PDO/Database/TestDatabaseArticleSQLite3PDO.php + cases/Db/SQLite3PDO/Database/TestDatabaseLabelSQLite3PDO.php + cases/Db/SQLite3PDO/Database/TestDatabaseCleanupSQLite3PDO.php From 095fe10aecd145a450816fac8f84dc0c63ec107c Mon Sep 17 00:00:00 2001 From: "J. King" Date: Fri, 22 Dec 2017 11:41:54 -0500 Subject: [PATCH 06/16] Reorganize PDO tests into namespaces --- .php_cs.dist | 1 + RoboFile.php | 59 ++++++++------ robo | 6 +- robo.bat | 6 +- tests/cases/Conf/TestConf.php | 5 +- .../cases/Db/SQLite3/Database/TestArticle.php | 14 ++++ .../cases/Db/SQLite3/Database/TestCleanup.php | 14 ++++ .../Database/TestDatabaseArticleSQLite3.php | 14 ---- .../Database/TestDatabaseCleanupSQLite3.php | 14 ---- .../Database/TestDatabaseFeedSQLite3.php | 14 ---- .../Database/TestDatabaseFolderSQLite3.php | 14 ---- .../Database/TestDatabaseLabelSQLite3.php | 10 --- .../Database/TestDatabaseMetaSQLite3.php | 14 ---- .../TestDatabaseMiscellanySQLite3.php | 14 ---- .../Database/TestDatabaseSessionSQLite3.php | 10 --- .../TestDatabaseSubscriptionSQLite3.php | 14 ---- .../Database/TestDatabaseUserSQLite3.php | 14 ---- tests/cases/Db/SQLite3/Database/TestFeed.php | 14 ++++ .../cases/Db/SQLite3/Database/TestFolder.php | 14 ++++ tests/cases/Db/SQLite3/Database/TestLabel.php | 10 +++ tests/cases/Db/SQLite3/Database/TestMeta.php | 14 ++++ .../Db/SQLite3/Database/TestMiscellany.php | 14 ++++ .../cases/Db/SQLite3/Database/TestSession.php | 10 +++ .../Db/SQLite3/Database/TestSubscription.php | 14 ++++ tests/cases/Db/SQLite3/Database/TestUser.php | 14 ++++ ...erCreationSQLite3.php => TestCreation.php} | 5 +- ...TestDbDriverSQLite3.php => TestDriver.php} | 19 +++-- ...TestDbResultSQLite3.php => TestResult.php} | 22 +++--- ...StatementSQLite3.php => TestStatement.php} | 10 +-- ...TestDbUpdateSQLite3.php => TestUpdate.php} | 13 ++- tests/cases/Db/TestResultAggregate.php | 19 ++--- tests/cases/Db/TestResultEmpty.php | 16 ++-- tests/cases/Db/TestTransaction.php | 9 ++- tests/cases/Exception/TestException.php | 14 ++-- tests/cases/Feed/TestFeed.php | 15 ++-- ...{TestFeedFetching.php => TestFetching.php} | 11 ++- .../Lang/{TestLang.php => TestBasic.php} | 14 ++-- .../{TestLangErrors.php => TestErrors.php} | 9 ++- .../{testLangComplex.php => testComplex.php} | 11 +-- tests/cases/Misc/TestContext.php | 4 +- tests/cases/Misc/TestValueInfo.php | 12 +-- .../{TestNCNV1_2.php => TestV1_2.php} | 18 +++-- ...NVersionDiscovery.php => TestVersions.php} | 13 +-- .../{TestTinyTinyAPI.php => TestAPI.php} | 11 ++- .../{TestTinyTinyIcon.php => TestIcon.php} | 11 ++- tests/cases/Service/TestService.php | 8 +- tests/cases/User/TestAuthorization.php | 79 ++++++++++--------- ...rMockExternal.php => TestMockExternal.php} | 8 +- ...rMockInternal.php => TestMockInternal.php} | 10 ++- ...UserInternalDriver.php => Testnternal.php} | 8 +- tests/lib/Database/Setup.php | 4 +- tests/phpunit.xml | 52 ++++++------ 52 files changed, 425 insertions(+), 341 deletions(-) create mode 100644 tests/cases/Db/SQLite3/Database/TestArticle.php create mode 100644 tests/cases/Db/SQLite3/Database/TestCleanup.php delete mode 100644 tests/cases/Db/SQLite3/Database/TestDatabaseArticleSQLite3.php delete mode 100644 tests/cases/Db/SQLite3/Database/TestDatabaseCleanupSQLite3.php delete mode 100644 tests/cases/Db/SQLite3/Database/TestDatabaseFeedSQLite3.php delete mode 100644 tests/cases/Db/SQLite3/Database/TestDatabaseFolderSQLite3.php delete mode 100644 tests/cases/Db/SQLite3/Database/TestDatabaseLabelSQLite3.php delete mode 100644 tests/cases/Db/SQLite3/Database/TestDatabaseMetaSQLite3.php delete mode 100644 tests/cases/Db/SQLite3/Database/TestDatabaseMiscellanySQLite3.php delete mode 100644 tests/cases/Db/SQLite3/Database/TestDatabaseSessionSQLite3.php delete mode 100644 tests/cases/Db/SQLite3/Database/TestDatabaseSubscriptionSQLite3.php delete mode 100644 tests/cases/Db/SQLite3/Database/TestDatabaseUserSQLite3.php create mode 100644 tests/cases/Db/SQLite3/Database/TestFeed.php create mode 100644 tests/cases/Db/SQLite3/Database/TestFolder.php create mode 100644 tests/cases/Db/SQLite3/Database/TestLabel.php create mode 100644 tests/cases/Db/SQLite3/Database/TestMeta.php create mode 100644 tests/cases/Db/SQLite3/Database/TestMiscellany.php create mode 100644 tests/cases/Db/SQLite3/Database/TestSession.php create mode 100644 tests/cases/Db/SQLite3/Database/TestSubscription.php create mode 100644 tests/cases/Db/SQLite3/Database/TestUser.php rename tests/cases/Db/SQLite3/{TestDbDriverCreationSQLite3.php => TestCreation.php} (97%) rename tests/cases/Db/SQLite3/{TestDbDriverSQLite3.php => TestDriver.php} (96%) rename tests/cases/Db/SQLite3/{TestDbResultSQLite3.php => TestResult.php} (87%) rename tests/cases/Db/SQLite3/{TestDbStatementSQLite3.php => TestStatement.php} (91%) rename tests/cases/Db/SQLite3/{TestDbUpdateSQLite3.php => TestUpdate.php} (92%) rename tests/cases/Feed/{TestFeedFetching.php => TestFetching.php} (91%) rename tests/cases/Lang/{TestLang.php => TestBasic.php} (81%) rename tests/cases/Lang/{TestLangErrors.php => TestErrors.php} (88%) rename tests/cases/Lang/{testLangComplex.php => testComplex.php} (92%) rename tests/cases/REST/NextCloudNews/{TestNCNV1_2.php => TestV1_2.php} (99%) rename tests/cases/REST/NextCloudNews/{TestNCNVersionDiscovery.php => TestVersions.php} (85%) rename tests/cases/REST/TinyTinyRSS/{TestTinyTinyAPI.php => TestAPI.php} (99%) rename tests/cases/REST/TinyTinyRSS/{TestTinyTinyIcon.php => TestIcon.php} (88%) rename tests/cases/User/{TestUserMockExternal.php => TestMockExternal.php} (54%) rename tests/cases/User/{TestUserMockInternal.php => TestMockInternal.php} (57%) rename tests/cases/User/{TestUserInternalDriver.php => Testnternal.php} (64%) diff --git a/.php_cs.dist b/.php_cs.dist index 3b91594..90db01f 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -10,6 +10,7 @@ const BASE = __DIR__.DIRECTORY_SEPARATOR; $paths = [ __FILE__, BASE."arsse.php", + BASE."RoboFile.php", BASE."lib", BASE."tests", ]; diff --git a/RoboFile.php b/RoboFile.php index 02ae1c4..d0d1d24 100644 --- a/RoboFile.php +++ b/RoboFile.php @@ -11,15 +11,15 @@ class RoboFile extends \Robo\Tasks { const BASE = __DIR__.\DIRECTORY_SEPARATOR; const BASE_TEST = self::BASE."tests".\DIRECTORY_SEPARATOR; - /** - * Runs the full test suite - * - * Arguments passed to the task are passed on to PHPUnit. Thus one may, for + /** + * Runs the full test suite + * + * Arguments passed to the task are passed on to PHPUnit. Thus one may, for * example, run the following command and get the expected results: - * - * ./robo test --testsuite TTRSS --exclude-group slow --testdox - * - * Please see the PHPUnit documentation for available options. + * + * ./robo test --testsuite TTRSS --exclude-group slow --testdox + * + * Please see the PHPUnit documentation for available options. */ public function test(array $args): Result { // start the built-in PHP server, which is required for some of the tests @@ -30,32 +30,32 @@ class RoboFile extends \Robo\Tasks { return $this->taskExec("php")->arg($execpath)->option("-c", $confpath)->args($args)->run(); } - /** - * Runs the full test suite - * - * This is an alias of the "test" task. + /** + * Runs the full test suite + * + * This is an alias of the "test" task. */ public function testFull(array $args): Result { return $this->test($args); } - /** - * Runs a quick subset of the test suite - * - * See help for the "test" task for more details. + /** + * Runs a quick subset of the test suite + * + * See help for the "test" task for more details. */ public function testQuick(array $args): Result { return $this->test(array_merge(["--exclude-group", "slow,optional"], $args)); } - /** Produces a code coverage report - * + /** Produces a code coverage report + * * By default this task produces an HTML-format coverage report in * arsse/tests/coverage/. Additional reports may be produced by passing * arguments to this task as one would to PHPUnit. - * + * * Robo first tries to use phpdbg and will fall back to Xdebug if available. - * Because Xdebug slows down non-coverage tasks, however, phpdbg is highly + * Because Xdebug slows down non-coverage tasks, however, phpdbg is highly * recommanded is debugging facilities are not otherwise needed. */ public function coverage(array $args): Result { @@ -79,14 +79,14 @@ class RoboFile extends \Robo\Tasks { } } - /** Packages a given commit of the software into a release tarball - * + /** Packages a given commit of the software into a release tarball + * * The version to package may be any Git tree-ish identifier: a tag, a branch, * or any commit hash. If none is provided on the command line, Robo will prompt * for a commit to package; the default is "head". - * + * * Note that while it is possible to re-package old versions, the resultant tarball - * may not be equivalent due to subsequent changes in the exclude list, or because + * may not be equivalent due to subsequent changes in the exclude list, or because * of new tooling. */ public function package(string $version = null): Result { @@ -128,4 +128,13 @@ class RoboFile extends \Robo\Tasks { $this->_exec("git worktree prune"); return $out; } -} \ No newline at end of file + + public function clean($opts = ['demo|d' => false]): Result { + $t = $this->taskExec(realpath(self::BASE."vendor/bin/php-cs-fixer")); + $t->arg("fix"); + if ($opts['demo']) { + $t->args("--dry-run", "--diff")->option("--diff-format", "udiff"); + } + return $t->run(); + } +} diff --git a/robo b/robo index ab928f7..7d4d4d7 100755 --- a/robo +++ b/robo @@ -3,4 +3,8 @@ base=`dirname "$0"` roboCommand="$1" shift -"$base/vendor/bin/robo" "$roboCommand" -- $* \ No newline at end of file +if [ "$1" == "clean" ]; then + "$base/vendor/bin/robo" "$roboCommand" $* +else + "$base/vendor/bin/robo" "$roboCommand" -- $* +fi \ No newline at end of file diff --git a/robo.bat b/robo.bat index f0c1de4..297f954 100644 --- a/robo.bat +++ b/robo.bat @@ -14,4 +14,8 @@ if "%~1" neq "" ( ) if defined args set args=%args:~1% -call "%base%vendor\bin\robo" "%roboCommand%" -- %args% \ No newline at end of file +if "%1"=="clean" ( + call "%base%vendor\bin\robo" "%roboCommand%" %args% +) else ( + call "%base%vendor\bin\robo" "%roboCommand%" -- %args% +) diff --git a/tests/cases/Conf/TestConf.php b/tests/cases/Conf/TestConf.php index d03e79c..edfcfbb 100644 --- a/tests/cases/Conf/TestConf.php +++ b/tests/cases/Conf/TestConf.php @@ -4,12 +4,13 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Conf; +use JKingWeb\Arsse\Conf; use org\bovigo\vfs\vfsStream; /** @covers \JKingWeb\Arsse\Conf */ -class TestConf extends Test\AbstractTest { +class TestConf extends \JKingWeb\Arsse\Test\AbstractTest { public static $vfs; public static $path; diff --git a/tests/cases/Db/SQLite3/Database/TestArticle.php b/tests/cases/Db/SQLite3/Database/TestArticle.php new file mode 100644 index 0000000..67480ad --- /dev/null +++ b/tests/cases/Db/SQLite3/Database/TestArticle.php @@ -0,0 +1,14 @@ + */ +class TestArticle extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3; + use \JKingWeb\Arsse\Test\Database\SeriesArticle; +} diff --git a/tests/cases/Db/SQLite3/Database/TestCleanup.php b/tests/cases/Db/SQLite3/Database/TestCleanup.php new file mode 100644 index 0000000..3ecba41 --- /dev/null +++ b/tests/cases/Db/SQLite3/Database/TestCleanup.php @@ -0,0 +1,14 @@ + */ +class TestCleanup extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3; + use \JKingWeb\Arsse\Test\Database\SeriesCleanup; +} diff --git a/tests/cases/Db/SQLite3/Database/TestDatabaseArticleSQLite3.php b/tests/cases/Db/SQLite3/Database/TestDatabaseArticleSQLite3.php deleted file mode 100644 index 9c013e9..0000000 --- a/tests/cases/Db/SQLite3/Database/TestDatabaseArticleSQLite3.php +++ /dev/null @@ -1,14 +0,0 @@ - */ -class TestDatabaseArticleSQLite3 extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3; - use Test\Database\SeriesArticle; -} diff --git a/tests/cases/Db/SQLite3/Database/TestDatabaseCleanupSQLite3.php b/tests/cases/Db/SQLite3/Database/TestDatabaseCleanupSQLite3.php deleted file mode 100644 index 09c9988..0000000 --- a/tests/cases/Db/SQLite3/Database/TestDatabaseCleanupSQLite3.php +++ /dev/null @@ -1,14 +0,0 @@ - */ -class TestDatabaseCleanupSQLite3 extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3; - use Test\Database\SeriesCleanup; -} diff --git a/tests/cases/Db/SQLite3/Database/TestDatabaseFeedSQLite3.php b/tests/cases/Db/SQLite3/Database/TestDatabaseFeedSQLite3.php deleted file mode 100644 index a91286a..0000000 --- a/tests/cases/Db/SQLite3/Database/TestDatabaseFeedSQLite3.php +++ /dev/null @@ -1,14 +0,0 @@ - */ -class TestDatabaseFeedSQLite3 extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3; - use Test\Database\SeriesFeed; -} diff --git a/tests/cases/Db/SQLite3/Database/TestDatabaseFolderSQLite3.php b/tests/cases/Db/SQLite3/Database/TestDatabaseFolderSQLite3.php deleted file mode 100644 index d10aafc..0000000 --- a/tests/cases/Db/SQLite3/Database/TestDatabaseFolderSQLite3.php +++ /dev/null @@ -1,14 +0,0 @@ - */ -class TestDatabaseFolderSQLite3 extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3; - use Test\Database\SeriesFolder; -} diff --git a/tests/cases/Db/SQLite3/Database/TestDatabaseLabelSQLite3.php b/tests/cases/Db/SQLite3/Database/TestDatabaseLabelSQLite3.php deleted file mode 100644 index 815bd49..0000000 --- a/tests/cases/Db/SQLite3/Database/TestDatabaseLabelSQLite3.php +++ /dev/null @@ -1,10 +0,0 @@ - */ -class TestDatabaseLabelSQLite3 extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3; - use Test\Database\SeriesLabel; -} diff --git a/tests/cases/Db/SQLite3/Database/TestDatabaseMetaSQLite3.php b/tests/cases/Db/SQLite3/Database/TestDatabaseMetaSQLite3.php deleted file mode 100644 index 2dbd172..0000000 --- a/tests/cases/Db/SQLite3/Database/TestDatabaseMetaSQLite3.php +++ /dev/null @@ -1,14 +0,0 @@ - */ -class TestDatabaseMetaSQLite3 extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3; - use Test\Database\SeriesMeta; -} diff --git a/tests/cases/Db/SQLite3/Database/TestDatabaseMiscellanySQLite3.php b/tests/cases/Db/SQLite3/Database/TestDatabaseMiscellanySQLite3.php deleted file mode 100644 index cc8eb08..0000000 --- a/tests/cases/Db/SQLite3/Database/TestDatabaseMiscellanySQLite3.php +++ /dev/null @@ -1,14 +0,0 @@ - */ -class TestDatabaseMiscellanySQLite3 extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3; - use Test\Database\SeriesMiscellany; -} diff --git a/tests/cases/Db/SQLite3/Database/TestDatabaseSessionSQLite3.php b/tests/cases/Db/SQLite3/Database/TestDatabaseSessionSQLite3.php deleted file mode 100644 index be10b88..0000000 --- a/tests/cases/Db/SQLite3/Database/TestDatabaseSessionSQLite3.php +++ /dev/null @@ -1,10 +0,0 @@ - */ -class TestDatabaseSessionSQLite3 extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3; - use Test\Database\SeriesSession; -} diff --git a/tests/cases/Db/SQLite3/Database/TestDatabaseSubscriptionSQLite3.php b/tests/cases/Db/SQLite3/Database/TestDatabaseSubscriptionSQLite3.php deleted file mode 100644 index 54cc11c..0000000 --- a/tests/cases/Db/SQLite3/Database/TestDatabaseSubscriptionSQLite3.php +++ /dev/null @@ -1,14 +0,0 @@ - */ -class TestDatabaseSubscriptionSQLite3 extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3; - use Test\Database\SeriesSubscription; -} diff --git a/tests/cases/Db/SQLite3/Database/TestDatabaseUserSQLite3.php b/tests/cases/Db/SQLite3/Database/TestDatabaseUserSQLite3.php deleted file mode 100644 index 5898c4e..0000000 --- a/tests/cases/Db/SQLite3/Database/TestDatabaseUserSQLite3.php +++ /dev/null @@ -1,14 +0,0 @@ - */ -class TestDatabaseUserSQLite3 extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3; - use Test\Database\SeriesUser; -} diff --git a/tests/cases/Db/SQLite3/Database/TestFeed.php b/tests/cases/Db/SQLite3/Database/TestFeed.php new file mode 100644 index 0000000..0d5bc9d --- /dev/null +++ b/tests/cases/Db/SQLite3/Database/TestFeed.php @@ -0,0 +1,14 @@ + */ +class TestFeed extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3; + use \JKingWeb\Arsse\Test\Database\SeriesFeed; +} diff --git a/tests/cases/Db/SQLite3/Database/TestFolder.php b/tests/cases/Db/SQLite3/Database/TestFolder.php new file mode 100644 index 0000000..0f6c2a8 --- /dev/null +++ b/tests/cases/Db/SQLite3/Database/TestFolder.php @@ -0,0 +1,14 @@ + */ +class TestFolder extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3; + use \JKingWeb\Arsse\Test\Database\SeriesFolder; +} diff --git a/tests/cases/Db/SQLite3/Database/TestLabel.php b/tests/cases/Db/SQLite3/Database/TestLabel.php new file mode 100644 index 0000000..2f5af8d --- /dev/null +++ b/tests/cases/Db/SQLite3/Database/TestLabel.php @@ -0,0 +1,10 @@ + */ +class TestLabel extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3; + use \JKingWeb\Arsse\Test\Database\SeriesLabel; +} diff --git a/tests/cases/Db/SQLite3/Database/TestMeta.php b/tests/cases/Db/SQLite3/Database/TestMeta.php new file mode 100644 index 0000000..17f8900 --- /dev/null +++ b/tests/cases/Db/SQLite3/Database/TestMeta.php @@ -0,0 +1,14 @@ + */ +class TestMeta extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3; + use \JKingWeb\Arsse\Test\Database\SeriesMeta; +} diff --git a/tests/cases/Db/SQLite3/Database/TestMiscellany.php b/tests/cases/Db/SQLite3/Database/TestMiscellany.php new file mode 100644 index 0000000..d9b408c --- /dev/null +++ b/tests/cases/Db/SQLite3/Database/TestMiscellany.php @@ -0,0 +1,14 @@ + */ +class TestMiscellany extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3; + use \JKingWeb\Arsse\Test\Database\SeriesMiscellany; +} diff --git a/tests/cases/Db/SQLite3/Database/TestSession.php b/tests/cases/Db/SQLite3/Database/TestSession.php new file mode 100644 index 0000000..22600ab --- /dev/null +++ b/tests/cases/Db/SQLite3/Database/TestSession.php @@ -0,0 +1,10 @@ + */ +class TestSession extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3; + use \JKingWeb\Arsse\Test\Database\SeriesSession; +} diff --git a/tests/cases/Db/SQLite3/Database/TestSubscription.php b/tests/cases/Db/SQLite3/Database/TestSubscription.php new file mode 100644 index 0000000..9f91caf --- /dev/null +++ b/tests/cases/Db/SQLite3/Database/TestSubscription.php @@ -0,0 +1,14 @@ + */ +class TestSubscription extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3; + use \JKingWeb\Arsse\Test\Database\SeriesSubscription; +} diff --git a/tests/cases/Db/SQLite3/Database/TestUser.php b/tests/cases/Db/SQLite3/Database/TestUser.php new file mode 100644 index 0000000..0204212 --- /dev/null +++ b/tests/cases/Db/SQLite3/Database/TestUser.php @@ -0,0 +1,14 @@ + */ +class TestUser extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3; + use \JKingWeb\Arsse\Test\Database\SeriesUser; +} diff --git a/tests/cases/Db/SQLite3/TestDbDriverCreationSQLite3.php b/tests/cases/Db/SQLite3/TestCreation.php similarity index 97% rename from tests/cases/Db/SQLite3/TestDbDriverCreationSQLite3.php rename to tests/cases/Db/SQLite3/TestCreation.php index ebd9249..4f04918 100644 --- a/tests/cases/Db/SQLite3/TestDbDriverCreationSQLite3.php +++ b/tests/cases/Db/SQLite3/TestCreation.php @@ -4,9 +4,10 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Db\SQLite3; use JKingWeb\Arsse\Arsse; +use JKingWeb\Arsse\Conf; use JKingWeb\Arsse\Db\SQLite3\Driver; use org\bovigo\vfs\vfsStream; use Phake; @@ -14,7 +15,7 @@ use Phake; /** * @covers \JKingWeb\Arsse\Db\SQLite3\Driver * @covers \JKingWeb\Arsse\Db\SQLite3\ExceptionBuilder */ -class TestDbDriverCreationSQLite3 extends Test\AbstractTest { +class TestCreation extends \JKingWeb\Arsse\Test\AbstractTest { protected $data; protected $drv; protected $ch; diff --git a/tests/cases/Db/SQLite3/TestDbDriverSQLite3.php b/tests/cases/Db/SQLite3/TestDriver.php similarity index 96% rename from tests/cases/Db/SQLite3/TestDbDriverSQLite3.php rename to tests/cases/Db/SQLite3/TestDriver.php index 224f2cc..d2e602c 100644 --- a/tests/cases/Db/SQLite3/TestDbDriverSQLite3.php +++ b/tests/cases/Db/SQLite3/TestDriver.php @@ -4,12 +4,19 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Db\SQLite3; + +use JKingWeb\Arsse\Arsse; +use JKingWeb\Arsse\Conf; +use JKingWeb\Arsse\Database; +use JKingWeb\Arsse\Db\SQLite3\Driver; +use JKingWeb\Arsse\Db\SQLite3\Result; +use JKingWeb\Arsse\Db\SQLite3\Statement; /** * @covers \JKingWeb\Arsse\Db\SQLite3\Driver * @covers \JKingWeb\Arsse\Db\SQLite3\ExceptionBuilder */ -class TestDbDriverSQLite3 extends Test\AbstractTest { +class TestDriver extends \JKingWeb\Arsse\Test\AbstractTest { protected $data; protected $drv; protected $ch; @@ -21,10 +28,10 @@ class TestDbDriverSQLite3 extends Test\AbstractTest { $this->clearData(); $conf = new Conf(); Arsse::$conf = $conf; - $conf->dbDriver = Db\SQLite3\Driver::class; + $conf->dbDriver = Driver::class; $conf->dbSQLite3Timeout = 0; $conf->dbSQLite3File = tempnam(sys_get_temp_dir(), 'ook'); - $this->drv = new Db\SQLite3\Driver(); + $this->drv = new Driver(); $this->ch = new \SQLite3(Arsse::$conf->dbSQLite3File); $this->ch->enableExceptions(true); } @@ -80,7 +87,7 @@ class TestDbDriverSQLite3 extends Test\AbstractTest { } public function testMakeAValidQuery() { - $this->assertInstanceOf(Db\Result::class, $this->drv->query("SELECT 1")); + $this->assertInstanceOf(Result::class, $this->drv->query("SELECT 1")); } public function testMakeAnInvalidQuery() { @@ -108,7 +115,7 @@ class TestDbDriverSQLite3 extends Test\AbstractTest { public function testPrepareAValidQuery() { $s = $this->drv->prepare("SELECT ?, ?", "int", "int"); - $this->assertInstanceOf(Db\Statement::class, $s); + $this->assertInstanceOf(Statement::class, $s); } public function testPrepareAnInvalidQuery() { diff --git a/tests/cases/Db/SQLite3/TestDbResultSQLite3.php b/tests/cases/Db/SQLite3/TestResult.php similarity index 87% rename from tests/cases/Db/SQLite3/TestDbResultSQLite3.php rename to tests/cases/Db/SQLite3/TestResult.php index 31b5bca..3b841f1 100644 --- a/tests/cases/Db/SQLite3/TestDbResultSQLite3.php +++ b/tests/cases/Db/SQLite3/TestResult.php @@ -4,10 +4,12 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Db\SQLite3; + +use JKingWeb\Arsse\Db\SQLite3\Result; /** @covers \JKingWeb\Arsse\Db\SQLite3\Result */ -class TestDbResultSQLite3 extends Test\AbstractTest { +class TestResult extends \JKingWeb\Arsse\Test\AbstractTest { protected $c; public function setUp() { @@ -28,7 +30,7 @@ class TestDbResultSQLite3 extends Test\AbstractTest { public function testConstructResult() { $set = $this->c->query("SELECT 1"); - $this->assertInstanceOf(Db\Result::class, new Db\SQLite3\Result($set)); + $this->assertInstanceOf(Result::class, new Result($set)); } public function testGetChangeCountAndLastInsertId() { @@ -36,7 +38,7 @@ class TestDbResultSQLite3 extends Test\AbstractTest { $set = $this->c->query("INSERT INTO test(col) values(1)"); $rows = $this->c->changes(); $id = $this->c->lastInsertRowID(); - $r = new Db\SQLite3\Result($set, [$rows,$id]); + $r = new Result($set, [$rows,$id]); $this->assertEquals($rows, $r->changes()); $this->assertEquals($id, $r->lastId()); } @@ -44,7 +46,7 @@ class TestDbResultSQLite3 extends Test\AbstractTest { public function testIterateOverResults() { $set = $this->c->query("SELECT 1 as col union select 2 as col union select 3 as col"); $rows = []; - foreach (new Db\SQLite3\Result($set) as $index => $row) { + foreach (new Result($set) as $index => $row) { $rows[$index] = $row['col']; } $this->assertEquals([0 => 1, 1 => 2, 2 => 3], $rows); @@ -53,7 +55,7 @@ class TestDbResultSQLite3 extends Test\AbstractTest { public function testIterateOverResultsTwice() { $set = $this->c->query("SELECT 1 as col union select 2 as col union select 3 as col"); $rows = []; - $test = new Db\SQLite3\Result($set); + $test = new Result($set); foreach ($test as $row) { $rows[] = $row['col']; } @@ -66,7 +68,7 @@ class TestDbResultSQLite3 extends Test\AbstractTest { public function testGetSingleValues() { $set = $this->c->query("SELECT 1867 as year union select 1970 as year union select 2112 as year"); - $test = new Db\SQLite3\Result($set); + $test = new Result($set); $this->assertEquals(1867, $test->getValue()); $this->assertEquals(1970, $test->getValue()); $this->assertEquals(2112, $test->getValue()); @@ -75,7 +77,7 @@ class TestDbResultSQLite3 extends Test\AbstractTest { public function testGetFirstValuesOnly() { $set = $this->c->query("SELECT 1867 as year, 19 as century union select 1970 as year, 20 as century union select 2112 as year, 22 as century"); - $test = new Db\SQLite3\Result($set); + $test = new Result($set); $this->assertEquals(1867, $test->getValue()); $this->assertEquals(1970, $test->getValue()); $this->assertEquals(2112, $test->getValue()); @@ -88,7 +90,7 @@ class TestDbResultSQLite3 extends Test\AbstractTest { ['album' => '2112', 'track' => '2112'], ['album' => 'Clockwork Angels', 'track' => 'The Wreckers'], ]; - $test = new Db\SQLite3\Result($set); + $test = new Result($set); $this->assertEquals($rows[0], $test->getRow()); $this->assertEquals($rows[1], $test->getRow()); $this->assertSame(null, $test->getRow()); @@ -100,7 +102,7 @@ class TestDbResultSQLite3 extends Test\AbstractTest { ['album' => '2112', 'track' => '2112'], ['album' => 'Clockwork Angels', 'track' => 'The Wreckers'], ]; - $test = new Db\SQLite3\Result($set); + $test = new Result($set); $this->assertEquals($rows, $test->getAll()); } } diff --git a/tests/cases/Db/SQLite3/TestDbStatementSQLite3.php b/tests/cases/Db/SQLite3/TestStatement.php similarity index 91% rename from tests/cases/Db/SQLite3/TestDbStatementSQLite3.php rename to tests/cases/Db/SQLite3/TestStatement.php index cc92883..7e722c2 100644 --- a/tests/cases/Db/SQLite3/TestDbStatementSQLite3.php +++ b/tests/cases/Db/SQLite3/TestStatement.php @@ -4,18 +4,18 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Db\SQLite3; use JKingWeb\Arsse\Db\Statement; /** * @covers \JKingWeb\Arsse\Db\SQLite3\Statement * @covers \JKingWeb\Arsse\Db\SQLite3\ExceptionBuilder */ -class TestDbStatementSQLite3 extends Test\AbstractTest { - use Test\Db\BindingTests; +class TestStatement extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Db\BindingTests; protected $c; - protected static $imp = Db\SQLite3\Statement::class; + protected static $imp = \JKingWeb\Arsse\Db\SQLite3\Statement::class; public function setUp() { $this->clearData(); @@ -48,7 +48,7 @@ class TestDbStatementSQLite3 extends Test\AbstractTest { public function testConstructStatement() { $nativeStatement = $this->c->prepare("SELECT ? as value"); - $this->assertInstanceOf(Statement::class, new Db\SQLite3\Statement($this->c, $nativeStatement)); + $this->assertInstanceOf(Statement::class, new \JKingWeb\Arsse\Db\SQLite3\Statement($this->c, $nativeStatement)); } public function testBindMissingValue() { diff --git a/tests/cases/Db/SQLite3/TestDbUpdateSQLite3.php b/tests/cases/Db/SQLite3/TestUpdate.php similarity index 92% rename from tests/cases/Db/SQLite3/TestDbUpdateSQLite3.php rename to tests/cases/Db/SQLite3/TestUpdate.php index 7c48d2f..3767d75 100644 --- a/tests/cases/Db/SQLite3/TestDbUpdateSQLite3.php +++ b/tests/cases/Db/SQLite3/TestUpdate.php @@ -4,14 +4,19 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Db\SQLite3; +use JKingWeb\Arsse\Arsse; +use JKingWeb\Arsse\Conf; +use JKingWeb\Arsse\Database; +use JKingWeb\Arsse\Db\Exception; +use JKingWeb\Arsse\Db\SQLite3\Driver; use org\bovigo\vfs\vfsStream; /** * @covers \JKingWeb\Arsse\Db\SQLite3\Driver * @covers \JKingWeb\Arsse\Db\SQLite3\ExceptionBuilder */ -class TestDbUpdateSQLite3 extends Test\AbstractTest { +class TestUpdate extends \JKingWeb\Arsse\Test\AbstractTest { protected $data; protected $drv; protected $vfs; @@ -29,12 +34,12 @@ class TestDbUpdateSQLite3 extends Test\AbstractTest { if (!$conf) { $conf = new Conf(); } - $conf->dbDriver = Db\SQLite3\Driver::class; + $conf->dbDriver = Driver::class; $conf->dbSQLite3File = ":memory:"; Arsse::$conf = $conf; $this->base = $this->vfs->url(); $this->path = $this->base."/SQLite3/"; - $this->drv = new Db\SQLite3\Driver(); + $this->drv = new Driver(); } public function tearDown() { diff --git a/tests/cases/Db/TestResultAggregate.php b/tests/cases/Db/TestResultAggregate.php index a44121e..cb450ac 100644 --- a/tests/cases/Db/TestResultAggregate.php +++ b/tests/cases/Db/TestResultAggregate.php @@ -1,18 +1,19 @@ */ -class TestResultAggregate extends Test\AbstractTest { +class TestResultAggregate extends \JKingWeb\Arsse\Test\AbstractTest { public function testGetChangeCountAndLastInsertId() { $in = [ new Result([], 3, 4), new Result([], 27, 10), new Result([], 12, 2112), ]; - $r = new Db\ResultAggregate(...$in); + $r = new ResultAggregate(...$in); $this->assertEquals(42, $r->changes()); $this->assertEquals(2112, $r->lastId()); } @@ -24,7 +25,7 @@ class TestResultAggregate extends Test\AbstractTest { new Result([['col' => 3]]), ]; $rows = []; - foreach (new Db\ResultAggregate(...$in) as $index => $row) { + foreach (new ResultAggregate(...$in) as $index => $row) { $rows[$index] = $row['col']; } $this->assertEquals([0 => 1, 1 => 2, 2 => 3], $rows); @@ -37,7 +38,7 @@ class TestResultAggregate extends Test\AbstractTest { new Result([['col' => 3]]), ]; $rows = []; - $test = new Db\ResultAggregate(...$in); + $test = new ResultAggregate(...$in); foreach ($test as $row) { $rows[] = $row['col']; } @@ -49,7 +50,7 @@ class TestResultAggregate extends Test\AbstractTest { } public function testGetSingleValues() { - $test = new Db\ResultAggregate(...[ + $test = new ResultAggregate(...[ new Result([['year' => 1867]]), new Result([['year' => 1970]]), new Result([['year' => 2112]]), @@ -61,7 +62,7 @@ class TestResultAggregate extends Test\AbstractTest { } public function testGetFirstValuesOnly() { - $test = new Db\ResultAggregate(...[ + $test = new ResultAggregate(...[ new Result([['year' => 1867, 'century' => 19]]), new Result([['year' => 1970, 'century' => 20]]), new Result([['year' => 2112, 'century' => 22]]), @@ -73,7 +74,7 @@ class TestResultAggregate extends Test\AbstractTest { } public function testGetRows() { - $test = new Db\ResultAggregate(...[ + $test = new ResultAggregate(...[ new Result([['album' => '2112', 'track' => '2112']]), new Result([['album' => 'Clockwork Angels', 'track' => 'The Wreckers']]), ]); @@ -87,7 +88,7 @@ class TestResultAggregate extends Test\AbstractTest { } public function testGetAllRows() { - $test = new Db\ResultAggregate(...[ + $test = new ResultAggregate(...[ new Result([['album' => '2112', 'track' => '2112']]), new Result([['album' => 'Clockwork Angels', 'track' => 'The Wreckers']]), ]); diff --git a/tests/cases/Db/TestResultEmpty.php b/tests/cases/Db/TestResultEmpty.php index 65b1380..03df4d4 100644 --- a/tests/cases/Db/TestResultEmpty.php +++ b/tests/cases/Db/TestResultEmpty.php @@ -1,35 +1,37 @@ */ -class TestResultEmpty extends Test\AbstractTest { +class TestResultEmpty extends \JKingWeb\Arsse\Test\AbstractTest { public function testGetChangeCountAndLastInsertId() { - $r = new Db\ResultEmpty; + $r = new ResultEmpty; $this->assertEquals(0, $r->changes()); $this->assertEquals(0, $r->lastId()); } public function testIterateOverResults() { $rows = []; - foreach (new Db\ResultEmpty as $index => $row) { + foreach (new ResultEmpty as $index => $row) { $rows[$index] = $row['col']; } $this->assertEquals([], $rows); } public function testGetSingleValues() { - $test = new Db\ResultEmpty; + $test = new ResultEmpty; $this->assertSame(null, $test->getValue()); } public function testGetRows() { - $test = new Db\ResultEmpty; + $test = new ResultEmpty; $this->assertSame(null, $test->getRow()); } public function testGetAllRows() { - $test = new Db\ResultEmpty; + $test = new ResultEmpty; $rows = []; $this->assertEquals($rows, $test->getAll()); } diff --git a/tests/cases/Db/TestTransaction.php b/tests/cases/Db/TestTransaction.php index 118e3e8..9469d6c 100644 --- a/tests/cases/Db/TestTransaction.php +++ b/tests/cases/Db/TestTransaction.php @@ -4,19 +4,20 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Db; use JKingWeb\Arsse\Db\Transaction; +use JKingWeb\Arsse\Db\Exception; use Phake; /** * @covers \JKingWeb\Arsse\Db\Transaction */ -class TestTransaction extends Test\AbstractTest { +class TestTransaction extends \JKingWeb\Arsse\Test\AbstractTest { protected $drv; public function setUp() { $this->clearData(); - $drv = Phake::mock(Db\SQLite3\Driver::class); + $drv = Phake::mock(\JKingWeb\Arsse\Db\SQLite3\Driver::class); Phake::when($drv)->savepointRelease->thenReturn(true); Phake::when($drv)->savepointUndo->thenReturn(true); Phake::when($drv)->savepointCreate->thenReturn(1)->thenReturn(2); @@ -51,7 +52,7 @@ class TestTransaction extends Test\AbstractTest { } public function testIgnoreRollbackErrors() { - Phake::when($this->drv)->savepointUndo->thenThrow(new Db\Exception("savepointStale")); + Phake::when($this->drv)->savepointUndo->thenThrow(new Exception("savepointStale")); $tr1 = new Transaction($this->drv); $tr2 = new Transaction($this->drv); unset($tr1, $tr2); // no exception should bubble up diff --git a/tests/cases/Exception/TestException.php b/tests/cases/Exception/TestException.php index 821280f..6dba703 100644 --- a/tests/cases/Exception/TestException.php +++ b/tests/cases/Exception/TestException.php @@ -4,12 +4,16 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Exception; +use JKingWeb\Arsse\Arsse; +use JKingWeb\Arsse\Lang; +use JKingWeb\Arsse\Exception; +use JKingWeb\Arsse\Lang\Exception as LangException; use Phake; /** @covers \JKingWeb\Arsse\AbstractException */ -class TestException extends Test\AbstractTest { +class TestException extends \JKingWeb\Arsse\Test\AbstractTest { public function setUp() { $this->clearData(false); // create a mock Lang object so as not to create a dependency loop @@ -43,7 +47,7 @@ class TestException extends Test\AbstractTest { */ public function testDerivedClass() { $this->assertException("fileMissing", "Lang"); - throw new Lang\Exception("fileMissing"); + throw new LangException("fileMissing"); } /** @@ -51,7 +55,7 @@ class TestException extends Test\AbstractTest { */ public function testDerivedClassWithMessageParameters() { $this->assertException("fileMissing", "Lang"); - throw new Lang\Exception("fileMissing", "en"); + throw new LangException("fileMissing", "en"); } /** @@ -67,6 +71,6 @@ class TestException extends Test\AbstractTest { */ public function testDerivedClassWithMissingMessage() { $this->assertException("uncoded"); - throw new Lang\Exception("testThisExceptionMessageDoesNotExist"); + throw new LangException("testThisExceptionMessageDoesNotExist"); } } diff --git a/tests/cases/Feed/TestFeed.php b/tests/cases/Feed/TestFeed.php index 23e89f9..d458c4a 100644 --- a/tests/cases/Feed/TestFeed.php +++ b/tests/cases/Feed/TestFeed.php @@ -4,16 +4,21 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Feed; +use JKingWeb\Arsse\Arsse; +use JKingWeb\Arsse\Conf; +use JKingWeb\Arsse\Feed; +use JKingWeb\Arsse\Database; use JKingWeb\Arsse\Misc\Date; +use JKingWeb\Arsse\Test\Result; use Phake; /** * @covers \JKingWeb\Arsse\Feed * @covers \JKingWeb\Arsse\Feed\Exception * @group slow */ -class TestFeed extends Test\AbstractTest { +class TestFeed extends \JKingWeb\Arsse\Test\AbstractTest { protected static $host = "http://localhost:8000/"; protected $base = ""; protected $latest = [ @@ -329,7 +334,7 @@ class TestFeed extends Test\AbstractTest { } public function testMatchLatestArticles() { - Phake::when(Arsse::$db)->feedMatchLatest(1, $this->anything())->thenReturn(new Test\Result($this->latest)); + Phake::when(Arsse::$db)->feedMatchLatest(1, $this->anything())->thenReturn(new Result($this->latest)); $f = new Feed(1, $this->base."Matching/1"); $this->assertCount(0, $f->newItems); $this->assertCount(0, $f->changedItems); @@ -345,8 +350,8 @@ class TestFeed extends Test\AbstractTest { } public function testMatchHistoricalArticles() { - Phake::when(Arsse::$db)->feedMatchLatest(1, $this->anything())->thenReturn(new Test\Result($this->latest)); - Phake::when(Arsse::$db)->feedMatchIds(1, $this->anything(), $this->anything(), $this->anything(), $this->anything())->thenReturn(new Test\Result($this->others)); + Phake::when(Arsse::$db)->feedMatchLatest(1, $this->anything())->thenReturn(new Result($this->latest)); + Phake::when(Arsse::$db)->feedMatchIds(1, $this->anything(), $this->anything(), $this->anything(), $this->anything())->thenReturn(new Result($this->others)); $f = new Feed(1, $this->base."Matching/5"); $this->assertCount(0, $f->newItems); $this->assertCount(0, $f->changedItems); diff --git a/tests/cases/Feed/TestFeedFetching.php b/tests/cases/Feed/TestFetching.php similarity index 91% rename from tests/cases/Feed/TestFeedFetching.php rename to tests/cases/Feed/TestFetching.php index e5de504..afe1391 100644 --- a/tests/cases/Feed/TestFeedFetching.php +++ b/tests/cases/Feed/TestFetching.php @@ -4,14 +4,17 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Feed; +use JKingWeb\Arsse\Arsse; +use JKingWeb\Arsse\Conf; +use JKingWeb\Arsse\Feed; use Phake; -/** - * @covers \JKingWeb\Arsse\Feed +/** + * @covers \JKingWeb\Arsse\Feed * @group slow */ -class TestFeedFetching extends Test\AbstractTest { +class TestFetching extends \JKingWeb\Arsse\Test\AbstractTest { protected static $host = "http://localhost:8000/"; protected $base = ""; diff --git a/tests/cases/Lang/TestLang.php b/tests/cases/Lang/TestBasic.php similarity index 81% rename from tests/cases/Lang/TestLang.php rename to tests/cases/Lang/TestBasic.php index 4e6db2a..fbd7129 100644 --- a/tests/cases/Lang/TestLang.php +++ b/tests/cases/Lang/TestBasic.php @@ -4,13 +4,15 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Lang; +use JKingWeb\Arsse\Lang as TestClass; use org\bovigo\vfs\vfsStream; + /** @covers \JKingWeb\Arsse\Lang */ -class TestLang extends Test\AbstractTest { - use Test\Lang\Setup; +class TestBasic extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Lang\Setup; public $files; public $path; @@ -38,14 +40,14 @@ class TestLang extends Test\AbstractTest { */ public function testLoadInternalStrings() { $this->assertEquals("", $this->l->set("", true)); - $this->assertCount(sizeof(Lang::REQUIRED), $this->l->dump()); + $this->assertCount(sizeof(TestClass::REQUIRED), $this->l->dump()); } /** * @depends testLoadInternalStrings */ public function testLoadDefaultLanguage() { - $this->assertEquals(Lang::DEFAULT, $this->l->set(Lang::DEFAULT, true)); + $this->assertEquals(TestClass::DEFAULT, $this->l->set(TestClass::DEFAULT, true)); $str = $this->l->dump(); $this->assertArrayHasKey('Exception.JKingWeb/Arsse/Exception.uncoded', $str); $this->assertArrayHasKey('Test.presentText', $str); @@ -55,7 +57,7 @@ class TestLang extends Test\AbstractTest { * @depends testLoadDefaultLanguage */ public function testLoadSupplementaryLanguage() { - $this->l->set(Lang::DEFAULT, true); + $this->l->set(TestClass::DEFAULT, true); $this->assertEquals("ja", $this->l->set("ja", true)); $str = $this->l->dump(); $this->assertArrayHasKey('Exception.JKingWeb/Arsse/Exception.uncoded', $str); diff --git a/tests/cases/Lang/TestLangErrors.php b/tests/cases/Lang/TestErrors.php similarity index 88% rename from tests/cases/Lang/TestLangErrors.php rename to tests/cases/Lang/TestErrors.php index 5051a7f..bd3f8c0 100644 --- a/tests/cases/Lang/TestLangErrors.php +++ b/tests/cases/Lang/TestErrors.php @@ -4,13 +4,14 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Lang; +use JKingWeb\Arsse\Lang as TestClass; use org\bovigo\vfs\vfsStream; /** @covers \JKingWeb\Arsse\Lang */ -class TestLangErrors extends Test\AbstractTest { - use Test\Lang\Setup; +class TestErrors extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Lang\Setup; public $files; public $path; @@ -62,7 +63,7 @@ class TestLangErrors extends Test\AbstractTest { } public function testLoadMissingDefaultLanguage() { - unlink($this->path.Lang::DEFAULT.".php"); + unlink($this->path.TestClass::DEFAULT.".php"); $this->assertException("defaultFileMissing", "Lang"); $this->l->set("fr", true); } diff --git a/tests/cases/Lang/testLangComplex.php b/tests/cases/Lang/testComplex.php similarity index 92% rename from tests/cases/Lang/testLangComplex.php rename to tests/cases/Lang/testComplex.php index d4399df..73ae9b0 100644 --- a/tests/cases/Lang/testLangComplex.php +++ b/tests/cases/Lang/testComplex.php @@ -4,20 +4,21 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Lang; +use JKingWeb\Arsse\Lang as TestClass; use org\bovigo\vfs\vfsStream; /** @covers \JKingWeb\Arsse\Lang */ -class TestLangComplex extends Test\AbstractTest { - use Test\Lang\Setup; +class TestComplex extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Lang\Setup; public $files; public $path; public $l; public function setUpSeries() { - $this->l->set(Lang::DEFAULT, true); + $this->l->set(TestClass::DEFAULT, true); } public function testLazyLoad() { @@ -68,7 +69,7 @@ class TestLangComplex extends Test\AbstractTest { */ public function testFetchAMessageWithSingleNumericParameter() { $this->l->set("en_ca", true); - $this->assertEquals('Default language file "en" missing', $this->l->msg('Exception.JKingWeb/Arsse/Lang/Exception.defaultFileMissing', Lang::DEFAULT)); + $this->assertEquals('Default language file "en" missing', $this->l->msg('Exception.JKingWeb/Arsse/Lang/Exception.defaultFileMissing', TestClass::DEFAULT)); } /** diff --git a/tests/cases/Misc/TestContext.php b/tests/cases/Misc/TestContext.php index 4f9b330..63bf953 100644 --- a/tests/cases/Misc/TestContext.php +++ b/tests/cases/Misc/TestContext.php @@ -4,12 +4,12 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Misc; use JKingWeb\Arsse\Misc\Context; /** @covers \JKingWeb\Arsse\Misc\Context */ -class TestContext extends Test\AbstractTest { +class TestContext extends \JKingWeb\Arsse\Test\AbstractTest { public function testVerifyInitialState() { $c = new Context; foreach ((new \ReflectionObject($c))->getMethods(\ReflectionMethod::IS_PUBLIC) as $m) { diff --git a/tests/cases/Misc/TestValueInfo.php b/tests/cases/Misc/TestValueInfo.php index 039a21e..87c6f01 100644 --- a/tests/cases/Misc/TestValueInfo.php +++ b/tests/cases/Misc/TestValueInfo.php @@ -4,13 +4,15 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Misc; +use JKingWeb\Arsse\ExceptionType; use JKingWeb\Arsse\Misc\ValueInfo as I; use JKingWeb\Arsse\Test\Misc\StrClass; +use JKingWeb\Arsse\Test\Result; /** @covers \JKingWeb\Arsse\Misc\ValueInfo */ -class TestValueInfo extends Test\AbstractTest { +class TestValueInfo extends \JKingWeb\Arsse\Test\AbstractTest { public function setUp() { $this->clearData(); } @@ -398,7 +400,7 @@ class TestValueInfo extends Test\AbstractTest { [1e-6, [null,true], [true, false], [0, false], [1e-6, true], ["0.000001", true], [[1e-6], false]], [[1,2,3], [null,true], [true, false], [0, false], [0.0, false], ["", false], [[1,2,3], true] ], [['a'=>1,'b'=>2], [null,true], [true, false], [0, false], [0.0, false], ["", false], [['a'=>1,'b'=>2], true] ], - [new Test\Result([['a'=>1,'b'=>2]]), [null,true], [true, false], [0, false], [0.0, false], ["", false], [[['a'=>1,'b'=>2]], true] ], + [new Result([['a'=>1,'b'=>2]]), [null,true], [true, false], [0, false], [0.0, false], ["", false], [[['a'=>1,'b'=>2]], true] ], ]; $params = [ [I::T_MIXED, "Mixed" ], @@ -496,8 +498,8 @@ class TestValueInfo extends Test\AbstractTest { } // Array-mode tests $tests = [ - [I::T_INT | I::M_DROP, new Test\Result([1, 2, 2.2, 3]), [1,2,null,3] ], - [I::T_INT, new Test\Result([1, 2, 2.2, 3]), [1,2,2,3] ], + [I::T_INT | I::M_DROP, new Result([1, 2, 2.2, 3]), [1,2,null,3] ], + [I::T_INT, new Result([1, 2, 2.2, 3]), [1,2,2,3] ], [I::T_STRING | I::M_STRICT, "Bare string", ["Bare string"]], ]; foreach ($tests as $index => $test) { diff --git a/tests/cases/REST/NextCloudNews/TestNCNV1_2.php b/tests/cases/REST/NextCloudNews/TestV1_2.php similarity index 99% rename from tests/cases/REST/NextCloudNews/TestNCNV1_2.php rename to tests/cases/REST/NextCloudNews/TestV1_2.php index 6b3d209..17c4679 100644 --- a/tests/cases/REST/NextCloudNews/TestNCNV1_2.php +++ b/tests/cases/REST/NextCloudNews/TestV1_2.php @@ -4,8 +4,13 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\REST\NextCloudNews; +use JKingWeb\Arsse\Arsse; +use JKingWeb\Arsse\Conf; +use JKingWeb\Arsse\User; +use JKingWeb\Arsse\Database; +use JKingWeb\Arsse\Service; use JKingWeb\Arsse\REST\Request; use JKingWeb\Arsse\REST\Response; use JKingWeb\Arsse\Test\Result; @@ -13,10 +18,11 @@ use JKingWeb\Arsse\Misc\Date; use JKingWeb\Arsse\Misc\Context; use JKingWeb\Arsse\Db\ExceptionInput; use JKingWeb\Arsse\Db\Transaction; +use JKingWeb\Arsse\REST\NextCloudNews\V1_2; use Phake; /** @covers \JKingWeb\Arsse\REST\NextCloudNews\V1_2 */ -class TestNCNV1_2 extends Test\AbstractTest { +class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { protected $h; protected $feeds = [ // expected sample output of a feed list from the database, and the resultant expected transformation by the REST handler 'db' => [ @@ -304,7 +310,7 @@ class TestNCNV1_2 extends Test\AbstractTest { // create a mock database interface Arsse::$db = Phake::mock(Database::class); Phake::when(Arsse::$db)->begin->thenReturn(Phake::mock(Transaction::class)); - $this->h = new REST\NextCloudNews\V1_2(); + $this->h = new V1_2(); } public function tearDown() { @@ -318,7 +324,7 @@ class TestNCNV1_2 extends Test\AbstractTest { public function testSendAuthenticationChallenge() { Phake::when(Arsse::$user)->authHTTP->thenReturn(false); - $exp = new Response(401, "", "", ['WWW-Authenticate: Basic realm="'.REST\NextCloudNews\V1_2::REALM.'"']); + $exp = new Response(401, "", "", ['WWW-Authenticate: Basic realm="'.V1_2::REALM.'"']); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/"))); } @@ -496,7 +502,7 @@ class TestNCNV1_2 extends Test\AbstractTest { public function testRetrieveServerVersion() { $exp = new Response(200, [ - 'version' => REST\NextCloudNews\V1_2::VERSION, + 'version' => V1_2::VERSION, 'arsse_version' => Arsse::VERSION, ]); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/version"))); @@ -867,7 +873,7 @@ class TestNCNV1_2 extends Test\AbstractTest { Phake::when(Arsse::$db)->metaGet("service_last_checkin")->thenReturn(Date::transform($valid, "sql"))->thenReturn(Date::transform($invalid, "sql")); Phake::when(Arsse::$db)->driverCharsetAcceptable->thenReturn(true)->thenReturn(false); $arr1 = $arr2 = [ - 'version' => REST\NextCloudNews\V1_2::VERSION, + 'version' => V1_2::VERSION, 'arsse_version' => Arsse::VERSION, 'warnings' => [ 'improperlyConfiguredCron' => false, diff --git a/tests/cases/REST/NextCloudNews/TestNCNVersionDiscovery.php b/tests/cases/REST/NextCloudNews/TestVersions.php similarity index 85% rename from tests/cases/REST/NextCloudNews/TestNCNVersionDiscovery.php rename to tests/cases/REST/NextCloudNews/TestVersions.php index 3074917..3081d57 100644 --- a/tests/cases/REST/NextCloudNews/TestNCNVersionDiscovery.php +++ b/tests/cases/REST/NextCloudNews/TestVersions.php @@ -4,20 +4,21 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\REST\NextCloudNews; +use JKingWeb\Arsse\REST\NextCloudNews\Versions; use JKingWeb\Arsse\REST\Request; use JKingWeb\Arsse\REST\Response; /** @covers \JKingWeb\Arsse\REST\NextCloudNews\Versions */ -class TestNCNVersionDiscovery extends Test\AbstractTest { +class TestVersions extends \JKingWeb\Arsse\Test\AbstractTest { public function setUp() { $this->clearData(); } public function testFetchVersionList() { $exp = new Response(200, ['apiLevels' => ['v1-2']]); - $h = new REST\NextCloudNews\Versions(); + $h = new Versions; $req = new Request("GET", "/"); $res = $h->dispatch($req); $this->assertEquals($exp, $res); @@ -31,7 +32,7 @@ class TestNCNVersionDiscovery extends Test\AbstractTest { public function testRespondToOptionsRequest() { $exp = new Response(204, "", "", ["Allow: HEAD,GET"]); - $h = new REST\NextCloudNews\Versions(); + $h = new Versions; $req = new Request("OPTIONS", "/"); $res = $h->dispatch($req); $this->assertEquals($exp, $res); @@ -39,7 +40,7 @@ class TestNCNVersionDiscovery extends Test\AbstractTest { public function testUseIncorrectMethod() { $exp = new Response(405, "", "", ["Allow: HEAD,GET"]); - $h = new REST\NextCloudNews\Versions(); + $h = new Versions; $req = new Request("POST", "/"); $res = $h->dispatch($req); $this->assertEquals($exp, $res); @@ -47,7 +48,7 @@ class TestNCNVersionDiscovery extends Test\AbstractTest { public function testUseIncorrectPath() { $exp = new Response(404); - $h = new REST\NextCloudNews\Versions(); + $h = new Versions; $req = new Request("GET", "/ook"); $res = $h->dispatch($req); $this->assertEquals($exp, $res); diff --git a/tests/cases/REST/TinyTinyRSS/TestTinyTinyAPI.php b/tests/cases/REST/TinyTinyRSS/TestAPI.php similarity index 99% rename from tests/cases/REST/TinyTinyRSS/TestTinyTinyAPI.php rename to tests/cases/REST/TinyTinyRSS/TestAPI.php index 14f5364..9858508 100644 --- a/tests/cases/REST/TinyTinyRSS/TestTinyTinyAPI.php +++ b/tests/cases/REST/TinyTinyRSS/TestAPI.php @@ -4,8 +4,13 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\REST\TinyTinyRSS; +use JKingWeb\Arsse\Arsse; +use JKingWeb\Arsse\Conf; +use JKingWeb\Arsse\User; +use JKingWeb\Arsse\Database; +use JKingWeb\Arsse\Service; use JKingWeb\Arsse\REST\Request; use JKingWeb\Arsse\REST\Response; use JKingWeb\Arsse\Test\Result; @@ -18,7 +23,7 @@ use Phake; /** @covers \JKingWeb\Arsse\REST\TinyTinyRSS\API * @covers \JKingWeb\Arsse\REST\TinyTinyRSS\Exception */ -class TestTinyTinyAPI extends Test\AbstractTest { +class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest { protected $h; protected $folders = [ ['id' => 5, 'parent' => 3, 'children' => 0, 'feeds' => 1, 'name' => "Local"], @@ -161,7 +166,7 @@ LONG_STRING; 'expires' => "2112-12-21 21:12:00", 'user' => Arsse::$user->id, ]); - $this->h = new REST\TinyTinyRSS\API(); + $this->h = new API(); } public function tearDown() { diff --git a/tests/cases/REST/TinyTinyRSS/TestTinyTinyIcon.php b/tests/cases/REST/TinyTinyRSS/TestIcon.php similarity index 88% rename from tests/cases/REST/TinyTinyRSS/TestTinyTinyIcon.php rename to tests/cases/REST/TinyTinyRSS/TestIcon.php index 1b439b3..64f3358 100644 --- a/tests/cases/REST/TinyTinyRSS/TestTinyTinyIcon.php +++ b/tests/cases/REST/TinyTinyRSS/TestIcon.php @@ -4,14 +4,19 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\REST\TinyTinyRSS; +use JKingWeb\Arsse\Arsse; +use JKingWeb\Arsse\Conf; +use JKingWeb\Arsse\User; +use JKingWeb\Arsse\Database; +use JKingWeb\Arsse\REST\TinyTinyRSS\Icon; use JKingWeb\Arsse\REST\Request; use JKingWeb\Arsse\REST\Response; use Phake; /** @covers \JKingWeb\Arsse\REST\TinyTinyRSS\Icon */ -class TestTinyTinyIcon extends Test\AbstractTest { +class TestIcon extends \JKingWeb\Arsse\Test\AbstractTest { protected $h; public function setUp() { @@ -20,7 +25,7 @@ class TestTinyTinyIcon extends Test\AbstractTest { // create a mock user manager // create a mock database interface Arsse::$db = Phake::mock(Database::class); - $this->h = new REST\TinyTinyRSS\Icon(); + $this->h = new Icon(); } public function tearDown() { diff --git a/tests/cases/Service/TestService.php b/tests/cases/Service/TestService.php index 76604df..1ec4a3d 100644 --- a/tests/cases/Service/TestService.php +++ b/tests/cases/Service/TestService.php @@ -4,13 +4,17 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Service; +use JKingWeb\Arsse\Arsse; +use JKingWeb\Arsse\Conf; +use JKingWeb\Arsse\Database; +use JKingWeb\Arsse\Service; use JKingWeb\Arsse\Misc\Date; use Phake; /** @covers \JKingWeb\Arsse\Service */ -class TestService extends Test\AbstractTest { +class TestService extends \JKingWeb\Arsse\Test\AbstractTest { protected $srv; public function setUp() { diff --git a/tests/cases/User/TestAuthorization.php b/tests/cases/User/TestAuthorization.php index a20819d..5692f01 100644 --- a/tests/cases/User/TestAuthorization.php +++ b/tests/cases/User/TestAuthorization.php @@ -4,42 +4,47 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\User; + +use JKingWeb\Arsse\Arsse; +use JKingWeb\Arsse\Conf; +use JKingWeb\Arsse\User; +use JKingWeb\Arsse\User\Driver; use Phake; /** @covers \JKingWeb\Arsse\User */ -class TestAuthorization extends Test\AbstractTest { +class TestAuthorization extends \JKingWeb\Arsse\Test\AbstractTest { const USERS = [ - 'user@example.com' => User\Driver::RIGHTS_NONE, - 'user@example.org' => User\Driver::RIGHTS_NONE, - 'dman@example.com' => User\Driver::RIGHTS_DOMAIN_MANAGER, - 'dman@example.org' => User\Driver::RIGHTS_DOMAIN_MANAGER, - 'dadm@example.com' => User\Driver::RIGHTS_DOMAIN_ADMIN, - 'dadm@example.org' => User\Driver::RIGHTS_DOMAIN_ADMIN, - 'gman@example.com' => User\Driver::RIGHTS_GLOBAL_MANAGER, - 'gman@example.org' => User\Driver::RIGHTS_GLOBAL_MANAGER, - 'gadm@example.com' => User\Driver::RIGHTS_GLOBAL_ADMIN, - 'gadm@example.org' => User\Driver::RIGHTS_GLOBAL_ADMIN, + 'user@example.com' => Driver::RIGHTS_NONE, + 'user@example.org' => Driver::RIGHTS_NONE, + 'dman@example.com' => Driver::RIGHTS_DOMAIN_MANAGER, + 'dman@example.org' => Driver::RIGHTS_DOMAIN_MANAGER, + 'dadm@example.com' => Driver::RIGHTS_DOMAIN_ADMIN, + 'dadm@example.org' => Driver::RIGHTS_DOMAIN_ADMIN, + 'gman@example.com' => Driver::RIGHTS_GLOBAL_MANAGER, + 'gman@example.org' => Driver::RIGHTS_GLOBAL_MANAGER, + 'gadm@example.com' => Driver::RIGHTS_GLOBAL_ADMIN, + 'gadm@example.org' => Driver::RIGHTS_GLOBAL_ADMIN, // invalid rights levels - 'bad1@example.com' => User\Driver::RIGHTS_NONE+1, - 'bad1@example.org' => User\Driver::RIGHTS_NONE+1, - 'bad2@example.com' => User\Driver::RIGHTS_DOMAIN_MANAGER+1, - 'bad2@example.org' => User\Driver::RIGHTS_DOMAIN_MANAGER+1, - 'bad3@example.com' => User\Driver::RIGHTS_DOMAIN_ADMIN+1, - 'bad3@example.org' => User\Driver::RIGHTS_DOMAIN_ADMIN+1, - 'bad4@example.com' => User\Driver::RIGHTS_GLOBAL_MANAGER+1, - 'bad4@example.org' => User\Driver::RIGHTS_GLOBAL_MANAGER+1, - 'bad5@example.com' => User\Driver::RIGHTS_GLOBAL_ADMIN+1, - 'bad5@example.org' => User\Driver::RIGHTS_GLOBAL_ADMIN+1, + 'bad1@example.com' => Driver::RIGHTS_NONE+1, + 'bad1@example.org' => Driver::RIGHTS_NONE+1, + 'bad2@example.com' => Driver::RIGHTS_DOMAIN_MANAGER+1, + 'bad2@example.org' => Driver::RIGHTS_DOMAIN_MANAGER+1, + 'bad3@example.com' => Driver::RIGHTS_DOMAIN_ADMIN+1, + 'bad3@example.org' => Driver::RIGHTS_DOMAIN_ADMIN+1, + 'bad4@example.com' => Driver::RIGHTS_GLOBAL_MANAGER+1, + 'bad4@example.org' => Driver::RIGHTS_GLOBAL_MANAGER+1, + 'bad5@example.com' => Driver::RIGHTS_GLOBAL_ADMIN+1, + 'bad5@example.org' => Driver::RIGHTS_GLOBAL_ADMIN+1, ]; const LEVELS = [ - User\Driver::RIGHTS_NONE, - User\Driver::RIGHTS_DOMAIN_MANAGER, - User\Driver::RIGHTS_DOMAIN_ADMIN, - User\Driver::RIGHTS_GLOBAL_MANAGER, - User\Driver::RIGHTS_GLOBAL_ADMIN, + Driver::RIGHTS_NONE, + Driver::RIGHTS_DOMAIN_MANAGER, + Driver::RIGHTS_DOMAIN_ADMIN, + Driver::RIGHTS_GLOBAL_MANAGER, + Driver::RIGHTS_GLOBAL_ADMIN, ]; const DOMAINS = [ '@example.com', @@ -49,7 +54,7 @@ class TestAuthorization extends Test\AbstractTest { protected $data; - public function setUp(string $drv = Test\User\DriverInternalMock::class, string $db = null) { + public function setUp(string $drv = \JkingWeb\Arsse\Test\User\DriverInternalMock::class, string $db = null) { $this->clearData(); $conf = new Conf(); $conf->userDriver = $drv; @@ -91,7 +96,7 @@ class TestAuthorization extends Test\AbstractTest { public function testRegularUserLogic() { foreach (self::USERS as $actor => $rights) { - if ($rights != User\Driver::RIGHTS_NONE) { + if ($rights != Driver::RIGHTS_NONE) { continue; } Arsse::$user->auth($actor, ""); @@ -118,7 +123,7 @@ class TestAuthorization extends Test\AbstractTest { public function testDomainManagerLogic() { foreach (self::USERS as $actor => $actorRights) { - if ($actorRights != User\Driver::RIGHTS_DOMAIN_MANAGER) { + if ($actorRights != Driver::RIGHTS_DOMAIN_MANAGER) { continue; } $actorDomain = substr($actor, strrpos($actor, "@")+1); @@ -139,7 +144,7 @@ class TestAuthorization extends Test\AbstractTest { } // and they should only be able to set their own rights to regular user foreach (self::LEVELS as $level) { - if ($actor==$affected && in_array($level, [User\Driver::RIGHTS_NONE, User\Driver::RIGHTS_DOMAIN_MANAGER])) { + if ($actor==$affected && in_array($level, [User\Driver::RIGHTS_NONE, Driver::RIGHTS_DOMAIN_MANAGER])) { $this->assertTrue(Arsse::$user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied."); } else { $this->assertFalse(Arsse::$user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed."); @@ -159,7 +164,7 @@ class TestAuthorization extends Test\AbstractTest { public function testDomainAdministratorLogic() { foreach (self::USERS as $actor => $actorRights) { - if ($actorRights != User\Driver::RIGHTS_DOMAIN_ADMIN) { + if ($actorRights != Driver::RIGHTS_DOMAIN_ADMIN) { continue; } $actorDomain = substr($actor, strrpos($actor, "@")+1); @@ -201,7 +206,7 @@ class TestAuthorization extends Test\AbstractTest { public function testGlobalManagerLogic() { foreach (self::USERS as $actor => $actorRights) { - if ($actorRights != User\Driver::RIGHTS_GLOBAL_MANAGER) { + if ($actorRights != Driver::RIGHTS_GLOBAL_MANAGER) { continue; } $actorDomain = substr($actor, strrpos($actor, "@")+1); @@ -218,7 +223,7 @@ class TestAuthorization extends Test\AbstractTest { } // and they should only be able to set their own rights to regular user foreach (self::LEVELS as $level) { - if ($actor==$affected && in_array($level, [User\Driver::RIGHTS_NONE, User\Driver::RIGHTS_GLOBAL_MANAGER])) { + if ($actor==$affected && in_array($level, [User\Driver::RIGHTS_NONE, Driver::RIGHTS_GLOBAL_MANAGER])) { $this->assertTrue(Arsse::$user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied."); } else { $this->assertFalse(Arsse::$user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed."); @@ -234,7 +239,7 @@ class TestAuthorization extends Test\AbstractTest { public function testGlobalAdministratorLogic() { foreach (self::USERS as $actor => $actorRights) { - if ($actorRights != User\Driver::RIGHTS_GLOBAL_ADMIN) { + if ($actorRights != Driver::RIGHTS_GLOBAL_ADMIN) { continue; } Arsse::$user->auth($actor, ""); @@ -302,7 +307,7 @@ class TestAuthorization extends Test\AbstractTest { public function testExternalExceptionLogic() { // set up the test for an external driver - $this->setUp(Test\User\DriverExternalMock::class, Test\User\Database::class); + $this->setUp(\JKingWeb\Arsse\Test\User\DriverExternalMock::class, \JKingWeb\Arsse\Test\User\Database::class); // run the previous test with the external driver set up $this->testInternalExceptionLogic(); } @@ -318,7 +323,7 @@ class TestAuthorization extends Test\AbstractTest { } try { call_user_func_array(array(Arsse::$user, $func), $args); - } catch (User\ExceptionAuthz $e) { + } catch (\JKingWeb\Arsse\User\ExceptionAuthz $e) { $err[] = $func; } } diff --git a/tests/cases/User/TestUserMockExternal.php b/tests/cases/User/TestMockExternal.php similarity index 54% rename from tests/cases/User/TestUserMockExternal.php rename to tests/cases/User/TestMockExternal.php index 3f418f3..928edc7 100644 --- a/tests/cases/User/TestUserMockExternal.php +++ b/tests/cases/User/TestMockExternal.php @@ -4,14 +4,14 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\User; /** @covers \JKingWeb\Arsse\User */ -class TestUserMockExternal extends Test\AbstractTest { - use Test\User\CommonTests; +class TestMockExternal extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\User\CommonTests; const USER1 = "john.doe@example.com"; const USER2 = "jane.doe@example.com"; - public $drv = Test\User\DriverExternalMock::class; + public $drv = \JKingWeb\Arsse\Test\User\DriverExternalMock::class; } diff --git a/tests/cases/User/TestUserMockInternal.php b/tests/cases/User/TestMockInternal.php similarity index 57% rename from tests/cases/User/TestUserMockInternal.php rename to tests/cases/User/TestMockInternal.php index 5386853..aa76505 100644 --- a/tests/cases/User/TestUserMockInternal.php +++ b/tests/cases/User/TestMockInternal.php @@ -4,16 +4,18 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\User; + +use JKingWeb\Arsse\Arsse; /** @covers \JKingWeb\Arsse\User */ -class TestUserMockInternal extends Test\AbstractTest { - use Test\User\CommonTests; +class TestMockInternal extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\User\CommonTests; const USER1 = "john.doe@example.com"; const USER2 = "jane.doe@example.com"; - public $drv = Test\User\DriverInternalMock::class; + public $drv = \JKingWeb\Arsse\Test\User\DriverInternalMock::class; public function setUpSeries() { Arsse::$db = null; diff --git a/tests/cases/User/TestUserInternalDriver.php b/tests/cases/User/Testnternal.php similarity index 64% rename from tests/cases/User/TestUserInternalDriver.php rename to tests/cases/User/Testnternal.php index cfd8978..1bbf256 100644 --- a/tests/cases/User/TestUserInternalDriver.php +++ b/tests/cases/User/Testnternal.php @@ -4,17 +4,17 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\User; /** * @covers \JKingWeb\Arsse\User * @covers \JKingWeb\Arsse\User\Internal\Driver * @covers \JKingWeb\Arsse\User\Internal\InternalFunctions */ -class TestUserInternalDriver extends Test\AbstractTest { - use Test\User\CommonTests; +class TestInternal extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\User\CommonTests; const USER1 = "john.doe@example.com"; const USER2 = "jane.doe@example.com"; - public $drv = User\Internal\Driver::class; + public $drv = \JKingWeb\Arsse\User\Internal\Driver::class; } diff --git a/tests/lib/Database/Setup.php b/tests/lib/Database/Setup.php index 4142818..42abc75 100644 --- a/tests/lib/Database/Setup.php +++ b/tests/lib/Database/Setup.php @@ -89,7 +89,7 @@ trait Setup { 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"); $row = array_combine($cols, $row); - foreach($data as $index => $test) { + foreach ($data as $index => $test) { foreach ($test as $col => $value) { switch ($types[$col]) { case "datetime": @@ -106,7 +106,7 @@ trait Setup { break; } } - if($row===$test) { + if ($row===$test) { $data[$index] = $test; break; } diff --git a/tests/phpunit.xml b/tests/phpunit.xml index 37e5bbe..12cf9bc 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -21,9 +21,9 @@ cases/Exception/TestException.php - cases/Lang/TestLang.php - cases/Lang/TestLangComplex.php - cases/Lang/TestLangErrors.php + cases/Lang/TestBasic.php + cases/Lang/TestComplex.php + cases/Lang/TestErrors.php cases/Conf/TestConf.php @@ -33,24 +33,24 @@ cases/Misc/TestContext.php - cases/User/TestUserMockInternal.php - cases/User/TestUserMockExternal.php - cases/User/TestUserInternalDriver.php + cases/User/TestMockInternal.php + cases/User/TestMockExternal.php + cases/User/TestInternal.php cases/User/TestAuthorization.php - cases/Feed/TestFeedFetching.php + cases/Feed/TestFetching.php cases/Feed/TestFeed.php cases/Db/TestTransaction.php cases/Db/TestResultAggregate.php cases/Db/TestResultEmpty.php - cases/Db/SQLite3/TestDbResultSQLite3.php - cases/Db/SQLite3/TestDbStatementSQLite3.php - cases/Db/SQLite3/TestDbDriverCreationSQLite3.php - cases/Db/SQLite3/TestDbDriverSQLite3.php - cases/Db/SQLite3/TestDbUpdateSQLite3.php + cases/Db/SQLite3/TestResult.php + cases/Db/SQLite3/TestStatement.php + cases/Db/SQLite3/TestCreation.php + cases/Db/SQLite3/TestDriver.php + cases/Db/SQLite3/TestUpdate.php cases/Db/SQLite3PDO/TestDbResultSQLite3PDO.php cases/Db/SQLite3PDO/TestDbStatementSQLite3PDO.php @@ -59,16 +59,16 @@ cases/Db/SQLite3PDO/TestDbUpdateSQLite3PDO.php - cases/Db/SQLite3/Database/TestDatabaseMiscellanySQLite3.php - cases/Db/SQLite3/Database/TestDatabaseMetaSQLite3.php - cases/Db/SQLite3/Database/TestDatabaseUserSQLite3.php - cases/Db/SQLite3/Database/TestDatabaseSessionSQLite3.php - cases/Db/SQLite3/Database/TestDatabaseFolderSQLite3.php - cases/Db/SQLite3/Database/TestDatabaseFeedSQLite3.php - cases/Db/SQLite3/Database/TestDatabaseSubscriptionSQLite3.php - cases/Db/SQLite3/Database/TestDatabaseArticleSQLite3.php - cases/Db/SQLite3/Database/TestDatabaseLabelSQLite3.php - cases/Db/SQLite3/Database/TestDatabaseCleanupSQLite3.php + cases/Db/SQLite3/Database/TestMiscellany.php + cases/Db/SQLite3/Database/TestMeta.php + cases/Db/SQLite3/Database/TestUser.php + cases/Db/SQLite3/Database/TestSession.php + cases/Db/SQLite3/Database/TestFolder.php + cases/Db/SQLite3/Database/TestFeed.php + cases/Db/SQLite3/Database/TestSubscription.php + cases/Db/SQLite3/Database/TestArticle.php + cases/Db/SQLite3/Database/TestLabel.php + cases/Db/SQLite3/Database/TestCleanup.php cases/Db/SQLite3PDO/Database/TestDatabaseMiscellanySQLite3PDO.php cases/Db/SQLite3PDO/Database/TestDatabaseUserSQLite3PDO.php @@ -82,12 +82,12 @@ - cases/REST/NextCloudNews/TestNCNVersionDiscovery.php - cases/REST/NextCloudNews/TestNCNV1_2.php + cases/REST/NextCloudNews/TestVersions.php + cases/REST/NextCloudNews/TestV1_2.php - cases/REST/TinyTinyRSS/TestTinyTinyAPI.php - cases/REST/TinyTinyRSS/TestTinyTinyIcon.php + cases/REST/TinyTinyRSS/TestAPI.php + cases/REST/TinyTinyRSS/TestIcon.php From a5318d1b12201475ad6b4d0ae6d5bbd20dc620b0 Mon Sep 17 00:00:00 2001 From: "J. King" Date: Fri, 22 Dec 2017 11:45:24 -0500 Subject: [PATCH 07/16] Part 2 --- tests/cases/Db/SQLite3/TestDriver.php | 6 ++-- tests/cases/Db/SQLite3/TestResult.php | 4 +-- tests/cases/Db/SQLite3/TestStatement.php | 4 +-- tests/cases/Db/SQLite3/TestUpdate.php | 2 +- .../Db/SQLite3PDO/Database/TestArticle.php | 14 +++++++++ .../Db/SQLite3PDO/Database/TestCleanup.php | 14 +++++++++ .../TestDatabaseArticleSQLite3PDO.php | 16 ---------- .../TestDatabaseCleanupSQLite3PDO.php | 16 ---------- .../Database/TestDatabaseFeedSQLite3PDO.php | 16 ---------- .../Database/TestDatabaseFolderSQLite3PDO.php | 16 ---------- .../Database/TestDatabaseLabelSQLite3PDO.php | 12 -------- .../Database/TestDatabaseMetaSQLite3PDO.php | 16 ---------- .../TestDatabaseMiscellanySQLite3PDO.php | 16 ---------- .../TestDatabaseSessionSQLite3PDO.php | 12 -------- .../TestDatabaseSubscriptionSQLite3PDO.php | 16 ---------- .../Database/TestDatabaseUserSQLite3PDO.php | 16 ---------- .../cases/Db/SQLite3PDO/Database/TestFeed.php | 14 +++++++++ .../Db/SQLite3PDO/Database/TestFolder.php | 14 +++++++++ .../Db/SQLite3PDO/Database/TestLabel.php | 10 +++++++ .../cases/Db/SQLite3PDO/Database/TestMeta.php | 14 +++++++++ .../Db/SQLite3PDO/Database/TestMiscellany.php | 14 +++++++++ .../Db/SQLite3PDO/Database/TestSession.php | 10 +++++++ .../SQLite3PDO/Database/TestSubscription.php | 14 +++++++++ .../cases/Db/SQLite3PDO/Database/TestUser.php | 14 +++++++++ ...reationSQLite3PDO.php => TestCreation.php} | 5 ++-- ...tDbDriverSQLite3PDO.php => TestDriver.php} | 21 +++++++++----- ...tDbResultSQLite3PDO.php => TestResult.php} | 28 ++++++++++-------- ...tementSQLite3PDO.php => TestStatement.php} | 14 +++++---- ...tDbUpdateSQLite3PDO.php => TestUpdate.php} | 15 ++++++---- tests/lib/Database/DriverSQLite3.php | 2 +- tests/phpunit.xml | 29 ++++++++++--------- 31 files changed, 207 insertions(+), 207 deletions(-) create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestArticle.php create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestCleanup.php delete mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseArticleSQLite3PDO.php delete mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseCleanupSQLite3PDO.php delete mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseFeedSQLite3PDO.php delete mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseFolderSQLite3PDO.php delete mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseLabelSQLite3PDO.php delete mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseMetaSQLite3PDO.php delete mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseMiscellanySQLite3PDO.php delete mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseSessionSQLite3PDO.php delete mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseSubscriptionSQLite3PDO.php delete mode 100644 tests/cases/Db/SQLite3PDO/Database/TestDatabaseUserSQLite3PDO.php create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestFeed.php create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestFolder.php create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestLabel.php create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestMeta.php create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestMiscellany.php create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestSession.php create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestSubscription.php create mode 100644 tests/cases/Db/SQLite3PDO/Database/TestUser.php rename tests/cases/Db/SQLite3PDO/{TestDbDriverCreationSQLite3PDO.php => TestCreation.php} (97%) rename tests/cases/Db/SQLite3PDO/{TestDbDriverSQLite3PDO.php => TestDriver.php} (96%) rename tests/cases/Db/SQLite3PDO/{TestDbResultSQLite3PDO.php => TestResult.php} (84%) rename tests/cases/Db/SQLite3PDO/{TestDbStatementSQLite3PDO.php => TestStatement.php} (90%) rename tests/cases/Db/SQLite3PDO/{TestDbUpdateSQLite3PDO.php => TestUpdate.php} (90%) diff --git a/tests/cases/Db/SQLite3/TestDriver.php b/tests/cases/Db/SQLite3/TestDriver.php index d2e602c..6453326 100644 --- a/tests/cases/Db/SQLite3/TestDriver.php +++ b/tests/cases/Db/SQLite3/TestDriver.php @@ -10,8 +10,8 @@ use JKingWeb\Arsse\Arsse; use JKingWeb\Arsse\Conf; use JKingWeb\Arsse\Database; use JKingWeb\Arsse\Db\SQLite3\Driver; -use JKingWeb\Arsse\Db\SQLite3\Result; -use JKingWeb\Arsse\Db\SQLite3\Statement; +use JKingWeb\Arsse\Db\Result; +use JKingWeb\Arsse\Db\Statement; /** * @covers \JKingWeb\Arsse\Db\SQLite3\Driver @@ -22,7 +22,7 @@ class TestDriver extends \JKingWeb\Arsse\Test\AbstractTest { protected $ch; public function setUp() { - if (!Db\SQLite3\Driver::requirementsMet()) { + if (!Driver::requirementsMet()) { $this->markTestSkipped("SQLite extension not loaded"); } $this->clearData(); diff --git a/tests/cases/Db/SQLite3/TestResult.php b/tests/cases/Db/SQLite3/TestResult.php index 3b841f1..7c302a6 100644 --- a/tests/cases/Db/SQLite3/TestResult.php +++ b/tests/cases/Db/SQLite3/TestResult.php @@ -13,10 +13,10 @@ class TestResult extends \JKingWeb\Arsse\Test\AbstractTest { protected $c; public function setUp() { - $this->clearData(); - if (!Db\SQLite3\Driver::requirementsMet()) { + if (!\JKingWeb\Arsse\Db\SQLite3\Driver::requirementsMet()) { $this->markTestSkipped("SQLite extension not loaded"); } + $this->clearData(); $c = new \SQLite3(":memory:"); $c->enableExceptions(true); $this->c = $c; diff --git a/tests/cases/Db/SQLite3/TestStatement.php b/tests/cases/Db/SQLite3/TestStatement.php index 7e722c2..b139f08 100644 --- a/tests/cases/Db/SQLite3/TestStatement.php +++ b/tests/cases/Db/SQLite3/TestStatement.php @@ -18,10 +18,10 @@ class TestStatement extends \JKingWeb\Arsse\Test\AbstractTest { protected static $imp = \JKingWeb\Arsse\Db\SQLite3\Statement::class; public function setUp() { - $this->clearData(); - if (!extension_loaded("sqlite3")) { + if (!\JKingWeb\Arsse\Db\SQLite3\Driver::requirementsMet()) { $this->markTestSkipped("SQLite extension not loaded"); } + $this->clearData(); $c = new \SQLite3(":memory:"); $c->enableExceptions(true); $this->c = $c; diff --git a/tests/cases/Db/SQLite3/TestUpdate.php b/tests/cases/Db/SQLite3/TestUpdate.php index 3767d75..d6c8fc3 100644 --- a/tests/cases/Db/SQLite3/TestUpdate.php +++ b/tests/cases/Db/SQLite3/TestUpdate.php @@ -26,7 +26,7 @@ class TestUpdate extends \JKingWeb\Arsse\Test\AbstractTest { const MINIMAL2 = "pragma user_version=2"; public function setUp(Conf $conf = null) { - if (!extension_loaded("sqlite3")) { + if (!Driver::requirementsMet()) { $this->markTestSkipped("SQLite extension not loaded"); } $this->clearData(); diff --git a/tests/cases/Db/SQLite3PDO/Database/TestArticle.php b/tests/cases/Db/SQLite3PDO/Database/TestArticle.php new file mode 100644 index 0000000..ac63498 --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestArticle.php @@ -0,0 +1,14 @@ + */ +class TestArticle extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; + use \JKingWeb\Arsse\Test\Database\SeriesArticle; +} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestCleanup.php b/tests/cases/Db/SQLite3PDO/Database/TestCleanup.php new file mode 100644 index 0000000..76e4530 --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestCleanup.php @@ -0,0 +1,14 @@ + */ +class TestCleanup extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; + use \JKingWeb\Arsse\Test\Database\SeriesCleanup; +} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseArticleSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseArticleSQLite3PDO.php deleted file mode 100644 index a904781..0000000 --- a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseArticleSQLite3PDO.php +++ /dev/null @@ -1,16 +0,0 @@ - - * @group optional */ -class TestDatabaseArticleSQLite3PDO extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3PDO; - use Test\Database\SeriesArticle; -} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseCleanupSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseCleanupSQLite3PDO.php deleted file mode 100644 index 8eb5ef7..0000000 --- a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseCleanupSQLite3PDO.php +++ /dev/null @@ -1,16 +0,0 @@ - - * @group optional */ -class TestDatabaseCleanupSQLite3PDO extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3PDO; - use Test\Database\SeriesCleanup; -} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseFeedSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseFeedSQLite3PDO.php deleted file mode 100644 index 07ed2ab..0000000 --- a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseFeedSQLite3PDO.php +++ /dev/null @@ -1,16 +0,0 @@ - - * @group optional */ -class TestDatabaseFeedSQLite3PDO extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3PDO; - use Test\Database\SeriesFeed; -} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseFolderSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseFolderSQLite3PDO.php deleted file mode 100644 index d348de9..0000000 --- a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseFolderSQLite3PDO.php +++ /dev/null @@ -1,16 +0,0 @@ - - * @group optional */ -class TestDatabaseFolderSQLite3PDO extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3PDO; - use Test\Database\SeriesFolder; -} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseLabelSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseLabelSQLite3PDO.php deleted file mode 100644 index 3d367af..0000000 --- a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseLabelSQLite3PDO.php +++ /dev/null @@ -1,12 +0,0 @@ - - * @group optional */ -class TestDatabaseLabelSQLite3PDO extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3PDO; - use Test\Database\SeriesLabel; -} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseMetaSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseMetaSQLite3PDO.php deleted file mode 100644 index 8f8ad5e..0000000 --- a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseMetaSQLite3PDO.php +++ /dev/null @@ -1,16 +0,0 @@ - - * @group optional */ -class TestDatabaseMetaSQLite3PDO extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3PDO; - use Test\Database\SeriesMeta; -} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseMiscellanySQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseMiscellanySQLite3PDO.php deleted file mode 100644 index 2e1d01e..0000000 --- a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseMiscellanySQLite3PDO.php +++ /dev/null @@ -1,16 +0,0 @@ - - * @group optional */ -class TestDatabaseMiscellanySQLite3PDO extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3PDO; - use Test\Database\SeriesMiscellany; -} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseSessionSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseSessionSQLite3PDO.php deleted file mode 100644 index bd0a857..0000000 --- a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseSessionSQLite3PDO.php +++ /dev/null @@ -1,12 +0,0 @@ - - * @group optional */ -class TestDatabaseSessionSQLite3PDO extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3PDO; - use Test\Database\SeriesSession; -} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseSubscriptionSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseSubscriptionSQLite3PDO.php deleted file mode 100644 index 99ec86c..0000000 --- a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseSubscriptionSQLite3PDO.php +++ /dev/null @@ -1,16 +0,0 @@ - - * @group optional */ -class TestDatabaseSubscriptionSQLite3PDO extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3PDO; - use Test\Database\SeriesSubscription; -} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseUserSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/Database/TestDatabaseUserSQLite3PDO.php deleted file mode 100644 index ef5ec44..0000000 --- a/tests/cases/Db/SQLite3PDO/Database/TestDatabaseUserSQLite3PDO.php +++ /dev/null @@ -1,16 +0,0 @@ - - * @group optional */ -class TestDatabaseUserSQLite3PDO extends Test\AbstractTest { - use Test\Database\Setup; - use Test\Database\DriverSQLite3PDO; - use Test\Database\SeriesUser; -} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestFeed.php b/tests/cases/Db/SQLite3PDO/Database/TestFeed.php new file mode 100644 index 0000000..9ffc733 --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestFeed.php @@ -0,0 +1,14 @@ + */ +class TestFeed extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; + use \JKingWeb\Arsse\Test\Database\SeriesFeed; +} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestFolder.php b/tests/cases/Db/SQLite3PDO/Database/TestFolder.php new file mode 100644 index 0000000..30cff60 --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestFolder.php @@ -0,0 +1,14 @@ + */ +class TestFolder extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; + use \JKingWeb\Arsse\Test\Database\SeriesFolder; +} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestLabel.php b/tests/cases/Db/SQLite3PDO/Database/TestLabel.php new file mode 100644 index 0000000..38bd123 --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestLabel.php @@ -0,0 +1,10 @@ + */ +class TestLabel extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; + use \JKingWeb\Arsse\Test\Database\SeriesLabel; +} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestMeta.php b/tests/cases/Db/SQLite3PDO/Database/TestMeta.php new file mode 100644 index 0000000..7d3e7ed --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestMeta.php @@ -0,0 +1,14 @@ + */ +class TestMeta extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; + use \JKingWeb\Arsse\Test\Database\SeriesMeta; +} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestMiscellany.php b/tests/cases/Db/SQLite3PDO/Database/TestMiscellany.php new file mode 100644 index 0000000..cc8ca60 --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestMiscellany.php @@ -0,0 +1,14 @@ + */ +class TestMiscellany extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; + use \JKingWeb\Arsse\Test\Database\SeriesMiscellany; +} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestSession.php b/tests/cases/Db/SQLite3PDO/Database/TestSession.php new file mode 100644 index 0000000..dbc71fb --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestSession.php @@ -0,0 +1,10 @@ + */ +class TestSession extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; + use \JKingWeb\Arsse\Test\Database\SeriesSession; +} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestSubscription.php b/tests/cases/Db/SQLite3PDO/Database/TestSubscription.php new file mode 100644 index 0000000..0205b48 --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestSubscription.php @@ -0,0 +1,14 @@ + */ +class TestSubscription extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; + use \JKingWeb\Arsse\Test\Database\SeriesSubscription; +} diff --git a/tests/cases/Db/SQLite3PDO/Database/TestUser.php b/tests/cases/Db/SQLite3PDO/Database/TestUser.php new file mode 100644 index 0000000..b77822d --- /dev/null +++ b/tests/cases/Db/SQLite3PDO/Database/TestUser.php @@ -0,0 +1,14 @@ + */ +class TestUser extends \JKingWeb\Arsse\Test\AbstractTest { + use \JKingWeb\Arsse\Test\Database\Setup; + use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; + use \JKingWeb\Arsse\Test\Database\SeriesUser; +} diff --git a/tests/cases/Db/SQLite3PDO/TestDbDriverCreationSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/TestCreation.php similarity index 97% rename from tests/cases/Db/SQLite3PDO/TestDbDriverCreationSQLite3PDO.php rename to tests/cases/Db/SQLite3PDO/TestCreation.php index 436d4e2..34cb824 100644 --- a/tests/cases/Db/SQLite3PDO/TestDbDriverCreationSQLite3PDO.php +++ b/tests/cases/Db/SQLite3PDO/TestCreation.php @@ -4,9 +4,10 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Db\SQLite3PDO; use JKingWeb\Arsse\Arsse; +use JKingWeb\Arsse\Conf; use JKingWeb\Arsse\Db\SQLite3\PDODriver as Driver; use org\bovigo\vfs\vfsStream; use Phake; @@ -15,7 +16,7 @@ use Phake; * @covers \JKingWeb\Arsse\Db\SQLite3\PDODriver * @covers \JKingWeb\Arsse\Db\PDODriver * @covers \JKingWeb\Arsse\Db\PDOError */ -class TestDbDriverCreationSQLite3PDO extends Test\AbstractTest { +class TestCreation extends \JKingWeb\Arsse\Test\AbstractTest { protected $data; protected $drv; protected $ch; diff --git a/tests/cases/Db/SQLite3PDO/TestDbDriverSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/TestDriver.php similarity index 96% rename from tests/cases/Db/SQLite3PDO/TestDbDriverSQLite3PDO.php rename to tests/cases/Db/SQLite3PDO/TestDriver.php index bb96112..1fec09c 100644 --- a/tests/cases/Db/SQLite3PDO/TestDbDriverSQLite3PDO.php +++ b/tests/cases/Db/SQLite3PDO/TestDriver.php @@ -4,28 +4,35 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Db\SQLite3PDO; + +use JKingWeb\Arsse\Arsse; +use JKingWeb\Arsse\Conf; +use JKingWeb\Arsse\Database; +use JKingWeb\Arsse\Db\SQLite3\PDODriver; +use JKingWeb\Arsse\Db\Result; +use JKingWeb\Arsse\Db\Statement; /** * @covers \JKingWeb\Arsse\Db\SQLite3\PDODriver * @covers \JKingWeb\Arsse\Db\PDODriver * @covers \JKingWeb\Arsse\Db\PDOError */ -class TestDbDriverSQLite3PDO extends Test\AbstractTest { +class TestDriver extends \JKingWeb\Arsse\Test\AbstractTest { protected $data; protected $drv; protected $ch; public function setUp() { - if (!Db\SQLite3\PDODriver::requirementsMet()) { + if (!PDODriver::requirementsMet()) { $this->markTestSkipped("PDO-SQLite extension not loaded"); } $this->clearData(); $conf = new Conf(); Arsse::$conf = $conf; - $conf->dbDriver = Db\SQLite3\PDODriver::class; + $conf->dbDriver = PDODriver::class; $conf->dbSQLite3Timeout = 0; $conf->dbSQLite3File = tempnam(sys_get_temp_dir(), 'ook'); - $this->drv = new Db\SQLite3\PDODriver(); + $this->drv = new PDODriver(); $this->ch = new \PDO("sqlite:".Arsse::$conf->dbSQLite3File, "", "", [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]); } @@ -80,7 +87,7 @@ class TestDbDriverSQLite3PDO extends Test\AbstractTest { } public function testMakeAValidQuery() { - $this->assertInstanceOf(Db\Result::class, $this->drv->query("SELECT 1")); + $this->assertInstanceOf(Result::class, $this->drv->query("SELECT 1")); } public function testMakeAnInvalidQuery() { @@ -108,7 +115,7 @@ class TestDbDriverSQLite3PDO extends Test\AbstractTest { public function testPrepareAValidQuery() { $s = $this->drv->prepare("SELECT ?, ?", "int", "int"); - $this->assertInstanceOf(Db\Statement::class, $s); + $this->assertInstanceOf(Statement::class, $s); } public function testPrepareAnInvalidQuery() { diff --git a/tests/cases/Db/SQLite3PDO/TestDbResultSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/TestResult.php similarity index 84% rename from tests/cases/Db/SQLite3PDO/TestDbResultSQLite3PDO.php rename to tests/cases/Db/SQLite3PDO/TestResult.php index fbc745f..7537b87 100644 --- a/tests/cases/Db/SQLite3PDO/TestDbResultSQLite3PDO.php +++ b/tests/cases/Db/SQLite3PDO/TestResult.php @@ -4,17 +4,21 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Db\SQLite3PDO; + +use JKingWeb\Arsse\Db\Result; +use JKingWeb\Arsse\Db\PDOResult; +use JKingWeb\Arsse\Db\SQLite3\PDODriver; /** @covers \JKingWeb\Arsse\Db\PDOResult */ -class TestDbResultSQLite3PDO extends Test\AbstractTest { +class TestResult extends \JKingWeb\Arsse\Test\AbstractTest { protected $c; public function setUp() { - $this->clearData(); - if (!Db\SQLite3\PDODriver::requirementsMet()) { + if (!PDODriver::requirementsMet()) { $this->markTestSkipped("PDO-SQLite extension not loaded"); } + $this->clearData(); $c = new \PDO("sqlite::memory:", "", "", [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]); $this->c = $c; } @@ -26,7 +30,7 @@ class TestDbResultSQLite3PDO extends Test\AbstractTest { public function testConstructResult() { $set = $this->c->query("SELECT 1"); - $this->assertInstanceOf(Db\Result::class, new Db\PDOResult($set)); + $this->assertInstanceOf(Result::class, new PDOResult($set)); } public function testGetChangeCountAndLastInsertId() { @@ -34,7 +38,7 @@ class TestDbResultSQLite3PDO extends Test\AbstractTest { $set = $this->c->query("INSERT INTO test(col) values(1)"); $rows = $set->rowCount(); $id = $this->c->lastInsertID(); - $r = new Db\PDOResult($set, [$rows,$id]); + $r = new PDOResult($set, [$rows,$id]); $this->assertSame((int) $rows, $r->changes()); $this->assertSame((int) $id, $r->lastId()); } @@ -42,7 +46,7 @@ class TestDbResultSQLite3PDO extends Test\AbstractTest { public function testIterateOverResults() { $set = $this->c->query("SELECT 1 as col union select 2 as col union select 3 as col"); $rows = []; - foreach (new Db\PDOResult($set) as $index => $row) { + foreach (new PDOResult($set) as $index => $row) { $rows[$index] = $row['col']; } $this->assertSame([0 => "1", 1 => "2", 2 => "3"], $rows); @@ -51,7 +55,7 @@ class TestDbResultSQLite3PDO extends Test\AbstractTest { public function testIterateOverResultsTwice() { $set = $this->c->query("SELECT 1 as col union select 2 as col union select 3 as col"); $rows = []; - $test = new Db\PDOResult($set); + $test = new PDOResult($set); foreach ($test as $row) { $rows[] = $row['col']; } @@ -64,7 +68,7 @@ class TestDbResultSQLite3PDO extends Test\AbstractTest { public function testGetSingleValues() { $set = $this->c->query("SELECT 1867 as year union select 1970 as year union select 2112 as year"); - $test = new Db\PDOResult($set); + $test = new PDOResult($set); $this->assertEquals(1867, $test->getValue()); $this->assertEquals(1970, $test->getValue()); $this->assertEquals(2112, $test->getValue()); @@ -73,7 +77,7 @@ class TestDbResultSQLite3PDO extends Test\AbstractTest { public function testGetFirstValuesOnly() { $set = $this->c->query("SELECT 1867 as year, 19 as century union select 1970 as year, 20 as century union select 2112 as year, 22 as century"); - $test = new Db\PDOResult($set); + $test = new PDOResult($set); $this->assertEquals(1867, $test->getValue()); $this->assertEquals(1970, $test->getValue()); $this->assertEquals(2112, $test->getValue()); @@ -86,7 +90,7 @@ class TestDbResultSQLite3PDO extends Test\AbstractTest { ['album' => '2112', 'track' => '2112'], ['album' => 'Clockwork Angels', 'track' => 'The Wreckers'], ]; - $test = new Db\PDOResult($set); + $test = new PDOResult($set); $this->assertEquals($rows[0], $test->getRow()); $this->assertEquals($rows[1], $test->getRow()); $this->assertSame(null, $test->getRow()); @@ -98,7 +102,7 @@ class TestDbResultSQLite3PDO extends Test\AbstractTest { ['album' => '2112', 'track' => '2112'], ['album' => 'Clockwork Angels', 'track' => 'The Wreckers'], ]; - $test = new Db\PDOResult($set); + $test = new PDOResult($set); $this->assertEquals($rows, $test->getAll()); } } diff --git a/tests/cases/Db/SQLite3PDO/TestDbStatementSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/TestStatement.php similarity index 90% rename from tests/cases/Db/SQLite3PDO/TestDbStatementSQLite3PDO.php rename to tests/cases/Db/SQLite3PDO/TestStatement.php index 76ca24a..d9d6b02 100644 --- a/tests/cases/Db/SQLite3PDO/TestDbStatementSQLite3PDO.php +++ b/tests/cases/Db/SQLite3PDO/TestStatement.php @@ -4,23 +4,25 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Db\SQLite3PDO; use JKingWeb\Arsse\Db\Statement; +use JKingWeb\Arsse\Db\PDOStatement; +use JKingWeb\Arsse\Db\SQLite3\PDODriver; /** * @covers \JKingWeb\Arsse\Db\PDOStatement * @covers \JKingWeb\Arsse\Db\PDOError */ -class TestDbStatementSQLite3PDO extends Test\AbstractTest { +class TestStatement extends \JKingWeb\Arsse\Test\AbstractTest { protected $c; - protected static $imp = Db\PDOStatement::class; + protected static $imp = \JKingWeb\Arsse\Db\PDOStatement::class; public function setUp() { - $this->clearData(); - if (!Db\SQLite3\PDODriver::requirementsMet()) { + if (!PDODriver::requirementsMet()) { $this->markTestSkipped("PDO-SQLite extension not loaded"); } + $this->clearData(); $c = new \PDO("sqlite::memory:", "", "", [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]); $this->c = $c; } @@ -46,7 +48,7 @@ class TestDbStatementSQLite3PDO extends Test\AbstractTest { public function testConstructStatement() { $nativeStatement = $this->c->prepare("SELECT ? as value"); - $this->assertInstanceOf(Statement::class, new Db\PDOStatement($this->c, $nativeStatement)); + $this->assertInstanceOf(Statement::class, new PDOStatement($this->c, $nativeStatement)); } public function testBindMissingValue() { diff --git a/tests/cases/Db/SQLite3PDO/TestDbUpdateSQLite3PDO.php b/tests/cases/Db/SQLite3PDO/TestUpdate.php similarity index 90% rename from tests/cases/Db/SQLite3PDO/TestDbUpdateSQLite3PDO.php rename to tests/cases/Db/SQLite3PDO/TestUpdate.php index b75ba7d..9c8df84 100644 --- a/tests/cases/Db/SQLite3PDO/TestDbUpdateSQLite3PDO.php +++ b/tests/cases/Db/SQLite3PDO/TestUpdate.php @@ -4,14 +4,19 @@ * See LICENSE and AUTHORS files for details */ declare(strict_types=1); -namespace JKingWeb\Arsse; +namespace JKingWeb\Arsse\TestCase\Db\SQLite3PDO; +use JKingWeb\Arsse\Arsse; +use JKingWeb\Arsse\Conf; +use JKingWeb\Arsse\Database; +use JKingWeb\Arsse\Db\Exception; +use JKingWeb\Arsse\Db\SQLite3\PDODriver; use org\bovigo\vfs\vfsStream; /** * @covers \JKingWeb\Arsse\Db\SQLite3\PDODriver * @covers \JKingWeb\Arsse\Db\PDOError */ -class TestDbUpdateSQLite3PDO extends Test\AbstractTest { +class TestUpdate extends \JKingWeb\Arsse\Test\AbstractTest { protected $data; protected $drv; protected $vfs; @@ -21,7 +26,7 @@ class TestDbUpdateSQLite3PDO extends Test\AbstractTest { const MINIMAL2 = "pragma user_version=2"; public function setUp(Conf $conf = null) { - if (!Db\SQLite3\PDODriver::requirementsMet()) { + if (!PDODriver::requirementsMet()) { $this->markTestSkipped("PDO-SQLite extension not loaded"); } $this->clearData(); @@ -29,12 +34,12 @@ class TestDbUpdateSQLite3PDO extends Test\AbstractTest { if (!$conf) { $conf = new Conf(); } - $conf->dbDriver = Db\SQLite3\PDODriver::class; + $conf->dbDriver = PDODriver::class; $conf->dbSQLite3File = ":memory:"; Arsse::$conf = $conf; $this->base = $this->vfs->url(); $this->path = $this->base."/SQLite3/"; - $this->drv = new Db\SQLite3\PDODriver(); + $this->drv = new PDODriver(); } public function tearDown() { diff --git a/tests/lib/Database/DriverSQLite3.php b/tests/lib/Database/DriverSQLite3.php index 04dc887..1b76eea 100644 --- a/tests/lib/Database/DriverSQLite3.php +++ b/tests/lib/Database/DriverSQLite3.php @@ -11,7 +11,7 @@ use JKingWeb\Arsse\Db\SQLite3\Driver; trait DriverSQLite3 { public function setUpDriver() { - if (!extension_loaded("sqlite3")) { + if (!Driver::requirementsMet()) { $this->markTestSkipped("SQLite extension not loaded"); } Arsse::$conf->dbSQLite3File = ":memory:"; diff --git a/tests/phpunit.xml b/tests/phpunit.xml index 12cf9bc..9696d57 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -52,11 +52,11 @@ cases/Db/SQLite3/TestDriver.php cases/Db/SQLite3/TestUpdate.php - cases/Db/SQLite3PDO/TestDbResultSQLite3PDO.php - cases/Db/SQLite3PDO/TestDbStatementSQLite3PDO.php - cases/Db/SQLite3PDO/TestDbDriverCreationSQLite3PDO.php - cases/Db/SQLite3PDO/TestDbDriverSQLite3PDO.php - cases/Db/SQLite3PDO/TestDbUpdateSQLite3PDO.php + cases/Db/SQLite3PDO/TestResult.php + cases/Db/SQLite3PDO/TestStatement.php + cases/Db/SQLite3PDO/TestCreation.php + cases/Db/SQLite3PDO/TestDriver.php + cases/Db/SQLite3PDO/TestUpdate.php cases/Db/SQLite3/Database/TestMiscellany.php @@ -70,15 +70,16 @@ cases/Db/SQLite3/Database/TestLabel.php cases/Db/SQLite3/Database/TestCleanup.php - cases/Db/SQLite3PDO/Database/TestDatabaseMiscellanySQLite3PDO.php - cases/Db/SQLite3PDO/Database/TestDatabaseUserSQLite3PDO.php - cases/Db/SQLite3PDO/Database/TestDatabaseSessionSQLite3PDO.php - cases/Db/SQLite3PDO/Database/TestDatabaseFolderSQLite3PDO.php - cases/Db/SQLite3PDO/Database/TestDatabaseFeedSQLite3PDO.php - cases/Db/SQLite3PDO/Database/TestDatabaseSubscriptionSQLite3PDO.php - cases/Db/SQLite3PDO/Database/TestDatabaseArticleSQLite3PDO.php - cases/Db/SQLite3PDO/Database/TestDatabaseLabelSQLite3PDO.php - cases/Db/SQLite3PDO/Database/TestDatabaseCleanupSQLite3PDO.php + cases/Db/SQLite3PDO/Database/TestMiscellany.php + cases/Db/SQLite3PDO/Database/TestMeta.php + cases/Db/SQLite3PDO/Database/TestUser.php + cases/Db/SQLite3PDO/Database/TestSession.php + cases/Db/SQLite3PDO/Database/TestFolder.php + cases/Db/SQLite3PDO/Database/TestFeed.php + cases/Db/SQLite3PDO/Database/TestSubscription.php + cases/Db/SQLite3PDO/Database/TestArticle.php + cases/Db/SQLite3PDO/Database/TestLabel.php + cases/Db/SQLite3PDO/Database/TestCleanup.php From 39cad91b786224aa36de2988b9126b82cac57129 Mon Sep 17 00:00:00 2001 From: "J. King" Date: Fri, 22 Dec 2017 11:51:58 -0500 Subject: [PATCH 08/16] CS fixes --- lib/Db/AbstractDriver.php | 2 +- lib/Db/PDODriver.php | 2 +- lib/Db/PDOError.php | 4 +++- lib/Db/SQLite3/Driver.php | 2 +- lib/Db/SQLite3/PDODriver.php | 2 +- tests/cases/Db/SQLite3PDO/TestStatement.php | 1 - tests/cases/Lang/TestBasic.php | 1 - tests/cases/User/TestAuthorization.php | 1 - tests/lib/Database/Setup.php | 2 +- 9 files changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/Db/AbstractDriver.php b/lib/Db/AbstractDriver.php index 624074c..2cd8969 100644 --- a/lib/Db/AbstractDriver.php +++ b/lib/Db/AbstractDriver.php @@ -13,7 +13,7 @@ abstract class AbstractDriver implements Driver { protected $transDepth = 0; protected $transStatus = []; - protected abstract function getError(): string; + abstract protected function getError(): string; /** @codeCoverageIgnore */ public function schemaVersion(): int { diff --git a/lib/Db/PDODriver.php b/lib/Db/PDODriver.php index 99dfe67..c6ec0d4 100644 --- a/lib/Db/PDODriver.php +++ b/lib/Db/PDODriver.php @@ -44,4 +44,4 @@ trait PDODriver { } return new PDOStatement($this->db, $s, $paramTypes); } -} \ No newline at end of file +} diff --git a/lib/Db/PDOError.php b/lib/Db/PDOError.php index 929fe1e..37341e4 100644 --- a/lib/Db/PDOError.php +++ b/lib/Db/PDOError.php @@ -28,9 +28,11 @@ trait PDOError { default: return [Exception::class, "engineErrorGeneral", $err[1]." - ".$err[2]]; } + // no break default: return [Exception::class, "engineErrorGeneral", $err[2]]; // @codeCoverageIgnore } + // no break default: return [Exception::class, "engineErrorGeneral", $err[0].": ".$err[2]]; // @codeCoverageIgnore } @@ -39,4 +41,4 @@ trait PDOError { public function getError(): string { return (string) $this->db->errorInfo()[2]; } -} \ No newline at end of file +} diff --git a/lib/Db/SQLite3/Driver.php b/lib/Db/SQLite3/Driver.php index 7c7beea..5b94bc0 100644 --- a/lib/Db/SQLite3/Driver.php +++ b/lib/Db/SQLite3/Driver.php @@ -82,7 +82,7 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver { } elseif (PDODriver::requirementsMet()) { return new PDODriver; } else { - throw new Exception("extMissing", self::driverName()); + throw new Exception("extMissing", self::driverName()); } } diff --git a/lib/Db/SQLite3/PDODriver.php b/lib/Db/SQLite3/PDODriver.php index a78dc24..42e1cf8 100644 --- a/lib/Db/SQLite3/PDODriver.php +++ b/lib/Db/SQLite3/PDODriver.php @@ -35,7 +35,7 @@ class PDODriver extends Driver { } elseif (Driver::requirementsMet()) { return new Driver; } else { - throw new Exception("extMissing", self::driverName()); + throw new Exception("extMissing", self::driverName()); } } diff --git a/tests/cases/Db/SQLite3PDO/TestStatement.php b/tests/cases/Db/SQLite3PDO/TestStatement.php index d9d6b02..5eb6cb9 100644 --- a/tests/cases/Db/SQLite3PDO/TestStatement.php +++ b/tests/cases/Db/SQLite3PDO/TestStatement.php @@ -14,7 +14,6 @@ use JKingWeb\Arsse\Db\SQLite3\PDODriver; * @covers \JKingWeb\Arsse\Db\PDOStatement * @covers \JKingWeb\Arsse\Db\PDOError */ class TestStatement extends \JKingWeb\Arsse\Test\AbstractTest { - protected $c; protected static $imp = \JKingWeb\Arsse\Db\PDOStatement::class; diff --git a/tests/cases/Lang/TestBasic.php b/tests/cases/Lang/TestBasic.php index fbd7129..351ede8 100644 --- a/tests/cases/Lang/TestBasic.php +++ b/tests/cases/Lang/TestBasic.php @@ -9,7 +9,6 @@ namespace JKingWeb\Arsse\TestCase\Lang; use JKingWeb\Arsse\Lang as TestClass; use org\bovigo\vfs\vfsStream; - /** @covers \JKingWeb\Arsse\Lang */ class TestBasic extends \JKingWeb\Arsse\Test\AbstractTest { use \JKingWeb\Arsse\Test\Lang\Setup; diff --git a/tests/cases/User/TestAuthorization.php b/tests/cases/User/TestAuthorization.php index 5692f01..088f674 100644 --- a/tests/cases/User/TestAuthorization.php +++ b/tests/cases/User/TestAuthorization.php @@ -6,7 +6,6 @@ declare(strict_types=1); namespace JKingWeb\Arsse\TestCase\User; - use JKingWeb\Arsse\Arsse; use JKingWeb\Arsse\Conf; use JKingWeb\Arsse\User; diff --git a/tests/lib/Database/Setup.php b/tests/lib/Database/Setup.php index 42abc75..c9b982f 100644 --- a/tests/lib/Database/Setup.php +++ b/tests/lib/Database/Setup.php @@ -103,7 +103,7 @@ trait Setup { break; case "bool": $test[$col] = (int) ValueInfo::normalize($value, ValueInfo::T_BOOL | ValueInfo::M_DROP | valueInfo::M_NULL); - break; + break; } } if ($row===$test) { From bc9ffa0e17cd076bd51a24e707f6d9133fcf392c Mon Sep 17 00:00:00 2001 From: "J. King" Date: Sat, 30 Dec 2017 15:59:45 -0500 Subject: [PATCH 09/16] Rewrite SQLite 3 type binding tests --- lib/Db/AbstractStatement.php | 11 - lib/Db/PDOStatement.php | 3 - lib/Db/SQLite3/Statement.php | 3 - lib/Db/Statement.php | 4 - tests/cases/Db/SQLite3/TestStatement.php | 209 ++++++++++++++++++- tests/lib/Db/BindingTests.php | 253 ----------------------- 6 files changed, 207 insertions(+), 276 deletions(-) delete mode 100644 tests/lib/Db/BindingTests.php diff --git a/lib/Db/AbstractStatement.php b/lib/Db/AbstractStatement.php index 1269a60..8390194 100644 --- a/lib/Db/AbstractStatement.php +++ b/lib/Db/AbstractStatement.php @@ -50,22 +50,11 @@ abstract class AbstractStatement implements Statement { protected function cast($v, string $t, bool $nullable) { switch ($t) { - case "date": - if (is_null($v) && !$nullable) { - $v = 0; - } - return Date::transform($v, "date"); - case "time": - if (is_null($v) && !$nullable) { - $v = 0; - } - return Date::transform($v, "time"); case "datetime": if (is_null($v) && !$nullable) { $v = 0; } return Date::transform($v, "sql"); - case "null": case "integer": case "float": case "binary": diff --git a/lib/Db/PDOStatement.php b/lib/Db/PDOStatement.php index a8d459e..6a2534d 100644 --- a/lib/Db/PDOStatement.php +++ b/lib/Db/PDOStatement.php @@ -10,11 +10,8 @@ class PDOStatement extends AbstractStatement { use PDOError; const BINDINGS = [ - "null" => \PDO::PARAM_NULL, "integer" => \PDO::PARAM_INT, "float" => \PDO::PARAM_STR, - "date" => \PDO::PARAM_STR, - "time" => \PDO::PARAM_STR, "datetime" => \PDO::PARAM_STR, "binary" => \PDO::PARAM_LOB, "string" => \PDO::PARAM_STR, diff --git a/lib/Db/SQLite3/Statement.php b/lib/Db/SQLite3/Statement.php index 34228ef..55da642 100644 --- a/lib/Db/SQLite3/Statement.php +++ b/lib/Db/SQLite3/Statement.php @@ -17,11 +17,8 @@ class Statement extends \JKingWeb\Arsse\Db\AbstractStatement { const SQLITE_CONSTRAINT = 19; const SQLITE_MISMATCH = 20; const BINDINGS = [ - "null" => \SQLITE3_NULL, "integer" => \SQLITE3_INTEGER, "float" => \SQLITE3_FLOAT, - "date" => \SQLITE3_TEXT, - "time" => \SQLITE3_TEXT, "datetime" => \SQLITE3_TEXT, "binary" => \SQLITE3_BLOB, "string" => \SQLITE3_TEXT, diff --git a/lib/Db/Statement.php b/lib/Db/Statement.php index f95bdfb..02677c4 100644 --- a/lib/Db/Statement.php +++ b/lib/Db/Statement.php @@ -8,16 +8,12 @@ namespace JKingWeb\Arsse\Db; interface Statement { const TYPES = [ - "null" => "null", - "nil" => "null", "int" => "integer", "integer" => "integer", "float" => "float", "double" => "float", "real" => "float", "numeric" => "float", - "date" => "date", - "time" => "time", "datetime" => "datetime", "timestamp" => "datetime", "blob" => "binary", diff --git a/tests/cases/Db/SQLite3/TestStatement.php b/tests/cases/Db/SQLite3/TestStatement.php index b139f08..4bcb326 100644 --- a/tests/cases/Db/SQLite3/TestStatement.php +++ b/tests/cases/Db/SQLite3/TestStatement.php @@ -12,8 +12,6 @@ use JKingWeb\Arsse\Db\Statement; * @covers \JKingWeb\Arsse\Db\SQLite3\Statement * @covers \JKingWeb\Arsse\Db\SQLite3\ExceptionBuilder */ class TestStatement extends \JKingWeb\Arsse\Test\AbstractTest { - use \JKingWeb\Arsse\Test\Db\BindingTests; - protected $c; protected static $imp = \JKingWeb\Arsse\Db\SQLite3\Statement::class; @@ -46,6 +44,213 @@ class TestStatement extends \JKingWeb\Arsse\Test\AbstractTest { } } + /** @dataProvider provideBindings */ + public function testBindATypedValue($value, $type, $exp) { + $typeStr = "'".str_replace("'", "''", $type)."'"; + $nativeStatement = $this->c->prepare( + "SELECT ( + (CASE WHEN substr($typeStr, 0, 7) <> 'strict ' then null else 1 end) is null + and ? is null + ) or ( + $exp = ? + ) as pass" + ); + $s = new self::$imp($this->c, $nativeStatement); + $s->rebindArray([$type, $type]); + $act = (bool) $s->run(...[$value, $value])->getRow()['pass']; + $this->assertTrue($act); + } + + public function provideBindings() { + $dateMutable = new \DateTime("Noon Today", new \DateTimezone("America/Toronto")); + $dateImmutable = new \DateTimeImmutable("Noon Today", new \DateTimezone("America/Toronto")); + $dateUTC = new \DateTime("@".$dateMutable->getTimestamp(), new \DateTimezone("UTC")); + return [ + /* input, type, expected binding as SQL fragment */ + [null, "integer", "null"], + [null, "float", "null"], + [null, "string", "null"], + [null, "binary", "null"], + [null, "datetime", "null"], + [null, "boolean", "null"], + [null, "strict integer", "0"], + [null, "strict float", "0.0"], + [null, "strict string", "''"], + [null, "strict binary", "x''"], + [null, "strict datetime", "'1970-01-01 00:00:00'"], + [null, "strict boolean", "0"], + // true + [true, "integer", "1"], + [true, "float", "1.0"], + [true, "string", "'1'"], + [true, "binary", "x'31'"], + [true, "datetime", "null"], + [true, "boolean", "1"], + [true, "strict integer", "1"], + [true, "strict float", "1.0"], + [true, "strict string", "'1'"], + [true, "strict binary", "x'31'"], + [true, "strict datetime", "'1970-01-01 00:00:01'"], + [true, "strict boolean", "1"], + // false + [false, "integer", "0"], + [false, "float", "0.0"], + [false, "string", "''"], + [false, "binary", "x''"], + [false, "datetime", "null"], + [false, "boolean", "0"], + [false, "strict integer", "0"], + [false, "strict float", "0.0"], + [false, "strict string", "''"], + [false, "strict binary", "x''"], + [false, "strict datetime", "'1970-01-01 00:00:00'"], + [false, "strict boolean", "0"], + // integer + [2112, "integer", "2112"], + [2112, "float", "2112.0"], + [2112, "string", "'2112'"], + [2112, "binary", "x'32313132'"], + [2112, "datetime", "'1970-01-01 00:35:12'"], + [2112, "boolean", "1"], + [2112, "strict integer", "2112"], + [2112, "strict float", "2112.0"], + [2112, "strict string", "'2112'"], + [2112, "strict binary", "x'32313132'"], + [2112, "strict datetime", "'1970-01-01 00:35:12'"], + [2112, "strict boolean", "1"], + // integer zero + [0, "integer", "0"], + [0, "float", "0.0"], + [0, "string", "'0'"], + [0, "binary", "x'30'"], + [0, "datetime", "'1970-01-01 00:00:00'"], + [0, "boolean", "0"], + [0, "strict integer", "0"], + [0, "strict float", "0.0"], + [0, "strict string", "'0'"], + [0, "strict binary", "x'30'"], + [0, "strict datetime", "'1970-01-01 00:00:00'"], + [0, "strict boolean", "0"], + // float + [2112.99, "integer", "2112"], + [2112.99, "float", "2112.99"], + [2112.99, "string", "'2112.99'"], + [2112.99, "binary", "x'323131322e3939'"], + [2112.99, "datetime", "'1970-01-01 00:35:12'"], + [2112.99, "boolean", "1"], + [2112.99, "strict integer", "2112"], + [2112.99, "strict float", "2112.99"], + [2112.99, "strict string", "'2112.99'"], + [2112.99, "strict binary", "x'323131322e3939'"], + [2112.99, "strict datetime", "'1970-01-01 00:35:12'"], + [2112.99, "strict boolean", "1"], + // float zero + [0.0, "integer", "0"], + [0.0, "float", "0.0"], + [0.0, "string", "'0'"], + [0.0, "binary", "x'30'"], + [0.0, "datetime", "'1970-01-01 00:00:00'"], + [0.0, "boolean", "0"], + [0.0, "strict integer", "0"], + [0.0, "strict float", "0.0"], + [0.0, "strict string", "'0'"], + [0.0, "strict binary", "x'30'"], + [0.0, "strict datetime", "'1970-01-01 00:00:00'"], + [0.0, "strict boolean", "0"], + // ASCII string + ["Random string", "integer", "0"], + ["Random string", "float", "0.0"], + ["Random string", "string", "'Random string'"], + ["Random string", "binary", "x'52616e646f6d20737472696e67'"], + ["Random string", "datetime", "null"], + ["Random string", "boolean", "1"], + ["Random string", "strict integer", "0"], + ["Random string", "strict float", "0.0"], + ["Random string", "strict string", "'Random string'"], + ["Random string", "strict binary", "x'52616e646f6d20737472696e67'"], + ["Random string", "strict datetime", "'1970-01-01 00:00:00'"], + ["Random string", "strict boolean", "1"], + // UTF-8 string + ["é", "integer", "0"], + ["é", "float", "0.0"], + ["é", "string", "char(233)"], + ["é", "binary", "x'c3a9'"], + ["é", "datetime", "null"], + ["é", "boolean", "1"], + ["é", "strict integer", "0"], + ["é", "strict float", "0.0"], + ["é", "strict string", "char(233)"], + ["é", "strict binary", "x'c3a9'"], + ["é", "strict datetime", "'1970-01-01 00:00:00'"], + ["é", "strict boolean", "1"], + // binary string + [chr(233).chr(233), "integer", "0"], + [chr(233).chr(233), "float", "0.0"], + [chr(233).chr(233), "string", "'".chr(233).chr(233)."'"], + [chr(233).chr(233), "binary", "x'e9e9'"], + [chr(233).chr(233), "datetime", "null"], + [chr(233).chr(233), "boolean", "1"], + [chr(233).chr(233), "strict integer", "0"], + [chr(233).chr(233), "strict float", "0.0"], + [chr(233).chr(233), "strict string", "'".chr(233).chr(233)."'"], + [chr(233).chr(233), "strict binary", "x'e9e9'"], + [chr(233).chr(233), "strict datetime", "'1970-01-01 00:00:00'"], + [chr(233).chr(233), "strict boolean", "1"], + // ISO 8601 date string + ["2017-01-09T13:11:17", "integer", "2017"], + ["2017-01-09T13:11:17", "float", "2017.0"], + ["2017-01-09T13:11:17", "string", "'2017-01-09T13:11:17'"], + ["2017-01-09T13:11:17", "binary", "x'323031372d30312d30395431333a31313a3137'"], + ["2017-01-09T13:11:17", "datetime", "'2017-01-09 13:11:17'"], + ["2017-01-09T13:11:17", "boolean", "1"], + ["2017-01-09T13:11:17", "strict integer", "2017"], + ["2017-01-09T13:11:17", "strict float", "2017.0"], + ["2017-01-09T13:11:17", "strict string", "'2017-01-09T13:11:17'"], + ["2017-01-09T13:11:17", "strict binary", "x'323031372d30312d30395431333a31313a3137'"], + ["2017-01-09T13:11:17", "strict datetime", "'2017-01-09 13:11:17'"], + ["2017-01-09T13:11:17", "strict boolean", "1"], + // arbitrary date string + ["Today", "integer", "0"], + ["Today", "float", "0.0"], + ["Today", "string", "'Today'"], + ["Today", "binary", "x'546f646179'"], + ["Today", "datetime", "'".date_create("Today", new \DateTimezone("UTC"))->format("Y-m-d H:i:s")."'"], + ["Today", "boolean", "1"], + ["Today", "strict integer", "0"], + ["Today", "strict float", "0.0"], + ["Today", "strict string", "'Today'"], + ["Today", "strict binary", "x'546f646179'"], + ["Today", "strict datetime", "'".date_create("Today", new \DateTimezone("UTC"))->format("Y-m-d H:i:s")."'"], + ["Today", "strict boolean", "1"], + // mutable date object + [$dateMutable, "integer", $dateUTC->getTimestamp()], + [$dateMutable, "float", $dateUTC->getTimestamp().".0"], + [$dateMutable, "string", "'".$dateUTC->format("Y-m-d H:i:s")."'"], + [$dateMutable, "binary", "x'".bin2hex($dateUTC->format("Y-m-d H:i:s"))."'"], + [$dateMutable, "datetime", "'".$dateUTC->format("Y-m-d H:i:s")."'"], + [$dateMutable, "boolean", "1"], + [$dateMutable, "strict integer", $dateUTC->getTimestamp()], + [$dateMutable, "strict float", $dateUTC->getTimestamp().".0"], + [$dateMutable, "strict string", "'".$dateUTC->format("Y-m-d H:i:s")."'"], + [$dateMutable, "strict binary", "x'".bin2hex($dateUTC->format("Y-m-d H:i:s"))."'"], + [$dateMutable, "strict datetime", "'".$dateUTC->format("Y-m-d H:i:s")."'"], + [$dateMutable, "strict boolean", "1"], + // immutable date object + [$dateImmutable, "integer", $dateUTC->getTimestamp()], + [$dateImmutable, "float", $dateUTC->getTimestamp().".0"], + [$dateImmutable, "string", "'".$dateUTC->format("Y-m-d H:i:s")."'"], + [$dateImmutable, "binary", "x'".bin2hex($dateUTC->format("Y-m-d H:i:s"))."'"], + [$dateImmutable, "datetime", "'".$dateUTC->format("Y-m-d H:i:s")."'"], + [$dateImmutable, "boolean", "1"], + [$dateImmutable, "strict integer", $dateUTC->getTimestamp()], + [$dateImmutable, "strict float", $dateUTC->getTimestamp().".0"], + [$dateImmutable, "strict string", "'".$dateUTC->format("Y-m-d H:i:s")."'"], + [$dateImmutable, "strict binary", "x'".bin2hex($dateUTC->format("Y-m-d H:i:s"))."'"], + [$dateImmutable, "strict datetime", "'".$dateUTC->format("Y-m-d H:i:s")."'"], + [$dateImmutable, "strict boolean", "1"], + ]; + } + public function testConstructStatement() { $nativeStatement = $this->c->prepare("SELECT ? as value"); $this->assertInstanceOf(Statement::class, new \JKingWeb\Arsse\Db\SQLite3\Statement($this->c, $nativeStatement)); diff --git a/tests/lib/Db/BindingTests.php b/tests/lib/Db/BindingTests.php deleted file mode 100644 index 5ba5ff3..0000000 --- a/tests/lib/Db/BindingTests.php +++ /dev/null @@ -1,253 +0,0 @@ - null, - "integer" => null, - "float" => null, - "date" => null, - "time" => null, - "datetime" => null, - "binary" => null, - "string" => null, - "boolean" => null, - ]; - $this->checkBinding($input, $exp); - // types may also be strict (e.g. "strict integer") and never pass null to the database; this is useful for NOT NULL columns - // only null input should yield different results, so only this test has different expectations - $exp = [ - "null" => null, - "integer" => 0, - "float" => 0.0, - "date" => gmdate("Y-m-d", 0), - "time" => gmdate("H:i:s", 0), - "datetime" => gmdate("Y-m-d H:i:s", 0), - "binary" => "", - "string" => "", - "boolean" => 0, - ]; - $this->checkBinding($input, $exp, true); - } - - public function testBindTrue() { - $input = true; - $exp = [ - "null" => null, - "integer" => 1, - "float" => 1.0, - "date" => null, - "time" => null, - "datetime" => null, - "binary" => "1", - "string" => "1", - "boolean" => 1, - ]; - $this->checkBinding($input, $exp); - $this->checkBinding($input, $exp, true); - } - - public function testBindFalse() { - $input = false; - $exp = [ - "null" => null, - "integer" => 0, - "float" => 0.0, - "date" => null, - "time" => null, - "datetime" => null, - "binary" => "", - "string" => "", - "boolean" => 0, - ]; - $this->checkBinding($input, $exp); - $this->checkBinding($input, $exp, true); - } - - public function testBindInteger() { - $input = 2112; - $exp = [ - "null" => null, - "integer" => 2112, - "float" => 2112.0, - "date" => gmdate("Y-m-d", 2112), - "time" => gmdate("H:i:s", 2112), - "datetime" => gmdate("Y-m-d H:i:s", 2112), - "binary" => "2112", - "string" => "2112", - "boolean" => 1, - ]; - $this->checkBinding($input, $exp); - $this->checkBinding($input, $exp, true); - } - - public function testBindIntegerZero() { - $input = 0; - $exp = [ - "null" => null, - "integer" => 0, - "float" => 0.0, - "date" => gmdate("Y-m-d", 0), - "time" => gmdate("H:i:s", 0), - "datetime" => gmdate("Y-m-d H:i:s", 0), - "binary" => "0", - "string" => "0", - "boolean" => 0, - ]; - $this->checkBinding($input, $exp); - $this->checkBinding($input, $exp, true); - } - - public function testBindFloat() { - $input = 2112.0; - $exp = [ - "null" => null, - "integer" => 2112, - "float" => 2112.0, - "date" => gmdate("Y-m-d", 2112), - "time" => gmdate("H:i:s", 2112), - "datetime" => gmdate("Y-m-d H:i:s", 2112), - "binary" => "2112", - "string" => "2112", - "boolean" => 1, - ]; - $this->checkBinding($input, $exp); - $this->checkBinding($input, $exp, true); - } - - public function testBindFloatZero() { - $input = 0.0; - $exp = [ - "null" => null, - "integer" => 0, - "float" => 0.0, - "date" => gmdate("Y-m-d", 0), - "time" => gmdate("H:i:s", 0), - "datetime" => gmdate("Y-m-d H:i:s", 0), - "binary" => "0", - "string" => "0", - "boolean" => 0, - ]; - $this->checkBinding($input, $exp); - $this->checkBinding($input, $exp, true); - } - - public function testBindAsciiString() { - $input = "Random string"; - $exp = [ - "null" => null, - "integer" => 0, - "float" => 0.0, - "date" => null, - "time" => null, - "datetime" => null, - "binary" => $input, - "string" => $input, - "boolean" => 1, - ]; - $this->checkBinding($input, $exp); - $this->checkBinding($input, $exp, true); - } - - public function testBindUtf8String() { - $input = "é"; - $exp = [ - "null" => null, - "integer" => 0, - "float" => 0.0, - "date" => null, - "time" => null, - "datetime" => null, - "binary" => $input, - "string" => $input, - "boolean" => 1, - ]; - $this->checkBinding($input, $exp); - $this->checkBinding($input, $exp, true); - } - - public function testBindBinaryString() { - // FIXME: This test may be unreliable; SQLite happily stores invalid UTF-8 text as bytes untouched, but other engines probably don't do this - $input = chr(233); - $exp = [ - "null" => null, - "integer" => 0, - "float" => 0.0, - "date" => null, - "time" => null, - "datetime" => null, - "binary" => $input, - "string" => $input, - "boolean" => 1, - ]; - $this->checkBinding($input, $exp); - $this->checkBinding($input, $exp, true); - } - - public function testBindIso8601DateString() { - $input = "2017-01-09T13:11:17"; - $time = strtotime($input." UTC"); - $exp = [ - "null" => null, - "integer" => 2017, - "float" => 2017.0, - "date" => gmdate("Y-m-d", $time), - "time" => gmdate("H:i:s", $time), - "datetime" => gmdate("Y-m-d H:i:s", $time), - "binary" => $input, - "string" => $input, - "boolean" => 1, - ]; - $this->checkBinding($input, $exp); - $this->checkBinding($input, $exp, true); - } - - public function testBindArbitraryDateString() { - $input = "Today"; - $time = date_create($input, new \DateTimezone("UTC"))->getTimestamp(); - $exp = [ - "null" => null, - "integer" => 0, - "float" => 0.0, - "date" => gmdate("Y-m-d", $time), - "time" => gmdate("H:i:s", $time), - "datetime" => gmdate("Y-m-d H:i:s", $time), - "binary" => $input, - "string" => $input, - "boolean" => 1, - ]; - $this->checkBinding($input, $exp); - $this->checkBinding($input, $exp, true); - } - - public function testBindMutableDateObject($class = '\DateTime') { - $input = new $class("Noon Today"); - $time = $input->getTimestamp(); - $exp = [ - "null" => null, - "integer" => $time, - "float" => (float) $time, - "date" => gmdate("Y-m-d", $time), - "time" => gmdate("H:i:s", $time), - "datetime" => gmdate("Y-m-d H:i:s", $time), - "binary" => gmdate("Y-m-d H:i:s", $time), - "string" => gmdate("Y-m-d H:i:s", $time), - "boolean" => 1, - ]; - $this->checkBinding($input, $exp); - $this->checkBinding($input, $exp, true); - } - - public function testBindImmutableDateObject() { - $this->testBindMutableDateObject('\DateTimeImmutable'); - } -} From 31aea0a06ab1ec1d24bbc16c8d862efe0e12b260 Mon Sep 17 00:00:00 2001 From: "J. King" Date: Sat, 30 Dec 2017 16:16:59 -0500 Subject: [PATCH 10/16] PDO-SQLite type binding tests Db namespace is now back to full coverage --- tests/cases/Db/SQLite3PDO/TestStatement.php | 207 ++++++++++++++++++++ 1 file changed, 207 insertions(+) diff --git a/tests/cases/Db/SQLite3PDO/TestStatement.php b/tests/cases/Db/SQLite3PDO/TestStatement.php index 5eb6cb9..69e0345 100644 --- a/tests/cases/Db/SQLite3PDO/TestStatement.php +++ b/tests/cases/Db/SQLite3PDO/TestStatement.php @@ -45,6 +45,213 @@ class TestStatement extends \JKingWeb\Arsse\Test\AbstractTest { } } + /** @dataProvider provideBindings */ + public function testBindATypedValue($value, $type, $exp) { + $typeStr = "'".str_replace("'", "''", $type)."'"; + $nativeStatement = $this->c->prepare( + "SELECT ( + (CASE WHEN substr($typeStr, 0, 7) <> 'strict ' then null else 1 end) is null + and ? is null + ) or ( + $exp = ? + ) as pass" + ); + $s = new self::$imp($this->c, $nativeStatement); + $s->rebind(...[$type, $type]); + $act = (bool) $s->run(...[$value, $value])->getRow()['pass']; + $this->assertTrue($act); + } + + public function provideBindings() { + $dateMutable = new \DateTime("Noon Today", new \DateTimezone("America/Toronto")); + $dateImmutable = new \DateTimeImmutable("Noon Today", new \DateTimezone("America/Toronto")); + $dateUTC = new \DateTime("@".$dateMutable->getTimestamp(), new \DateTimezone("UTC")); + return [ + /* input, type, expected binding as SQL fragment */ + [null, "integer", "null"], + [null, "float", "null"], + [null, "string", "null"], + [null, "binary", "null"], + [null, "datetime", "null"], + [null, "boolean", "null"], + [null, "strict integer", "0"], + [null, "strict float", "'0'"], + [null, "strict string", "''"], + [null, "strict binary", "x''"], + [null, "strict datetime", "'1970-01-01 00:00:00'"], + [null, "strict boolean", "0"], + // true + [true, "integer", "1"], + [true, "float", "'1'"], + [true, "string", "'1'"], + [true, "binary", "x'31'"], + [true, "datetime", "null"], + [true, "boolean", "1"], + [true, "strict integer", "1"], + [true, "strict float", "'1'"], + [true, "strict string", "'1'"], + [true, "strict binary", "x'31'"], + [true, "strict datetime", "'1970-01-01 00:00:01'"], + [true, "strict boolean", "1"], + // false + [false, "integer", "0"], + [false, "float", "'0'"], + [false, "string", "''"], + [false, "binary", "x''"], + [false, "datetime", "null"], + [false, "boolean", "0"], + [false, "strict integer", "0"], + [false, "strict float", "'0'"], + [false, "strict string", "''"], + [false, "strict binary", "x''"], + [false, "strict datetime", "'1970-01-01 00:00:00'"], + [false, "strict boolean", "0"], + // integer + [2112, "integer", "2112"], + [2112, "float", "'2112'"], + [2112, "string", "'2112'"], + [2112, "binary", "x'32313132'"], + [2112, "datetime", "'1970-01-01 00:35:12'"], + [2112, "boolean", "1"], + [2112, "strict integer", "2112"], + [2112, "strict float", "'2112'"], + [2112, "strict string", "'2112'"], + [2112, "strict binary", "x'32313132'"], + [2112, "strict datetime", "'1970-01-01 00:35:12'"], + [2112, "strict boolean", "1"], + // integer zero + [0, "integer", "0"], + [0, "float", "'0'"], + [0, "string", "'0'"], + [0, "binary", "x'30'"], + [0, "datetime", "'1970-01-01 00:00:00'"], + [0, "boolean", "0"], + [0, "strict integer", "0"], + [0, "strict float", "'0'"], + [0, "strict string", "'0'"], + [0, "strict binary", "x'30'"], + [0, "strict datetime", "'1970-01-01 00:00:00'"], + [0, "strict boolean", "0"], + // float + [2112.5, "integer", "2112"], + [2112.5, "float", "'2112.5'"], + [2112.5, "string", "'2112.5'"], + [2112.5, "binary", "x'323131322e35'"], + [2112.5, "datetime", "'1970-01-01 00:35:12'"], + [2112.5, "boolean", "1"], + [2112.5, "strict integer", "2112"], + [2112.5, "strict float", "'2112.5'"], + [2112.5, "strict string", "'2112.5'"], + [2112.5, "strict binary", "x'323131322e35'"], + [2112.5, "strict datetime", "'1970-01-01 00:35:12'"], + [2112.5, "strict boolean", "1"], + // float zero + [0.0, "integer", "0"], + [0.0, "float", "'0'"], + [0.0, "string", "'0'"], + [0.0, "binary", "x'30'"], + [0.0, "datetime", "'1970-01-01 00:00:00'"], + [0.0, "boolean", "0"], + [0.0, "strict integer", "0"], + [0.0, "strict float", "'0'"], + [0.0, "strict string", "'0'"], + [0.0, "strict binary", "x'30'"], + [0.0, "strict datetime", "'1970-01-01 00:00:00'"], + [0.0, "strict boolean", "0"], + // ASCII string + ["Random string", "integer", "0"], + ["Random string", "float", "'0'"], + ["Random string", "string", "'Random string'"], + ["Random string", "binary", "x'52616e646f6d20737472696e67'"], + ["Random string", "datetime", "null"], + ["Random string", "boolean", "1"], + ["Random string", "strict integer", "0"], + ["Random string", "strict float", "'0'"], + ["Random string", "strict string", "'Random string'"], + ["Random string", "strict binary", "x'52616e646f6d20737472696e67'"], + ["Random string", "strict datetime", "'1970-01-01 00:00:00'"], + ["Random string", "strict boolean", "1"], + // UTF-8 string + ["é", "integer", "0"], + ["é", "float", "'0'"], + ["é", "string", "char(233)"], + ["é", "binary", "x'c3a9'"], + ["é", "datetime", "null"], + ["é", "boolean", "1"], + ["é", "strict integer", "0"], + ["é", "strict float", "'0'"], + ["é", "strict string", "char(233)"], + ["é", "strict binary", "x'c3a9'"], + ["é", "strict datetime", "'1970-01-01 00:00:00'"], + ["é", "strict boolean", "1"], + // binary string + [chr(233).chr(233), "integer", "0"], + [chr(233).chr(233), "float", "'0'"], + [chr(233).chr(233), "string", "'".chr(233).chr(233)."'"], + [chr(233).chr(233), "binary", "x'e9e9'"], + [chr(233).chr(233), "datetime", "null"], + [chr(233).chr(233), "boolean", "1"], + [chr(233).chr(233), "strict integer", "0"], + [chr(233).chr(233), "strict float", "'0'"], + [chr(233).chr(233), "strict string", "'".chr(233).chr(233)."'"], + [chr(233).chr(233), "strict binary", "x'e9e9'"], + [chr(233).chr(233), "strict datetime", "'1970-01-01 00:00:00'"], + [chr(233).chr(233), "strict boolean", "1"], + // ISO 8601 date string + ["2017-01-09T13:11:17", "integer", "2017"], + ["2017-01-09T13:11:17", "float", "'2017'"], + ["2017-01-09T13:11:17", "string", "'2017-01-09T13:11:17'"], + ["2017-01-09T13:11:17", "binary", "x'323031372d30312d30395431333a31313a3137'"], + ["2017-01-09T13:11:17", "datetime", "'2017-01-09 13:11:17'"], + ["2017-01-09T13:11:17", "boolean", "1"], + ["2017-01-09T13:11:17", "strict integer", "2017"], + ["2017-01-09T13:11:17", "strict float", "'2017'"], + ["2017-01-09T13:11:17", "strict string", "'2017-01-09T13:11:17'"], + ["2017-01-09T13:11:17", "strict binary", "x'323031372d30312d30395431333a31313a3137'"], + ["2017-01-09T13:11:17", "strict datetime", "'2017-01-09 13:11:17'"], + ["2017-01-09T13:11:17", "strict boolean", "1"], + // arbitrary date string + ["Today", "integer", "0"], + ["Today", "float", "'0'"], + ["Today", "string", "'Today'"], + ["Today", "binary", "x'546f646179'"], + ["Today", "datetime", "'".date_create("Today", new \DateTimezone("UTC"))->format("Y-m-d H:i:s")."'"], + ["Today", "boolean", "1"], + ["Today", "strict integer", "0"], + ["Today", "strict float", "'0'"], + ["Today", "strict string", "'Today'"], + ["Today", "strict binary", "x'546f646179'"], + ["Today", "strict datetime", "'".date_create("Today", new \DateTimezone("UTC"))->format("Y-m-d H:i:s")."'"], + ["Today", "strict boolean", "1"], + // mutable date object + [$dateMutable, "integer", $dateUTC->getTimestamp()], + [$dateMutable, "float", "'".$dateUTC->getTimestamp()."'"], + [$dateMutable, "string", "'".$dateUTC->format("Y-m-d H:i:s")."'"], + [$dateMutable, "binary", "x'".bin2hex($dateUTC->format("Y-m-d H:i:s"))."'"], + [$dateMutable, "datetime", "'".$dateUTC->format("Y-m-d H:i:s")."'"], + [$dateMutable, "boolean", "1"], + [$dateMutable, "strict integer", $dateUTC->getTimestamp()], + [$dateMutable, "strict float", "'".$dateUTC->getTimestamp()."'"], + [$dateMutable, "strict string", "'".$dateUTC->format("Y-m-d H:i:s")."'"], + [$dateMutable, "strict binary", "x'".bin2hex($dateUTC->format("Y-m-d H:i:s"))."'"], + [$dateMutable, "strict datetime", "'".$dateUTC->format("Y-m-d H:i:s")."'"], + [$dateMutable, "strict boolean", "1"], + // immutable date object + [$dateImmutable, "integer", $dateUTC->getTimestamp()], + [$dateImmutable, "float", "'".$dateUTC->getTimestamp()."'"], + [$dateImmutable, "string", "'".$dateUTC->format("Y-m-d H:i:s")."'"], + [$dateImmutable, "binary", "x'".bin2hex($dateUTC->format("Y-m-d H:i:s"))."'"], + [$dateImmutable, "datetime", "'".$dateUTC->format("Y-m-d H:i:s")."'"], + [$dateImmutable, "boolean", "1"], + [$dateImmutable, "strict integer", $dateUTC->getTimestamp()], + [$dateImmutable, "strict float", "'".$dateUTC->getTimestamp()."'"], + [$dateImmutable, "strict string", "'".$dateUTC->format("Y-m-d H:i:s")."'"], + [$dateImmutable, "strict binary", "x'".bin2hex($dateUTC->format("Y-m-d H:i:s"))."'"], + [$dateImmutable, "strict datetime", "'".$dateUTC->format("Y-m-d H:i:s")."'"], + [$dateImmutable, "strict boolean", "1"], + ]; + } + public function testConstructStatement() { $nativeStatement = $this->c->prepare("SELECT ? as value"); $this->assertInstanceOf(Statement::class, new PDOStatement($this->c, $nativeStatement)); From bc9fcb975f67b50a597d8a55799aed73723d6442 Mon Sep 17 00:00:00 2001 From: "J. King" Date: Sat, 30 Dec 2017 17:04:21 -0500 Subject: [PATCH 11/16] Change "rebind" to "retype" to better reflect what actually happens --- lib/Db/AbstractStatement.php | 8 ++++---- lib/Db/PDOStatement.php | 2 +- lib/Db/SQLite3/Statement.php | 2 +- lib/Db/Statement.php | 4 ++-- tests/cases/Db/SQLite3/TestStatement.php | 6 +++--- tests/cases/Db/SQLite3PDO/TestStatement.php | 6 +++--- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/Db/AbstractStatement.php b/lib/Db/AbstractStatement.php index 8390194..f787cbc 100644 --- a/lib/Db/AbstractStatement.php +++ b/lib/Db/AbstractStatement.php @@ -18,18 +18,18 @@ abstract class AbstractStatement implements Statement { return $this->runArray($values); } - public function rebind(...$bindings): bool { - return $this->rebindArray($bindings); + public function retype(...$bindings): bool { + return $this->retypeArray($bindings); } - public function rebindArray(array $bindings, bool $append = false): bool { + public function retypeArray(array $bindings, bool $append = false): bool { if (!$append) { $this->types = []; } foreach ($bindings as $binding) { if (is_array($binding)) { // recursively flatten any arrays, which may be provided for SET or IN() clauses - $this->rebindArray($binding, true); + $this->retypeArray($binding, true); } else { $binding = trim(strtolower($binding)); if (strpos($binding, "strict ")===0) { diff --git a/lib/Db/PDOStatement.php b/lib/Db/PDOStatement.php index 6a2534d..d05255c 100644 --- a/lib/Db/PDOStatement.php +++ b/lib/Db/PDOStatement.php @@ -24,7 +24,7 @@ class PDOStatement extends AbstractStatement { public function __construct(\PDO $db, \PDOStatement $st, array $bindings = []) { $this->db = $db; $this->st = $st; - $this->rebindArray($bindings); + $this->retypeArray($bindings); } public function __destruct() { diff --git a/lib/Db/SQLite3/Statement.php b/lib/Db/SQLite3/Statement.php index 55da642..ee3cd44 100644 --- a/lib/Db/SQLite3/Statement.php +++ b/lib/Db/SQLite3/Statement.php @@ -31,7 +31,7 @@ class Statement extends \JKingWeb\Arsse\Db\AbstractStatement { public function __construct(\SQLite3 $db, \SQLite3Stmt $st, array $bindings = []) { $this->db = $db; $this->st = $st; - $this->rebindArray($bindings); + $this->retypeArray($bindings); } public function __destruct() { diff --git a/lib/Db/Statement.php b/lib/Db/Statement.php index 02677c4..b59e075 100644 --- a/lib/Db/Statement.php +++ b/lib/Db/Statement.php @@ -29,6 +29,6 @@ interface Statement { public function run(...$values): Result; public function runArray(array $values = []): Result; - public function rebind(...$bindings): bool; - public function rebindArray(array $bindings): bool; + public function retype(...$bindings): bool; + public function retypeArray(array $bindings): bool; } diff --git a/tests/cases/Db/SQLite3/TestStatement.php b/tests/cases/Db/SQLite3/TestStatement.php index 4bcb326..dcb0e2a 100644 --- a/tests/cases/Db/SQLite3/TestStatement.php +++ b/tests/cases/Db/SQLite3/TestStatement.php @@ -35,10 +35,10 @@ class TestStatement extends \JKingWeb\Arsse\Test\AbstractTest { $s = new self::$imp($this->c, $nativeStatement); $types = array_unique(Statement::TYPES); foreach ($types as $type) { - $s->rebindArray([$strict ? "strict $type" : $type]); + $s->retypeArray([$strict ? "strict $type" : $type]); $val = $s->runArray([$input])->getRow()['value']; $this->assertSame($expectations[$type], $val, "Binding from type $type failed comparison."); - $s->rebind(...[$strict ? "strict $type" : $type]); + $s->retype(...[$strict ? "strict $type" : $type]); $val = $s->run(...[$input])->getRow()['value']; $this->assertSame($expectations[$type], $val, "Binding from type $type failed comparison."); } @@ -56,7 +56,7 @@ class TestStatement extends \JKingWeb\Arsse\Test\AbstractTest { ) as pass" ); $s = new self::$imp($this->c, $nativeStatement); - $s->rebindArray([$type, $type]); + $s->retypeArray([$type, $type]); $act = (bool) $s->run(...[$value, $value])->getRow()['pass']; $this->assertTrue($act); } diff --git a/tests/cases/Db/SQLite3PDO/TestStatement.php b/tests/cases/Db/SQLite3PDO/TestStatement.php index 69e0345..9a269a1 100644 --- a/tests/cases/Db/SQLite3PDO/TestStatement.php +++ b/tests/cases/Db/SQLite3PDO/TestStatement.php @@ -36,10 +36,10 @@ class TestStatement extends \JKingWeb\Arsse\Test\AbstractTest { $s = new self::$imp($this->c, $nativeStatement); $types = array_unique(Statement::TYPES); foreach ($types as $type) { - $s->rebindArray([$strict ? "strict $type" : $type]); + $s->retypeArray([$strict ? "strict $type" : $type]); $val = $s->runArray([$input])->getRow()['value']; $this->assertSame($expectations[$type], $val, "Binding from type $type failed comparison."); - $s->rebind(...[$strict ? "strict $type" : $type]); + $s->retype(...[$strict ? "strict $type" : $type]); $val = $s->run(...[$input])->getRow()['value']; $this->assertSame($expectations[$type], $val, "Binding from type $type failed comparison."); } @@ -57,7 +57,7 @@ class TestStatement extends \JKingWeb\Arsse\Test\AbstractTest { ) as pass" ); $s = new self::$imp($this->c, $nativeStatement); - $s->rebind(...[$type, $type]); + $s->retype(...[$type, $type]); $act = (bool) $s->run(...[$value, $value])->getRow()['pass']; $this->assertTrue($act); } From ba0aeab7ec2b987ed09d80344bae8c457b5daaef Mon Sep 17 00:00:00 2001 From: "J. King" Date: Sat, 30 Dec 2017 18:50:56 -0500 Subject: [PATCH 12/16] Make SQL statement type conversion use ValueInfo normalizer This sees the addition of a dateOutFormat parameter to ValueInfo::normalize(), as well as a general simplification of how parameter binding works. Some value type-casting results are slightly different, but this simply makes SQL statement objects consistent with the rest of the system. --- lib/Db/AbstractStatement.php | 42 +++++++++++++-------- lib/Db/PDOStatement.php | 31 +-------------- lib/Db/SQLite3/Statement.php | 31 +-------------- lib/Misc/ValueInfo.php | 20 +++++----- tests/cases/Db/SQLite3/TestStatement.php | 10 ++--- tests/cases/Db/SQLite3PDO/TestStatement.php | 10 ++--- 6 files changed, 52 insertions(+), 92 deletions(-) diff --git a/lib/Db/AbstractStatement.php b/lib/Db/AbstractStatement.php index f787cbc..57185ee 100644 --- a/lib/Db/AbstractStatement.php +++ b/lib/Db/AbstractStatement.php @@ -7,12 +7,14 @@ declare(strict_types=1); namespace JKingWeb\Arsse\Db; use JKingWeb\Arsse\Misc\Date; +use JKingWeb\Arsse\Misc\ValueInfo; abstract class AbstractStatement implements Statement { protected $types = []; protected $isNullable = []; abstract public function runArray(array $values = []): Result; + abstract protected function bindValue($value, string $type, int $position): bool; public function run(...$values): Result { return $this->runArray($values); @@ -51,31 +53,41 @@ abstract class AbstractStatement implements Statement { protected function cast($v, string $t, bool $nullable) { switch ($t) { case "datetime": + $v = Date::transform($v, "sql"); if (is_null($v) && !$nullable) { $v = 0; + $v = Date::transform($v, "sql"); } - return Date::transform($v, "sql"); + return $v; case "integer": + return ValueInfo::normalize($v, ValueInfo::T_INT | ($nullable ? ValueInfo::M_NULL : 0), null, "sql"); case "float": + return ValueInfo::normalize($v, ValueInfo::T_FLOAT | ($nullable ? ValueInfo::M_NULL : 0), null, "sql"); case "binary": case "string": + return ValueInfo::normalize($v, ValueInfo::T_STRING | ($nullable ? ValueInfo::M_NULL : 0), null, "sql"); case "boolean": - if ($t=="binary") { - $t = "string"; - } - if ($v instanceof \DateTimeInterface) { - if ($t=="string") { - return Date::transform($v, "sql"); - } else { - $v = $v->getTimestamp(); - settype($v, $t); - } - } else { - settype($v, $t); - } - return $v; + $v = ValueInfo::normalize($v, ValueInfo::T_BOOL | ($nullable ? ValueInfo::M_NULL : 0), null, "sql"); + return is_null($v) ? $v : (int) $v; default: throw new Exception("paramTypeUnknown", $type); // @codeCoverageIgnore } } + + protected function bindValues(array $values, int $offset = 0): int { + $a = $offset; + foreach ($values as $value) { + if (is_array($value)) { + // recursively flatten any arrays, which may be provided for SET or IN() clauses + $a += $this->bindValues($value, $a); + } elseif (array_key_exists($a, $this->types)) { + $value = $this->cast($value, $this->types[$a], $this->isNullable[$a]); + $this->bindValue($value, $this->types[$a], $a+1); + $a++; + } else { + throw new Exception("paramTypeMissing", $a+1); + } + } + return $a - $offset; + } } diff --git a/lib/Db/PDOStatement.php b/lib/Db/PDOStatement.php index d05255c..cfd9cef 100644 --- a/lib/Db/PDOStatement.php +++ b/lib/Db/PDOStatement.php @@ -49,34 +49,7 @@ class PDOStatement extends AbstractStatement { return new PDOResult($this->st, [$changes, $lastId]); } - protected function bindValues(array $values, int $offset = 0): int { - $a = $offset; - foreach ($values as $value) { - if (is_array($value)) { - // recursively flatten any arrays, which may be provided for SET or IN() clauses - $a += $this->bindValues($value, $a); - } elseif (array_key_exists($a, $this->types)) { - // if the parameter type is something other than the known values, this is an error - assert(array_key_exists($this->types[$a], self::BINDINGS), new Exception("paramTypeUnknown", $this->types[$a])); - // if the parameter type is null or the value is null (and the type is nullable), just bind null - if ($this->types[$a]=="null" || ($this->isNullable[$a] && is_null($value))) { - $this->st->bindValue($a+1, null, \PDO::PARAM_NULL); - } else { - // otherwise cast the value to the right type and bind the result - $type = self::BINDINGS[$this->types[$a]]; - $value = $this->cast($value, $this->types[$a], $this->isNullable[$a]); - // re-adjust for null casts - if ($value===null) { - $type = \PDO::PARAM_NULL; - } - // perform binding - $this->st->bindValue($a+1, $value, $type); - } - $a++; - } else { - throw new Exception("paramTypeMissing", $a+1); - } - } - return $a - $offset; + protected function bindValue($value, string $type, int $position): bool { + return $this->st->bindValue($position, $value, is_null($value) ? \PDO::PARAM_NULL : self::BINDINGS[$type]); } } diff --git a/lib/Db/SQLite3/Statement.php b/lib/Db/SQLite3/Statement.php index ee3cd44..ab07b47 100644 --- a/lib/Db/SQLite3/Statement.php +++ b/lib/Db/SQLite3/Statement.php @@ -56,34 +56,7 @@ class Statement extends \JKingWeb\Arsse\Db\AbstractStatement { return new Result($r, [$changes, $lastId], $this); } - protected function bindValues(array $values, int $offset = 0): int { - $a = $offset; - foreach ($values as $value) { - if (is_array($value)) { - // recursively flatten any arrays, which may be provided for SET or IN() clauses - $a += $this->bindValues($value, $a); - } elseif (array_key_exists($a, $this->types)) { - // if the parameter type is something other than the known values, this is an error - assert(array_key_exists($this->types[$a], self::BINDINGS), new Exception("paramTypeUnknown", $this->types[$a])); - // if the parameter type is null or the value is null (and the type is nullable), just bind null - if ($this->types[$a]=="null" || ($this->isNullable[$a] && is_null($value))) { - $this->st->bindValue($a+1, null, \SQLITE3_NULL); - } else { - // otherwise cast the value to the right type and bind the result - $type = self::BINDINGS[$this->types[$a]]; - $value = $this->cast($value, $this->types[$a], $this->isNullable[$a]); - // re-adjust for null casts - if ($value===null) { - $type = \SQLITE3_NULL; - } - // perform binding - $this->st->bindValue($a+1, $value, $type); - } - $a++; - } else { - throw new Exception("paramTypeMissing", $a+1); - } - } - return $a - $offset; + protected function bindValue($value, string $type, int $position): bool { + return $this->st->bindValue($position, $value, is_null($value) ? \SQLITE3_NULL : self::BINDINGS[$type]); } } diff --git a/lib/Misc/ValueInfo.php b/lib/Misc/ValueInfo.php index a025bb5..870dad0 100644 --- a/lib/Misc/ValueInfo.php +++ b/lib/Misc/ValueInfo.php @@ -34,7 +34,7 @@ class ValueInfo { const M_STRICT = 1 << 30; // throw an exception if the type doesn't match const M_ARRAY = 1 << 31; // the value should be a flat array of values of the specified type; indexed and associative are both acceptable - public static function normalize($value, int $type, string $dateFormat = null) { + public static function normalize($value, int $type, string $dateInFormat = null, $dateOutFormat = null) { $allowNull = ($type & self::M_NULL); $strict = ($type & (self::M_STRICT | self::M_DROP)); $drop = ($type & self::M_DROP); @@ -48,7 +48,7 @@ class ValueInfo { if ($arrayVal) { $value = self::normalize($value, self::T_ARRAY); foreach ($value as $key => $v) { - $value[$key] = self::normalize($v, $type | ($allowNull ? self::M_NULL : 0) | ($strict ? self::M_STRICT : 0) | ($drop ? self::M_DROP : 0), $dateFormat); + $value[$key] = self::normalize($v, $type | ($allowNull ? self::M_NULL : 0) | ($strict ? self::M_STRICT : 0) | ($drop ? self::M_DROP : 0), $dateInFormat, $dateOutFormat); } return $value; } @@ -131,12 +131,14 @@ class ValueInfo { if (is_string($value)) { return $value; } + $dateOutFormat = $dateOutFormat ?? "iso8601"; + $dateOutFormat = isset(Date::FORMAT[$dateOutFormat]) ? Date::FORMAT[$dateOutFormat][1] : $dateOutFormat; if ($value instanceof \DateTimeImmutable) { - return $value->setTimezone(new \DateTimeZone("UTC"))->format(Date::FORMAT['iso8601'][1]); + return $value->setTimezone(new \DateTimeZone("UTC"))->format($dateOutFormat); } elseif ($value instanceof \DateTime) { $out = clone $value; $out->setTimezone(new \DateTimeZone("UTC")); - return $out->format(Date::FORMAT['iso8601'][1]); + return $out->format($dateOutFormat); } elseif (is_float($value) && is_finite($value)) { $out = (string) $value; if (!strpos($out, "E")) { @@ -175,9 +177,9 @@ class ValueInfo { return \DateTime::createFromFormat("U.u", sprintf("%F", $value), new \DateTimeZone("UTC")); } elseif (is_string($value)) { try { - if (!is_null($dateFormat)) { + if (!is_null($dateInFormat)) { $out = false; - if ($dateFormat=="microtime") { + if ($dateInFormat=="microtime") { // PHP is not able to correctly handle the output of microtime() as the input of DateTime::createFromFormat(), so we fudge it to look like a float if (preg_match("<^0\.\d{6}00 \d+$>", $value)) { $value = substr($value, 11).".".substr($value, 2, 6); @@ -185,10 +187,10 @@ class ValueInfo { throw new \Exception; } } - $f = isset(Date::FORMAT[$dateFormat]) ? Date::FORMAT[$dateFormat][0] : $dateFormat; - if ($dateFormat=="iso8601" || $dateFormat=="iso8601m") { + $f = isset(Date::FORMAT[$dateInFormat]) ? Date::FORMAT[$dateInFormat][0] : $dateInFormat; + if ($dateInFormat=="iso8601" || $dateInFormat=="iso8601m") { // DateTime::createFromFormat() doesn't provide one catch-all for ISO 8601 timezone specifiers, so we try all of them till one works - if ($dateFormat=="iso8601m") { + if ($dateInFormat=="iso8601m") { $f2 = Date::FORMAT["iso8601"][0]; $zones = [$f."", $f."\Z", $f."P", $f."O", $f2."", $f2."\Z", $f2."P", $f2."O"]; } else { diff --git a/tests/cases/Db/SQLite3/TestStatement.php b/tests/cases/Db/SQLite3/TestStatement.php index dcb0e2a..5a195a8 100644 --- a/tests/cases/Db/SQLite3/TestStatement.php +++ b/tests/cases/Db/SQLite3/TestStatement.php @@ -90,7 +90,7 @@ class TestStatement extends \JKingWeb\Arsse\Test\AbstractTest { [true, "strict float", "1.0"], [true, "strict string", "'1'"], [true, "strict binary", "x'31'"], - [true, "strict datetime", "'1970-01-01 00:00:01'"], + [true, "strict datetime", "'1970-01-01 00:00:00'"], [true, "strict boolean", "1"], // false [false, "integer", "0"], @@ -197,14 +197,14 @@ class TestStatement extends \JKingWeb\Arsse\Test\AbstractTest { [chr(233).chr(233), "strict datetime", "'1970-01-01 00:00:00'"], [chr(233).chr(233), "strict boolean", "1"], // ISO 8601 date string - ["2017-01-09T13:11:17", "integer", "2017"], - ["2017-01-09T13:11:17", "float", "2017.0"], + ["2017-01-09T13:11:17", "integer", "0"], + ["2017-01-09T13:11:17", "float", "0.0"], ["2017-01-09T13:11:17", "string", "'2017-01-09T13:11:17'"], ["2017-01-09T13:11:17", "binary", "x'323031372d30312d30395431333a31313a3137'"], ["2017-01-09T13:11:17", "datetime", "'2017-01-09 13:11:17'"], ["2017-01-09T13:11:17", "boolean", "1"], - ["2017-01-09T13:11:17", "strict integer", "2017"], - ["2017-01-09T13:11:17", "strict float", "2017.0"], + ["2017-01-09T13:11:17", "strict integer", "0"], + ["2017-01-09T13:11:17", "strict float", "0.0"], ["2017-01-09T13:11:17", "strict string", "'2017-01-09T13:11:17'"], ["2017-01-09T13:11:17", "strict binary", "x'323031372d30312d30395431333a31313a3137'"], ["2017-01-09T13:11:17", "strict datetime", "'2017-01-09 13:11:17'"], diff --git a/tests/cases/Db/SQLite3PDO/TestStatement.php b/tests/cases/Db/SQLite3PDO/TestStatement.php index 9a269a1..8fe7086 100644 --- a/tests/cases/Db/SQLite3PDO/TestStatement.php +++ b/tests/cases/Db/SQLite3PDO/TestStatement.php @@ -91,7 +91,7 @@ class TestStatement extends \JKingWeb\Arsse\Test\AbstractTest { [true, "strict float", "'1'"], [true, "strict string", "'1'"], [true, "strict binary", "x'31'"], - [true, "strict datetime", "'1970-01-01 00:00:01'"], + [true, "strict datetime", "'1970-01-01 00:00:00'"], [true, "strict boolean", "1"], // false [false, "integer", "0"], @@ -198,14 +198,14 @@ class TestStatement extends \JKingWeb\Arsse\Test\AbstractTest { [chr(233).chr(233), "strict datetime", "'1970-01-01 00:00:00'"], [chr(233).chr(233), "strict boolean", "1"], // ISO 8601 date string - ["2017-01-09T13:11:17", "integer", "2017"], - ["2017-01-09T13:11:17", "float", "'2017'"], + ["2017-01-09T13:11:17", "integer", "0"], + ["2017-01-09T13:11:17", "float", "'0'"], ["2017-01-09T13:11:17", "string", "'2017-01-09T13:11:17'"], ["2017-01-09T13:11:17", "binary", "x'323031372d30312d30395431333a31313a3137'"], ["2017-01-09T13:11:17", "datetime", "'2017-01-09 13:11:17'"], ["2017-01-09T13:11:17", "boolean", "1"], - ["2017-01-09T13:11:17", "strict integer", "2017"], - ["2017-01-09T13:11:17", "strict float", "'2017'"], + ["2017-01-09T13:11:17", "strict integer", "0"], + ["2017-01-09T13:11:17", "strict float", "'0'"], ["2017-01-09T13:11:17", "strict string", "'2017-01-09T13:11:17'"], ["2017-01-09T13:11:17", "strict binary", "x'323031372d30312d30395431333a31313a3137'"], ["2017-01-09T13:11:17", "strict datetime", "'2017-01-09 13:11:17'"], From 029c23d0cf9ffd4b9a9f2a66d013bb0c9a9b5d5b Mon Sep 17 00:00:00 2001 From: "J. King" Date: Sun, 31 Dec 2017 13:58:37 -0500 Subject: [PATCH 13/16] Adapt API tests to allow for PDO mocks --- tests/cases/REST/NextCloudNews/TestV1_2.php | 27 ++-- tests/cases/REST/TinyTinyRSS/TestAPI.php | 142 ++++++++++---------- 2 files changed, 88 insertions(+), 81 deletions(-) diff --git a/tests/cases/REST/NextCloudNews/TestV1_2.php b/tests/cases/REST/NextCloudNews/TestV1_2.php index 17c4679..f73e785 100644 --- a/tests/cases/REST/NextCloudNews/TestV1_2.php +++ b/tests/cases/REST/NextCloudNews/TestV1_2.php @@ -317,6 +317,10 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { $this->clearData(); } + protected function v($value) { + return $value; + } + protected function assertResponse(Response $exp, Response $act, string $text = null) { $this->assertEquals($exp, $act, $text); $this->assertSame($exp->payload, $act->payload, $text); @@ -404,13 +408,13 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { public function testListFolders() { $list = [ ['id' => 1, 'name' => "Software", 'parent' => null], - ['id' => "12", 'name' => "Hardware", 'parent' => null], + ['id' => 12, 'name' => "Hardware", 'parent' => null], ]; $out = [ ['id' => 1, 'name' => "Software"], ['id' => 12, 'name' => "Hardware"], ]; - Phake::when(Arsse::$db)->folderList(Arsse::$user->id, null, false)->thenReturn(new Result([]))->thenReturn(new Result($list)); + Phake::when(Arsse::$db)->folderList(Arsse::$user->id, null, false)->thenReturn(new Result([]))->thenReturn(new Result($this->v($list))); $exp = new Response(200, ['folders' => []]); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/folders"))); $exp = new Response(200, ['folders' => $out]); @@ -434,8 +438,8 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->folderAdd($this->anything(), $this->anything())->thenThrow(new \Exception); Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, $in[0])->thenReturn(1)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, $in[1])->thenReturn(2)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call - Phake::when(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 1)->thenReturn($db[0]); - Phake::when(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 2)->thenReturn($db[1]); + Phake::when(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 1)->thenReturn($this->v($db[0])); + Phake::when(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 2)->thenReturn($this->v($db[1])); // set up mocks that produce errors Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, [])->thenThrow(new ExceptionInput("missing")); Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, ['name' => ""])->thenThrow(new ExceptionInput("missing")); @@ -518,8 +522,8 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { 'starredCount' => 5, 'newestItemId' => 4758915, ]; - Phake::when(Arsse::$db)->subscriptionList(Arsse::$user->id)->thenReturn(new Result([]))->thenReturn(new Result($this->feeds['db'])); - Phake::when(Arsse::$db)->articleStarred(Arsse::$user->id)->thenReturn(['total' => 0])->thenReturn(['total' => 5]); + Phake::when(Arsse::$db)->subscriptionList(Arsse::$user->id)->thenReturn(new Result([]))->thenReturn(new Result($this->v($this->feeds['db']))); + Phake::when(Arsse::$db)->articleStarred(Arsse::$user->id)->thenReturn($this->v(['total' => 0]))->thenReturn($this->v(['total' => 5])); Phake::when(Arsse::$db)->editionLatest(Arsse::$user->id)->thenReturn(0)->thenReturn(4758915); $exp = new Response(200, $exp1); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds"))); @@ -544,9 +548,9 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->subscriptionAdd(Arsse::$user->id, "http://example.com/news.atom")->thenReturn(2112)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call Phake::when(Arsse::$db)->subscriptionAdd(Arsse::$user->id, "http://example.org/news.atom")->thenReturn(42)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call Phake::when(Arsse::$db)->subscriptionAdd(Arsse::$user->id, "")->thenThrow(new \JKingWeb\Arsse\Feed\Exception("", new \PicoFeed\Reader\SubscriptionNotFoundException)); - Phake::when(Arsse::$db)->subscriptionPropertiesGet(Arsse::$user->id, 2112)->thenReturn($this->feeds['db'][0]); - Phake::when(Arsse::$db)->subscriptionPropertiesGet(Arsse::$user->id, 42)->thenReturn($this->feeds['db'][1]); - Phake::when(Arsse::$db)->subscriptionPropertiesGet(Arsse::$user->id, 47)->thenReturn($this->feeds['db'][2]); + Phake::when(Arsse::$db)->subscriptionPropertiesGet(Arsse::$user->id, 2112)->thenReturn($this->v($this->feeds['db'][0])); + Phake::when(Arsse::$db)->subscriptionPropertiesGet(Arsse::$user->id, 42)->thenReturn($this->v($this->feeds['db'][1])); + Phake::when(Arsse::$db)->subscriptionPropertiesGet(Arsse::$user->id, 47)->thenReturn($this->v($this->feeds['db'][2])); Phake::when(Arsse::$db)->editionLatest(Arsse::$user->id, (new Context)->subscription(2112))->thenReturn(0); Phake::when(Arsse::$db)->editionLatest(Arsse::$user->id, (new Context)->subscription(42))->thenReturn(4758915); Phake::when(Arsse::$db)->editionLatest(Arsse::$user->id, (new Context)->subscription(47))->thenReturn(2112); @@ -654,7 +658,7 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { 'userId' => "", ], ]; - Phake::when(Arsse::$db)->feedListStale->thenReturn(array_column($out, "id")); + Phake::when(Arsse::$db)->feedListStale->thenReturn($this->v(array_column($out, "id"))); $exp = new Response(200, ['feeds' => $out]); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/all"))); // retrieving the list when not an admin fails @@ -689,7 +693,6 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { } public function testListArticles() { - $res = new Result($this->articles['db']); $t = new \DateTime; $in = [ ['type' => 0, 'id' => 42], // type=0 => subscription/feed @@ -705,7 +708,7 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { ['lastModified' => $t->getTimestamp()], ['oldestFirst' => false, 'batchSize' => 5, 'offset' => 0], // offset=0 should not set the latestEdition context ]; - Phake::when(Arsse::$db)->articleList(Arsse::$user->id, $this->anything(), Database::LIST_TYPICAL)->thenReturn($res); + Phake::when(Arsse::$db)->articleList(Arsse::$user->id, $this->anything(), Database::LIST_TYPICAL)->thenReturn(new Result($this->v($this->articles['db']))); Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->reverse(true)->subscription(42), Database::LIST_TYPICAL)->thenThrow(new ExceptionInput("idMissing")); Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->reverse(true)->folder(2112), Database::LIST_TYPICAL)->thenThrow(new ExceptionInput("idMissing")); Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->reverse(true)->subscription(-1), Database::LIST_TYPICAL)->thenThrow(new ExceptionInput("typeViolation")); diff --git a/tests/cases/REST/TinyTinyRSS/TestAPI.php b/tests/cases/REST/TinyTinyRSS/TestAPI.php index 9858508..c9ef20e 100644 --- a/tests/cases/REST/TinyTinyRSS/TestAPI.php +++ b/tests/cases/REST/TinyTinyRSS/TestAPI.php @@ -122,6 +122,10 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest { LONG_STRING; + protected function v($value) { + return $value; + } + protected function req($data) : Response { return $this->h->dispatch(new Request("POST", "", json_encode($data))); } @@ -320,8 +324,8 @@ LONG_STRING; // set of various mocks for testing Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, $db[0])->thenReturn(2)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, $db[1])->thenReturn(3)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call - Phake::when(Arsse::$db)->folderList(Arsse::$user->id, null, false)->thenReturn(new Result([$out[0], $out[2]])); - Phake::when(Arsse::$db)->folderList(Arsse::$user->id, 1, false)->thenReturn(new Result([$out[1]])); + Phake::when(Arsse::$db)->folderList(Arsse::$user->id, null, false)->thenReturn(new Result($this->v([$out[0], $out[2]]))); + Phake::when(Arsse::$db)->folderList(Arsse::$user->id, 1, false)->thenReturn(new Result($this->v([$out[1]]))); // set up mocks that produce errors Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, $db[2])->thenThrow(new ExceptionInput("idMissing")); // parent folder does not exist Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, [])->thenThrow(new ExceptionInput("missing")); @@ -522,12 +526,12 @@ LONG_STRING; Phake::when(Arsse::$db)->subscriptionAdd(...$db[7])->thenThrow(new \JKingWeb\Arsse\Feed\Exception("http://example.com/7", new \PicoFeed\Parser\MalformedXmlException())); Phake::when(Arsse::$db)->subscriptionAdd(...$db[8])->thenReturn(4); Phake::when(Arsse::$db)->subscriptionAdd(...$db[9])->thenThrow(new ExceptionInput("constraintViolation")); - Phake::when(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 42)->thenReturn(['id' => 42]); - Phake::when(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 47)->thenReturn(['id' => 47]); + Phake::when(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 42)->thenReturn($this->v(['id' => 42])); + Phake::when(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 47)->thenReturn($this->v(['id' => 47])); Phake::when(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 2112)->thenThrow(new ExceptionInput("subjectMissing")); Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, $this->anything(), $this->anything())->thenReturn(true); Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 4, $this->anything())->thenThrow(new ExceptionInput("idMissing")); - Phake::when(Arsse::$db)->subscriptionList(Arsse::$user->id)->thenReturn(new Result($list)); + Phake::when(Arsse::$db)->subscriptionList(Arsse::$user->id)->thenReturn(new Result($this->v($list))); for ($a = 0; $a < (sizeof($in) - 4); $a++) { $exp = $this->respGood($out[$a]); $this->assertResponse($exp, $this->req($in[$a]), "Failed test $a"); @@ -647,11 +651,11 @@ LONG_STRING; public function testRetrieveTheGlobalUnreadCount() { $in = ['op' => "getUnread", 'sid' => "PriestsOfSyrinx"]; - Phake::when(Arsse::$db)->subscriptionList(Arsse::$user->id)->thenReturn(new Result([ + Phake::when(Arsse::$db)->subscriptionList(Arsse::$user->id)->thenReturn(new Result($this->v([ ['id' => 1, 'unread' => 2112], ['id' => 2, 'unread' => 42], ['id' => 3, 'unread' => 47], - ])); + ]))); $exp = $this->respGood(['unread' => (string) (2112 + 42 + 47)]); $this->assertResponse($exp, $this->req($in)); } @@ -679,7 +683,7 @@ LONG_STRING; ['op' => "updateFeed", 'sid' => "PriestsOfSyrinx"], ]; Phake::when(Arsse::$db)->feedUpdate(11)->thenReturn(true); - Phake::when(Arsse::$db)->subscriptionPropertiesGet(Arsse::$user->id, 1)->thenReturn(['id' => 1, 'feed' => 11]); + Phake::when(Arsse::$db)->subscriptionPropertiesGet(Arsse::$user->id, 1)->thenReturn($this->v(['id' => 1, 'feed' => 11])); Phake::when(Arsse::$db)->subscriptionPropertiesGet(Arsse::$user->id, 2)->thenThrow(new ExceptionInput("subjectMissing")); $exp = $this->respGood(['status' => "OK"]); $this->assertResponse($exp, $this->req($in[0])); @@ -711,8 +715,8 @@ LONG_STRING; // set of various mocks for testing Phake::when(Arsse::$db)->labelAdd(Arsse::$user->id, $db[0])->thenReturn(2)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call Phake::when(Arsse::$db)->labelAdd(Arsse::$user->id, $db[1])->thenReturn(3)->thenThrow(new ExceptionInput("constraintViolation")); // error on the second call - Phake::when(Arsse::$db)->labelPropertiesGet(Arsse::$user->id, "Software", true)->thenReturn($out[0]); - Phake::when(Arsse::$db)->labelPropertiesGet(Arsse::$user->id, "Hardware", true)->thenReturn($out[1]); + Phake::when(Arsse::$db)->labelPropertiesGet(Arsse::$user->id, "Software", true)->thenReturn($this->v($out[0])); + Phake::when(Arsse::$db)->labelPropertiesGet(Arsse::$user->id, "Hardware", true)->thenReturn($this->v($out[1])); // set up mocks that produce errors Phake::when(Arsse::$db)->labelAdd(Arsse::$user->id, [])->thenThrow(new ExceptionInput("missing")); Phake::when(Arsse::$db)->labelAdd(Arsse::$user->id, ['name' => ""])->thenThrow(new ExceptionInput("missing")); @@ -819,12 +823,12 @@ LONG_STRING; ['op' => "getCategories", 'sid' => "PriestsOfSyrinx", 'enable_nested' => true], ['op' => "getCategories", 'sid' => "PriestsOfSyrinx", 'enable_nested' => true, 'unread_only' => true], ]; - Phake::when(Arsse::$db)->folderList($this->anything(), null, true)->thenReturn(new Result($this->folders)); - Phake::when(Arsse::$db)->folderList($this->anything(), null, false)->thenReturn(new Result($this->topFolders)); - Phake::when(Arsse::$db)->subscriptionList($this->anything())->thenReturn(new Result($this->subscriptions)); - Phake::when(Arsse::$db)->labelList($this->anything())->thenReturn(new Result($this->labels)); + Phake::when(Arsse::$db)->folderList($this->anything(), null, true)->thenReturn(new Result($this->v($this->folders))); + Phake::when(Arsse::$db)->folderList($this->anything(), null, false)->thenReturn(new Result($this->v($this->topFolders))); + Phake::when(Arsse::$db)->subscriptionList($this->anything())->thenReturn(new Result($this->v($this->subscriptions))); + Phake::when(Arsse::$db)->labelList($this->anything())->thenReturn(new Result($this->v($this->labels))); Phake::when(Arsse::$db)->articleCount($this->anything(), $this->anything())->thenReturn(7); // FIXME: this should check an unread+modifiedSince context - Phake::when(Arsse::$db)->articleStarred($this->anything())->thenReturn($this->starred); + Phake::when(Arsse::$db)->articleStarred($this->anything())->thenReturn($this->v($this->starred)); $exp = [ [ ['id' => "5", 'title' => "Local", 'unread' => 10, 'order_id' => 1], @@ -884,11 +888,11 @@ LONG_STRING; public function testRetrieveCounterList() { $in = ['op' => "getCounters", 'sid' => "PriestsOfSyrinx"]; - Phake::when(Arsse::$db)->folderList($this->anything())->thenReturn(new Result($this->folders)); - Phake::when(Arsse::$db)->subscriptionList($this->anything())->thenReturn(new Result($this->subscriptions)); - Phake::when(Arsse::$db)->labelList($this->anything(), false)->thenReturn(new Result($this->usedLabels)); + Phake::when(Arsse::$db)->folderList($this->anything())->thenReturn(new Result($this->v($this->folders))); + Phake::when(Arsse::$db)->subscriptionList($this->anything())->thenReturn(new Result($this->v($this->subscriptions))); + Phake::when(Arsse::$db)->labelList($this->anything(), false)->thenReturn(new Result($this->v($this->usedLabels))); Phake::when(Arsse::$db)->articleCount($this->anything(), $this->anything())->thenReturn(7); // FIXME: this should check an unread+modifiedSince context - Phake::when(Arsse::$db)->articleStarred($this->anything())->thenReturn($this->starred); + Phake::when(Arsse::$db)->articleStarred($this->anything())->thenReturn($this->v($this->starred)); $exp = [ ['id' => "global-unread", 'counter' => 35], ['id' => "subscribed-feeds", 'counter' => 6], @@ -925,9 +929,9 @@ LONG_STRING; ['op' => "getLabels", 'sid' => "PriestsOfSyrinx", 'article_id' => 3], ['op' => "getLabels", 'sid' => "PriestsOfSyrinx", 'article_id' => 4], ]; - Phake::when(Arsse::$db)->labelList($this->anything())->thenReturn(new Result($this->labels)); - Phake::when(Arsse::$db)->articleLabelsGet($this->anything(), 1)->thenReturn([1,3]); - Phake::when(Arsse::$db)->articleLabelsGet($this->anything(), 2)->thenReturn([3]); + Phake::when(Arsse::$db)->labelList($this->anything())->thenReturn(new Result($this->v($this->labels))); + Phake::when(Arsse::$db)->articleLabelsGet($this->anything(), 1)->thenReturn($this->v([1,3])); + Phake::when(Arsse::$db)->articleLabelsGet($this->anything(), 2)->thenReturn($this->v([3])); Phake::when(Arsse::$db)->articleLabelsGet($this->anything(), 3)->thenReturn([]); Phake::when(Arsse::$db)->articleLabelsGet($this->anything(), 4)->thenThrow(new ExceptionInput("idMissing")); $exp = [ @@ -1005,11 +1009,11 @@ LONG_STRING; ['op' => "getFeedTree", 'sid' => "PriestsOfSyrinx", 'include_empty' => true], ['op' => "getFeedTree", 'sid' => "PriestsOfSyrinx"], ]; - Phake::when(Arsse::$db)->folderList($this->anything(), null, true)->thenReturn(new Result($this->folders)); - Phake::when(Arsse::$db)->subscriptionList($this->anything())->thenReturn(new Result($this->subscriptions)); - Phake::when(Arsse::$db)->labelList($this->anything(), true)->thenReturn(new Result($this->labels)); + Phake::when(Arsse::$db)->folderList($this->anything(), null, true)->thenReturn(new Result($this->v($this->folders))); + Phake::when(Arsse::$db)->subscriptionList($this->anything())->thenReturn(new Result($this->v($this->subscriptions))); + Phake::when(Arsse::$db)->labelList($this->anything(), true)->thenReturn(new Result($this->v($this->labels))); Phake::when(Arsse::$db)->articleCount($this->anything(), $this->anything())->thenReturn(7); // FIXME: this should check an unread+modifiedSince context - Phake::when(Arsse::$db)->articleStarred($this->anything())->thenReturn($this->starred); + Phake::when(Arsse::$db)->articleStarred($this->anything())->thenReturn($this->v($this->starred)); // the expectations are packed tightly since they're very verbose; one can use var_export() (or convert to JSON) to pretty-print them $exp = ['categories'=>['identifier'=>'id','label'=>'name','items'=>[['name'=>'Special','id'=>'CAT:-1','bare_id'=>-1,'type'=>'category','unread'=>0,'items'=>[['name'=>'All articles','id'=>'FEED:-4','bare_id'=>-4,'icon'=>'images/folder.png','unread'=>35,'type'=>'feed','auxcounter'=>0,'error'=>'','updated'=>'',],['name'=>'Fresh articles','id'=>'FEED:-3','bare_id'=>-3,'icon'=>'images/fresh.png','unread'=>7,'type'=>'feed','auxcounter'=>0,'error'=>'','updated'=>'',],['name'=>'Starred articles','id'=>'FEED:-1','bare_id'=>-1,'icon'=>'images/star.png','unread'=>4,'type'=>'feed','auxcounter'=>0,'error'=>'','updated'=>'',],['name'=>'Published articles','id'=>'FEED:-2','bare_id'=>-2,'icon'=>'images/feed.png','unread'=>0,'type'=>'feed','auxcounter'=>0,'error'=>'','updated'=>'',],['name'=>'Archived articles','id'=>'FEED:0','bare_id'=>0,'icon'=>'images/archive.png','unread'=>0,'type'=>'feed','auxcounter'=>0,'error'=>'','updated'=>'',],['name'=>'Recently read','id'=>'FEED:-6','bare_id'=>-6,'icon'=>'images/time.png','unread'=>0,'type'=>'feed','auxcounter'=>0,'error'=>'','updated'=>'',],],],['name'=>'Labels','id'=>'CAT:-2','bare_id'=>-2,'type'=>'category','unread'=>6,'items'=>[['name'=>'Fascinating','id'=>'FEED:-1027','bare_id'=>-1027,'unread'=>0,'icon'=>'images/label.png','type'=>'feed','auxcounter'=>0,'error'=>'','updated'=>'','fg_color'=>'','bg_color'=>'',],['name'=>'Interesting','id'=>'FEED:-1029','bare_id'=>-1029,'unread'=>0,'icon'=>'images/label.png','type'=>'feed','auxcounter'=>0,'error'=>'','updated'=>'','fg_color'=>'','bg_color'=>'',],['name'=>'Logical','id'=>'FEED:-1025','bare_id'=>-1025,'unread'=>0,'icon'=>'images/label.png','type'=>'feed','auxcounter'=>0,'error'=>'','updated'=>'','fg_color'=>'','bg_color'=>'',],],],['name'=>'Photography','id'=>'CAT:4','bare_id'=>4,'parent_id'=>null,'type'=>'category','auxcounter'=>0,'unread'=>0,'child_unread'=>0,'checkbox'=>false,'param'=>'(0 feeds)','items'=>[],],['name'=>'Politics','id'=>'CAT:3','bare_id'=>3,'parent_id'=>null,'type'=>'category','auxcounter'=>0,'unread'=>0,'child_unread'=>0,'checkbox'=>false,'param'=>'(3 feeds)','items'=>[['name'=>'Local','id'=>'CAT:5','bare_id'=>5,'parent_id'=>3,'type'=>'category','auxcounter'=>0,'unread'=>0,'child_unread'=>0,'checkbox'=>false,'param'=>'(1 feed)','items'=>[['name'=>'Toronto Star','id'=>'FEED:2','bare_id'=>2,'icon'=>'feed-icons/2.ico','error'=>'oops','param'=>'2011-11-11T11:11:11','unread'=>0,'auxcounter'=>0,'checkbox'=>false,],],],['name'=>'National','id'=>'CAT:6','bare_id'=>6,'parent_id'=>3,'type'=>'category','auxcounter'=>0,'unread'=>0,'child_unread'=>0,'checkbox'=>false,'param'=>'(2 feeds)','items'=>[['name'=>'CBC News','id'=>'FEED:4','bare_id'=>4,'icon'=>'feed-icons/4.ico','error'=>'','param'=>'2017-10-09T15:58:34','unread'=>0,'auxcounter'=>0,'checkbox'=>false,],['name'=>'Ottawa Citizen','id'=>'FEED:5','bare_id'=>5,'icon'=>false,'error'=>'','param'=>'2017-07-07T17:07:17','unread'=>0,'auxcounter'=>0,'checkbox'=>false,],],],],],['name'=>'Science','id'=>'CAT:1','bare_id'=>1,'parent_id'=>null,'type'=>'category','auxcounter'=>0,'unread'=>0,'child_unread'=>0,'checkbox'=>false,'param'=>'(2 feeds)','items'=>[['name'=>'Rocketry','id'=>'CAT:2','bare_id'=>2,'parent_id'=>1,'type'=>'category','auxcounter'=>0,'unread'=>0,'child_unread'=>0,'checkbox'=>false,'param'=>'(1 feed)','items'=>[['name'=>'NASA JPL','id'=>'FEED:1','bare_id'=>1,'icon'=>false,'error'=>'','param'=>'2017-09-15T22:54:16','unread'=>0,'auxcounter'=>0,'checkbox'=>false,],],],['name'=>'Ars Technica','id'=>'FEED:3','bare_id'=>3,'icon'=>'feed-icons/3.ico','error'=>'argh','param'=>'2016-05-23T06:40:02','unread'=>0,'auxcounter'=>0,'checkbox'=>false,],],],['name'=>'Uncategorized','id'=>'CAT:0','bare_id'=>0,'type'=>'category','auxcounter'=>0,'unread'=>0,'child_unread'=>0,'checkbox'=>false,'parent_id'=>null,'param'=>'(1 feed)','items'=>[['name'=>'Eurogamer','id'=>'FEED:6','bare_id'=>6,'icon'=>'feed-icons/6.ico','error'=>'','param'=>'2010-02-12T20:08:47','unread'=>0,'auxcounter'=>0,'checkbox'=>false,],],],],],]; $this->assertResponse($this->respGood($exp), $this->req($in[0])); @@ -1094,24 +1098,24 @@ LONG_STRING; ['op' => "getFeeds", 'sid' => "PriestsOfSyrinx", 'cat_id' => 6, 'offset' => 2], ]; // statistical mocks - Phake::when(Arsse::$db)->articleStarred($this->anything())->thenReturn($this->starred); + Phake::when(Arsse::$db)->articleStarred($this->anything())->thenReturn($this->v($this->starred)); Phake::when(Arsse::$db)->articleCount->thenReturn(7); // FIXME: this should check an unread+modifiedSince context Phake::when(Arsse::$db)->articleCount($this->anything(), (new Context)->unread(true))->thenReturn(35); // label mocks - Phake::when(Arsse::$db)->labelList($this->anything())->thenReturn(new Result($this->labels)); - Phake::when(Arsse::$db)->labelList($this->anything(), false)->thenReturn(new Result($this->usedLabels)); + Phake::when(Arsse::$db)->labelList($this->anything())->thenReturn(new Result($this->v($this->labels))); + Phake::when(Arsse::$db)->labelList($this->anything(), false)->thenReturn(new Result($this->v($this->usedLabels))); // subscription and folder list and unread count mocks Phake::when(Arsse::$db)->folderList->thenThrow(new ExceptionInput("subjectMissing")); Phake::when(Arsse::$db)->subscriptionList->thenThrow(new ExceptionInput("subjectMissing")); - Phake::when(Arsse::$db)->folderList($this->anything())->thenReturn(new Result($this->folders)); - Phake::when(Arsse::$db)->subscriptionList($this->anything(), null, true)->thenReturn(new Result($this->subscriptions)); - Phake::when(Arsse::$db)->subscriptionList($this->anything(), null, false)->thenReturn(new Result($this->filterSubs(null))); - Phake::when(Arsse::$db)->folderList($this->anything(), null)->thenReturn(new Result($this->folders)); - Phake::when(Arsse::$db)->folderList($this->anything(), null, false)->thenReturn(new Result($this->filterFolders(null))); + Phake::when(Arsse::$db)->folderList($this->anything())->thenReturn(new Result($this->v($this->folders))); + Phake::when(Arsse::$db)->subscriptionList($this->anything(), null, true)->thenReturn(new Result($this->v($this->subscriptions))); + Phake::when(Arsse::$db)->subscriptionList($this->anything(), null, false)->thenReturn(new Result($this->v($this->filterSubs(null)))); + Phake::when(Arsse::$db)->folderList($this->anything(), null)->thenReturn(new Result($this->v($this->folders))); + Phake::when(Arsse::$db)->folderList($this->anything(), null, false)->thenReturn(new Result($this->v($this->filterFolders(null)))); foreach ($this->folders as $f) { - Phake::when(Arsse::$db)->folderList($this->anything(), $f['id'], false)->thenReturn(new Result($this->filterFolders($f['id']))); + Phake::when(Arsse::$db)->folderList($this->anything(), $f['id'], false)->thenReturn(new Result($this->v($this->filterFolders($f['id'])))); Phake::when(Arsse::$db)->articleCount($this->anything(), (new Context)->unread(true)->folder($f['id']))->thenReturn($this->reduceFolders($f['id'])); - Phake::when(Arsse::$db)->subscriptionList($this->anything(), $f['id'], false)->thenReturn(new Result($this->filterSubs($f['id']))); + Phake::when(Arsse::$db)->subscriptionList($this->anything(), $f['id'], false)->thenReturn(new Result($this->v($this->filterSubs($f['id'])))); } $exp = [ [ @@ -1263,10 +1267,10 @@ LONG_STRING; ['op' => "updateArticle", 'sid' => "PriestsOfSyrinx", 'article_ids' => "42, 2112, -1", 'field' => 4], // invalid field ['op' => "updateArticle", 'sid' => "PriestsOfSyrinx", 'article_ids' => "0, -1", 'field' => 3], // no valid IDs ]; - Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->articles([42, 2112])->starred(true), $this->anything())->thenReturn(new Result([['id' => 42]])); - Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->articles([42, 2112])->starred(false), $this->anything())->thenReturn(new Result([['id' => 2112]])); - Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->articles([42, 2112])->unread(true), $this->anything())->thenReturn(new Result([['id' => 42]])); - Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->articles([42, 2112])->unread(false), $this->anything())->thenReturn(new Result([['id' => 2112]])); + Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->articles([42, 2112])->starred(true), $this->anything())->thenReturn(new Result($this->v([['id' => 42]]))); + Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->articles([42, 2112])->starred(false), $this->anything())->thenReturn(new Result($this->v([['id' => 2112]]))); + Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->articles([42, 2112])->unread(true), $this->anything())->thenReturn(new Result($this->v([['id' => 42]]))); + Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->articles([42, 2112])->unread(false), $this->anything())->thenReturn(new Result($this->v([['id' => 2112]]))); Phake::when(Arsse::$db)->articleMark->thenReturn(1); Phake::when(Arsse::$db)->articleMark($this->anything(), ['starred' => false], (new Context)->articles([42, 2112]))->thenReturn(2); Phake::when(Arsse::$db)->articleMark($this->anything(), ['starred' => true], (new Context)->articles([42, 2112]))->thenReturn(4); @@ -1327,13 +1331,13 @@ LONG_STRING; ['op' => "getArticle", 'sid' => "PriestsOfSyrinx", 'article_id' => "101"], ['op' => "getArticle", 'sid' => "PriestsOfSyrinx", 'article_id' => "102"], ]; - Phake::when(Arsse::$db)->labelList($this->anything())->thenReturn(new Result($this->labels)); - Phake::when(Arsse::$db)->labelList($this->anything(), false)->thenReturn(new Result($this->usedLabels)); + Phake::when(Arsse::$db)->labelList($this->anything())->thenReturn(new Result($this->v($this->labels))); + Phake::when(Arsse::$db)->labelList($this->anything(), false)->thenReturn(new Result($this->v($this->usedLabels))); Phake::when(Arsse::$db)->articleLabelsGet($this->anything(), 101)->thenReturn([]); - Phake::when(Arsse::$db)->articleLabelsGet($this->anything(), 102)->thenReturn([1,3]); - Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->articles([101, 102]))->thenReturn(new Result($this->articles)); - Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->articles([101]))->thenReturn(new Result([$this->articles[0]])); - Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->articles([102]))->thenReturn(new Result([$this->articles[1]])); + Phake::when(Arsse::$db)->articleLabelsGet($this->anything(), 102)->thenReturn($this->v([1,3])); + Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->articles([101, 102]))->thenReturn(new Result($this->v($this->articles))); + Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->articles([101]))->thenReturn(new Result($this->v([$this->articles[0]]))); + Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->articles([102]))->thenReturn(new Result($this->v([$this->articles[1]]))); $exp = $this->respErr("INCORRECT_USAGE"); $this->assertResponse($exp, $this->req($in[0])); $this->assertResponse($exp, $this->req($in[1])); @@ -1436,22 +1440,22 @@ LONG_STRING; ['op' => "getCompactHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -3], ['op' => "getCompactHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -3, 'view_mode' => "marked"], ]; - Phake::when(Arsse::$db)->articleList->thenReturn(new Result([['id' => 0]])); + Phake::when(Arsse::$db)->articleList->thenReturn(new Result($this->v([['id' => 0]]))); Phake::when(Arsse::$db)->articleCount->thenReturn(0); Phake::when(Arsse::$db)->articleCount($this->anything(), (new Context)->unread(true))->thenReturn(1); $c = (new Context)->reverse(true); Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->subscription(2112), Database::LIST_MINIMAL)->thenThrow(new ExceptionInput("subjectMissing")); - Phake::when(Arsse::$db)->articleList($this->anything(), $c, Database::LIST_MINIMAL)->thenReturn(new Result($this->articles)); - Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->starred(true), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 1]])); - Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->label(1088), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 2]])); - Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->unread(true), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 3]])); - Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->label(1088)->unread(true), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 4]])); - Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->subscription(42)->starred(true), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 5]])); - Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->subscription(42)->annotated(true), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 6]])); - Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->limit(5), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 7]])); - Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->offset(2), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 8]])); - Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->limit(5)->offset(2), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 9]])); - Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->oldestArticle(48), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 10]])); + Phake::when(Arsse::$db)->articleList($this->anything(), $c, Database::LIST_MINIMAL)->thenReturn(new Result($this->v($this->articles))); + Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->starred(true), Database::LIST_MINIMAL)->thenReturn(new Result($this->v([['id' => 1]]))); + Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->label(1088), Database::LIST_MINIMAL)->thenReturn(new Result($this->v([['id' => 2]]))); + Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->unread(true), Database::LIST_MINIMAL)->thenReturn(new Result($this->v([['id' => 3]]))); + Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->label(1088)->unread(true), Database::LIST_MINIMAL)->thenReturn(new Result($this->v([['id' => 4]]))); + Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->subscription(42)->starred(true), Database::LIST_MINIMAL)->thenReturn(new Result($this->v([['id' => 5]]))); + Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->subscription(42)->annotated(true), Database::LIST_MINIMAL)->thenReturn(new Result($this->v([['id' => 6]]))); + Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->limit(5), Database::LIST_MINIMAL)->thenReturn(new Result($this->v([['id' => 7]]))); + Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->offset(2), Database::LIST_MINIMAL)->thenReturn(new Result($this->v([['id' => 8]]))); + Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->limit(5)->offset(2), Database::LIST_MINIMAL)->thenReturn(new Result($this->v([['id' => 9]]))); + Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->oldestArticle(48), Database::LIST_MINIMAL)->thenReturn(new Result($this->v([['id' => 10]]))); $out1 = [ $this->respErr("INCORRECT_USAGE"), $this->respGood([]), @@ -1483,9 +1487,9 @@ LONG_STRING; $this->assertResponse($out1[$a], $this->req($in1[$a]), "Test $a failed"); } for ($a = 0; $a < sizeof($in2); $a++) { - Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->unread(false)->markedSince(Date::sub("PT24H")), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 1001]])); - Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->unread(true)->modifiedSince(Date::sub("PT24H")), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 1002]])); - Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->unread(true)->modifiedSince(Date::sub("PT24H"))->starred(true), Database::LIST_MINIMAL)->thenReturn(new Result([['id' => 1003]])); + Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->unread(false)->markedSince(Date::sub("PT24H")), Database::LIST_MINIMAL)->thenReturn(new Result($this->v([['id' => 1001]]))); + Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->unread(true)->modifiedSince(Date::sub("PT24H")), Database::LIST_MINIMAL)->thenReturn(new Result($this->v([['id' => 1002]]))); + Phake::when(Arsse::$db)->articleList($this->anything(), (clone $c)->unread(true)->modifiedSince(Date::sub("PT24H"))->starred(true), Database::LIST_MINIMAL)->thenReturn(new Result($this->v([['id' => 1003]]))); $this->assertResponse($out2[$a], $this->req($in2[$a]), "Test $a failed"); } } @@ -1531,10 +1535,10 @@ LONG_STRING; ['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -3], ['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -3, 'view_mode' => "marked"], ]; - Phake::when(Arsse::$db)->labelList($this->anything())->thenReturn(new Result($this->labels)); - Phake::when(Arsse::$db)->labelList($this->anything(), false)->thenReturn(new Result($this->usedLabels)); + Phake::when(Arsse::$db)->labelList($this->anything())->thenReturn(new Result($this->v($this->labels))); + Phake::when(Arsse::$db)->labelList($this->anything(), false)->thenReturn(new Result($this->v($this->usedLabels))); Phake::when(Arsse::$db)->articleLabelsGet->thenReturn([]); - Phake::when(Arsse::$db)->articleLabelsGet($this->anything(), 2112)->thenReturn([1,3]); + Phake::when(Arsse::$db)->articleLabelsGet($this->anything(), 2112)->thenReturn($this->v([1,3])); Phake::when(Arsse::$db)->articleCategoriesGet->thenReturn([]); Phake::when(Arsse::$db)->articleCategoriesGet($this->anything(), 2112)->thenReturn(["Boring","Illogical"]); Phake::when(Arsse::$db)->articleList->thenReturn($this->generateHeadlines(0)); @@ -1616,10 +1620,10 @@ LONG_STRING; ['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => 42, 'skip' => 47, 'include_header' => true, 'order_by' => "date_reverse"], ['op' => "getHeadlines", 'sid' => "PriestsOfSyrinx", 'feed_id' => -4, 'show_excerpt' => true], ]; - Phake::when(Arsse::$db)->labelList($this->anything())->thenReturn(new Result($this->labels)); - Phake::when(Arsse::$db)->labelList($this->anything(), false)->thenReturn(new Result($this->usedLabels)); + Phake::when(Arsse::$db)->labelList($this->anything())->thenReturn(new Result($this->v($this->labels))); + Phake::when(Arsse::$db)->labelList($this->anything(), false)->thenReturn(new Result($this->v($this->usedLabels))); Phake::when(Arsse::$db)->articleLabelsGet->thenReturn([]); - Phake::when(Arsse::$db)->articleLabelsGet($this->anything(), 2112)->thenReturn([1,3]); + Phake::when(Arsse::$db)->articleLabelsGet($this->anything(), 2112)->thenReturn($this->v([1,3])); Phake::when(Arsse::$db)->articleCategoriesGet->thenReturn([]); Phake::when(Arsse::$db)->articleCategoriesGet($this->anything(), 2112)->thenReturn(["Boring","Illogical"]); Phake::when(Arsse::$db)->articleList->thenReturn($this->generateHeadlines(1)); @@ -1720,7 +1724,7 @@ LONG_STRING; } protected function generateHeadlines(int $id): Result { - return new Result([ + return new Result($this->v([ [ 'id' => $id, 'url' => 'http://example.com/1', @@ -1761,7 +1765,7 @@ LONG_STRING; 'media_type' => "text/plain", 'note' => "Note 2", ], - ]); + ])); } protected function outputHeadlines(int $id): Response { From 77793f95cbb90362e8ff58615b7c4aa42803f49d Mon Sep 17 00:00:00 2001 From: "J. King" Date: Sun, 31 Dec 2017 17:24:40 -0500 Subject: [PATCH 14/16] Controller fixes for PDO databases; fixes #72 --- lib/REST/NextCloudNews/V1_2.php | 4 +- lib/REST/TinyTinyRSS/API.php | 50 +++++++++---------- .../cases/REST/NextCloudNews/PDO/TestV1_2.php | 24 +++++++++ tests/cases/REST/TinyTinyRSS/PDO/TestAPI.php | 25 ++++++++++ tests/phpunit.xml | 2 + 5 files changed, 78 insertions(+), 27 deletions(-) create mode 100644 tests/cases/REST/NextCloudNews/PDO/TestV1_2.php create mode 100644 tests/cases/REST/TinyTinyRSS/PDO/TestAPI.php diff --git a/lib/REST/NextCloudNews/V1_2.php b/lib/REST/NextCloudNews/V1_2.php index 814ee57..2b8fa5d 100644 --- a/lib/REST/NextCloudNews/V1_2.php +++ b/lib/REST/NextCloudNews/V1_2.php @@ -353,7 +353,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { $out = []; foreach ($feeds as $feed) { // since in our implementation feeds don't belong the users, the 'userId' field will always be an empty string - $out[] = ['id' => $feed, 'userId' => ""]; + $out[] = ['id' => (int) $feed, 'userId' => ""]; } return new Response(200, ['feeds' => $out]); } @@ -419,7 +419,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { $out[] = $this->feedTranslate($sub); } $out = ['feeds' => $out]; - $out['starredCount'] = Arsse::$db->articleStarred(Arsse::$user->id)['total']; + $out['starredCount'] = (int) Arsse::$db->articleStarred(Arsse::$user->id)['total']; $newest = Arsse::$db->editionLatest(Arsse::$user->id); if ($newest) { $out['newestItemId'] = $newest; diff --git a/lib/REST/TinyTinyRSS/API.php b/lib/REST/TinyTinyRSS/API.php index 7435c80..d3f7728 100644 --- a/lib/REST/TinyTinyRSS/API.php +++ b/lib/REST/TinyTinyRSS/API.php @@ -231,7 +231,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { // prepare data for each subscription; we also add unread counts for their host categories foreach (Arsse::$db->subscriptionList($user) as $f) { // add the feed to the list of feeds - $feeds[] = ['id' => (string) $f['id'], 'updated' => Date::transform($f['updated'], "iso8601", "sql"),'counter' => $f['unread'], 'has_img' => (int) (strlen((string) $f['favicon']) > 0)]; // ID is cast to string for consistency with TTRSS + $feeds[] = ['id' => (string) $f['id'], 'updated' => Date::transform($f['updated'], "iso8601", "sql"),'counter' => (int) $f['unread'], 'has_img' => (int) (strlen((string) $f['favicon']) > 0)]; // ID is cast to string for consistency with TTRSS // add the feed's unread count to the global unread count $countAll += $f['unread']; // add the feed's unread count to its category unread count @@ -242,7 +242,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { // prepare data for each non-empty label foreach (Arsse::$db->labelList($user, false) as $l) { $unread = $l['articles'] - $l['read']; - $labels[] = ['id' => $this->labelOut($l['id']), 'counter' => $unread, 'auxcounter' => $l['articles']]; + $labels[] = ['id' => $this->labelOut($l['id']), 'counter' => $unread, 'auxcounter' => (int) $l['articles']]; $categories[$catmap[self::CAT_LABELS]]['counter'] += $unread; } // do a second pass on categories, summing descendant unread counts for ancestors @@ -266,14 +266,14 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { } // do a third pass on categories, building a final category list; this is done so that the original sort order is retained foreach ($categories as $c) { - $cats[] = ['id' => $c['id'], 'kind' => "cat", 'counter' => $catCounts[$c['id']]]; + $cats[] = ['id' => (int) $c['id'], 'kind' => "cat", 'counter' => $catCounts[$c['id']]]; } // prepare data for the virtual feeds and other counters $special = [ ['id' => "global-unread", 'counter' => $countAll], //this should not count archived articles, but we do not have an archive ['id' => "subscribed-feeds", 'counter' => $countSubs], ['id' => self::FEED_ARCHIVED, 'counter' => 0, 'auxcounter' => 0], // Archived articles - ['id' => self::FEED_STARRED, 'counter' => $starred['unread'], 'auxcounter' => $starred['total']], // Starred articles + ['id' => self::FEED_STARRED, 'counter' => (int) $starred['unread'], 'auxcounter' => (int) $starred['total']], // Starred articles ['id' => self::FEED_PUBLISHED, 'counter' => 0, 'auxcounter' => 0], // Published articles ['id' => self::FEED_FRESH, 'counter' => $fresh, 'auxcounter' => 0], // Fresh articles ['id' => self::FEED_ALL, 'counter' => $countAll, 'auxcounter' => 0], // All articles @@ -323,7 +323,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { 'id' => "FEED:".self::FEED_STARRED, 'bare_id' => self::FEED_STARRED, 'icon' => "images/star.png", - 'unread' => Arsse::$db->articleStarred($user)['unread'], + 'unread' => (int) Arsse::$db->articleStarred($user)['unread'], ], $tSpecial), array_merge([ // Published articles 'name' => Arsse::$lang->msg("API.TTRSS.Feed.Published"), @@ -406,7 +406,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { return ['categories' => ['identifier' => "id", 'label' => "name", 'items' => $out]]; } - protected function enumerateFeeds(array $subs, int $parent = null): array { + protected function enumerateFeeds(array $subs, $parent = null): array { $out = []; foreach ($subs as $s) { if ($s['folder'] != $parent) { @@ -415,7 +415,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { $out[] = [ 'name' => $s['title'], 'id' => "FEED:".$s['id'], - 'bare_id' => $s['id'], + 'bare_id' => (int) $s['id'], 'icon' => $s['favicon'] ? "feed-icons/".$s['id'].".ico" : false, 'error' => (string) $s['err_msg'], 'param' => Date::transform($s['updated'], "iso8601", "sql"), @@ -428,7 +428,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { return $out; } - protected function enumerateCategories(array $cats, array $subs, int $parent = null, bool $all = false): array { + protected function enumerateCategories(array $cats, array $subs, $parent = null, bool $all = false): array { $out = []; $feedTotal = 0; foreach ($cats as $c) { @@ -442,8 +442,8 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { $out[] = [ 'name' => $c['name'], 'id' => "CAT:".$c['id'], - 'bare_id' => $c['id'], - 'parent_id' => $c['parent'], // top-level categories are not supposed to have this property; we deviated and have the property set to null because it's simpler that way + 'bare_id' => (int) $c['id'], + 'parent_id' => (int) $c['parent'] ?: null, // top-level categories are not supposed to have this property; we deviated and have the property set to null because it's simpler that way 'type' => "category", 'auxcounter' => 0, 'unread' => 0, @@ -714,13 +714,13 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { // NOTE: the list is a flat one: it includes children, but not other descendents foreach (Arsse::$db->folderList($user, $cat, false) as $c) { // get the number of unread for the category and its descendents; those with zero unread are excluded in "unread-only" mode - $count = Arsse::$db->articleCount($user, (new Context)->unread(true)->folder($c['id'])); + $count = Arsse::$db->articleCount($user, (new Context)->unread(true)->folder((int) $c['id'])); if (!$unread || $count) { $out[] = [ - 'id' => $c['id'], - 'title' => $c['name'], - 'unread' => $count, - 'is_cat' => true, + 'id' => (int) $c['id'], + 'title' => $c['name'], + 'unread' => (int) $count, + 'is_cat' => true, 'order_id' => ++$order, ]; } @@ -764,9 +764,9 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { } // otherwise, append the subscription $out[] = [ - 'id' => $s['id'], + 'id' => (int) $s['id'], 'title' => $s['title'], - 'unread' => $s['unread'], + 'unread' => (int) $s['unread'], 'cat_id' => (int) $s['folder'], 'feed_url' => $s['url'], 'has_icon' => (bool) $s['favicon'], @@ -920,8 +920,8 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { return (abs($id) - self::LABEL_OFFSET); } - protected function labelOut(int $id): int { - return ($id * -1 - self::LABEL_OFFSET); + protected function labelOut($id): int { + return ((int) $id * -1 - self::LABEL_OFFSET); } public function opGetLabels(array $data): array { @@ -1194,12 +1194,12 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { return $out; } - protected function articleLabelList(array $labels, int $id): array { + protected function articleLabelList(array $labels, $id): array { $out = []; if (!$labels) { return $out; } - foreach (Arsse::$db->articleLabelsGet(Arsse::$user->id, $id) as $label) { + foreach (Arsse::$db->articleLabelsGet(Arsse::$user->id, (int) $id) as $label) { $out[] = [ $this->labelOut($label), // ID $labels[$label], // name @@ -1224,7 +1224,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { $out = []; try { foreach ($this->fetchArticles($data, Database::LIST_MINIMAL) as $row) { - $out[] = ['id' => $row['id']]; + $out[] = ['id' => (int) $row['id']]; } } catch (ExceptionInput $e) { // ignore database errors (feeds/categories that don't exist) @@ -1246,7 +1246,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { try { foreach ($this->fetchArticles($data, Database::LIST_FULL) as $article) { $row = [ - 'id' => $article['id'], + 'id' => (int) $article['id'], 'guid' => $article['guid'] ? "SHA256:".$article['guid'] : "", 'title' => $article['title'], 'link' => $article['url'], @@ -1313,9 +1313,9 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { // wrap the output with (but after) the header $out = [ [ - 'id' => $data['feed_id'], + 'id' => (int) $data['feed_id'], 'is_cat' => $data['is_cat'] ?? false, - 'first_id' => $firstID, + 'first_id' => (int) $firstID, ], $out, ]; diff --git a/tests/cases/REST/NextCloudNews/PDO/TestV1_2.php b/tests/cases/REST/NextCloudNews/PDO/TestV1_2.php new file mode 100644 index 0000000..ecf4fb7 --- /dev/null +++ b/tests/cases/REST/NextCloudNews/PDO/TestV1_2.php @@ -0,0 +1,24 @@ + */ +class TestV1_2 extends \JKingWeb\Arsse\TestCase\REST\NextCloudNews\TestV1_2 { + protected function v($value) { + if (!is_array($value)) { + return $value; + } + foreach($value as $k => $v) { + if (is_array($v)) { + $value[$k] = $this->v($v); + } elseif (is_int($v) || is_float($v)) { + $value[$k] = (string) $v; + } + } + return $value; + } +} diff --git a/tests/cases/REST/TinyTinyRSS/PDO/TestAPI.php b/tests/cases/REST/TinyTinyRSS/PDO/TestAPI.php new file mode 100644 index 0000000..7586eee --- /dev/null +++ b/tests/cases/REST/TinyTinyRSS/PDO/TestAPI.php @@ -0,0 +1,25 @@ + + * @covers \JKingWeb\Arsse\REST\TinyTinyRSS\Exception */ +class TestAPI extends \JKingWeb\Arsse\TestCase\REST\TinyTinyRSS\TestAPI { + protected function v($value) { + if (!is_array($value)) { + return $value; + } + foreach($value as $k => $v) { + if (is_array($v)) { + $value[$k] = $this->v($v); + } elseif (is_int($v) || is_float($v)) { + $value[$k] = (string) $v; + } + } + return $value; + } +} \ No newline at end of file diff --git a/tests/phpunit.xml b/tests/phpunit.xml index 9696d57..12fe2c0 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -85,10 +85,12 @@ cases/REST/NextCloudNews/TestVersions.php cases/REST/NextCloudNews/TestV1_2.php + cases/REST/NextCloudNews/PDO/TestV1_2.php cases/REST/TinyTinyRSS/TestAPI.php cases/REST/TinyTinyRSS/TestIcon.php + cases/REST/TinyTinyRSS/PDO/TestAPI.php From 65b08d173519bb3b21a0e8b2907c415cbe502441 Mon Sep 17 00:00:00 2001 From: "J. King" Date: Sun, 31 Dec 2017 17:30:30 -0500 Subject: [PATCH 15/16] Tweak --- .../cases/REST/NextCloudNews/PDO/TestV1_2.php | 14 +---------- tests/cases/REST/TinyTinyRSS/PDO/TestAPI.php | 14 +---------- tests/lib/PDOTest.php | 23 +++++++++++++++++++ 3 files changed, 25 insertions(+), 26 deletions(-) create mode 100644 tests/lib/PDOTest.php diff --git a/tests/cases/REST/NextCloudNews/PDO/TestV1_2.php b/tests/cases/REST/NextCloudNews/PDO/TestV1_2.php index ecf4fb7..c6a3ef7 100644 --- a/tests/cases/REST/NextCloudNews/PDO/TestV1_2.php +++ b/tests/cases/REST/NextCloudNews/PDO/TestV1_2.php @@ -8,17 +8,5 @@ namespace JKingWeb\Arsse\TestCase\REST\NextCloudNews\PDO; /** @covers \JKingWeb\Arsse\REST\NextCloudNews\V1_2 */ class TestV1_2 extends \JKingWeb\Arsse\TestCase\REST\NextCloudNews\TestV1_2 { - protected function v($value) { - if (!is_array($value)) { - return $value; - } - foreach($value as $k => $v) { - if (is_array($v)) { - $value[$k] = $this->v($v); - } elseif (is_int($v) || is_float($v)) { - $value[$k] = (string) $v; - } - } - return $value; - } + use \JKingWeb\Arsse\Test\PDOTest; } diff --git a/tests/cases/REST/TinyTinyRSS/PDO/TestAPI.php b/tests/cases/REST/TinyTinyRSS/PDO/TestAPI.php index 7586eee..9c9e36f 100644 --- a/tests/cases/REST/TinyTinyRSS/PDO/TestAPI.php +++ b/tests/cases/REST/TinyTinyRSS/PDO/TestAPI.php @@ -9,17 +9,5 @@ namespace JKingWeb\Arsse\TestCase\REST\TinyTinyRSS\PDO; /** @covers \JKingWeb\Arsse\REST\TinyTinyRSS\API * @covers \JKingWeb\Arsse\REST\TinyTinyRSS\Exception */ class TestAPI extends \JKingWeb\Arsse\TestCase\REST\TinyTinyRSS\TestAPI { - protected function v($value) { - if (!is_array($value)) { - return $value; - } - foreach($value as $k => $v) { - if (is_array($v)) { - $value[$k] = $this->v($v); - } elseif (is_int($v) || is_float($v)) { - $value[$k] = (string) $v; - } - } - return $value; - } + use \JKingWeb\Arsse\Test\PDOTest; } \ No newline at end of file diff --git a/tests/lib/PDOTest.php b/tests/lib/PDOTest.php new file mode 100644 index 0000000..3013734 --- /dev/null +++ b/tests/lib/PDOTest.php @@ -0,0 +1,23 @@ + $v) { + if (is_array($v)) { + $value[$k] = $this->v($v); + } elseif (is_int($v) || is_float($v)) { + $value[$k] = (string) $v; + } + } + return $value; + } +} \ No newline at end of file From a8f8a27c651b885f30221d253e2090ae7242b2c9 Mon Sep 17 00:00:00 2001 From: "J. King" Date: Sun, 31 Dec 2017 17:54:26 -0500 Subject: [PATCH 16/16] Documentation changes for PDO support --- CHANGELOG | 6 ++++++ README.md | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 19d5a44..40ebe4b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +Version 0.3.0 (2018-??-??) +========================== + +New features: +- Support for SQLite3 via PDO + Version 0.2.1 (2017-12-07) ========================== diff --git a/README.md b/README.md index 77addd6..79da425 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The Arsse has the following requirements: - PHP 7.0.7 or later with the following extensions: - [intl](http://php.net/manual/en/book.intl.php), [json](http://php.net/manual/en/book.json.php), [hash](http://php.net/manual/en/book.hash.php), and [pcre](http://php.net/manual/en/book.pcre.php) - [dom](http://php.net/manual/en/book.dom.php), [simplexml](http://php.net/manual/en/book.simplexml.php), and [iconv](http://php.net/manual/en/book.iconv.php) (for picoFeed) - - [sqlite3](http://php.net/manual/en/book.sqlite3.php) + - [sqlite3](http://php.net/manual/en/book.sqlite3.php) or [pdo_sqlite](http://ca1.php.net/manual/en/ref.pdo-sqlite.php) - Privileges to create and run daemon processes on the server ## Installation