mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
Use common cleanup code for all database-related tests
This commit is contained in:
parent
925560d4ba
commit
8a49202036
13 changed files with 203 additions and 206 deletions
|
@ -11,51 +11,69 @@ use JKingWeb\Arsse\Db\Result;
|
||||||
use JKingWeb\Arsse\Test\DatabaseInformation;
|
use JKingWeb\Arsse\Test\DatabaseInformation;
|
||||||
|
|
||||||
abstract class BaseDriver extends \JKingWeb\Arsse\Test\AbstractTest {
|
abstract class BaseDriver extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
|
protected static $dbInfo;
|
||||||
|
protected static $interface;
|
||||||
protected $drv;
|
protected $drv;
|
||||||
protected $interface;
|
|
||||||
protected $create;
|
protected $create;
|
||||||
protected $lock;
|
protected $lock;
|
||||||
protected $setVersion;
|
protected $setVersion;
|
||||||
protected $conf = [
|
protected static $conf = [
|
||||||
'dbTimeoutExec' => 0.5,
|
'dbTimeoutExec' => 0.5,
|
||||||
'dbSQLite3Timeout' => 0,
|
'dbSQLite3Timeout' => 0,
|
||||||
|
//'dbSQLite3File' => "(temporary file)",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public static function setUpBeforeClass() {
|
||||||
|
// establish a clean baseline
|
||||||
|
static::clearData();
|
||||||
|
static::$dbInfo = new DatabaseInformation(static::$implementation);
|
||||||
|
static::setConf(static::$conf);
|
||||||
|
static::$interface = (static::$dbInfo->interfaceConstructor)();
|
||||||
|
}
|
||||||
|
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
self::clearData();
|
self::clearData();
|
||||||
self::setConf($this->conf);
|
self::setConf(static::$conf);
|
||||||
$info = new DatabaseInformation($this->implementation);
|
if (!static::$interface) {
|
||||||
$this->interface = ($info->interfaceConstructor)();
|
$this->markTestSkipped(static::$implementation." database driver not available");
|
||||||
if (!$this->interface) {
|
|
||||||
$this->markTestSkipped("$this->implementation database driver not available");
|
|
||||||
}
|
}
|
||||||
$this->drv = new $info->driverClass;
|
// completely clear the database and ensure the schema version can easily be altered
|
||||||
$this->exec("DROP TABLE IF EXISTS arsse_test");
|
(static::$dbInfo->razeFunction)(static::$interface, [
|
||||||
$this->exec("DROP TABLE IF EXISTS arsse_meta");
|
"CREATE TABLE arsse_meta(key varchar(255) primary key not null, value text)",
|
||||||
$this->exec("CREATE TABLE arsse_meta(key varchar(255) primary key not null, value text)");
|
"INSERT INTO arsse_meta(key,value) values('schema_version','0')",
|
||||||
$this->exec("INSERT INTO arsse_meta(key,value) values('schema_version','0')");
|
]);
|
||||||
|
// construct a fresh driver for each test
|
||||||
|
$this->drv = new static::$dbInfo->driverClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tearDown() {
|
public function tearDown() {
|
||||||
self::clearData();
|
// deconstruct the driver
|
||||||
unset($this->drv);
|
unset($this->drv);
|
||||||
try {
|
if (static::$interface) {
|
||||||
$this->exec("ROLLBACK");
|
// completely clear the database
|
||||||
} catch(\Throwable $e) {
|
(static::$dbInfo->razeFunction)(static::$interface);
|
||||||
}
|
}
|
||||||
$this->exec("DROP TABLE IF EXISTS arsse_meta");
|
self::clearData();
|
||||||
$this->exec("DROP TABLE IF EXISTS arsse_test");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function exec(string $q): bool {
|
public static function tearDownAfterClass() {
|
||||||
|
static::$implementation = null;
|
||||||
|
static::$dbInfo = null;
|
||||||
|
self::clearData();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function exec($q): bool {
|
||||||
// PDO implementation
|
// PDO implementation
|
||||||
$this->interface->exec($q);
|
$q = (!is_array($q)) ? [$q] : $q;
|
||||||
|
foreach ($q as $query) {
|
||||||
|
static::$interface->exec((string) $query);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function query(string $q) {
|
protected function query(string $q) {
|
||||||
// PDO implementation
|
// PDO implementation
|
||||||
return $this->interface->query($q)->fetchColumn();
|
return static::$interface->query($q)->fetchColumn();
|
||||||
}
|
}
|
||||||
|
|
||||||
# TESTS
|
# TESTS
|
||||||
|
@ -87,7 +105,8 @@ abstract class BaseDriver extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
$this->exec($this->create);
|
$this->exec($this->create);
|
||||||
$this->exec($this->lock);
|
$this->exec($this->lock);
|
||||||
$this->assertException("general", "Db", "ExceptionTimeout");
|
$this->assertException("general", "Db", "ExceptionTimeout");
|
||||||
$this->drv->exec($this->lock);
|
$lock = is_array($this->lock) ? implode("; ",$this->lock) : $this->lock;
|
||||||
|
$this->drv->exec($lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testExecConstraintViolation() {
|
public function testExecConstraintViolation() {
|
||||||
|
@ -115,7 +134,8 @@ abstract class BaseDriver extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
$this->exec($this->create);
|
$this->exec($this->create);
|
||||||
$this->exec($this->lock);
|
$this->exec($this->lock);
|
||||||
$this->assertException("general", "Db", "ExceptionTimeout");
|
$this->assertException("general", "Db", "ExceptionTimeout");
|
||||||
$this->drv->exec($this->lock);
|
$lock = is_array($this->lock) ? implode("; ",$this->lock) : $this->lock;
|
||||||
|
$this->drv->exec($lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testQueryConstraintViolation() {
|
public function testQueryConstraintViolation() {
|
||||||
|
@ -342,12 +362,20 @@ abstract class BaseDriver extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
$this->assertSame(1, $this->drv->schemaVersion());
|
$this->assertSame(1, $this->drv->schemaVersion());
|
||||||
$this->drv->exec(str_replace("#", "2", $this->setVersion));
|
$this->drv->exec(str_replace("#", "2", $this->setVersion));
|
||||||
$this->assertSame(2, $this->drv->schemaVersion());
|
$this->assertSame(2, $this->drv->schemaVersion());
|
||||||
|
// SQLite is unaffected by the removal of the metadata table; other backends are
|
||||||
|
// in neither case should a query for the schema version produce an error, however
|
||||||
|
$this->exec("DROP TABLE IF EXISTS arsse_meta");
|
||||||
|
$exp = (static::$dbInfo->backend == "SQLite 3") ? 2 : 0;
|
||||||
|
$this->assertSame($exp, $this->drv->schemaVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLockTheDatabase() {
|
public function testLockTheDatabase() {
|
||||||
|
// PostgreSQL doesn't actually lock the whole database, only the metadata table
|
||||||
|
// normally the application will first query this table to ensure the schema version is correct,
|
||||||
|
// so the effect is usually the same
|
||||||
$this->drv->savepointCreate(true);
|
$this->drv->savepointCreate(true);
|
||||||
$this->assertException();
|
$this->assertException();
|
||||||
$this->exec($this->create);
|
$this->exec(str_replace("#", "3", $this->setVersion));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUnlockTheDatabase() {
|
public function testUnlockTheDatabase() {
|
||||||
|
@ -355,6 +383,6 @@ abstract class BaseDriver extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
$this->drv->savepointRelease();
|
$this->drv->savepointRelease();
|
||||||
$this->drv->savepointCreate(true);
|
$this->drv->savepointCreate(true);
|
||||||
$this->drv->savepointUndo();
|
$this->drv->savepointUndo();
|
||||||
$this->assertTrue($this->exec($this->create));
|
$this->assertTrue($this->exec(str_replace("#", "3", $this->setVersion)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,29 +10,45 @@ use JKingWeb\Arsse\Db\Result;
|
||||||
use JKingWeb\Arsse\Test\DatabaseInformation;
|
use JKingWeb\Arsse\Test\DatabaseInformation;
|
||||||
|
|
||||||
abstract class BaseResult extends \JKingWeb\Arsse\Test\AbstractTest {
|
abstract class BaseResult extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
|
protected static $dbInfo;
|
||||||
|
protected static $interface;
|
||||||
protected $resultClass;
|
protected $resultClass;
|
||||||
protected $stringOutput;
|
protected $stringOutput;
|
||||||
protected $interface;
|
|
||||||
|
|
||||||
abstract protected function exec(string $q);
|
|
||||||
abstract protected function makeResult(string $q): array;
|
abstract protected function makeResult(string $q): array;
|
||||||
|
|
||||||
|
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() {
|
public function setUp() {
|
||||||
self::clearData();
|
self::clearData();
|
||||||
self::setConf();
|
self::setConf();
|
||||||
$info = new DatabaseInformation($this->implementation);
|
if (!static::$interface) {
|
||||||
$this->interface = ($info->interfaceConstructor)();
|
$this->markTestSkipped(static::$implementation." database driver not available");
|
||||||
if (!$this->interface) {
|
|
||||||
$this->markTestSkipped("$this->implementation database driver not available");
|
|
||||||
}
|
}
|
||||||
$this->resultClass = $info->resultClass;
|
// completely clear the database
|
||||||
$this->stringOutput = $info->stringOutput;
|
(static::$dbInfo->razeFunction)(static::$interface);
|
||||||
$this->exec("DROP TABLE IF EXISTS arsse_meta");
|
$this->resultClass = static::$dbInfo->resultClass;
|
||||||
|
$this->stringOutput = static::$dbInfo->stringOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tearDown() {
|
public function tearDown() {
|
||||||
|
if (static::$interface) {
|
||||||
|
// completely clear the database
|
||||||
|
(static::$dbInfo->razeFunction)(static::$interface);
|
||||||
|
}
|
||||||
|
self::clearData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function tearDownAfterClass() {
|
||||||
|
static::$implementation = null;
|
||||||
|
static::$dbInfo = null;
|
||||||
self::clearData();
|
self::clearData();
|
||||||
$this->exec("DROP TABLE IF EXISTS arsse_meta");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testConstructResult() {
|
public function testConstructResult() {
|
||||||
|
|
|
@ -10,29 +10,45 @@ use JKingWeb\Arsse\Db\Statement;
|
||||||
use JKingWeb\Arsse\Test\DatabaseInformation;
|
use JKingWeb\Arsse\Test\DatabaseInformation;
|
||||||
|
|
||||||
abstract class BaseStatement extends \JKingWeb\Arsse\Test\AbstractTest {
|
abstract class BaseStatement extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
|
protected static $dbInfo;
|
||||||
|
protected static $interface;
|
||||||
protected $statementClass;
|
protected $statementClass;
|
||||||
protected $stringOutput;
|
protected $stringOutput;
|
||||||
protected $interface;
|
|
||||||
|
|
||||||
abstract protected function exec(string $q);
|
|
||||||
abstract protected function makeStatement(string $q, array $types = []): array;
|
abstract protected function makeStatement(string $q, array $types = []): array;
|
||||||
abstract protected function decorateTypeSyntax(string $value, string $type): string;
|
abstract protected function decorateTypeSyntax(string $value, string $type): string;
|
||||||
|
|
||||||
|
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() {
|
public function setUp() {
|
||||||
self::clearData();
|
self::clearData();
|
||||||
self::setConf();
|
self::setConf();
|
||||||
$info = new DatabaseInformation($this->implementation);
|
if (!static::$interface) {
|
||||||
$this->interface = ($info->interfaceConstructor)();
|
$this->markTestSkipped(static::$implementation." database driver not available");
|
||||||
if (!$this->interface) {
|
|
||||||
$this->markTestSkipped("$this->implementation database driver not available");
|
|
||||||
}
|
}
|
||||||
$this->statementClass = $info->statementClass;
|
// completely clear the database
|
||||||
$this->stringOutput = $info->stringOutput;
|
(static::$dbInfo->razeFunction)(static::$interface);
|
||||||
$this->exec("DROP TABLE IF EXISTS arsse_meta");
|
$this->statementClass = static::$dbInfo->statementClass;
|
||||||
|
$this->stringOutput = static::$dbInfo->stringOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tearDown() {
|
public function tearDown() {
|
||||||
$this->exec("DROP TABLE IF EXISTS arsse_meta");
|
if (static::$interface) {
|
||||||
|
// completely clear the database
|
||||||
|
(static::$dbInfo->razeFunction)(static::$interface);
|
||||||
|
}
|
||||||
|
self::clearData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function tearDownAfterClass() {
|
||||||
|
static::$implementation = null;
|
||||||
|
static::$dbInfo = null;
|
||||||
self::clearData();
|
self::clearData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +72,7 @@ abstract class BaseStatement extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
|
|
||||||
/** @dataProvider provideBinaryBindings */
|
/** @dataProvider provideBinaryBindings */
|
||||||
public function testHandleBinaryData($value, string $type, string $exp) {
|
public function testHandleBinaryData($value, string $type, string $exp) {
|
||||||
if (in_array($this->implementation, ["PostgreSQL", "PDO PostgreSQL"])) {
|
if (in_array(static::$implementation, ["PostgreSQL", "PDO PostgreSQL"])) {
|
||||||
$this->markTestSkipped("Correct handling of binary data with PostgreSQL is currently unknown");
|
$this->markTestSkipped("Correct handling of binary data with PostgreSQL is currently unknown");
|
||||||
}
|
}
|
||||||
if ($exp=="null") {
|
if ($exp=="null") {
|
||||||
|
|
19
tests/cases/Db/PostgreSQL/TestDatabase.php
Normal file
19
tests/cases/Db/PostgreSQL/TestDatabase.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
/** @license MIT
|
||||||
|
* Copyright 2017 J. King, Dustin Wilson et al.
|
||||||
|
* See LICENSE and AUTHORS files for details */
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\Arsse\TestCase\Db\PosgreSQL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \JKingWeb\Arsse\Database<extended>
|
||||||
|
* @covers \JKingWeb\Arsse\Misc\Query<extended>
|
||||||
|
*/
|
||||||
|
class TestDatabase extends \JKingWeb\Arsse\TestCase\Database\Base {
|
||||||
|
protected static $implementation = "PDO PostgreSQL";
|
||||||
|
|
||||||
|
protected function nextID(string $table): int {
|
||||||
|
return static::$drv->query("SELECT select cast(last_value as bigint) + 1 from pg_sequences where sequencename = '{$table}_id_seq'")->getValue();
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,13 +11,8 @@ namespace JKingWeb\Arsse\TestCase\Db\PostgreSQL;
|
||||||
* @covers \JKingWeb\Arsse\Db\PDODriver
|
* @covers \JKingWeb\Arsse\Db\PDODriver
|
||||||
* @covers \JKingWeb\Arsse\Db\PDOError */
|
* @covers \JKingWeb\Arsse\Db\PDOError */
|
||||||
class TestDriver extends \JKingWeb\Arsse\TestCase\Db\BaseDriver {
|
class TestDriver extends \JKingWeb\Arsse\TestCase\Db\BaseDriver {
|
||||||
protected $implementation = "PDO PostgreSQL";
|
protected static $implementation = "PDO PostgreSQL";
|
||||||
protected $create = "CREATE TABLE arsse_test(id bigserial primary key)";
|
protected $create = "CREATE TABLE arsse_test(id bigserial primary key)";
|
||||||
protected $lock = "BEGIN; LOCK TABLE arsse_test IN EXCLUSIVE MODE NOWAIT";
|
protected $lock = ["BEGIN", "LOCK TABLE arsse_test IN EXCLUSIVE MODE NOWAIT"];
|
||||||
protected $setVersion = "UPDATE arsse_meta set value = '#' where key = 'schema_version'";
|
protected $setVersion = "UPDATE arsse_meta set value = '#' where key = 'schema_version'";
|
||||||
|
|
||||||
public function tearDown() {
|
|
||||||
parent::tearDown();
|
|
||||||
unset($this->interface);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,19 +10,10 @@ namespace JKingWeb\Arsse\TestCase\Db\PostgreSQL;
|
||||||
* @covers \JKingWeb\Arsse\Db\PDOStatement<extended>
|
* @covers \JKingWeb\Arsse\Db\PDOStatement<extended>
|
||||||
* @covers \JKingWeb\Arsse\Db\PDOError */
|
* @covers \JKingWeb\Arsse\Db\PDOError */
|
||||||
class TestStatement extends \JKingWeb\Arsse\TestCase\Db\BaseStatement {
|
class TestStatement extends \JKingWeb\Arsse\TestCase\Db\BaseStatement {
|
||||||
protected $implementation = "PDO PostgreSQL";
|
protected static $implementation = "PDO PostgreSQL";
|
||||||
|
|
||||||
public function tearDown() {
|
|
||||||
parent::tearDown();
|
|
||||||
unset($this->interface);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function exec(string $q) {
|
|
||||||
$this->interface->exec($q);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function makeStatement(string $q, array $types = []): array {
|
protected function makeStatement(string $q, array $types = []): array {
|
||||||
return [$this->interface, $this->interface->prepare($q), $types];
|
return [static::$interface, static::$interface->prepare($q), $types];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function decorateTypeSyntax(string $value, string $type): string {
|
protected function decorateTypeSyntax(string $value, string $type): string {
|
||||||
|
|
|
@ -10,92 +10,39 @@ namespace JKingWeb\Arsse\TestCase\Db\SQLite3;
|
||||||
* @covers \JKingWeb\Arsse\Db\SQLite3\Driver<extended>
|
* @covers \JKingWeb\Arsse\Db\SQLite3\Driver<extended>
|
||||||
* @covers \JKingWeb\Arsse\Db\SQLite3\ExceptionBuilder */
|
* @covers \JKingWeb\Arsse\Db\SQLite3\ExceptionBuilder */
|
||||||
class TestDriver extends \JKingWeb\Arsse\TestCase\Db\BaseDriver {
|
class TestDriver extends \JKingWeb\Arsse\TestCase\Db\BaseDriver {
|
||||||
protected $implementation = "SQLite 3";
|
protected static $implementation = "SQLite 3";
|
||||||
protected $create = "CREATE TABLE arsse_test(id integer primary key)";
|
protected $create = "CREATE TABLE arsse_test(id integer primary key)";
|
||||||
protected $lock = "BEGIN EXCLUSIVE TRANSACTION";
|
protected $lock = "BEGIN EXCLUSIVE TRANSACTION";
|
||||||
protected $setVersion = "PRAGMA user_version=#";
|
protected $setVersion = "PRAGMA user_version=#";
|
||||||
protected static $file;
|
protected static $file;
|
||||||
|
|
||||||
public static function setUpBeforeClass() {
|
public static function setUpBeforeClass() {
|
||||||
self::$file = tempnam(sys_get_temp_dir(), 'ook');
|
// create a temporary database file rather than using a memory database
|
||||||
|
// some tests require one connection to block another, so a memory database is not suitable
|
||||||
|
static::$file = tempnam(sys_get_temp_dir(), 'ook');
|
||||||
|
static::$conf['dbSQLite3File'] = static::$file;
|
||||||
|
parent::setUpBeforeclass();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function tearDownAfterClass() {
|
public static function tearDownAfterClass() {
|
||||||
@unlink(self::$file);
|
if (static::$interface) {
|
||||||
self::$file = null;
|
static::$interface->close();
|
||||||
}
|
}
|
||||||
|
parent::tearDownAfterClass();
|
||||||
public function setUp() {
|
@unlink(static::$file);
|
||||||
$this->conf['dbSQLite3File'] = self::$file;
|
static::$file = null;
|
||||||
parent::setUp();
|
|
||||||
$this->exec("PRAGMA user_version=0");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tearDown() {
|
protected function exec($q): bool {
|
||||||
parent::tearDown();
|
// SQLite's implementation coincidentally matches PDO's, but we reproduce it here for correctness' sake
|
||||||
$this->exec("PRAGMA user_version=0");
|
$q = (!is_array($q)) ? [$q] : $q;
|
||||||
$this->interface->close();
|
foreach ($q as $query) {
|
||||||
unset($this->interface);
|
static::$interface->exec((string) $query);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function exec(string $q): bool {
|
|
||||||
$this->interface->exec($q);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function query(string $q) {
|
protected function query(string $q) {
|
||||||
return $this->interface->querySingle($q);
|
return static::$interface->querySingle($q);
|
||||||
}
|
|
||||||
|
|
||||||
public function provideDrivers() {
|
|
||||||
self::clearData();
|
|
||||||
self::setConf([
|
|
||||||
'dbTimeoutExec' => 0.5,
|
|
||||||
'dbSQLite3Timeout' => 0,
|
|
||||||
'dbSQLite3File' => tempnam(sys_get_temp_dir(), 'ook'),
|
|
||||||
]);
|
|
||||||
$i = $this->provideDbInterfaces();
|
|
||||||
$d = $this->provideDbDrivers();
|
|
||||||
$pdoExec = function (string $q) {
|
|
||||||
$this->interface->exec($q);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
$pdoQuery = function (string $q) {
|
|
||||||
return $this->interface->query($q)->fetchColumn();
|
|
||||||
};
|
|
||||||
return [
|
|
||||||
'SQLite 3' => [
|
|
||||||
$i['SQLite 3']['interface'],
|
|
||||||
$d['SQLite 3'],
|
|
||||||
"CREATE TABLE arsse_test(id integer primary key)",
|
|
||||||
"BEGIN EXCLUSIVE TRANSACTION",
|
|
||||||
"PRAGMA user_version=#",
|
|
||||||
function (string $q) {
|
|
||||||
$this->interface->exec($q);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
function (string $q) {
|
|
||||||
return $this->interface->querySingle($q);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'PDO SQLite 3' => [
|
|
||||||
$i['PDO SQLite 3']['interface'],
|
|
||||||
$d['PDO SQLite 3'],
|
|
||||||
"CREATE TABLE arsse_test(id integer primary key)",
|
|
||||||
"BEGIN EXCLUSIVE TRANSACTION",
|
|
||||||
"PRAGMA user_version=#",
|
|
||||||
$pdoExec,
|
|
||||||
$pdoQuery,
|
|
||||||
],
|
|
||||||
'PDO PostgreSQL' => [
|
|
||||||
$i['PDO PostgreSQL']['interface'],
|
|
||||||
$d['PDO PostgreSQL'],
|
|
||||||
"CREATE TABLE arsse_test(id bigserial primary key)",
|
|
||||||
"BEGIN; LOCK TABLE arsse_test IN EXCLUSIVE MODE NOWAIT",
|
|
||||||
"UPDATE arsse_meta set value = '#' where key = 'schema_version'",
|
|
||||||
$pdoExec,
|
|
||||||
$pdoQuery,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,22 +12,19 @@ use JKingWeb\Arsse\Test\DatabaseInformation;
|
||||||
* @covers \JKingWeb\Arsse\Db\SQLite3\Result<extended>
|
* @covers \JKingWeb\Arsse\Db\SQLite3\Result<extended>
|
||||||
*/
|
*/
|
||||||
class TestResult extends \JKingWeb\Arsse\TestCase\Db\BaseResult {
|
class TestResult extends \JKingWeb\Arsse\TestCase\Db\BaseResult {
|
||||||
protected $implementation = "SQLite 3";
|
protected static $implementation = "SQLite 3";
|
||||||
|
|
||||||
public function tearDown() {
|
public static function tearDownAfterClass() {
|
||||||
parent::tearDown();
|
if (static::$interface) {
|
||||||
$this->interface->close();
|
static::$interface->close();
|
||||||
unset($this->interface);
|
}
|
||||||
}
|
parent::tearDownAfterClass();
|
||||||
|
|
||||||
protected function exec(string $q) {
|
|
||||||
$this->interface->exec($q);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function makeResult(string $q): array {
|
protected function makeResult(string $q): array {
|
||||||
$set = $this->interface->query($q);
|
$set = static::$interface->query($q);
|
||||||
$rows = $this->interface->changes();
|
$rows = static::$interface->changes();
|
||||||
$id = $this->interface->lastInsertRowID();
|
$id = static::$interface->lastInsertRowID();
|
||||||
return [$set, [$rows, $id]];
|
return [$set, [$rows, $id]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,20 +10,15 @@ namespace JKingWeb\Arsse\TestCase\Db\SQLite3;
|
||||||
* @covers \JKingWeb\Arsse\Db\SQLite3\Statement<extended>
|
* @covers \JKingWeb\Arsse\Db\SQLite3\Statement<extended>
|
||||||
* @covers \JKingWeb\Arsse\Db\SQLite3\ExceptionBuilder */
|
* @covers \JKingWeb\Arsse\Db\SQLite3\ExceptionBuilder */
|
||||||
class TestStatement extends \JKingWeb\Arsse\TestCase\Db\BaseStatement {
|
class TestStatement extends \JKingWeb\Arsse\TestCase\Db\BaseStatement {
|
||||||
protected $implementation = "SQLite 3";
|
protected static $implementation = "SQLite 3";
|
||||||
|
|
||||||
public function tearDown() {
|
public static function tearDownAfterClass() {
|
||||||
parent::tearDown();
|
static::$interface->close();
|
||||||
$this->interface->close();
|
parent::tearDownAfterClass();
|
||||||
unset($this->interface);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function exec(string $q) {
|
|
||||||
$this->interface->exec($q);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function makeStatement(string $q, array $types = []): array {
|
protected function makeStatement(string $q, array $types = []): array {
|
||||||
return [$this->interface, $this->interface->prepare($q), $types];
|
return [static::$interface, static::$interface->prepare($q), $types];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function decorateTypeSyntax(string $value, string $type): string {
|
protected function decorateTypeSyntax(string $value, string $type): string {
|
||||||
|
|
|
@ -11,30 +11,23 @@ namespace JKingWeb\Arsse\TestCase\Db\SQLite3PDO;
|
||||||
* @covers \JKingWeb\Arsse\Db\PDODriver
|
* @covers \JKingWeb\Arsse\Db\PDODriver
|
||||||
* @covers \JKingWeb\Arsse\Db\PDOError */
|
* @covers \JKingWeb\Arsse\Db\PDOError */
|
||||||
class TestDriver extends \JKingWeb\Arsse\TestCase\Db\BaseDriver {
|
class TestDriver extends \JKingWeb\Arsse\TestCase\Db\BaseDriver {
|
||||||
protected $implementation = "PDO SQLite 3";
|
protected static $implementation = "PDO SQLite 3";
|
||||||
protected $create = "CREATE TABLE arsse_test(id integer primary key)";
|
protected $create = "CREATE TABLE arsse_test(id integer primary key)";
|
||||||
protected $lock = "BEGIN EXCLUSIVE TRANSACTION";
|
protected $lock = "BEGIN EXCLUSIVE TRANSACTION";
|
||||||
protected $setVersion = "PRAGMA user_version=#";
|
protected $setVersion = "PRAGMA user_version=#";
|
||||||
protected static $file;
|
protected static $file;
|
||||||
|
|
||||||
public static function setUpBeforeClass() {
|
public static function setUpBeforeClass() {
|
||||||
self::$file = tempnam(sys_get_temp_dir(), 'ook');
|
// create a temporary database file rather than using a memory database
|
||||||
|
// some tests require one connection to block another, so a memory database is not suitable
|
||||||
|
static::$file = tempnam(sys_get_temp_dir(), 'ook');
|
||||||
|
static::$conf['dbSQLite3File'] = static::$file;
|
||||||
|
parent::setUpBeforeclass();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function tearDownAfterClass() {
|
public static function tearDownAfterClass() {
|
||||||
|
parent::tearDownAfterClass();
|
||||||
@unlink(self::$file);
|
@unlink(self::$file);
|
||||||
self::$file = null;
|
self::$file = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setUp() {
|
|
||||||
$this->conf['dbSQLite3File'] = self::$file;
|
|
||||||
parent::setUp();
|
|
||||||
$this->exec("PRAGMA user_version=0");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function tearDown() {
|
|
||||||
parent::tearDown();
|
|
||||||
$this->exec("PRAGMA user_version=0");
|
|
||||||
unset($this->interface);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,19 +10,10 @@ namespace JKingWeb\Arsse\TestCase\Db\SQLite3PDO;
|
||||||
* @covers \JKingWeb\Arsse\Db\PDOStatement<extended>
|
* @covers \JKingWeb\Arsse\Db\PDOStatement<extended>
|
||||||
* @covers \JKingWeb\Arsse\Db\PDOError */
|
* @covers \JKingWeb\Arsse\Db\PDOError */
|
||||||
class TestStatement extends \JKingWeb\Arsse\TestCase\Db\BaseStatement {
|
class TestStatement extends \JKingWeb\Arsse\TestCase\Db\BaseStatement {
|
||||||
protected $implementation = "PDO SQLite 3";
|
protected static $implementation = "PDO SQLite 3";
|
||||||
|
|
||||||
public function tearDown() {
|
|
||||||
parent::tearDown();
|
|
||||||
unset($this->interface);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function exec(string $q) {
|
|
||||||
$this->interface->exec($q);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function makeStatement(string $q, array $types = []): array {
|
protected function makeStatement(string $q, array $types = []): array {
|
||||||
return [$this->interface, $this->interface->prepare($q), $types];
|
return [static::$interface, static::$interface->prepare($q), $types];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function decorateTypeSyntax(string $value, string $type): string {
|
protected function decorateTypeSyntax(string $value, string $type): string {
|
||||||
|
|
|
@ -12,41 +12,30 @@ use JKingWeb\Arsse\Test\DatabaseInformation;
|
||||||
* @covers \JKingWeb\Arsse\Db\PDOResult<extended>
|
* @covers \JKingWeb\Arsse\Db\PDOResult<extended>
|
||||||
*/
|
*/
|
||||||
class TestResultPDO extends \JKingWeb\Arsse\TestCase\Db\BaseResult {
|
class TestResultPDO extends \JKingWeb\Arsse\TestCase\Db\BaseResult {
|
||||||
protected static $firstAvailableDriver;
|
protected static $implementation;
|
||||||
|
|
||||||
public static function setUpBeforeClass() {
|
public static function setUpBeforeClass() {
|
||||||
self::setConf();
|
self::setConf();
|
||||||
// we only need to test one PDO implementation (they all use the same result class), so we find the first usable one
|
// we only need to test one PDO implementation (they all use the same result class), so we find the first usable one
|
||||||
$drivers = DatabaseInformation::listPDO();
|
$drivers = DatabaseInformation::listPDO();
|
||||||
self::$firstAvailableDriver = $drivers[0];
|
self::$implementation = $drivers[0];
|
||||||
foreach ($drivers as $driver) {
|
foreach ($drivers as $driver) {
|
||||||
$info = new DatabaseInformation($driver);
|
$info = new DatabaseInformation($driver);
|
||||||
$interface = ($info->interfaceConstructor)();
|
$interface = ($info->interfaceConstructor)();
|
||||||
if ($interface) {
|
if ($interface) {
|
||||||
self::$firstAvailableDriver = $driver;
|
self::$implementation = $driver;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
unset($interface);
|
||||||
|
unset($info);
|
||||||
public function setUp() {
|
parent::setUpBeforeClass();
|
||||||
$this->implementation = self::$firstAvailableDriver;
|
|
||||||
parent::setUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function tearDown() {
|
|
||||||
parent::tearDown();
|
|
||||||
unset($this->interface);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function exec(string $q) {
|
|
||||||
$this->interface->exec($q);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function makeResult(string $q): array {
|
protected function makeResult(string $q): array {
|
||||||
$set = $this->interface->query($q);
|
$set = static::$interface->query($q);
|
||||||
$rows = $set->rowCount();
|
$rows = $set->rowCount();
|
||||||
$id = $this->interface->lastInsertID();
|
$id = static::$interface->lastInsertID();
|
||||||
return [$set, [$rows, $id]];
|
return [$set, [$rows, $id]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,12 @@ class DatabaseInformation {
|
||||||
$tables = $db->query($listTables)->getAll();
|
$tables = $db->query($listTables)->getAll();
|
||||||
$tables = sizeof($tables) ? array_column($tables, "name") : [];
|
$tables = sizeof($tables) ? array_column($tables, "name") : [];
|
||||||
} elseif ($db instanceof \PDO) {
|
} elseif ($db instanceof \PDO) {
|
||||||
$tables = $db->query($listTables)->fetchAll(\PDO::FETCH_ASSOC);
|
retry:
|
||||||
|
try {
|
||||||
|
$tables = $db->query($listTables)->fetchAll(\PDO::FETCH_ASSOC);
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
$tables = sizeof($tables) ? array_column($tables, "name") : [];
|
$tables = sizeof($tables) ? array_column($tables, "name") : [];
|
||||||
} else {
|
} else {
|
||||||
$tables = [];
|
$tables = [];
|
||||||
|
@ -72,6 +77,11 @@ class DatabaseInformation {
|
||||||
return $tables;
|
return $tables;
|
||||||
};
|
};
|
||||||
$sqlite3TruncateFunction = function($db, array $afterStatements = []) use ($sqlite3TableList) {
|
$sqlite3TruncateFunction = function($db, array $afterStatements = []) use ($sqlite3TableList) {
|
||||||
|
// rollback any pending transaction
|
||||||
|
try {
|
||||||
|
$db->exec("ROLLBACK");
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
}
|
||||||
foreach ($sqlite3TableList($db) as $table) {
|
foreach ($sqlite3TableList($db) as $table) {
|
||||||
if ($table == "arsse_meta") {
|
if ($table == "arsse_meta") {
|
||||||
$db->exec("DELETE FROM $table where key <> 'schema_version'");
|
$db->exec("DELETE FROM $table where key <> 'schema_version'");
|
||||||
|
@ -84,6 +94,11 @@ class DatabaseInformation {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
$sqlite3RazeFunction = function($db, array $afterStatements = []) use ($sqlite3TableList) {
|
$sqlite3RazeFunction = function($db, array $afterStatements = []) use ($sqlite3TableList) {
|
||||||
|
// rollback any pending transaction
|
||||||
|
try {
|
||||||
|
$db->exec("ROLLBACK");
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
}
|
||||||
$db->exec("PRAGMA foreign_keys=0");
|
$db->exec("PRAGMA foreign_keys=0");
|
||||||
foreach ($sqlite3TableList($db) as $table) {
|
foreach ($sqlite3TableList($db) as $table) {
|
||||||
$db->exec("DROP TABLE IF EXISTS $table");
|
$db->exec("DROP TABLE IF EXISTS $table");
|
||||||
|
@ -163,7 +178,12 @@ class DatabaseInformation {
|
||||||
return $d;
|
return $d;
|
||||||
},
|
},
|
||||||
'truncateFunction' => function($db, array $afterStatements = []) use ($pgObjectList) {
|
'truncateFunction' => function($db, array $afterStatements = []) use ($pgObjectList) {
|
||||||
foreach ($objectList($db) as $obj) {
|
// rollback any pending transaction
|
||||||
|
try {
|
||||||
|
$db->exec("ROLLBACK");
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
}
|
||||||
|
foreach ($pgObjectList($db) as $obj) {
|
||||||
if ($obj['type'] != "TABLE") {
|
if ($obj['type'] != "TABLE") {
|
||||||
continue;
|
continue;
|
||||||
} elseif ($obj['name'] == "arsse_meta") {
|
} elseif ($obj['name'] == "arsse_meta") {
|
||||||
|
@ -177,8 +197,8 @@ class DatabaseInformation {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'razeFunction' => function($db, array $afterStatements = []) use ($pgObjectList) {
|
'razeFunction' => function($db, array $afterStatements = []) use ($pgObjectList) {
|
||||||
foreach ($objectList($db) as $obj) {
|
foreach ($pgObjectList($db) as $obj) {
|
||||||
$db->exec("DROP {$obj['type']} {$obj['name']} IF EXISTS cascade");
|
$db->exec("DROP {$obj['type']} IF EXISTS {$obj['name']} cascade");
|
||||||
|
|
||||||
}
|
}
|
||||||
foreach ($afterStatements as $st) {
|
foreach ($afterStatements as $st) {
|
||||||
|
|
Loading…
Reference in a new issue