J. King
6 years ago
54 changed files with 2049 additions and 514 deletions
@ -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); |
|||
} |
|||
} |
@ -0,0 +1,44 @@ |
|||
<?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]]; |
|||
} |
|||
// no break |
|||
default: |
|||
return [Exception::class, "engineErrorGeneral", $err[2]]; // @codeCoverageIgnore |
|||
} |
|||
// no break |
|||
default: |
|||
return [Exception::class, "engineErrorGeneral", $err[0].": ".$err[2]]; // @codeCoverageIgnore |
|||
} |
|||
} |
|||
|
|||
public function getError(): string { |
|||
return (string) $this->db->errorInfo()[2]; |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -0,0 +1,55 @@ |
|||
<?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 = [ |
|||
"integer" => \PDO::PARAM_INT, |
|||
"float" => \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->retypeArray($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 bindValue($value, string $type, int $position): bool { |
|||
return $this->st->bindValue($position, $value, is_null($value) ? \PDO::PARAM_NULL : self::BINDINGS[$type]); |
|||
} |
|||
} |
@ -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"); |
|||
} |
|||
} |
@ -0,0 +1,14 @@ |
|||
<?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\SQLite3PDO\Database; |
|||
|
|||
/** @covers \JKingWeb\Arsse\Database<extended> */ |
|||
class TestArticle extends \JKingWeb\Arsse\Test\AbstractTest { |
|||
use \JKingWeb\Arsse\Test\Database\Setup; |
|||
use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; |
|||
use \JKingWeb\Arsse\Test\Database\SeriesArticle; |
|||
} |
@ -0,0 +1,14 @@ |
|||
<?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\SQLite3PDO\Database; |
|||
|
|||
/** @covers \JKingWeb\Arsse\Database<extended> */ |
|||
class TestCleanup extends \JKingWeb\Arsse\Test\AbstractTest { |
|||
use \JKingWeb\Arsse\Test\Database\Setup; |
|||
use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; |
|||
use \JKingWeb\Arsse\Test\Database\SeriesCleanup; |
|||
} |
@ -0,0 +1,14 @@ |
|||
<?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\SQLite3PDO\Database; |
|||
|
|||
/** @covers \JKingWeb\Arsse\Database<extended> */ |
|||
class TestFeed extends \JKingWeb\Arsse\Test\AbstractTest { |
|||
use \JKingWeb\Arsse\Test\Database\Setup; |
|||
use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; |
|||
use \JKingWeb\Arsse\Test\Database\SeriesFeed; |
|||
} |
@ -0,0 +1,14 @@ |
|||
<?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\SQLite3PDO\Database; |
|||
|
|||
/** @covers \JKingWeb\Arsse\Database<extended> */ |
|||
class TestFolder extends \JKingWeb\Arsse\Test\AbstractTest { |
|||
use \JKingWeb\Arsse\Test\Database\Setup; |
|||
use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; |
|||
use \JKingWeb\Arsse\Test\Database\SeriesFolder; |
|||
} |
@ -0,0 +1,10 @@ |
|||
<?php |
|||
declare(strict_types=1); |
|||
namespace JKingWeb\Arsse\TestCase\Db\SQLite3PDO\Database; |
|||
|
|||
/** @covers \JKingWeb\Arsse\Database<extended> */ |
|||
class TestLabel extends \JKingWeb\Arsse\Test\AbstractTest { |
|||
use \JKingWeb\Arsse\Test\Database\Setup; |
|||
use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; |
|||
use \JKingWeb\Arsse\Test\Database\SeriesLabel; |
|||
} |
@ -0,0 +1,14 @@ |
|||
<?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\SQLite3PDO\Database; |
|||
|
|||
/** @covers \JKingWeb\Arsse\Database<extended> */ |
|||
class TestMeta extends \JKingWeb\Arsse\Test\AbstractTest { |
|||
use \JKingWeb\Arsse\Test\Database\Setup; |
|||
use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; |
|||
use \JKingWeb\Arsse\Test\Database\SeriesMeta; |
|||
} |
@ -0,0 +1,14 @@ |
|||
<?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\SQLite3PDO\Database; |
|||
|
|||
/** @covers \JKingWeb\Arsse\Database<extended> */ |
|||
class TestMiscellany extends \JKingWeb\Arsse\Test\AbstractTest { |
|||
use \JKingWeb\Arsse\Test\Database\Setup; |
|||
use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; |
|||
use \JKingWeb\Arsse\Test\Database\SeriesMiscellany; |
|||
} |
@ -0,0 +1,10 @@ |
|||
<?php |
|||
declare(strict_types=1); |
|||
namespace JKingWeb\Arsse\TestCase\Db\SQLite3PDO\Database; |
|||
|
|||
/** @covers \JKingWeb\Arsse\Database<extended> */ |
|||
class TestSession extends \JKingWeb\Arsse\Test\AbstractTest { |
|||
use \JKingWeb\Arsse\Test\Database\Setup; |
|||
use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; |
|||
use \JKingWeb\Arsse\Test\Database\SeriesSession; |
|||
} |
@ -0,0 +1,14 @@ |
|||
<?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\SQLite3PDO\Database; |
|||
|
|||
/** @covers \JKingWeb\Arsse\Database<extended> */ |
|||
class TestSubscription extends \JKingWeb\Arsse\Test\AbstractTest { |
|||
use \JKingWeb\Arsse\Test\Database\Setup; |
|||
use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; |
|||
use \JKingWeb\Arsse\Test\Database\SeriesSubscription; |
|||
} |
@ -0,0 +1,14 @@ |
|||
<?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\SQLite3PDO\Database; |
|||
|
|||
/** @covers \JKingWeb\Arsse\Database<extended> */ |
|||
class TestUser extends \JKingWeb\Arsse\Test\AbstractTest { |
|||
use \JKingWeb\Arsse\Test\Database\Setup; |
|||
use \JKingWeb\Arsse\Test\Database\DriverSQLite3PDO; |
|||
use \JKingWeb\Arsse\Test\Database\SeriesUser; |
|||
} |
@ -0,0 +1,196 @@ |
|||
<?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\SQLite3PDO; |
|||
|
|||
use JKingWeb\Arsse\Arsse; |
|||
use JKingWeb\Arsse\Conf; |
|||
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 TestCreation extends \JKingWeb\Arsse\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; |
|||
} |
|||
} |
@ -0,0 +1,344 @@ |
|||
<?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\SQLite3PDO; |
|||
|
|||
use JKingWeb\Arsse\Arsse; |
|||
use JKingWeb\Arsse\Conf; |
|||
use JKingWeb\Arsse\Database; |
|||
use JKingWeb\Arsse\Db\SQLite3\PDODriver; |
|||
use JKingWeb\Arsse\Db\Result; |
|||
use JKingWeb\Arsse\Db\Statement; |
|||
|
|||
/** |
|||
* @covers \JKingWeb\Arsse\Db\SQLite3\PDODriver<extended> |
|||
* @covers \JKingWeb\Arsse\Db\PDODriver |
|||
* @covers \JKingWeb\Arsse\Db\PDOError */ |
|||
class TestDriver extends \JKingWeb\Arsse\Test\AbstractTest { |
|||
protected $data; |
|||
protected $drv; |
|||
protected $ch; |
|||
|
|||
public function setUp() { |
|||
if (!PDODriver::requirementsMet()) { |
|||
$this->markTestSkipped("PDO-SQLite extension not loaded"); |
|||
} |
|||
$this->clearData(); |
|||
$conf = new Conf(); |
|||
Arsse::$conf = $conf; |
|||
$conf->dbDriver = PDODriver::class; |
|||
$conf->dbSQLite3Timeout = 0; |
|||
$conf->dbSQLite3File = tempnam(sys_get_temp_dir(), 'ook'); |
|||
$this->drv = new 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(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(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)")); |
|||
} |
|||
} |
@ -0,0 +1,108 @@ |
|||
<?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\SQLite3PDO; |
|||
|
|||
use JKingWeb\Arsse\Db\Result; |
|||
use JKingWeb\Arsse\Db\PDOResult; |
|||
use JKingWeb\Arsse\Db\SQLite3\PDODriver; |
|||
|
|||
/** @covers \JKingWeb\Arsse\Db\PDOResult<extended> */ |
|||
class TestResult extends \JKingWeb\Arsse\Test\AbstractTest { |
|||
protected $c; |
|||
|
|||
public function setUp() { |
|||
if (!PDODriver::requirementsMet()) { |
|||
$this->markTestSkipped("PDO-SQLite extension not loaded"); |
|||
} |
|||
$this->clearData(); |
|||
$c = new \PDO("sqlite::memory:", "", "", [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]); |
|||
$this->c = $c; |
|||
} |
|||
|
|||
public function tearDown() { |
|||
unset($this->c); |
|||
$this->clearData(); |
|||
} |
|||
|
|||
public function testConstructResult() { |
|||
$set = $this->c->query("SELECT 1"); |
|||
$this->assertInstanceOf(Result::class, new 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 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 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 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 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 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 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 PDOResult($set); |
|||
$this->assertEquals($rows, $test->getAll()); |
|||
} |
|||
} |
@ -0,0 +1,313 @@ |
|||
<?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\SQLite3PDO; |
|||
|
|||
use JKingWeb\Arsse\Db\Statement; |
|||
use JKingWeb\Arsse\Db\PDOStatement; |
|||
use JKingWeb\Arsse\Db\SQLite3\PDODriver; |
|||
|
|||
/** |
|||
* @covers \JKingWeb\Arsse\Db\PDOStatement<extended> |
|||
* @covers \JKingWeb\Arsse\Db\PDOError */ |
|||
class TestStatement extends \JKingWeb\Arsse\Test\AbstractTest { |
|||
protected $c; |
|||
protected static $imp = \JKingWeb\Arsse\Db\PDOStatement::class; |
|||
|
|||
public function setUp() { |
|||
if (!PDODriver::requirementsMet()) { |
|||
$this->markTestSkipped("PDO-SQLite extension not loaded"); |
|||
} |
|||
$this->clearData(); |
|||
$c = new \PDO("sqlite::memory:", "", "", [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]); |
|||
$this->c = $c; |
|||
} |
|||
|
|||
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->retypeArray([$strict ? "strict $type" : $type]); |
|||
$val = $s->runArray([$input])->getRow()['value']; |
|||
$this->assertSame($expectations[$type], $val, "Binding from type $type failed comparison."); |
|||
$s->retype(...[$strict ? "strict $type" : $type]); |
|||
$val = $s->run(...[$input])->getRow()['value']; |
|||
$this->assertSame($expectations[$type], $val, "Binding from type $type failed comparison."); |
|||
} |
|||
} |
|||
|
|||
/** @dataProvider provideBindings */ |
|||
public function testBindATypedValue($value, $type, $exp) { |
|||
$typeStr = "'".str_replace("'", "''", $type)."'"; |
|||
$nativeStatement = $this->c->prepare( |
|||
"SELECT ( |
|||
(CASE WHEN substr($typeStr, 0, 7) <> 'strict ' then null else 1 end) is null |
|||
and ? is null |
|||
) or ( |
|||
$exp = ? |
|||
) as pass" |
|||
); |
|||
$s = new self::$imp($this->c, $nativeStatement); |
|||
$s->retype(...[$type, $type]); |
|||
$act = (bool) $s->run(...[$value, $value])->getRow()['pass']; |
|||
$this->assertTrue($act); |
|||
} |
|||
|
|||
public function provideBindings() { |
|||
$dateMutable = new \DateTime("Noon Today", new \DateTimezone("America/Toronto")); |
|||
$dateImmutable = new \DateTimeImmutable("Noon Today", new \DateTimezone("America/Toronto")); |
|||
$dateUTC = new \DateTime("@".$dateMutable->getTimestamp(), new \DateTimezone("UTC")); |
|||
return [ |
|||
/* input, type, expected binding as SQL fragment */ |
|||
[null, "integer", "null"], |
|||
[null, "float", "null"], |
|||
[null, "string", "null"], |
|||
[null, "binary", "null"], |
|||
[null, "datetime", "null"], |
|||
[null, "boolean", "null"], |
|||
[null, "strict integer", "0"], |
|||
[null, "strict float", "'0'"], |
|||
[null, "strict string", "''"], |
|||
[null, "strict binary", "x''"], |
|||
[null, "strict datetime", "'1970-01-01 00:00:00'"], |
|||
[null, "strict boolean", "0"], |
|||
// true |
|||
[true, "integer", "1"], |
|||
[true, "float", "'1'"], |
|||
[true, "string", "'1'"], |
|||
[true, "binary", "x'31'"], |
|||
[true, "datetime", "null"], |
|||
[true, "boolean", "1"], |
|||
[true, "strict integer", "1"], |
|||
[true, "strict float", "'1'"], |
|||
[true, "strict string", "'1'"], |
|||
[true, "strict binary", "x'31'"], |
|||
[true, "strict datetime", "'1970-01-01 00:00:00'"], |
|||
[true, "strict boolean", "1"], |
|||
// false |
|||
[false, "integer", "0"], |
|||
[false, "float", "'0'"], |
|||
[false, "string", "''"], |
|||
[false, "binary", "x''"], |
|||
[false, "datetime", "null"], |
|||
[false, "boolean", "0"], |
|||
[false, "strict integer", "0"], |
|||
[false, "strict float", "'0'"], |
|||
[false, "strict string", "''"], |
|||
[false, "strict binary", "x''"], |
|||
[false, "strict datetime", "'1970-01-01 00:00:00'"], |
|||
[false, "strict boolean", "0"], |
|||
// integer |
|||
[2112, "integer", "2112"], |
|||
[2112, "float", "'2112'"], |
|||
[2112, "string", "'2112'"], |
|||
[2112, "binary", "x'32313132'"], |
|||
[2112, "datetime", "'1970-01-01 00:35:12'"], |
|||
[2112, "boolean", "1"], |
|||
[2112, "strict integer", "2112"], |
|||
[2112, "strict float", "'2112'"], |
|||
[2112, "strict string", "'2112'"], |
|||
[2112, "strict binary", "x'32313132'"], |
|||
[2112, "strict datetime", "'1970-01-01 00:35:12'"], |
|||
[2112, "strict boolean", "1"], |
|||
// integer zero |
|||
[0, "integer", "0"], |
|||
[0, "float", "'0'"], |
|||
[0, "string", "'0'"], |
|||
[0, "binary", "x'30'"], |
|||
[0, "datetime", "'1970-01-01 00:00:00'"], |
|||
[0, "boolean", "0"], |
|||
[0, "strict integer", "0"], |
|||
[0, "strict float", "'0'"], |
|||
[0, "strict string", "'0'"], |
|||
[0, "strict binary", "x'30'"], |
|||
[0, "strict datetime", "'1970-01-01 00:00:00'"], |
|||
[0, "strict boolean", "0"], |
|||
// float |
|||
[2112.5, "integer", "2112"], |
|||
[2112.5, "float", "'2112.5'"], |
|||
[2112.5, "string", "'2112.5'"], |
|||
[2112.5, "binary", "x'323131322e35'"], |
|||
[2112.5, "datetime", "'1970-01-01 00:35:12'"], |
|||
[2112.5, "boolean", "1"], |
|||
[2112.5, "strict integer", "2112"], |
|||
[2112.5, "strict float", "'2112.5'"], |
|||
[2112.5, "strict string", "'2112.5'"], |
|||
[2112.5, "strict binary", "x'323131322e35'"], |
|||
[2112.5, "strict datetime", "'1970-01-01 00:35:12'"], |
|||
[2112.5, "strict boolean", "1"], |
|||
// float zero |
|||
[0.0, "integer", "0"], |
|||
[0.0, "float", "'0'"], |
|||
[0.0, "string", "'0'"], |
|||
[0.0, "binary", "x'30'"], |
|||
[0.0, "datetime", "'1970-01-01 00:00:00'"], |
|||
[0.0, "boolean", "0"], |
|||
[0.0, "strict integer", "0"], |
|||
[0.0, "strict float", "'0'"], |
|||
[0.0, "strict string", "'0'"], |
|||
[0.0, "strict binary", "x'30'"], |
|||
[0.0, "strict datetime", "'1970-01-01 00:00:00'"], |
|||
[0.0, "strict boolean", "0"], |
|||
// ASCII string |
|||
["Random string", "integer", "0"], |
|||
["Random string", "float", "'0'"], |
|||
["Random string", "string", "'Random string'"], |
|||
["Random string", "binary", "x'52616e646f6d20737472696e67'"], |
|||
["Random string", "datetime", "null"], |
|||
["Random string", "boolean", "1"], |
|||
["Random string", "strict integer", "0"], |
|||
["Random string", "strict float", "'0'"], |
|||
["Random string", "strict string", "'Random string'"], |
|||
["Random string", "strict binary", "x'52616e646f6d20737472696e67'"], |
|||
["Random string", "strict datetime", "'1970-01-01 00:00:00'"], |
|||
["Random string", "strict boolean", "1"], |
|||
// UTF-8 string |
|||
["é", "integer", "0"], |
|||
["é", "float", "'0'"], |
|||
["é", "string", "char(233)"], |
|||
["é", "binary", "x'c3a9'"], |
|||
["é", "datetime", "null"], |
|||
["é", "boolean", "1"], |
|||
["é", "strict integer", "0"], |
|||
["é", "strict float", "'0'"], |
|||
["é", "strict string", "char(233)"], |
|||
["é", "strict binary", "x'c3a9'"], |
|||
["é", "strict datetime", "'1970-01-01 00:00:00'"], |
|||
["é", "strict boolean", "1"], |
|||
// binary string |
|||
[chr(233).chr(233), "integer", "0"], |
|||
[chr(233).chr(233), "float", "'0'"], |
|||
[chr(233).chr(233), "string", "'".chr(233).chr(233)."'"], |
|||
[chr(233).chr(233), "binary", "x'e9e9'"], |
|||
[chr(233).chr(233), "datetime", "null"], |
|||
[chr(233).chr(233), "boolean", "1"], |
|||
[chr(233).chr(233), "strict integer", "0"], |
|||
[chr(233).chr(233), "strict float", "'0'"], |
|||
[chr(233).chr(233), "strict string", "'".chr(233).chr(233)."'"], |
|||
[chr(233).chr(233), "strict binary", "x'e9e9'"], |
|||
[chr(233).chr(233), "strict datetime", "'1970-01-01 00:00:00'"], |
|||
[chr(233).chr(233), "strict boolean", "1"], |
|||
// ISO 8601 date string |
|||
["2017-01-09T13:11:17", "integer", "0"], |
|||
["2017-01-09T13:11:17", "float", "'0'"], |
|||
["2017-01-09T13:11:17", "string", "'2017-01-09T13:11:17'"], |
|||
["2017-01-09T13:11:17", "binary", "x'323031372d30312d30395431333a31313a3137'"], |
|||
["2017-01-09T13:11:17", "datetime", "'2017-01-09 13:11:17'"], |
|||
["2017-01-09T13:11:17", "boolean", "1"], |
|||
["2017-01-09T13:11:17", "strict integer", "0"], |
|||
["2017-01-09T13:11:17", "strict float", "'0'"], |
|||
["2017-01-09T13:11:17", "strict string", "'2017-01-09T13:11:17'"], |
|||
["2017-01-09T13:11:17", "strict binary", "x'323031372d30312d30395431333a31313a3137'"], |
|||
["2017-01-09T13:11:17", "strict datetime", "'2017-01-09 13:11:17'"], |
|||
["2017-01-09T13:11:17", "strict boolean", "1"], |
|||
// arbitrary date string |
|||
["Today", "integer", "0"], |
|||
["Today", "float", "'0'"], |
|||
["Today", "string", "'Today'"], |
|||
["Today", "binary", "x'546f646179'"], |
|||
["Today", "datetime", "'".date_create("Today", new \DateTimezone("UTC"))->format("Y-m-d H:i:s")."'"], |
|||
["Today", "boolean", "1"], |
|||
["Today", "strict integer", "0"], |
|||
["Today", "strict float", "'0'"], |
|||
["Today", "strict string", "'Today'"], |
|||
["Today", "strict binary", "x'546f646179'"], |
|||
["Today", "strict datetime", "'".date_create("Today", new \DateTimezone("UTC"))->format("Y-m-d H:i:s")."'"], |
|||
["Today", "strict boolean", "1"], |
|||
// mutable date object |
|||
[$dateMutable, "integer", $dateUTC->getTimestamp()], |
|||
[$dateMutable, "float", "'".$dateUTC->getTimestamp()."'"], |
|||
[$dateMutable, "string", "'".$dateUTC->format("Y-m-d H:i:s")."'"], |
|||
[$dateMutable, "binary", "x'".bin2hex($dateUTC->format("Y-m-d H:i:s"))."'"], |
|||
[$dateMutable, "datetime", "'".$dateUTC->format("Y-m-d H:i:s")."'"], |
|||
[$dateMutable, "boolean", "1"], |
|||
[$dateMutable, "strict integer", $dateUTC->getTimestamp()], |
|||
[$dateMutable, "strict float", "'".$dateUTC->getTimestamp()."'"], |
|||
[$dateMutable, "strict string", "'".$dateUTC->format("Y-m-d H:i:s")."'"], |
|||
[$dateMutable, "strict binary", "x'".bin2hex($dateUTC->format("Y-m-d H:i:s"))."'"], |
|||
[$dateMutable, "strict datetime", "'".$dateUTC->format("Y-m-d H:i:s")."'"], |
|||
[$dateMutable, "strict boolean", "1"], |
|||
// immutable date object |
|||
[$dateImmutable, "integer", $dateUTC->getTimestamp()], |
|||
[$dateImmutable, "float", "'".$dateUTC->getTimestamp()."'"], |
|||
[$dateImmutable, "string", "'".$dateUTC->format("Y-m-d H:i:s")."'"], |
|||
[$dateImmutable, "binary", "x'".bin2hex($dateUTC->format("Y-m-d H:i:s"))."'"], |
|||
[$dateImmutable, "datetime", "'".$dateUTC->format("Y-m-d H:i:s")."'"], |
|||
[$dateImmutable, "boolean", "1"], |
|||
[$dateImmutable, "strict integer", $dateUTC->getTimestamp()], |
|||
[$dateImmutable, "strict float", "'".$dateUTC->getTimestamp()."'"], |
|||
[$dateImmutable, "strict string", "'".$dateUTC->format("Y-m-d H:i:s")."'"], |
|||
[$dateImmutable, "strict binary", "x'".bin2hex($dateUTC->format("Y-m-d H:i:s"))."'"], |
|||
[$dateImmutable, "strict datetime", "'".$dateUTC->format("Y-m-d H:i:s")."'"], |
|||
[$dateImmutable, "strict boolean", "1"], |
|||
]; |
|||
} |
|||
|
|||
public function testConstructStatement() { |
|||
$nativeStatement = $this->c->prepare("SELECT ? as value"); |
|||
$this->assertInstanceOf(Statement::class, new PDOStatement($this->c, $nativeStatement)); |
|||
} |
|||
|
|||
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']); |
|||
} |
|||
} |
@ -0,0 +1,125 @@ |
|||
<?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\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(Conf $conf = null) { |
|||
if (!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 = PDODriver::class; |
|||
$conf->dbSQLite3File = ":memory:"; |
|||
Arsse::$conf = $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); |
|||
$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); |
|||
} |
|||
} |
@ -0,0 +1,12 @@ |
|||
<?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\REST\NextCloudNews\PDO; |
|||
|
|||
/** @covers \JKingWeb\Arsse\REST\NextCloudNews\V1_2<extended> */ |
|||
class TestV1_2 extends \JKingWeb\Arsse\TestCase\REST\NextCloudNews\TestV1_2 { |
|||
use \JKingWeb\Arsse\Test\PDOTest; |
|||
} |
@ -0,0 +1,13 @@ |
|||
<?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\REST\TinyTinyRSS\PDO; |
|||
|
|||
/** @covers \JKingWeb\Arsse\REST\TinyTinyRSS\API<extended> |
|||
* @covers \JKingWeb\Arsse\REST\TinyTinyRSS\Exception */ |
|||
class TestAPI extends \JKingWeb\Arsse\TestCase\REST\TinyTinyRSS\TestAPI { |
|||
use \JKingWeb\Arsse\Test\PDOTest; |
|||
} |
@ -0,0 +1,24 @@ |
|||
<?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\Test\Database; |
|||
|
|||
use JKingWeb\Arsse\Arsse; |
|||
use JKingWeb\Arsse\Db\SQLite3\PDODriver; |
|||
|
|||
trait DriverSQLite3PDO { |
|||
public function setUpDriver() { |
|||
if (!PDODriver::requirementsMet()) { |
|||
$this->markTestSkipped("PDO-SQLite extension not loaded"); |
|||
} |
|||
Arsse::$conf->dbSQLite3File = ":memory:"; |
|||
$this->drv = new PDODriver(); |
|||
} |
|||
|
|||
public function nextID(string $table): int { |
|||
return (int) $this->drv->query("SELECT (case when max(id) then max(id) else 0 end)+1 from $table")->getValue(); |
|||
} |
|||
} |
@ -1,253 +0,0 @@ |
|||
<?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\Test\Db; |
|||
|
|||
use JKingWeb\Arsse\Db\Statement; |
|||
|
|||
trait BindingTests { |
|||
public function testBindNull() { |
|||
$input = null; |
|||
$exp = [ |
|||
"null" => null, |
|||
"integer" => null, |
|||
"float" => null, |
|||
"date" => null, |
|||
"time" => null, |
|||
"datetime" => null, |
|||
"binary" => null, |
|||
"string" => null, |
|||
"boolean" => null, |
|||
]; |
|||
$this->checkBinding($input, $exp); |
|||
// types may also be strict (e.g. "strict integer") and never pass null to the database; this is useful for NOT NULL columns |
|||
// only null input should yield different results, so only this test has different expectations |
|||
$exp = [ |
|||
"null" => null, |
|||
"integer" => 0, |
|||
"float" => 0.0, |
|||
"date" => gmdate("Y-m-d", 0), |
|||
"time" => gmdate("H:i:s", 0), |
|||
"datetime" => gmdate("Y-m-d H:i:s", 0), |
|||
"binary" => "", |
|||
"string" => "", |
|||
"boolean" => 0, |
|||
]; |
|||
$this->checkBinding($input, $exp, true); |
|||
} |
|||
|
|||
public function testBindTrue() { |
|||
$input = true; |
|||
$exp = [ |
|||
"null" => null, |
|||
"integer" => 1, |
|||
"float" => 1.0, |
|||
"date" => null, |
|||
"time" => null, |
|||
"datetime" => null, |
|||
"binary" => "1", |
|||
"string" => "1", |
|||
"boolean" => 1, |
|||
]; |
|||
$this->checkBinding($input, $exp); |
|||
$this->checkBinding($input, $exp, true); |
|||
} |
|||
|
|||
public function testBindFalse() { |
|||
$input = false; |
|||
$exp = [ |
|||
"null" => null, |
|||
"integer" => 0, |
|||
"float" => 0.0, |
|||
"date" => null, |
|||
"time" => null, |
|||
"datetime" => null, |
|||
"binary" => "", |
|||
"string" => "", |
|||
"boolean" => 0, |
|||
]; |
|||
$this->checkBinding($input, $exp); |
|||
$this->checkBinding($input, $exp, true); |
|||
} |
|||
|
|||
public function testBindInteger() { |
|||
$input = 2112; |
|||
$exp = [ |
|||
"null" => null, |
|||
"integer" => 2112, |
|||
"float" => 2112.0, |
|||
"date" => gmdate("Y-m-d", 2112), |
|||
"time" => gmdate("H:i:s", 2112), |
|||
"datetime" => gmdate("Y-m-d H:i:s", 2112), |
|||
"binary" => "2112", |
|||
"string" => "2112", |
|||
"boolean" => 1, |
|||
]; |
|||
$this->checkBinding($input, $exp); |
|||
$this->checkBinding($input, $exp, true); |
|||
} |
|||
|
|||
public function testBindIntegerZero() { |
|||
$input = 0; |
|||
$exp = [ |
|||
"null" => null, |
|||
"integer" => 0, |
|||
"float" => 0.0, |
|||
"date" => gmdate("Y-m-d", 0), |
|||
"time" => gmdate("H:i:s", 0), |
|||
"datetime" => gmdate("Y-m-d H:i:s", 0), |
|||
"binary" => "0", |
|||
"string" => "0", |
|||
"boolean" => 0, |
|||
]; |
|||
$this->checkBinding($input, $exp); |
|||
$this->checkBinding($input, $exp, true); |
|||
} |
|||
|
|||
public function testBindFloat() { |
|||
$input = 2112.0; |
|||
$exp = [ |
|||
"null" => null, |
|||
"integer" => 2112, |
|||
"float" => 2112.0, |
|||
"date" => gmdate("Y-m-d", 2112), |
|||
"time" => gmdate("H:i:s", 2112), |
|||
"datetime" => gmdate("Y-m-d H:i:s", 2112), |
|||
"binary" => "2112", |
|||
"string" => "2112", |
|||
"boolean" => 1, |
|||
]; |
|||
$this->checkBinding($input, $exp); |
|||
$this->checkBinding($input, $exp, true); |
|||
} |
|||
|
|||
public function testBindFloatZero() { |
|||
$input = 0.0; |
|||
$exp = [ |
|||
"null" => null, |
|||
"integer" => 0, |
|||
"float" => 0.0, |
|||
"date" => gmdate("Y-m-d", 0), |
|||
"time" => gmdate("H:i:s", 0), |
|||
"datetime" => gmdate("Y-m-d H:i:s", 0), |
|||
"binary" => "0", |
|||
"string" => "0", |
|||
"boolean" => 0, |
|||
]; |
|||
$this->checkBinding($input, $exp); |
|||
$this->checkBinding($input, $exp, true); |
|||
} |
|||
|
|||
public function testBindAsciiString() { |
|||
$input = "Random string"; |
|||
$exp = [ |
|||
"null" => null, |
|||
"integer" => 0, |
|||
"float" => 0.0, |
|||
"date" => null, |
|||
"time" => null, |
|||
"datetime" => null, |
|||
"binary" => $input, |
|||
"string" => $input, |
|||
"boolean" => 1, |
|||
]; |
|||
$this->checkBinding($input, $exp); |
|||
$this->checkBinding($input, $exp, true); |
|||
} |
|||
|
|||
public function testBindUtf8String() { |
|||
$input = "é"; |
|||
$exp = [ |
|||
"null" => null, |
|||
"integer" => 0, |
|||
"float" => 0.0, |
|||
"date" => null, |
|||
"time" => null, |
|||
"datetime" => null, |
|||
"binary" => $input, |
|||
"string" => $input, |
|||
"boolean" => 1, |
|||
]; |
|||
$this->checkBinding($input, $exp); |
|||
$this->checkBinding($input, $exp, true); |
|||
} |
|||
|
|||
public function testBindBinaryString() { |
|||
// FIXME: This test may be unreliable; SQLite happily stores invalid UTF-8 text as bytes untouched, but other engines probably don't do this |
|||
$input = chr(233); |
|||
$exp = [ |
|||
"null" => null, |
|||
"integer" => 0, |
|||
"float" => 0.0, |
|||
"date" => null, |
|||
"time" => null, |
|||
"datetime" => null, |
|||
"binary" => $input, |
|||
"string" => $input, |
|||
"boolean" => 1, |
|||
]; |
|||
$this->checkBinding($input, $exp); |
|||
$this->checkBinding($input, $exp, true); |
|||
} |
|||
|
|||
public function testBindIso8601DateString() { |
|||
$input = "2017-01-09T13:11:17"; |
|||
$time = strtotime($input." UTC"); |
|||
$exp = [ |
|||
"null" => null, |
|||
"integer" => 2017, |
|||
"float" => 2017.0, |
|||
"date" => gmdate("Y-m-d", $time), |
|||
"time" => gmdate("H:i:s", $time), |
|||
"datetime" => gmdate("Y-m-d H:i:s", $time), |
|||
"binary" => $input, |
|||
"string" => $input, |
|||
"boolean" => 1, |
|||
]; |
|||
$this->checkBinding($input, $exp); |
|||
$this->checkBinding($input, $exp, true); |
|||
} |
|||
|
|||
public function testBindArbitraryDateString() { |
|||
$input = "Today"; |
|||
$time = date_create($input, new \DateTimezone("UTC"))->getTimestamp(); |
|||
$exp = [ |
|||
"null" => null, |
|||
"integer" => 0, |
|||
"float" => 0.0, |
|||
"date" => gmdate("Y-m-d", $time), |
|||
"time" => gmdate("H:i:s", $time), |
|||
"datetime" => gmdate("Y-m-d H:i:s", $time), |
|||
"binary" => $input, |
|||
"string" => $input, |
|||
"boolean" => 1, |
|||
]; |
|||
$this->checkBinding($input, $exp); |
|||
$this->checkBinding($input, $exp, true); |
|||
} |
|||
|
|||
public function testBindMutableDateObject($class = '\DateTime') { |
|||
$input = new $class("Noon Today"); |
|||
$time = $input->getTimestamp(); |
|||
$exp = [ |
|||
"null" => null, |
|||
"integer" => $time, |
|||
"float" => (float) $time, |
|||
"date" => gmdate("Y-m-d", $time), |
|||
"time" => gmdate("H:i:s", $time), |
|||
"datetime" => gmdate("Y-m-d H:i:s", $time), |
|||
"binary" => gmdate("Y-m-d H:i:s", $time), |
|||
"string" => gmdate("Y-m-d H:i:s", $time), |
|||
"boolean" => 1, |
|||
]; |
|||
$this->checkBinding($input, $exp); |
|||
$this->checkBinding($input, $exp, true); |
|||
} |
|||
|
|||
public function testBindImmutableDateObject() { |
|||
$this->testBindMutableDateObject('\DateTimeImmutable'); |
|||
} |
|||
} |
@ -0,0 +1,23 @@ |
|||
<?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\Test; |
|||
|
|||
trait PDOTest { |
|||
protected function v($value) { |
|||
if (!is_array($value)) { |
|||
return $value; |
|||
} |
|||
foreach($value as $k => $v) { |
|||
if (is_array($v)) { |
|||
$value[$k] = $this->v($v); |
|||
} elseif (is_int($v) || is_float($v)) { |
|||
$value[$k] = (string) $v; |
|||
} |
|||
} |
|||
return $value; |
|||
} |
|||
} |
Loading…
Reference in new issue