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