mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 13:12:41 +00:00
Partially tested pdo_sqlite driver; improves #72
This commit is contained in:
parent
ef75b5e9ab
commit
ad6a09ffa1
12 changed files with 1138 additions and 1 deletions
47
lib/Db/PDODriver.php
Normal file
47
lib/Db/PDODriver.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?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\Db;
|
||||
|
||||
trait PDODriver {
|
||||
use PDOError;
|
||||
|
||||
public function exec(string $query): bool {
|
||||
try {
|
||||
$this->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);
|
||||
}
|
||||
}
|
42
lib/Db/PDOError.php
Normal file
42
lib/Db/PDOError.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?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\Db;
|
||||
|
||||
trait PDOError {
|
||||
public function exceptionBuild() {
|
||||
if ($this instanceof Statement) {
|
||||
$err = $this->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];
|
||||
}
|
||||
}
|
49
lib/Db/PDOResult.php
Normal file
49
lib/Db/PDOResult.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?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\Db;
|
||||
|
||||
use JKingWeb\Arsse\Db\Exception;
|
||||
|
||||
class PDOResult extends AbstractResult {
|
||||
protected $set;
|
||||
protected $cur = null;
|
||||
protected $rows = 0;
|
||||
protected $id = 0;
|
||||
|
||||
// actual public methods
|
||||
|
||||
public function changes() {
|
||||
return $this->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);
|
||||
}
|
||||
}
|
85
lib/Db/PDOStatement.php
Normal file
85
lib/Db/PDOStatement.php
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?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\Db;
|
||||
|
||||
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,
|
||||
"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;
|
||||
}
|
||||
}
|
46
lib/Db/SQLite3/PDODriver.php
Normal file
46
lib/Db/SQLite3/PDODriver.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?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\Db\SQLite3;
|
||||
|
||||
use JKingWeb\Arsse\Arsse;
|
||||
use JKingWeb\Arsse\Db\Exception;
|
||||
use JKingWeb\Arsse\Db\ExceptionInput;
|
||||
use JKingWeb\Arsse\Db\ExceptionTimeout;
|
||||
|
||||
class PDODriver extends Driver {
|
||||
use \JKingWeb\Arsse\Db\PDODriver;
|
||||
|
||||
protected $db;
|
||||
|
||||
public static function requirementsMet(): bool {
|
||||
return class_exists("PDO") && in_array("sqlite", \PDO::getAvailableDrivers());
|
||||
}
|
||||
|
||||
protected function makeConnection(string $file, string $key) {
|
||||
$this->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");
|
||||
}
|
||||
}
|
|
@ -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',
|
||||
|
|
195
tests/cases/Db/SQLite3PDO/TestDbDriverCreationSQLite3PDO.php
Normal file
195
tests/cases/Db/SQLite3PDO/TestDbDriverCreationSQLite3PDO.php
Normal file
|
@ -0,0 +1,195 @@
|
|||
<?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;
|
||||
|
||||
use JKingWeb\Arsse\Arsse;
|
||||
use JKingWeb\Arsse\Db\SQLite3\PDODriver as Driver;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
use Phake;
|
||||
|
||||
/**
|
||||
* @covers \JKingWeb\Arsse\Db\SQLite3\PDODriver<extended>
|
||||
* @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;
|
||||
}
|
||||
}
|
337
tests/cases/Db/SQLite3PDO/TestDbDriverSQLite3PDO.php
Normal file
337
tests/cases/Db/SQLite3PDO/TestDbDriverSQLite3PDO.php
Normal file
|
@ -0,0 +1,337 @@
|
|||
<?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;
|
||||
|
||||
/**
|
||||
* @covers \JKingWeb\Arsse\Db\SQLite3\PDODriver<extended>
|
||||
* @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)"));
|
||||
}
|
||||
}
|
104
tests/cases/Db/SQLite3PDO/TestDbResultSQLite3PDO.php
Normal file
104
tests/cases/Db/SQLite3PDO/TestDbResultSQLite3PDO.php
Normal file
|
@ -0,0 +1,104 @@
|
|||
<?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;
|
||||
|
||||
/** @covers \JKingWeb\Arsse\Db\PDOResult<extended> */
|
||||
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());
|
||||
}
|
||||
}
|
105
tests/cases/Db/SQLite3PDO/TestDbStatementSQLite3PDO.php
Normal file
105
tests/cases/Db/SQLite3PDO/TestDbStatementSQLite3PDO.php
Normal file
|
@ -0,0 +1,105 @@
|
|||
<?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;
|
||||
|
||||
use JKingWeb\Arsse\Db\Statement;
|
||||
|
||||
/**
|
||||
* @covers \JKingWeb\Arsse\Db\PDOStatement<extended>
|
||||
* @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']);
|
||||
}
|
||||
}
|
120
tests/cases/Db/SQLite3PDO/TestDbUpdateSQLite3PDO.php
Normal file
120
tests/cases/Db/SQLite3PDO/TestDbUpdateSQLite3PDO.php
Normal file
|
@ -0,0 +1,120 @@
|
|||
<?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;
|
||||
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
|
||||
/**
|
||||
* @covers \JKingWeb\Arsse\Db\SQLite3\PDODriver<extended>
|
||||
* @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);
|
||||
}
|
||||
}
|
|
@ -51,8 +51,14 @@
|
|||
<file>cases/Db/SQLite3/TestDbDriverCreationSQLite3.php</file>
|
||||
<file>cases/Db/SQLite3/TestDbDriverSQLite3.php</file>
|
||||
<file>cases/Db/SQLite3/TestDbUpdateSQLite3.php</file>
|
||||
|
||||
<file>cases/Db/SQLite3PDO/TestDbResultSQLite3PDO.php</file>
|
||||
<file>cases/Db/SQLite3PDO/TestDbStatementSQLite3PDO.php</file>
|
||||
<file>cases/Db/SQLite3PDO/TestDbDriverCreationSQLite3PDO.php</file>
|
||||
<file>cases/Db/SQLite3PDO/TestDbDriverSQLite3PDO.php</file>
|
||||
<file>cases/Db/SQLite3PDO/TestDbUpdateSQLite3PDO.php</file>
|
||||
</testsuite>
|
||||
<testsuite name="Database functions">
|
||||
<testsuite name="Database functions">
|
||||
<file>cases/Db/SQLite3/Database/TestDatabaseMiscellanySQLite3.php</file>
|
||||
<file>cases/Db/SQLite3/Database/TestDatabaseMetaSQLite3.php</file>
|
||||
<file>cases/Db/SQLite3/Database/TestDatabaseUserSQLite3.php</file>
|
||||
|
|
Loading…
Reference in a new issue