diff --git a/lib/Db/AbstractDriver.php b/lib/Db/AbstractDriver.php index 814159e4..6dbfb0a5 100644 --- a/lib/Db/AbstractDriver.php +++ b/lib/Db/AbstractDriver.php @@ -17,14 +17,6 @@ abstract class AbstractDriver implements Driver { abstract protected function unlock(bool $rollback = false): bool; abstract protected function getError(): string; - public function schemaVersion(): int { - try { - return (int) $this->query("SELECT value from arsse_meta where key = 'schema_version'")->getValue(); - } catch (Exception $e) { - return 0; - } - } - public function schemaUpdate(int $to, string $basePath = null): bool { $ver = $this->schemaVersion(); if (!Arsse::$conf->dbAutoUpdate) { diff --git a/lib/Db/PostgreSQL/Driver.php b/lib/Db/PostgreSQL/Driver.php index d70801e8..5b243857 100644 --- a/lib/Db/PostgreSQL/Driver.php +++ b/lib/Db/PostgreSQL/Driver.php @@ -101,6 +101,14 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver { return $this->query("SELECT pg_encoding_to_char(encoding) from pg_database where datname = current_database()")->getValue() == "UTF8"; } + public function schemaVersion(): int { + if ($this->query("SELECT count(*) from information_schema.tables where table_name = 'arsse_meta' and table_schema = current_schema()")->getValue()) { + return (int) $this->query("SELECT value from arsse_meta where key = 'schema_version'")->getValue(); + } else { + return 0; + } + } + public function savepointCreate(bool $lock = false): int { if (!$this->transStart) { $this->exec("BEGIN TRANSACTION"); diff --git a/tests/cases/Db/BaseDriver.php b/tests/cases/Db/BaseDriver.php index 87f2d173..ad04409f 100644 --- a/tests/cases/Db/BaseDriver.php +++ b/tests/cases/Db/BaseDriver.php @@ -82,6 +82,11 @@ abstract class BaseDriver extends \JKingWeb\Arsse\Test\AbstractTest { $class = get_class($this->drv); $this->assertTrue(strlen($class::driverName()) > 0); } + + public function testFetchSchemaId() { + $class = get_class($this->drv); + $this->assertTrue(strlen($class::schemaID()) > 0); + } public function testCheckCharacterSetAcceptability() { $this->assertTrue($this->drv->charsetAcceptable()); diff --git a/tests/cases/Db/BaseUpdate.php b/tests/cases/Db/BaseUpdate.php new file mode 100644 index 00000000..b4de0ea3 --- /dev/null +++ b/tests/cases/Db/BaseUpdate.php @@ -0,0 +1,136 @@ +interfaceConstructor)(); + } + + public function setUp() { + if (!static::$interface) { + $this->markTestSkipped(static::$implementation." database driver not available"); + } + self::clearData(); + self::setConf(); + // construct a fresh driver for each test + $this->drv = new static::$dbInfo->driverClass; + $schemaId = (get_class($this->drv))::schemaID(); + // set up a virtual filesystem for schema files + $this->vfs = vfsStream::setup("schemata", null, [$schemaId => []]); + $this->base = $this->vfs->url(); + $this->path = $this->base."/$schemaId/"; + // completely clear the database + (static::$dbInfo->razeFunction)(static::$interface); + } + + public function tearDown() { + // deconstruct the driver + unset($this->drv); + if (static::$interface) { + // completely clear the database + (static::$dbInfo->razeFunction)(static::$interface); + } + unset($this->path, $this->base, $this->vfs); + self::clearData(); + } + + public static function tearDownAfterClass() { + static::$implementation = null; + static::$dbInfo = null; + self::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", static::$minimal1); + $this->drv->schemaUpdate(1, $this->base); + $this->assertEquals(1, $this->drv->schemaVersion()); + } + + public function testPerformPartialUpdate() { + file_put_contents($this->path."0.sql", static::$minimal1); + file_put_contents($this->path."1.sql", "UPDATE arsse_meta set value = '1' where key = 'schema_version'"); + $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", static::$minimal1); + file_put_contents($this->path."1.sql", static::$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 + Arsse::$conf->dbAutoUpdate = false; + $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/cases/Db/PostgreSQL/TestUpdate.php b/tests/cases/Db/PostgreSQL/TestUpdate.php new file mode 100644 index 00000000..62f9140b --- /dev/null +++ b/tests/cases/Db/PostgreSQL/TestUpdate.php @@ -0,0 +1,16 @@ + + * @covers \JKingWeb\Arsse\Db\PDOError */ +class TestUpdate extends \JKingWeb\Arsse\TestCase\Db\BaseUpdate { + protected static $implementation = "PDO PostgreSQL"; + protected static $minimal1 = "CREATE TABLE arsse_meta(key text primary key, value text); INSERT INTO arsse_meta(key,value) values('schema_version','1');"; + protected static $minimal2 = "UPDATE arsse_meta set value = '2' where key = 'schema_version';"; +} diff --git a/tests/cases/Db/SQLite3/TestUpdate.php b/tests/cases/Db/SQLite3/TestUpdate.php index 1c219a1d..ecea58b9 100644 --- a/tests/cases/Db/SQLite3/TestUpdate.php +++ b/tests/cases/Db/SQLite3/TestUpdate.php @@ -6,113 +6,11 @@ declare(strict_types=1); 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 TestUpdate extends \JKingWeb\Arsse\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(array $conf = []) { - if (!Driver::requirementsMet()) { - $this->markTestSkipped("SQLite extension not loaded"); - } - self::clearData(); - $this->vfs = vfsStream::setup("schemata", null, ['SQLite3' => []]); - self::setConf($conf); - $this->base = $this->vfs->url(); - $this->path = $this->base."/SQLite3/"; - $this->drv = new Driver(); - } - - public function tearDown() { - unset($this->drv); - unset($this->data); - unset($this->vfs); - self::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 - $this->setUp(['dbAutoUpdate' => false]); - $this->assertException("updateManual", "Db"); - $this->drv->schemaUpdate(Database::SCHEMA_VERSION); - } - - public function testDeclineDowngrade() { - $this->assertException("updateTooNew", "Db"); - $this->drv->schemaUpdate(-1, $this->base); - } +class TestUpdate extends \JKingWeb\Arsse\TestCase\Db\BaseUpdate { + protected static $implementation = "SQLite 3"; + protected static $minimal1 = "create table arsse_meta(key text primary key not null, value text); pragma user_version=1"; + protected static $minimal2 = "pragma user_version=2"; } diff --git a/tests/cases/Db/SQLite3PDO/TestUpdate.php b/tests/cases/Db/SQLite3PDO/TestUpdate.php index 58caca37..4a23595f 100644 --- a/tests/cases/Db/SQLite3PDO/TestUpdate.php +++ b/tests/cases/Db/SQLite3PDO/TestUpdate.php @@ -6,114 +6,11 @@ declare(strict_types=1); 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 TestUpdate extends \JKingWeb\Arsse\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(array $conf = []) { - if (!PDODriver::requirementsMet()) { - $this->markTestSkipped("PDO-SQLite extension not loaded"); - } - self::clearData(); - $this->vfs = vfsStream::setup("schemata", null, ['SQLite3' => []]); - $conf['dbDriver'] = PDODriver::class; - self::setConf($conf); - $this->base = $this->vfs->url(); - $this->path = $this->base."/SQLite3/"; - $this->drv = new PDODriver(); - } - - public function tearDown() { - unset($this->drv); - unset($this->data); - unset($this->vfs); - self::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 - $this->setUp(['dbAutoUpdate' => false]); - $this->assertException("updateManual", "Db"); - $this->drv->schemaUpdate(Database::SCHEMA_VERSION); - } - - public function testDeclineDowngrade() { - $this->assertException("updateTooNew", "Db"); - $this->drv->schemaUpdate(-1, $this->base); - } +class TestUpdate extends \JKingWeb\Arsse\TestCase\Db\BaseUpdate { + protected static $implementation = "PDO SQLite 3"; + protected static $minimal1 = "create table arsse_meta(key text primary key not null, value text); pragma user_version=1"; + protected static $minimal2 = "pragma user_version=2"; } diff --git a/tests/phpunit.xml b/tests/phpunit.xml index 06b3d6ad..d4964100 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -61,7 +61,7 @@ cases/Db/PostgreSQL/TestStatement.php cases/Db/PostgreSQL/TestCreation.php cases/Db/PostgreSQL/TestDriver.php - + cases/Db/PostgreSQL/TestUpdate.php cases/Db/SQLite3/TestDatabase.php