J. King
6 years ago
25 changed files with 358 additions and 379 deletions
@ -0,0 +1,112 @@ |
|||||
|
<?php |
||||
|
/** @license MIT |
||||
|
* Copyright 2017 J. King, Dustin Wilson et al. |
||||
|
* See LICENSE and AUTHORS files for details */ |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
namespace JKingWeb\Arsse\TestCase\Db; |
||||
|
|
||||
|
use JKingWeb\Arsse\Db\Result; |
||||
|
use JKingWeb\Arsse\Test\DatabaseInformation; |
||||
|
|
||||
|
abstract class BaseResult extends \JKingWeb\Arsse\Test\AbstractTest { |
||||
|
protected $resultClass; |
||||
|
protected $stringOutput; |
||||
|
protected $interface; |
||||
|
|
||||
|
abstract protected function exec(string $q); |
||||
|
abstract protected function makeResult(string $q): array; |
||||
|
|
||||
|
public function setUp() { |
||||
|
self::setConf(); |
||||
|
$info = new DatabaseInformation($this->implementation); |
||||
|
$this->interface = ($info->interfaceConstructor)(); |
||||
|
if (!$this->interface) { |
||||
|
$this->markTestSkipped("$this->implementation database driver not available"); |
||||
|
} |
||||
|
$this->resultClass = $info->resultClass; |
||||
|
$this->stringOutput = $info->stringOutput; |
||||
|
$this->exec("DROP TABLE IF EXISTS arsse_meta"); |
||||
|
} |
||||
|
|
||||
|
public function tearDown() { |
||||
|
$this->exec("DROP TABLE IF EXISTS arsse_meta"); |
||||
|
} |
||||
|
|
||||
|
public function testConstructResult() { |
||||
|
$this->assertInstanceOf(Result::class, new $this->resultClass(...$this->makeResult("SELECT 1"))); |
||||
|
} |
||||
|
|
||||
|
public function testGetChangeCountAndLastInsertId() { |
||||
|
$this->makeResult("CREATE TABLE arsse_meta(key varchar(255) primary key not null, value text)"); |
||||
|
$out = $this->makeResult("INSERT INTO arsse_meta(key,value) values('test', 1)"); |
||||
|
$rows = $out[1][0]; |
||||
|
$id = $out[1][1]; |
||||
|
$r = new $this->resultClass(...$out); |
||||
|
$this->assertSame((int) $rows, $r->changes()); |
||||
|
$this->assertSame((int) $id, $r->lastId()); |
||||
|
} |
||||
|
|
||||
|
public function testIterateOverResults() { |
||||
|
$exp = [0 => 1, 1 => 2, 2 => 3]; |
||||
|
$exp = $this->stringOutput ? $this->stringify($exp) : $exp; |
||||
|
foreach (new $this->resultClass(...$this->makeResult("SELECT 1 as col union select 2 as col union select 3 as col")) as $index => $row) { |
||||
|
$rows[$index] = $row['col']; |
||||
|
} |
||||
|
$this->assertSame($exp, $rows); |
||||
|
} |
||||
|
|
||||
|
public function testIterateOverResultsTwice() { |
||||
|
$exp = [0 => 1, 1 => 2, 2 => 3]; |
||||
|
$exp = $this->stringOutput ? $this->stringify($exp) : $exp; |
||||
|
$result = new $this->resultClass(...$this->makeResult("SELECT 1 as col union select 2 as col union select 3 as col")); |
||||
|
foreach ($result as $index => $row) { |
||||
|
$rows[$index] = $row['col']; |
||||
|
} |
||||
|
$this->assertSame($exp, $rows); |
||||
|
$this->assertException("resultReused", "Db"); |
||||
|
foreach ($result as $row) { |
||||
|
$rows[] = $row['col']; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public function testGetSingleValues() { |
||||
|
$exp = [1867, 1970, 2112]; |
||||
|
$exp = $this->stringOutput ? $this->stringify($exp) : $exp; |
||||
|
$test = new $this->resultClass(...$this->makeResult("SELECT 1867 as year union select 1970 as year union select 2112 as year")); |
||||
|
$this->assertSame($exp[0], $test->getValue()); |
||||
|
$this->assertSame($exp[1], $test->getValue()); |
||||
|
$this->assertSame($exp[2], $test->getValue()); |
||||
|
$this->assertSame(null, $test->getValue()); |
||||
|
} |
||||
|
|
||||
|
public function testGetFirstValuesOnly() { |
||||
|
$exp = [1867, 1970, 2112]; |
||||
|
$exp = $this->stringOutput ? $this->stringify($exp) : $exp; |
||||
|
$test = new $this->resultClass(...$this->makeResult("SELECT 1867 as year, 19 as century union select 1970 as year, 20 as century union select 2112 as year, 22 as century")); |
||||
|
$this->assertSame($exp[0], $test->getValue()); |
||||
|
$this->assertSame($exp[1], $test->getValue()); |
||||
|
$this->assertSame($exp[2], $test->getValue()); |
||||
|
$this->assertSame(null, $test->getValue()); |
||||
|
} |
||||
|
|
||||
|
public function testGetRows() { |
||||
|
$exp = [ |
||||
|
['album' => '2112', 'track' => '2112'], |
||||
|
['album' => 'Clockwork Angels', 'track' => 'The Wreckers'], |
||||
|
]; |
||||
|
$test = new $this->resultClass(...$this->makeResult("SELECT '2112' as album, '2112' as track union select 'Clockwork Angels' as album, 'The Wreckers' as track")); |
||||
|
$this->assertSame($exp[0], $test->getRow()); |
||||
|
$this->assertSame($exp[1], $test->getRow()); |
||||
|
$this->assertSame(null, $test->getRow()); |
||||
|
} |
||||
|
|
||||
|
public function testGetAllRows() { |
||||
|
$exp = [ |
||||
|
['album' => '2112', 'track' => '2112'], |
||||
|
['album' => 'Clockwork Angels', 'track' => 'The Wreckers'], |
||||
|
]; |
||||
|
$test = new $this->resultClass(...$this->makeResult("SELECT '2112' as album, '2112' as track union select 'Clockwork Angels' as album, 'The Wreckers' as track")); |
||||
|
$this->assertEquals($exp, $test->getAll()); |
||||
|
} |
||||
|
} |
@ -0,0 +1,33 @@ |
|||||
|
<?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\SQLite3; |
||||
|
|
||||
|
use JKingWeb\Arsse\Test\DatabaseInformation; |
||||
|
|
||||
|
/** |
||||
|
* @covers \JKingWeb\Arsse\Db\SQLite3\Result<extended> |
||||
|
*/ |
||||
|
class TestResult extends \JKingWeb\Arsse\TestCase\Db\BaseResult { |
||||
|
protected $implementation = "SQLite 3"; |
||||
|
|
||||
|
public function tearDown() { |
||||
|
parent::tearDown(); |
||||
|
$this->interface->close(); |
||||
|
unset($this->interface); |
||||
|
} |
||||
|
|
||||
|
protected function exec(string $q) { |
||||
|
$this->interface->exec($q); |
||||
|
} |
||||
|
|
||||
|
protected function makeResult(string $q): array { |
||||
|
$set = $this->interface->query($q); |
||||
|
$rows = $this->interface->changes(); |
||||
|
$id = $this->interface->lastInsertRowID(); |
||||
|
return [$set, [$rows, $id]]; |
||||
|
} |
||||
|
} |
@ -1,155 +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\TestCase\Db; |
|
||||
|
|
||||
use JKingWeb\Arsse\Arsse; |
|
||||
use JKingWeb\Arsse\Db\Result; |
|
||||
use JKingWeb\Arsse\Db\PDOResult; |
|
||||
use JKingWeb\Arsse\Db\SQLite3\PDODriver; |
|
||||
|
|
||||
/** |
|
||||
* @covers \JKingWeb\Arsse\Db\PDOResult<extended> |
|
||||
* @covers \JKingWeb\Arsse\Db\SQLite3\Result<extended> |
|
||||
*/ |
|
||||
class TestResult extends \JKingWeb\Arsse\Test\AbstractTest { |
|
||||
public function provideResults() { |
|
||||
$this->setConf(); |
|
||||
$interfaces = $this->provideDbInterfaces(); |
|
||||
$constructors = [ |
|
||||
'SQLite 3' => function(string $query) use($interfaces) { |
|
||||
$drv = $interfaces['SQLite 3']['interface']; |
|
||||
$set = $drv->query($query); |
|
||||
$rows = $drv->changes(); |
|
||||
$id = $drv->lastInsertRowID(); |
|
||||
return [$set, [$rows, $id]]; |
|
||||
}, |
|
||||
]; |
|
||||
foreach ($constructors as $drv => $func) { |
|
||||
yield $drv => [isset($interfaces[$drv]['interface']), $interfaces[$drv]['stringOutput'], $interfaces[$drv]['result'], $func]; |
|
||||
} |
|
||||
// there is only one PDO result implementation, so we test the first implementation we find |
|
||||
$pdo = array_reduce($interfaces, function ($carry, $item) { |
|
||||
return $carry ?? ($item['interface'] instanceof \PDO ? $item : null); |
|
||||
}) ?? $interfaces['PDO SQLite 3']; |
|
||||
yield "PDO" => [isset($pdo['interface']), $pdo['stringOutput'], $pdo['result'], function(string $query) use($pdo) { |
|
||||
$drv = $pdo['interface']; |
|
||||
$set = $drv->query($query); |
|
||||
$rows = $set->rowCount(); |
|
||||
$id = $drv->lastInsertID(); |
|
||||
return [$set, [$rows, $id]]; |
|
||||
}]; |
|
||||
} |
|
||||
|
|
||||
/** @dataProvider provideResults */ |
|
||||
public function testConstructResult(bool $driverTestable, bool $stringCoersion, string $class, \Closure $func) { |
|
||||
if (!$driverTestable) { |
|
||||
$this->markTestSkipped(); |
|
||||
} |
|
||||
$this->assertInstanceOf(Result::class, new $class(...$func("SELECT 1"))); |
|
||||
} |
|
||||
|
|
||||
/** @dataProvider provideResults */ |
|
||||
public function testGetChangeCountAndLastInsertId(bool $driverTestable, bool $stringCoersion, string $class, \Closure $func) { |
|
||||
if (!$driverTestable) { |
|
||||
$this->markTestSkipped(); |
|
||||
} |
|
||||
$func("CREATE TABLE if not exists arsse_meta(key varchar(255) primary key not null, value text)"); |
|
||||
$out = $func("INSERT INTO arsse_meta(key,value) values('test', 1)"); |
|
||||
$rows = $out[1][0]; |
|
||||
$id = $out[1][1]; |
|
||||
$r = new $class(...$out); |
|
||||
$this->assertSame((int) $rows, $r->changes()); |
|
||||
$this->assertSame((int) $id, $r->lastId()); |
|
||||
} |
|
||||
|
|
||||
/** @dataProvider provideResults */ |
|
||||
public function testIterateOverResults(bool $driverTestable, bool $stringCoersion, string $class, \Closure $func) { |
|
||||
if (!$driverTestable) { |
|
||||
$this->markTestSkipped(); |
|
||||
} |
|
||||
$exp = [0 => 1, 1 => 2, 2 => 3]; |
|
||||
$exp = $stringCoersion ? $this->stringify($exp) : $exp; |
|
||||
foreach (new $class(...$func("SELECT 1 as col union select 2 as col union select 3 as col")) as $index => $row) { |
|
||||
$rows[$index] = $row['col']; |
|
||||
} |
|
||||
$this->assertSame($exp, $rows); |
|
||||
} |
|
||||
|
|
||||
/** @dataProvider provideResults */ |
|
||||
public function testIterateOverResultsTwice(bool $driverTestable, bool $stringCoersion, string $class, \Closure $func) { |
|
||||
if (!$driverTestable) { |
|
||||
$this->markTestSkipped(); |
|
||||
} |
|
||||
$exp = [0 => 1, 1 => 2, 2 => 3]; |
|
||||
$exp = $stringCoersion ? $this->stringify($exp) : $exp; |
|
||||
$result = new $class(...$func("SELECT 1 as col union select 2 as col union select 3 as col")); |
|
||||
foreach ($result as $index => $row) { |
|
||||
$rows[$index] = $row['col']; |
|
||||
} |
|
||||
$this->assertSame($exp, $rows); |
|
||||
$this->assertException("resultReused", "Db"); |
|
||||
foreach ($result as $row) { |
|
||||
$rows[] = $row['col']; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** @dataProvider provideResults */ |
|
||||
public function testGetSingleValues(bool $driverTestable, bool $stringCoersion, string $class, \Closure $func) { |
|
||||
if (!$driverTestable) { |
|
||||
$this->markTestSkipped(); |
|
||||
} |
|
||||
$exp = [1867, 1970, 2112]; |
|
||||
$exp = $stringCoersion ? $this->stringify($exp) : $exp; |
|
||||
$test = new $class(...$func("SELECT 1867 as year union select 1970 as year union select 2112 as year")); |
|
||||
$this->assertSame($exp[0], $test->getValue()); |
|
||||
$this->assertSame($exp[1], $test->getValue()); |
|
||||
$this->assertSame($exp[2], $test->getValue()); |
|
||||
$this->assertSame(null, $test->getValue()); |
|
||||
} |
|
||||
|
|
||||
/** @dataProvider provideResults */ |
|
||||
public function testGetFirstValuesOnly(bool $driverTestable, bool $stringCoersion, string $class, \Closure $func) { |
|
||||
if (!$driverTestable) { |
|
||||
$this->markTestSkipped(); |
|
||||
} |
|
||||
$exp = [1867, 1970, 2112]; |
|
||||
$exp = $stringCoersion ? $this->stringify($exp) : $exp; |
|
||||
$test = new $class(...$func("SELECT 1867 as year, 19 as century union select 1970 as year, 20 as century union select 2112 as year, 22 as century")); |
|
||||
$this->assertSame($exp[0], $test->getValue()); |
|
||||
$this->assertSame($exp[1], $test->getValue()); |
|
||||
$this->assertSame($exp[2], $test->getValue()); |
|
||||
$this->assertSame(null, $test->getValue()); |
|
||||
} |
|
||||
|
|
||||
/** @dataProvider provideResults */ |
|
||||
public function testGetRows(bool $driverTestable, bool $stringCoersion, string $class, \Closure $func) { |
|
||||
if (!$driverTestable) { |
|
||||
$this->markTestSkipped(); |
|
||||
} |
|
||||
$exp = [ |
|
||||
['album' => '2112', 'track' => '2112'], |
|
||||
['album' => 'Clockwork Angels', 'track' => 'The Wreckers'], |
|
||||
]; |
|
||||
$test = new $class(...$func("SELECT '2112' as album, '2112' as track union select 'Clockwork Angels' as album, 'The Wreckers' as track")); |
|
||||
$this->assertSame($exp[0], $test->getRow()); |
|
||||
$this->assertSame($exp[1], $test->getRow()); |
|
||||
$this->assertSame(null, $test->getRow()); |
|
||||
} |
|
||||
|
|
||||
/** @dataProvider provideResults */ |
|
||||
public function testGetAllRows(bool $driverTestable, bool $stringCoersion, string $class, \Closure $func) { |
|
||||
if (!$driverTestable) { |
|
||||
$this->markTestSkipped(); |
|
||||
} |
|
||||
$exp = [ |
|
||||
['album' => '2112', 'track' => '2112'], |
|
||||
['album' => 'Clockwork Angels', 'track' => 'The Wreckers'], |
|
||||
]; |
|
||||
$test = new $class(...$func("SELECT '2112' as album, '2112' as track union select 'Clockwork Angels' as album, 'The Wreckers' as track")); |
|
||||
$this->assertEquals($exp, $test->getAll()); |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,52 @@ |
|||||
|
<?php |
||||
|
/** @license MIT |
||||
|
* Copyright 2017 J. King, Dustin Wilson et al. |
||||
|
* See LICENSE and AUTHORS files for details */ |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
namespace JKingWeb\Arsse\TestCase\Db; |
||||
|
|
||||
|
use JKingWeb\Arsse\Test\DatabaseInformation; |
||||
|
|
||||
|
/** |
||||
|
* @covers \JKingWeb\Arsse\Db\PDOResult<extended> |
||||
|
*/ |
||||
|
class TestResultPDO extends \JKingWeb\Arsse\TestCase\Db\BaseResult { |
||||
|
protected static $firstAvailableDriver; |
||||
|
|
||||
|
public static function setUpBeforeClass() { |
||||
|
self::setConf(); |
||||
|
// we only need to test one PDO implementation (they all use the same result class), so we find the first usable one |
||||
|
$drivers = DatabaseInformation::listPDO(); |
||||
|
self::$firstAvailableDriver = $drivers[0]; |
||||
|
foreach ($drivers as $driver) { |
||||
|
$info = new DatabaseInformation($driver); |
||||
|
$interface = ($info->interfaceConstructor)(); |
||||
|
if ($interface) { |
||||
|
self::$firstAvailableDriver = $driver; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public function setUp() { |
||||
|
$this->implementation = self::$firstAvailableDriver; |
||||
|
parent::setUp(); |
||||
|
} |
||||
|
|
||||
|
public function tearDown() { |
||||
|
parent::tearDown(); |
||||
|
unset($this->interface); |
||||
|
} |
||||
|
|
||||
|
protected function exec(string $q) { |
||||
|
$this->interface->exec($q); |
||||
|
} |
||||
|
|
||||
|
protected function makeResult(string $q): array { |
||||
|
$set = $this->interface->query($q); |
||||
|
$rows = $set->rowCount(); |
||||
|
$id = $this->interface->lastInsertID(); |
||||
|
return [$set, [$rows, $id]]; |
||||
|
} |
||||
|
} |
@ -0,0 +1,111 @@ |
|||||
|
<?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; |
||||
|
|
||||
|
use JKingWeb\Arsse\Arsse; |
||||
|
|
||||
|
class DatabaseInformation { |
||||
|
public $name; |
||||
|
public $backend; |
||||
|
public $pdo; |
||||
|
public $resultClass; |
||||
|
public $statementClass; |
||||
|
public $driverClass; |
||||
|
public $stringOutput; |
||||
|
public $interfaceConstructor; |
||||
|
|
||||
|
protected static $data; |
||||
|
|
||||
|
public function __construct(string $name) { |
||||
|
if (!isset(self::$data)) { |
||||
|
self::$data = self::getData(); |
||||
|
} |
||||
|
if (!isset(self::$data[$name])) { |
||||
|
throw new \Exception("Invalid database driver name"); |
||||
|
} |
||||
|
$this->name = $name; |
||||
|
foreach (self::$data[$name] as $key => $value) { |
||||
|
$this->$key = $value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static function list(): array { |
||||
|
if (!isset(self::$data)) { |
||||
|
self::$data = self::getData(); |
||||
|
} |
||||
|
return array_keys(self::$data); |
||||
|
} |
||||
|
|
||||
|
public static function listPDO(): array { |
||||
|
if (!isset(self::$data)) { |
||||
|
self::$data = self::getData(); |
||||
|
} |
||||
|
return array_values(array_filter(array_keys(self::$data), function($k) { |
||||
|
return self::$data[$k]['pdo']; |
||||
|
})); |
||||
|
} |
||||
|
|
||||
|
protected static function getData() { |
||||
|
return [ |
||||
|
'SQLite 3' => [ |
||||
|
'pdo' => false, |
||||
|
'backend' => "SQLite 3", |
||||
|
'statementClass' => \JKingWeb\Arsse\Db\SQLite3\Statement::class, |
||||
|
'resultClass' => \JKingWeb\Arsse\Db\SQLite3\Result::class, |
||||
|
'driverClass' => \JKingWeb\Arsse\Db\SQLite3\Driver::class, |
||||
|
'stringOutput' => false, |
||||
|
'interfaceConstructor' => function() { |
||||
|
try { |
||||
|
$d = new \SQLite3(Arsse::$conf->dbSQLite3File); |
||||
|
} catch (\Throwable $e) { |
||||
|
return; |
||||
|
} |
||||
|
$d->enableExceptions(true); |
||||
|
return $d; |
||||
|
}, |
||||
|
|
||||
|
], |
||||
|
'PDO SQLite 3' => [ |
||||
|
'pdo' => true, |
||||
|
'backend' => "SQLite 3", |
||||
|
'statementClass' => \JKingWeb\Arsse\Db\PDOStatement::class, |
||||
|
'resultClass' => \JKingWeb\Arsse\Db\PDOResult::class, |
||||
|
'driverClass' => \JKingWeb\Arsse\Db\SQLite3\PDODriver::class, |
||||
|
'stringOutput' => true, |
||||
|
'interfaceConstructor' => function() { |
||||
|
try { |
||||
|
$d = new \PDO("sqlite:".Arsse::$conf->dbSQLite3File, "", "", [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]); |
||||
|
$d->exec("PRAGMA busy_timeout=0"); |
||||
|
return $d; |
||||
|
} catch (\Throwable $e) { |
||||
|
return; |
||||
|
} |
||||
|
}, |
||||
|
], |
||||
|
'PDO PostgreSQL' => [ |
||||
|
'pdo' => true, |
||||
|
'backend' => "PostgreSQL", |
||||
|
'statementClass' => \JKingWeb\Arsse\Db\PDOStatement::class, |
||||
|
'resultClass' => \JKingWeb\Arsse\Db\PDOResult::class, |
||||
|
'driverClass' => \JKingWeb\Arsse\Db\PostgreSQL\PDODriver::class, |
||||
|
'stringOutput' => true, |
||||
|
'interfaceConstructor' => function() { |
||||
|
$connString = \JKingWeb\Arsse\Db\PostgreSQL\Driver::makeConnectionString(true, Arsse::$conf->dbPostgreSQLUser, Arsse::$conf->dbPostgreSQLPass, Arsse::$conf->dbPostgreSQLDb, Arsse::$conf->dbPostgreSQLHost, Arsse::$conf->dbPostgreSQLPort, ""); |
||||
|
try { |
||||
|
$d = new \PDO("pgsql:".$connString, Arsse::$conf->dbPostgreSQLUser, Arsse::$conf->dbPostgreSQLPass, [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]); |
||||
|
} catch (\Throwable $e) { |
||||
|
return; |
||||
|
} |
||||
|
foreach (\JKingWeb\Arsse\Db\PostgreSQL\PDODriver::makeSetupQueries(Arsse::$conf->dbPostgreSQLSchema) as $q) { |
||||
|
$d->exec($q); |
||||
|
} |
||||
|
return $d; |
||||
|
}, |
||||
|
], |
||||
|
]; |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue