Browse Source

Test PostgreSQL schema upgrade

This was in fact buggy due to the schema version check causing an error
microsub
J. King 5 years ago
parent
commit
8dfedd30ef
  1. 8
      lib/Db/AbstractDriver.php
  2. 8
      lib/Db/PostgreSQL/Driver.php
  3. 5
      tests/cases/Db/BaseDriver.php
  4. 136
      tests/cases/Db/BaseUpdate.php
  5. 16
      tests/cases/Db/PostgreSQL/TestUpdate.php
  6. 110
      tests/cases/Db/SQLite3/TestUpdate.php
  7. 111
      tests/cases/Db/SQLite3PDO/TestUpdate.php
  8. 2
      tests/phpunit.xml

8
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) {

8
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");

5
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());

136
tests/cases/Db/BaseUpdate.php

@ -0,0 +1,136 @@
<?php
/** @license MIT
* Copyright 2017 J. King, Dustin Wilson et al.
* See LICENSE and AUTHORS files for details */
declare(strict_types=1);
namespace JKingWeb\Arsse\TestCase\Db;
use JKingWeb\Arsse\Arsse;
use JKingWeb\Arsse\Database;
use JKingWeb\Arsse\Db\Exception;
use JKingWeb\Arsse\Test\DatabaseInformation;
use org\bovigo\vfs\vfsStream;
class BaseUpdate extends \JKingWeb\Arsse\Test\AbstractTest {
protected static $dbInfo;
protected static $interface;
protected $drv;
protected $vfs;
protected $base;
protected $path;
public static function setUpBeforeClass() {
// establish a clean baseline
static::clearData();
static::$dbInfo = new DatabaseInformation(static::$implementation);
static::setConf();
static::$interface = (static::$dbInfo->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);
}
}

16
tests/cases/Db/PostgreSQL/TestUpdate.php

@ -0,0 +1,16 @@
<?php
/** @license MIT
* Copyright 2017 J. King, Dustin Wilson et al.
* See LICENSE and AUTHORS files for details */
declare(strict_types=1);
namespace JKingWeb\Arsse\TestCase\Db\PostgreSQL;
/**
* @covers \JKingWeb\Arsse\Db\PostgreSQL\PDODriver<extended>
* @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';";
}

110
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<extended>
* @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";
}

111
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<extended>
* @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";
}

2
tests/phpunit.xml

@ -61,7 +61,7 @@
<file>cases/Db/PostgreSQL/TestStatement.php</file>
<file>cases/Db/PostgreSQL/TestCreation.php</file>
<file>cases/Db/PostgreSQL/TestDriver.php</file>
<!--<file>cases/Db/PostgreSQL/TestUpdate.php</file>-->
<file>cases/Db/PostgreSQL/TestUpdate.php</file>
</testsuite>
<testsuite name="Database functions">
<file>cases/Db/SQLite3/TestDatabase.php</file>

Loading…
Cancel
Save