From 06ebef2a446ee7386e2072b38a3e087b1ae90a2a Mon Sep 17 00:00:00 2001 From: Christopher Vagnetoft Date: Fri, 27 Sep 2024 16:14:32 +0200 Subject: [PATCH] Implement most unit tests --- src/Daemon.php | 15 ++- src/Storage/Collection.php | 38 +++++++- src/bootstrap.php | 2 +- tests/DaemonTest.php | 53 +++++++++- tests/Storage/CollectionTest.php | 160 ++++++++++++++++++++++++++++++- tests/Storage/DatabaseTest.php | 20 +++- 6 files changed, 269 insertions(+), 19 deletions(-) diff --git a/src/Daemon.php b/src/Daemon.php index f684a9f..a677ca4 100644 --- a/src/Daemon.php +++ b/src/Daemon.php @@ -20,10 +20,19 @@ class Daemon private Database $db; + public function __construct(private string $databaseFile) + { + $this->db = new Database($this->databaseFile); + + } + + public function getDatabase(): Database + { + return $this->db; + } + public function start(): self { - $this->db = new Database("/tmp/foo.db"); - $this->listener = new TcpServer("0.0.0.0:8000"); $this->http = new HttpServer( @@ -56,7 +65,7 @@ class Daemon } } - private function onRequest(ServerRequestInterface $request): ResponseInterface + public function onRequest(ServerRequestInterface $request): ResponseInterface { $paths = explode("/",trim($request->getUri()->getPath(),"/")); diff --git a/src/Storage/Collection.php b/src/Storage/Collection.php index b747164..9d56e80 100644 --- a/src/Storage/Collection.php +++ b/src/Storage/Collection.php @@ -14,6 +14,34 @@ class Collection { } + public function deleteKey(string $key): void + { + $this->loadKeys(); + + if (!array_key_exists($key, $this->keys)) return; + + $this->db->prepare("DELETE FROM vals WHERE key_id=:id") + ->execute([ 'id' => $this->keys[$key] ]); + $this->db->prepare("DELETE FROM keys WHERE id=:id") + ->execute([ 'id' => $this->keys[$key] ]); + } + + public function deleteKeyValue(string $key, int $valueId): void + { + $this->loadKeys(); + + if (!array_key_exists($key, $this->keys)) return; + + $this->db->prepare("DELETE FROM vals WHERE key_id=:id AND id=:valueid") + ->execute([ 'id' => $this->keys[$key], 'valueid' => $valueId ]); + } + + public function deleteValue(int $valueId): void + { + $this->db->prepare("DELETE FROM vals WHERE id=:valueid") + ->execute([ 'valueid' => $valueId ]); + } + public function setValue(string $key, mixed $value, ?DateTime $validFrom = null, ?DateTime $validUntil = null, ?int $updateId = null) { // echo "setValue: {$key} = ".json_encode($value)."\n"; @@ -90,12 +118,14 @@ class Collection $valQuery = $this->db->prepare( "SELECT * FROM vals WHERE ". "key_id=:id AND (". - "(valid_from IS NULL) OR (datetime(valid_from) < datetime(:now)) AND ". - "(valid_until IS NULL) OR (datetime(valid_until) > datetime(:now))) ". + "((valid_from IS NULL) OR (datetime(valid_from) < datetime(:now))) AND ". + "((valid_until IS NULL) OR (datetime(valid_until) > datetime(:now)))) ". "ORDER BY valid_from ASC"); - $valQuery->execute([ "id" => $id, "now" => $when->format('Y-m-d H:i:sP') ]); + $valQuery->execute([ "id" => $id, "now" => $when->format('Y-m-d H:i:s P') ]); $last = null; - while ($row = $valQuery->fetch(PDO::FETCH_ASSOC)) $last = $row; + while ($row = $valQuery->fetch(PDO::FETCH_ASSOC)) { + $last = $row; + } if ($last) $resolved[$key] = json_decode($last['value']); } diff --git a/src/bootstrap.php b/src/bootstrap.php index e417a2c..3d0af31 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -5,5 +5,5 @@ require_once __DIR__."/../vendor/autoload.php"; define("APP_ROOT", dirname(__DIR__)); define("APP_DATA", APP_ROOT."/var"); -$daemon = new NoccyLabs\ParamDb\Daemon(); +$daemon = new NoccyLabs\ParamDb\Daemon(APP_DATA."/data.db"); $daemon->start(); diff --git a/tests/DaemonTest.php b/tests/DaemonTest.php index 1efef4d..39e0e0b 100644 --- a/tests/DaemonTest.php +++ b/tests/DaemonTest.php @@ -2,15 +2,42 @@ namespace NoccyLabs\ParamDb; +use NoccyLabs\ParamDb\Storage\Collection; use PHPUnit\Framework\Attributes\CoversClass; +use DateTime; +use React\Http\Message\ServerRequest; #[CoversClass(Daemon::class)] class DaemonTest extends \PHPUnit\Framework\TestCase { - + + private Daemon $daemon; + + public function setUp(): void + { + $this->daemon = new Daemon(":memory:"); + + $data = self::getTestValues(); + $coll = $this->daemon->getDatabase()->createCollection("test"); + $this->insertTestData($coll, $data); + } + public function testHandlingGetRequest() { - $this->markTestSkipped(); + $request = new ServerRequest("GET", "/test"); + $response = $this->daemon->onRequest($request); + + $this->assertEquals(200, $response->getStatusCode()); + + $resolved = json_decode($response->getBody()->getContents(), true); + + $expect = [ + 'first' => 'c', + 'second' => 'c', + ]; + + $this->assertEquals($expect, $resolved); + } public function testHandlingSetRequest() @@ -33,4 +60,26 @@ class DaemonTest extends \PHPUnit\Framework\TestCase $this->markTestSkipped(); } + private function insertTestData(Collection $collection, array $data) + { + foreach ($data as [ $key, $value, $from, $until, $id ]) { + $collection->setValue($key, $value, $from, $until); + } + } + + public static function getTestValues(): array + { + return [ + // $key, $value, ?$from, ?$until, $expectedId + [ 'first', 'a', new DateTime("10 days ago 12:00:00"), new DateTime("9 days ago 12:00:00"), 1 ], + [ 'first', 'b', new DateTime("5 days ago 12:00:00"), new DateTime("4 days ago 12:00:00"), 2 ], + [ 'first', 'c', new DateTime("1 days ago 12:00:00"), new DateTime("1 days 12:00:00"), 3 ], + [ 'first', 'd', new DateTime("5 days 12:00:00"), new DateTime("6 days 12:00:00"), 4 ], + [ 'second', 'a', new DateTime("10 days ago 12:00:00"), new DateTime("9 days ago 12:00:00"), 5 ], + [ 'second', 'b', new DateTime("5 days ago 12:00:00"), new DateTime("4 days ago 12:00:00"), 6 ], + [ 'second', 'c', null, null, 7 ], + [ 'second', 'd', new DateTime("5 days"), new DateTime("6 days"), 8 ], + ]; + } + } \ No newline at end of file diff --git a/tests/Storage/CollectionTest.php b/tests/Storage/CollectionTest.php index b1152fd..4245b6b 100644 --- a/tests/Storage/CollectionTest.php +++ b/tests/Storage/CollectionTest.php @@ -2,30 +2,180 @@ namespace NoccyLabs\ParamDb\Storage; +use DateTime; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; #[CoversClass(Collection::class)] +#[CoversClass(Database::class)] class CollectionTest extends \PHPUnit\Framework\TestCase { + private Database $db; + + private Collection $coll; + + public function setUp(): void + { + $this->db = new Database(":memory:"); + + $this->coll = $this->db->createCollection("foo"); + $this->insertTestData($this->coll, self::getTestValues()); + } + public function testInsertingValues() { - $this->markTestSkipped(); + $this->coll->setValue('third', 'c'); + + $resolved = $this->coll->resolveValues(); + + $expect = [ + 'first' => 'c', + 'second' => 'c', + 'third' => 'c', + ]; + + $this->assertEquals($expect, $resolved); + + $this->coll->setValue('third', 'd', new DateTime("5 days"), new DateTime("6 days")); + + $resolved = $this->coll->resolveValues(); + + $this->assertEquals($expect, $resolved); + + $this->coll->setValue('third', 'b', new DateTime("5 days ago"), new DateTime("4 days ago")); + + $resolved = $this->coll->resolveValues(); + + $this->assertEquals($expect, $resolved); + + } + + public function testUpdatingValues() + { + $this->coll->setValue('third', 'c'); + + $resolved = $this->coll->resolveValues(); + + $expect = [ + 'first' => 'c', + 'second' => 'c', + 'third' => 'c', + ]; + + $this->assertEquals($expect, $resolved); + + $this->coll->setValue('third', 'd', new DateTime("5 days"), new DateTime("6 days")); + + $resolved = $this->coll->resolveValues(); + + $this->assertEquals($expect, $resolved); + + $this->coll->setValue('third', 'b', new DateTime("5 days ago"), new DateTime("4 days ago")); + + $resolved = $this->coll->resolveValues(); + + $this->assertEquals($expect, $resolved); } public function testResolvingValues() { - $this->markTestSkipped(); + $resolved = $this->coll->resolveValues(); + + $expect = [ + 'first' => 'c', + 'second' => 'c', + ]; + + $this->assertEquals($expect, $resolved); } - public function testProperSelectionOrderValues() + public function testGettingAllValues() { - $this->markTestSkipped(); + $resolved = $this->coll->getAllValues(); + $values = self::getTestValuesSorted(); + $expect = []; + + foreach ($values as [$key,$value,$from,$until, $id]) { + if (!isset($expect[$key])) { + $expect[$key] = []; + } + if ($from && $until) { + $validity = [ 'validity' => [ 'from' => $from->format('Y-m-d H:i:s P'), 'until' => $until->format('Y-m-d H:i:s P') ]]; + } elseif ($from) { + $validity = [ 'validity' => [ 'from' => $from->format('Y-m-d H:i:s P') ]]; + } elseif ($until) { + $validity = [ 'validity' => [ 'until' => $until->format('Y-m-d H:i:s P') ]]; + } else { + $validity = []; + } + $expect[$key][] = [ 'value' => $value, 'id' => $id, ...$validity ]; + } + + $this->assertEquals($expect, $resolved); } public function testDeletingValues() { - $this->markTestSkipped(); + $resolved = $this->coll->resolveValues(); + + $expect = [ + 'first' => 'c', + 'second' => 'c', + ]; + + $this->assertEquals($expect, $resolved); + + $this->coll->deleteValue(5); + $resolved = $this->coll->resolveValues(); + + $this->assertEquals($expect, $resolved); + + $this->coll->deleteValue(7); + $resolved = $this->coll->resolveValues(); + + $expect = [ + 'first' => 'c', + ]; + + $this->assertEquals($expect, $resolved); + } + + private function insertTestData(Collection $collection, array $data) + { + foreach ($data as [ $key, $value, $from, $until, $id ]) { + $collection->setValue($key, $value, $from, $until); + } + } + + public static function getTestValues(): array + { + return [ + // $key, $value, ?$from, ?$until, $expectedId + [ 'first', 'a', new DateTime("10 days ago 12:00:00"), new DateTime("9 days ago 12:00:00"), 1 ], + [ 'first', 'b', new DateTime("5 days ago 12:00:00"), new DateTime("4 days ago 12:00:00"), 2 ], + [ 'first', 'c', new DateTime("1 days ago 12:00:00"), new DateTime("1 days 12:00:00"), 3 ], + [ 'first', 'd', new DateTime("5 days 12:00:00"), new DateTime("6 days 12:00:00"), 4 ], + [ 'second', 'a', new DateTime("10 days ago 12:00:00"), new DateTime("9 days ago 12:00:00"), 5 ], + [ 'second', 'b', new DateTime("5 days ago 12:00:00"), new DateTime("4 days ago 12:00:00"), 6 ], + [ 'second', 'c', null, null, 7 ], + [ 'second', 'd', new DateTime("5 days"), new DateTime("6 days"), 8 ], + ]; + } + + public static function getTestValuesSorted(): array + { + return [ + // $key, $value, ?$from, ?$until, $expectedId + [ 'first', 'a', new DateTime("10 days ago 12:00:00"), new DateTime("9 days ago 12:00:00"), 1 ], + [ 'first', 'b', new DateTime("5 days ago 12:00:00"), new DateTime("4 days ago 12:00:00"), 2 ], + [ 'first', 'c', new DateTime("1 days ago 12:00:00"), new DateTime("1 days 12:00:00"), 3 ], + [ 'first', 'd', new DateTime("5 days 12:00:00"), new DateTime("6 days 12:00:00"), 4 ], + [ 'second', 'c', null, null, 7 ], + [ 'second', 'a', new DateTime("10 days ago 12:00:00"), new DateTime("9 days ago 12:00:00"), 5 ], + [ 'second', 'b', new DateTime("5 days ago 12:00:00"), new DateTime("4 days ago 12:00:00"), 6 ], + [ 'second', 'd', new DateTime("5 days"), new DateTime("6 days"), 8 ], + ]; } } \ No newline at end of file diff --git a/tests/Storage/DatabaseTest.php b/tests/Storage/DatabaseTest.php index 434700c..e9974cf 100644 --- a/tests/Storage/DatabaseTest.php +++ b/tests/Storage/DatabaseTest.php @@ -10,17 +10,26 @@ class DatabaseTest extends \PHPUnit\Framework\TestCase public function testOpeningDatabases() { - $this->markTestSkipped(); + $database = new Database(":memory:"); + $this->assertInstanceOf(Database::class, $database); } public function testCreatingCollections() { - $this->markTestSkipped(); + $database = new Database(":memory:"); + + $collection = $database->createCollection("test"); + $this->assertInstanceOf(Collection::class, $collection); } public function testOpeningCollections() { - $this->markTestSkipped(); + $database = new Database(":memory:"); + + $collection = $database->createCollection("test"); + + $collection = $database->getCollection("test"); + $this->assertInstanceOf(Collection::class, $collection); } public function testPurgingCollection() @@ -30,7 +39,10 @@ class DatabaseTest extends \PHPUnit\Framework\TestCase public function testExceptionsIfCollectionNotExists() { - $this->markTestSkipped(); + $database = new Database(":memory:"); + + $this->expectException(\Exception::class); + $collection = $database->getCollection("test"); } } \ No newline at end of file