diff --git a/lib/Database.php b/lib/Database.php
index a75e89db..7c74eebd 100644
--- a/lib/Database.php
+++ b/lib/Database.php
@@ -8,10 +8,6 @@ use JKingWeb\Arsse\Misc\Date;
class Database {
const SCHEMA_VERSION = 1;
- const FORMAT_TS = "Y-m-d h:i:s";
- const FORMAT_DATE = "Y-m-d";
- const FORMAT_TIME = "h:i:s";
-
/** @var Db\Driver */
public $db;
@@ -29,7 +25,7 @@ class Database {
return debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3)[2]['function'];
}
- static public function listDrivers(): array {
+ static public function driverList(): array {
$sep = \DIRECTORY_SEPARATOR;
$path = __DIR__.$sep."Db".$sep;
$classes = [];
@@ -41,11 +37,11 @@ class Database {
return $classes;
}
- public function schemaVersion(): int {
+ public function driverSchemaVersion(): int {
return $this->db->schemaVersion();
}
- public function schemaUpdate(): bool {
+ public function driverSchemaUpdate(): bool {
if($this->db->schemaVersion() < self::SCHEMA_VERSION) return $this->db->schemaUpdate(self::SCHEMA_VERSION);
return false;
}
@@ -86,17 +82,16 @@ class Database {
return $this->db->prepare("SELECT value from arsse_meta where key is ?", "str")->run($key)->getValue();
}
- public function metaSet(string $key, string $value, string $type = "str"): bool {
- $out = !$this->db->prepare("UPDATE arsse_meta set value = ? where key is ?", $type, "str")->run($value, $key)->changes();
+ public function metaSet(string $key, $value, string $type = "str"): bool {
+ $out = $this->db->prepare("UPDATE arsse_meta set value = ? where key is ?", $type, "str")->run($value, $key)->changes();
if(!$out) {
- $out = $this->db->prepare("INSERT INTO arsse_meta(key,value)", "str", $type)->run($key, $value)->changes();
+ $out = $this->db->prepare("INSERT INTO arsse_meta(key,value) values(?,?)", "str", $type)->run($key, $value)->changes();
}
return (bool) $out;
}
public function metaRemove(string $key): bool {
- $this->db->prepare("DELETE from arsse_meta where key is ?", "str")->run($key);
- return true;
+ return (bool) $this->db->prepare("DELETE from arsse_meta where key is ?", "str")->run($key)->changes();
}
public function userExists(string $user): bool {
@@ -776,7 +771,7 @@ class Database {
return (bool) $out;
}
- public function articleValidateId(string $user, int $id): array {
+ protected function articleValidateId(string $user, int $id): array {
$out = $this->db->prepare(
"SELECT
arsse_articles.id as article,
diff --git a/lib/Feed.php b/lib/Feed.php
index ef2f7a07..3a6e2a03 100644
--- a/lib/Feed.php
+++ b/lib/Feed.php
@@ -52,7 +52,7 @@ class Feed {
$this->nextFetch = $this->computeNextFetch();
}
- public function download(string $url, string $lastModified = '', string $etag = '', string $username = '', string $password = ''): bool {
+ protected function download(string $url, string $lastModified = '', string $etag = '', string $username = '', string $password = ''): bool {
try {
$this->reader = new Reader($this->config);
$this->resource = $this->reader->download($url, $lastModified, $etag, $username, $password);
@@ -62,7 +62,7 @@ class Feed {
return true;
}
- public function parse(): bool {
+ protected function parse(): bool {
try {
$this->parser = $this->reader->getParser(
$this->resource->getUrl(),
@@ -192,7 +192,7 @@ class Feed {
return $out;
}
- public function matchToDatabase(int $feedID = null): bool {
+ protected function matchToDatabase(int $feedID = null): bool {
// first perform deduplication on items
$items = $this->deduplicateItems($this->data->items);
// if we haven't been given a database feed ID to check against, all items are new
@@ -221,7 +221,7 @@ class Feed {
return true;
}
- public function matchItems(array $items, array $articles): array {
+ protected function matchItems(array $items, array $articles): array {
$new = $edited = [];
// iterate through the articles and for each determine whether it is existing, edited, or entirely new
foreach($items as $i) {
@@ -260,7 +260,7 @@ class Feed {
return [$new, $edited];
}
- public function computeNextFetch(): \DateTime {
+ protected function computeNextFetch(): \DateTime {
$now = Date::normalize(time());
if(!$this->modified) {
$diff = $now->getTimestamp() - $this->lastModified->getTimestamp();
@@ -318,7 +318,7 @@ class Feed {
return $offset;
}
- public function computeLastModified() {
+ protected function computeLastModified() {
if(!$this->modified) return $this->lastModified;
$dates = $this->gatherDates();
if(sizeof($dates)) {
diff --git a/lib/Service.php b/lib/Service.php
index 268151cc..2fed9a18 100644
--- a/lib/Service.php
+++ b/lib/Service.php
@@ -9,6 +9,18 @@ class Service {
protected $drv;
/** @var \DateInterval */
protected $interval;
+
+ static public function driverList(): array {
+ $sep = \DIRECTORY_SEPARATOR;
+ $path = __DIR__.$sep."Service".$sep;
+ $classes = [];
+ foreach(glob($path."*".$sep."Driver.php") as $file) {
+ $name = basename(dirname($file));
+ $class = NS_BASE."User\\$name\\Driver";
+ $classes[$class] = $class::driverName();
+ }
+ return $classes;
+ }
protected static function interval(): \DateInterval {
return new \DateInterval(Arsse::$conf->serviceFrequency); // FIXME: this needs to fall back in case of incorrect input
@@ -32,6 +44,7 @@ class Service {
$this->drv->exec();
$this->drv->clean();
static::cleanupPost();
+ unset($list);
}
$t->add($this->interval);
do {
diff --git a/lib/User.php b/lib/User.php
index 759430e5..13ab49f9 100644
--- a/lib/User.php
+++ b/lib/User.php
@@ -19,7 +19,7 @@ class User {
protected $authzSupported = 0;
protected $actor = [];
- static public function listDrivers(): array {
+ static public function driverList(): array {
$sep = \DIRECTORY_SEPARATOR;
$path = __DIR__.$sep."User".$sep;
$classes = [];
diff --git a/tests/Db/SQLite3/Database/TestDatabaseMetaSQLite3.php b/tests/Db/SQLite3/Database/TestDatabaseMetaSQLite3.php
new file mode 100644
index 00000000..3356eec6
--- /dev/null
+++ b/tests/Db/SQLite3/Database/TestDatabaseMetaSQLite3.php
@@ -0,0 +1,9 @@
+ [
+ 'columns' => [
+ 'key' => 'str',
+ 'value' => 'str',
+ ],
+ 'rows' => [
+ //['schema_version', "".\JKingWeb\Arsse\Database::SCHEMA_VERSION],
+ ['album',"A Farewell to Kings"],
+ ],
+ ],
+ ];
+
+ function setUpSeries() {
+ // the schema_version key is a special case, and to avoid jumping through hoops for every test we deal with it now
+ $this->data = $this->dataBare;
+ // as far as tests are concerned the schema version is part of the expectations primed into the database
+ array_unshift($this->data['arsse_meta']['rows'], ['schema_version', "".Database::SCHEMA_VERSION]);
+ // but it's already been inserted by the driver, so we prime without it
+ $this->primeDatabase($this->dataBare);
+ }
+
+ function testAddANewValue() {
+ $this->assertTrue(Arsse::$db->metaSet("favourite", "Cygnus X-1"));
+ $state = $this->primeExpectations($this->data, ['arsse_meta' => ['key','value']]);
+ $state['arsse_meta']['rows'][] = ["favourite","Cygnus X-1"];
+ $this->compareExpectations($state);
+ }
+
+ function testAddANewTypedValue() {
+ $this->assertTrue(Arsse::$db->metaSet("answer", 42, "int"));
+ $this->assertTrue(Arsse::$db->metaSet("true", true, "bool"));
+ $this->assertTrue(Arsse::$db->metaSet("false", false, "bool"));
+ $this->assertTrue(Arsse::$db->metaSet("millennium", new \DateTime("2000-01-01T00:00:00Z"), "datetime"));
+ $state = $this->primeExpectations($this->data, ['arsse_meta' => ['key','value']]);
+ $state['arsse_meta']['rows'][] = ["answer","42"];
+ $state['arsse_meta']['rows'][] = ["true","1"];
+ $state['arsse_meta']['rows'][] = ["false","0"];
+ $state['arsse_meta']['rows'][] = ["millennium","2000-01-01 00:00:00"];
+ $this->compareExpectations($state);
+ }
+
+ function testChangeAnExistingValue() {
+ $this->assertTrue(Arsse::$db->metaSet("album", "Hemispheres"));
+ $state = $this->primeExpectations($this->data, ['arsse_meta' => ['key','value']]);
+ $state['arsse_meta']['rows'][1][1] = "Hemispheres";
+ $this->compareExpectations($state);
+ }
+
+ function testRemoveAValue() {
+ $this->assertTrue(Arsse::$db->metaRemove("album"));
+ $this->assertFalse(Arsse::$db->metaRemove("album"));
+ $state = $this->primeExpectations($this->data, ['arsse_meta' => ['key','value']]);
+ unset($state['arsse_meta']['rows'][1]);
+ $this->compareExpectations($state);
+ }
+
+ function testRetrieveAValue() {
+ $this->assertSame("".Database::SCHEMA_VERSION, Arsse::$db->metaGet("schema_version"));
+ $this->assertSame("A Farewell to Kings", Arsse::$db->metaGet("album"));
+ $this->assertSame(null, Arsse::$db->metaGet("this_key_does_not_exist"));
+ }
+}
\ No newline at end of file
diff --git a/tests/lib/Database/Setup.php b/tests/lib/Database/Setup.php
index 3e406ced..57484fce 100644
--- a/tests/lib/Database/Setup.php
+++ b/tests/lib/Database/Setup.php
@@ -11,6 +11,7 @@ use Phake;
trait Setup {
protected $drv;
+ protected $primed = false;
function setUp() {
// establish a clean baseline
@@ -21,20 +22,21 @@ trait Setup {
$this->setUpDriver();
// create the database interface with the suitable driver
Arsse::$db = new Database($this->drv);
- Arsse::$db->schemaUpdate();
+ Arsse::$db->driverSchemaUpdate();
// create a mock user manager
Arsse::$user = Phake::mock(User::class);
Phake::when(Arsse::$user)->authorize->thenReturn(true);
// call the additional setup method if it exists
if(method_exists($this, "setUpSeries")) $this->setUpSeries();
- // prime the database with series data
- if(isset($this->data)) $this->primeDatabase($this->data);
+ // prime the database with series data if it hasn't already been done
+ if(!$this->primed && isset($this->data)) $this->primeDatabase($this->data);
}
function tearDown() {
// call the additional teardiwn method if it exists
if(method_exists($this, "tearDownSeries")) $this->tearDownSeries();
// clean up
+ $this->primed = false;
$this->drv = null;
$this->clearData();
}
@@ -51,6 +53,7 @@ trait Setup {
}
}
$tr->commit();
+ $this->primed = true;
return true;
}
diff --git a/tests/phpunit.xml b/tests/phpunit.xml
index 27b7b207..04178c48 100644
--- a/tests/phpunit.xml
+++ b/tests/phpunit.xml
@@ -42,6 +42,7 @@
Db/SQLite3/TestDbUpdateSQLite3.php
+ Db/SQLite3/Database/TestDatabaseMetaSQLite3.php
Db/SQLite3/Database/TestDatabaseUserSQLite3.php
Db/SQLite3/Database/TestDatabaseFolderSQLite3.php
Db/SQLite3/Database/TestDatabaseFeedSQLite3.php