mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
Still more database changes
- Restructured tests - Localized driver name for SQLite driver (fixes #37) - Ensured that binding type definitions are required
This commit is contained in:
parent
2b7a236147
commit
eeb1818bb5
8 changed files with 54 additions and 31 deletions
|
@ -23,6 +23,7 @@ abstract class AbstractException extends \Exception {
|
||||||
"Db/Exception.fileCorrupt" => 10207,
|
"Db/Exception.fileCorrupt" => 10207,
|
||||||
"Db/Exception.paramTypeInvalid" => 10401,
|
"Db/Exception.paramTypeInvalid" => 10401,
|
||||||
"Db/Exception.paramTypeUnknown" => 10402,
|
"Db/Exception.paramTypeUnknown" => 10402,
|
||||||
|
"Db/Exception.paramTypeMissing" => 10403,
|
||||||
"Db/Update/Exception.tooNew" => 10211,
|
"Db/Update/Exception.tooNew" => 10211,
|
||||||
"Db/Update/Exception.fileMissing" => 10212,
|
"Db/Update/Exception.fileMissing" => 10212,
|
||||||
"Db/Update/Exception.fileUnusable" => 10213,
|
"Db/Update/Exception.fileUnusable" => 10213,
|
||||||
|
|
|
@ -291,7 +291,10 @@ class Database {
|
||||||
throw new Feed\Exception($url, $e);
|
throw new Feed\Exception($url, $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->db->prepare("INSERT INTO newssync_feeds(url,title,favicon,source,updated,modified,etag,username,password) values(?,?,?,?,?,?,?,?,?)", "str", "str", "str", "str", "datetime", "datetime", "str", "str", "str")->run(
|
$this->db->prepare(
|
||||||
|
"INSERT INTO newssync_feeds(url,title,favicon,source,updated,modified,etag,username,password) values(?,?,?,?,?,?,?,?,?)",
|
||||||
|
"str", "str", "str", "str", "datetime", "datetime", "str", "str", "str"
|
||||||
|
)->run(
|
||||||
$url,
|
$url,
|
||||||
$feed->title,
|
$feed->title,
|
||||||
// Grab the favicon for the Goodfeed; returns an empty string if it cannot find one.
|
// Grab the favicon for the Goodfeed; returns an empty string if it cannot find one.
|
||||||
|
|
|
@ -38,7 +38,7 @@ class DriverSQLite3 extends AbstractDriver {
|
||||||
|
|
||||||
|
|
||||||
static public function driverName(): string {
|
static public function driverName(): string {
|
||||||
return "SQLite 3";
|
return Lang::msg("Driver.Db.$name.Name");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function schemaVersion(): int {
|
public function schemaVersion(): int {
|
||||||
|
|
|
@ -26,7 +26,7 @@ class StatementSQLite3 extends AbstractStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __destruct() {
|
public function __destruct() {
|
||||||
$this->st->close();
|
try {$this->st->close();} catch(\Throwable $e) {}
|
||||||
unset($this->st);
|
unset($this->st);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,14 +49,14 @@ class StatementSQLite3 extends AbstractStatement {
|
||||||
if(!array_key_exists($this->types[$a], self::BINDINGS)) throw new Exception("paramTypeUnknown", $this->types[$a]);
|
if(!array_key_exists($this->types[$a], self::BINDINGS)) throw new Exception("paramTypeUnknown", $this->types[$a]);
|
||||||
$type = self::BINDINGS[$this->types[$a]];
|
$type = self::BINDINGS[$this->types[$a]];
|
||||||
} else {
|
} else {
|
||||||
$type = \SQLITE3_TEXT;
|
throw new Exception("paramTypeMissing", $a+1);
|
||||||
}
|
}
|
||||||
// cast value if necessary
|
// cast value if necessary
|
||||||
$values[$a] = $this->cast($values[$a], $this->types[$a]);
|
$values[$a] = $this->cast($values[$a], $this->types[$a]);
|
||||||
// re-adjust for null casts
|
// re-adjust for null casts
|
||||||
if($values[$a]===null) $type = \SQLITE3_NULL;
|
if($values[$a]===null) $type = \SQLITE3_NULL;
|
||||||
// perform binding
|
// perform binding
|
||||||
$this->st->bindParam($a+1, $values[$a], $type);
|
$this->st->bindValue($a+1, $values[$a], $type);
|
||||||
}
|
}
|
||||||
return new ResultSQLite3($this->st->execute(), $this->db->changes(), $this);
|
return new ResultSQLite3($this->st->execute(), $this->db->changes(), $this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use \Webmozart\Glob\Glob;
|
||||||
class Lang {
|
class Lang {
|
||||||
const DEFAULT = "en"; // fallback locale
|
const DEFAULT = "en"; // fallback locale
|
||||||
const REQUIRED = [ // collection of absolutely required strings to handle pathological errors
|
const REQUIRED = [ // collection of absolutely required strings to handle pathological errors
|
||||||
'Exception.JKingWeb/NewsSync/Exception.uncoded' => 'The specified exception symbol {0} has no code specified in Exception.php',
|
'Exception.JKingWeb/NewsSync/Exception.uncoded' => 'The specified exception symbol {0} has no code specified in AbstractException.php',
|
||||||
'Exception.JKingWeb/NewsSync/Exception.unknown' => 'An unknown error has occurred',
|
'Exception.JKingWeb/NewsSync/Exception.unknown' => 'An unknown error has occurred',
|
||||||
'Exception.JKingWeb/NewsSync/Lang/Exception.defaultFileMissing' => 'Default language file "{0}" missing',
|
'Exception.JKingWeb/NewsSync/Lang/Exception.defaultFileMissing' => 'Default language file "{0}" missing',
|
||||||
'Exception.JKingWeb/NewsSync/Lang/Exception.fileMissing' => 'Language file "{0}" is not available',
|
'Exception.JKingWeb/NewsSync/Lang/Exception.fileMissing' => 'Language file "{0}" is not available',
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
return [
|
return [
|
||||||
'Driver.User.Internal.Name' => 'Internal',
|
'Driver.User.Internal.Name' => 'Internal',
|
||||||
|
'Driver.Db.SQLite3.Name' => 'SQLite 3',
|
||||||
|
|
||||||
// this should only be encountered in testing (because tests should cover all exceptions!)
|
// this should only be encountered in testing (because tests should cover all exceptions!)
|
||||||
'Exception.JKingWeb/NewsSync/Exception.uncoded' => 'The specified exception symbol {0} has no code specified in Exception.php',
|
'Exception.JKingWeb/NewsSync/Exception.uncoded' => 'The specified exception symbol {0} has no code specified in AbstractException.php',
|
||||||
// this should not usually be encountered
|
// this should not usually be encountered
|
||||||
'Exception.JKingWeb/NewsSync/Exception.unknown' => 'An unknown error has occurred',
|
'Exception.JKingWeb/NewsSync/Exception.unknown' => 'An unknown error has occurred',
|
||||||
|
|
||||||
|
@ -29,6 +30,7 @@ return [
|
||||||
'Exception.JKingWeb/NewsSync/Db/Exception.fileCorrupt' => 'Database file "{0}" is corrupt or not a valid database',
|
'Exception.JKingWeb/NewsSync/Db/Exception.fileCorrupt' => 'Database file "{0}" is corrupt or not a valid database',
|
||||||
'Exception.JKingWeb/NewsSync/Db/Exception.paramTypeInvalid' => 'Prepared statement parameter type "{0}" is invalid',
|
'Exception.JKingWeb/NewsSync/Db/Exception.paramTypeInvalid' => 'Prepared statement parameter type "{0}" is invalid',
|
||||||
'Exception.JKingWeb/NewsSync/Db/Exception.paramTypeUnknown' => 'Prepared statement parameter type "{0}" is valid, but not implemented',
|
'Exception.JKingWeb/NewsSync/Db/Exception.paramTypeUnknown' => 'Prepared statement parameter type "{0}" is valid, but not implemented',
|
||||||
|
'Exception.JKingWeb/NewsSync/Db/Exception.paramTypeMissing' => 'Prepared statement parameter type for parameter #{0} was not specified',
|
||||||
|
|
||||||
'Exception.JKingWeb/NewsSync/Db/Update/Exception.manual' =>
|
'Exception.JKingWeb/NewsSync/Db/Update/Exception.manual' =>
|
||||||
'{from_version, select,
|
'{from_version, select,
|
||||||
|
|
|
@ -1,32 +1,66 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
namespace JKingWeb\NewsSync;
|
namespace JKingWeb\NewsSync;
|
||||||
|
use JKingWeb\NewsSync\Db\Statement;
|
||||||
|
|
||||||
|
|
||||||
class TestDbStatementSQLite3 extends \PHPUnit\Framework\TestCase {
|
class TestDbStatementSQLite3 extends \PHPUnit\Framework\TestCase {
|
||||||
use Test\Tools, Test\Db\BindingTests;
|
use Test\Tools, Test\Db\BindingTests;
|
||||||
|
|
||||||
protected $c;
|
protected $c;
|
||||||
protected $s;
|
|
||||||
static protected $imp = Db\StatementSQLite3::class;
|
static protected $imp = Db\StatementSQLite3::class;
|
||||||
|
|
||||||
function setUp() {
|
function setUp() {
|
||||||
date_default_timezone_set("UTC");
|
date_default_timezone_set("UTC");
|
||||||
$c = new \SQLite3(":memory:");
|
$c = new \SQLite3(":memory:");
|
||||||
$c->enableExceptions(true);
|
$c->enableExceptions(true);
|
||||||
$s = $c->prepare("SELECT ? as value");
|
|
||||||
$this->c = $c;
|
$this->c = $c;
|
||||||
$this->s = $s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function tearDown() {
|
function tearDown() {
|
||||||
try {$this->s->close();} catch(\Exception $e) {}
|
try {$this->s->close();} catch(\Exception $e) {}
|
||||||
$this->c->close();
|
$this->c->close();
|
||||||
unset($this->s);
|
|
||||||
unset($this->c);
|
unset($this->c);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testConstructStatement() {
|
protected function checkBinding($input, array $expectations) {
|
||||||
$this->assertInstanceOf(Db\StatementSQLite3::class, new Db\StatementSQLite3($this->c, $this->s));
|
$nativeStatement = $this->c->prepare("SELECT ? as value");
|
||||||
|
$s = new self::$imp($this->c, $nativeStatement);
|
||||||
|
$types = array_unique(Statement::TYPES);
|
||||||
|
foreach($types as $type) {
|
||||||
|
$s->rebindArray([$type]);
|
||||||
|
$val = $s->runArray([$input])->get()['value'];
|
||||||
|
$this->assertSame($expectations[$type], $val, "Type $type failed comparison.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testConstructStatement() {
|
||||||
|
$nativeStatement = $this->c->prepare("SELECT ? as value");
|
||||||
|
$this->assertInstanceOf(Db\StatementSQLite3::class, new Db\StatementSQLite3($this->c, $nativeStatement));
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBindMissingValue() {
|
||||||
|
$nativeStatement = $this->c->prepare("SELECT ? as value");
|
||||||
|
$s = new self::$imp($this->c, $nativeStatement);
|
||||||
|
$val = $s->runArray()->get()['value'];
|
||||||
|
$this->assertSame(null, $val);
|
||||||
|
}
|
||||||
|
|
||||||
|
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])->get();
|
||||||
|
$this->assertSame($exp, $val);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBindWithoutType() {
|
||||||
|
$this->assertException("paramTypeMissing", "Db");
|
||||||
|
$nativeStatement = $this->c->prepare("SELECT ? as value");
|
||||||
|
$s = new self::$imp($this->c, $nativeStatement, []);
|
||||||
|
$val = $s->runArray([1])->get();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -4,13 +4,6 @@ namespace JKingWeb\NewsSync\Test\Db;
|
||||||
use JKingWeb\NewsSync\Db\Statement;
|
use JKingWeb\NewsSync\Db\Statement;
|
||||||
|
|
||||||
trait BindingTests {
|
trait BindingTests {
|
||||||
|
|
||||||
function testBindMissingValue() {
|
|
||||||
$s = new self::$imp($this->c, $this->s);
|
|
||||||
$val = $s->runArray()->get()['value'];
|
|
||||||
$this->assertSame(null, $val);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testBindNull() {
|
function testBindNull() {
|
||||||
$input = null;
|
$input = null;
|
||||||
$exp = [
|
$exp = [
|
||||||
|
@ -226,14 +219,4 @@ trait BindingTests {
|
||||||
function testBindImmutableDateObject() {
|
function testBindImmutableDateObject() {
|
||||||
$this->testBindMutableDateObject('\DateTimeImmutable');
|
$this->testBindMutableDateObject('\DateTimeImmutable');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function checkBinding($input, array $expectations) {
|
|
||||||
$s = new self::$imp($this->c, $this->s);
|
|
||||||
$types = array_unique(Statement::TYPES);
|
|
||||||
foreach($types as $type) {
|
|
||||||
$s->rebindArray([$type]);
|
|
||||||
$val = $s->runArray([$input])->get()['value'];
|
|
||||||
$this->assertSame($expectations[$type], $val, "Type $type failed comparison.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
Reference in a new issue