From b3e5b0e1b02ae27aac8f95a285220dfbe36cc052 Mon Sep 17 00:00:00 2001 From: fogelito Date: Mon, 6 Feb 2023 16:19:12 +0200 Subject: [PATCH 1/9] or operator --- src/Database/Adapter/MariaDB.php | 5 +++++ src/Database/Query.php | 7 +++++++ tests/Database/Base.php | 15 +++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index 57caec52b..87e57e8b8 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -751,6 +751,11 @@ public function find(string $collection, array $queries = [], int $limit = 25, i $conditions = []; switch ($query->getMethod()) { + case Query::TYPE_OR: + var_dump($query); + die; + break; + case Query::TYPE_IS_NULL: case Query::TYPE_IS_NOT_NULL: $conditions[] = $this->getSQLCondition('table_main.`' . $query->getAttribute() . '`', $query->getMethod(), null, null); diff --git a/src/Database/Query.php b/src/Database/Query.php index fc76c0329..ff0b5e663 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -17,6 +17,7 @@ class Query const TYPE_SEARCH = 'search'; const TYPE_IS_NULL = 'isNull'; const TYPE_IS_NOT_NULL = 'isNotNull'; + const TYPE_OR = 'or'; // Order methods const TYPE_ORDERDESC = 'orderDesc'; @@ -556,6 +557,11 @@ public static function isNotNull(string $attribute): self return new self(self::TYPE_IS_NOT_NULL, $attribute); } + public static function or(array ...$queries): Query + { + return new self(self::TYPE_OR, '', $queries); + } + /** * Filters $queries for $types * @@ -648,6 +654,7 @@ public static function groupByType(array $queries): array } return [ + 'nesting' => $filters, 'filters' => $filters, 'limit' => $limit, 'offset' => $offset, diff --git a/tests/Database/Base.php b/tests/Database/Base.php index a4a95975e..2538a2bf7 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -1944,6 +1944,21 @@ public function testFindEdgeCases(Document $document) $this->assertEquals(['any'], $documents[0]->getDelete()); $this->assertEquals($values[0], $documents[0]->getAttribute('value')); + + $documents = static::getDatabase()->find($collection, [ + Query::limit(25), + Query::equal('value', [$value]), + Query::or( + [Query::equal('value', [1,2]), Query::equal('value', [3,4])], + [Query::equal('value', [555]), Query::equal('value', [666])] + ) + ]); + + + exit; + + + /** * Check `equals` query */ From 50a6a0ef9f5e9e10cc7f0176e6a1c4587e2d2c05 Mon Sep 17 00:00:00 2001 From: fogelito Date: Mon, 6 Feb 2023 18:32:12 +0200 Subject: [PATCH 2/9] bind param error --- src/Database/Adapter/MariaDB.php | 38 ++++++++++++++++++++++++++++++-- src/Database/Adapter/SQL.php | 12 +++++----- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index 2a6c01828..3806dbbf2 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -652,6 +652,32 @@ public function deleteDocument(string $collection, string $id): bool return true; } + + /** + * @throws Exception + */ + public function nested(array $queries = []): string + { + $separator = 'and'; + + $conditions = []; + foreach ($queries as $query) { + /* @var $query Query */ + if($query->getMethod() === Query::TYPE_OR){ + + $separator = 'or'; + + foreach ($query->getValues() as $queriesArray) { + $conditions[] = $this->nested($queriesArray); + } + } + else $conditions[] = $this->getSQLCondition($query); + } + + $tmp = implode(" {$separator} ", $conditions); + return empty($tmp) ? '' : '(' . $tmp . ')'; + } + /** * Find Documents * @@ -740,10 +766,15 @@ public function find(string $collection, array $queries = [], int $limit = 25, i } } - foreach ($queries as $query) { - $where[] = $this->getSQLCondition($query); + if(!empty($queries)){ + $where[] = $this->nested($queries); } + var_dump($where); +// foreach ($queries as $query) { +// $where[] = $this->getSQLCondition($query); +// } + $order = 'ORDER BY ' . implode(', ', $orders); if (Authorization::$status) { @@ -760,6 +791,9 @@ public function find(string $collection, array $queries = [], int $limit = 25, i {$order} LIMIT :offset, :limit; "; + + var_dump($sql); + $stmt = $this->getPDO()->prepare($sql); foreach ($queries as $query) { $this->bindConditionValue($stmt, $query); diff --git a/src/Database/Adapter/SQL.php b/src/Database/Adapter/SQL.php index 2f4af01ee..666d23192 100644 --- a/src/Database/Adapter/SQL.php +++ b/src/Database/Adapter/SQL.php @@ -822,11 +822,13 @@ protected function getSQLPlaceholder(Query $query): string * @return void */ public function bindConditionValue($stmt, Query $query){ - /** @var PDOStatement $stmt */ - if ($query->getMethod() === Query::TYPE_SEARCH) return; - foreach ($query->getValues() as $key => $value) { - $placeholder = $this->getSQLPlaceholder($query).'_'.$key; - $stmt->bindValue($placeholder, $value, $this->getPDOType($value)); + if($query->getMethod() !== Query::TYPE_OR){ + /** @var PDOStatement $stmt */ + if ($query->getMethod() === Query::TYPE_SEARCH) return; + foreach ($query->getValues() as $key => $value) { + $placeholder = $this->getSQLPlaceholder($query).'_'.$key; + $stmt->bindValue($placeholder, $value, $this->getPDOType($value)); + } } } } From c309434c9ce94e47646ab4d37f74a9e45a40667e Mon Sep 17 00:00:00 2001 From: fogelito Date: Tue, 7 Feb 2023 14:39:12 +0200 Subject: [PATCH 3/9] Nested conditions --- src/Database/Adapter/MariaDB.php | 55 +------ src/Database/Adapter/Postgres.php | 26 ++-- src/Database/Adapter/SQL.php | 48 +++++- tests/Database/Adapter/MongoDBTest.php | 196 ++++++++++++------------- tests/Database/Base.php | 38 +++-- 5 files changed, 179 insertions(+), 184 deletions(-) diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index 3806dbbf2..c6137e37f 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -653,31 +653,6 @@ public function deleteDocument(string $collection, string $id): bool } - /** - * @throws Exception - */ - public function nested(array $queries = []): string - { - $separator = 'and'; - - $conditions = []; - foreach ($queries as $query) { - /* @var $query Query */ - if($query->getMethod() === Query::TYPE_OR){ - - $separator = 'or'; - - foreach ($query->getValues() as $queriesArray) { - $conditions[] = $this->nested($queriesArray); - } - } - else $conditions[] = $this->getSQLCondition($query); - } - - $tmp = implode(" {$separator} ", $conditions); - return empty($tmp) ? '' : '(' . $tmp . ')'; - } - /** * Find Documents * @@ -767,14 +742,9 @@ public function find(string $collection, array $queries = [], int $limit = 25, i } if(!empty($queries)){ - $where[] = $this->nested($queries); + $where[] = $this->getSQLConditions($queries); } - var_dump($where); -// foreach ($queries as $query) { -// $where[] = $this->getSQLCondition($query); -// } - $order = 'ORDER BY ' . implode(', ', $orders); if (Authorization::$status) { @@ -791,13 +761,8 @@ public function find(string $collection, array $queries = [], int $limit = 25, i {$order} LIMIT :offset, :limit; "; - - var_dump($sql); - $stmt = $this->getPDO()->prepare($sql); - foreach ($queries as $query) { - $this->bindConditionValue($stmt, $query); - } + $this->bindNestedConditionValue($stmt, $queries); if (!empty($cursor) && !empty($orderAttributes) && array_key_exists(0, $orderAttributes)) { $attribute = $orderAttributes[0]; @@ -861,8 +826,8 @@ public function count(string $collection, array $queries = [], int $max = 0): in $where = []; $limit = ($max === 0) ? '' : 'LIMIT :max'; - foreach ($queries as $query) { - $where[] = $this->getSQLCondition($query); + if(!empty($queries)){ + $where[] = $this->getSQLConditions($queries); } if (Authorization::$status) { @@ -880,9 +845,7 @@ public function count(string $collection, array $queries = [], int $max = 0): in ) table_count "; $stmt = $this->getPDO()->prepare($sql); - foreach ($queries as $query) { - $this->bindConditionValue($stmt, $query); - } + $this->bindNestedConditionValue($stmt, $queries); if ($max !== 0) { $stmt->bindValue(':max', $max, PDO::PARAM_INT); @@ -914,8 +877,8 @@ public function sum(string $collection, string $attribute, array $queries = [], $where = []; $limit = ($max === 0) ? '' : 'LIMIT :max'; - foreach ($queries as $query) { - $where[] = $this->getSQLCondition($query); + if(!empty($queries)){ + $where[] = $this->getSQLConditions($queries); } if (Authorization::$status) { @@ -934,9 +897,7 @@ public function sum(string $collection, string $attribute, array $queries = [], ) table_count "); - foreach ($queries as $query) { - $this->bindConditionValue($stmt, $query); - } + $this->bindNestedConditionValue($stmt, $queries); if ($max !== 0) { $stmt->bindValue(':max', $max, PDO::PARAM_INT); diff --git a/src/Database/Adapter/Postgres.php b/src/Database/Adapter/Postgres.php index e080932b1..0d9c39838 100644 --- a/src/Database/Adapter/Postgres.php +++ b/src/Database/Adapter/Postgres.php @@ -749,8 +749,8 @@ public function find(string $collection, array $queries = [], int $limit = 25, i } } - foreach ($queries as $query) { - $where[] = $this->getSQLCondition($query); + if(!empty($queries)){ + $where[] = $this->getSQLConditions($queries); } $order = 'ORDER BY ' . implode(', ', $orders); @@ -770,9 +770,7 @@ public function find(string $collection, array $queries = [], int $limit = 25, i "; $stmt = $this->getPDO()->prepare($sql); - foreach ($queries as $query) { - $this->bindConditionValue($stmt, $query); - } + $this->bindNestedConditionValue($stmt, $queries); if (!empty($cursor) && !empty($orderAttributes) && array_key_exists(0, $orderAttributes)) { $attribute = $orderAttributes[0]; @@ -837,8 +835,8 @@ public function count(string $collection, array $queries = [], int $max = 0): in $where = []; $limit = ($max === 0) ? '' : 'LIMIT :max'; - foreach ($queries as $query) { - $where[] = $this->getSQLCondition($query); + if(!empty($queries)){ + $where[] = $this->getSQLConditions($queries); } if (Authorization::$status) { @@ -856,9 +854,7 @@ public function count(string $collection, array $queries = [], int $max = 0): in ) table_count "; $stmt = $this->getPDO()->prepare($sql); - foreach ($queries as $query) { - $this->bindConditionValue($stmt, $query); - } + $this->bindNestedConditionValue($stmt, $queries); if ($max !== 0) { $stmt->bindValue(':max', $max, PDO::PARAM_INT); @@ -893,8 +889,8 @@ public function sum(string $collection, string $attribute, array $queries = [], $permissions = (Authorization::$status) ? $this->getSQLPermissionsCondition($collection, $roles) : '1=1'; // Disable join when no authorization required - foreach ($queries as $query) { - $where[] = $this->getSQLCondition($query); + if(!empty($queries)){ + $where[] = $this->getSQLConditions($queries); } if (Authorization::$status) { @@ -909,9 +905,7 @@ public function sum(string $collection, string $attribute, array $queries = [], {$limit} ) table_count"); - foreach ($queries as $query) { - $this->bindConditionValue($stmt, $query); - } + $this->bindNestedConditionValue($stmt, $queries); if ($max !== 0) { $stmt->bindValue(':max', $max, PDO::PARAM_INT); @@ -1125,7 +1119,7 @@ protected function getSQLCondition(Query $query): string default: $conditions = []; foreach ($query->getValues() as $key => $value) { - $conditions[] = $attribute.' '.$this->getSQLOperator($query->getMethod()).':'.$placeholder.'_'.$key; + $conditions[] = $attribute.' '.$this->getSQLOperator($query->getMethod()).' :'.$placeholder.'_'.$key; } $condition = implode(' OR ', $conditions); return empty($condition) ? '' : '(' . $condition . ')'; diff --git a/src/Database/Adapter/SQL.php b/src/Database/Adapter/SQL.php index 666d23192..87de0389c 100644 --- a/src/Database/Adapter/SQL.php +++ b/src/Database/Adapter/SQL.php @@ -818,17 +818,49 @@ protected function getSQLPlaceholder(Query $query): string /** * @param $stmt - * @param Query $query + * @param Query[] $queries * @return void */ - public function bindConditionValue($stmt, Query $query){ - if($query->getMethod() !== Query::TYPE_OR){ - /** @var PDOStatement $stmt */ - if ($query->getMethod() === Query::TYPE_SEARCH) return; - foreach ($query->getValues() as $key => $value) { - $placeholder = $this->getSQLPlaceholder($query).'_'.$key; - $stmt->bindValue($placeholder, $value, $this->getPDOType($value)); + public function bindNestedConditionValue($stmt, array $queries = []){ + /** @var PDOStatement $stmt */ + foreach ($queries as $query) { + if(is_array($query)){ + $this->bindNestedConditionValue($stmt, $query); + } + else { + if ($query->getMethod() === Query::TYPE_SEARCH) continue; + if ($query->getMethod() === Query::TYPE_OR){ + $this->bindNestedConditionValue($stmt, $query->getValues()); // Nested $queries are in values + }else { + foreach ($query->getValues() as $key => $value) { + $placeholder = $this->getSQLPlaceholder($query).'_'.$key; + $stmt->bindValue($placeholder, $value, $this->getPDOType($value)); + } + } } } } + + /** + * @throws Exception + */ + public function getSQLConditions(array $queries = []): string + { + $separator = 'and'; + $conditions = []; + foreach ($queries as $query) { + /* @var $query Query */ + if($query->getMethod() === Query::TYPE_OR){ + $separator = 'or'; + foreach ($query->getValues() as $queriesArray) { + $conditions[] = $this->getSQLConditions($queriesArray); + } + } + else $conditions[] = $this->getSQLCondition($query); + } + + $tmp = implode(" {$separator} ", $conditions); + return empty($tmp) ? '' : '(' . $tmp . ')'; + } + } diff --git a/tests/Database/Adapter/MongoDBTest.php b/tests/Database/Adapter/MongoDBTest.php index 03c7f12a2..fa765ba00 100644 --- a/tests/Database/Adapter/MongoDBTest.php +++ b/tests/Database/Adapter/MongoDBTest.php @@ -1,99 +1,99 @@ connect('redis', 6379); - $redis->flushAll(); - $cache = new Cache(new RedisAdapter($redis)); - - $schema = 'utopiaTests'; // same as $this->testDatabase - $client = new Client( - $schema, - 'mongo', - 27017, - 'root', - 'example' - , false - ); - - $database = new Database(new Mongo($client), $cache); - $database->setDefaultDatabase($schema); - $database->setNamespace('myapp_' . uniqid()); - - - return self::$database = $database; - } - - /** - * @throws Exception - */ - public function testCreateExistsDelete() - { - // Mongo creates databases on the fly, so exists would always pass. So we - // overide this test to remove the exists check. - $this->assertNotNull(static::getDatabase()->create($this->testDatabase)); - $this->assertEquals(true, static::getDatabase()->delete($this->testDatabase)); - $this->assertEquals(true, static::getDatabase()->create($this->testDatabase)); - $this->assertEquals(true, static::getDatabase()->setDefaultDatabase($this->testDatabase)); - } - - public function testRenameAttribute() - { - $this->assertTrue(true); - } - - public function testRenameAttributeExisting() - { - $this->assertTrue(true); - } - - public function testUpdateAttributeStructure() - { - $this->assertTrue(true); - } - - public function testKeywords() - { - $this->assertTrue(true); - } -} +// +//namespace Utopia\Tests\Adapter; +// +//use Exception; +//use Redis; +//use Utopia\Cache\Cache; +//use Utopia\Cache\Adapter\Redis as RedisAdapter; +//use Utopia\Database\Adapter\Mongo; +//use Utopia\Database\Database; +//use Utopia\Mongo\Client; +//use Utopia\Tests\Base; +// +//class MongoDBTest extends Base +//{ +// static $pool = null; +// +// /** +// * @var Database +// */ +// static $database = null; +// +// +// /** +// * Return name of adapter +// * +// * @return string +// */ +// static function getAdapterName(): string +// { +// return "mongodb"; +// } +// +// /** +// * @return Database +// * @throws Exception +// */ +// static function getDatabase(): Database +// { +// if (!is_null(self::$database)) { +// return self::$database; +// } +// +// $redis = new Redis(); +// $redis->connect('redis', 6379); +// $redis->flushAll(); +// $cache = new Cache(new RedisAdapter($redis)); +// +// $schema = 'utopiaTests'; // same as $this->testDatabase +// $client = new Client( +// $schema, +// 'mongo', +// 27017, +// 'root', +// 'example' +// , false +// ); +// +// $database = new Database(new Mongo($client), $cache); +// $database->setDefaultDatabase($schema); +// $database->setNamespace('myapp_' . uniqid()); +// +// +// return self::$database = $database; +// } +// +// /** +// * @throws Exception +// */ +// public function testCreateExistsDelete() +// { +// // Mongo creates databases on the fly, so exists would always pass. So we +// // overide this test to remove the exists check. +// $this->assertNotNull(static::getDatabase()->create($this->testDatabase)); +// $this->assertEquals(true, static::getDatabase()->delete($this->testDatabase)); +// $this->assertEquals(true, static::getDatabase()->create($this->testDatabase)); +// $this->assertEquals(true, static::getDatabase()->setDefaultDatabase($this->testDatabase)); +// } +// +// public function testRenameAttribute() +// { +// $this->assertTrue(true); +// } +// +// public function testRenameAttributeExisting() +// { +// $this->assertTrue(true); +// } +// +// public function testUpdateAttributeStructure() +// { +// $this->assertTrue(true); +// } +// +// public function testKeywords() +// { +// $this->assertTrue(true); +// } +//} diff --git a/tests/Database/Base.php b/tests/Database/Base.php index be74ee7f2..c852344a5 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -1968,21 +1968,6 @@ public function testFindEdgeCases(Document $document) $this->assertEquals(['any'], $documents[0]->getDelete()); $this->assertEquals($values[0], $documents[0]->getAttribute('value')); - - $documents = static::getDatabase()->find($collection, [ - Query::limit(25), - Query::equal('value', [$value]), - Query::or( - [Query::equal('value', [1,2]), Query::equal('value', [3,4])], - [Query::equal('value', [555]), Query::equal('value', [666])] - ) - ]); - - - exit; - - - /** * Check `equals` query */ @@ -1997,6 +1982,29 @@ public function testFindEdgeCases(Document $document) } } + + public function testFindOr(){ + $documents = static::getDatabase()->find('movies', [ + Query::equal('active', [true]), + Query::or( + [ + Query::equal('year', [2013]), + ], + [ + Query::equal('price', [39.5]), + ], + [ + Query::equal('name', ["Frozen II"]), + Query::equal('year', [2019]) + ] + ), + Query::limit(25) + ]); + + $this->assertEquals(4, count($documents)); + + } + /** * @depends testFind */ From 2d3e8ead29cc55fd29cfe2d64cdc24f1782a3dab Mon Sep 17 00:00:00 2001 From: fogelito Date: Tue, 7 Feb 2023 14:54:00 +0200 Subject: [PATCH 4/9] remove nesting entry --- src/Database/Query.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Database/Query.php b/src/Database/Query.php index 320907ea4..b43b2f4ea 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -664,7 +664,6 @@ public static function groupByType(array $queries): array } return [ - 'nesting' => $filters, 'filters' => $filters, 'limit' => $limit, 'offset' => $offset, From 58fe422d0c0abaeed090b350b92a308d405d63e1 Mon Sep 17 00:00:00 2001 From: fogelito Date: Sun, 15 Oct 2023 20:23:29 +0300 Subject: [PATCH 5/9] Query OR --- phpunit.xml | 2 +- src/Database/Adapter/MariaDB.php | 12 +- src/Database/Adapter/SQL.php | 15 +- src/Database/Query.php | 8 +- src/Database/Validator/IndexedQueries.php | 7 + src/Database/Validator/Queries.php | 3 +- src/Database/Validator/Query/Filter.php | 22 + tests/Database/Adapter/MongoDBTest.php | 194 +- tests/Database/Adapter/MySQLTest.php | 142 +- tests/Database/Adapter/PostgresTest.php | 116 +- tests/Database/Adapter/SQLiteTest.php | 136 +- tests/Database/Base.php | 19548 ++++++++++---------- 12 files changed, 10134 insertions(+), 10071 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index 31b947dd6..3833748e0 100755 --- a/phpunit.xml +++ b/phpunit.xml @@ -7,7 +7,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="false"> + stopOnFailure="true"> ./tests/ diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index 49a6533ce..fe73f0c9d 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -1063,6 +1063,7 @@ public function find(string $collection, array $queries = [], ?int $limit = 25, $sql = $this->setTimeoutForQuery($sql, $timeout ? $timeout : static::$timeout); } + var_dump($sql); $stmt = $this->getPDO()->prepare($sql); foreach ($queries as $query) { $this->bindConditionValue($stmt, $query); @@ -1302,7 +1303,7 @@ protected function getAttributeProjection(array $selections, string $prefix = '' return \implode(', ', $selections); } - /* + /** * Get SQL Condition * * @param Query $query @@ -1323,6 +1324,15 @@ protected function getSQLCondition(Query $query): string $placeholder = $this->getSQLPlaceholder($query); switch ($query->getMethod()) { + case Query::TYPE_OR: + $conditions = []; + /* @var $q Query */ + foreach ($query->getValue() as $q){ + $conditions[] = $this->getSQLCondition($q); + } + + return ' OR ' . implode(' ', $conditions); + case Query::TYPE_SEARCH: return "MATCH(table_main.{$attribute}) AGAINST (:{$placeholder}_0 IN BOOLEAN MODE)"; diff --git a/src/Database/Adapter/SQL.php b/src/Database/Adapter/SQL.php index 72ad43f16..f3c430ed8 100644 --- a/src/Database/Adapter/SQL.php +++ b/src/Database/Adapter/SQL.php @@ -705,6 +705,17 @@ protected function bindConditionValue(mixed $stmt, Query $query): void return; } + if($query->getMethod() === Query::TYPE_OR){ + var_dump($query); + } + + +// var_dump($query); +// if(is_array($query->getValue())){ +// $this->bindConditionValue($stmt, $query); +// return; +// } + foreach ($query->getValues() as $key => $value) { $value = match ($query->getMethod()) { Query::TYPE_STARTS_WITH => $this->escapeWildcards($value) . '%', @@ -950,12 +961,12 @@ public function bindNestedConditionValue($stmt, array $queries = []){ } } } - } + }} /** * @throws Exception */ - public function getSQLConditions2(array $queries = []): string + public function getSQLConditions(array $queries = []): string { $separator = 'and'; $conditions = []; diff --git a/src/Database/Query.php b/src/Database/Query.php index a693356d4..f4601cd2d 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -694,9 +694,13 @@ public static function endsWith(string $attribute, string $value): self return new self(self::TYPE_ENDS_WITH, $attribute, [$value]); } - public static function or(array ...$queries): Query + /** + * @param array $queries + * @return static + */ + public static function or(array $queries): self { - return new self(self::TYPE_OR, '', $queries); + return new self(self::TYPE_OR, '', [$queries]); } /** diff --git a/src/Database/Validator/IndexedQueries.php b/src/Database/Validator/IndexedQueries.php index 24846460e..e1a494114 100644 --- a/src/Database/Validator/IndexedQueries.php +++ b/src/Database/Validator/IndexedQueries.php @@ -67,11 +67,18 @@ public function isValid($value): bool return false; } $queries = []; + foreach ($value as $query) { if (!$query instanceof Query) { $query = Query::parse($query); } + if($query->getMethod() === Query::TYPE_OR){ + if(!self::isValid($query->getValue())){ + return false; + } + } + $queries[] = $query; } diff --git a/src/Database/Validator/Queries.php b/src/Database/Validator/Queries.php index c80a18366..808553fd8 100644 --- a/src/Database/Validator/Queries.php +++ b/src/Database/Validator/Queries.php @@ -82,7 +82,8 @@ public function isValid($value): bool Query::TYPE_BETWEEN, Query::TYPE_STARTS_WITH, Query::TYPE_CONTAINS, - Query::TYPE_ENDS_WITH => Base::METHOD_TYPE_FILTER, + Query::TYPE_ENDS_WITH, + Query::TYPE_OR => Base::METHOD_TYPE_FILTER, default => '', }; diff --git a/src/Database/Validator/Query/Filter.php b/src/Database/Validator/Query/Filter.php index 9eab6b28e..ef4e75814 100644 --- a/src/Database/Validator/Query/Filter.php +++ b/src/Database/Validator/Query/Filter.php @@ -172,6 +172,22 @@ public function isValid($value): bool case Query::TYPE_IS_NOT_NULL: return $this->isValidAttributeAndValues($attribute, $value->getValues()); + case Query::TYPE_OR: +// if (count($value->getValues()) != 2) { +// $this->message = \ucfirst($method) . ' queries require exactly two values.'; +// return false; +// } +// +// if (!is_array($value->getValues()[0]) || !is_array($value->getValues()[1])) { +// $this->message = \ucfirst($method) . ' queries requires arrays'; +// return false; +// } +// +// $queries = array_merge($value->getValues()[0], $value->getValues()[1]); +// var_dump($queries); + return true; + break; + default: return false; } @@ -181,4 +197,10 @@ public function getMethodType(): string { return self::METHOD_TYPE_FILTER; } + + + public function nested($queries): string + { + + } } diff --git a/tests/Database/Adapter/MongoDBTest.php b/tests/Database/Adapter/MongoDBTest.php index 62c48ae37..3cfcd753a 100644 --- a/tests/Database/Adapter/MongoDBTest.php +++ b/tests/Database/Adapter/MongoDBTest.php @@ -1,98 +1,98 @@ connect('redis', 6379); - $redis->flushAll(); - $cache = new Cache(new RedisAdapter($redis)); - - $schema = 'utopiaTests'; // same as $this->testDatabase - $client = new Client( - $schema, - 'mongo', - 27017, - 'root', - 'example', - false - ); - - $database = new Database(new Mongo($client), $cache); - $database->setDefaultDatabase($schema); - $database->setNamespace('myapp_' . uniqid()); - - if ($database->exists('utopiaTests')) { - $database->delete('utopiaTests'); - } - - $database->create(); - - return self::$database = $database; - } - - /** - * @throws Exception - */ - public function testCreateExistsDelete(): void - { - // Mongo creates databases on the fly, so exists would always pass. So we override this test to remove the exists check. - $this->assertNotNull(static::getDatabase()->create()); - $this->assertEquals(true, static::getDatabase()->delete($this->testDatabase)); - $this->assertEquals(true, static::getDatabase()->create()); - $this->assertEquals(true, static::getDatabase()->setDefaultDatabase($this->testDatabase)); - } - - public function testRenameAttribute(): void - { - $this->assertTrue(true); - } - - public function testRenameAttributeExisting(): void - { - $this->assertTrue(true); - } - - public function testUpdateAttributeStructure(): void - { - $this->assertTrue(true); - } - - public function testKeywords(): void - { - $this->assertTrue(true); - } -} +// +//namespace Utopia\Tests\Adapter; +// +//use Exception; +//use Redis; +//use Utopia\Cache\Cache; +//use Utopia\Cache\Adapter\Redis as RedisAdapter; +//use Utopia\Database\Adapter\Mongo; +//use Utopia\Database\Database; +//use Utopia\Mongo\Client; +//use Utopia\Tests\Base; +// +//class MongoDBTest extends Base +//{ +// public static ?Database $database = null; +// +// +// /** +// * Return name of adapter +// * +// * @return string +// */ +// public static function getAdapterName(): string +// { +// return "mongodb"; +// } +// +// /** +// * @return Database +// * @throws Exception +// */ +// public static function getDatabase(): Database +// { +// if (!is_null(self::$database)) { +// return self::$database; +// } +// +// $redis = new Redis(); +// $redis->connect('redis', 6379); +// $redis->flushAll(); +// $cache = new Cache(new RedisAdapter($redis)); +// +// $schema = 'utopiaTests'; // same as $this->testDatabase +// $client = new Client( +// $schema, +// 'mongo', +// 27017, +// 'root', +// 'example', +// false +// ); +// +// $database = new Database(new Mongo($client), $cache); +// $database->setDefaultDatabase($schema); +// $database->setNamespace('myapp_' . uniqid()); +// +// if ($database->exists('utopiaTests')) { +// $database->delete('utopiaTests'); +// } +// +// $database->create(); +// +// return self::$database = $database; +// } +// +// /** +// * @throws Exception +// */ +// public function testCreateExistsDelete(): void +// { +// // Mongo creates databases on the fly, so exists would always pass. So we override this test to remove the exists check. +// $this->assertNotNull(static::getDatabase()->create()); +// $this->assertEquals(true, static::getDatabase()->delete($this->testDatabase)); +// $this->assertEquals(true, static::getDatabase()->create()); +// $this->assertEquals(true, static::getDatabase()->setDefaultDatabase($this->testDatabase)); +// } +// +// public function testRenameAttribute(): void +// { +// $this->assertTrue(true); +// } +// +// public function testRenameAttributeExisting(): void +// { +// $this->assertTrue(true); +// } +// +// public function testUpdateAttributeStructure(): void +// { +// $this->assertTrue(true); +// } +// +// public function testKeywords(): void +// { +// $this->assertTrue(true); +// } +//} diff --git a/tests/Database/Adapter/MySQLTest.php b/tests/Database/Adapter/MySQLTest.php index 0faefca61..e1a181792 100644 --- a/tests/Database/Adapter/MySQLTest.php +++ b/tests/Database/Adapter/MySQLTest.php @@ -1,72 +1,72 @@ connect('redis', 6379); - $redis->flushAll(); - - $cache = new Cache(new RedisAdapter($redis)); - - $database = new Database(new MySQL($pdo), $cache); - $database->setDefaultDatabase('utopiaTests'); - $database->setNamespace('myapp_'.uniqid()); - - if ($database->exists('utopiaTests')) { - $database->delete('utopiaTests'); - } - - $database->create(); - - return self::$database = $database; - } -} +// +//namespace Utopia\Tests\Adapter; +// +//use PDO; +//use Redis; +//use Utopia\Cache\Cache; +//use Utopia\Database\Database; +//use Utopia\Database\Adapter\MySQL; +//use Utopia\Cache\Adapter\Redis as RedisAdapter; +//use Utopia\Tests\Base; +// +//class MySQLTest extends Base +//{ +// public static ?Database $database = null; +// +// // TODO@kodumbeats hacky way to identify adapters for tests +// // Remove once all methods are implemented +// /** +// * Return name of adapter +// * +// * @return string +// */ +// public static function getAdapterName(): string +// { +// return "mysql"; +// } +// +// /** +// * +// * @return int +// */ +// public static function getUsedIndexes(): int +// { +// return MySQL::getCountOfDefaultIndexes(); +// } +// +// /** +// * @return Database +// */ +// public static function getDatabase(): Database +// { +// if (!is_null(self::$database)) { +// return self::$database; +// } +// +// $dbHost = 'mysql'; +// $dbPort = '3307'; +// $dbUser = 'root'; +// $dbPass = 'password'; +// +// $pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, MySQL::getPDOAttributes()); +// +// $redis = new Redis(); +// $redis->connect('redis', 6379); +// $redis->flushAll(); +// +// $cache = new Cache(new RedisAdapter($redis)); +// +// $database = new Database(new MySQL($pdo), $cache); +// $database->setDefaultDatabase('utopiaTests'); +// $database->setNamespace('myapp_'.uniqid()); +// +// if ($database->exists('utopiaTests')) { +// $database->delete('utopiaTests'); +// } +// +// $database->create(); +// +// return self::$database = $database; +// } +//} diff --git a/tests/Database/Adapter/PostgresTest.php b/tests/Database/Adapter/PostgresTest.php index 9965a8684..a4b7b6b9c 100644 --- a/tests/Database/Adapter/PostgresTest.php +++ b/tests/Database/Adapter/PostgresTest.php @@ -1,59 +1,59 @@ connect('redis', 6379); - $redis->flushAll(); - $cache = new Cache(new RedisAdapter($redis)); - - $database = new Database(new Postgres($pdo), $cache); - $database->setDefaultDatabase('utopiaTests'); - $database->setNamespace('myapp_'.uniqid()); - - if ($database->exists('utopiaTests')) { - $database->delete('utopiaTests'); - } - - $database->create(); - - return self::$database = $database; - } -} +// +//namespace Utopia\Tests\Adapter; +// +//use PDO; +//use Redis; +//use Utopia\Database\Database; +//use Utopia\Database\Adapter\Postgres; +//use Utopia\Cache\Cache; +//use Utopia\Cache\Adapter\Redis as RedisAdapter; +//use Utopia\Tests\Base; +// +//class PostgresTest extends Base +//{ +// public static ?Database $database = null; +// +// /** +// * Return name of adapter +// * +// * @return string +// */ +// public static function getAdapterName(): string +// { +// return "postgres"; +// } +// +// /** +// * @reture Adapter +// */ +// public static function getDatabase(): Database +// { +// if (!is_null(self::$database)) { +// return self::$database; +// } +// +// $dbHost = 'postgres'; +// $dbPort = '5432'; +// $dbUser = 'root'; +// $dbPass = 'password'; +// +// $pdo = new PDO("pgsql:host={$dbHost};port={$dbPort};", $dbUser, $dbPass, Postgres::getPDOAttributes()); +// $redis = new Redis(); +// $redis->connect('redis', 6379); +// $redis->flushAll(); +// $cache = new Cache(new RedisAdapter($redis)); +// +// $database = new Database(new Postgres($pdo), $cache); +// $database->setDefaultDatabase('utopiaTests'); +// $database->setNamespace('myapp_'.uniqid()); +// +// if ($database->exists('utopiaTests')) { +// $database->delete('utopiaTests'); +// } +// +// $database->create(); +// +// return self::$database = $database; +// } +//} diff --git a/tests/Database/Adapter/SQLiteTest.php b/tests/Database/Adapter/SQLiteTest.php index 8a8d24199..8f4a176de 100644 --- a/tests/Database/Adapter/SQLiteTest.php +++ b/tests/Database/Adapter/SQLiteTest.php @@ -1,69 +1,69 @@ connect('redis', 6379); - $redis->flushAll(); - - $cache = new Cache(new RedisAdapter($redis)); - - $database = new Database(new SQLite($pdo), $cache); - $database->setDefaultDatabase('utopiaTests'); - $database->setNamespace('myapp_'.uniqid()); - - return self::$database = $database; - } -} +// +//namespace Utopia\Tests\Adapter; +// +//use PDO; +//use Redis; +//use Utopia\Cache\Cache; +//use Utopia\Database\Database; +//use Utopia\Cache\Adapter\Redis as RedisAdapter; +//use Utopia\Database\Adapter\SQLite; +//use Utopia\Tests\Base; +// +//class SQLiteTest extends Base +//{ +// public static ?Database $database = null; +// +// // TODO@kodumbeats hacky way to identify adapters for tests +// // Remove once all methods are implemented +// /** +// * Return name of adapter +// * +// * @return string +// */ +// public static function getAdapterName(): string +// { +// return "sqlite"; +// } +// +// /** +// * +// * @return int +// */ +// public static function getUsedIndexes(): int +// { +// return SQLite::getCountOfDefaultIndexes(); +// } +// +// /** +// * @return Database +// */ +// public static function getDatabase(): Database +// { +// if (!is_null(self::$database)) { +// return self::$database; +// } +// +// $sqliteDir = __DIR__."/database.sql"; +// +// if (file_exists($sqliteDir)) { +// unlink($sqliteDir); +// } +// +// $dsn = $sqliteDir; +// $dsn = 'memory'; // Overwrite for fast tests +// $pdo = new PDO("sqlite:" . $dsn, null, null, SQLite::getPDOAttributes()); +// +// $redis = new Redis(); +// $redis->connect('redis', 6379); +// $redis->flushAll(); +// +// $cache = new Cache(new RedisAdapter($redis)); +// +// $database = new Database(new SQLite($pdo), $cache); +// $database->setDefaultDatabase('utopiaTests'); +// $database->setNamespace('myapp_'.uniqid()); +// +// return self::$database = $database; +// } +//} diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 945738e1d..30c92b2d1 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -2746,9787 +2746,9795 @@ public function testFindEdgeCases(Document $document): void } } - public function testFindOr(){ $documents = static::getDatabase()->find('movies', [ Query::equal('active', [true]), + Query::or([ + Query::equal('name', ['shmuel']), + ]), Query::or( - [ - Query::equal('year', [2013]), - ], - [ - Query::equal('price', [39.5]), - ], [ Query::equal('name', ["Frozen II"]), Query::equal('year', [2019]) ] ), - Query::limit(25) - ]); - - $this->assertEquals(4, count($documents)); - - } - - /** - * @depends testFind - */ - public function testFindOne(): void - { - $document = static::getDatabase()->findOne('movies', [ - Query::offset(2), - Query::orderAsc('name') - ]); - - $this->assertTrue($document instanceof Document); - $this->assertEquals('Frozen', $document->getAttribute('name')); - - $document = static::getDatabase()->findOne('movies', [ - Query::offset(10) - ]); - $this->assertEquals(false, $document); - } - - public function testFindNull(): void - { - $documents = static::getDatabase()->find('movies', [ - Query::isNull('nullable'), - ]); - - $this->assertEquals(5, count($documents)); - } - - public function testFindNotNull(): void - { - $documents = static::getDatabase()->find('movies', [ - Query::isNotNull('nullable'), - ]); - - $this->assertEquals(1, count($documents)); - } - - public function testFindStartsWith(): void - { - $documents = static::getDatabase()->find('movies', [ - Query::startsWith('name', 'Work'), - ]); - - $this->assertEquals(2, count($documents)); - - if ($this->getDatabase()->getAdapter() instanceof SQL) { - $documents = static::getDatabase()->find('movies', [ - Query::startsWith('name', '%ork'), - ]); - } else { - $documents = static::getDatabase()->find('movies', [ - Query::startsWith('name', '.*ork'), - ]); - } - - $this->assertEquals(0, count($documents)); - } - - public function testFindStartsWithWords(): void - { - $documents = static::getDatabase()->find('movies', [ - Query::startsWith('name', 'Work in Progress'), - ]); - - $this->assertEquals(2, count($documents)); - } - - public function testFindEndsWith(): void - { - $documents = static::getDatabase()->find('movies', [ - Query::endsWith('name', 'Marvel'), - ]); - - $this->assertEquals(1, count($documents)); - } - - public function testFindSelect(): void - { - $documents = static::getDatabase()->find('movies', [ - Query::select(['name', 'year']) - ]); - - foreach ($documents as $document) { - $this->assertArrayHasKey('name', $document); - $this->assertArrayHasKey('year', $document); - $this->assertArrayNotHasKey('director', $document); - $this->assertArrayNotHasKey('price', $document); - $this->assertArrayNotHasKey('active', $document); - $this->assertArrayNotHasKey('$id', $document); - $this->assertArrayNotHasKey('$internalId', $document); - $this->assertArrayNotHasKey('$collection', $document); - $this->assertArrayNotHasKey('$createdAt', $document); - $this->assertArrayNotHasKey('$updatedAt', $document); - $this->assertArrayNotHasKey('$permissions', $document); - } - - $documents = static::getDatabase()->find('movies', [ - Query::select(['name', 'year', '$id']) - ]); - - foreach ($documents as $document) { - $this->assertArrayHasKey('name', $document); - $this->assertArrayHasKey('year', $document); - $this->assertArrayNotHasKey('director', $document); - $this->assertArrayNotHasKey('price', $document); - $this->assertArrayNotHasKey('active', $document); - $this->assertArrayHasKey('$id', $document); - $this->assertArrayNotHasKey('$internalId', $document); - $this->assertArrayNotHasKey('$collection', $document); - $this->assertArrayNotHasKey('$createdAt', $document); - $this->assertArrayNotHasKey('$updatedAt', $document); - $this->assertArrayNotHasKey('$permissions', $document); - } - - $documents = static::getDatabase()->find('movies', [ - Query::select(['name', 'year', '$internalId']) - ]); - - foreach ($documents as $document) { - $this->assertArrayHasKey('name', $document); - $this->assertArrayHasKey('year', $document); - $this->assertArrayNotHasKey('director', $document); - $this->assertArrayNotHasKey('price', $document); - $this->assertArrayNotHasKey('active', $document); - $this->assertArrayNotHasKey('$id', $document); - $this->assertArrayHasKey('$internalId', $document); - $this->assertArrayNotHasKey('$collection', $document); - $this->assertArrayNotHasKey('$createdAt', $document); - $this->assertArrayNotHasKey('$updatedAt', $document); - $this->assertArrayNotHasKey('$permissions', $document); - } - - $documents = static::getDatabase()->find('movies', [ - Query::select(['name', 'year', '$collection']) - ]); - - foreach ($documents as $document) { - $this->assertArrayHasKey('name', $document); - $this->assertArrayHasKey('year', $document); - $this->assertArrayNotHasKey('director', $document); - $this->assertArrayNotHasKey('price', $document); - $this->assertArrayNotHasKey('active', $document); - $this->assertArrayNotHasKey('$id', $document); - $this->assertArrayNotHasKey('$internalId', $document); - $this->assertArrayHasKey('$collection', $document); - $this->assertArrayNotHasKey('$createdAt', $document); - $this->assertArrayNotHasKey('$updatedAt', $document); - $this->assertArrayNotHasKey('$permissions', $document); - } - - $documents = static::getDatabase()->find('movies', [ - Query::select(['name', 'year', '$createdAt']) - ]); - - foreach ($documents as $document) { - $this->assertArrayHasKey('name', $document); - $this->assertArrayHasKey('year', $document); - $this->assertArrayNotHasKey('director', $document); - $this->assertArrayNotHasKey('price', $document); - $this->assertArrayNotHasKey('active', $document); - $this->assertArrayNotHasKey('$id', $document); - $this->assertArrayNotHasKey('$internalId', $document); - $this->assertArrayNotHasKey('$collection', $document); - $this->assertArrayHasKey('$createdAt', $document); - $this->assertArrayNotHasKey('$updatedAt', $document); - $this->assertArrayNotHasKey('$permissions', $document); - } - - $documents = static::getDatabase()->find('movies', [ - Query::select(['name', 'year', '$updatedAt']) - ]); - - foreach ($documents as $document) { - $this->assertArrayHasKey('name', $document); - $this->assertArrayHasKey('year', $document); - $this->assertArrayNotHasKey('director', $document); - $this->assertArrayNotHasKey('price', $document); - $this->assertArrayNotHasKey('active', $document); - $this->assertArrayNotHasKey('$id', $document); - $this->assertArrayNotHasKey('$internalId', $document); - $this->assertArrayNotHasKey('$collection', $document); - $this->assertArrayNotHasKey('$createdAt', $document); - $this->assertArrayHasKey('$updatedAt', $document); - $this->assertArrayNotHasKey('$permissions', $document); - } - - $documents = static::getDatabase()->find('movies', [ - Query::select(['name', 'year', '$permissions']) - ]); - - foreach ($documents as $document) { - $this->assertArrayHasKey('name', $document); - $this->assertArrayHasKey('year', $document); - $this->assertArrayNotHasKey('director', $document); - $this->assertArrayNotHasKey('price', $document); - $this->assertArrayNotHasKey('active', $document); - $this->assertArrayNotHasKey('$id', $document); - $this->assertArrayNotHasKey('$internalId', $document); - $this->assertArrayNotHasKey('$collection', $document); - $this->assertArrayNotHasKey('$createdAt', $document); - $this->assertArrayNotHasKey('$updatedAt', $document); - $this->assertArrayHasKey('$permissions', $document); - } - } - - /** - * @depends testFind - */ - public function testCount(): void - { - $count = static::getDatabase()->count('movies'); - $this->assertEquals(6, $count); - $count = static::getDatabase()->count('movies', [Query::equal('year', [2019])]); - $this->assertEquals(2, $count); - $count = static::getDatabase()->count('movies', [Query::equal('with-dash', ['Works'])]); - $this->assertEquals(2, $count); - $count = static::getDatabase()->count('movies', [Query::equal('with-dash', ['Works2', 'Works3'])]); - $this->assertEquals(4, $count); - - Authorization::unsetRole('user:x'); - $count = static::getDatabase()->count('movies'); - $this->assertEquals(5, $count); - - Authorization::disable(); - $count = static::getDatabase()->count('movies'); - $this->assertEquals(6, $count); - Authorization::reset(); - - Authorization::disable(); - $count = static::getDatabase()->count('movies', [], 3); - $this->assertEquals(3, $count); - Authorization::reset(); - - /** - * Test that OR queries are handled correctly - */ - Authorization::disable(); - $count = static::getDatabase()->count('movies', [ - Query::equal('director', ['TBD', 'Joe Johnston']), - Query::equal('year', [2025]), - ]); - $this->assertEquals(1, $count); - Authorization::reset(); - } - - /** - * @depends testFind - */ - public function testSum(): void - { - Authorization::setRole('user:x'); - $sum = static::getDatabase()->sum('movies', 'year', [Query::equal('year', [2019]),]); - $this->assertEquals(2019 + 2019, $sum); - $sum = static::getDatabase()->sum('movies', 'year'); - $this->assertEquals(2013 + 2019 + 2011 + 2019 + 2025 + 2026, $sum); - $sum = static::getDatabase()->sum('movies', 'price', [Query::equal('year', [2019]),]); - $this->assertEquals(round(39.50 + 25.99, 2), round($sum, 2)); - $sum = static::getDatabase()->sum('movies', 'price', [Query::equal('year', [2019]),]); - $this->assertEquals(round(39.50 + 25.99, 2), round($sum, 2)); - - $sum = static::getDatabase()->sum('movies', 'year', [Query::equal('year', [2019])], 1); - $this->assertEquals(2019, $sum); - - Authorization::unsetRole('user:x'); - Authorization::unsetRole('userx'); - $sum = static::getDatabase()->sum('movies', 'year', [Query::equal('year', [2019]),]); - $this->assertEquals(2019 + 2019, $sum); - $sum = static::getDatabase()->sum('movies', 'year'); - $this->assertEquals(2013 + 2019 + 2011 + 2019 + 2025, $sum); - $sum = static::getDatabase()->sum('movies', 'price', [Query::equal('year', [2019]),]); - $this->assertEquals(round(39.50 + 25.99, 2), round($sum, 2)); - $sum = static::getDatabase()->sum('movies', 'price', [Query::equal('year', [2019]),]); - $this->assertEquals(round(39.50 + 25.99, 2), round($sum, 2)); - } - - public function testEncodeDecode(): void - { - $collection = new Document([ - '$collection' => ID::custom(Database::METADATA), - '$id' => ID::custom('users'), - 'name' => 'Users', - 'attributes' => [ - [ - '$id' => ID::custom('name'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 256, - 'signed' => true, - 'required' => false, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('email'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 1024, - 'signed' => true, - 'required' => false, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('status'), - 'type' => Database::VAR_INTEGER, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('password'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('passwordUpdate'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('registration'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'array' => false, - 'filters' => ['datetime'], - ], - [ - '$id' => ID::custom('emailVerification'), - 'type' => Database::VAR_BOOLEAN, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('reset'), - 'type' => Database::VAR_BOOLEAN, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => false, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => ID::custom('prefs'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'array' => false, - 'filters' => ['json'] - ], - [ - '$id' => ID::custom('sessions'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'array' => false, - 'filters' => ['json'], - ], - [ - '$id' => ID::custom('tokens'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'array' => false, - 'filters' => ['json'], - ], - [ - '$id' => ID::custom('memberships'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'array' => false, - 'filters' => ['json'], - ], - [ - '$id' => ID::custom('roles'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 128, - 'signed' => true, - 'required' => false, - 'array' => true, - 'filters' => [], - ], - [ - '$id' => ID::custom('tags'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 128, - 'signed' => true, - 'required' => false, - 'array' => true, - 'filters' => ['json'], - ], - ], - 'indexes' => [ + Query::or( [ - '$id' => ID::custom('_key_email'), - 'type' => Database::INDEX_UNIQUE, - 'attributes' => ['email'], - 'lengths' => [1024], - 'orders' => [Database::ORDER_ASC], + Query::or( + [ + Query::equal('year', [2013]), + ] + ) ] - ], - ]); - - $document = new Document([ - '$id' => ID::custom('608fdbe51361a'), - '$permissions' => [ - Permission::read(Role::any()), - Permission::create(Role::user('608fdbe51361a')), - Permission::update(Role::user('608fdbe51361a')), - Permission::delete(Role::user('608fdbe51361a')), - ], - 'email' => 'test@example.com', - 'emailVerification' => false, - 'status' => 1, - 'password' => 'randomhash', - 'passwordUpdate' => '2000-06-12 14:12:55', - 'registration' => '1975-06-12 14:12:55+01:00', - 'reset' => false, - 'name' => 'My Name', - 'prefs' => new \stdClass(), - 'sessions' => [], - 'tokens' => [], - 'memberships' => [], - 'roles' => [ - 'admin', - 'developer', - 'tester', - ], - 'tags' => [ - ['$id' => '1', 'label' => 'x'], - ['$id' => '2', 'label' => 'y'], - ['$id' => '3', 'label' => 'z'], - ], + ), + Query::limit(25) ]); - $result = static::getDatabase()->encode($collection, $document); - - $this->assertEquals('608fdbe51361a', $result->getAttribute('$id')); - $this->assertContains('read("any")', $result->getAttribute('$permissions')); - $this->assertContains('read("any")', $result->getPermissions()); - $this->assertContains('any', $result->getRead()); - $this->assertContains(Permission::create(Role::user(ID::custom('608fdbe51361a'))), $result->getPermissions()); - $this->assertContains('user:608fdbe51361a', $result->getCreate()); - $this->assertContains('user:608fdbe51361a', $result->getWrite()); - $this->assertEquals('test@example.com', $result->getAttribute('email')); - $this->assertEquals(false, $result->getAttribute('emailVerification')); - $this->assertEquals(1, $result->getAttribute('status')); - $this->assertEquals('randomhash', $result->getAttribute('password')); - $this->assertEquals('2000-06-12 14:12:55.000', $result->getAttribute('passwordUpdate')); - $this->assertEquals('1975-06-12 13:12:55.000', $result->getAttribute('registration')); - $this->assertEquals(false, $result->getAttribute('reset')); - $this->assertEquals('My Name', $result->getAttribute('name')); - $this->assertEquals('{}', $result->getAttribute('prefs')); - $this->assertEquals('[]', $result->getAttribute('sessions')); - $this->assertEquals('[]', $result->getAttribute('tokens')); - $this->assertEquals('[]', $result->getAttribute('memberships')); - $this->assertEquals(['admin', 'developer', 'tester',], $result->getAttribute('roles')); - $this->assertEquals(['{"$id":"1","label":"x"}', '{"$id":"2","label":"y"}', '{"$id":"3","label":"z"}',], $result->getAttribute('tags')); - - $result = static::getDatabase()->decode($collection, $document); - - $this->assertEquals('608fdbe51361a', $result->getAttribute('$id')); - $this->assertContains('read("any")', $result->getAttribute('$permissions')); - $this->assertContains('read("any")', $result->getPermissions()); - $this->assertContains('any', $result->getRead()); - $this->assertContains(Permission::create(Role::user('608fdbe51361a')), $result->getPermissions()); - $this->assertContains('user:608fdbe51361a', $result->getCreate()); - $this->assertContains('user:608fdbe51361a', $result->getWrite()); - $this->assertEquals('test@example.com', $result->getAttribute('email')); - $this->assertEquals(false, $result->getAttribute('emailVerification')); - $this->assertEquals(1, $result->getAttribute('status')); - $this->assertEquals('randomhash', $result->getAttribute('password')); - $this->assertEquals('2000-06-12T14:12:55.000+00:00', $result->getAttribute('passwordUpdate')); - $this->assertEquals('1975-06-12T13:12:55.000+00:00', $result->getAttribute('registration')); - $this->assertEquals(false, $result->getAttribute('reset')); - $this->assertEquals('My Name', $result->getAttribute('name')); - $this->assertEquals([], $result->getAttribute('prefs')); - $this->assertEquals([], $result->getAttribute('sessions')); - $this->assertEquals([], $result->getAttribute('tokens')); - $this->assertEquals([], $result->getAttribute('memberships')); - $this->assertEquals(['admin', 'developer', 'tester',], $result->getAttribute('roles')); - $this->assertEquals([ - new Document(['$id' => '1', 'label' => 'x']), - new Document(['$id' => '2', 'label' => 'y']), - new Document(['$id' => '3', 'label' => 'z']), - ], $result->getAttribute('tags')); - } - - /** - * @depends testCreateDocument - */ - public function testReadPermissionsSuccess(Document $document): Document - { - Authorization::cleanRoles(); - Authorization::setRole(Role::any()->toString()); - - $document = static::getDatabase()->createDocument('documents', new Document([ - '$permissions' => [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'string' => 'text📝', - 'integer' => 5, - 'bigint' => 8589934592, // 2^33 - 'float' => 5.55, - 'boolean' => true, - 'colors' => ['pink', 'green', 'blue'], - ])); - - $this->assertEquals(false, $document->isEmpty()); - - Authorization::cleanRoles(); - - $document = static::getDatabase()->getDocument($document->getCollection(), $document->getId()); - $this->assertEquals(true, $document->isEmpty()); - - Authorization::setRole(Role::any()->toString()); - - return $document; - } - - public function testReadPermissionsFailure(): Document - { - Authorization::cleanRoles(); - Authorization::setRole(Role::any()->toString()); - - $document = static::getDatabase()->createDocument('documents', new Document([ - '$permissions' => [ - Permission::read(Role::user('1')), - Permission::create(Role::user('1')), - Permission::update(Role::user('1')), - Permission::delete(Role::user('1')), - ], - 'string' => 'text📝', - 'integer' => 5, - 'bigint' => 8589934592, // 2^33 - 'float' => 5.55, - 'boolean' => true, - 'colors' => ['pink', 'green', 'blue'], - ])); - - Authorization::cleanRoles(); - - $document = static::getDatabase()->getDocument($document->getCollection(), $document->getId()); - - $this->assertEquals(true, $document->isEmpty()); - - Authorization::setRole(Role::any()->toString()); - - return $document; - } - - /** - * @depends testCreateDocument - */ - public function testWritePermissionsSuccess(Document $document): void - { - Authorization::cleanRoles(); - - $this->expectException(AuthorizationException::class); - static::getDatabase()->createDocument('documents', new Document([ - '$permissions' => [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'string' => 'text📝', - 'integer' => 5, - 'bigint' => 8589934592, // 2^33 - 'float' => 5.55, - 'boolean' => true, - 'colors' => ['pink', 'green', 'blue'], - ])); - } - - /** - * @depends testCreateDocument - */ - public function testWritePermissionsUpdateFailure(Document $document): Document - { - $this->expectException(AuthorizationException::class); - - Authorization::cleanRoles(); - Authorization::setRole(Role::any()->toString()); - - $document = static::getDatabase()->createDocument('documents', new Document([ - '$permissions' => [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'string' => 'text📝', - 'integer' => 5, - 'bigint' => 8589934592, // 2^33 - 'float' => 5.55, - 'boolean' => true, - 'colors' => ['pink', 'green', 'blue'], - ])); - - Authorization::cleanRoles(); - - $document = static::getDatabase()->updateDocument('documents', $document->getId(), new Document([ - '$id' => ID::custom($document->getId()), - '$permissions' => [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'string' => 'text📝', - 'integer' => 6, - 'bigint' => 8589934592, // 2^33 - 'float' => 5.55, - 'boolean' => true, - 'colors' => ['pink', 'green', 'blue'], - ])); - - return $document; - } - - /** - * @depends testCreateDocument - */ - public function testNoChangeUpdateDocumentWithoutPermission(Document $document): Document - { - $document = static::getDatabase()->createDocument('documents', new Document([ - '$id' => ID::unique(), - '$permissions' => [], - 'string' => 'text📝', - 'integer' => 5, - 'bigint' => 8589934592, // 2^33 - 'float' => 5.55, - 'boolean' => true, - 'colors' => ['pink', 'green', 'blue'], - ])); - - $updatedDocument = static::getDatabase()->updateDocument( - 'documents', - $document->getId(), - $document - ); - - // Document should not be updated as there is no change. - // It should also not throw any authorization exception without any permission because of no change. - $this->assertEquals($updatedDocument->getUpdatedAt(), $document->getUpdatedAt()); - - return $document; - } - - public function testNoChangeUpdateDocumentWithRelationWithoutPermission(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - $attribute = new Document([ - '$id' => ID::custom("name"), - 'type' => Database::VAR_STRING, - 'size' => 100, - 'required' => false, - 'default' => null, - 'signed' => false, - 'array' => false, - 'filters' => [], - ]); - - $permissions = [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::delete(Role::any()), - ]; - for ($i=1; $i < 6; $i++) { - static::getDatabase()->createCollection("level{$i}", [$attribute], [], $permissions); - } - - for ($i = 1; $i < 5; $i++) { - $collectionId = $i; - $relatedCollectionId = $i+1; - static::getDatabase()->createRelationship( - collection: "level{$collectionId}", - relatedCollection: "level{$relatedCollectionId}", - type: Database::RELATION_ONE_TO_ONE, - id: "level{$relatedCollectionId}" - ); - } - - // Create document with relationship with nested data - $level1 = static::getDatabase()->createDocument('level1', new Document([ - '$id' => 'level1', - '$permissions' => [], - 'name' => 'Level 1', - 'level2' => [ - '$id' => 'level2', - '$permissions' => [], - 'name' => 'Level 2', - 'level3' => [ - '$id' => 'level3', - '$permissions' => [], - 'name' => 'Level 3', - 'level4' => [ - '$id' => 'level4', - '$permissions' => [], - 'name' => 'Level 4', - 'level5' => [ - '$id' => 'level5', - '$permissions' => [], - 'name' => 'Level 5', - ] - ], - ], - ], - ])); - static::getDatabase()->updateDocument('level1', $level1->getId(), new Document($level1->getArrayCopy())); - $updatedLevel1 = static::getDatabase()->getDocument('level1', $level1->getId()); - $this->assertEquals($level1, $updatedLevel1); - - try { - static::getDatabase()->updateDocument('level1', $level1->getId(), $level1->setAttribute('name', 'haha')); - $this->fail('Failed to throw exception'); - } catch(Exception $e) { - $this->assertInstanceOf(AuthorizationException::class, $e); - } - $level1->setAttribute('name', 'Level 1'); - static::getDatabase()->updateCollection('level3', [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], false); - $level2 = $level1->getAttribute('level2'); - $level3 = $level2->getAttribute('level3'); - - $level3->setAttribute('name', 'updated value'); - $level2->setAttribute('level3', $level3); - $level1->setAttribute('level2', $level2); - - $level1 = static::getDatabase()->updateDocument('level1', $level1->getId(), $level1); - $this->assertEquals('updated value', $level1['level2']['level3']['name']); - - for ($i=1; $i < 6; $i++) { - static::getDatabase()->deleteCollection("level{$i}"); - } - } - - public function testExceptionAttributeLimit(): void - { - if ($this->getDatabase()->getLimitForAttributes() > 0) { - // Load the collection up to the limit - $attributes = []; - for ($i = 0; $i < $this->getDatabase()->getLimitForAttributes(); $i++) { - $attributes[] = new Document([ - '$id' => ID::custom("test{$i}"), - 'type' => Database::VAR_INTEGER, - 'size' => 0, - 'required' => false, - 'default' => null, - 'signed' => true, - 'array' => false, - 'filters' => [], - ]); - } - - static::getDatabase()->createCollection('attributeLimit', $attributes); - - $this->expectException(LimitException::class); - $this->assertEquals(false, static::getDatabase()->createAttribute('attributeLimit', "breaking", Database::VAR_INTEGER, 0, true)); - } - - // Default assertion for other adapters - $this->assertEquals(1, 1); - } - - /** - * @depends testExceptionAttributeLimit - */ - public function testCheckAttributeCountLimit(): void - { - if ($this->getDatabase()->getLimitForAttributes() > 0) { - $collection = static::getDatabase()->getCollection('attributeLimit'); - - // create same attribute in testExceptionAttributeLimit - $attribute = new Document([ - '$id' => ID::custom('breaking'), - 'type' => Database::VAR_INTEGER, - 'size' => 0, - 'required' => true, - 'default' => null, - 'signed' => true, - 'array' => false, - 'filters' => [], - ]); - - $this->expectException(LimitException::class); - $this->assertEquals(false, static::getDatabase()->checkAttribute($collection, $attribute)); - } - - // Default assertion for other adapters - $this->assertEquals(1, 1); - } - - /** - * Using phpunit dataProviders to check that all these combinations of types/sizes throw exceptions - * https://phpunit.de/manual/3.7/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers - * - * @return array> - */ - public function rowWidthExceedsMaximum(): array - { - return [ - // These combinations of attributes gets exactly to the 64k limit - // [$key, $stringSize, $stringCount, $intCount, $floatCount, $boolCount] - // [0, 1024, 15, 0, 731, 3], - // [1, 512, 31, 0, 0, 833], - // [2, 256, 62, 128, 0, 305], - // [3, 128, 125, 30, 24, 2], - // - // Taken 500 bytes off for tests - [0, 1024, 15, 0, 304, 3], - [1, 512, 31, 0, 0, 333], - [2, 256, 62, 103, 0, 5], - [3, 128, 124, 30, 12, 14], - ]; - } - - /** - * @dataProvider rowWidthExceedsMaximum - */ - public function testExceptionWidthLimit(int $key, int $stringSize, int $stringCount, int $intCount, int $floatCount, int $boolCount): void - { - if (static::getDatabase()->getAdapter()::getDocumentSizeLimit() > 0) { - $attributes = []; - - // Load the collection up to the limit - // Strings - for ($i = 0; $i < $stringCount; $i++) { - $attributes[] = new Document([ - '$id' => ID::custom("test_string{$i}"), - 'type' => Database::VAR_STRING, - 'size' => $stringSize, - 'required' => false, - 'default' => null, - 'signed' => true, - 'array' => false, - 'filters' => [], - ]); - } - - // Integers - for ($i = 0; $i < $intCount; $i++) { - $attributes[] = new Document([ - '$id' => ID::custom("test_int{$i}"), - 'type' => Database::VAR_INTEGER, - 'size' => 0, - 'required' => false, - 'default' => null, - 'signed' => true, - 'array' => false, - 'filters' => [], - ]); - } - - // Floats - for ($i = 0; $i < $floatCount; $i++) { - $attributes[] = new Document([ - '$id' => ID::custom("test_float{$i}"), - 'type' => Database::VAR_FLOAT, - 'size' => 0, - 'required' => false, - 'default' => null, - 'signed' => true, - 'array' => false, - 'filters' => [], - ]); - } - - // Booleans - for ($i = 0; $i < $boolCount; $i++) { - $attributes[] = new Document([ - '$id' => ID::custom("test_bool{$i}"), - 'type' => Database::VAR_BOOLEAN, - 'size' => 0, - 'required' => false, - 'default' => null, - 'signed' => true, - 'array' => false, - 'filters' => [], - ]); - } - - $collection = static::getDatabase()->createCollection("widthLimit{$key}", $attributes); - - $this->expectException(LimitException::class); - $this->assertEquals(false, static::getDatabase()->createAttribute("widthLimit{$key}", "breaking", Database::VAR_STRING, 100, true)); - } - - // Default assertion for other adapters - $this->assertEquals(1, 1); - } - - /** - * @dataProvider rowWidthExceedsMaximum - * @depends testExceptionWidthLimit - */ - public function testCheckAttributeWidthLimit(int $key, int $stringSize, int $stringCount, int $intCount, int $floatCount, int $boolCount): void - { - if (static::getDatabase()->getAdapter()::getDocumentSizeLimit()> 0) { - $collection = static::getDatabase()->getCollection("widthLimit{$key}"); - - // create same attribute in testExceptionWidthLimit - $attribute = new Document([ - '$id' => ID::custom('breaking'), - 'type' => Database::VAR_STRING, - 'size' => 100, - 'required' => true, - 'default' => null, - 'signed' => true, - 'array' => false, - 'filters' => [], - ]); - - $this->expectException(LimitException::class); - $this->assertEquals(false, static::getDatabase()->checkAttribute($collection, $attribute)); - } - - // Default assertion for other adapters - $this->assertEquals(1, 1); - } - - public function testExceptionIndexLimit(): void - { - static::getDatabase()->createCollection('indexLimit'); - - // add unique attributes for indexing - for ($i = 0; $i < 64; $i++) { - $this->assertEquals(true, static::getDatabase()->createAttribute('indexLimit', "test{$i}", Database::VAR_STRING, 16, true)); - } - - // Testing for indexLimit - // Add up to the limit, then check if the next index throws IndexLimitException - for ($i = 0; $i < ($this->getDatabase()->getLimitForIndexes()); $i++) { - $this->assertEquals(true, static::getDatabase()->createIndex('indexLimit', "index{$i}", Database::INDEX_KEY, ["test{$i}"], [16])); - } - $this->expectException(LimitException::class); - $this->assertEquals(false, static::getDatabase()->createIndex('indexLimit', "index64", Database::INDEX_KEY, ["test64"], [16])); - - static::getDatabase()->deleteCollection('indexLimit'); - } - - /** - * @depends testGetDocument - */ - public function testExceptionDuplicate(Document $document): void - { - $document->setAttribute('$id', 'duplicated'); - static::getDatabase()->createDocument($document->getCollection(), $document); - - $this->expectException(DuplicateException::class); - static::getDatabase()->createDocument($document->getCollection(), $document); - } - - /** - * @depends testGetDocument - */ - public function testExceptionCaseInsensitiveDuplicate(Document $document): Document - { - $document->setAttribute('$id', 'caseSensitive'); - $document->setAttribute('$internalId', '200'); - static::getDatabase()->createDocument($document->getCollection(), $document); - - $document->setAttribute('$id', 'CaseSensitive'); - - $this->expectException(DuplicateException::class); - static::getDatabase()->createDocument($document->getCollection(), $document); - - return $document; - } - - /** - * @depends testFind - */ - public function testUniqueIndexDuplicate(): void - { - $this->expectException(DuplicateException::class); - - $this->assertEquals(true, static::getDatabase()->createIndex('movies', 'uniqueIndex', Database::INDEX_UNIQUE, ['name'], [128], [Database::ORDER_ASC])); - - static::getDatabase()->createDocument('movies', new Document([ - '$permissions' => [ - Permission::read(Role::any()), - Permission::read(Role::user('1')), - Permission::read(Role::user('2')), - Permission::create(Role::any()), - Permission::create(Role::user('1x')), - Permission::create(Role::user('2x')), - Permission::update(Role::any()), - Permission::update(Role::user('1x')), - Permission::update(Role::user('2x')), - Permission::delete(Role::any()), - Permission::delete(Role::user('1x')), - Permission::delete(Role::user('2x')), - ], - 'name' => 'Frozen', - 'director' => 'Chris Buck & Jennifer Lee', - 'year' => 2013, - 'price' => 39.50, - 'active' => true, - 'generes' => ['animation', 'kids'], - 'with-dash' => 'Works4' - ])); - } - - /** - * @depends testUniqueIndexDuplicate - */ - public function testUniqueIndexDuplicateUpdate(): void - { - Authorization::setRole(Role::users()->toString()); - // create document then update to conflict with index - $document = static::getDatabase()->createDocument('movies', new Document([ - '$permissions' => [ - Permission::read(Role::any()), - Permission::read(Role::user('1')), - Permission::read(Role::user('2')), - Permission::create(Role::any()), - Permission::create(Role::user('1x')), - Permission::create(Role::user('2x')), - Permission::update(Role::any()), - Permission::update(Role::user('1x')), - Permission::update(Role::user('2x')), - Permission::delete(Role::any()), - Permission::delete(Role::user('1x')), - Permission::delete(Role::user('2x')), - ], - 'name' => 'Frozen 5', - 'director' => 'Chris Buck & Jennifer Lee', - 'year' => 2013, - 'price' => 39.50, - 'active' => true, - 'generes' => ['animation', 'kids'], - 'with-dash' => 'Works4' - ])); - - $this->expectException(DuplicateException::class); - - static::getDatabase()->updateDocument('movies', $document->getId(), $document->setAttribute('name', 'Frozen')); - } - - public function testGetAttributeLimit(): void - { - $this->assertIsInt($this->getDatabase()->getLimitForAttributes()); - } - - public function testGetIndexLimit(): void - { - $this->assertEquals(59, $this->getDatabase()->getLimitForIndexes()); - } - - public function testGetId(): void - { - $this->assertEquals(20, strlen(ID::unique())); - $this->assertEquals(13, strlen(ID::unique(0))); - $this->assertEquals(13, strlen(ID::unique(-1))); - $this->assertEquals(23, strlen(ID::unique(10))); - - // ensure two sequential calls to getId do not give the same result - $this->assertNotEquals(ID::unique(10), ID::unique(10)); - } - - public function testRenameIndex(): void - { - $database = static::getDatabase(); - - $numbers = $database->createCollection('numbers'); - $database->createAttribute('numbers', 'verbose', Database::VAR_STRING, 128, true); - $database->createAttribute('numbers', 'symbol', Database::VAR_INTEGER, 0, true); - - $database->createIndex('numbers', 'index1', Database::INDEX_KEY, ['verbose'], [128], [Database::ORDER_ASC]); - $database->createIndex('numbers', 'index2', Database::INDEX_KEY, ['symbol'], [0], [Database::ORDER_ASC]); - - $index = $database->renameIndex('numbers', 'index1', 'index3'); - - $this->assertTrue($index); - - $numbers = $database->getCollection('numbers'); - - $this->assertEquals('index2', $numbers->getAttribute('indexes')[1]['$id']); - $this->assertEquals('index3', $numbers->getAttribute('indexes')[0]['$id']); - $this->assertCount(2, $numbers->getAttribute('indexes')); - } - - /** - * @depends testRenameIndex - * @expectedException Exception - */ - public function testRenameIndexMissing(): void - { - $database = static::getDatabase(); - $this->expectExceptionMessage('Index not found'); - $index = $database->renameIndex('numbers', 'index1', 'index4'); - } - - /** - * @depends testRenameIndex - * @expectedException Exception - */ - public function testRenameIndexExisting(): void - { - $database = static::getDatabase(); - $this->expectExceptionMessage('Index name already used'); - $index = $database->renameIndex('numbers', 'index3', 'index2'); - } - - public function testRenameAttribute(): void - { - $database = static::getDatabase(); - - $colors = $database->createCollection('colors'); - $database->createAttribute('colors', 'name', Database::VAR_STRING, 128, true); - $database->createAttribute('colors', 'hex', Database::VAR_STRING, 128, true); - - $database->createIndex('colors', 'index1', Database::INDEX_KEY, ['name'], [128], [Database::ORDER_ASC]); - - $database->createDocument('colors', new Document([ - '$permissions' => [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'black', - 'hex' => '#000000' - ])); - - $attribute = $database->renameAttribute('colors', 'name', 'verbose'); - - $this->assertTrue($attribute); - - $colors = $database->getCollection('colors'); - $this->assertEquals('hex', $colors->getAttribute('attributes')[1]['$id']); - $this->assertEquals('verbose', $colors->getAttribute('attributes')[0]['$id']); - $this->assertCount(2, $colors->getAttribute('attributes')); - - // Attribute in index is renamed automatically on adapter-level. What we need to check is if metadata is properly updated - $this->assertEquals('verbose', $colors->getAttribute('indexes')[0]->getAttribute("attributes")[0]); - $this->assertCount(1, $colors->getAttribute('indexes')); - - // Document should be there if adapter migrated properly - $document = $database->findOne('colors'); - $this->assertTrue($document instanceof Document); - $this->assertEquals('black', $document->getAttribute('verbose')); - $this->assertEquals('#000000', $document->getAttribute('hex')); - $this->assertEquals(null, $document->getAttribute('name')); - } - - /** - * @depends testRenameAttribute - * @expectedException Exception - */ - public function textRenameAttributeMissing(): void - { - $database = static::getDatabase(); - $this->expectExceptionMessage('Attribute not found'); - $database->renameAttribute('colors', 'name2', 'name3'); - } - - /** - * @depends testRenameAttribute - * @expectedException Exception - */ - public function testRenameAttributeExisting(): void - { - $database = static::getDatabase(); - $this->expectExceptionMessage('Attribute name already used'); - $database->renameAttribute('colors', 'verbose', 'hex'); - } - - public function testUpdateAttributeDefault(): void - { - $database = static::getDatabase(); - - $flowers = $database->createCollection('flowers'); - $database->createAttribute('flowers', 'name', Database::VAR_STRING, 128, true); - $database->createAttribute('flowers', 'inStock', Database::VAR_INTEGER, 0, false); - $database->createAttribute('flowers', 'date', Database::VAR_STRING, 128, false); - - $database->createDocument('flowers', new Document([ - '$id' => 'flowerWithDate', - '$permissions' => [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Violet', - 'inStock' => 51, - 'date' => '2000-06-12 14:12:55.000' - ])); - - $doc = $database->createDocument('flowers', new Document([ - '$permissions' => [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Lily' - ])); - - $this->assertNull($doc->getAttribute('inStock')); - - $database->updateAttributeDefault('flowers', 'inStock', 100); - - $doc = $database->createDocument('flowers', new Document([ - '$permissions' => [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Iris' - ])); - - $this->assertIsNumeric($doc->getAttribute('inStock')); - $this->assertEquals(100, $doc->getAttribute('inStock')); - - $database->updateAttributeDefault('flowers', 'inStock', null); - } - - /** - * @depends testUpdateAttributeDefault - */ - public function testUpdateAttributeRequired(): void - { - $database = static::getDatabase(); - - $database->updateAttributeRequired('flowers', 'inStock', true); - - $this->expectExceptionMessage('Invalid document structure: Missing required attribute "inStock"'); - - $doc = $database->createDocument('flowers', new Document([ - '$permissions' => [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Lily With Missing Stocks' - ])); - } - - /** - * @depends testUpdateAttributeDefault - */ - public function testUpdateAttributeFilter(): void - { - $database = static::getDatabase(); - - $database->createAttribute('flowers', 'cartModel', Database::VAR_STRING, 2000, false); - - $doc = $database->createDocument('flowers', new Document([ - '$permissions' => [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Lily With CartData', - 'inStock' => 50, - 'cartModel' => '{"color":"string","size":"number"}' - ])); - - $this->assertIsString($doc->getAttribute('cartModel')); - $this->assertEquals('{"color":"string","size":"number"}', $doc->getAttribute('cartModel')); - - $database->updateAttributeFilters('flowers', 'cartModel', ['json']); - - $doc = $database->getDocument('flowers', $doc->getId()); - $this->assertIsArray($doc->getAttribute('cartModel')); - $this->assertCount(2, $doc->getAttribute('cartModel')); - $this->assertEquals('string', $doc->getAttribute('cartModel')['color']); - $this->assertEquals('number', $doc->getAttribute('cartModel')['size']); - } - - /** - * @depends testUpdateAttributeDefault - */ - public function testUpdateAttributeFormat(): void - { - $database = static::getDatabase(); - - $database->createAttribute('flowers', 'price', Database::VAR_INTEGER, 0, false); - - $doc = $database->createDocument('flowers', new Document([ - '$permissions' => [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - '$id' => ID::custom('LiliPriced'), - 'name' => 'Lily Priced', - 'inStock' => 50, - 'cartModel' => '{}', - 'price' => 500 - ])); - - $this->assertIsNumeric($doc->getAttribute('price')); - $this->assertEquals(500, $doc->getAttribute('price')); - - Structure::addFormat('priceRange', function ($attribute) { - $min = $attribute['formatOptions']['min']; - $max = $attribute['formatOptions']['max']; - - return new Range($min, $max); - }, Database::VAR_INTEGER); - - $database->updateAttributeFormat('flowers', 'price', 'priceRange'); - $database->updateAttributeFormatOptions('flowers', 'price', ['min' => 1, 'max' => 10000]); - - $this->expectExceptionMessage('Invalid document structure: Attribute "price" has invalid format. Value must be a valid range between 1 and 10,000'); - - $doc = $database->createDocument('flowers', new Document([ - '$permissions' => [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Lily Overpriced', - 'inStock' => 50, - 'cartModel' => '{}', - 'price' => 15000 - ])); - } - - /** - * @depends testUpdateAttributeDefault - * @depends testUpdateAttributeFormat - */ - public function testUpdateAttributeStructure(): void - { - // TODO: When this becomes relevant, add many more tests (from all types to all types, chaging size up&down, switchign between array/non-array... - - Structure::addFormat('priceRangeNew', function ($attribute) { - $min = $attribute['formatOptions']['min']; - $max = $attribute['formatOptions']['max']; - return new Range($min, $max); - }, Database::VAR_INTEGER); - - $database = static::getDatabase(); - - // price attribute - $collection = $database->getCollection('flowers'); - $attribute = $collection->getAttribute('attributes')[4]; - $this->assertEquals(true, $attribute['signed']); - $this->assertEquals(0, $attribute['size']); - $this->assertEquals(null, $attribute['default']); - $this->assertEquals(false, $attribute['array']); - $this->assertEquals(false, $attribute['required']); - $this->assertEquals('priceRange', $attribute['format']); - $this->assertEquals(['min'=>1, 'max'=>10000], $attribute['formatOptions']); - - $database->updateAttribute('flowers', 'price', default: 100); - $collection = $database->getCollection('flowers'); - $attribute = $collection->getAttribute('attributes')[4]; - $this->assertEquals('integer', $attribute['type']); - $this->assertEquals(true, $attribute['signed']); - $this->assertEquals(0, $attribute['size']); - $this->assertEquals(100, $attribute['default']); - $this->assertEquals(false, $attribute['array']); - $this->assertEquals(false, $attribute['required']); - $this->assertEquals('priceRange', $attribute['format']); - $this->assertEquals(['min'=>1, 'max'=>10000], $attribute['formatOptions']); - - $database->updateAttribute('flowers', 'price', format: 'priceRangeNew'); - $collection = $database->getCollection('flowers'); - $attribute = $collection->getAttribute('attributes')[4]; - $this->assertEquals('integer', $attribute['type']); - $this->assertEquals(true, $attribute['signed']); - $this->assertEquals(0, $attribute['size']); - $this->assertEquals(100, $attribute['default']); - $this->assertEquals(false, $attribute['array']); - $this->assertEquals(false, $attribute['required']); - $this->assertEquals('priceRangeNew', $attribute['format']); - $this->assertEquals(['min'=>1, 'max'=>10000], $attribute['formatOptions']); - - $database->updateAttribute('flowers', 'price', format: ''); - $collection = $database->getCollection('flowers'); - $attribute = $collection->getAttribute('attributes')[4]; - $this->assertEquals('integer', $attribute['type']); - $this->assertEquals(true, $attribute['signed']); - $this->assertEquals(0, $attribute['size']); - $this->assertEquals(100, $attribute['default']); - $this->assertEquals(false, $attribute['array']); - $this->assertEquals(false, $attribute['required']); - $this->assertEquals('', $attribute['format']); - $this->assertEquals(['min'=>1, 'max'=>10000], $attribute['formatOptions']); - - $database->updateAttribute('flowers', 'price', formatOptions: ['min' => 1, 'max' => 999]); - $collection = $database->getCollection('flowers'); - $attribute = $collection->getAttribute('attributes')[4]; - $this->assertEquals('integer', $attribute['type']); - $this->assertEquals(true, $attribute['signed']); - $this->assertEquals(0, $attribute['size']); - $this->assertEquals(100, $attribute['default']); - $this->assertEquals(false, $attribute['array']); - $this->assertEquals(false, $attribute['required']); - $this->assertEquals('', $attribute['format']); - $this->assertEquals(['min'=>1, 'max'=>999], $attribute['formatOptions']); - - $database->updateAttribute('flowers', 'price', formatOptions: []); - $collection = $database->getCollection('flowers'); - $attribute = $collection->getAttribute('attributes')[4]; - $this->assertEquals('integer', $attribute['type']); - $this->assertEquals(true, $attribute['signed']); - $this->assertEquals(0, $attribute['size']); - $this->assertEquals(100, $attribute['default']); - $this->assertEquals(false, $attribute['array']); - $this->assertEquals(false, $attribute['required']); - $this->assertEquals('', $attribute['format']); - $this->assertEquals([], $attribute['formatOptions']); - - $database->updateAttribute('flowers', 'price', signed: false); - $collection = $database->getCollection('flowers'); - $attribute = $collection->getAttribute('attributes')[4]; - $this->assertEquals('integer', $attribute['type']); - $this->assertEquals(false, $attribute['signed']); - $this->assertEquals(0, $attribute['size']); - $this->assertEquals(100, $attribute['default']); - $this->assertEquals(false, $attribute['array']); - $this->assertEquals(false, $attribute['required']); - $this->assertEquals('', $attribute['format']); - $this->assertEquals([], $attribute['formatOptions']); - - $database->updateAttribute('flowers', 'price', required: true); - $collection = $database->getCollection('flowers'); - $attribute = $collection->getAttribute('attributes')[4]; - $this->assertEquals('integer', $attribute['type']); - $this->assertEquals(false, $attribute['signed']); - $this->assertEquals(0, $attribute['size']); - $this->assertEquals(null, $attribute['default']); - $this->assertEquals(false, $attribute['array']); - $this->assertEquals(true, $attribute['required']); - $this->assertEquals('', $attribute['format']); - $this->assertEquals([], $attribute['formatOptions']); - - $database->updateAttribute('flowers', 'price', type: Database::VAR_STRING, size: Database::LENGTH_KEY, format: ''); - $collection = $database->getCollection('flowers'); - $attribute = $collection->getAttribute('attributes')[4]; - $this->assertEquals('string', $attribute['type']); - $this->assertEquals(false, $attribute['signed']); - $this->assertEquals(255, $attribute['size']); - $this->assertEquals(null, $attribute['default']); - $this->assertEquals(false, $attribute['array']); - $this->assertEquals(true, $attribute['required']); - $this->assertEquals('', $attribute['format']); - $this->assertEquals([], $collection->getAttribute('attributes')[4]['formatOptions']); - - // Date attribute - $attribute = $collection->getAttribute('attributes')[2]; - $this->assertEquals('date', $attribute['key']); - $this->assertEquals('string', $attribute['type']); - $this->assertEquals(null, $attribute['default']); - - $database->updateAttribute('flowers', 'date', type: Database::VAR_DATETIME, size: 0, filters: ['datetime']); - $collection = $database->getCollection('flowers'); - $attribute = $collection->getAttribute('attributes')[2]; - $this->assertEquals('datetime', $attribute['type']); - $this->assertEquals(0, $attribute['size']); - $this->assertEquals(null, $attribute['default']); - $this->assertEquals(false, $attribute['required']); - $this->assertEquals(true, $attribute['signed']); - $this->assertEquals(false, $attribute['array']); - $this->assertEquals('', $attribute['format']); - $this->assertEquals([], $attribute['formatOptions']); - - $doc = $database->getDocument('flowers', 'LiliPriced'); - $this->assertIsString($doc->getAttribute('price')); - $this->assertEquals('500', $doc->getAttribute('price')); - - $doc = $database->getDocument('flowers', 'flowerWithDate'); - $this->assertEquals('2000-06-12T14:12:55.000+00:00', $doc->getAttribute('date')); - } - - /** - * @depends testCreatedAtUpdatedAt - */ - public function testCreatedAtUpdatedAtAssert(): void - { - $document = static::getDatabase()->getDocument('created_at', 'uid123'); - $this->assertEquals(true, !$document->isEmpty()); - sleep(1); - $document->setAttribute('title', 'new title'); - static::getDatabase()->updateDocument('created_at', 'uid123', $document); - $document = static::getDatabase()->getDocument('created_at', 'uid123'); - - $this->assertGreaterThan($document->getCreatedAt(), $document->getUpdatedAt()); - $this->expectException(DuplicateException::class); - - static::getDatabase()->createCollection('created_at'); - } - - public function testCreateDatetime(): void - { - static::getDatabase()->createCollection('datetime'); - - $this->assertEquals(true, static::getDatabase()->createAttribute('datetime', 'date', Database::VAR_DATETIME, 0, true, null, true, false, null, [], ['datetime'])); - $this->assertEquals(true, static::getDatabase()->createAttribute('datetime', 'date2', Database::VAR_DATETIME, 0, false, null, true, false, null, [], ['datetime'])); - - $doc = static::getDatabase()->createDocument('datetime', new Document([ - '$id' => ID::custom('id1234'), - '$permissions' => [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'date' => DateTime::now(), - ])); - - $this->assertEquals(29, strlen($doc->getCreatedAt())); - $this->assertEquals(29, strlen($doc->getUpdatedAt())); - $this->assertEquals('+00:00', substr($doc->getCreatedAt(), -6)); - $this->assertEquals('+00:00', substr($doc->getUpdatedAt(), -6)); - $this->assertGreaterThan('2020-08-16T19:30:08.363+00:00', $doc->getCreatedAt()); - $this->assertGreaterThan('2020-08-16T19:30:08.363+00:00', $doc->getUpdatedAt()); - - $document = static::getDatabase()->getDocument('datetime', 'id1234'); - $dateValidator = new DatetimeValidator(); - $this->assertEquals(null, $document->getAttribute('date2')); - $this->assertEquals(true, $dateValidator->isValid($document->getAttribute('date'))); - $this->assertEquals(false, $dateValidator->isValid($document->getAttribute('date2'))); - - $documents = static::getDatabase()->find('datetime', [ - Query::greaterThan('date', '1975-12-06 10:00:00+01:00'), - Query::lessThan('date', '2030-12-06 10:00:00-01:00'), - ]); - - $this->assertEquals(1, count($documents)); - - $this->expectException(StructureException::class); - static::getDatabase()->createDocument('datetime', new Document([ - '$permissions' => [ - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'date' => "1975-12-06 00:00:61" - ])); - } - - public function testCreateDateTimeAttributeFailure(): void - { - static::getDatabase()->createCollection('datetime_fail'); - - /** Test for FAILURE */ - $this->expectException(Exception::class); - static::getDatabase()->createAttribute('datetime_fail', 'date_fail', Database::VAR_DATETIME, 0, false); - } - - public function testKeywords(): void - { - $database = static::getDatabase(); - $keywords = $database->getKeywords(); - - // Collection name tests - $attributes = [ - new Document([ - '$id' => ID::custom('attribute1'), - 'type' => Database::VAR_STRING, - 'size' => 256, - 'required' => false, - 'signed' => true, - 'array' => false, - 'filters' => [], - ]), - ]; - - $indexes = [ - new Document([ - '$id' => ID::custom('index1'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['attribute1'], - 'lengths' => [256], - 'orders' => ['ASC'], - ]), - ]; - - foreach ($keywords as $keyword) { - $collection = $database->createCollection($keyword, $attributes, $indexes); - $this->assertEquals($keyword, $collection->getId()); - - $document = $database->createDocument($keyword, new Document([ - '$permissions' => [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - '$id' => ID::custom('helloWorld'), - 'attribute1' => 'Hello World', - ])); - $this->assertEquals('helloWorld', $document->getId()); - - $document = $database->getDocument($keyword, 'helloWorld'); - $this->assertEquals('helloWorld', $document->getId()); - - $documents = $database->find($keyword); - $this->assertCount(1, $documents); - $this->assertEquals('helloWorld', $documents[0]->getId()); - - $collection = $database->deleteCollection($keyword); - $this->assertTrue($collection); - } - - // TODO: updateCollection name tests - - // Attribute name tests - foreach ($keywords as $keyword) { - $collectionName = 'rk' . $keyword; // rk is short-hand for reserved-keyword. We do this sicne there are some limits (64 chars max) - - $collection = $database->createCollection($collectionName); - $this->assertEquals($collectionName, $collection->getId()); - - $attribute = static::getDatabase()->createAttribute($collectionName, $keyword, Database::VAR_STRING, 128, true); - $this->assertEquals(true, $attribute); - - $document = new Document([ - '$permissions' => [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - '$id' => 'reservedKeyDocument' - ]); - $document->setAttribute($keyword, 'Reserved:' . $keyword); - - $document = $database->createDocument($collectionName, $document); - $this->assertEquals('reservedKeyDocument', $document->getId()); - $this->assertEquals('Reserved:' . $keyword, $document->getAttribute($keyword)); - - $document = $database->getDocument($collectionName, 'reservedKeyDocument'); - $this->assertEquals('reservedKeyDocument', $document->getId()); - $this->assertEquals('Reserved:' . $keyword, $document->getAttribute($keyword)); - - $documents = $database->find($collectionName); - $this->assertCount(1, $documents); - $this->assertEquals('reservedKeyDocument', $documents[0]->getId()); - $this->assertEquals('Reserved:' . $keyword, $documents[0]->getAttribute($keyword)); - - $documents = $database->find($collectionName, [Query::equal($keyword, ["Reserved:${keyword}"])]); - $this->assertCount(1, $documents); - $this->assertEquals('reservedKeyDocument', $documents[0]->getId()); - - $documents = $database->find($collectionName, [ - Query::orderDesc($keyword) - ]); - $this->assertCount(1, $documents); - $this->assertEquals('reservedKeyDocument', $documents[0]->getId()); - - - $collection = $database->deleteCollection($collectionName); - $this->assertTrue($collection); - - // TODO: updateAttribute name tests - } - - // TODO: Index name tests - } - - public function testWritePermissions(): void - { - Authorization::setRole(Role::any()->toString()); - $database = static::getDatabase(); - - $database->createCollection('animals', permissions: [ - Permission::create(Role::any()), - ], documentSecurity: true); - - $database->createAttribute('animals', 'type', Database::VAR_STRING, 128, true); - - $dog = $database->createDocument('animals', new Document([ - '$id' => 'dog', - '$permissions' => [ - Permission::delete(Role::any()), - ], - 'type' => 'Dog' - ])); - - $cat = $database->createDocument('animals', new Document([ - '$id' => 'cat', - '$permissions' => [ - Permission::update(Role::any()), - ], - 'type' => 'Cat' - ])); - - // No read permissions: - - $docs = $database->find('animals'); - $this->assertCount(0, $docs); - - $doc = $database->getDocument('animals', 'dog'); - $this->assertTrue($doc->isEmpty()); - - $doc = $database->getDocument('animals', 'cat'); - $this->assertTrue($doc->isEmpty()); - - // Cannot delete with update permission: - $didFail = false; - - try { - $database->deleteDocument('animals', 'cat'); - } catch (AuthorizationException) { - $didFail = true; - } - - $this->assertTrue($didFail); - - // Cannot update with delete permission: - $didFail = false; - - try { - $newDog = $dog->setAttribute('type', 'newDog'); - $database->updateDocument('animals', 'dog', $newDog); - } catch (AuthorizationException) { - $didFail = true; - } - - $this->assertTrue($didFail); - - // Can delete: - $database->deleteDocument('animals', 'dog'); - - // Can update: - $newCat = $cat->setAttribute('type', 'newCat'); - $database->updateDocument('animals', 'cat', $newCat); - - $docs = Authorization::skip(fn () => $database->find('animals')); - $this->assertCount(1, $docs); - $this->assertEquals('cat', $docs[0]['$id']); - $this->assertEquals('newCat', $docs[0]['type']); - } - - public function testNoInvalidKeysWithRelationships(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - static::getDatabase()->createCollection('species'); - static::getDatabase()->createCollection('creatures'); - static::getDatabase()->createCollection('characterstics'); - - static::getDatabase()->createAttribute('species', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('creatures', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('characterstics', 'name', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'species', - relatedCollection: 'creatures', - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - id: 'creature', - twoWayKey:'species' - ); - static::getDatabase()->createRelationship( - collection: 'creatures', - relatedCollection: 'characterstics', - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - id: 'characterstic', - twoWayKey:'creature' - ); - - $species = static::getDatabase()->createDocument('species', new Document([ - '$id' => ID::custom('1'), - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Canine', - 'creature' => [ - '$id' => ID::custom('1'), - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Dog', - 'characterstic' => [ - '$id' => ID::custom('1'), - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - ], - 'name' => 'active', - ] - ] - ])); - static::getDatabase()->updateDocument('species', $species->getId(), new Document([ - '$id' => ID::custom('1'), - '$collection' => 'species', - 'creature' => [ - '$id' => ID::custom('1'), - '$collection' => 'creatures', - 'characterstic' => [ - '$id' => ID::custom('1'), - 'name' => 'active', - '$collection' => 'characterstics', - ] - ] - ])); - $updatedSpecies = static::getDatabase()->getDocument('species', $species->getId()); - $this->assertEquals($species, $updatedSpecies); - } - - // Relationships - public function testOneToOneOneWayRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('person'); - static::getDatabase()->createCollection('library'); - - static::getDatabase()->createAttribute('person', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('library', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('library', 'area', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'person', - relatedCollection: 'library', - type: Database::RELATION_ONE_TO_ONE - ); - - // Check metadata for collection - $collection = static::getDatabase()->getCollection('person'); - $attributes = $collection->getAttribute('attributes', []); - - foreach ($attributes as $attribute) { - if ($attribute['key'] === 'library') { - $this->assertEquals('relationship', $attribute['type']); - $this->assertEquals('library', $attribute['$id']); - $this->assertEquals('library', $attribute['key']); - $this->assertEquals('library', $attribute['options']['relatedCollection']); - $this->assertEquals(Database::RELATION_ONE_TO_ONE, $attribute['options']['relationType']); - $this->assertEquals(false, $attribute['options']['twoWay']); - $this->assertEquals('person', $attribute['options']['twoWayKey']); - } - } - - try { - static::getDatabase()->deleteAttribute('person', 'library'); - $this->fail('Failed to throw Exception'); - } catch (Exception $e) { - $this->assertEquals('Cannot delete relationship as an attribute', $e->getMessage()); - } - - // Create document with relationship with nested data - $person1 = static::getDatabase()->createDocument('person', new Document([ - '$id' => 'person1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Person 1', - 'library' => [ - '$id' => 'library1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Library 1', - 'area' => 'Area 1', - ], - ])); - - // Update a document with non existing related document. It should not get added to the list. - static::getDatabase()->updateDocument( - 'person', - 'person1', - $person1->setAttribute('library', 'no-library') - ); - - $person1Document = static::getDatabase()->getDocument('person', 'person1'); - // Assert document does not contain non existing relation document. - $this->assertEquals(null, $person1Document->getAttribute('library')); - - static::getDatabase()->updateDocument( - 'person', - 'person1', - $person1->setAttribute('library', 'library1') - ); - - // Update through create - $library10 = static::getDatabase()->createDocument('library', new Document([ - '$id' => 'library10', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - ], - 'name' => 'Library 10', - 'area' => 'Area 10', - ])); - $person10 = static::getDatabase()->createDocument('person', new Document([ - '$id' => 'person10', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Person 10', - 'library' => [ - '$id' => $library10->getId(), - 'name' => 'Library 10 Updated', - 'area' => 'Area 10 Updated', - ], - ])); - $this->assertEquals('Library 10 Updated', $person10->getAttribute('library')->getAttribute('name')); - $library10 = static::getDatabase()->getDocument('library', $library10->getId()); - $this->assertEquals('Library 10 Updated', $library10->getAttribute('name')); - - // Create document with relationship with related ID - static::getDatabase()->createDocument('library', new Document([ - '$id' => 'library2', - '$permissions' => [ - Permission::read(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Library 2', - 'area' => 'Area 2', - ])); - static::getDatabase()->createDocument('person', new Document([ - '$id' => 'person2', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Person 2', - 'library' => 'library2', - ])); - - // Get documents with relationship - $person1 = static::getDatabase()->getDocument('person', 'person1'); - $library = $person1->getAttribute('library'); - $this->assertEquals('library1', $library['$id']); - $this->assertArrayNotHasKey('person', $library); - - $person = static::getDatabase()->getDocument('person', 'person2'); - $library = $person->getAttribute('library'); - $this->assertEquals('library2', $library['$id']); - $this->assertArrayNotHasKey('person', $library); - - // Get related documents - $library = static::getDatabase()->getDocument('library', 'library1'); - $this->assertArrayNotHasKey('person', $library); - - $library = static::getDatabase()->getDocument('library', 'library2'); - $this->assertArrayNotHasKey('person', $library); - - $people = static::getDatabase()->find('person', [ - Query::select(['name']) - ]); - - $this->assertArrayNotHasKey('library', $people[0]); - - $people = static::getDatabase()->find('person'); - $this->assertEquals(3, \count($people)); - - // Select related document attributes - $person = static::getDatabase()->findOne('person', [ - Query::select(['*', 'library.name']) - ]); - - if (!$person instanceof Document) { - throw new Exception('Person not found'); - } - - $this->assertEquals('Library 1', $person->getAttribute('library')->getAttribute('name')); - $this->assertArrayNotHasKey('area', $person->getAttribute('library')); - - $person = static::getDatabase()->getDocument('person', 'person1', [ - Query::select(['*', 'library.name', '$id']) - ]); - - $this->assertEquals('Library 1', $person->getAttribute('library')->getAttribute('name')); - $this->assertArrayNotHasKey('area', $person->getAttribute('library')); - - - - $document = static::getDatabase()->getDocument('person', $person->getId(), [ - Query::select(['name']), - ]); - $this->assertArrayNotHasKey('library', $document); - $this->assertEquals('Person 1', $document['name']); - - $document = static::getDatabase()->getDocument('person', $person->getId(), [ - Query::select(['*']), - ]); - $this->assertEquals('library1', $document['library']); - - $document = static::getDatabase()->getDocument('person', $person->getId(), [ - Query::select(['library.*']), - ]); - $this->assertEquals('Library 1', $document['library']['name']); - $this->assertArrayNotHasKey('name', $document); - - // Update root document attribute without altering relationship - $person1 = static::getDatabase()->updateDocument( - 'person', - $person1->getId(), - $person1->setAttribute('name', 'Person 1 Updated') - ); - - $this->assertEquals('Person 1 Updated', $person1->getAttribute('name')); - $person1 = static::getDatabase()->getDocument('person', 'person1'); - $this->assertEquals('Person 1 Updated', $person1->getAttribute('name')); - - // Update nested document attribute - $person1 = static::getDatabase()->updateDocument( - 'person', - $person1->getId(), - $person1->setAttribute( - 'library', - $person1 - ->getAttribute('library') - ->setAttribute('name', 'Library 1 Updated') - ) - ); - - $this->assertEquals('Library 1 Updated', $person1->getAttribute('library')->getAttribute('name')); - $person1 = static::getDatabase()->getDocument('person', 'person1'); - $this->assertEquals('Library 1 Updated', $person1->getAttribute('library')->getAttribute('name')); - - // Create new document with no relationship - $person3 = static::getDatabase()->createDocument('person', new Document([ - '$id' => 'person3', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Person 3', - ])); - - // Update to relate to created document - $person3 = static::getDatabase()->updateDocument( - 'person', - $person3->getId(), - $person3->setAttribute('library', new Document([ - '$id' => 'library3', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - ], - 'name' => 'Library 3', - 'area' => 'Area 3', - ])) - ); - - $this->assertEquals('library3', $person3->getAttribute('library')['$id']); - $person3 = static::getDatabase()->getDocument('person', 'person3'); - $this->assertEquals('Library 3', $person3['library']['name']); - - $libraryDocument = static::getDatabase()->getDocument('library', 'library3'); - $libraryDocument->setAttribute('name', 'Library 3 updated'); - static::getDatabase()->updateDocument('library', 'library3', $libraryDocument); - $libraryDocument = static::getDatabase()->getDocument('library', 'library3'); - $this->assertEquals('Library 3 updated', $libraryDocument['name']); - - $person3 = static::getDatabase()->getDocument('person', 'person3'); - // Todo: This is failing - $this->assertEquals($libraryDocument['name'], $person3['library']['name']); - $this->assertEquals('library3', $person3->getAttribute('library')['$id']); - - // One to one can't relate to multiple documents, unique index throws duplicate - try { - static::getDatabase()->updateDocument( - 'person', - $person1->getId(), - $person1->setAttribute('library', 'library2') - ); - $this->fail('Failed to throw duplicate exception'); - } catch (Exception $e) { - $this->assertInstanceOf(DuplicateException::class, $e); - } - - // Create new document - $library4 = static::getDatabase()->createDocument('library', new Document([ - '$id' => 'library4', - '$permissions' => [ - Permission::read(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Library 4', - 'area' => 'Area 4', - ])); - - // Relate existing document to new document - static::getDatabase()->updateDocument( - 'person', - $person1->getId(), - $person1->setAttribute('library', 'library4') - ); - - // Relate existing document to new document as nested data - static::getDatabase()->updateDocument( - 'person', - $person1->getId(), - $person1->setAttribute('library', $library4) - ); - - // Rename relationship key - static::getDatabase()->updateRelationship( - collection: 'person', - id: 'library', - newKey: 'newLibrary' - ); - - // Get document with again - $person = static::getDatabase()->getDocument('person', 'person1'); - $library = $person->getAttribute('newLibrary'); - $this->assertEquals('library4', $library['$id']); - - // Create person with no relationship - static::getDatabase()->createDocument('person', new Document([ - '$id' => 'person4', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Person 4', - ])); - - // Can delete parent document with no relation with on delete set to restrict - $deleted = static::getDatabase()->deleteDocument('person', 'person4'); - $this->assertEquals(true, $deleted); - - $person4 = static::getDatabase()->getDocument('person', 'person4'); - $this->assertEquals(true, $person4->isEmpty()); - - // Cannot delete document while still related to another with on delete set to restrict - try { - static::getDatabase()->deleteDocument('person', 'person1'); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); - } - - // Can delete child document while still related to another with on delete set to restrict - $person5 = static::getDatabase()->createDocument('person', new Document([ - '$id' => 'person5', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Person 5', - 'newLibrary' => [ - '$id' => 'library5', - '$permissions' => [ - Permission::read(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Library 5', - 'area' => 'Area 5', - ], - ])); - $deleted = static::getDatabase()->deleteDocument('library', 'library5'); - $this->assertEquals(true, $deleted); - $person5 = static::getDatabase()->getDocument('person', 'person5'); - $this->assertEquals(null, $person5->getAttribute('newLibrary')); - - // Change on delete to set null - static::getDatabase()->updateRelationship( - collection: 'person', - id: 'newLibrary', - onDelete: Database::RELATION_MUTATE_SET_NULL - ); - - // Delete parent, no effect on children for one-way - static::getDatabase()->deleteDocument('person', 'person1'); - - // Delete child, set parent relating attribute to null for one-way - static::getDatabase()->deleteDocument('library', 'library2'); - - // Check relation was set to null - $person2 = static::getDatabase()->getDocument('person', 'person2'); - $this->assertEquals(null, $person2->getAttribute('newLibrary', '')); - - // Relate to another document - static::getDatabase()->updateDocument( - 'person', - $person2->getId(), - $person2->setAttribute('newLibrary', 'library4') - ); - - // Change on delete to cascade - static::getDatabase()->updateRelationship( - collection: 'person', - id: 'newLibrary', - onDelete: Database::RELATION_MUTATE_CASCADE - ); - - // Delete parent, will delete child - static::getDatabase()->deleteDocument('person', 'person2'); - - // Check parent and child were deleted - $person = static::getDatabase()->getDocument('person', 'person2'); - $this->assertEquals(true, $person->isEmpty()); - - $library = static::getDatabase()->getDocument('library', 'library4'); - $this->assertEquals(true, $library->isEmpty()); - - // Delete relationship - static::getDatabase()->deleteRelationship( - 'person', - 'newLibrary' - ); - - // Check parent doesn't have relationship anymore - $person = static::getDatabase()->getDocument('person', 'person1'); - $library = $person->getAttribute('newLibrary', ''); - $this->assertEquals(null, $library); - } - - /** - * @throws AuthorizationException - * @throws LimitException - * @throws DuplicateException - * @throws StructureException - * @throws Throwable - */ - public function testOneToOneTwoWayRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('country'); - static::getDatabase()->createCollection('city'); - - static::getDatabase()->createAttribute('country', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('city', 'code', Database::VAR_STRING, 3, true); - static::getDatabase()->createAttribute('city', 'name', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'country', - relatedCollection: 'city', - type: Database::RELATION_ONE_TO_ONE, - twoWay: true - ); - - $collection = static::getDatabase()->getCollection('country'); - $attributes = $collection->getAttribute('attributes', []); - foreach ($attributes as $attribute) { - if ($attribute['key'] === 'city') { - $this->assertEquals('relationship', $attribute['type']); - $this->assertEquals('city', $attribute['$id']); - $this->assertEquals('city', $attribute['key']); - $this->assertEquals('city', $attribute['options']['relatedCollection']); - $this->assertEquals(Database::RELATION_ONE_TO_ONE, $attribute['options']['relationType']); - $this->assertEquals(true, $attribute['options']['twoWay']); - $this->assertEquals('country', $attribute['options']['twoWayKey']); - } - } - - $collection = static::getDatabase()->getCollection('city'); - $attributes = $collection->getAttribute('attributes', []); - foreach ($attributes as $attribute) { - if ($attribute['key'] === 'country') { - $this->assertEquals('relationship', $attribute['type']); - $this->assertEquals('country', $attribute['$id']); - $this->assertEquals('country', $attribute['key']); - $this->assertEquals('country', $attribute['options']['relatedCollection']); - $this->assertEquals(Database::RELATION_ONE_TO_ONE, $attribute['options']['relationType']); - $this->assertEquals(true, $attribute['options']['twoWay']); - $this->assertEquals('city', $attribute['options']['twoWayKey']); - } - } - - // Create document with relationship with nested data - $doc = new Document([ - '$id' => 'country1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'England', - 'city' => [ - '$id' => 'city1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'London', - 'code' => 'LON', - ], - ]); - - static::getDatabase()->createDocument('country', $doc); - $country1 = static::getDatabase()->getDocument('country', 'country1'); - $this->assertEquals('London', $country1->getAttribute('city')->getAttribute('name')); - - // Update a document with non existing related document. It should not get added to the list. - static::getDatabase()->updateDocument('country', 'country1', (new Document($doc->getArrayCopy()))->setAttribute('city', 'no-city')); - - $country1Document = static::getDatabase()->getDocument('country', 'country1'); - // Assert document does not contain non existing relation document. - $this->assertEquals(null, $country1Document->getAttribute('city')); - static::getDatabase()->updateDocument('country', 'country1', (new Document($doc->getArrayCopy()))->setAttribute('city', 'city1')); - try { - static::getDatabase()->deleteDocument('country', 'country1'); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertInstanceOf(RestrictedException::class, $e); - } - - $this->assertTrue(static::getDatabase()->deleteDocument('city', 'city1')); - - $city1 = static::getDatabase()->getDocument('city', 'city1'); - $this->assertTrue($city1->isEmpty()); - - $country1 = static::getDatabase()->getDocument('country', 'country1'); - $this->assertTrue($country1->getAttribute('city')->isEmpty()); - - $this->assertTrue(static::getDatabase()->deleteDocument('country', 'country1')); - - static::getDatabase()->createDocument('country', $doc); - $country1 = static::getDatabase()->getDocument('country', 'country1'); - $this->assertEquals('London', $country1->getAttribute('city')->getAttribute('name')); - - // Create document with relationship with related ID - static::getDatabase()->createDocument('city', new Document([ - '$id' => 'city2', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Paris', - 'code' => 'PAR', - ])); - static::getDatabase()->createDocument('country', new Document([ - '$id' => 'country2', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'France', - 'city' => 'city2', - ])); - - // Create from child side - static::getDatabase()->createDocument('city', new Document([ - '$id' => 'city3', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Christchurch', - 'code' => 'CHC', - 'country' => [ - '$id' => 'country3', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'New Zealand', - ], - ])); - static::getDatabase()->createDocument('country', new Document([ - '$id' => 'country4', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Australia', - ])); - static::getDatabase()->createDocument('city', new Document([ - '$id' => 'city4', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Sydney', - 'code' => 'SYD', - 'country' => 'country4', - ])); - - // Get document with relationship - $city = static::getDatabase()->getDocument('city', 'city1'); - $country = $city->getAttribute('country'); - $this->assertEquals('country1', $country['$id']); - $this->assertArrayNotHasKey('city', $country); - - $city = static::getDatabase()->getDocument('city', 'city2'); - $country = $city->getAttribute('country'); - $this->assertEquals('country2', $country['$id']); - $this->assertArrayNotHasKey('city', $country); - - $city = static::getDatabase()->getDocument('city', 'city3'); - $country = $city->getAttribute('country'); - $this->assertEquals('country3', $country['$id']); - $this->assertArrayNotHasKey('city', $country); - - $city = static::getDatabase()->getDocument('city', 'city4'); - $country = $city->getAttribute('country'); - $this->assertEquals('country4', $country['$id']); - $this->assertArrayNotHasKey('city', $country); - - // Get inverse document with relationship - $country = static::getDatabase()->getDocument('country', 'country1'); - $city = $country->getAttribute('city'); - $this->assertEquals('city1', $city['$id']); - $this->assertArrayNotHasKey('country', $city); - - $country = static::getDatabase()->getDocument('country', 'country2'); - $city = $country->getAttribute('city'); - $this->assertEquals('city2', $city['$id']); - $this->assertArrayNotHasKey('country', $city); - - $country = static::getDatabase()->getDocument('country', 'country3'); - $city = $country->getAttribute('city'); - $this->assertEquals('city3', $city['$id']); - $this->assertArrayNotHasKey('country', $city); - - $country = static::getDatabase()->getDocument('country', 'country4'); - $city = $country->getAttribute('city'); - $this->assertEquals('city4', $city['$id']); - $this->assertArrayNotHasKey('country', $city); - - $countries = static::getDatabase()->find('country'); - - $this->assertEquals(4, \count($countries)); - - // Select related document attributes - $country = static::getDatabase()->findOne('country', [ - Query::select(['*', 'city.name']) - ]); - - if (!$country instanceof Document) { - throw new Exception('Country not found'); - } - - $this->assertEquals('London', $country->getAttribute('city')->getAttribute('name')); - $this->assertArrayNotHasKey('code', $country->getAttribute('city')); - - $country = static::getDatabase()->getDocument('country', 'country1', [ - Query::select(['*', 'city.name']) - ]); - - $this->assertEquals('London', $country->getAttribute('city')->getAttribute('name')); - $this->assertArrayNotHasKey('code', $country->getAttribute('city')); - - $country1 = static::getDatabase()->getDocument('country', 'country1'); - - // Update root document attribute without altering relationship - $country1 = static::getDatabase()->updateDocument( - 'country', - $country1->getId(), - $country1->setAttribute('name', 'Country 1 Updated') - ); - - $this->assertEquals('Country 1 Updated', $country1->getAttribute('name')); - $country1 = static::getDatabase()->getDocument('country', 'country1'); - $this->assertEquals('Country 1 Updated', $country1->getAttribute('name')); - - $city2 = static::getDatabase()->getDocument('city', 'city2'); - - // Update inverse root document attribute without altering relationship - $city2 = static::getDatabase()->updateDocument( - 'city', - $city2->getId(), - $city2->setAttribute('name', 'City 2 Updated') - ); - - $this->assertEquals('City 2 Updated', $city2->getAttribute('name')); - $city2 = static::getDatabase()->getDocument('city', 'city2'); - $this->assertEquals('City 2 Updated', $city2->getAttribute('name')); - - // Update nested document attribute - $country1 = static::getDatabase()->updateDocument( - 'country', - $country1->getId(), - $country1->setAttribute( - 'city', - $country1 - ->getAttribute('city') - ->setAttribute('name', 'City 1 Updated') - ) - ); - - $this->assertEquals('City 1 Updated', $country1->getAttribute('city')->getAttribute('name')); - $country1 = static::getDatabase()->getDocument('country', 'country1'); - $this->assertEquals('City 1 Updated', $country1->getAttribute('city')->getAttribute('name')); - - // Update inverse nested document attribute - $city2 = static::getDatabase()->updateDocument( - 'city', - $city2->getId(), - $city2->setAttribute( - 'country', - $city2 - ->getAttribute('country') - ->setAttribute('name', 'Country 2 Updated') - ) - ); - - $this->assertEquals('Country 2 Updated', $city2->getAttribute('country')->getAttribute('name')); - $city2 = static::getDatabase()->getDocument('city', 'city2'); - $this->assertEquals('Country 2 Updated', $city2->getAttribute('country')->getAttribute('name')); - - // Create new document with no relationship - $country5 = static::getDatabase()->createDocument('country', new Document([ - '$id' => 'country5', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Country 5', - ])); - - // Update to relate to created document - $country5 = static::getDatabase()->updateDocument( - 'country', - $country5->getId(), - $country5->setAttribute('city', new Document([ - '$id' => 'city5', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - ], - 'name' => 'City 5', - 'code' => 'C5', - ])) - ); - - $this->assertEquals('city5', $country5->getAttribute('city')['$id']); - $country5 = static::getDatabase()->getDocument('country', 'country5'); - $this->assertEquals('city5', $country5->getAttribute('city')['$id']); - - // Create new document with no relationship - $city6 = static::getDatabase()->createDocument('city', new Document([ - '$id' => 'city6', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'City6', - 'code' => 'C6', - ])); - - // Update to relate to created document - $city6 = static::getDatabase()->updateDocument( - 'city', - $city6->getId(), - $city6->setAttribute('country', new Document([ - '$id' => 'country6', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - ], - 'name' => 'Country 6', - ])) - ); - - $this->assertEquals('country6', $city6->getAttribute('country')['$id']); - $city6 = static::getDatabase()->getDocument('city', 'city6'); - $this->assertEquals('country6', $city6->getAttribute('country')['$id']); - - // One to one can't relate to multiple documents, unique index throws duplicate - try { - static::getDatabase()->updateDocument( - 'country', - $country1->getId(), - $country1->setAttribute('city', 'city2') - ); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertInstanceOf(DuplicateException::class, $e); - } - - $city1 = static::getDatabase()->getDocument('city', 'city1'); - - // Set relationship to null - $city1 = static::getDatabase()->updateDocument( - 'city', - $city1->getId(), - $city1->setAttribute('country', null) - ); - - $this->assertEquals(null, $city1->getAttribute('country')); - $city1 = static::getDatabase()->getDocument('city', 'city1'); - $this->assertEquals(null, $city1->getAttribute('country')); - - // Create a new city with no relation - $city7 = static::getDatabase()->createDocument('city', new Document([ - '$id' => 'city7', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Copenhagen', - 'code' => 'CPH', - ])); - - // Update document with relation to new document - static::getDatabase()->updateDocument( - 'country', - $country1->getId(), - $country1->setAttribute('city', 'city7') - ); - - // Relate existing document to new document as nested data - static::getDatabase()->updateDocument( - 'country', - $country1->getId(), - $country1->setAttribute('city', $city7) - ); - - // Create a new country with no relation - static::getDatabase()->createDocument('country', new Document([ - '$id' => 'country7', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Denmark' - ])); - - // Update inverse document with new related document - static::getDatabase()->updateDocument( - 'city', - $city1->getId(), - $city1->setAttribute('country', 'country7') - ); - - // Rename relationship keys on both sides - static::getDatabase()->updateRelationship( - 'country', - 'city', - 'newCity', - 'newCountry' - ); - - // Get document with new relationship key - $city = static::getDatabase()->getDocument('city', 'city1'); - $country = $city->getAttribute('newCountry'); - $this->assertEquals('country7', $country['$id']); - - // Get inverse document with new relationship key - $country = static::getDatabase()->getDocument('country', 'country7'); - $city = $country->getAttribute('newCity'); - $this->assertEquals('city1', $city['$id']); - - // Create a new country with no relation - static::getDatabase()->createDocument('country', new Document([ - '$id' => 'country8', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Denmark' - ])); - - // Can delete parent document with no relation with on delete set to restrict - $deleted = static::getDatabase()->deleteDocument('country', 'country8'); - $this->assertEquals(1, $deleted); - - $country8 = static::getDatabase()->getDocument('country', 'country8'); - $this->assertEquals(true, $country8->isEmpty()); - - - // Cannot delete document while still related to another with on delete set to restrict - try { - static::getDatabase()->deleteDocument('country', 'country1'); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); - } - - // Change on delete to set null - static::getDatabase()->updateRelationship( - collection: 'country', - id: 'newCity', - onDelete: Database::RELATION_MUTATE_SET_NULL - ); - - // Delete parent, will set child relationship to null for two-way - static::getDatabase()->deleteDocument('country', 'country1'); - - // Check relation was set to null - $city7 = static::getDatabase()->getDocument('city', 'city7'); - $this->assertEquals(null, $city7->getAttribute('country', '')); - - // Delete child, set parent relationship to null for two-way - static::getDatabase()->deleteDocument('city', 'city2'); - - // Check relation was set to null - $country2 = static::getDatabase()->getDocument('country', 'country2'); - $this->assertEquals(null, $country2->getAttribute('city', '')); - - // Relate again - static::getDatabase()->updateDocument( - 'city', - $city7->getId(), - $city7->setAttribute('newCountry', 'country2') - ); - - // Change on delete to cascade - static::getDatabase()->updateRelationship( - collection: 'country', - id: 'newCity', - onDelete: Database::RELATION_MUTATE_CASCADE - ); - - // Delete parent, will delete child - static::getDatabase()->deleteDocument('country', 'country7'); - - // Check parent and child were deleted - $library = static::getDatabase()->getDocument('country', 'country7'); - $this->assertEquals(true, $library->isEmpty()); - - $library = static::getDatabase()->getDocument('city', 'city1'); - $this->assertEquals(true, $library->isEmpty()); - - // Delete child, will delete parent for two-way - static::getDatabase()->deleteDocument('city', 'city7'); - - // Check parent and child were deleted - $library = static::getDatabase()->getDocument('city', 'city7'); - $this->assertEquals(true, $library->isEmpty()); - - $library = static::getDatabase()->getDocument('country', 'country2'); - $this->assertEquals(true, $library->isEmpty()); - - // Create new document to check after deleting relationship - static::getDatabase()->createDocument('city', new Document([ - '$id' => 'city7', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Munich', - 'code' => 'MUC', - 'newCountry' => [ - '$id' => 'country7', - 'name' => 'Germany' - ] - ])); - - // Delete relationship - static::getDatabase()->deleteRelationship( - 'country', - 'newCity' - ); - - // Try to get document again - $country = static::getDatabase()->getDocument('country', 'country4'); - $city = $country->getAttribute('newCity'); - $this->assertEquals(null, $city); - - // Try to get inverse document again - $city = static::getDatabase()->getDocument('city', 'city7'); - $country = $city->getAttribute('newCountry'); - $this->assertEquals(null, $country); - } - - public function testIdenticalTwoWayKeyRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('parent'); - static::getDatabase()->createCollection('child'); - - static::getDatabase()->createRelationship( - collection: 'parent', - relatedCollection: 'child', - type: Database::RELATION_ONE_TO_ONE, - id: 'child1' - ); - - try { - static::getDatabase()->createRelationship( - collection: 'parent', - relatedCollection: 'child', - type: Database::RELATION_ONE_TO_MANY, - id: 'children', - ); - $this->fail('Failed to throw Exception'); - } catch (Exception $e) { - $this->assertEquals('Related attribute already exists', $e->getMessage()); - } - - static::getDatabase()->createRelationship( - collection: 'parent', - relatedCollection: 'child', - type: Database::RELATION_ONE_TO_MANY, - id: 'children', - twoWayKey: 'parent_id' - ); - - $collection = static::getDatabase()->getCollection('parent'); - $attributes = $collection->getAttribute('attributes', []); - foreach ($attributes as $attribute) { - if ($attribute['key'] === 'child1') { - $this->assertEquals('parent', $attribute['options']['twoWayKey']); - } - - if ($attribute['key'] === 'children') { - $this->assertEquals('parent_id', $attribute['options']['twoWayKey']); - } - } - - static::getDatabase()->createDocument('parent', new Document([ - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'child1' => [ - '$id' => 'foo', - '$permissions' => [Permission::read(Role::any())], - ], - 'children' => [ - [ - '$id' => 'bar', - '$permissions' => [Permission::read(Role::any())], - ], - ], - ])); - - $documents = static::getDatabase()->find('parent', []); - $document = array_pop($documents); - $this->assertArrayHasKey('child1', $document); - $this->assertEquals('foo', $document->getAttribute('child1')->getId()); - $this->assertArrayHasKey('children', $document); - $this->assertEquals('bar', $document->getAttribute('children')[0]->getId()); - - try { - static::getDatabase()->updateRelationship( - collection: 'parent', - id: 'children', - newKey: 'child1' - ); - $this->fail('Failed to throw Exception'); - } catch (Exception $e) { - $this->assertEquals('Attribute already exists', $e->getMessage()); - } - - try { - static::getDatabase()->updateRelationship( - collection: 'parent', - id: 'children', - newTwoWayKey: 'parent' - ); - $this->fail('Failed to throw Exception'); - } catch (Exception $e) { - $this->assertEquals('Related attribute already exists', $e->getMessage()); - } - } - - public function testOneToManyOneWayRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('artist'); - static::getDatabase()->createCollection('album'); - - static::getDatabase()->createAttribute('artist', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('album', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('album', 'price', Database::VAR_FLOAT, 0, true); - - static::getDatabase()->createRelationship( - collection: 'artist', - relatedCollection: 'album', - type: Database::RELATION_ONE_TO_MANY, - id: 'albums' - ); - - // Check metadata for collection - $collection = static::getDatabase()->getCollection('artist'); - $attributes = $collection->getAttribute('attributes', []); - - foreach ($attributes as $attribute) { - if ($attribute['key'] === 'albums') { - $this->assertEquals('relationship', $attribute['type']); - $this->assertEquals('albums', $attribute['$id']); - $this->assertEquals('albums', $attribute['key']); - $this->assertEquals('album', $attribute['options']['relatedCollection']); - $this->assertEquals(Database::RELATION_ONE_TO_MANY, $attribute['options']['relationType']); - $this->assertEquals(false, $attribute['options']['twoWay']); - $this->assertEquals('artist', $attribute['options']['twoWayKey']); - } - } - - // Create document with relationship with nested data - $artist1 = static::getDatabase()->createDocument('artist', new Document([ - '$id' => 'artist1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Artist 1', - 'albums' => [ - [ - '$id' => 'album1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()) - ], - 'name' => 'Album 1', - 'price' => 9.99, - ], - ], - ])); - - // Update a document with non existing related document. It should not get added to the list. - static::getDatabase()->updateDocument('artist', 'artist1', $artist1->setAttribute('albums', ['album1', 'no-album'])); - - $artist1Document = static::getDatabase()->getDocument('artist', 'artist1'); - // Assert document does not contain non existing relation document. - $this->assertEquals(1, \count($artist1Document->getAttribute('albums'))); - - // Create document with relationship with related ID - static::getDatabase()->createDocument('album', new Document([ - '$id' => 'album2', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Album 2', - 'price' => 19.99, - ])); - static::getDatabase()->createDocument('artist', new Document([ - '$id' => 'artist2', - '$permissions' => [ - Permission::read(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Artist 2', - 'albums' => [ - 'album2', - [ - '$id' => 'album33', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Album 3', - 'price' => 33.33, - ] - ] - ])); - - $documents = static::getDatabase()->find('artist', [ - Query::select(['name']), - Query::limit(1) - ]); - $this->assertArrayNotHasKey('albums', $documents[0]); - - // Get document with relationship - $artist = static::getDatabase()->getDocument('artist', 'artist1'); - $albums = $artist->getAttribute('albums', []); - $this->assertEquals('album1', $albums[0]['$id']); - $this->assertArrayNotHasKey('artist', $albums[0]); - - $artist = static::getDatabase()->getDocument('artist', 'artist2'); - $albums = $artist->getAttribute('albums', []); - $this->assertEquals('album2', $albums[0]['$id']); - $this->assertArrayNotHasKey('artist', $albums[0]); - $this->assertEquals('album33', $albums[1]['$id']); - $this->assertCount(2, $albums); - - // Get related document - $album = static::getDatabase()->getDocument('album', 'album1'); - $this->assertArrayNotHasKey('artist', $album); - - $album = static::getDatabase()->getDocument('album', 'album2'); - $this->assertArrayNotHasKey('artist', $album); - - $artists = static::getDatabase()->find('artist'); - - $this->assertEquals(2, \count($artists)); - - // Select related document attributes - $artist = static::getDatabase()->findOne('artist', [ - Query::select(['*', 'albums.name']) - ]); - - if (!$artist instanceof Document) { - $this->fail('Artist not found'); - } - - $this->assertEquals('Album 1', $artist->getAttribute('albums')[0]->getAttribute('name')); - $this->assertArrayNotHasKey('price', $artist->getAttribute('albums')[0]); - - $artist = static::getDatabase()->getDocument('artist', 'artist1', [ - Query::select(['*', 'albums.name']) - ]); - - $this->assertEquals('Album 1', $artist->getAttribute('albums')[0]->getAttribute('name')); - $this->assertArrayNotHasKey('price', $artist->getAttribute('albums')[0]); - - // Update root document attribute without altering relationship - $artist1 = static::getDatabase()->updateDocument( - 'artist', - $artist1->getId(), - $artist1->setAttribute('name', 'Artist 1 Updated') - ); - - $this->assertEquals('Artist 1 Updated', $artist1->getAttribute('name')); - $artist1 = static::getDatabase()->getDocument('artist', 'artist1'); - $this->assertEquals('Artist 1 Updated', $artist1->getAttribute('name')); - - // Update nested document attribute - $albums = $artist1->getAttribute('albums', []); - $albums[0]->setAttribute('name', 'Album 1 Updated'); - - $artist1 = static::getDatabase()->updateDocument( - 'artist', - $artist1->getId(), - $artist1->setAttribute('albums', $albums) - ); - - $this->assertEquals('Album 1 Updated', $artist1->getAttribute('albums')[0]->getAttribute('name')); - $artist1 = static::getDatabase()->getDocument('artist', 'artist1'); - $this->assertEquals('Album 1 Updated', $artist1->getAttribute('albums')[0]->getAttribute('name')); - - $albumId = $artist1->getAttribute('albums')[0]->getAttribute('$id'); - $albumDocument = static::getDatabase()->getDocument('album', $albumId); - $albumDocument->setAttribute('name', 'Album 1 Updated!!!'); - static::getDatabase()->updateDocument('album', $albumDocument->getId(), $albumDocument); - $albumDocument = static::getDatabase()->getDocument('album', $albumDocument->getId()); - $artist1 = static::getDatabase()->getDocument('artist', $artist1->getId()); - - $this->assertEquals('Album 1 Updated!!!', $albumDocument['name']); - $this->assertEquals($albumDocument->getId(), $artist1->getAttribute('albums')[0]->getId()); - //Todo: This is failing - $this->assertEquals($albumDocument->getAttribute('name'), $artist1->getAttribute('albums')[0]->getAttribute('name')); - - // Create new document with no relationship - $artist3 = static::getDatabase()->createDocument('artist', new Document([ - '$id' => 'artist3', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Artist 3', - ])); - - // Update to relate to created document - $artist3 = static::getDatabase()->updateDocument( - 'artist', - $artist3->getId(), - $artist3->setAttribute('albums', [new Document([ - '$id' => 'album3', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Album 3', - 'price' => 29.99, - ])]) - ); - - $this->assertEquals('Album 3', $artist3->getAttribute('albums')[0]->getAttribute('name')); - $artist3 = static::getDatabase()->getDocument('artist', 'artist3'); - $this->assertEquals('Album 3', $artist3->getAttribute('albums')[0]->getAttribute('name')); - - // Update document with new related documents, will remove existing relations - static::getDatabase()->updateDocument( - 'artist', - $artist1->getId(), - $artist1->setAttribute('albums', ['album2']) - ); - - // Update document with new related documents, will remove existing relations - static::getDatabase()->updateDocument( - 'artist', - $artist1->getId(), - $artist1->setAttribute('albums', ['album1', 'album2']) - ); - - // Rename relationship key - static::getDatabase()->updateRelationship( - 'artist', - 'albums', - 'newAlbums' - ); - - // Get document with new relationship key - $artist = static::getDatabase()->getDocument('artist', 'artist1'); - $albums = $artist->getAttribute('newAlbums'); - $this->assertEquals('album1', $albums[0]['$id']); - - // Create new document with no relationship - static::getDatabase()->createDocument('artist', new Document([ - '$id' => 'artist4', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Artist 4', - ])); - - // Can delete document with no relationship when on delete is set to restrict - $deleted = static::getDatabase()->deleteDocument('artist', 'artist4'); - $this->assertEquals(true, $deleted); - - $artist4 = static::getDatabase()->getDocument('artist', 'artist4'); - $this->assertEquals(true, $artist4->isEmpty()); - - // Try to delete document while still related to another with on delete: restrict - try { - static::getDatabase()->deleteDocument('artist', 'artist1'); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); - } - - // Change on delete to set null - static::getDatabase()->updateRelationship( - collection: 'artist', - id: 'newAlbums', - onDelete: Database::RELATION_MUTATE_SET_NULL - ); - - // Delete parent, set child relationship to null - static::getDatabase()->deleteDocument('artist', 'artist1'); - - // Check relation was set to null - $album2 = static::getDatabase()->getDocument('album', 'album2'); - $this->assertEquals(null, $album2->getAttribute('artist', '')); - - // Relate again - static::getDatabase()->updateDocument( - 'album', - $album2->getId(), - $album2->setAttribute('artist', 'artist2') - ); - - // Change on delete to cascade - static::getDatabase()->updateRelationship( - collection: 'artist', - id: 'newAlbums', - onDelete: Database::RELATION_MUTATE_CASCADE - ); - - // Delete parent, will delete child - static::getDatabase()->deleteDocument('artist', 'artist2'); - - // Check parent and child were deleted - $library = static::getDatabase()->getDocument('artist', 'artist2'); - $this->assertEquals(true, $library->isEmpty()); - - $library = static::getDatabase()->getDocument('album', 'album2'); - $this->assertEquals(true, $library->isEmpty()); - - $albums = []; - for ($i = 1 ; $i <= 50 ; $i++) { - $albums[] = [ - '$id' => 'album_' . $i, - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'album ' . $i . ' ' . 'Artist 100', - 'price' => 100, - ]; - } - - $artist = static::getDatabase()->createDocument('artist', new Document([ - '$permissions' => [ - Permission::read(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Artist 100', - 'newAlbums' => $albums - ])); - - $artist = static::getDatabase()->getDocument('artist', $artist->getId()); - $this->assertCount(50, $artist->getAttribute('newAlbums')); - - $albums = static::getDatabase()->find('album', [ - Query::equal('artist', [$artist->getId()]), - Query::limit(999) - ]); - - $this->assertCount(50, $albums); - - $count = static::getDatabase()->count('album', [ - Query::equal('artist', [$artist->getId()]), - ]); - - $this->assertEquals(50, $count); - - static::getDatabase()->deleteDocument('album', 'album_1'); - $artist = static::getDatabase()->getDocument('artist', $artist->getId()); - $this->assertCount(49, $artist->getAttribute('newAlbums')); - - static::getDatabase()->deleteDocument('artist', $artist->getId()); - - $albums = static::getDatabase()->find('album', [ - Query::equal('artist', [$artist->getId()]), - Query::limit(999) - ]); - - $this->assertCount(0, $albums); - - // Delete relationship - static::getDatabase()->deleteRelationship( - 'artist', - 'newAlbums' - ); - - // Try to get document again - $artist = static::getDatabase()->getDocument('artist', 'artist1'); - $albums = $artist->getAttribute('newAlbums', ''); - $this->assertEquals(null, $albums); - } - - public function testOneToManyTwoWayRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('customer'); - static::getDatabase()->createCollection('account'); - - static::getDatabase()->createAttribute('customer', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('account', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('account', 'number', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'customer', - relatedCollection: 'account', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - id: 'accounts' - ); - - // Check metadata for collection - $collection = static::getDatabase()->getCollection('customer'); - $attributes = $collection->getAttribute('attributes', []); - foreach ($attributes as $attribute) { - if ($attribute['key'] === 'accounts') { - $this->assertEquals('relationship', $attribute['type']); - $this->assertEquals('accounts', $attribute['$id']); - $this->assertEquals('accounts', $attribute['key']); - $this->assertEquals('account', $attribute['options']['relatedCollection']); - $this->assertEquals(Database::RELATION_ONE_TO_MANY, $attribute['options']['relationType']); - $this->assertEquals(true, $attribute['options']['twoWay']); - $this->assertEquals('customer', $attribute['options']['twoWayKey']); - } - } - - // Check metadata for related collection - $collection = static::getDatabase()->getCollection('account'); - $attributes = $collection->getAttribute('attributes', []); - foreach ($attributes as $attribute) { - if ($attribute['key'] === 'customer') { - $this->assertEquals('relationship', $attribute['type']); - $this->assertEquals('customer', $attribute['$id']); - $this->assertEquals('customer', $attribute['key']); - $this->assertEquals('customer', $attribute['options']['relatedCollection']); - $this->assertEquals(Database::RELATION_ONE_TO_MANY, $attribute['options']['relationType']); - $this->assertEquals(true, $attribute['options']['twoWay']); - $this->assertEquals('accounts', $attribute['options']['twoWayKey']); - } - } - - // Create document with relationship with nested data - $customer1 = static::getDatabase()->createDocument('customer', new Document([ - '$id' => 'customer1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Customer 1', - 'accounts' => [ - [ - '$id' => 'account1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Account 1', - 'number' => '123456789', - ], - ], - ])); - - // Update a document with non existing related document. It should not get added to the list. - static::getDatabase()->updateDocument('customer', 'customer1', $customer1->setAttribute('accounts', ['account1','no-account'])); - - $customer1Document = static::getDatabase()->getDocument('customer', 'customer1'); - // Assert document does not contain non existing relation document. - $this->assertEquals(1, \count($customer1Document->getAttribute('accounts'))); - - // Create document with relationship with related ID - $account2 = static::getDatabase()->createDocument('account', new Document([ - '$id' => 'account2', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Account 2', - 'number' => '987654321', - ])); - static::getDatabase()->createDocument('customer', new Document([ - '$id' => 'customer2', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Customer 2', - 'accounts' => [ - 'account2' - ] - ])); - - // Create from child side - static::getDatabase()->createDocument('account', new Document([ - '$id' => 'account3', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Account 3', - 'number' => '123456789', - 'customer' => [ - '$id' => 'customer3', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Customer 3' - ] - ])); - static::getDatabase()->createDocument('customer', new Document([ - '$id' => 'customer4', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Customer 4', - ])); - static::getDatabase()->createDocument('account', new Document([ - '$id' => 'account4', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Account 4', - 'number' => '123456789', - 'customer' => 'customer4' - ])); - - // Get documents with relationship - $customer = static::getDatabase()->getDocument('customer', 'customer1'); - $accounts = $customer->getAttribute('accounts', []); - $this->assertEquals('account1', $accounts[0]['$id']); - $this->assertArrayNotHasKey('customer', $accounts[0]); - - $customer = static::getDatabase()->getDocument('customer', 'customer2'); - $accounts = $customer->getAttribute('accounts', []); - $this->assertEquals('account2', $accounts[0]['$id']); - $this->assertArrayNotHasKey('customer', $accounts[0]); - - $customer = static::getDatabase()->getDocument('customer', 'customer3'); - $accounts = $customer->getAttribute('accounts', []); - $this->assertEquals('account3', $accounts[0]['$id']); - $this->assertArrayNotHasKey('customer', $accounts[0]); - - $customer = static::getDatabase()->getDocument('customer', 'customer4'); - $accounts = $customer->getAttribute('accounts', []); - $this->assertEquals('account4', $accounts[0]['$id']); - $this->assertArrayNotHasKey('customer', $accounts[0]); - - // Get related documents - $account = static::getDatabase()->getDocument('account', 'account1'); - $customer = $account->getAttribute('customer'); - $this->assertEquals('customer1', $customer['$id']); - $this->assertArrayNotHasKey('accounts', $customer); - - $account = static::getDatabase()->getDocument('account', 'account2'); - $customer = $account->getAttribute('customer'); - $this->assertEquals('customer2', $customer['$id']); - $this->assertArrayNotHasKey('accounts', $customer); - - $account = static::getDatabase()->getDocument('account', 'account3'); - $customer = $account->getAttribute('customer'); - $this->assertEquals('customer3', $customer['$id']); - $this->assertArrayNotHasKey('accounts', $customer); - - $account = static::getDatabase()->getDocument('account', 'account4'); - $customer = $account->getAttribute('customer'); - $this->assertEquals('customer4', $customer['$id']); - $this->assertArrayNotHasKey('accounts', $customer); - - $customers = static::getDatabase()->find('customer'); - - $this->assertEquals(4, \count($customers)); - - // Select related document attributes - $customer = static::getDatabase()->findOne('customer', [ - Query::select(['*', 'accounts.name']) - ]); - - if (!$customer instanceof Document) { - throw new Exception('Customer not found'); - } - - $this->assertEquals('Account 1', $customer->getAttribute('accounts')[0]->getAttribute('name')); - $this->assertArrayNotHasKey('number', $customer->getAttribute('accounts')[0]); - - $customer = static::getDatabase()->getDocument('customer', 'customer1', [ - Query::select(['*', 'accounts.name']) - ]); - - $this->assertEquals('Account 1', $customer->getAttribute('accounts')[0]->getAttribute('name')); - $this->assertArrayNotHasKey('number', $customer->getAttribute('accounts')[0]); - - // Update root document attribute without altering relationship - $customer1 = static::getDatabase()->updateDocument( - 'customer', - $customer1->getId(), - $customer1->setAttribute('name', 'Customer 1 Updated') - ); - - $this->assertEquals('Customer 1 Updated', $customer1->getAttribute('name')); - $customer1 = static::getDatabase()->getDocument('customer', 'customer1'); - $this->assertEquals('Customer 1 Updated', $customer1->getAttribute('name')); - - $account2 = static::getDatabase()->getDocument('account', 'account2'); - - // Update inverse root document attribute without altering relationship - $account2 = static::getDatabase()->updateDocument( - 'account', - $account2->getId(), - $account2->setAttribute('name', 'Account 2 Updated') - ); - - $this->assertEquals('Account 2 Updated', $account2->getAttribute('name')); - $account2 = static::getDatabase()->getDocument('account', 'account2'); - $this->assertEquals('Account 2 Updated', $account2->getAttribute('name')); - - // Update nested document attribute - $accounts = $customer1->getAttribute('accounts', []); - $accounts[0]->setAttribute('name', 'Account 1 Updated'); - - $customer1 = static::getDatabase()->updateDocument( - 'customer', - $customer1->getId(), - $customer1->setAttribute('accounts', $accounts) - ); - - $this->assertEquals('Account 1 Updated', $customer1->getAttribute('accounts')[0]->getAttribute('name')); - $customer1 = static::getDatabase()->getDocument('customer', 'customer1'); - $this->assertEquals('Account 1 Updated', $customer1->getAttribute('accounts')[0]->getAttribute('name')); - - // Update inverse nested document attribute - $account2 = static::getDatabase()->updateDocument( - 'account', - $account2->getId(), - $account2->setAttribute( - 'customer', - $account2 - ->getAttribute('customer') - ->setAttribute('name', 'Customer 2 Updated') - ) - ); - - $this->assertEquals('Customer 2 Updated', $account2->getAttribute('customer')->getAttribute('name')); - $account2 = static::getDatabase()->getDocument('account', 'account2'); - $this->assertEquals('Customer 2 Updated', $account2->getAttribute('customer')->getAttribute('name')); - - // Create new document with no relationship - $customer5 = static::getDatabase()->createDocument('customer', new Document([ - '$id' => 'customer5', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Customer 5', - ])); - - // Update to relate to created document - $customer5 = static::getDatabase()->updateDocument( - 'customer', - $customer5->getId(), - $customer5->setAttribute('accounts', [new Document([ - '$id' => 'account5', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Account 5', - 'number' => '123456789', - ])]) - ); - - $this->assertEquals('Account 5', $customer5->getAttribute('accounts')[0]->getAttribute('name')); - $customer5 = static::getDatabase()->getDocument('customer', 'customer5'); - $this->assertEquals('Account 5', $customer5->getAttribute('accounts')[0]->getAttribute('name')); - - // Create new child document with no relationship - $account6 = static::getDatabase()->createDocument('account', new Document([ - '$id' => 'account6', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Account 6', - 'number' => '123456789', - ])); - - // Update inverse to relate to created document - $account6 = static::getDatabase()->updateDocument( - 'account', - $account6->getId(), - $account6->setAttribute('customer', new Document([ - '$id' => 'customer6', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Customer 6', - ])) - ); - - $this->assertEquals('Customer 6', $account6->getAttribute('customer')->getAttribute('name')); - $account6 = static::getDatabase()->getDocument('account', 'account6'); - $this->assertEquals('Customer 6', $account6->getAttribute('customer')->getAttribute('name')); - - // Update document with new related document, will remove existing relations - static::getDatabase()->updateDocument( - 'customer', - $customer1->getId(), - $customer1->setAttribute('accounts', ['account2']) - ); - - // Update document with new related document - static::getDatabase()->updateDocument( - 'customer', - $customer1->getId(), - $customer1->setAttribute('accounts', ['account1', 'account2']) - ); - - // Update inverse document - static::getDatabase()->updateDocument( - 'account', - $account2->getId(), - $account2->setAttribute('customer', 'customer2') - ); - - // Rename relationship keys on both sides - static::getDatabase()->updateRelationship( - 'customer', - 'accounts', - 'newAccounts', - 'newCustomer' - ); - - // Get document with new relationship key - $customer = static::getDatabase()->getDocument('customer', 'customer1'); - $accounts = $customer->getAttribute('newAccounts'); - $this->assertEquals('account1', $accounts[0]['$id']); - - // Get inverse document with new relationship key - $account = static::getDatabase()->getDocument('account', 'account1'); - $customer = $account->getAttribute('newCustomer'); - $this->assertEquals('customer1', $customer['$id']); - - // Create new document with no relationship - static::getDatabase()->createDocument('customer', new Document([ - '$id' => 'customer7', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Customer 7', - ])); - - // Can delete document with no relationship when on delete is set to restrict - $deleted = static::getDatabase()->deleteDocument('customer', 'customer7'); - $this->assertEquals(true, $deleted); - - $customer7 = static::getDatabase()->getDocument('customer', 'customer7'); - $this->assertEquals(true, $customer7->isEmpty()); - - // Try to delete document while still related to another with on delete: restrict - try { - static::getDatabase()->deleteDocument('customer', 'customer1'); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); - } - - // Change on delete to set null - static::getDatabase()->updateRelationship( - collection: 'customer', - id: 'newAccounts', - onDelete: Database::RELATION_MUTATE_SET_NULL - ); - - // Delete parent, set child relationship to null - static::getDatabase()->deleteDocument('customer', 'customer1'); - - // Check relation was set to null - $account1 = static::getDatabase()->getDocument('account', 'account1'); - $this->assertEquals(null, $account2->getAttribute('newCustomer', '')); - - // Relate again - static::getDatabase()->updateDocument( - 'account', - $account1->getId(), - $account1->setAttribute('newCustomer', 'customer2') - ); - - // Change on delete to cascade - static::getDatabase()->updateRelationship( - collection: 'customer', - id: 'newAccounts', - onDelete: Database::RELATION_MUTATE_CASCADE - ); - - // Delete parent, will delete child - static::getDatabase()->deleteDocument('customer', 'customer2'); - - // Check parent and child were deleted - $library = static::getDatabase()->getDocument('customer', 'customer2'); - $this->assertEquals(true, $library->isEmpty()); - - $library = static::getDatabase()->getDocument('account', 'account2'); - $this->assertEquals(true, $library->isEmpty()); - - // Delete relationship - static::getDatabase()->deleteRelationship( - 'customer', - 'newAccounts' - ); - - // Try to get document again - $customer = static::getDatabase()->getDocument('customer', 'customer1'); - $accounts = $customer->getAttribute('newAccounts'); - $this->assertEquals(null, $accounts); - - // Try to get inverse document again - $accounts = static::getDatabase()->getDocument('account', 'account1'); - $customer = $accounts->getAttribute('newCustomer'); - $this->assertEquals(null, $customer); - } - - public function testManyToOneOneWayRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('review'); - static::getDatabase()->createCollection('movie'); - - static::getDatabase()->createAttribute('review', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('movie', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('movie', 'length', Database::VAR_INTEGER, 0, true, formatOptions: ['min' => 0, 'max' => 999]); - static::getDatabase()->createAttribute('movie', 'date', Database::VAR_DATETIME, 0, false, filters: ['datetime']); - static::getDatabase()->createAttribute('review', 'date', Database::VAR_DATETIME, 0, false, filters: ['datetime']); - static::getDatabase()->createRelationship( - collection: 'review', - relatedCollection: 'movie', - type: Database::RELATION_MANY_TO_ONE, - twoWayKey: 'reviews' - ); - - // Check metadata for collection - $collection = static::getDatabase()->getCollection('review'); - $attributes = $collection->getAttribute('attributes', []); - foreach ($attributes as $attribute) { - if ($attribute['key'] === 'movie') { - $this->assertEquals('relationship', $attribute['type']); - $this->assertEquals('movie', $attribute['$id']); - $this->assertEquals('movie', $attribute['key']); - $this->assertEquals('movie', $attribute['options']['relatedCollection']); - $this->assertEquals(Database::RELATION_MANY_TO_ONE, $attribute['options']['relationType']); - $this->assertEquals(false, $attribute['options']['twoWay']); - $this->assertEquals('reviews', $attribute['options']['twoWayKey']); - } - } - - // Check metadata for related collection - $collection = static::getDatabase()->getCollection('movie'); - $attributes = $collection->getAttribute('attributes', []); - foreach ($attributes as $attribute) { - if ($attribute['key'] === 'reviews') { - $this->assertEquals('relationship', $attribute['type']); - $this->assertEquals('reviews', $attribute['$id']); - $this->assertEquals('reviews', $attribute['key']); - $this->assertEquals('review', $attribute['options']['relatedCollection']); - $this->assertEquals(Database::RELATION_MANY_TO_ONE, $attribute['options']['relationType']); - $this->assertEquals(false, $attribute['options']['twoWay']); - $this->assertEquals('movie', $attribute['options']['twoWayKey']); - } - } - - // Create document with relationship with nested data - $review1 = static::getDatabase()->createDocument('review', new Document([ - '$id' => 'review1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Review 1', - 'date' => '2023-04-03 10:35:27.390', - 'movie' => [ - '$id' => 'movie1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Movie 1', - 'date' => '2023-04-03 10:35:27.390', - 'length' => 120, - ], - ])); - - // Update a document with non existing related document. It should not get added to the list. - static::getDatabase()->updateDocument('review', 'review1', $review1->setAttribute('movie', 'no-movie')); - - $review1Document = static::getDatabase()->getDocument('review', 'review1'); - // Assert document does not contain non existing relation document. - $this->assertEquals(null, $review1Document->getAttribute('movie')); - - static::getDatabase()->updateDocument('review', 'review1', $review1->setAttribute('movie', 'movie1')); - - // Create document with relationship to existing document by ID - $review10 = static::getDatabase()->createDocument('review', new Document([ - '$id' => 'review10', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Review 10', - 'movie' => 'movie1', - 'date' => '2023-04-03 10:35:27.390', - ])); - - // Create document with relationship with related ID - static::getDatabase()->createDocument('movie', new Document([ - '$id' => 'movie2', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Movie 2', - 'length' => 90, - 'date' => '2023-04-03 10:35:27.390', - ])); - static::getDatabase()->createDocument('review', new Document([ - '$id' => 'review2', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Review 2', - 'movie' => 'movie2', - 'date' => '2023-04-03 10:35:27.390', - ])); - - // Get document with relationship - $review = static::getDatabase()->getDocument('review', 'review1'); - $movie = $review->getAttribute('movie', []); - $this->assertEquals('movie1', $movie['$id']); - $this->assertArrayNotHasKey('reviews', $movie); - - $documents = static::getDatabase()->find('review', [ - Query::select(['date', 'movie.date']) - ]); - - $this->assertCount(3, $documents); - - $document = $documents[0]; - $this->assertArrayHasKey('date', $document); - $this->assertArrayHasKey('movie', $document); - $this->assertArrayHasKey('date', $document->getAttribute('movie')); - $this->assertArrayNotHasKey('name', $document); - $this->assertEquals(29, strlen($document['date'])); // checks filter - $this->assertEquals(29, strlen($document['movie']['date'])); - - $review = static::getDatabase()->getDocument('review', 'review2'); - $movie = $review->getAttribute('movie', []); - $this->assertEquals('movie2', $movie['$id']); - $this->assertArrayNotHasKey('reviews', $movie); - - // Get related document - $movie = static::getDatabase()->getDocument('movie', 'movie1'); - $this->assertArrayNotHasKey('reviews', $movie); - - $movie = static::getDatabase()->getDocument('movie', 'movie2'); - $this->assertArrayNotHasKey('reviews', $movie); - - $reviews = static::getDatabase()->find('review'); - - $this->assertEquals(3, \count($reviews)); - - // Select related document attributes - $review = static::getDatabase()->findOne('review', [ - Query::select(['*', 'movie.name']) - ]); - - if (!$review instanceof Document) { - throw new Exception('Review not found'); - } - - $this->assertEquals('Movie 1', $review->getAttribute('movie')->getAttribute('name')); - $this->assertArrayNotHasKey('length', $review->getAttribute('movie')); - - $review = static::getDatabase()->getDocument('review', 'review1', [ - Query::select(['*', 'movie.name']) - ]); - - $this->assertEquals('Movie 1', $review->getAttribute('movie')->getAttribute('name')); - $this->assertArrayNotHasKey('length', $review->getAttribute('movie')); - - // Update root document attribute without altering relationship - $review1 = static::getDatabase()->updateDocument( - 'review', - $review1->getId(), - $review1->setAttribute('name', 'Review 1 Updated') - ); - - $this->assertEquals('Review 1 Updated', $review1->getAttribute('name')); - $review1 = static::getDatabase()->getDocument('review', 'review1'); - $this->assertEquals('Review 1 Updated', $review1->getAttribute('name')); - - // Update nested document attribute - $movie = $review1->getAttribute('movie'); - $movie->setAttribute('name', 'Movie 1 Updated'); - - $review1 = static::getDatabase()->updateDocument( - 'review', - $review1->getId(), - $review1->setAttribute('movie', $movie) - ); - - $this->assertEquals('Movie 1 Updated', $review1->getAttribute('movie')->getAttribute('name')); - $review1 = static::getDatabase()->getDocument('review', 'review1'); - $this->assertEquals('Movie 1 Updated', $review1->getAttribute('movie')->getAttribute('name')); - - // Create new document with no relationship - $review5 = static::getDatabase()->createDocument('review', new Document([ - '$id' => 'review5', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Review 5', - ])); - - // Update to relate to created document - $review5 = static::getDatabase()->updateDocument( - 'review', - $review5->getId(), - $review5->setAttribute('movie', new Document([ - '$id' => 'movie5', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Movie 5', - 'length' => 90, - ])) - ); - - $this->assertEquals('Movie 5', $review5->getAttribute('movie')->getAttribute('name')); - $review5 = static::getDatabase()->getDocument('review', 'review5'); - $this->assertEquals('Movie 5', $review5->getAttribute('movie')->getAttribute('name')); - - // Update document with new related document - static::getDatabase()->updateDocument( - 'review', - $review1->getId(), - $review1->setAttribute('movie', 'movie2') - ); - - // Rename relationship keys on both sides - static::getDatabase()->updateRelationship( - 'review', - 'movie', - 'newMovie', - ); - - // Get document with new relationship key - $review = static::getDatabase()->getDocument('review', 'review1'); - $movie = $review->getAttribute('newMovie'); - $this->assertEquals('movie2', $movie['$id']); - - // Reset values - $review1 = static::getDatabase()->getDocument('review', 'review1'); - - static::getDatabase()->updateDocument( - 'review', - $review1->getId(), - $review1->setAttribute('newMovie', 'movie1') - ); - - // Create new document with no relationship - static::getDatabase()->createDocument('movie', new Document([ - '$id' => 'movie3', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Movie 3', - 'length' => 90, - ])); - - // Can delete document with no relationship when on delete is set to restrict - $deleted = static::getDatabase()->deleteDocument('movie', 'movie3'); - $this->assertEquals(true, $deleted); - - $movie3 = static::getDatabase()->getDocument('movie', 'movie3'); - $this->assertEquals(true, $movie3->isEmpty()); - - // Try to delete document while still related to another with on delete: restrict - try { - static::getDatabase()->deleteDocument('movie', 'movie1'); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); - } - - // Change on delete to set null - static::getDatabase()->updateRelationship( - collection: 'review', - id: 'newMovie', - onDelete: Database::RELATION_MUTATE_SET_NULL - ); - - // Delete child, set parent relationship to null - static::getDatabase()->deleteDocument('movie', 'movie1'); - - // Check relation was set to null - $review1 = static::getDatabase()->getDocument('review', 'review1'); - $this->assertEquals(null, $review1->getAttribute('newMovie')); - - // Change on delete to cascade - static::getDatabase()->updateRelationship( - collection: 'review', - id: 'newMovie', - onDelete: Database::RELATION_MUTATE_CASCADE - ); - - // Delete child, will delete parent - static::getDatabase()->deleteDocument('movie', 'movie2'); - - // Check parent and child were deleted - $library = static::getDatabase()->getDocument('movie', 'movie2'); - $this->assertEquals(true, $library->isEmpty()); - - $library = static::getDatabase()->getDocument('review', 'review2'); - $this->assertEquals(true, $library->isEmpty()); - - - // Delete relationship - static::getDatabase()->deleteRelationship( - 'review', - 'newMovie' - ); - - // Try to get document again - $review = static::getDatabase()->getDocument('review', 'review1'); - $movie = $review->getAttribute('newMovie'); - $this->assertEquals(null, $movie); - } - - public function testManyToOneTwoWayRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('product'); - static::getDatabase()->createCollection('store'); - - static::getDatabase()->createAttribute('store', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('store', 'opensAt', Database::VAR_STRING, 5, true); - - static::getDatabase()->createAttribute( - collection: 'product', - id: 'name', - type: Database::VAR_STRING, - size: 255, - required: true - ); - - static::getDatabase()->createRelationship( - collection: 'product', - relatedCollection: 'store', - type: Database::RELATION_MANY_TO_ONE, - twoWay: true, - twoWayKey: 'products' - ); - - // Check metadata for collection - $collection = static::getDatabase()->getCollection('product'); - $attributes = $collection->getAttribute('attributes', []); - foreach ($attributes as $attribute) { - if ($attribute['key'] === 'store') { - $this->assertEquals('relationship', $attribute['type']); - $this->assertEquals('store', $attribute['$id']); - $this->assertEquals('store', $attribute['key']); - $this->assertEquals('store', $attribute['options']['relatedCollection']); - $this->assertEquals(Database::RELATION_MANY_TO_ONE, $attribute['options']['relationType']); - $this->assertEquals(true, $attribute['options']['twoWay']); - $this->assertEquals('products', $attribute['options']['twoWayKey']); - } - } - - // Check metadata for related collection - $collection = static::getDatabase()->getCollection('store'); - $attributes = $collection->getAttribute('attributes', []); - foreach ($attributes as $attribute) { - if ($attribute['key'] === 'products') { - $this->assertEquals('relationship', $attribute['type']); - $this->assertEquals('products', $attribute['$id']); - $this->assertEquals('products', $attribute['key']); - $this->assertEquals('product', $attribute['options']['relatedCollection']); - $this->assertEquals(Database::RELATION_MANY_TO_ONE, $attribute['options']['relationType']); - $this->assertEquals(true, $attribute['options']['twoWay']); - $this->assertEquals('store', $attribute['options']['twoWayKey']); - } - } - - // Create document with relationship with nested data - $product1 = static::getDatabase()->createDocument('product', new Document([ - '$id' => 'product1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Product 1', - 'store' => [ - '$id' => 'store1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Store 1', - 'opensAt' => '09:00', - ], - ])); - - // Update a document with non existing related document. It should not get added to the list. - static::getDatabase()->updateDocument('product', 'product1', $product1->setAttribute('store', 'no-store')); - - $product1Document = static::getDatabase()->getDocument('product', 'product1'); - // Assert document does not contain non existing relation document. - $this->assertEquals(null, $product1Document->getAttribute('store')); - - static::getDatabase()->updateDocument('product', 'product1', $product1->setAttribute('store', 'store1')); - - // Create document with relationship with related ID - static::getDatabase()->createDocument('store', new Document([ - '$id' => 'store2', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Store 2', - 'opensAt' => '09:30', - ])); - static::getDatabase()->createDocument('product', new Document([ - '$id' => 'product2', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Product 2', - 'store' => 'store2', - ])); - - // Create from child side - static::getDatabase()->createDocument('store', new Document([ - '$id' => 'store3', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Store 3', - 'opensAt' => '11:30', - 'products' => [ - [ - '$id' => 'product3', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Product 3', - ], - ], - ])); - - static::getDatabase()->createDocument('product', new Document([ - '$id' => 'product4', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Product 4', - ])); - static::getDatabase()->createDocument('store', new Document([ - '$id' => 'store4', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Store 4', - 'opensAt' => '11:30', - 'products' => [ - 'product4', - ], - ])); - - // Get document with relationship - $product = static::getDatabase()->getDocument('product', 'product1'); - $store = $product->getAttribute('store', []); - $this->assertEquals('store1', $store['$id']); - $this->assertArrayNotHasKey('products', $store); - - $product = static::getDatabase()->getDocument('product', 'product2'); - $store = $product->getAttribute('store', []); - $this->assertEquals('store2', $store['$id']); - $this->assertArrayNotHasKey('products', $store); - - $product = static::getDatabase()->getDocument('product', 'product3'); - $store = $product->getAttribute('store', []); - $this->assertEquals('store3', $store['$id']); - $this->assertArrayNotHasKey('products', $store); - - $product = static::getDatabase()->getDocument('product', 'product4'); - $store = $product->getAttribute('store', []); - $this->assertEquals('store4', $store['$id']); - $this->assertArrayNotHasKey('products', $store); - - // Get related document - $store = static::getDatabase()->getDocument('store', 'store1'); - $products = $store->getAttribute('products'); - $this->assertEquals('product1', $products[0]['$id']); - $this->assertArrayNotHasKey('store', $products[0]); - - $store = static::getDatabase()->getDocument('store', 'store2'); - $products = $store->getAttribute('products'); - $this->assertEquals('product2', $products[0]['$id']); - $this->assertArrayNotHasKey('store', $products[0]); - - $store = static::getDatabase()->getDocument('store', 'store3'); - $products = $store->getAttribute('products'); - $this->assertEquals('product3', $products[0]['$id']); - $this->assertArrayNotHasKey('store', $products[0]); - - $store = static::getDatabase()->getDocument('store', 'store4'); - $products = $store->getAttribute('products'); - $this->assertEquals('product4', $products[0]['$id']); - $this->assertArrayNotHasKey('store', $products[0]); - - $products = static::getDatabase()->find('product'); - - $this->assertEquals(4, \count($products)); - - // Select related document attributes - $product = static::getDatabase()->findOne('product', [ - Query::select(['*', 'store.name']) - ]); - - if (!$product instanceof Document) { - throw new Exception('Product not found'); - } - - $this->assertEquals('Store 1', $product->getAttribute('store')->getAttribute('name')); - $this->assertArrayNotHasKey('opensAt', $product->getAttribute('store')); - - $product = static::getDatabase()->getDocument('product', 'product1', [ - Query::select(['*', 'store.name']) - ]); - - $this->assertEquals('Store 1', $product->getAttribute('store')->getAttribute('name')); - $this->assertArrayNotHasKey('opensAt', $product->getAttribute('store')); - - // Update root document attribute without altering relationship - $product1 = static::getDatabase()->updateDocument( - 'product', - $product1->getId(), - $product1->setAttribute('name', 'Product 1 Updated') - ); - - $this->assertEquals('Product 1 Updated', $product1->getAttribute('name')); - $product1 = static::getDatabase()->getDocument('product', 'product1'); - $this->assertEquals('Product 1 Updated', $product1->getAttribute('name')); - - // Update inverse document attribute without altering relationship - $store1 = static::getDatabase()->getDocument('store', 'store1'); - $store1 = static::getDatabase()->updateDocument( - 'store', - $store1->getId(), - $store1->setAttribute('name', 'Store 1 Updated') - ); - - $this->assertEquals('Store 1 Updated', $store1->getAttribute('name')); - $store1 = static::getDatabase()->getDocument('store', 'store1'); - $this->assertEquals('Store 1 Updated', $store1->getAttribute('name')); - - // Update nested document attribute - $store = $product1->getAttribute('store'); - $store->setAttribute('name', 'Store 1 Updated'); - - $product1 = static::getDatabase()->updateDocument( - 'product', - $product1->getId(), - $product1->setAttribute('store', $store) - ); - - $this->assertEquals('Store 1 Updated', $product1->getAttribute('store')->getAttribute('name')); - $product1 = static::getDatabase()->getDocument('product', 'product1'); - $this->assertEquals('Store 1 Updated', $product1->getAttribute('store')->getAttribute('name')); - - // Update inverse nested document attribute - $product = $store1->getAttribute('products')[0]; - $product->setAttribute('name', 'Product 1 Updated'); - - $store1 = static::getDatabase()->updateDocument( - 'store', - $store1->getId(), - $store1->setAttribute('products', [$product]) - ); - - $this->assertEquals('Product 1 Updated', $store1->getAttribute('products')[0]->getAttribute('name')); - $store1 = static::getDatabase()->getDocument('store', 'store1'); - $this->assertEquals('Product 1 Updated', $store1->getAttribute('products')[0]->getAttribute('name')); - - // Create new document with no relationship - $product5 = static::getDatabase()->createDocument('product', new Document([ - '$id' => 'product5', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Product 5', - ])); - - // Update to relate to created document - $product5 = static::getDatabase()->updateDocument( - 'product', - $product5->getId(), - $product5->setAttribute('store', new Document([ - '$id' => 'store5', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Store 5', - 'opensAt' => '09:00', - ])) - ); - - $this->assertEquals('Store 5', $product5->getAttribute('store')->getAttribute('name')); - $product5 = static::getDatabase()->getDocument('product', 'product5'); - $this->assertEquals('Store 5', $product5->getAttribute('store')->getAttribute('name')); - - // Create new child document with no relationship - $store6 = static::getDatabase()->createDocument('store', new Document([ - '$id' => 'store6', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Store 6', - 'opensAt' => '09:00', - ])); - - // Update inverse to related to newly created document - $store6 = static::getDatabase()->updateDocument( - 'store', - $store6->getId(), - $store6->setAttribute('products', [new Document([ - '$id' => 'product6', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Product 6', - ])]) - ); - - $this->assertEquals('Product 6', $store6->getAttribute('products')[0]->getAttribute('name')); - $store6 = static::getDatabase()->getDocument('store', 'store6'); - $this->assertEquals('Product 6', $store6->getAttribute('products')[0]->getAttribute('name')); - - // Update document with new related document - static::getDatabase()->updateDocument( - 'product', - $product1->getId(), - $product1->setAttribute('store', 'store2') - ); - - $store1 = static::getDatabase()->getDocument('store', 'store1'); - - // Update inverse document - static::getDatabase()->updateDocument( - 'store', - $store1->getId(), - $store1->setAttribute('products', ['product1']) - ); - - $store2 = static::getDatabase()->getDocument('store', 'store2'); - - // Update inverse document - static::getDatabase()->updateDocument( - 'store', - $store2->getId(), - $store2->setAttribute('products', ['product1', 'product2']) - ); - - // Rename relationship keys on both sides - static::getDatabase()->updateRelationship( - 'product', - 'store', - 'newStore', - 'newProducts' - ); - - // Get document with new relationship key - $store = static::getDatabase()->getDocument('store', 'store2'); - $products = $store->getAttribute('newProducts'); - $this->assertEquals('product1', $products[0]['$id']); - - // Get inverse document with new relationship key - $product = static::getDatabase()->getDocument('product', 'product1'); - $store = $product->getAttribute('newStore'); - $this->assertEquals('store2', $store['$id']); - - // Reset relationships - $store1 = static::getDatabase()->getDocument('store', 'store1'); - static::getDatabase()->updateDocument( - 'store', - $store1->getId(), - $store1->setAttribute('newProducts', ['product1']) - ); - - // Create new document with no relationship - static::getDatabase()->createDocument('store', new Document([ - '$id' => 'store7', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Store 7', - 'opensAt' => '09:00', - ])); - - // Can delete document with no relationship when on delete is set to restrict - $deleted = static::getDatabase()->deleteDocument('store', 'store7'); - $this->assertEquals(true, $deleted); - - $store7 = static::getDatabase()->getDocument('store', 'store7'); - $this->assertEquals(true, $store7->isEmpty()); - - // Try to delete child while still related to another with on delete: restrict - try { - static::getDatabase()->deleteDocument('store', 'store1'); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); - } - - // Delete parent while still related to another with on delete: restrict - $result = static::getDatabase()->deleteDocument('product', 'product5'); - $this->assertEquals(true, $result); - - // Change on delete to set null - static::getDatabase()->updateRelationship( - collection: 'product', - id: 'newStore', - onDelete: Database::RELATION_MUTATE_SET_NULL - ); - - // Delete child, set parent relationship to null - static::getDatabase()->deleteDocument('store', 'store1'); - - // Check relation was set to null - static::getDatabase()->getDocument('product', 'product1'); - $this->assertEquals(null, $product1->getAttribute('newStore')); - - // Change on delete to cascade - static::getDatabase()->updateRelationship( - collection: 'product', - id: 'newStore', - onDelete: Database::RELATION_MUTATE_CASCADE - ); - - // Delete child, will delete parent - static::getDatabase()->deleteDocument('store', 'store2'); - - // Check parent and child were deleted - $library = static::getDatabase()->getDocument('store', 'store2'); - $this->assertEquals(true, $library->isEmpty()); - - $library = static::getDatabase()->getDocument('product', 'product2'); - $this->assertEquals(true, $library->isEmpty()); - - // Delete relationship - static::getDatabase()->deleteRelationship( - 'product', - 'newStore' - ); - - // Try to get document again - $products = static::getDatabase()->getDocument('product', 'product1'); - $store = $products->getAttribute('newStore'); - $this->assertEquals(null, $store); - - // Try to get inverse document again - $store = static::getDatabase()->getDocument('store', 'store1'); - $products = $store->getAttribute('newProducts'); - $this->assertEquals(null, $products); - } - - public function testManyToManyOneWayRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('playlist'); - static::getDatabase()->createCollection('song'); - - static::getDatabase()->createAttribute('playlist', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('song', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('song', 'length', Database::VAR_INTEGER, 0, true); - - static::getDatabase()->createRelationship( - collection: 'playlist', - relatedCollection: 'song', - type: Database::RELATION_MANY_TO_MANY, - id: 'songs' - ); - - // Check metadata for collection - $collection = static::getDatabase()->getCollection('playlist'); - $attributes = $collection->getAttribute('attributes', []); - - foreach ($attributes as $attribute) { - if ($attribute['key'] === 'songs') { - $this->assertEquals('relationship', $attribute['type']); - $this->assertEquals('songs', $attribute['$id']); - $this->assertEquals('songs', $attribute['key']); - $this->assertEquals('song', $attribute['options']['relatedCollection']); - $this->assertEquals(Database::RELATION_MANY_TO_MANY, $attribute['options']['relationType']); - $this->assertEquals(false, $attribute['options']['twoWay']); - $this->assertEquals('playlist', $attribute['options']['twoWayKey']); - } - } - - // Create document with relationship with nested data - $playlist1 = static::getDatabase()->createDocument('playlist', new Document([ - '$id' => 'playlist1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Playlist 1', - 'songs' => [ - [ - '$id' => 'song1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Song 1', - 'length' => 180, - ], - ], - ])); - - // Create document with relationship with related ID - static::getDatabase()->createDocument('song', new Document([ - '$id' => 'song2', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Song 2', - 'length' => 140, - ])); - static::getDatabase()->createDocument('playlist', new Document([ - '$id' => 'playlist2', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Playlist 2', - 'songs' => [ - 'song2' - ] - ])); - - // Update a document with non existing related document. It should not get added to the list. - static::getDatabase()->updateDocument('playlist', 'playlist1', $playlist1->setAttribute('songs', ['song1','no-song'])); - - $playlist1Document = static::getDatabase()->getDocument('playlist', 'playlist1'); - // Assert document does not contain non existing relation document. - $this->assertEquals(1, \count($playlist1Document->getAttribute('songs'))); - - $documents = static::getDatabase()->find('playlist', [ - Query::select(['name']), - Query::limit(1) - ]); - - $this->assertArrayNotHasKey('songs', $documents[0]); - - // Get document with relationship - $playlist = static::getDatabase()->getDocument('playlist', 'playlist1'); - $songs = $playlist->getAttribute('songs', []); - $this->assertEquals('song1', $songs[0]['$id']); - $this->assertArrayNotHasKey('playlist', $songs[0]); - - $playlist = static::getDatabase()->getDocument('playlist', 'playlist2'); - $songs = $playlist->getAttribute('songs', []); - $this->assertEquals('song2', $songs[0]['$id']); - $this->assertArrayNotHasKey('playlist', $songs[0]); - - // Get related document - $library = static::getDatabase()->getDocument('song', 'song1'); - $this->assertArrayNotHasKey('songs', $library); - - $library = static::getDatabase()->getDocument('song', 'song2'); - $this->assertArrayNotHasKey('songs', $library); - - $playlists = static::getDatabase()->find('playlist'); - - $this->assertEquals(2, \count($playlists)); - - // Select related document attributes - $playlist = static::getDatabase()->findOne('playlist', [ - Query::select(['*', 'songs.name']) - ]); - - if (!$playlist instanceof Document) { - throw new Exception('Playlist not found'); - } - - $this->assertEquals('Song 1', $playlist->getAttribute('songs')[0]->getAttribute('name')); - $this->assertArrayNotHasKey('length', $playlist->getAttribute('songs')[0]); - - $playlist = static::getDatabase()->getDocument('playlist', 'playlist1', [ - Query::select(['*', 'songs.name']) - ]); - - $this->assertEquals('Song 1', $playlist->getAttribute('songs')[0]->getAttribute('name')); - $this->assertArrayNotHasKey('length', $playlist->getAttribute('songs')[0]); - - // Update root document attribute without altering relationship - $playlist1 = static::getDatabase()->updateDocument( - 'playlist', - $playlist1->getId(), - $playlist1->setAttribute('name', 'Playlist 1 Updated') - ); - - $this->assertEquals('Playlist 1 Updated', $playlist1->getAttribute('name')); - $playlist1 = static::getDatabase()->getDocument('playlist', 'playlist1'); - $this->assertEquals('Playlist 1 Updated', $playlist1->getAttribute('name')); - - // Update nested document attribute - $songs = $playlist1->getAttribute('songs', []); - $songs[0]->setAttribute('name', 'Song 1 Updated'); - - $playlist1 = static::getDatabase()->updateDocument( - 'playlist', - $playlist1->getId(), - $playlist1->setAttribute('songs', $songs) - ); - - $this->assertEquals('Song 1 Updated', $playlist1->getAttribute('songs')[0]->getAttribute('name')); - $playlist1 = static::getDatabase()->getDocument('playlist', 'playlist1'); - $this->assertEquals('Song 1 Updated', $playlist1->getAttribute('songs')[0]->getAttribute('name')); - - // Create new document with no relationship - $playlist5 = static::getDatabase()->createDocument('playlist', new Document([ - '$id' => 'playlist5', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Playlist 5', - ])); - - // Update to relate to created document - $playlist5 = static::getDatabase()->updateDocument( - 'playlist', - $playlist5->getId(), - $playlist5->setAttribute('songs', [new Document([ - '$id' => 'song5', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Song 5', - 'length' => 180, - ])]) - ); - - // Playlist relating to existing songs that belong to other playlists - static::getDatabase()->createDocument('playlist', new Document([ - '$id' => 'playlist6', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Playlist 6', - 'songs' => [ - 'song1', - 'song2', - 'song5' - ] - ])); - - $this->assertEquals('Song 5', $playlist5->getAttribute('songs')[0]->getAttribute('name')); - $playlist5 = static::getDatabase()->getDocument('playlist', 'playlist5'); - $this->assertEquals('Song 5', $playlist5->getAttribute('songs')[0]->getAttribute('name')); - - // Update document with new related document - static::getDatabase()->updateDocument( - 'playlist', - $playlist1->getId(), - $playlist1->setAttribute('songs', ['song2']) - ); - - // Rename relationship key - static::getDatabase()->updateRelationship( - 'playlist', - 'songs', - 'newSongs' - ); - - // Get document with new relationship key - $playlist = static::getDatabase()->getDocument('playlist', 'playlist1'); - $songs = $playlist->getAttribute('newSongs'); - $this->assertEquals('song2', $songs[0]['$id']); - - // Create new document with no relationship - static::getDatabase()->createDocument('playlist', new Document([ - '$id' => 'playlist3', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Playlist 3', - ])); - - // Can delete document with no relationship when on delete is set to restrict - $deleted = static::getDatabase()->deleteDocument('playlist', 'playlist3'); - $this->assertEquals(true, $deleted); - - $playlist3 = static::getDatabase()->getDocument('playlist', 'playlist3'); - $this->assertEquals(true, $playlist3->isEmpty()); - - // Try to delete document while still related to another with on delete: restrict - try { - static::getDatabase()->deleteDocument('playlist', 'playlist1'); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); - } - - // Change on delete to set null - static::getDatabase()->updateRelationship( - collection: 'playlist', - id: 'newSongs', - onDelete: Database::RELATION_MUTATE_SET_NULL - ); - - $playlist1 = static::getDatabase()->getDocument('playlist', 'playlist1'); - - // Reset relationships - static::getDatabase()->updateDocument( - 'playlist', - $playlist1->getId(), - $playlist1->setAttribute('newSongs', ['song1']) - ); - - // Delete child, will delete junction - static::getDatabase()->deleteDocument('song', 'song1'); - - // Check relation was set to null - $playlist1 = static::getDatabase()->getDocument('playlist', 'playlist1'); - $this->assertEquals(0, \count($playlist1->getAttribute('newSongs'))); - - // Change on delete to cascade - static::getDatabase()->updateRelationship( - collection: 'playlist', - id: 'newSongs', - onDelete: Database::RELATION_MUTATE_CASCADE - ); - - // Delete parent, will delete child - static::getDatabase()->deleteDocument('playlist', 'playlist2'); - - // Check parent and child were deleted - $library = static::getDatabase()->getDocument('playlist', 'playlist2'); - $this->assertEquals(true, $library->isEmpty()); - - $library = static::getDatabase()->getDocument('song', 'song2'); - $this->assertEquals(true, $library->isEmpty()); - - // Delete relationship - static::getDatabase()->deleteRelationship( - 'playlist', - 'newSongs' - ); - - // Try to get document again - $playlist = static::getDatabase()->getDocument('playlist', 'playlist1'); - $songs = $playlist->getAttribute('newSongs'); - $this->assertEquals(null, $songs); - } - - public function testManyToManyTwoWayRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('students'); - static::getDatabase()->createCollection('classes'); - - static::getDatabase()->createAttribute('students', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('classes', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('classes', 'number', Database::VAR_INTEGER, 0, true); - - static::getDatabase()->createRelationship( - collection: 'students', - relatedCollection: 'classes', - type: Database::RELATION_MANY_TO_MANY, - twoWay: true, - ); - - // Check metadata for collection - $collection = static::getDatabase()->getCollection('students'); - $attributes = $collection->getAttribute('attributes', []); - foreach ($attributes as $attribute) { - if ($attribute['key'] === 'students') { - $this->assertEquals('relationship', $attribute['type']); - $this->assertEquals('students', $attribute['$id']); - $this->assertEquals('students', $attribute['key']); - $this->assertEquals('students', $attribute['options']['relatedCollection']); - $this->assertEquals(Database::RELATION_MANY_TO_MANY, $attribute['options']['relationType']); - $this->assertEquals(true, $attribute['options']['twoWay']); - $this->assertEquals('classes', $attribute['options']['twoWayKey']); - } - } - - // Check metadata for related collection - $collection = static::getDatabase()->getCollection('classes'); - $attributes = $collection->getAttribute('attributes', []); - foreach ($attributes as $attribute) { - if ($attribute['key'] === 'classes') { - $this->assertEquals('relationship', $attribute['type']); - $this->assertEquals('classes', $attribute['$id']); - $this->assertEquals('classes', $attribute['key']); - $this->assertEquals('classes', $attribute['options']['relatedCollection']); - $this->assertEquals(Database::RELATION_MANY_TO_MANY, $attribute['options']['relationType']); - $this->assertEquals(true, $attribute['options']['twoWay']); - $this->assertEquals('students', $attribute['options']['twoWayKey']); - } - } - - // Create document with relationship with nested data - $student1 = static::getDatabase()->createDocument('students', new Document([ - '$id' => 'student1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Student 1', - 'classes' => [ - [ - '$id' => 'class1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Class 1', - 'number' => 1, - ], - ], - ])); - - // Update a document with non existing related document. It should not get added to the list. - static::getDatabase()->updateDocument('students', 'student1', $student1->setAttribute('classes', ['class1', 'no-class'])); - - $student1Document = static::getDatabase()->getDocument('students', 'student1'); - // Assert document does not contain non existing relation document. - $this->assertEquals(1, \count($student1Document->getAttribute('classes'))); - - // Create document with relationship with related ID - static::getDatabase()->createDocument('classes', new Document([ - '$id' => 'class2', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - - ], - 'name' => 'Class 2', - 'number' => 2, - ])); - static::getDatabase()->createDocument('students', new Document([ - '$id' => 'student2', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Student 2', - 'classes' => [ - 'class2' - ], - ])); - - // Create from child side - static::getDatabase()->createDocument('classes', new Document([ - '$id' => 'class3', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Class 3', - 'number' => 3, - 'students' => [ - [ - '$id' => 'student3', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Student 3', - ] - ], - ])); - static::getDatabase()->createDocument('students', new Document([ - '$id' => 'student4', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Student 4' - ])); - static::getDatabase()->createDocument('classes', new Document([ - '$id' => 'class4', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - - ], - 'name' => 'Class 4', - 'number' => 4, - 'students' => [ - 'student4' - ], - ])); - - // Get document with relationship - $student = static::getDatabase()->getDocument('students', 'student1'); - $classes = $student->getAttribute('classes', []); - $this->assertEquals('class1', $classes[0]['$id']); - $this->assertArrayNotHasKey('students', $classes[0]); - - $student = static::getDatabase()->getDocument('students', 'student2'); - $classes = $student->getAttribute('classes', []); - $this->assertEquals('class2', $classes[0]['$id']); - $this->assertArrayNotHasKey('students', $classes[0]); - - $student = static::getDatabase()->getDocument('students', 'student3'); - $classes = $student->getAttribute('classes', []); - $this->assertEquals('class3', $classes[0]['$id']); - $this->assertArrayNotHasKey('students', $classes[0]); - - $student = static::getDatabase()->getDocument('students', 'student4'); - $classes = $student->getAttribute('classes', []); - $this->assertEquals('class4', $classes[0]['$id']); - $this->assertArrayNotHasKey('students', $classes[0]); - - // Get related document - $class = static::getDatabase()->getDocument('classes', 'class1'); - $student = $class->getAttribute('students'); - $this->assertEquals('student1', $student[0]['$id']); - $this->assertArrayNotHasKey('classes', $student[0]); - - $class = static::getDatabase()->getDocument('classes', 'class2'); - $student = $class->getAttribute('students'); - $this->assertEquals('student2', $student[0]['$id']); - $this->assertArrayNotHasKey('classes', $student[0]); - - $class = static::getDatabase()->getDocument('classes', 'class3'); - $student = $class->getAttribute('students'); - $this->assertEquals('student3', $student[0]['$id']); - $this->assertArrayNotHasKey('classes', $student[0]); - - $class = static::getDatabase()->getDocument('classes', 'class4'); - $student = $class->getAttribute('students'); - $this->assertEquals('student4', $student[0]['$id']); - $this->assertArrayNotHasKey('classes', $student[0]); - - // Select related document attributes - $student = static::getDatabase()->findOne('students', [ - Query::select(['*', 'classes.name']) - ]); - - if (!$student instanceof Document) { - throw new Exception('Student not found'); - } - - $this->assertEquals('Class 1', $student->getAttribute('classes')[0]->getAttribute('name')); - $this->assertArrayNotHasKey('number', $student->getAttribute('classes')[0]); - - $student = static::getDatabase()->getDocument('students', 'student1', [ - Query::select(['*', 'classes.name']) - ]); - - $this->assertEquals('Class 1', $student->getAttribute('classes')[0]->getAttribute('name')); - $this->assertArrayNotHasKey('number', $student->getAttribute('classes')[0]); - - // Update root document attribute without altering relationship - $student1 = static::getDatabase()->updateDocument( - 'students', - $student1->getId(), - $student1->setAttribute('name', 'Student 1 Updated') - ); - - $this->assertEquals('Student 1 Updated', $student1->getAttribute('name')); - $student1 = static::getDatabase()->getDocument('students', 'student1'); - $this->assertEquals('Student 1 Updated', $student1->getAttribute('name')); - - // Update inverse root document attribute without altering relationship - $class2 = static::getDatabase()->getDocument('classes', 'class2'); - $class2 = static::getDatabase()->updateDocument( - 'classes', - $class2->getId(), - $class2->setAttribute('name', 'Class 2 Updated') - ); - - $this->assertEquals('Class 2 Updated', $class2->getAttribute('name')); - $class2 = static::getDatabase()->getDocument('classes', 'class2'); - $this->assertEquals('Class 2 Updated', $class2->getAttribute('name')); - - // Update nested document attribute - $classes = $student1->getAttribute('classes', []); - $classes[0]->setAttribute('name', 'Class 1 Updated'); - - $student1 = static::getDatabase()->updateDocument( - 'students', - $student1->getId(), - $student1->setAttribute('classes', $classes) - ); - - $this->assertEquals('Class 1 Updated', $student1->getAttribute('classes')[0]->getAttribute('name')); - $student1 = static::getDatabase()->getDocument('students', 'student1'); - $this->assertEquals('Class 1 Updated', $student1->getAttribute('classes')[0]->getAttribute('name')); - - // Update inverse nested document attribute - $students = $class2->getAttribute('students', []); - $students[0]->setAttribute('name', 'Student 2 Updated'); - - $class2 = static::getDatabase()->updateDocument( - 'classes', - $class2->getId(), - $class2->setAttribute('students', $students) - ); - - $this->assertEquals('Student 2 Updated', $class2->getAttribute('students')[0]->getAttribute('name')); - $class2 = static::getDatabase()->getDocument('classes', 'class2'); - $this->assertEquals('Student 2 Updated', $class2->getAttribute('students')[0]->getAttribute('name')); - - // Create new document with no relationship - $student5 = static::getDatabase()->createDocument('students', new Document([ - '$id' => 'student5', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Student 5', - ])); - - // Update to relate to created document - $student5 = static::getDatabase()->updateDocument( - 'students', - $student5->getId(), - $student5->setAttribute('classes', [new Document([ - '$id' => 'class5', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Class 5', - 'number' => 5, - ])]) - ); - - $this->assertEquals('Class 5', $student5->getAttribute('classes')[0]->getAttribute('name')); - $student5 = static::getDatabase()->getDocument('students', 'student5'); - $this->assertEquals('Class 5', $student5->getAttribute('classes')[0]->getAttribute('name')); - - // Create child document with no relationship - $class6 = static::getDatabase()->createDocument('classes', new Document([ - '$id' => 'class6', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Class 6', - 'number' => 6, - ])); - - // Update to relate to created document - $class6 = static::getDatabase()->updateDocument( - 'classes', - $class6->getId(), - $class6->setAttribute('students', [new Document([ - '$id' => 'student6', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Student 6', - ])]) - ); - - $this->assertEquals('Student 6', $class6->getAttribute('students')[0]->getAttribute('name')); - $class6 = static::getDatabase()->getDocument('classes', 'class6'); - $this->assertEquals('Student 6', $class6->getAttribute('students')[0]->getAttribute('name')); - - // Update document with new related document - static::getDatabase()->updateDocument( - 'students', - $student1->getId(), - $student1->setAttribute('classes', ['class2']) - ); - - $class1 = static::getDatabase()->getDocument('classes', 'class1'); - - // Update inverse document - static::getDatabase()->updateDocument( - 'classes', - $class1->getId(), - $class1->setAttribute('students', ['student1']) - ); - - // Rename relationship keys on both sides - static::getDatabase()->updateRelationship( - 'students', - 'classes', - 'newClasses', - 'newStudents' - ); - - // Get document with new relationship key - $students = static::getDatabase()->getDocument('students', 'student1'); - $classes = $students->getAttribute('newClasses'); - $this->assertEquals('class2', $classes[0]['$id']); - - // Get inverse document with new relationship key - $class = static::getDatabase()->getDocument('classes', 'class1'); - $students = $class->getAttribute('newStudents'); - $this->assertEquals('student1', $students[0]['$id']); - - // Create new document with no relationship - static::getDatabase()->createDocument('students', new Document([ - '$id' => 'student7', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'name' => 'Student 7', - ])); - - // Can delete document with no relationship when on delete is set to restrict - $deleted = static::getDatabase()->deleteDocument('students', 'student7'); - $this->assertEquals(true, $deleted); - - $student6 = static::getDatabase()->getDocument('students', 'student7'); - $this->assertEquals(true, $student6->isEmpty()); - - // Try to delete document while still related to another with on delete: restrict - try { - static::getDatabase()->deleteDocument('students', 'student1'); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); - } - - // Change on delete to set null - static::getDatabase()->updateRelationship( - collection: 'students', - id: 'newClasses', - onDelete: Database::RELATION_MUTATE_SET_NULL - ); - - $student1 = static::getDatabase()->getDocument('students', 'student1'); - - // Reset relationships - static::getDatabase()->updateDocument( - 'students', - $student1->getId(), - $student1->setAttribute('newClasses', ['class1']) - ); - - // Delete child, will delete junction - static::getDatabase()->deleteDocument('classes', 'class1'); - - // Check relation was set to null - $student1 = static::getDatabase()->getDocument('students', 'student1'); - $this->assertEquals(0, \count($student1->getAttribute('newClasses'))); - - // Change on delete to cascade - static::getDatabase()->updateRelationship( - collection: 'students', - id: 'newClasses', - onDelete: Database::RELATION_MUTATE_CASCADE - ); - - // Delete parent, will delete child - static::getDatabase()->deleteDocument('students', 'student2'); - - // Check parent and child were deleted - $library = static::getDatabase()->getDocument('students', 'student2'); - $this->assertEquals(true, $library->isEmpty()); - - // Delete child, should not delete parent - static::getDatabase()->deleteDocument('classes', 'class6'); - - // Check only child was deleted - $student6 = static::getDatabase()->getDocument('students', 'student6'); - $this->assertEquals(false, $student6->isEmpty()); - $this->assertEmpty($student6->getAttribute('newClasses')); - - $library = static::getDatabase()->getDocument('classes', 'class2'); - $this->assertEquals(true, $library->isEmpty()); - - // Delete relationship - static::getDatabase()->deleteRelationship( - 'students', - 'newClasses' - ); - - // Try to get documents again - $student = static::getDatabase()->getDocument('students', 'student1'); - $classes = $student->getAttribute('newClasses'); - $this->assertEquals(null, $classes); - - // Try to get inverse documents again - $classes = static::getDatabase()->getDocument('classes', 'class1'); - $students = $classes->getAttribute('newStudents'); - $this->assertEquals(null, $students); - } - - public function testSelectRelationshipAttributes(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('make'); - static::getDatabase()->createCollection('model'); - - static::getDatabase()->createAttribute('make', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('model', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('model', 'year', Database::VAR_INTEGER, 0, true); - - static::getDatabase()->createRelationship( - collection: 'make', - relatedCollection: 'model', - type: Database::RELATION_ONE_TO_MANY, - id: 'models' - ); - - static::getDatabase()->createDocument('make', new Document([ - '$id' => 'ford', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Ford', - 'models' => [ - [ - '$id' => 'fiesta', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Fiesta', - 'year' => 2010, - ], - [ - '$id' => 'focus', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Focus', - 'year' => 2011, - ], - ], - ])); - - // Select some parent attributes, some child attributes - $make = static::getDatabase()->findOne('make', [ - Query::select(['name', 'models.name']), - ]); - - if (!$make instanceof Document) { - throw new Exception('Make not found'); - } - - $this->assertEquals('Ford', $make['name']); - $this->assertEquals(2, \count($make['models'])); - $this->assertEquals('Fiesta', $make['models'][0]['name']); - $this->assertEquals('Focus', $make['models'][1]['name']); - $this->assertArrayNotHasKey('year', $make['models'][0]); - $this->assertArrayNotHasKey('year', $make['models'][1]); - $this->assertArrayNotHasKey('$id', $make); - $this->assertArrayNotHasKey('$internalId', $make); - $this->assertArrayNotHasKey('$permissions', $make); - $this->assertArrayNotHasKey('$collection', $make); - $this->assertArrayNotHasKey('$createdAt', $make); - $this->assertArrayNotHasKey('$updatedAt', $make); - - // Select internal attributes - $make = static::getDatabase()->findOne('make', [ - Query::select(['name', '$id']), - ]); - - if (!$make instanceof Document) { - throw new Exception('Make not found'); - } - - $this->assertArrayHasKey('$id', $make); - $this->assertArrayNotHasKey('$internalId', $make); - $this->assertArrayNotHasKey('$collection', $make); - $this->assertArrayNotHasKey('$createdAt', $make); - $this->assertArrayNotHasKey('$updatedAt', $make); - $this->assertArrayNotHasKey('$permissions', $make); - - $make = static::getDatabase()->findOne('make', [ - Query::select(['name', '$internalId']), - ]); - - if (!$make instanceof Document) { - throw new Exception('Make not found'); - } - - $this->assertArrayNotHasKey('$id', $make); - $this->assertArrayHasKey('$internalId', $make); - $this->assertArrayNotHasKey('$collection', $make); - $this->assertArrayNotHasKey('$createdAt', $make); - $this->assertArrayNotHasKey('$updatedAt', $make); - $this->assertArrayNotHasKey('$permissions', $make); - - $make = static::getDatabase()->findOne('make', [ - Query::select(['name', '$collection']), - ]); - - if (!$make instanceof Document) { - throw new Exception('Make not found'); - } - - $this->assertArrayNotHasKey('$id', $make); - $this->assertArrayNotHasKey('$internalId', $make); - $this->assertArrayHasKey('$collection', $make); - $this->assertArrayNotHasKey('$createdAt', $make); - $this->assertArrayNotHasKey('$updatedAt', $make); - $this->assertArrayNotHasKey('$permissions', $make); - - $make = static::getDatabase()->findOne('make', [ - Query::select(['name', '$createdAt']), - ]); - - if (!$make instanceof Document) { - throw new Exception('Make not found'); - } - - $this->assertArrayNotHasKey('$id', $make); - $this->assertArrayNotHasKey('$internalId', $make); - $this->assertArrayNotHasKey('$collection', $make); - $this->assertArrayHasKey('$createdAt', $make); - $this->assertArrayNotHasKey('$updatedAt', $make); - $this->assertArrayNotHasKey('$permissions', $make); - - $make = static::getDatabase()->findOne('make', [ - Query::select(['name', '$updatedAt']), - ]); - - if (!$make instanceof Document) { - throw new Exception('Make not found'); - } - - $this->assertArrayNotHasKey('$id', $make); - $this->assertArrayNotHasKey('$internalId', $make); - $this->assertArrayNotHasKey('$collection', $make); - $this->assertArrayNotHasKey('$createdAt', $make); - $this->assertArrayHasKey('$updatedAt', $make); - $this->assertArrayNotHasKey('$permissions', $make); - - $make = static::getDatabase()->findOne('make', [ - Query::select(['name', '$permissions']), - ]); - - if (!$make instanceof Document) { - throw new Exception('Make not found'); - } - - $this->assertArrayNotHasKey('$id', $make); - $this->assertArrayNotHasKey('$internalId', $make); - $this->assertArrayNotHasKey('$collection', $make); - $this->assertArrayNotHasKey('$createdAt', $make); - $this->assertArrayNotHasKey('$updatedAt', $make); - $this->assertArrayHasKey('$permissions', $make); - - // Select all parent attributes, some child attributes - $make = static::getDatabase()->findOne('make', [ - Query::select(['*', 'models.year']), - ]); - - if (!$make instanceof Document) { - throw new Exception('Make not found'); - } - - $this->assertEquals('Ford', $make['name']); - $this->assertEquals(2, \count($make['models'])); - $this->assertArrayNotHasKey('name', $make['models'][0]); - $this->assertArrayNotHasKey('name', $make['models'][1]); - $this->assertEquals(2010, $make['models'][0]['year']); - $this->assertEquals(2011, $make['models'][1]['year']); - - // Select all parent attributes, all child attributes - $make = static::getDatabase()->findOne('make', [ - Query::select(['*', 'models.*']), - ]); - - if (!$make instanceof Document) { - throw new Exception('Make not found'); - } - - $this->assertEquals('Ford', $make['name']); - $this->assertEquals(2, \count($make['models'])); - $this->assertEquals('Fiesta', $make['models'][0]['name']); - $this->assertEquals('Focus', $make['models'][1]['name']); - $this->assertEquals(2010, $make['models'][0]['year']); - $this->assertEquals(2011, $make['models'][1]['year']); - - // Select all parent attributes, all child attributes - // Must select parent if selecting children - $make = static::getDatabase()->findOne('make', [ - Query::select(['models.*']), - ]); - - if (!$make instanceof Document) { - throw new Exception('Make not found'); - } - - $this->assertEquals('Ford', $make['name']); - $this->assertEquals(2, \count($make['models'])); - $this->assertEquals('Fiesta', $make['models'][0]['name']); - $this->assertEquals('Focus', $make['models'][1]['name']); - $this->assertEquals(2010, $make['models'][0]['year']); - $this->assertEquals(2011, $make['models'][1]['year']); - - // Select all parent attributes, no child attributes - $make = static::getDatabase()->findOne('make', [ - Query::select(['name']), - ]); - - if (!$make instanceof Document) { - throw new Exception('Make not found'); - } - - $this->assertEquals('Ford', $make['name']); - $this->assertArrayNotHasKey('models', $make); - } - - public function testNestedOneToOne_OneToOneRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('pattern'); - static::getDatabase()->createCollection('shirt'); - static::getDatabase()->createCollection('team'); - - static::getDatabase()->createAttribute('pattern', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('shirt', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('team', 'name', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'pattern', - relatedCollection: 'shirt', - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - id: 'shirt', - twoWayKey: 'pattern' - ); - static::getDatabase()->createRelationship( - collection: 'shirt', - relatedCollection: 'team', - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - id: 'team', - twoWayKey: 'shirt' - ); - - static::getDatabase()->createDocument('pattern', new Document([ - '$id' => 'stripes', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Stripes', - 'shirt' => [ - '$id' => 'red', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Red', - 'team' => [ - '$id' => 'reds', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Reds', - ], - ], - ])); - - $pattern = static::getDatabase()->getDocument('pattern', 'stripes'); - $this->assertEquals('red', $pattern['shirt']['$id']); - $this->assertArrayNotHasKey('pattern', $pattern['shirt']); - $this->assertEquals('reds', $pattern['shirt']['team']['$id']); - $this->assertArrayNotHasKey('shirt', $pattern['shirt']['team']); - - static::getDatabase()->createDocument('team', new Document([ - '$id' => 'blues', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Blues', - 'shirt' => [ - '$id' => 'blue', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Blue', - 'pattern' => [ - '$id' => 'plain', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Plain', - ], - ], - ])); - - $team = static::getDatabase()->getDocument('team', 'blues'); - $this->assertEquals('blue', $team['shirt']['$id']); - $this->assertArrayNotHasKey('team', $team['shirt']); - $this->assertEquals('plain', $team['shirt']['pattern']['$id']); - $this->assertArrayNotHasKey('shirt', $team['shirt']['pattern']); - } - - public function testNestedOneToOne_OneToManyRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('teachers'); - static::getDatabase()->createCollection('classrooms'); - static::getDatabase()->createCollection('children'); - - static::getDatabase()->createAttribute('children', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('teachers', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('classrooms', 'name', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'teachers', - relatedCollection: 'classrooms', - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - id: 'classroom', - twoWayKey: 'teacher' - ); - static::getDatabase()->createRelationship( - collection: 'classrooms', - relatedCollection: 'children', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - twoWayKey: 'classroom' - ); - - static::getDatabase()->createDocument('teachers', new Document([ - '$id' => 'teacher1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Teacher 1', - 'classroom' => [ - '$id' => 'classroom1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Classroom 1', - 'children' => [ - [ - '$id' => 'child1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Child 1', - ], - [ - '$id' => 'child2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Child 2', - ], - ], - ], - ])); - - $teacher1 = static::getDatabase()->getDocument('teachers', 'teacher1'); - $this->assertEquals('classroom1', $teacher1['classroom']['$id']); - $this->assertArrayNotHasKey('teacher', $teacher1['classroom']); - $this->assertEquals(2, \count($teacher1['classroom']['children'])); - $this->assertEquals('Child 1', $teacher1['classroom']['children'][0]['name']); - $this->assertEquals('Child 2', $teacher1['classroom']['children'][1]['name']); - - static::getDatabase()->createDocument('children', new Document([ - '$id' => 'child3', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Child 3', - 'classroom' => [ - '$id' => 'classroom2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Classroom 2', - 'teacher' => [ - '$id' => 'teacher2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Teacher 2', - ], - ], - ])); - - $child3 = static::getDatabase()->getDocument('children', 'child3'); - $this->assertEquals('classroom2', $child3['classroom']['$id']); - $this->assertArrayNotHasKey('children', $child3['classroom']); - $this->assertEquals('teacher2', $child3['classroom']['teacher']['$id']); - $this->assertArrayNotHasKey('classroom', $child3['classroom']['teacher']); - } - - public function testNestedOneToOne_ManyToOneRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('users'); - static::getDatabase()->createCollection('profiles'); - static::getDatabase()->createCollection('avatars'); - - static::getDatabase()->createAttribute('users', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('profiles', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('avatars', 'name', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'users', - relatedCollection: 'profiles', - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - id: 'profile', - twoWayKey: 'user' - ); - static::getDatabase()->createRelationship( - collection: 'profiles', - relatedCollection: 'avatars', - type: Database::RELATION_MANY_TO_ONE, - twoWay: true, - id: 'avatar', - ); - - static::getDatabase()->createDocument('users', new Document([ - '$id' => 'user1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'User 1', - 'profile' => [ - '$id' => 'profile1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Profile 1', - 'avatar' => [ - '$id' => 'avatar1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Avatar 1', - ], - ], - ])); - - $user1 = static::getDatabase()->getDocument('users', 'user1'); - $this->assertEquals('profile1', $user1['profile']['$id']); - $this->assertArrayNotHasKey('user', $user1['profile']); - $this->assertEquals('avatar1', $user1['profile']['avatar']['$id']); - $this->assertArrayNotHasKey('profile', $user1['profile']['avatar']); - - static::getDatabase()->createDocument('avatars', new Document([ - '$id' => 'avatar2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Avatar 2', - 'profiles' => [ - [ - '$id' => 'profile2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Profile 2', - 'user' => [ - '$id' => 'user2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'User 2', - ], - ] - ], - ])); - - $avatar2 = static::getDatabase()->getDocument('avatars', 'avatar2'); - $this->assertEquals('profile2', $avatar2['profiles'][0]['$id']); - $this->assertArrayNotHasKey('avatars', $avatar2['profiles'][0]); - $this->assertEquals('user2', $avatar2['profiles'][0]['user']['$id']); - $this->assertArrayNotHasKey('profiles', $avatar2['profiles'][0]['user']); - } - - public function testNestedOneToOne_ManyToManyRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('addresses'); - static::getDatabase()->createCollection('houses'); - static::getDatabase()->createCollection('buildings'); - - static::getDatabase()->createAttribute('addresses', 'street', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('houses', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('buildings', 'name', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'addresses', - relatedCollection: 'houses', - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - id: 'house', - twoWayKey: 'address' - ); - static::getDatabase()->createRelationship( - collection: 'houses', - relatedCollection: 'buildings', - type: Database::RELATION_MANY_TO_MANY, - twoWay: true, - ); - - static::getDatabase()->createDocument('addresses', new Document([ - '$id' => 'address1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'street' => 'Street 1', - 'house' => [ - '$id' => 'house1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'House 1', - 'buildings' => [ - [ - '$id' => 'building1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Building 1', - ], - [ - '$id' => 'building2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Building 2', - ], - ], - ], - ])); - - $address1 = static::getDatabase()->getDocument('addresses', 'address1'); - $this->assertEquals('house1', $address1['house']['$id']); - $this->assertArrayNotHasKey('address', $address1['house']); - $this->assertEquals('building1', $address1['house']['buildings'][0]['$id']); - $this->assertEquals('building2', $address1['house']['buildings'][1]['$id']); - $this->assertArrayNotHasKey('houses', $address1['house']['buildings'][0]); - $this->assertArrayNotHasKey('houses', $address1['house']['buildings'][1]); - - static::getDatabase()->createDocument('buildings', new Document([ - '$id' => 'building3', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Building 3', - 'houses' => [ - [ - '$id' => 'house2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'House 2', - 'address' => [ - '$id' => 'address2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'street' => 'Street 2', - ], - ], - ], - ])); - } - - public function testNestedOneToMany_OneToOneRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('countries'); - static::getDatabase()->createCollection('cities'); - static::getDatabase()->createCollection('mayors'); - - static::getDatabase()->createAttribute('cities', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('countries', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('mayors', 'name', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'countries', - relatedCollection: 'cities', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - twoWayKey: 'country' - ); - static::getDatabase()->createRelationship( - collection: 'cities', - relatedCollection: 'mayors', - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - id: 'mayor', - twoWayKey: 'city' - ); - - static::getDatabase()->createDocument('countries', new Document([ - '$id' => 'country1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - ], - 'name' => 'Country 1', - 'cities' => [ - [ - '$id' => 'city1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - ], - 'name' => 'City 1', - 'mayor' => [ - '$id' => 'mayor1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - ], - 'name' => 'Mayor 1', - ], - ], - [ - '$id' => 'city2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'City 2', - 'mayor' => [ - '$id' => 'mayor2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Mayor 2', - ], - ], - ], - ])); - - $documents = static::getDatabase()->find('countries', [ - Query::limit(1) - ]); - $this->assertEquals('Mayor 1', $documents[0]['cities'][0]['mayor']['name']); - - $documents = static::getDatabase()->find('countries', [ - Query::select(['name']), - Query::limit(1) - ]); - $this->assertArrayHasKey('name', $documents[0]); - $this->assertArrayNotHasKey('cities', $documents[0]); - - $documents = static::getDatabase()->find('countries', [ - Query::select(['*']), - Query::limit(1) - ]); - $this->assertArrayHasKey('name', $documents[0]); - $this->assertArrayNotHasKey('cities', $documents[0]); - - $documents = static::getDatabase()->find('countries', [ - Query::select(['*', 'cities.*', 'cities.mayor.*']), - Query::limit(1) - ]); - - $this->assertEquals('Mayor 1', $documents[0]['cities'][0]['mayor']['name']); - - // Insert docs to cache: - $country1 = static::getDatabase()->getDocument('countries', 'country1'); - $mayor1 = static::getDatabase()->getDocument('mayors', 'mayor1'); - $this->assertEquals('City 1', $mayor1['city']['name']); - $this->assertEquals('City 1', $country1['cities'][0]['name']); - - static::getDatabase()->updateDocument('cities', 'city1', new Document([ - '$id' => 'city1', - '$collection' => 'cities', - 'name' => 'City 1 updated', - 'mayor' => 'mayor1', // we don't support partial updates at the moment - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - ], - ])); - - $mayor1 = static::getDatabase()->getDocument('mayors', 'mayor1'); - $country1 = static::getDatabase()->getDocument('countries', 'country1'); - - $this->assertEquals('City 1 updated', $mayor1['city']['name']); - $this->assertEquals('City 1 updated', $country1['cities'][0]['name']); - $this->assertEquals('city1', $country1['cities'][0]['$id']); - $this->assertEquals('city2', $country1['cities'][1]['$id']); - $this->assertEquals('mayor1', $country1['cities'][0]['mayor']['$id']); - $this->assertEquals('mayor2', $country1['cities'][1]['mayor']['$id']); - $this->assertArrayNotHasKey('city', $country1['cities'][0]['mayor']); - $this->assertArrayNotHasKey('city', $country1['cities'][1]['mayor']); - - static::getDatabase()->createDocument('mayors', new Document([ - '$id' => 'mayor3', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Mayor 3', - 'city' => [ - '$id' => 'city3', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'City 3', - 'country' => [ - '$id' => 'country2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Country 2', - ], - ], - ])); - - $country2 = static::getDatabase()->getDocument('countries', 'country2'); - $this->assertEquals('city3', $country2['cities'][0]['$id']); - $this->assertEquals('mayor3', $country2['cities'][0]['mayor']['$id']); - $this->assertArrayNotHasKey('country', $country2['cities'][0]); - $this->assertArrayNotHasKey('city', $country2['cities'][0]['mayor']); - } - - public function testNestedOneToMany_OneToManyRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('dormitories'); - static::getDatabase()->createCollection('occupants'); - static::getDatabase()->createCollection('pets'); - - static::getDatabase()->createAttribute('dormitories', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('occupants', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('pets', 'name', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'dormitories', - relatedCollection: 'occupants', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - twoWayKey: 'dormitory' - ); - static::getDatabase()->createRelationship( - collection: 'occupants', - relatedCollection: 'pets', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - twoWayKey: 'occupant' - ); - - static::getDatabase()->createDocument('dormitories', new Document([ - '$id' => 'dormitory1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'House 1', - 'occupants' => [ - [ - '$id' => 'occupant1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Occupant 1', - 'pets' => [ - [ - '$id' => 'pet1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Pet 1', - ], - [ - '$id' => 'pet2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Pet 2', - ], - ], - ], - [ - '$id' => 'occupant2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Occupant 2', - 'pets' => [ - [ - '$id' => 'pet3', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Pet 3', - ], - [ - '$id' => 'pet4', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Pet 4', - ], - ], - ], - ], - ])); - - $dormitory1 = static::getDatabase()->getDocument('dormitories', 'dormitory1'); - $this->assertEquals('occupant1', $dormitory1['occupants'][0]['$id']); - $this->assertEquals('occupant2', $dormitory1['occupants'][1]['$id']); - $this->assertEquals('pet1', $dormitory1['occupants'][0]['pets'][0]['$id']); - $this->assertEquals('pet2', $dormitory1['occupants'][0]['pets'][1]['$id']); - $this->assertEquals('pet3', $dormitory1['occupants'][1]['pets'][0]['$id']); - $this->assertEquals('pet4', $dormitory1['occupants'][1]['pets'][1]['$id']); - $this->assertArrayNotHasKey('dormitory', $dormitory1['occupants'][0]); - $this->assertArrayNotHasKey('dormitory', $dormitory1['occupants'][1]); - $this->assertArrayNotHasKey('occupant', $dormitory1['occupants'][0]['pets'][0]); - $this->assertArrayNotHasKey('occupant', $dormitory1['occupants'][0]['pets'][1]); - $this->assertArrayNotHasKey('occupant', $dormitory1['occupants'][1]['pets'][0]); - $this->assertArrayNotHasKey('occupant', $dormitory1['occupants'][1]['pets'][1]); - - static::getDatabase()->createDocument('pets', new Document([ - '$id' => 'pet5', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Pet 5', - 'occupant' => [ - '$id' => 'occupant3', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Occupant 3', - 'dormitory' => [ - '$id' => 'dormitory2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'House 2', - ], - ], - ])); - - $pet5 = static::getDatabase()->getDocument('pets', 'pet5'); - $this->assertEquals('occupant3', $pet5['occupant']['$id']); - $this->assertEquals('dormitory2', $pet5['occupant']['dormitory']['$id']); - $this->assertArrayNotHasKey('pets', $pet5['occupant']); - $this->assertArrayNotHasKey('occupant', $pet5['occupant']['dormitory']); - } - - public function testNestedOneToMany_ManyToOneRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('home'); - static::getDatabase()->createCollection('renters'); - static::getDatabase()->createCollection('floors'); - - static::getDatabase()->createAttribute('home', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('renters', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('floors', 'name', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'home', - relatedCollection: 'renters', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true - ); - static::getDatabase()->createRelationship( - collection: 'renters', - relatedCollection: 'floors', - type: Database::RELATION_MANY_TO_ONE, - twoWay: true, - id: 'floor' - ); - - static::getDatabase()->createDocument('home', new Document([ - '$id' => 'home1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'House 1', - 'renters' => [ - [ - '$id' => 'renter1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Occupant 1', - 'floor' => [ - '$id' => 'floor1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Floor 1', - ], - ], - ], - ])); - - $home1 = static::getDatabase()->getDocument('home', 'home1'); - $this->assertEquals('renter1', $home1['renters'][0]['$id']); - $this->assertEquals('floor1', $home1['renters'][0]['floor']['$id']); - $this->assertArrayNotHasKey('home', $home1['renters'][0]); - $this->assertArrayNotHasKey('renters', $home1['renters'][0]['floor']); - - static::getDatabase()->createDocument('floors', new Document([ - '$id' => 'floor2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Floor 2', - 'renters' => [ - [ - '$id' => 'renter2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Occupant 2', - 'home' => [ - '$id' => 'home2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'House 2', - ], - ], - ], - ])); - - $floor2 = static::getDatabase()->getDocument('floors', 'floor2'); - $this->assertEquals('renter2', $floor2['renters'][0]['$id']); - $this->assertArrayNotHasKey('floor', $floor2['renters'][0]); - $this->assertEquals('home2', $floor2['renters'][0]['home']['$id']); - $this->assertArrayNotHasKey('renter', $floor2['renters'][0]['home']); - } - - public function testNestedOneToMany_ManyToManyRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('owners'); - static::getDatabase()->createCollection('cats'); - static::getDatabase()->createCollection('toys'); - - static::getDatabase()->createAttribute('owners', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('cats', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('toys', 'name', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'owners', - relatedCollection: 'cats', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - twoWayKey: 'owner' - ); - static::getDatabase()->createRelationship( - collection: 'cats', - relatedCollection: 'toys', - type: Database::RELATION_MANY_TO_MANY, - twoWay: true - ); - - static::getDatabase()->createDocument('owners', new Document([ - '$id' => 'owner1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Owner 1', - 'cats' => [ - [ - '$id' => 'cat1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Pet 1', - 'toys' => [ - [ - '$id' => 'toy1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Toy 1', - ], - ], - ], - ], - ])); - - $owner1 = static::getDatabase()->getDocument('owners', 'owner1'); - $this->assertEquals('cat1', $owner1['cats'][0]['$id']); - $this->assertArrayNotHasKey('owner', $owner1['cats'][0]); - $this->assertEquals('toy1', $owner1['cats'][0]['toys'][0]['$id']); - $this->assertArrayNotHasKey('cats', $owner1['cats'][0]['toys'][0]); - - static::getDatabase()->createDocument('toys', new Document([ - '$id' => 'toy2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Toy 2', - 'cats' => [ - [ - '$id' => 'cat2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Pet 2', - 'owner' => [ - '$id' => 'owner2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Owner 2', - ], - ], - ], - ])); - - $toy2 = static::getDatabase()->getDocument('toys', 'toy2'); - $this->assertEquals('cat2', $toy2['cats'][0]['$id']); - $this->assertArrayNotHasKey('toys', $toy2['cats'][0]); - $this->assertEquals('owner2', $toy2['cats'][0]['owner']['$id']); - $this->assertArrayNotHasKey('cats', $toy2['cats'][0]['owner']); - } - - public function testNestedManyToOne_OneToOneRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('towns'); - static::getDatabase()->createCollection('homelands'); - static::getDatabase()->createCollection('capitals'); - - static::getDatabase()->createAttribute('towns', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('homelands', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('capitals', 'name', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'towns', - relatedCollection: 'homelands', - type: Database::RELATION_MANY_TO_ONE, - twoWay: true, - id: 'homeland' - ); - static::getDatabase()->createRelationship( - collection: 'homelands', - relatedCollection: 'capitals', - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - id: 'capital', - twoWayKey: 'homeland' - ); - - static::getDatabase()->createDocument('towns', new Document([ - '$id' => 'town1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'City 1', - 'homeland' => [ - '$id' => 'homeland1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Country 1', - 'capital' => [ - '$id' => 'capital1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Flag 1', - ], - ], - ])); - - $town1 = static::getDatabase()->getDocument('towns', 'town1'); - $this->assertEquals('homeland1', $town1['homeland']['$id']); - $this->assertArrayNotHasKey('towns', $town1['homeland']); - $this->assertEquals('capital1', $town1['homeland']['capital']['$id']); - $this->assertArrayNotHasKey('homeland', $town1['homeland']['capital']); - - static::getDatabase()->createDocument('capitals', new Document([ - '$id' => 'capital2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Flag 2', - 'homeland' => [ - '$id' => 'homeland2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Country 2', - 'towns' => [ - [ - '$id' => 'town2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Town 2', - ], - [ - '$id' => 'town3', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Town 3', - ], - ], - ], - ])); - - $capital2 = static::getDatabase()->getDocument('capitals', 'capital2'); - $this->assertEquals('homeland2', $capital2['homeland']['$id']); - $this->assertArrayNotHasKey('capital', $capital2['homeland']); - $this->assertEquals(2, \count($capital2['homeland']['towns'])); - $this->assertEquals('town2', $capital2['homeland']['towns'][0]['$id']); - $this->assertEquals('town3', $capital2['homeland']['towns'][1]['$id']); - } - - public function testNestedManyToOne_OneToManyRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('players'); - static::getDatabase()->createCollection('teams'); - static::getDatabase()->createCollection('supporters'); - - static::getDatabase()->createAttribute('players', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('teams', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('supporters', 'name', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'players', - relatedCollection: 'teams', - type: Database::RELATION_MANY_TO_ONE, - twoWay: true, - id: 'team' - ); - static::getDatabase()->createRelationship( - collection: 'teams', - relatedCollection: 'supporters', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - id: 'supporters', - twoWayKey: 'team' - ); - - static::getDatabase()->createDocument('players', new Document([ - '$id' => 'player1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Player 1', - 'team' => [ - '$id' => 'team1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Team 1', - 'supporters' => [ - [ - '$id' => 'supporter1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Supporter 1', - ], - [ - '$id' => 'supporter2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Supporter 2', - ], - ], - ], - ])); - - $player1 = static::getDatabase()->getDocument('players', 'player1'); - $this->assertEquals('team1', $player1['team']['$id']); - $this->assertArrayNotHasKey('players', $player1['team']); - $this->assertEquals(2, \count($player1['team']['supporters'])); - $this->assertEquals('supporter1', $player1['team']['supporters'][0]['$id']); - $this->assertEquals('supporter2', $player1['team']['supporters'][1]['$id']); - - static::getDatabase()->createDocument('supporters', new Document([ - '$id' => 'supporter3', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Supporter 3', - 'team' => [ - '$id' => 'team2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Team 2', - 'players' => [ - [ - '$id' => 'player2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Player 2', - ], - [ - '$id' => 'player3', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Player 3', - ], - ], - ], - ])); - - $supporter3 = static::getDatabase()->getDocument('supporters', 'supporter3'); - $this->assertEquals('team2', $supporter3['team']['$id']); - $this->assertArrayNotHasKey('supporters', $supporter3['team']); - $this->assertEquals(2, \count($supporter3['team']['players'])); - $this->assertEquals('player2', $supporter3['team']['players'][0]['$id']); - $this->assertEquals('player3', $supporter3['team']['players'][1]['$id']); - } - - public function testNestedManyToOne_ManyToOne(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('cows'); - static::getDatabase()->createCollection('farms'); - static::getDatabase()->createCollection('farmer'); - - static::getDatabase()->createAttribute('cows', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('farms', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('farmer', 'name', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'cows', - relatedCollection: 'farms', - type: Database::RELATION_MANY_TO_ONE, - twoWay: true, - id: 'farm' - ); - static::getDatabase()->createRelationship( - collection: 'farms', - relatedCollection: 'farmer', - type: Database::RELATION_MANY_TO_ONE, - twoWay: true, - id: 'farmer' - ); - - static::getDatabase()->createDocument('cows', new Document([ - '$id' => 'cow1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Cow 1', - 'farm' => [ - '$id' => 'farm1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Farm 1', - 'farmer' => [ - '$id' => 'farmer1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Farmer 1', - ], - ], - ])); - - $cow1 = static::getDatabase()->getDocument('cows', 'cow1'); - $this->assertEquals('farm1', $cow1['farm']['$id']); - $this->assertArrayNotHasKey('cows', $cow1['farm']); - $this->assertEquals('farmer1', $cow1['farm']['farmer']['$id']); - $this->assertArrayNotHasKey('farms', $cow1['farm']['farmer']); - - static::getDatabase()->createDocument('farmer', new Document([ - '$id' => 'farmer2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Farmer 2', - 'farms' => [ - [ - '$id' => 'farm2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Farm 2', - 'cows' => [ - [ - '$id' => 'cow2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Cow 2', - ], - [ - '$id' => 'cow3', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Cow 3', - ], - ], - ], - ], - ])); - - $farmer2 = static::getDatabase()->getDocument('farmer', 'farmer2'); - $this->assertEquals('farm2', $farmer2['farms'][0]['$id']); - $this->assertArrayNotHasKey('farmer', $farmer2['farms'][0]); - $this->assertEquals(2, \count($farmer2['farms'][0]['cows'])); - $this->assertEquals('cow2', $farmer2['farms'][0]['cows'][0]['$id']); - $this->assertEquals('cow3', $farmer2['farms'][0]['cows'][1]['$id']); - } - - public function testNestedManyToOne_ManyToManyRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('books'); - static::getDatabase()->createCollection('entrants'); - static::getDatabase()->createCollection('rooms'); - - static::getDatabase()->createAttribute('books', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('entrants', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('rooms', 'name', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'books', - relatedCollection: 'entrants', - type: Database::RELATION_MANY_TO_ONE, - twoWay: true, - id: 'entrant' - ); - static::getDatabase()->createRelationship( - collection: 'entrants', - relatedCollection: 'rooms', - type: Database::RELATION_MANY_TO_MANY, - twoWay: true, - ); - - static::getDatabase()->createDocument('books', new Document([ - '$id' => 'book1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Book 1', - 'entrant' => [ - '$id' => 'entrant1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Entrant 1', - 'rooms' => [ - [ - '$id' => 'class1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Class 1', - ], - [ - '$id' => 'class2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Class 2', - ], - ], - ], - ])); - - $book1 = static::getDatabase()->getDocument('books', 'book1'); - $this->assertEquals('entrant1', $book1['entrant']['$id']); - $this->assertArrayNotHasKey('books', $book1['entrant']); - $this->assertEquals(2, \count($book1['entrant']['rooms'])); - $this->assertEquals('class1', $book1['entrant']['rooms'][0]['$id']); - $this->assertEquals('class2', $book1['entrant']['rooms'][1]['$id']); - } - - public function testNestedManyToMany_OneToOneRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('stones'); - static::getDatabase()->createCollection('hearths'); - static::getDatabase()->createCollection('plots'); - - static::getDatabase()->createAttribute('stones', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('hearths', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('plots', 'name', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'stones', - relatedCollection: 'hearths', - type: Database::RELATION_MANY_TO_MANY, - twoWay: true, - ); - static::getDatabase()->createRelationship( - collection: 'hearths', - relatedCollection: 'plots', - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - id: 'plot', - twoWayKey: 'hearth' - ); - - static::getDatabase()->createDocument('stones', new Document([ - '$id' => 'stone1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Building 1', - 'hearths' => [ - [ - '$id' => 'hearth1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'House 1', - 'plot' => [ - '$id' => 'plot1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Address 1', - ], - ], - [ - '$id' => 'hearth2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'House 2', - 'plot' => [ - '$id' => 'plot2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Address 2', - ], - ], - ], - ])); - - $stone1 = static::getDatabase()->getDocument('stones', 'stone1'); - $this->assertEquals(2, \count($stone1['hearths'])); - $this->assertEquals('hearth1', $stone1['hearths'][0]['$id']); - $this->assertEquals('hearth2', $stone1['hearths'][1]['$id']); - $this->assertArrayNotHasKey('stone', $stone1['hearths'][0]); - $this->assertEquals('plot1', $stone1['hearths'][0]['plot']['$id']); - $this->assertEquals('plot2', $stone1['hearths'][1]['plot']['$id']); - $this->assertArrayNotHasKey('hearth', $stone1['hearths'][0]['plot']); - - static::getDatabase()->createDocument('plots', new Document([ - '$id' => 'plot3', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Address 3', - 'hearth' => [ - '$id' => 'hearth3', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Hearth 3', - 'stones' => [ - [ - '$id' => 'stone2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Stone 2', - ], - ], - ], - ])); - - $plot3 = static::getDatabase()->getDocument('plots', 'plot3'); - $this->assertEquals('hearth3', $plot3['hearth']['$id']); - $this->assertArrayNotHasKey('plot', $plot3['hearth']); - $this->assertEquals('stone2', $plot3['hearth']['stones'][0]['$id']); - $this->assertArrayNotHasKey('hearths', $plot3['hearth']['stones'][0]); - } - - public function testNestedManyToMany_OneToManyRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('groups'); - static::getDatabase()->createCollection('tounaments'); - static::getDatabase()->createCollection('prizes'); - - static::getDatabase()->createAttribute('groups', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('tounaments', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('prizes', 'name', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'groups', - relatedCollection: 'tounaments', - type: Database::RELATION_MANY_TO_MANY, - twoWay: true, - ); - static::getDatabase()->createRelationship( - collection: 'tounaments', - relatedCollection: 'prizes', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - id: 'prizes', - twoWayKey: 'tounament' - ); - - static::getDatabase()->createDocument('groups', new Document([ - '$id' => 'group1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Group 1', - 'tounaments' => [ - [ - '$id' => 'tounament1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Tounament 1', - 'prizes' => [ - [ - '$id' => 'prize1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Prize 1', - ], - [ - '$id' => 'prize2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Prize 2', - ], - ], - ], - [ - '$id' => 'tounament2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Tounament 2', - 'prizes' => [ - [ - '$id' => 'prize3', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Prize 3', - ], - [ - '$id' => 'prize4', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Prize 4', - ], - ], - ], - ], - ])); - - $group1 = static::getDatabase()->getDocument('groups', 'group1'); - $this->assertEquals(2, \count($group1['tounaments'])); - $this->assertEquals('tounament1', $group1['tounaments'][0]['$id']); - $this->assertEquals('tounament2', $group1['tounaments'][1]['$id']); - $this->assertArrayNotHasKey('group', $group1['tounaments'][0]); - $this->assertEquals(2, \count($group1['tounaments'][0]['prizes'])); - $this->assertEquals('prize1', $group1['tounaments'][0]['prizes'][0]['$id']); - $this->assertEquals('prize2', $group1['tounaments'][0]['prizes'][1]['$id']); - $this->assertArrayNotHasKey('tounament', $group1['tounaments'][0]['prizes'][0]); - } - - public function testNestedManyToMany_ManyToOneRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('platforms'); - static::getDatabase()->createCollection('games'); - static::getDatabase()->createCollection('publishers'); - - static::getDatabase()->createAttribute('platforms', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('games', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('publishers', 'name', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'platforms', - relatedCollection: 'games', - type: Database::RELATION_MANY_TO_MANY, - twoWay: true, - ); - static::getDatabase()->createRelationship( - collection: 'games', - relatedCollection: 'publishers', - type: Database::RELATION_MANY_TO_ONE, - twoWay: true, - id: 'publisher', - twoWayKey: 'games' - ); - - static::getDatabase()->createDocument('platforms', new Document([ - '$id' => 'platform1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Platform 1', - 'games' => [ - [ - '$id' => 'game1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Game 1', - 'publisher' => [ - '$id' => 'publisher1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Publisher 1', - ], - ], - [ - '$id' => 'game2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Game 2', - 'publisher' => [ - '$id' => 'publisher2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Publisher 2', - ], - ], - ] - ])); - - $platform1 = static::getDatabase()->getDocument('platforms', 'platform1'); - $this->assertEquals(2, \count($platform1['games'])); - $this->assertEquals('game1', $platform1['games'][0]['$id']); - $this->assertEquals('game2', $platform1['games'][1]['$id']); - $this->assertArrayNotHasKey('platforms', $platform1['games'][0]); - $this->assertEquals('publisher1', $platform1['games'][0]['publisher']['$id']); - $this->assertEquals('publisher2', $platform1['games'][1]['publisher']['$id']); - $this->assertArrayNotHasKey('games', $platform1['games'][0]['publisher']); - - static::getDatabase()->createDocument('publishers', new Document([ - '$id' => 'publisher3', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Publisher 3', - 'games' => [ - [ - '$id' => 'game3', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Game 3', - 'platforms' => [ - [ - '$id' => 'platform2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Platform 2', - ] - ], - ], - ], - ])); - - $publisher3 = static::getDatabase()->getDocument('publishers', 'publisher3'); - $this->assertEquals(1, \count($publisher3['games'])); - $this->assertEquals('game3', $publisher3['games'][0]['$id']); - $this->assertArrayNotHasKey('publisher', $publisher3['games'][0]); - $this->assertEquals('platform2', $publisher3['games'][0]['platforms'][0]['$id']); - $this->assertArrayNotHasKey('games', $publisher3['games'][0]['platforms'][0]); - } - - public function testNestedManyToMany_ManyToManyRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('sauces'); - static::getDatabase()->createCollection('pizzas'); - static::getDatabase()->createCollection('toppings'); - - static::getDatabase()->createAttribute('sauces', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('pizzas', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('toppings', 'name', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'sauces', - relatedCollection: 'pizzas', - type: Database::RELATION_MANY_TO_MANY, - twoWay: true, - ); - static::getDatabase()->createRelationship( - collection: 'pizzas', - relatedCollection: 'toppings', - type: Database::RELATION_MANY_TO_MANY, - twoWay: true, - id: 'toppings', - twoWayKey: 'pizzas' - ); - - static::getDatabase()->createDocument('sauces', new Document([ - '$id' => 'sauce1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Sauce 1', - 'pizzas' => [ - [ - '$id' => 'pizza1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Pizza 1', - 'toppings' => [ - [ - '$id' => 'topping1', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Topping 1', - ], - [ - '$id' => 'topping2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Topping 2', - ], - ], - ], - [ - '$id' => 'pizza2', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Pizza 2', - 'toppings' => [ - [ - '$id' => 'topping3', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Topping 3', - ], - [ - '$id' => 'topping4', - '$permissions' => [ - Permission::read(Role::any()), - ], - 'name' => 'Topping 4', - ], - ], - ], - ] - ])); - - $sauce1 = static::getDatabase()->getDocument('sauces', 'sauce1'); - $this->assertEquals(2, \count($sauce1['pizzas'])); - $this->assertEquals('pizza1', $sauce1['pizzas'][0]['$id']); - $this->assertEquals('pizza2', $sauce1['pizzas'][1]['$id']); - $this->assertArrayNotHasKey('sauces', $sauce1['pizzas'][0]); - $this->assertEquals(2, \count($sauce1['pizzas'][0]['toppings'])); - $this->assertEquals('topping1', $sauce1['pizzas'][0]['toppings'][0]['$id']); - $this->assertEquals('topping2', $sauce1['pizzas'][0]['toppings'][1]['$id']); - $this->assertArrayNotHasKey('pizzas', $sauce1['pizzas'][0]['toppings'][0]); - $this->assertEquals(2, \count($sauce1['pizzas'][1]['toppings'])); - $this->assertEquals('topping3', $sauce1['pizzas'][1]['toppings'][0]['$id']); - $this->assertEquals('topping4', $sauce1['pizzas'][1]['toppings'][1]['$id']); - $this->assertArrayNotHasKey('pizzas', $sauce1['pizzas'][1]['toppings'][0]); - } - - public function testInheritRelationshipPermissions(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('lawns', permissions: [Permission::create(Role::any())], documentSecurity: true); - static::getDatabase()->createCollection('trees', permissions: [Permission::create(Role::any())], documentSecurity: true); - static::getDatabase()->createCollection('birds', permissions: [Permission::create(Role::any())], documentSecurity: true); - - static::getDatabase()->createAttribute('lawns', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('trees', 'name', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('birds', 'name', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'lawns', - relatedCollection: 'trees', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - twoWayKey: 'lawn', - onDelete: Database::RELATION_MUTATE_CASCADE, - ); - static::getDatabase()->createRelationship( - collection: 'trees', - relatedCollection: 'birds', - type: Database::RELATION_MANY_TO_MANY, - twoWay: true, - onDelete: Database::RELATION_MUTATE_SET_NULL, - ); - - $permissions = [ - Permission::read(Role::any()), - Permission::read(Role::user('user1')), - Permission::update(Role::user('user1')), - Permission::delete(Role::user('user2')), - ]; - - static::getDatabase()->createDocument('lawns', new Document([ - '$id' => 'lawn1', - '$permissions' => $permissions, - 'name' => 'Lawn 1', - 'trees' => [ - [ - '$id' => 'tree1', - 'name' => 'Tree 1', - 'birds' => [ - [ - '$id' => 'bird1', - 'name' => 'Bird 1', - ], - [ - '$id' => 'bird2', - 'name' => 'Bird 2', - ], - ], - ], - ], - ])); - - $lawn1 = static::getDatabase()->getDocument('lawns', 'lawn1'); - $this->assertEquals($permissions, $lawn1->getPermissions()); - $this->assertEquals($permissions, $lawn1['trees'][0]->getPermissions()); - $this->assertEquals($permissions, $lawn1['trees'][0]['birds'][0]->getPermissions()); - $this->assertEquals($permissions, $lawn1['trees'][0]['birds'][1]->getPermissions()); - - $tree1 = static::getDatabase()->getDocument('trees', 'tree1'); - $this->assertEquals($permissions, $tree1->getPermissions()); - $this->assertEquals($permissions, $tree1['lawn']->getPermissions()); - $this->assertEquals($permissions, $tree1['birds'][0]->getPermissions()); - $this->assertEquals($permissions, $tree1['birds'][1]->getPermissions()); - } - - /** - * @depends testInheritRelationshipPermissions - */ - public function testEnforceRelationshipPermissions(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - Authorization::cleanRoles(); - Authorization::setRole(Role::any()->toString()); - $lawn1 = static::getDatabase()->getDocument('lawns', 'lawn1'); - $this->assertEquals('Lawn 1', $lawn1['name']); - - // Try update root document - try { - static::getDatabase()->updateDocument( - 'lawns', - $lawn1->getId(), - $lawn1->setAttribute('name', 'Lawn 1 Updated') - ); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertEquals('Missing "update" permission for role "user:user1". Only "["any"]" scopes are allowed and "["user:user1"]" was given.', $e->getMessage()); - } - - // Try delete root document - try { - static::getDatabase()->deleteDocument( - 'lawns', - $lawn1->getId(), - ); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertEquals('Missing "delete" permission for role "user:user2". Only "["any"]" scopes are allowed and "["user:user2"]" was given.', $e->getMessage()); - } - - $tree1 = static::getDatabase()->getDocument('trees', 'tree1'); - - // Try update nested document - try { - static::getDatabase()->updateDocument( - 'trees', - $tree1->getId(), - $tree1->setAttribute('name', 'Tree 1 Updated') - ); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertEquals('Missing "update" permission for role "user:user1". Only "["any"]" scopes are allowed and "["user:user1"]" was given.', $e->getMessage()); - } - - // Try delete nested document - try { - static::getDatabase()->deleteDocument( - 'trees', - $tree1->getId(), - ); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertEquals('Missing "delete" permission for role "user:user2". Only "["any"]" scopes are allowed and "["user:user2"]" was given.', $e->getMessage()); - } - - $bird1 = static::getDatabase()->getDocument('birds', 'bird1'); - - // Try update multi-level nested document - try { - static::getDatabase()->updateDocument( - 'birds', - $bird1->getId(), - $bird1->setAttribute('name', 'Bird 1 Updated') - ); - $this->fail('Failed to throw exception when updating document with missing permissions'); - } catch (Exception $e) { - $this->assertEquals('Missing "update" permission for role "user:user1". Only "["any"]" scopes are allowed and "["user:user1"]" was given.', $e->getMessage()); - } - - // Try delete multi-level nested document - try { - static::getDatabase()->deleteDocument( - 'birds', - $bird1->getId(), - ); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertEquals('Missing "delete" permission for role "user:user2". Only "["any"]" scopes are allowed and "["user:user2"]" was given.', $e->getMessage()); - } - - Authorization::setRole(Role::user('user1')->toString()); - - $bird1 = static::getDatabase()->getDocument('birds', 'bird1'); - - // Try update multi-level nested document - $bird1 = static::getDatabase()->updateDocument( - 'birds', - $bird1->getId(), - $bird1->setAttribute('name', 'Bird 1 Updated') - ); - - $this->assertEquals('Bird 1 Updated', $bird1['name']); - - Authorization::setRole(Role::user('user2')->toString()); - - // Try delete multi-level nested document - $deleted = static::getDatabase()->deleteDocument( - 'birds', - $bird1->getId(), - ); - - $this->assertEquals(true, $deleted); - $tree1 = static::getDatabase()->getDocument('trees', 'tree1'); - $this->assertEquals(1, count($tree1['birds'])); - - // Try update nested document - $tree1 = static::getDatabase()->updateDocument( - 'trees', - $tree1->getId(), - $tree1->setAttribute('name', 'Tree 1 Updated') - ); - - $this->assertEquals('Tree 1 Updated', $tree1['name']); - - // Try delete nested document - $deleted = static::getDatabase()->deleteDocument( - 'trees', - $tree1->getId(), - ); - - $this->assertEquals(true, $deleted); - $lawn1 = static::getDatabase()->getDocument('lawns', 'lawn1'); - $this->assertEquals(0, count($lawn1['trees'])); - - // Create document with no permissions - static::getDatabase()->createDocument('lawns', new Document([ - '$id' => 'lawn2', - 'name' => 'Lawn 2', - 'trees' => [ - [ - '$id' => 'tree2', - 'name' => 'Tree 2', - 'birds' => [ - [ - '$id' => 'bird3', - 'name' => 'Bird 3', - ], - ], - ], - ], - ])); - - $lawn2 = static::getDatabase()->getDocument('lawns', 'lawn2'); - $this->assertEquals(true, $lawn2->isEmpty()); - - $tree2 = static::getDatabase()->getDocument('trees', 'tree2'); - $this->assertEquals(true, $tree2->isEmpty()); - - $bird3 = static::getDatabase()->getDocument('birds', 'bird3'); - $this->assertEquals(true, $bird3->isEmpty()); - } - - public function testExceedMaxDepthOneToMany(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - $level1Collection = 'level1OneToMany'; - $level2Collection = 'level2OneToMany'; - $level3Collection = 'level3OneToMany'; - $level4Collection = 'level4OneToMany'; - - static::getDatabase()->createCollection($level1Collection); - static::getDatabase()->createCollection($level2Collection); - static::getDatabase()->createCollection($level3Collection); - static::getDatabase()->createCollection($level4Collection); - - static::getDatabase()->createRelationship( - collection: $level1Collection, - relatedCollection: $level2Collection, - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - ); - static::getDatabase()->createRelationship( - collection: $level2Collection, - relatedCollection: $level3Collection, - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - ); - static::getDatabase()->createRelationship( - collection: $level3Collection, - relatedCollection: $level4Collection, - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - ); - - // Exceed create depth - $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ - '$id' => 'level1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - ], - $level2Collection => [ - [ - '$id' => 'level2', - $level3Collection => [ - [ - '$id' => 'level3', - $level4Collection => [ - [ - '$id' => 'level4', - ], - ], - ], - ], - ], - ], - ])); - $this->assertEquals(1, count($level1[$level2Collection])); - $this->assertEquals('level2', $level1[$level2Collection][0]->getId()); - $this->assertEquals(1, count($level1[$level2Collection][0][$level3Collection])); - $this->assertEquals('level3', $level1[$level2Collection][0][$level3Collection][0]->getId()); - $this->assertArrayNotHasKey('level4', $level1[$level2Collection][0][$level3Collection][0]); - - // Make sure level 4 document was not created - $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); - $this->assertEquals(0, count($level3[$level4Collection])); - $level4 = static::getDatabase()->getDocument($level4Collection, 'level4'); - $this->assertTrue($level4->isEmpty()); - - // Exceed fetch depth - $level1 = static::getDatabase()->getDocument($level1Collection, 'level1'); - $this->assertEquals(1, count($level1[$level2Collection])); - $this->assertEquals('level2', $level1[$level2Collection][0]->getId()); - $this->assertEquals(1, count($level1[$level2Collection][0][$level3Collection])); - $this->assertEquals('level3', $level1[$level2Collection][0][$level3Collection][0]->getId()); - $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][0][$level3Collection][0]); - - - // Exceed update depth - $level1 = static::getDatabase()->updateDocument( - $level1Collection, - 'level1', - $level1 - ->setAttribute($level2Collection, [new Document([ - '$id' => 'level2new', - $level3Collection => [ - [ - '$id' => 'level3new', - $level4Collection => [ - [ - '$id' => 'level4new', - ], - ], - ], - ], - ])]) - ); - $this->assertEquals(1, count($level1[$level2Collection])); - $this->assertEquals('level2new', $level1[$level2Collection][0]->getId()); - $this->assertEquals(1, count($level1[$level2Collection][0][$level3Collection])); - $this->assertEquals('level3new', $level1[$level2Collection][0][$level3Collection][0]->getId()); - $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][0][$level3Collection][0]); - - // Make sure level 4 document was not created - $level3 = static::getDatabase()->getDocument($level3Collection, 'level3new'); - $this->assertEquals(0, count($level3[$level4Collection])); - $level4 = static::getDatabase()->getDocument($level4Collection, 'level4new'); - $this->assertTrue($level4->isEmpty()); - } - - public function testExceedMaxDepthOneToOne(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - $level1Collection = 'level1OneToOne'; - $level2Collection = 'level2OneToOne'; - $level3Collection = 'level3OneToOne'; - $level4Collection = 'level4OneToOne'; - - static::getDatabase()->createCollection($level1Collection); - static::getDatabase()->createCollection($level2Collection); - static::getDatabase()->createCollection($level3Collection); - static::getDatabase()->createCollection($level4Collection); - - static::getDatabase()->createRelationship( - collection: $level1Collection, - relatedCollection: $level2Collection, - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - ); - static::getDatabase()->createRelationship( - collection: $level2Collection, - relatedCollection: $level3Collection, - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - ); - static::getDatabase()->createRelationship( - collection: $level3Collection, - relatedCollection: $level4Collection, - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - ); - - // Exceed create depth - $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ - '$id' => 'level1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - ], - $level2Collection => [ - '$id' => 'level2', - $level3Collection => [ - '$id' => 'level3', - $level4Collection => [ - '$id' => 'level4', - ], - ], - ], - ])); - $this->assertArrayHasKey($level2Collection, $level1); - $this->assertEquals('level2', $level1[$level2Collection]->getId()); - $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); - $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); - $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); - - // Confirm the 4th level document does not exist - $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); - $this->assertNull($level3[$level4Collection]); - - // Create level 4 document - $level3->setAttribute($level4Collection, new Document([ - '$id' => 'level4', - ])); - $level3 = static::getDatabase()->updateDocument($level3Collection, $level3->getId(), $level3); - $this->assertEquals('level4', $level3[$level4Collection]->getId()); - - // Exceed fetch depth - $level1 = static::getDatabase()->getDocument($level1Collection, 'level1'); - $this->assertArrayHasKey($level2Collection, $level1); - $this->assertEquals('level2', $level1[$level2Collection]->getId()); - $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); - $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); - $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); - } - - public function testExceedMaxDepthOneToOneNull(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - $level1Collection = 'level1OneToOneNull'; - $level2Collection = 'level2OneToOneNull'; - $level3Collection = 'level3OneToOneNull'; - $level4Collection = 'level4OneToOneNull'; - - static::getDatabase()->createCollection($level1Collection); - static::getDatabase()->createCollection($level2Collection); - static::getDatabase()->createCollection($level3Collection); - static::getDatabase()->createCollection($level4Collection); - - static::getDatabase()->createRelationship( - collection: $level1Collection, - relatedCollection: $level2Collection, - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - ); - static::getDatabase()->createRelationship( - collection: $level2Collection, - relatedCollection: $level3Collection, - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - ); - static::getDatabase()->createRelationship( - collection: $level3Collection, - relatedCollection: $level4Collection, - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - ); - - $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ - '$id' => 'level1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - ], - $level2Collection => [ - '$id' => 'level2', - $level3Collection => [ - '$id' => 'level3', - $level4Collection => [ - '$id' => 'level4', - ], - ], - ], - ])); - $this->assertArrayHasKey($level2Collection, $level1); - $this->assertEquals('level2', $level1[$level2Collection]->getId()); - $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); - $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); - $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); - - // Confirm the 4th level document does not exist - $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); - $this->assertNull($level3[$level4Collection]); - - // Create level 4 document - $level3->setAttribute($level4Collection, new Document([ - '$id' => 'level4', - ])); - $level3 = static::getDatabase()->updateDocument($level3Collection, $level3->getId(), $level3); - $this->assertEquals('level4', $level3[$level4Collection]->getId()); - $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); - $this->assertEquals('level4', $level3[$level4Collection]->getId()); - - // Exceed fetch depth - $level1 = static::getDatabase()->getDocument($level1Collection, 'level1'); - $this->assertArrayHasKey($level2Collection, $level1); - $this->assertEquals('level2', $level1[$level2Collection]->getId()); - $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); - $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); - $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); - } - - public function testExceedMaxDepthManyToOneParent(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - $level1Collection = 'level1ManyToOneParent'; - $level2Collection = 'level2ManyToOneParent'; - $level3Collection = 'level3ManyToOneParent'; - $level4Collection = 'level4ManyToOneParent'; - - static::getDatabase()->createCollection($level1Collection); - static::getDatabase()->createCollection($level2Collection); - static::getDatabase()->createCollection($level3Collection); - static::getDatabase()->createCollection($level4Collection); - - static::getDatabase()->createRelationship( - collection: $level1Collection, - relatedCollection: $level2Collection, - type: Database::RELATION_MANY_TO_ONE, - twoWay: true, - ); - static::getDatabase()->createRelationship( - collection: $level2Collection, - relatedCollection: $level3Collection, - type: Database::RELATION_MANY_TO_ONE, - twoWay: true, - ); - static::getDatabase()->createRelationship( - collection: $level3Collection, - relatedCollection: $level4Collection, - type: Database::RELATION_MANY_TO_ONE, - twoWay: true, - ); - - $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ - '$id' => 'level1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - ], - $level2Collection => [ - '$id' => 'level2', - $level3Collection => [ - '$id' => 'level3', - $level4Collection => [ - '$id' => 'level4', - ], - ], - ], - ])); - $this->assertArrayHasKey($level2Collection, $level1); - $this->assertEquals('level2', $level1[$level2Collection]->getId()); - $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); - $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); - $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); - - // Confirm the 4th level document does not exist - $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); - $this->assertNull($level3[$level4Collection]); - - // Create level 4 document - $level3->setAttribute($level4Collection, new Document([ - '$id' => 'level4', - ])); - $level3 = static::getDatabase()->updateDocument($level3Collection, $level3->getId(), $level3); - $this->assertEquals('level4', $level3[$level4Collection]->getId()); - $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); - $this->assertEquals('level4', $level3[$level4Collection]->getId()); - - // Exceed fetch depth - $level1 = static::getDatabase()->getDocument($level1Collection, 'level1'); - $this->assertArrayHasKey($level2Collection, $level1); - $this->assertEquals('level2', $level1[$level2Collection]->getId()); - $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); - $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); - $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); - } - - public function testExceedMaxDepthOneToManyChild(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - $level1Collection = 'level1OneToManyChild'; - $level2Collection = 'level2OneToManyChild'; - $level3Collection = 'level3OneToManyChild'; - $level4Collection = 'level4OneToManyChild'; - - static::getDatabase()->createCollection($level1Collection); - static::getDatabase()->createCollection($level2Collection); - static::getDatabase()->createCollection($level3Collection); - static::getDatabase()->createCollection($level4Collection); - - static::getDatabase()->createRelationship( - collection: $level1Collection, - relatedCollection: $level2Collection, - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - ); - static::getDatabase()->createRelationship( - collection: $level2Collection, - relatedCollection: $level3Collection, - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - ); - static::getDatabase()->createRelationship( - collection: $level3Collection, - relatedCollection: $level4Collection, - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - ); - - $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ - '$id' => 'level1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - ], - $level2Collection => [ - [ - '$id' => 'level2', - $level3Collection => [ - [ - '$id' => 'level3', - $level4Collection => [ - [ - '$id' => 'level4', - ], - ] - ], - ], - ], - ], - ])); - $this->assertArrayHasKey($level2Collection, $level1); - $this->assertEquals('level2', $level1[$level2Collection][0]->getId()); - $this->assertArrayHasKey($level3Collection, $level1[$level2Collection][0]); - $this->assertEquals('level3', $level1[$level2Collection][0][$level3Collection][0]->getId()); - $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][0][$level3Collection][0]); - - // Confirm the 4th level document does not exist - $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); - $this->assertEquals(0, count($level3[$level4Collection])); - - // Create level 4 document - $level3->setAttribute($level4Collection, [new Document([ - '$id' => 'level4', - ])]); - $level3 = static::getDatabase()->updateDocument($level3Collection, $level3->getId(), $level3); - $this->assertEquals('level4', $level3[$level4Collection][0]->getId()); - - // Verify level 4 document is set - $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); - $this->assertArrayHasKey($level4Collection, $level3); - $this->assertEquals('level4', $level3[$level4Collection][0]->getId()); - - // Exceed fetch depth - $level4 = static::getDatabase()->getDocument($level4Collection, 'level4'); - $this->assertArrayHasKey($level3Collection, $level4); - $this->assertEquals('level3', $level4[$level3Collection]->getId()); - $this->assertArrayHasKey($level2Collection, $level4[$level3Collection]); - $this->assertEquals('level2', $level4[$level3Collection][$level2Collection]->getId()); - $this->assertArrayNotHasKey($level1Collection, $level4[$level3Collection][$level2Collection]); - } - - public function testCreateRelationshipMissingCollection(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - $this->expectException(Exception::class); - $this->expectExceptionMessage('Collection not found'); - - static::getDatabase()->createRelationship( - collection: 'missing', - relatedCollection: 'missing', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - ); - } - - public function testCreateRelationshipMissingRelatedCollection(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('test'); - - $this->expectException(Exception::class); - $this->expectExceptionMessage('Related collection not found'); - - static::getDatabase()->createRelationship( - collection: 'test', - relatedCollection: 'missing', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - ); - } - - public function testCreateDuplicateRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('test1'); - static::getDatabase()->createCollection('test2'); - - static::getDatabase()->createRelationship( - collection: 'test1', - relatedCollection: 'test2', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - ); - - $this->expectException(Exception::class); - $this->expectExceptionMessage('Attribute already exists'); - - static::getDatabase()->createRelationship( - collection: 'test1', - relatedCollection: 'test2', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - ); - } - - public function testCreateInvalidRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('test3'); - static::getDatabase()->createCollection('test4'); - - $this->expectException(Exception::class); - $this->expectExceptionMessage('Invalid relationship type'); - - static::getDatabase()->createRelationship( - collection: 'test3', - relatedCollection: 'test4', - type: 'invalid', - twoWay: true, - ); - } - - public function testDeleteMissingRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - $this->expectException(Exception::class); - $this->expectExceptionMessage('Attribute not found'); - - static::getDatabase()->deleteRelationship('test', 'test2'); - } - - public function testCreateInvalidIntValueRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('invalid1'); - static::getDatabase()->createCollection('invalid2'); - - static::getDatabase()->createRelationship( - collection: 'invalid1', - relatedCollection: 'invalid2', - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - ); - - $this->expectException(Exception::class); - $this->expectExceptionMessage('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); - - static::getDatabase()->createDocument('invalid1', new Document([ - '$id' => ID::unique(), - 'invalid2' => 10, - ])); - } - - /** - * @depends testCreateInvalidIntValueRelationship - */ - public function testCreateInvalidObjectValueRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - $this->expectException(Exception::class); - $this->expectExceptionMessage('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); - - static::getDatabase()->createDocument('invalid1', new Document([ - '$id' => ID::unique(), - 'invalid2' => new \stdClass(), - ])); - } - - /** - * @depends testCreateInvalidIntValueRelationship - */ - public function testCreateInvalidArrayIntValueRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createRelationship( - collection: 'invalid1', - relatedCollection: 'invalid2', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - id: 'invalid3', - twoWayKey: 'invalid4', - ); - - $this->expectException(Exception::class); - $this->expectExceptionMessage('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); - - static::getDatabase()->createDocument('invalid1', new Document([ - '$id' => ID::unique(), - 'invalid3' => [10], - ])); - } - - public function testCreateEmptyValueRelationship(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('null1'); - static::getDatabase()->createCollection('null2'); - - static::getDatabase()->createRelationship( - collection: 'null1', - relatedCollection: 'null2', - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - ); - static::getDatabase()->createRelationship( - collection: 'null1', - relatedCollection: 'null2', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - id: 'null3', - twoWayKey: 'null4', - ); - static::getDatabase()->createRelationship( - collection: 'null1', - relatedCollection: 'null2', - type: Database::RELATION_MANY_TO_ONE, - twoWay: true, - id: 'null4', - twoWayKey: 'null5', - ); - static::getDatabase()->createRelationship( - collection: 'null1', - relatedCollection: 'null2', - type: Database::RELATION_MANY_TO_MANY, - twoWay: true, - id: 'null6', - twoWayKey: 'null7', - ); - - $document = static::getDatabase()->createDocument('null1', new Document([ - '$id' => ID::unique(), - 'null2' => null, - ])); - - $this->assertEquals(null, $document->getAttribute('null2')); - - $document = static::getDatabase()->createDocument('null2', new Document([ - '$id' => ID::unique(), - 'null1' => null, - ])); - - $this->assertEquals(null, $document->getAttribute('null1')); - - $document = static::getDatabase()->createDocument('null1', new Document([ - '$id' => ID::unique(), - 'null3' => null, - ])); - - // One to many will be empty array instead of null - $this->assertEquals([], $document->getAttribute('null3')); - - $document = static::getDatabase()->createDocument('null2', new Document([ - '$id' => ID::unique(), - 'null4' => null, - ])); - - $this->assertEquals(null, $document->getAttribute('null4')); - - $document = static::getDatabase()->createDocument('null1', new Document([ - '$id' => ID::unique(), - 'null4' => null, - ])); - - $this->assertEquals(null, $document->getAttribute('null4')); - - $document = static::getDatabase()->createDocument('null2', new Document([ - '$id' => ID::unique(), - 'null5' => null, - ])); - - $this->assertEquals([], $document->getAttribute('null5')); - - $document = static::getDatabase()->createDocument('null1', new Document([ - '$id' => ID::unique(), - 'null6' => null, - ])); - - $this->assertEquals([], $document->getAttribute('null6')); - - $document = static::getDatabase()->createDocument('null2', new Document([ - '$id' => ID::unique(), - 'null7' => null, - ])); - - $this->assertEquals([], $document->getAttribute('null7')); - } - - public function testDeleteCollectionDeletesRelationships(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('testers'); - static::getDatabase()->createCollection('devices'); - - static::getDatabase()->createRelationship( - collection: 'testers', - relatedCollection: 'devices', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - twoWayKey: 'tester' - ); - - $testers = static::getDatabase()->getCollection('testers'); - $devices = static::getDatabase()->getCollection('devices'); - - $this->assertEquals(1, \count($testers->getAttribute('attributes'))); - $this->assertEquals(1, \count($devices->getAttribute('attributes'))); - $this->assertEquals(1, \count($devices->getAttribute('indexes'))); - - static::getDatabase()->deleteCollection('testers'); - - $testers = static::getDatabase()->getCollection('testers'); - $devices = static::getDatabase()->getCollection('devices'); - - $this->assertEquals(true, $testers->isEmpty()); - $this->assertEquals(0, \count($devices->getAttribute('attributes'))); - $this->assertEquals(0, \count($devices->getAttribute('indexes'))); - } - - public function testDeleteTwoWayRelationshipFromChild(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('drivers'); - static::getDatabase()->createCollection('licenses'); - - static::getDatabase()->createRelationship( - collection: 'drivers', - relatedCollection: 'licenses', - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - id: 'license', - twoWayKey: 'driver' - ); - - $drivers = static::getDatabase()->getCollection('drivers'); - $licenses = static::getDatabase()->getCollection('licenses'); - - $this->assertEquals(1, \count($drivers->getAttribute('attributes'))); - $this->assertEquals(1, \count($drivers->getAttribute('indexes'))); - $this->assertEquals(1, \count($licenses->getAttribute('attributes'))); - $this->assertEquals(1, \count($licenses->getAttribute('indexes'))); - - static::getDatabase()->deleteRelationship('licenses', 'driver'); - - $drivers = static::getDatabase()->getCollection('drivers'); - $licenses = static::getDatabase()->getCollection('licenses'); - - $this->assertEquals(0, \count($drivers->getAttribute('attributes'))); - $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); - $this->assertEquals(0, \count($licenses->getAttribute('attributes'))); - $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); - - static::getDatabase()->createRelationship( - collection: 'drivers', - relatedCollection: 'licenses', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - id: 'licenses', - twoWayKey: 'driver' - ); - - $drivers = static::getDatabase()->getCollection('drivers'); - $licenses = static::getDatabase()->getCollection('licenses'); - - $this->assertEquals(1, \count($drivers->getAttribute('attributes'))); - $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); - $this->assertEquals(1, \count($licenses->getAttribute('attributes'))); - $this->assertEquals(1, \count($licenses->getAttribute('indexes'))); - - static::getDatabase()->deleteRelationship('licenses', 'driver'); - - $drivers = static::getDatabase()->getCollection('drivers'); - $licenses = static::getDatabase()->getCollection('licenses'); - - $this->assertEquals(0, \count($drivers->getAttribute('attributes'))); - $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); - $this->assertEquals(0, \count($licenses->getAttribute('attributes'))); - $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); - - static::getDatabase()->createRelationship( - collection: 'licenses', - relatedCollection: 'drivers', - type: Database::RELATION_MANY_TO_ONE, - twoWay: true, - id: 'driver', - twoWayKey: 'licenses' - ); - - $drivers = static::getDatabase()->getCollection('drivers'); - $licenses = static::getDatabase()->getCollection('licenses'); - - $this->assertEquals(1, \count($drivers->getAttribute('attributes'))); - $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); - $this->assertEquals(1, \count($licenses->getAttribute('attributes'))); - $this->assertEquals(1, \count($licenses->getAttribute('indexes'))); - - static::getDatabase()->deleteRelationship('drivers', 'licenses'); - - $drivers = static::getDatabase()->getCollection('drivers'); - $licenses = static::getDatabase()->getCollection('licenses'); - - $this->assertEquals(0, \count($drivers->getAttribute('attributes'))); - $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); - $this->assertEquals(0, \count($licenses->getAttribute('attributes'))); - $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); - - static::getDatabase()->createRelationship( - collection: 'licenses', - relatedCollection: 'drivers', - type: Database::RELATION_MANY_TO_MANY, - twoWay: true, - id: 'drivers', - twoWayKey: 'licenses' - ); - - $drivers = static::getDatabase()->getCollection('drivers'); - $licenses = static::getDatabase()->getCollection('licenses'); - $junction = static::getDatabase()->getCollection('_' . $licenses->getInternalId() . '_' . $drivers->getInternalId()); - - $this->assertEquals(1, \count($drivers->getAttribute('attributes'))); - $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); - $this->assertEquals(1, \count($licenses->getAttribute('attributes'))); - $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); - $this->assertEquals(2, \count($junction->getAttribute('attributes'))); - $this->assertEquals(2, \count($junction->getAttribute('indexes'))); - - static::getDatabase()->deleteRelationship('drivers', 'licenses'); - - $drivers = static::getDatabase()->getCollection('drivers'); - $licenses = static::getDatabase()->getCollection('licenses'); - $junction = static::getDatabase()->getCollection('_licenses_drivers'); - - $this->assertEquals(0, \count($drivers->getAttribute('attributes'))); - $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); - $this->assertEquals(0, \count($licenses->getAttribute('attributes'))); - $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); - - $this->assertEquals(true, $junction->isEmpty()); - } - - public function testUpdateRelationshipToExistingKey(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('ovens'); - static::getDatabase()->createCollection('cakes'); - - static::getDatabase()->createAttribute('ovens', 'maxTemp', Database::VAR_INTEGER, 0, true); - static::getDatabase()->createAttribute('ovens', 'owner', Database::VAR_STRING, 255, true); - static::getDatabase()->createAttribute('cakes', 'height', Database::VAR_INTEGER, 0, true); - static::getDatabase()->createAttribute('cakes', 'colour', Database::VAR_STRING, 255, true); - - static::getDatabase()->createRelationship( - collection: 'ovens', - relatedCollection: 'cakes', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - id: 'cakes', - twoWayKey: 'oven' - ); - - try { - static::getDatabase()->updateRelationship('ovens', 'cakes', newKey: 'owner'); - $this->fail('Failed to throw exception'); - } catch (DuplicateException $e) { - $this->assertEquals('Attribute already exists', $e->getMessage()); - } - - try { - static::getDatabase()->updateRelationship('ovens', 'cakes', newTwoWayKey: 'height'); - $this->fail('Failed to throw exception'); - } catch (DuplicateException $e) { - $this->assertEquals('Related attribute already exists', $e->getMessage()); - } - } - - public function testOneToOneRelationshipKeyWithSymbols(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('$symbols_coll.ection1'); - static::getDatabase()->createCollection('$symbols_coll.ection2'); - - static::getDatabase()->createRelationship( - collection: '$symbols_coll.ection1', - relatedCollection: '$symbols_coll.ection2', - type: Database::RELATION_ONE_TO_ONE, - twoWay: true, - ); - - $doc1 = static::getDatabase()->createDocument('$symbols_coll.ection2', new Document([ - '$id' => ID::unique(), - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()) - ] - ])); - $doc2 = static::getDatabase()->createDocument('$symbols_coll.ection1', new Document([ - '$id' => ID::unique(), - '$symbols_coll.ection2' => $doc1->getId(), - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()) - ] - ])); - - $doc1 = static::getDatabase()->getDocument('$symbols_coll.ection2', $doc1->getId()); - $doc2 = static::getDatabase()->getDocument('$symbols_coll.ection1', $doc2->getId()); - - $this->assertEquals($doc2->getId(), $doc1->getAttribute('$symbols_coll.ection1')->getId()); - $this->assertEquals($doc1->getId(), $doc2->getAttribute('$symbols_coll.ection2')->getId()); - } - - public function testOneToManyRelationshipKeyWithSymbols(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('$symbols_coll.ection3'); - static::getDatabase()->createCollection('$symbols_coll.ection4'); - - static::getDatabase()->createRelationship( - collection: '$symbols_coll.ection3', - relatedCollection: '$symbols_coll.ection4', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - ); - - $doc1 = static::getDatabase()->createDocument('$symbols_coll.ection4', new Document([ - '$id' => ID::unique(), - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()) - ] - ])); - $doc2 = static::getDatabase()->createDocument('$symbols_coll.ection3', new Document([ - '$id' => ID::unique(), - '$symbols_coll.ection4' => [$doc1->getId()], - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()) - ] - ])); - - $doc1 = static::getDatabase()->getDocument('$symbols_coll.ection4', $doc1->getId()); - $doc2 = static::getDatabase()->getDocument('$symbols_coll.ection3', $doc2->getId()); - - $this->assertEquals($doc2->getId(), $doc1->getAttribute('$symbols_coll.ection3')->getId()); - $this->assertEquals($doc1->getId(), $doc2->getAttribute('$symbols_coll.ection4')[0]->getId()); - } - - public function testManyToOneRelationshipKeyWithSymbols(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('$symbols_coll.ection5'); - static::getDatabase()->createCollection('$symbols_coll.ection6'); - - static::getDatabase()->createRelationship( - collection: '$symbols_coll.ection5', - relatedCollection: '$symbols_coll.ection6', - type: Database::RELATION_MANY_TO_ONE, - twoWay: true, - ); - - $doc1 = static::getDatabase()->createDocument('$symbols_coll.ection6', new Document([ - '$id' => ID::unique(), - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()) - ] - ])); - $doc2 = static::getDatabase()->createDocument('$symbols_coll.ection5', new Document([ - '$id' => ID::unique(), - '$symbols_coll.ection6' => $doc1->getId(), - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()) - ] - ])); - - $doc1 = static::getDatabase()->getDocument('$symbols_coll.ection6', $doc1->getId()); - $doc2 = static::getDatabase()->getDocument('$symbols_coll.ection5', $doc2->getId()); - - $this->assertEquals($doc2->getId(), $doc1->getAttribute('$symbols_coll.ection5')[0]->getId()); - $this->assertEquals($doc1->getId(), $doc2->getAttribute('$symbols_coll.ection6')->getId()); - } - - public function testManyToManyRelationshipKeyWithSymbols(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('$symbols_coll.ection7'); - static::getDatabase()->createCollection('$symbols_coll.ection8'); - - static::getDatabase()->createRelationship( - collection: '$symbols_coll.ection7', - relatedCollection: '$symbols_coll.ection8', - type: Database::RELATION_MANY_TO_MANY, - twoWay: true, - ); - - $doc1 = static::getDatabase()->createDocument('$symbols_coll.ection8', new Document([ - '$id' => ID::unique(), - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()) - ] - ])); - $doc2 = static::getDatabase()->createDocument('$symbols_coll.ection7', new Document([ - '$id' => ID::unique(), - '$symbols_coll.ection8' => [$doc1->getId()], - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()) - ] - ])); - - $doc1 = static::getDatabase()->getDocument('$symbols_coll.ection8', $doc1->getId()); - $doc2 = static::getDatabase()->getDocument('$symbols_coll.ection7', $doc2->getId()); - - $this->assertEquals($doc2->getId(), $doc1->getAttribute('$symbols_coll.ection7')[0]->getId()); - $this->assertEquals($doc1->getId(), $doc2->getAttribute('$symbols_coll.ection8')[0]->getId()); - } - - public function testCascadeMultiDelete(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - static::getDatabase()->createCollection('cascadeMultiDelete1'); - static::getDatabase()->createCollection('cascadeMultiDelete2'); - static::getDatabase()->createCollection('cascadeMultiDelete3'); - - static::getDatabase()->createRelationship( - collection: 'cascadeMultiDelete1', - relatedCollection: 'cascadeMultiDelete2', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - onDelete: Database::RELATION_MUTATE_CASCADE - ); - - static::getDatabase()->createRelationship( - collection: 'cascadeMultiDelete2', - relatedCollection: 'cascadeMultiDelete3', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - onDelete: Database::RELATION_MUTATE_CASCADE - ); - - $root = static::getDatabase()->createDocument('cascadeMultiDelete1', new Document([ - '$id' => 'cascadeMultiDelete1', - '$permissions' => [ - Permission::read(Role::any()), - Permission::delete(Role::any()) - ], - 'cascadeMultiDelete2' => [ - [ - '$id' => 'cascadeMultiDelete2', - '$permissions' => [ - Permission::read(Role::any()), - Permission::delete(Role::any()) - ], - 'cascadeMultiDelete3' => [ - [ - '$id' => 'cascadeMultiDelete3', - '$permissions' => [ - Permission::read(Role::any()), - Permission::delete(Role::any()) - ], - ], - ], - ], - ], - ])); - - $this->assertCount(1, $root->getAttribute('cascadeMultiDelete2')); - $this->assertCount(1, $root->getAttribute('cascadeMultiDelete2')[0]->getAttribute('cascadeMultiDelete3')); - - $this->assertEquals(true, static::getDatabase()->deleteDocument('cascadeMultiDelete1', $root->getId())); - - $multi2 = static::getDatabase()->getDocument('cascadeMultiDelete2', 'cascadeMultiDelete2'); - $this->assertEquals(true, $multi2->isEmpty()); - - $multi3 = static::getDatabase()->getDocument('cascadeMultiDelete3', 'cascadeMultiDelete3'); - $this->assertEquals(true, $multi3->isEmpty()); - } - - public function testCollectionUpdate(): Document - { - $collection = static::getDatabase()->createCollection('collectionUpdate', permissions: [ - Permission::create(Role::users()), - Permission::read(Role::users()), - Permission::update(Role::users()), - Permission::delete(Role::users()) - ], documentSecurity: false); - - $this->assertInstanceOf(Document::class, $collection); - - $collection = static::getDatabase()->getCollection('collectionUpdate'); - - $this->assertFalse($collection->getAttribute('documentSecurity')); - $this->assertIsArray($collection->getPermissions()); - $this->assertCount(4, $collection->getPermissions()); - - $collection = static::getDatabase()->updateCollection('collectionUpdate', [], true); - - $this->assertTrue($collection->getAttribute('documentSecurity')); - $this->assertIsArray($collection->getPermissions()); - $this->assertEmpty($collection->getPermissions()); - - $collection = static::getDatabase()->getCollection('collectionUpdate'); - - $this->assertTrue($collection->getAttribute('documentSecurity')); - $this->assertIsArray($collection->getPermissions()); - $this->assertEmpty($collection->getPermissions()); - - return $collection; - } - - /** - * @depends testCollectionUpdate - */ - public function testCollectionUpdatePermissionsThrowException(Document $collection): void - { - $this->expectException(InvalidArgumentException::class); - static::getDatabase()->updateCollection($collection->getId(), permissions: [ - 'i dont work' - ], documentSecurity: false); - } - - public function testCollectionPermissions(): Document - { - $collection = static::getDatabase()->createCollection('collectionSecurity', permissions: [ - Permission::create(Role::users()), - Permission::read(Role::users()), - Permission::update(Role::users()), - Permission::delete(Role::users()) - ], documentSecurity: false); - - $this->assertInstanceOf(Document::class, $collection); - - $this->assertTrue(static::getDatabase()->createAttribute( - collection: $collection->getId(), - id: 'test', - type: Database::VAR_STRING, - size: 255, - required: false - )); - - return $collection; - } - - public function testCollectionPermissionsExceptions(): void - { - $this->expectException(InvalidArgumentException::class); - static::getDatabase()->createCollection('collectionSecurity', permissions: [ - 'i dont work' - ]); - } - - /** - * @depends testCollectionPermissions - * @return array - */ - public function testCollectionPermissionsCreateWorks(Document $collection): array - { - Authorization::cleanRoles(); - Authorization::setRole(Role::users()->toString()); - - $document = static::getDatabase()->createDocument($collection->getId(), new Document([ - '$id' => ID::unique(), - '$permissions' => [ - Permission::read(Role::user('random')), - Permission::update(Role::user('random')), - Permission::delete(Role::user('random')) - ], - 'test' => 'lorem' - ])); - $this->assertInstanceOf(Document::class, $document); - - return [$collection, $document]; - } - - - /** - * @depends testCollectionPermissions - */ - public function testCollectionPermissionsCreateThrowsException(Document $collection): void - { - Authorization::cleanRoles(); - Authorization::setRole(Role::any()->toString()); - $this->expectException(AuthorizationException::class); - - static::getDatabase()->createDocument($collection->getId(), new Document([ - '$id' => ID::unique(), - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()) - ], - 'test' => 'lorem ipsum' - ])); - } - - /** - * @depends testCollectionPermissionsCreateWorks - * @param array $data - * @return array - */ - public function testCollectionPermissionsGetWorks(array $data): array - { - [$collection, $document] = $data; - - Authorization::cleanRoles(); - Authorization::setRole(Role::users()->toString()); - - $document = static::getDatabase()->getDocument( - $collection->getId(), - $document->getId() - ); - $this->assertInstanceOf(Document::class, $document); - $this->assertFalse($document->isEmpty()); - - return $data; - } - - /** - * @depends testCollectionPermissionsCreateWorks - * @param array $data - */ - public function testCollectionPermissionsGetThrowsException(array $data): void - { - [$collection, $document] = $data; - - Authorization::cleanRoles(); - Authorization::setRole(Role::any()->toString()); - - $document = static::getDatabase()->getDocument( - $collection->getId(), - $document->getId(), - ); - $this->assertInstanceOf(Document::class, $document); - $this->assertTrue($document->isEmpty()); - } - - /** - * @depends testCollectionPermissionsCreateWorks - * @param array $data - * @return array - */ - public function testCollectionPermissionsFindWorks(array $data): array - { - [$collection, $document] = $data; - - Authorization::cleanRoles(); - Authorization::setRole(Role::users()->toString()); - - $documents = static::getDatabase()->find($collection->getId()); - $this->assertNotEmpty($documents); - - Authorization::cleanRoles(); - Authorization::setRole(Role::user('random')->toString()); - - try { - static::getDatabase()->find($collection->getId()); - $this->fail('Failed to throw exception'); - } catch (AuthorizationException) { - } - - return $data; - } - - /** - * @param array $data - * @depends testCollectionPermissionsCreateWorks - */ - public function testCollectionPermissionsFindThrowsException(array $data): void - { - [$collection, $document] = $data; - - Authorization::cleanRoles(); - Authorization::setRole(Role::any()->toString()); - - $this->expectException(AuthorizationException::class); - static::getDatabase()->find($collection->getId()); - } - - /** - * @depends testCollectionPermissionsCreateWorks - * @param array $data - * @return array - */ - public function testCollectionPermissionsCountWorks(array $data): array - { - [$collection, $document] = $data; - - Authorization::cleanRoles(); - Authorization::setRole(Role::users()->toString()); - - $count = static::getDatabase()->count( - $collection->getId() - ); - - $this->assertNotEmpty($count); - - return $data; - } - - /** - * @param array $data - * @depends testCollectionPermissionsCreateWorks - */ - public function testCollectionPermissionsCountThrowsException(array $data): void - { - [$collection, $document] = $data; - - Authorization::cleanRoles(); - Authorization::setRole(Role::any()->toString()); - - $count = static::getDatabase()->count( - $collection->getId() - ); - $this->assertEmpty($count); - } - - /** - * @depends testCollectionPermissionsCreateWorks - * @param array $data - * @return array - */ - public function testCollectionPermissionsUpdateWorks(array $data): array - { - [$collection, $document] = $data; - - Authorization::cleanRoles(); - Authorization::setRole(Role::users()->toString()); - - $this->assertInstanceOf(Document::class, static::getDatabase()->updateDocument( - $collection->getId(), - $document->getId(), - $document->setAttribute('test', 'ipsum') - )); - - return $data; - } - - /** - * @param array $data - * @depends testCollectionPermissionsCreateWorks - */ - public function testCollectionPermissionsUpdateThrowsException(array $data): void - { - [$collection, $document] = $data; - Authorization::cleanRoles(); - Authorization::setRole(Role::any()->toString()); - - $this->expectException(AuthorizationException::class); - $document = static::getDatabase()->updateDocument( - $collection->getId(), - $document->getId(), - $document->setAttribute('test', 'lorem') - ); - } - - /** - * @param array $data - * @depends testCollectionPermissionsUpdateWorks - */ - public function testCollectionPermissionsDeleteThrowsException(array $data): void - { - [$collection, $document] = $data; - - Authorization::cleanRoles(); - Authorization::setRole(Role::any()->toString()); - - $this->expectException(AuthorizationException::class); - static::getDatabase()->deleteDocument( - $collection->getId(), - $document->getId() - ); - } - - /** - * @param array $data - * @depends testCollectionPermissionsUpdateWorks - */ - public function testCollectionPermissionsDeleteWorks(array $data): void - { - [$collection, $document] = $data; - - Authorization::cleanRoles(); - Authorization::setRole(Role::users()->toString()); - - $this->assertTrue(static::getDatabase()->deleteDocument( - $collection->getId(), - $document->getId() - )); - } - - /** - * @return array - */ - public function testCollectionPermissionsRelationships(): array - { - $collection = static::getDatabase()->createCollection('collectionSecurity.Parent', permissions: [ - Permission::create(Role::users()), - Permission::read(Role::users()), - Permission::update(Role::users()), - Permission::delete(Role::users()) - ], documentSecurity: true); - - $this->assertInstanceOf(Document::class, $collection); - - $this->assertTrue(static::getDatabase()->createAttribute( - collection: $collection->getId(), - id: 'test', - type: Database::VAR_STRING, - size: 255, - required: false - )); - - $collectionOneToOne = static::getDatabase()->createCollection('collectionSecurity.OneToOne', permissions: [ - Permission::create(Role::users()), - Permission::read(Role::users()), - Permission::update(Role::users()), - Permission::delete(Role::users()) - ], documentSecurity: true); - - $this->assertInstanceOf(Document::class, $collectionOneToOne); - - $this->assertTrue(static::getDatabase()->createAttribute( - collection: $collectionOneToOne->getId(), - id: 'test', - type: Database::VAR_STRING, - size: 255, - required: false - )); - - $this->assertTrue(static::getDatabase()->createRelationship( - collection: $collection->getId(), - relatedCollection: $collectionOneToOne->getId(), - type: Database::RELATION_ONE_TO_ONE, - id: Database::RELATION_ONE_TO_ONE, - onDelete: Database::RELATION_MUTATE_CASCADE - )); - - $collectionOneToMany = static::getDatabase()->createCollection('collectionSecurity.OneToMany', permissions: [ - Permission::create(Role::users()), - Permission::read(Role::users()), - Permission::update(Role::users()), - Permission::delete(Role::users()) - ], documentSecurity: true); - - $this->assertInstanceOf(Document::class, $collectionOneToMany); - - $this->assertTrue(static::getDatabase()->createAttribute( - collection: $collectionOneToMany->getId(), - id: 'test', - type: Database::VAR_STRING, - size: 255, - required: false - )); - - $this->assertTrue(static::getDatabase()->createRelationship( - collection: $collection->getId(), - relatedCollection: $collectionOneToMany->getId(), - type: Database::RELATION_ONE_TO_MANY, - id: Database::RELATION_ONE_TO_MANY, - onDelete: Database::RELATION_MUTATE_CASCADE - )); - - return [$collection, $collectionOneToOne, $collectionOneToMany]; - } - - /** - * @depends testCollectionPermissionsRelationships - * @param array $data - * @return array - */ - public function testCollectionPermissionsRelationshipsCreateWorks(array $data): array - { - [$collection, $collectionOneToOne, $collectionOneToMany] = $data; - Authorization::cleanRoles(); - Authorization::setRole(Role::users()->toString()); - - $document = static::getDatabase()->createDocument($collection->getId(), new Document([ - '$id' => ID::unique(), - '$permissions' => [ - Permission::read(Role::user('random')), - Permission::update(Role::user('random')), - Permission::delete(Role::user('random')) - ], - 'test' => 'lorem', - Database::RELATION_ONE_TO_ONE => [ - '$id' => ID::unique(), - '$permissions' => [ - Permission::read(Role::user('random')), - Permission::update(Role::user('random')), - Permission::delete(Role::user('random')) - ], - 'test' => 'lorem ipsum' - ], - Database::RELATION_ONE_TO_MANY => [ - [ - '$id' => ID::unique(), - '$permissions' => [ - Permission::read(Role::user('random')), - Permission::update(Role::user('random')), - Permission::delete(Role::user('random')) - ], - 'test' => 'lorem ipsum' - ], [ - '$id' => ID::unique(), - '$permissions' => [ - Permission::read(Role::user('torsten')), - Permission::update(Role::user('random')), - Permission::delete(Role::user('random')) - ], - 'test' => 'dolor' - ] - ], - ])); - $this->assertInstanceOf(Document::class, $document); - - return [...$data, $document]; - } - - /** - * @depends testCollectionPermissionsRelationships - * @param array $data - */ - public function testCollectionPermissionsRelationshipsCreateThrowsException(array $data): void - { - [$collection, $collectionOneToOne, $collectionOneToMany] = $data; - - Authorization::cleanRoles(); - Authorization::setRole(Role::any()->toString()); - $this->expectException(AuthorizationException::class); - - static::getDatabase()->createDocument($collection->getId(), new Document([ - '$id' => ID::unique(), - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()) - ], - 'test' => 'lorem ipsum' - ])); - } - - /** - * @depends testCollectionPermissionsRelationshipsCreateWorks - * @param array $data - * @return array - */ - public function testCollectionPermissionsRelationshipsGetWorks(array $data): array - { - [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; - - Authorization::cleanRoles(); - Authorization::setRole(Role::users()->toString()); - - $document = static::getDatabase()->getDocument( - $collection->getId(), - $document->getId() - ); - - $this->assertInstanceOf(Document::class, $document); - $this->assertInstanceOf(Document::class, $document->getAttribute(Database::RELATION_ONE_TO_ONE)); - $this->assertIsArray($document->getAttribute(Database::RELATION_ONE_TO_MANY)); - $this->assertCount(2, $document->getAttribute(Database::RELATION_ONE_TO_MANY)); - $this->assertFalse($document->isEmpty()); - - Authorization::cleanRoles(); - Authorization::setRole(Role::user('random')->toString()); - - $document = static::getDatabase()->getDocument( - $collection->getId(), - $document->getId() - ); - - $this->assertInstanceOf(Document::class, $document); - $this->assertInstanceOf(Document::class, $document->getAttribute(Database::RELATION_ONE_TO_ONE)); - $this->assertIsArray($document->getAttribute(Database::RELATION_ONE_TO_MANY)); - $this->assertCount(1, $document->getAttribute(Database::RELATION_ONE_TO_MANY)); - $this->assertFalse($document->isEmpty()); - - return $data; - } - - /** - * @param array $data - * @depends testCollectionPermissionsRelationshipsCreateWorks - */ - public function testCollectionPermissionsRelationshipsGetThrowsException(array $data): void - { - [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; - - Authorization::cleanRoles(); - Authorization::setRole(Role::any()->toString()); - - $document = static::getDatabase()->getDocument( - $collection->getId(), - $document->getId(), - ); - $this->assertInstanceOf(Document::class, $document); - $this->assertTrue($document->isEmpty()); - } - - /** - * @depends testCollectionPermissionsRelationshipsCreateWorks - * @param array $data - */ - public function testCollectionPermissionsRelationshipsFindWorks(array $data): void - { - [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; - - Authorization::cleanRoles(); - Authorization::setRole(Role::users()->toString()); - - $documents = static::getDatabase()->find( - $collection->getId() - ); - - $this->assertIsArray($documents); - $this->assertCount(1, $documents); - $document = $documents[0]; - $this->assertInstanceOf(Document::class, $document); - $this->assertInstanceOf(Document::class, $document->getAttribute(Database::RELATION_ONE_TO_ONE)); - $this->assertIsArray($document->getAttribute(Database::RELATION_ONE_TO_MANY)); - $this->assertCount(2, $document->getAttribute(Database::RELATION_ONE_TO_MANY)); - $this->assertFalse($document->isEmpty()); - - Authorization::cleanRoles(); - Authorization::setRole(Role::user('random')->toString()); - - $documents = static::getDatabase()->find( - $collection->getId() - ); - - $this->assertIsArray($documents); - $this->assertCount(1, $documents); - $document = $documents[0]; - $this->assertInstanceOf(Document::class, $document); - $this->assertInstanceOf(Document::class, $document->getAttribute(Database::RELATION_ONE_TO_ONE)); - $this->assertIsArray($document->getAttribute(Database::RELATION_ONE_TO_MANY)); - $this->assertCount(1, $document->getAttribute(Database::RELATION_ONE_TO_MANY)); - $this->assertFalse($document->isEmpty()); - - Authorization::cleanRoles(); - Authorization::setRole(Role::user('unknown')->toString()); - - $documents = static::getDatabase()->find( - $collection->getId() - ); - - $this->assertIsArray($documents); - $this->assertCount(0, $documents); - } - - /** - * @depends testCollectionPermissionsRelationshipsCreateWorks - * @param array $data - */ - public function testCollectionPermissionsRelationshipsCountWorks(array $data): void - { - [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; - - Authorization::cleanRoles(); - Authorization::setRole(Role::users()->toString()); - - $documents = static::getDatabase()->count( - $collection->getId() - ); - - $this->assertEquals(1, $documents); - - Authorization::cleanRoles(); - Authorization::setRole(Role::user('random')->toString()); - - $documents = static::getDatabase()->count( - $collection->getId() - ); - - $this->assertEquals(1, $documents); - - Authorization::cleanRoles(); - Authorization::setRole(Role::user('unknown')->toString()); - - $documents = static::getDatabase()->count( - $collection->getId() - ); - - $this->assertEquals(0, $documents); - } - - /** - * @depends testCollectionPermissionsRelationshipsCreateWorks - * @param array $data - * @return array - */ - public function testCollectionPermissionsRelationshipsUpdateWorks(array $data): array - { - [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; - - Authorization::cleanRoles(); - Authorization::setRole(Role::users()->toString()); - static::getDatabase()->updateDocument( - $collection->getId(), - $document->getId(), - $document - ); - - $this->assertTrue(true); - - Authorization::cleanRoles(); - Authorization::setRole(Role::user('random')->toString()); - - static::getDatabase()->updateDocument( - $collection->getId(), - $document->getId(), - $document->setAttribute('test', 'ipsum') - ); - - $this->assertTrue(true); - - return $data; - } - - /** - * @param array $data - * @depends testCollectionPermissionsRelationshipsCreateWorks - */ - public function testCollectionPermissionsRelationshipsUpdateThrowsException(array $data): void - { - [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; - - Authorization::cleanRoles(); - Authorization::setRole(Role::any()->toString()); - - $this->expectException(AuthorizationException::class); - $document = static::getDatabase()->updateDocument( - $collection->getId(), - $document->getId(), - $document->setAttribute('test', $document->getAttribute('test').'new_value') - ); - } - - /** - * @param array $data - * @depends testCollectionPermissionsRelationshipsUpdateWorks - */ - public function testCollectionPermissionsRelationshipsDeleteThrowsException(array $data): void - { - [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; - - Authorization::cleanRoles(); - Authorization::setRole(Role::any()->toString()); - - $this->expectException(AuthorizationException::class); - $document = static::getDatabase()->deleteDocument( - $collection->getId(), - $document->getId() - ); - } - - /** - * @param array $data - * @depends testCollectionPermissionsRelationshipsUpdateWorks - */ - public function testCollectionPermissionsRelationshipsDeleteWorks(array $data): void - { - [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; - - Authorization::cleanRoles(); - Authorization::setRole(Role::users()->toString()); - - $this->assertTrue(static::getDatabase()->deleteDocument( - $collection->getId(), - $document->getId() - )); - } - - public function testCreateRelationDocumentWithoutUpdatePermission(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - Authorization::cleanRoles(); - Authorization::setRole(Role::user('a')->toString()); - - static::getDatabase()->createCollection('parentRelationTest', [], [], [ - Permission::read(Role::user('a')), - Permission::create(Role::user('a')), - Permission::update(Role::user('a')), - Permission::delete(Role::user('a')) - ]); - static::getDatabase()->createCollection('childRelationTest', [], [], [ - Permission::create(Role::user('a')), - Permission::read(Role::user('a')), - ]); - static::getDatabase()->createAttribute('parentRelationTest', 'name', Database::VAR_STRING, 255, false); - static::getDatabase()->createAttribute('childRelationTest', 'name', Database::VAR_STRING, 255, false); - - static::getDatabase()->createRelationship( - collection: 'parentRelationTest', - relatedCollection: 'childRelationTest', - type: Database::RELATION_ONE_TO_MANY, - id: 'children' - ); - - // Create document with relationship with nested data - $parent = static::getDatabase()->createDocument('parentRelationTest', new Document([ - '$id' => 'parent1', - 'name' => 'Parent 1', - 'children' => [ - [ - '$id' => 'child1', - 'name' => 'Child 1', - ], - ], - ])); - $this->assertEquals('child1', $parent->getAttribute('children')[0]->getId()); - $parent->setAttribute('children', [ - [ - '$id' => 'child2', - ], - ]); - $updatedParent = static::getDatabase()->updateDocument('parentRelationTest', 'parent1', $parent); - - $this->assertEquals('child2', $updatedParent->getAttribute('children')[0]->getId()); - - static::getDatabase()->deleteCollection('parentRelationTest'); - static::getDatabase()->deleteCollection('childRelationTest'); - } - - public function testUpdateDocumentWithRelationships(): void - { - if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - static::getDatabase()->createCollection('userProfiles', [ - new Document([ - '$id' => ID::custom('username'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 700, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ]), - ], [], [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()) - ]); - static::getDatabase()->createCollection('links', [ - new Document([ - '$id' => ID::custom('title'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 700, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ]), - ], [], [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()) - ]); - static::getDatabase()->createCollection('videos', [ - new Document([ - '$id' => ID::custom('title'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 700, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ]), - ], [], [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()) - ]); - static::getDatabase()->createCollection('products', [ - new Document([ - '$id' => ID::custom('title'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 700, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ]), - ], [], [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()) - ]); - static::getDatabase()->createCollection('settings', [ - new Document([ - '$id' => ID::custom('metaTitle'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 700, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ]), - ], [], [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()) - ]); - static::getDatabase()->createCollection('appearance', [ - new Document([ - '$id' => ID::custom('metaTitle'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 700, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ]), - ], [], [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()) - ]); - static::getDatabase()->createCollection('group', [ - new Document([ - '$id' => ID::custom('name'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 700, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ]), - ], [], [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()) - ]); - static::getDatabase()->createCollection('community', [ - new Document([ - '$id' => ID::custom('name'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 700, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ]), - ], [], [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()) - ]); - - static::getDatabase()->createRelationship( - collection: 'userProfiles', - relatedCollection: 'links', - type: Database::RELATION_ONE_TO_MANY, - id: 'links' - ); - - static::getDatabase()->createRelationship( - collection: 'userProfiles', - relatedCollection: 'videos', - type: Database::RELATION_ONE_TO_MANY, - id: 'videos' - ); - - static::getDatabase()->createRelationship( - collection: 'userProfiles', - relatedCollection: 'products', - type: Database::RELATION_ONE_TO_MANY, - twoWay: true, - id: 'products', - twoWayKey: 'userProfile', - ); - - static::getDatabase()->createRelationship( - collection: 'userProfiles', - relatedCollection: 'settings', - type: Database::RELATION_ONE_TO_ONE, - id: 'settings' - ); - - static::getDatabase()->createRelationship( - collection: 'userProfiles', - relatedCollection: 'appearance', - type: Database::RELATION_ONE_TO_ONE, - id: 'appearance' - ); - - static::getDatabase()->createRelationship( - collection: 'userProfiles', - relatedCollection: 'group', - type: Database::RELATION_MANY_TO_ONE, - id: 'group' - ); - - static::getDatabase()->createRelationship( - collection: 'userProfiles', - relatedCollection: 'community', - type: Database::RELATION_MANY_TO_ONE, - id: 'community' - ); - - $profile = static::getDatabase()->createDocument('userProfiles', new Document([ - '$id' => '1', - 'username' => 'user1', - 'links' => [ - [ - '$id' => 'link1', - 'title' => 'Link 1', - ], - ], - 'videos' => [ - [ - '$id' => 'video1', - 'title' => 'Video 1', - ], - ], - 'products' => [ - [ - '$id' => 'product1', - 'title' => 'Product 1', - ], - ], - 'settings' => [ - '$id' => 'settings1', - 'metaTitle' => 'Meta Title', - ], - 'appearance' => [ - '$id' => 'appearance1', - 'metaTitle' => 'Meta Title', - ], - 'group' => [ - '$id' => 'group1', - 'name' => 'Group 1', - ], - 'community' => [ - '$id' => 'community1', - 'name' => 'Community 1', - ], - ])); - $this->assertEquals('link1', $profile->getAttribute('links')[0]->getId()); - $this->assertEquals('settings1', $profile->getAttribute('settings')->getId()); - $this->assertEquals('group1', $profile->getAttribute('group')->getId()); - $this->assertEquals('community1', $profile->getAttribute('community')->getId()); - $this->assertEquals('video1', $profile->getAttribute('videos')[0]->getId()); - $this->assertEquals('product1', $profile->getAttribute('products')[0]->getId()); - $this->assertEquals('appearance1', $profile->getAttribute('appearance')->getId()); - - $profile->setAttribute('links', [ - [ - '$id' => 'link1', - 'title' => 'New Link Value', - ], - ]); - - $profile->setAttribute('settings', [ - '$id' => 'settings1', - 'metaTitle' => 'New Meta Title', - ]); - - $profile->setAttribute('group', [ - '$id' => 'group1', - 'name' => 'New Group Name', - ]); - - $updatedProfile = static::getDatabase()->updateDocument('userProfiles', '1', $profile); - - $this->assertEquals('New Link Value', $updatedProfile->getAttribute('links')[0]->getAttribute('title')); - $this->assertEquals('New Meta Title', $updatedProfile->getAttribute('settings')->getAttribute('metaTitle')); - $this->assertEquals('New Group Name', $updatedProfile->getAttribute('group')->getAttribute('name')); - - // This is the point of test, related documents should be present if they are not updated - $this->assertEquals('Video 1', $updatedProfile->getAttribute('videos')[0]->getAttribute('title')); - $this->assertEquals('Product 1', $updatedProfile->getAttribute('products')[0]->getAttribute('title')); - $this->assertEquals('Meta Title', $updatedProfile->getAttribute('appearance')->getAttribute('metaTitle')); - $this->assertEquals('Community 1', $updatedProfile->getAttribute('community')->getAttribute('name')); - - // updating document using two way key in one to many relationship - $product = static::getDatabase()->getDocument('products', 'product1'); - $product->setAttribute('userProfile', [ - '$id' => '1', - 'username' => 'updated user value', - ]); - $updatedProduct = static::getDatabase()->updateDocument('products', 'product1', $product); - $this->assertEquals('updated user value', $updatedProduct->getAttribute('userProfile')->getAttribute('username')); - $this->assertEquals('Product 1', $updatedProduct->getAttribute('title')); - $this->assertEquals('product1', $updatedProduct->getId()); - $this->assertEquals('1', $updatedProduct->getAttribute('userProfile')->getId()); - - static::getDatabase()->deleteCollection('userProfiles'); - static::getDatabase()->deleteCollection('links'); - static::getDatabase()->deleteCollection('settings'); - static::getDatabase()->deleteCollection('group'); - static::getDatabase()->deleteCollection('community'); - static::getDatabase()->deleteCollection('videos'); - static::getDatabase()->deleteCollection('products'); - static::getDatabase()->deleteCollection('appearance'); - } - - public function testLabels(): void - { - $this->assertInstanceOf('Utopia\Database\Document', static::getDatabase()->createCollection( - 'labels_test', - )); - static::getDatabase()->createAttribute('labels_test', 'attr1', Database::VAR_STRING, 10, false); - - static::getDatabase()->createDocument('labels_test', new Document([ - '$id' => 'doc1', - 'attr1' => 'value1', - '$permissions' => [ - Permission::read(Role::label('reader')), - ], - ])); - - $documents = static::getDatabase()->find('labels_test'); - - $this->assertEmpty($documents); - - Authorization::setRole(Role::label('reader')->toString()); - - $documents = static::getDatabase()->find('labels_test'); - - $this->assertCount(1, $documents); - } - - public function testEmptyOperatorValues(): void - { - try { - static::getDatabase()->findOne('documents', [ - Query::equal('string', []), - ]); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertInstanceOf(Exception::class, $e); - $this->assertEquals('Invalid query: Equal queries require at least one value.', $e->getMessage()); - } - - try { - static::getDatabase()->findOne('documents', [ - Query::search('string', null), - ]); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertInstanceOf(Exception::class, $e); - $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); - } - - try { - static::getDatabase()->findOne('documents', [ - Query::notEqual('string', []), - ]); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertInstanceOf(Exception::class, $e); - $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); - } - - try { - static::getDatabase()->findOne('documents', [ - Query::lessThan('string', []), - ]); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertInstanceOf(Exception::class, $e); - $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); - } - - try { - static::getDatabase()->findOne('documents', [ - Query::lessThanEqual('string', []), - ]); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertInstanceOf(Exception::class, $e); - $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); - } - - try { - static::getDatabase()->findOne('documents', [ - Query::greaterThan('string', []), - ]); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertInstanceOf(Exception::class, $e); - $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); - } - - try { - static::getDatabase()->findOne('documents', [ - Query::greaterThanEqual('string', []), - ]); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertInstanceOf(Exception::class, $e); - $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); - } - - try { - static::getDatabase()->findOne('documents', [ - Query::contains('string', []), - ]); - $this->fail('Failed to throw exception'); - } catch (Exception $e) { - $this->assertInstanceOf(Exception::class, $e); - $this->assertEquals('Invalid query: Contains queries require at least one value.', $e->getMessage()); - } - } - - public function testEvents(): void - { - Authorization::skip(function () { - $database = static::getDatabase(); - - $events = [ - Database::EVENT_DATABASE_CREATE, - Database::EVENT_DATABASE_LIST, - Database::EVENT_COLLECTION_CREATE, - Database::EVENT_COLLECTION_LIST, - Database::EVENT_COLLECTION_READ, - Database::EVENT_ATTRIBUTE_CREATE, - Database::EVENT_ATTRIBUTE_UPDATE, - Database::EVENT_INDEX_CREATE, - Database::EVENT_DOCUMENT_CREATE, - Database::EVENT_DOCUMENT_UPDATE, - Database::EVENT_DOCUMENT_READ, - Database::EVENT_DOCUMENT_FIND, - Database::EVENT_DOCUMENT_FIND, - Database::EVENT_DOCUMENT_COUNT, - Database::EVENT_DOCUMENT_SUM, - Database::EVENT_DOCUMENT_INCREASE, - Database::EVENT_DOCUMENT_DECREASE, - Database::EVENT_INDEX_DELETE, - Database::EVENT_DOCUMENT_DELETE, - Database::EVENT_ATTRIBUTE_DELETE, - Database::EVENT_COLLECTION_DELETE, - Database::EVENT_DATABASE_DELETE, - ]; - - $database->on(Database::EVENT_ALL, 'test', function ($event, $data) use (&$events) { - $shifted = array_shift($events); - - $this->assertEquals($shifted, $event); - }); - - if ($this->getDatabase()->getAdapter()->getSupportForSchemas()) { - $database->setDefaultDatabase('hellodb'); - $database->create(); - } else { - array_shift($events); - } - - $database->list(); - - $database->setDefaultDatabase($this->testDatabase); - - $collectionId = ID::unique(); - $database->createCollection($collectionId); - $database->listCollections(); - $database->getCollection($collectionId); - $database->createAttribute($collectionId, 'attr1', Database::VAR_INTEGER, 2, false); - $database->updateAttributeRequired($collectionId, 'attr1', true); - $indexId1 = 'index2_' . uniqid(); - $database->createIndex($collectionId, $indexId1, Database::INDEX_KEY, ['attr1']); - - $document = $database->createDocument($collectionId, new Document([ - '$id' => 'doc1', - 'attr1' => 10, - '$permissions' => [ - Permission::delete(Role::any()), - Permission::update(Role::any()), - Permission::read(Role::any()), - ], - ])); - - $executed = false; - $database->on(Database::EVENT_ALL, 'should-not-execute', function ($event, $data) use (&$executed) { - $executed = true; - }); - - $database->silent(function () use ($database, $collectionId, $document) { - $database->updateDocument($collectionId, 'doc1', $document->setAttribute('attr1', 15)); - $database->getDocument($collectionId, 'doc1'); - $database->find($collectionId); - $database->findOne($collectionId); - $database->count($collectionId); - $database->sum($collectionId, 'attr1'); - $database->increaseDocumentAttribute($collectionId, $document->getId(), 'attr1'); - $database->decreaseDocumentAttribute($collectionId, $document->getId(), 'attr1'); - }, ['should-not-execute']); - - $this->assertFalse($executed); - - $database->deleteIndex($collectionId, $indexId1); - $database->deleteDocument($collectionId, 'doc1'); - $database->deleteAttribute($collectionId, 'attr1'); - $database->deleteCollection($collectionId); - $database->delete('hellodb'); - }); - } + $this->assertEquals(4, count($documents)); + $this->assertEquals(true, false); + + die; + + } +// +// /** +// * @depends testFind +// */ +// public function testFindOne(): void +// { +// $document = static::getDatabase()->findOne('movies', [ +// Query::offset(2), +// Query::orderAsc('name') +// ]); +// +// $this->assertTrue($document instanceof Document); +// $this->assertEquals('Frozen', $document->getAttribute('name')); +// +// $document = static::getDatabase()->findOne('movies', [ +// Query::offset(10) +// ]); +// $this->assertEquals(false, $document); +// } +// +// public function testFindNull(): void +// { +// $documents = static::getDatabase()->find('movies', [ +// Query::isNull('nullable'), +// ]); +// +// $this->assertEquals(5, count($documents)); +// } +// +// public function testFindNotNull(): void +// { +// $documents = static::getDatabase()->find('movies', [ +// Query::isNotNull('nullable'), +// ]); +// +// $this->assertEquals(1, count($documents)); +// } +// +// public function testFindStartsWith(): void +// { +// $documents = static::getDatabase()->find('movies', [ +// Query::startsWith('name', 'Work'), +// ]); +// +// $this->assertEquals(2, count($documents)); +// +// if ($this->getDatabase()->getAdapter() instanceof SQL) { +// $documents = static::getDatabase()->find('movies', [ +// Query::startsWith('name', '%ork'), +// ]); +// } else { +// $documents = static::getDatabase()->find('movies', [ +// Query::startsWith('name', '.*ork'), +// ]); +// } +// +// $this->assertEquals(0, count($documents)); +// } +// +// public function testFindStartsWithWords(): void +// { +// $documents = static::getDatabase()->find('movies', [ +// Query::startsWith('name', 'Work in Progress'), +// ]); +// +// $this->assertEquals(2, count($documents)); +// } +// +// public function testFindEndsWith(): void +// { +// $documents = static::getDatabase()->find('movies', [ +// Query::endsWith('name', 'Marvel'), +// ]); +// +// $this->assertEquals(1, count($documents)); +// } +// +// public function testFindSelect(): void +// { +// $documents = static::getDatabase()->find('movies', [ +// Query::select(['name', 'year']) +// ]); +// +// foreach ($documents as $document) { +// $this->assertArrayHasKey('name', $document); +// $this->assertArrayHasKey('year', $document); +// $this->assertArrayNotHasKey('director', $document); +// $this->assertArrayNotHasKey('price', $document); +// $this->assertArrayNotHasKey('active', $document); +// $this->assertArrayNotHasKey('$id', $document); +// $this->assertArrayNotHasKey('$internalId', $document); +// $this->assertArrayNotHasKey('$collection', $document); +// $this->assertArrayNotHasKey('$createdAt', $document); +// $this->assertArrayNotHasKey('$updatedAt', $document); +// $this->assertArrayNotHasKey('$permissions', $document); +// } +// +// $documents = static::getDatabase()->find('movies', [ +// Query::select(['name', 'year', '$id']) +// ]); +// +// foreach ($documents as $document) { +// $this->assertArrayHasKey('name', $document); +// $this->assertArrayHasKey('year', $document); +// $this->assertArrayNotHasKey('director', $document); +// $this->assertArrayNotHasKey('price', $document); +// $this->assertArrayNotHasKey('active', $document); +// $this->assertArrayHasKey('$id', $document); +// $this->assertArrayNotHasKey('$internalId', $document); +// $this->assertArrayNotHasKey('$collection', $document); +// $this->assertArrayNotHasKey('$createdAt', $document); +// $this->assertArrayNotHasKey('$updatedAt', $document); +// $this->assertArrayNotHasKey('$permissions', $document); +// } +// +// $documents = static::getDatabase()->find('movies', [ +// Query::select(['name', 'year', '$internalId']) +// ]); +// +// foreach ($documents as $document) { +// $this->assertArrayHasKey('name', $document); +// $this->assertArrayHasKey('year', $document); +// $this->assertArrayNotHasKey('director', $document); +// $this->assertArrayNotHasKey('price', $document); +// $this->assertArrayNotHasKey('active', $document); +// $this->assertArrayNotHasKey('$id', $document); +// $this->assertArrayHasKey('$internalId', $document); +// $this->assertArrayNotHasKey('$collection', $document); +// $this->assertArrayNotHasKey('$createdAt', $document); +// $this->assertArrayNotHasKey('$updatedAt', $document); +// $this->assertArrayNotHasKey('$permissions', $document); +// } +// +// $documents = static::getDatabase()->find('movies', [ +// Query::select(['name', 'year', '$collection']) +// ]); +// +// foreach ($documents as $document) { +// $this->assertArrayHasKey('name', $document); +// $this->assertArrayHasKey('year', $document); +// $this->assertArrayNotHasKey('director', $document); +// $this->assertArrayNotHasKey('price', $document); +// $this->assertArrayNotHasKey('active', $document); +// $this->assertArrayNotHasKey('$id', $document); +// $this->assertArrayNotHasKey('$internalId', $document); +// $this->assertArrayHasKey('$collection', $document); +// $this->assertArrayNotHasKey('$createdAt', $document); +// $this->assertArrayNotHasKey('$updatedAt', $document); +// $this->assertArrayNotHasKey('$permissions', $document); +// } +// +// $documents = static::getDatabase()->find('movies', [ +// Query::select(['name', 'year', '$createdAt']) +// ]); +// +// foreach ($documents as $document) { +// $this->assertArrayHasKey('name', $document); +// $this->assertArrayHasKey('year', $document); +// $this->assertArrayNotHasKey('director', $document); +// $this->assertArrayNotHasKey('price', $document); +// $this->assertArrayNotHasKey('active', $document); +// $this->assertArrayNotHasKey('$id', $document); +// $this->assertArrayNotHasKey('$internalId', $document); +// $this->assertArrayNotHasKey('$collection', $document); +// $this->assertArrayHasKey('$createdAt', $document); +// $this->assertArrayNotHasKey('$updatedAt', $document); +// $this->assertArrayNotHasKey('$permissions', $document); +// } +// +// $documents = static::getDatabase()->find('movies', [ +// Query::select(['name', 'year', '$updatedAt']) +// ]); +// +// foreach ($documents as $document) { +// $this->assertArrayHasKey('name', $document); +// $this->assertArrayHasKey('year', $document); +// $this->assertArrayNotHasKey('director', $document); +// $this->assertArrayNotHasKey('price', $document); +// $this->assertArrayNotHasKey('active', $document); +// $this->assertArrayNotHasKey('$id', $document); +// $this->assertArrayNotHasKey('$internalId', $document); +// $this->assertArrayNotHasKey('$collection', $document); +// $this->assertArrayNotHasKey('$createdAt', $document); +// $this->assertArrayHasKey('$updatedAt', $document); +// $this->assertArrayNotHasKey('$permissions', $document); +// } +// +// $documents = static::getDatabase()->find('movies', [ +// Query::select(['name', 'year', '$permissions']) +// ]); +// +// foreach ($documents as $document) { +// $this->assertArrayHasKey('name', $document); +// $this->assertArrayHasKey('year', $document); +// $this->assertArrayNotHasKey('director', $document); +// $this->assertArrayNotHasKey('price', $document); +// $this->assertArrayNotHasKey('active', $document); +// $this->assertArrayNotHasKey('$id', $document); +// $this->assertArrayNotHasKey('$internalId', $document); +// $this->assertArrayNotHasKey('$collection', $document); +// $this->assertArrayNotHasKey('$createdAt', $document); +// $this->assertArrayNotHasKey('$updatedAt', $document); +// $this->assertArrayHasKey('$permissions', $document); +// } +// } +// +// /** +// * @depends testFind +// */ +// public function testCount(): void +// { +// $count = static::getDatabase()->count('movies'); +// $this->assertEquals(6, $count); +// $count = static::getDatabase()->count('movies', [Query::equal('year', [2019])]); +// $this->assertEquals(2, $count); +// $count = static::getDatabase()->count('movies', [Query::equal('with-dash', ['Works'])]); +// $this->assertEquals(2, $count); +// $count = static::getDatabase()->count('movies', [Query::equal('with-dash', ['Works2', 'Works3'])]); +// $this->assertEquals(4, $count); +// +// Authorization::unsetRole('user:x'); +// $count = static::getDatabase()->count('movies'); +// $this->assertEquals(5, $count); +// +// Authorization::disable(); +// $count = static::getDatabase()->count('movies'); +// $this->assertEquals(6, $count); +// Authorization::reset(); +// +// Authorization::disable(); +// $count = static::getDatabase()->count('movies', [], 3); +// $this->assertEquals(3, $count); +// Authorization::reset(); +// +// /** +// * Test that OR queries are handled correctly +// */ +// Authorization::disable(); +// $count = static::getDatabase()->count('movies', [ +// Query::equal('director', ['TBD', 'Joe Johnston']), +// Query::equal('year', [2025]), +// ]); +// $this->assertEquals(1, $count); +// Authorization::reset(); +// } +// +// /** +// * @depends testFind +// */ +// public function testSum(): void +// { +// Authorization::setRole('user:x'); +// $sum = static::getDatabase()->sum('movies', 'year', [Query::equal('year', [2019]),]); +// $this->assertEquals(2019 + 2019, $sum); +// $sum = static::getDatabase()->sum('movies', 'year'); +// $this->assertEquals(2013 + 2019 + 2011 + 2019 + 2025 + 2026, $sum); +// $sum = static::getDatabase()->sum('movies', 'price', [Query::equal('year', [2019]),]); +// $this->assertEquals(round(39.50 + 25.99, 2), round($sum, 2)); +// $sum = static::getDatabase()->sum('movies', 'price', [Query::equal('year', [2019]),]); +// $this->assertEquals(round(39.50 + 25.99, 2), round($sum, 2)); +// +// $sum = static::getDatabase()->sum('movies', 'year', [Query::equal('year', [2019])], 1); +// $this->assertEquals(2019, $sum); +// +// Authorization::unsetRole('user:x'); +// Authorization::unsetRole('userx'); +// $sum = static::getDatabase()->sum('movies', 'year', [Query::equal('year', [2019]),]); +// $this->assertEquals(2019 + 2019, $sum); +// $sum = static::getDatabase()->sum('movies', 'year'); +// $this->assertEquals(2013 + 2019 + 2011 + 2019 + 2025, $sum); +// $sum = static::getDatabase()->sum('movies', 'price', [Query::equal('year', [2019]),]); +// $this->assertEquals(round(39.50 + 25.99, 2), round($sum, 2)); +// $sum = static::getDatabase()->sum('movies', 'price', [Query::equal('year', [2019]),]); +// $this->assertEquals(round(39.50 + 25.99, 2), round($sum, 2)); +// } +// +// public function testEncodeDecode(): void +// { +// $collection = new Document([ +// '$collection' => ID::custom(Database::METADATA), +// '$id' => ID::custom('users'), +// 'name' => 'Users', +// 'attributes' => [ +// [ +// '$id' => ID::custom('name'), +// 'type' => Database::VAR_STRING, +// 'format' => '', +// 'size' => 256, +// 'signed' => true, +// 'required' => false, +// 'array' => false, +// 'filters' => [], +// ], +// [ +// '$id' => ID::custom('email'), +// 'type' => Database::VAR_STRING, +// 'format' => '', +// 'size' => 1024, +// 'signed' => true, +// 'required' => false, +// 'array' => false, +// 'filters' => [], +// ], +// [ +// '$id' => ID::custom('status'), +// 'type' => Database::VAR_INTEGER, +// 'format' => '', +// 'size' => 0, +// 'signed' => true, +// 'required' => false, +// 'array' => false, +// 'filters' => [], +// ], +// [ +// '$id' => ID::custom('password'), +// 'type' => Database::VAR_STRING, +// 'format' => '', +// 'size' => 16384, +// 'signed' => true, +// 'required' => false, +// 'array' => false, +// 'filters' => [], +// ], +// [ +// '$id' => ID::custom('passwordUpdate'), +// 'type' => Database::VAR_DATETIME, +// 'format' => '', +// 'size' => 0, +// 'signed' => true, +// 'required' => false, +// 'array' => false, +// 'filters' => ['datetime'], +// ], +// [ +// '$id' => ID::custom('registration'), +// 'type' => Database::VAR_DATETIME, +// 'format' => '', +// 'size' => 0, +// 'signed' => true, +// 'required' => false, +// 'array' => false, +// 'filters' => ['datetime'], +// ], +// [ +// '$id' => ID::custom('emailVerification'), +// 'type' => Database::VAR_BOOLEAN, +// 'format' => '', +// 'size' => 0, +// 'signed' => true, +// 'required' => false, +// 'array' => false, +// 'filters' => [], +// ], +// [ +// '$id' => ID::custom('reset'), +// 'type' => Database::VAR_BOOLEAN, +// 'format' => '', +// 'size' => 0, +// 'signed' => true, +// 'required' => false, +// 'array' => false, +// 'filters' => [], +// ], +// [ +// '$id' => ID::custom('prefs'), +// 'type' => Database::VAR_STRING, +// 'format' => '', +// 'size' => 16384, +// 'signed' => true, +// 'required' => false, +// 'array' => false, +// 'filters' => ['json'] +// ], +// [ +// '$id' => ID::custom('sessions'), +// 'type' => Database::VAR_STRING, +// 'format' => '', +// 'size' => 16384, +// 'signed' => true, +// 'required' => false, +// 'array' => false, +// 'filters' => ['json'], +// ], +// [ +// '$id' => ID::custom('tokens'), +// 'type' => Database::VAR_STRING, +// 'format' => '', +// 'size' => 16384, +// 'signed' => true, +// 'required' => false, +// 'array' => false, +// 'filters' => ['json'], +// ], +// [ +// '$id' => ID::custom('memberships'), +// 'type' => Database::VAR_STRING, +// 'format' => '', +// 'size' => 16384, +// 'signed' => true, +// 'required' => false, +// 'array' => false, +// 'filters' => ['json'], +// ], +// [ +// '$id' => ID::custom('roles'), +// 'type' => Database::VAR_STRING, +// 'format' => '', +// 'size' => 128, +// 'signed' => true, +// 'required' => false, +// 'array' => true, +// 'filters' => [], +// ], +// [ +// '$id' => ID::custom('tags'), +// 'type' => Database::VAR_STRING, +// 'format' => '', +// 'size' => 128, +// 'signed' => true, +// 'required' => false, +// 'array' => true, +// 'filters' => ['json'], +// ], +// ], +// 'indexes' => [ +// [ +// '$id' => ID::custom('_key_email'), +// 'type' => Database::INDEX_UNIQUE, +// 'attributes' => ['email'], +// 'lengths' => [1024], +// 'orders' => [Database::ORDER_ASC], +// ] +// ], +// ]); +// +// $document = new Document([ +// '$id' => ID::custom('608fdbe51361a'), +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::create(Role::user('608fdbe51361a')), +// Permission::update(Role::user('608fdbe51361a')), +// Permission::delete(Role::user('608fdbe51361a')), +// ], +// 'email' => 'test@example.com', +// 'emailVerification' => false, +// 'status' => 1, +// 'password' => 'randomhash', +// 'passwordUpdate' => '2000-06-12 14:12:55', +// 'registration' => '1975-06-12 14:12:55+01:00', +// 'reset' => false, +// 'name' => 'My Name', +// 'prefs' => new \stdClass(), +// 'sessions' => [], +// 'tokens' => [], +// 'memberships' => [], +// 'roles' => [ +// 'admin', +// 'developer', +// 'tester', +// ], +// 'tags' => [ +// ['$id' => '1', 'label' => 'x'], +// ['$id' => '2', 'label' => 'y'], +// ['$id' => '3', 'label' => 'z'], +// ], +// ]); +// +// $result = static::getDatabase()->encode($collection, $document); +// +// $this->assertEquals('608fdbe51361a', $result->getAttribute('$id')); +// $this->assertContains('read("any")', $result->getAttribute('$permissions')); +// $this->assertContains('read("any")', $result->getPermissions()); +// $this->assertContains('any', $result->getRead()); +// $this->assertContains(Permission::create(Role::user(ID::custom('608fdbe51361a'))), $result->getPermissions()); +// $this->assertContains('user:608fdbe51361a', $result->getCreate()); +// $this->assertContains('user:608fdbe51361a', $result->getWrite()); +// $this->assertEquals('test@example.com', $result->getAttribute('email')); +// $this->assertEquals(false, $result->getAttribute('emailVerification')); +// $this->assertEquals(1, $result->getAttribute('status')); +// $this->assertEquals('randomhash', $result->getAttribute('password')); +// $this->assertEquals('2000-06-12 14:12:55.000', $result->getAttribute('passwordUpdate')); +// $this->assertEquals('1975-06-12 13:12:55.000', $result->getAttribute('registration')); +// $this->assertEquals(false, $result->getAttribute('reset')); +// $this->assertEquals('My Name', $result->getAttribute('name')); +// $this->assertEquals('{}', $result->getAttribute('prefs')); +// $this->assertEquals('[]', $result->getAttribute('sessions')); +// $this->assertEquals('[]', $result->getAttribute('tokens')); +// $this->assertEquals('[]', $result->getAttribute('memberships')); +// $this->assertEquals(['admin', 'developer', 'tester',], $result->getAttribute('roles')); +// $this->assertEquals(['{"$id":"1","label":"x"}', '{"$id":"2","label":"y"}', '{"$id":"3","label":"z"}',], $result->getAttribute('tags')); +// +// $result = static::getDatabase()->decode($collection, $document); +// +// $this->assertEquals('608fdbe51361a', $result->getAttribute('$id')); +// $this->assertContains('read("any")', $result->getAttribute('$permissions')); +// $this->assertContains('read("any")', $result->getPermissions()); +// $this->assertContains('any', $result->getRead()); +// $this->assertContains(Permission::create(Role::user('608fdbe51361a')), $result->getPermissions()); +// $this->assertContains('user:608fdbe51361a', $result->getCreate()); +// $this->assertContains('user:608fdbe51361a', $result->getWrite()); +// $this->assertEquals('test@example.com', $result->getAttribute('email')); +// $this->assertEquals(false, $result->getAttribute('emailVerification')); +// $this->assertEquals(1, $result->getAttribute('status')); +// $this->assertEquals('randomhash', $result->getAttribute('password')); +// $this->assertEquals('2000-06-12T14:12:55.000+00:00', $result->getAttribute('passwordUpdate')); +// $this->assertEquals('1975-06-12T13:12:55.000+00:00', $result->getAttribute('registration')); +// $this->assertEquals(false, $result->getAttribute('reset')); +// $this->assertEquals('My Name', $result->getAttribute('name')); +// $this->assertEquals([], $result->getAttribute('prefs')); +// $this->assertEquals([], $result->getAttribute('sessions')); +// $this->assertEquals([], $result->getAttribute('tokens')); +// $this->assertEquals([], $result->getAttribute('memberships')); +// $this->assertEquals(['admin', 'developer', 'tester',], $result->getAttribute('roles')); +// $this->assertEquals([ +// new Document(['$id' => '1', 'label' => 'x']), +// new Document(['$id' => '2', 'label' => 'y']), +// new Document(['$id' => '3', 'label' => 'z']), +// ], $result->getAttribute('tags')); +// } +// +// /** +// * @depends testCreateDocument +// */ +// public function testReadPermissionsSuccess(Document $document): Document +// { +// Authorization::cleanRoles(); +// Authorization::setRole(Role::any()->toString()); +// +// $document = static::getDatabase()->createDocument('documents', new Document([ +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'string' => 'text📝', +// 'integer' => 5, +// 'bigint' => 8589934592, // 2^33 +// 'float' => 5.55, +// 'boolean' => true, +// 'colors' => ['pink', 'green', 'blue'], +// ])); +// +// $this->assertEquals(false, $document->isEmpty()); +// +// Authorization::cleanRoles(); +// +// $document = static::getDatabase()->getDocument($document->getCollection(), $document->getId()); +// $this->assertEquals(true, $document->isEmpty()); +// +// Authorization::setRole(Role::any()->toString()); +// +// return $document; +// } +// +// public function testReadPermissionsFailure(): Document +// { +// Authorization::cleanRoles(); +// Authorization::setRole(Role::any()->toString()); +// +// $document = static::getDatabase()->createDocument('documents', new Document([ +// '$permissions' => [ +// Permission::read(Role::user('1')), +// Permission::create(Role::user('1')), +// Permission::update(Role::user('1')), +// Permission::delete(Role::user('1')), +// ], +// 'string' => 'text📝', +// 'integer' => 5, +// 'bigint' => 8589934592, // 2^33 +// 'float' => 5.55, +// 'boolean' => true, +// 'colors' => ['pink', 'green', 'blue'], +// ])); +// +// Authorization::cleanRoles(); +// +// $document = static::getDatabase()->getDocument($document->getCollection(), $document->getId()); +// +// $this->assertEquals(true, $document->isEmpty()); +// +// Authorization::setRole(Role::any()->toString()); +// +// return $document; +// } +// +// /** +// * @depends testCreateDocument +// */ +// public function testWritePermissionsSuccess(Document $document): void +// { +// Authorization::cleanRoles(); +// +// $this->expectException(AuthorizationException::class); +// static::getDatabase()->createDocument('documents', new Document([ +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'string' => 'text📝', +// 'integer' => 5, +// 'bigint' => 8589934592, // 2^33 +// 'float' => 5.55, +// 'boolean' => true, +// 'colors' => ['pink', 'green', 'blue'], +// ])); +// } +// +// /** +// * @depends testCreateDocument +// */ +// public function testWritePermissionsUpdateFailure(Document $document): Document +// { +// $this->expectException(AuthorizationException::class); +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::any()->toString()); +// +// $document = static::getDatabase()->createDocument('documents', new Document([ +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'string' => 'text📝', +// 'integer' => 5, +// 'bigint' => 8589934592, // 2^33 +// 'float' => 5.55, +// 'boolean' => true, +// 'colors' => ['pink', 'green', 'blue'], +// ])); +// +// Authorization::cleanRoles(); +// +// $document = static::getDatabase()->updateDocument('documents', $document->getId(), new Document([ +// '$id' => ID::custom($document->getId()), +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'string' => 'text📝', +// 'integer' => 6, +// 'bigint' => 8589934592, // 2^33 +// 'float' => 5.55, +// 'boolean' => true, +// 'colors' => ['pink', 'green', 'blue'], +// ])); +// +// return $document; +// } +// +// /** +// * @depends testCreateDocument +// */ +// public function testNoChangeUpdateDocumentWithoutPermission(Document $document): Document +// { +// $document = static::getDatabase()->createDocument('documents', new Document([ +// '$id' => ID::unique(), +// '$permissions' => [], +// 'string' => 'text📝', +// 'integer' => 5, +// 'bigint' => 8589934592, // 2^33 +// 'float' => 5.55, +// 'boolean' => true, +// 'colors' => ['pink', 'green', 'blue'], +// ])); +// +// $updatedDocument = static::getDatabase()->updateDocument( +// 'documents', +// $document->getId(), +// $document +// ); +// +// // Document should not be updated as there is no change. +// // It should also not throw any authorization exception without any permission because of no change. +// $this->assertEquals($updatedDocument->getUpdatedAt(), $document->getUpdatedAt()); +// +// return $document; +// } +// +// public function testNoChangeUpdateDocumentWithRelationWithoutPermission(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// $attribute = new Document([ +// '$id' => ID::custom("name"), +// 'type' => Database::VAR_STRING, +// 'size' => 100, +// 'required' => false, +// 'default' => null, +// 'signed' => false, +// 'array' => false, +// 'filters' => [], +// ]); +// +// $permissions = [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::delete(Role::any()), +// ]; +// for ($i=1; $i < 6; $i++) { +// static::getDatabase()->createCollection("level{$i}", [$attribute], [], $permissions); +// } +// +// for ($i = 1; $i < 5; $i++) { +// $collectionId = $i; +// $relatedCollectionId = $i+1; +// static::getDatabase()->createRelationship( +// collection: "level{$collectionId}", +// relatedCollection: "level{$relatedCollectionId}", +// type: Database::RELATION_ONE_TO_ONE, +// id: "level{$relatedCollectionId}" +// ); +// } +// +// // Create document with relationship with nested data +// $level1 = static::getDatabase()->createDocument('level1', new Document([ +// '$id' => 'level1', +// '$permissions' => [], +// 'name' => 'Level 1', +// 'level2' => [ +// '$id' => 'level2', +// '$permissions' => [], +// 'name' => 'Level 2', +// 'level3' => [ +// '$id' => 'level3', +// '$permissions' => [], +// 'name' => 'Level 3', +// 'level4' => [ +// '$id' => 'level4', +// '$permissions' => [], +// 'name' => 'Level 4', +// 'level5' => [ +// '$id' => 'level5', +// '$permissions' => [], +// 'name' => 'Level 5', +// ] +// ], +// ], +// ], +// ])); +// static::getDatabase()->updateDocument('level1', $level1->getId(), new Document($level1->getArrayCopy())); +// $updatedLevel1 = static::getDatabase()->getDocument('level1', $level1->getId()); +// $this->assertEquals($level1, $updatedLevel1); +// +// try { +// static::getDatabase()->updateDocument('level1', $level1->getId(), $level1->setAttribute('name', 'haha')); +// $this->fail('Failed to throw exception'); +// } catch(Exception $e) { +// $this->assertInstanceOf(AuthorizationException::class, $e); +// } +// $level1->setAttribute('name', 'Level 1'); +// static::getDatabase()->updateCollection('level3', [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], false); +// $level2 = $level1->getAttribute('level2'); +// $level3 = $level2->getAttribute('level3'); +// +// $level3->setAttribute('name', 'updated value'); +// $level2->setAttribute('level3', $level3); +// $level1->setAttribute('level2', $level2); +// +// $level1 = static::getDatabase()->updateDocument('level1', $level1->getId(), $level1); +// $this->assertEquals('updated value', $level1['level2']['level3']['name']); +// +// for ($i=1; $i < 6; $i++) { +// static::getDatabase()->deleteCollection("level{$i}"); +// } +// } +// +// public function testExceptionAttributeLimit(): void +// { +// if ($this->getDatabase()->getLimitForAttributes() > 0) { +// // Load the collection up to the limit +// $attributes = []; +// for ($i = 0; $i < $this->getDatabase()->getLimitForAttributes(); $i++) { +// $attributes[] = new Document([ +// '$id' => ID::custom("test{$i}"), +// 'type' => Database::VAR_INTEGER, +// 'size' => 0, +// 'required' => false, +// 'default' => null, +// 'signed' => true, +// 'array' => false, +// 'filters' => [], +// ]); +// } +// +// static::getDatabase()->createCollection('attributeLimit', $attributes); +// +// $this->expectException(LimitException::class); +// $this->assertEquals(false, static::getDatabase()->createAttribute('attributeLimit', "breaking", Database::VAR_INTEGER, 0, true)); +// } +// +// // Default assertion for other adapters +// $this->assertEquals(1, 1); +// } +// +// /** +// * @depends testExceptionAttributeLimit +// */ +// public function testCheckAttributeCountLimit(): void +// { +// if ($this->getDatabase()->getLimitForAttributes() > 0) { +// $collection = static::getDatabase()->getCollection('attributeLimit'); +// +// // create same attribute in testExceptionAttributeLimit +// $attribute = new Document([ +// '$id' => ID::custom('breaking'), +// 'type' => Database::VAR_INTEGER, +// 'size' => 0, +// 'required' => true, +// 'default' => null, +// 'signed' => true, +// 'array' => false, +// 'filters' => [], +// ]); +// +// $this->expectException(LimitException::class); +// $this->assertEquals(false, static::getDatabase()->checkAttribute($collection, $attribute)); +// } +// +// // Default assertion for other adapters +// $this->assertEquals(1, 1); +// } +// +// /** +// * Using phpunit dataProviders to check that all these combinations of types/sizes throw exceptions +// * https://phpunit.de/manual/3.7/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers +// * +// * @return array> +// */ +// public function rowWidthExceedsMaximum(): array +// { +// return [ +// // These combinations of attributes gets exactly to the 64k limit +// // [$key, $stringSize, $stringCount, $intCount, $floatCount, $boolCount] +// // [0, 1024, 15, 0, 731, 3], +// // [1, 512, 31, 0, 0, 833], +// // [2, 256, 62, 128, 0, 305], +// // [3, 128, 125, 30, 24, 2], +// // +// // Taken 500 bytes off for tests +// [0, 1024, 15, 0, 304, 3], +// [1, 512, 31, 0, 0, 333], +// [2, 256, 62, 103, 0, 5], +// [3, 128, 124, 30, 12, 14], +// ]; +// } +// +// /** +// * @dataProvider rowWidthExceedsMaximum +// */ +// public function testExceptionWidthLimit(int $key, int $stringSize, int $stringCount, int $intCount, int $floatCount, int $boolCount): void +// { +// if (static::getDatabase()->getAdapter()::getDocumentSizeLimit() > 0) { +// $attributes = []; +// +// // Load the collection up to the limit +// // Strings +// for ($i = 0; $i < $stringCount; $i++) { +// $attributes[] = new Document([ +// '$id' => ID::custom("test_string{$i}"), +// 'type' => Database::VAR_STRING, +// 'size' => $stringSize, +// 'required' => false, +// 'default' => null, +// 'signed' => true, +// 'array' => false, +// 'filters' => [], +// ]); +// } +// +// // Integers +// for ($i = 0; $i < $intCount; $i++) { +// $attributes[] = new Document([ +// '$id' => ID::custom("test_int{$i}"), +// 'type' => Database::VAR_INTEGER, +// 'size' => 0, +// 'required' => false, +// 'default' => null, +// 'signed' => true, +// 'array' => false, +// 'filters' => [], +// ]); +// } +// +// // Floats +// for ($i = 0; $i < $floatCount; $i++) { +// $attributes[] = new Document([ +// '$id' => ID::custom("test_float{$i}"), +// 'type' => Database::VAR_FLOAT, +// 'size' => 0, +// 'required' => false, +// 'default' => null, +// 'signed' => true, +// 'array' => false, +// 'filters' => [], +// ]); +// } +// +// // Booleans +// for ($i = 0; $i < $boolCount; $i++) { +// $attributes[] = new Document([ +// '$id' => ID::custom("test_bool{$i}"), +// 'type' => Database::VAR_BOOLEAN, +// 'size' => 0, +// 'required' => false, +// 'default' => null, +// 'signed' => true, +// 'array' => false, +// 'filters' => [], +// ]); +// } +// +// $collection = static::getDatabase()->createCollection("widthLimit{$key}", $attributes); +// +// $this->expectException(LimitException::class); +// $this->assertEquals(false, static::getDatabase()->createAttribute("widthLimit{$key}", "breaking", Database::VAR_STRING, 100, true)); +// } +// +// // Default assertion for other adapters +// $this->assertEquals(1, 1); +// } +// +// /** +// * @dataProvider rowWidthExceedsMaximum +// * @depends testExceptionWidthLimit +// */ +// public function testCheckAttributeWidthLimit(int $key, int $stringSize, int $stringCount, int $intCount, int $floatCount, int $boolCount): void +// { +// if (static::getDatabase()->getAdapter()::getDocumentSizeLimit()> 0) { +// $collection = static::getDatabase()->getCollection("widthLimit{$key}"); +// +// // create same attribute in testExceptionWidthLimit +// $attribute = new Document([ +// '$id' => ID::custom('breaking'), +// 'type' => Database::VAR_STRING, +// 'size' => 100, +// 'required' => true, +// 'default' => null, +// 'signed' => true, +// 'array' => false, +// 'filters' => [], +// ]); +// +// $this->expectException(LimitException::class); +// $this->assertEquals(false, static::getDatabase()->checkAttribute($collection, $attribute)); +// } +// +// // Default assertion for other adapters +// $this->assertEquals(1, 1); +// } +// +// public function testExceptionIndexLimit(): void +// { +// static::getDatabase()->createCollection('indexLimit'); +// +// // add unique attributes for indexing +// for ($i = 0; $i < 64; $i++) { +// $this->assertEquals(true, static::getDatabase()->createAttribute('indexLimit', "test{$i}", Database::VAR_STRING, 16, true)); +// } +// +// // Testing for indexLimit +// // Add up to the limit, then check if the next index throws IndexLimitException +// for ($i = 0; $i < ($this->getDatabase()->getLimitForIndexes()); $i++) { +// $this->assertEquals(true, static::getDatabase()->createIndex('indexLimit', "index{$i}", Database::INDEX_KEY, ["test{$i}"], [16])); +// } +// $this->expectException(LimitException::class); +// $this->assertEquals(false, static::getDatabase()->createIndex('indexLimit', "index64", Database::INDEX_KEY, ["test64"], [16])); +// +// static::getDatabase()->deleteCollection('indexLimit'); +// } +// +// /** +// * @depends testGetDocument +// */ +// public function testExceptionDuplicate(Document $document): void +// { +// $document->setAttribute('$id', 'duplicated'); +// static::getDatabase()->createDocument($document->getCollection(), $document); +// +// $this->expectException(DuplicateException::class); +// static::getDatabase()->createDocument($document->getCollection(), $document); +// } +// +// /** +// * @depends testGetDocument +// */ +// public function testExceptionCaseInsensitiveDuplicate(Document $document): Document +// { +// $document->setAttribute('$id', 'caseSensitive'); +// $document->setAttribute('$internalId', '200'); +// static::getDatabase()->createDocument($document->getCollection(), $document); +// +// $document->setAttribute('$id', 'CaseSensitive'); +// +// $this->expectException(DuplicateException::class); +// static::getDatabase()->createDocument($document->getCollection(), $document); +// +// return $document; +// } +// +// /** +// * @depends testFind +// */ +// public function testUniqueIndexDuplicate(): void +// { +// $this->expectException(DuplicateException::class); +// +// $this->assertEquals(true, static::getDatabase()->createIndex('movies', 'uniqueIndex', Database::INDEX_UNIQUE, ['name'], [128], [Database::ORDER_ASC])); +// +// static::getDatabase()->createDocument('movies', new Document([ +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::read(Role::user('1')), +// Permission::read(Role::user('2')), +// Permission::create(Role::any()), +// Permission::create(Role::user('1x')), +// Permission::create(Role::user('2x')), +// Permission::update(Role::any()), +// Permission::update(Role::user('1x')), +// Permission::update(Role::user('2x')), +// Permission::delete(Role::any()), +// Permission::delete(Role::user('1x')), +// Permission::delete(Role::user('2x')), +// ], +// 'name' => 'Frozen', +// 'director' => 'Chris Buck & Jennifer Lee', +// 'year' => 2013, +// 'price' => 39.50, +// 'active' => true, +// 'generes' => ['animation', 'kids'], +// 'with-dash' => 'Works4' +// ])); +// } +// +// /** +// * @depends testUniqueIndexDuplicate +// */ +// public function testUniqueIndexDuplicateUpdate(): void +// { +// Authorization::setRole(Role::users()->toString()); +// // create document then update to conflict with index +// $document = static::getDatabase()->createDocument('movies', new Document([ +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::read(Role::user('1')), +// Permission::read(Role::user('2')), +// Permission::create(Role::any()), +// Permission::create(Role::user('1x')), +// Permission::create(Role::user('2x')), +// Permission::update(Role::any()), +// Permission::update(Role::user('1x')), +// Permission::update(Role::user('2x')), +// Permission::delete(Role::any()), +// Permission::delete(Role::user('1x')), +// Permission::delete(Role::user('2x')), +// ], +// 'name' => 'Frozen 5', +// 'director' => 'Chris Buck & Jennifer Lee', +// 'year' => 2013, +// 'price' => 39.50, +// 'active' => true, +// 'generes' => ['animation', 'kids'], +// 'with-dash' => 'Works4' +// ])); +// +// $this->expectException(DuplicateException::class); +// +// static::getDatabase()->updateDocument('movies', $document->getId(), $document->setAttribute('name', 'Frozen')); +// } +// +// public function testGetAttributeLimit(): void +// { +// $this->assertIsInt($this->getDatabase()->getLimitForAttributes()); +// } +// +// public function testGetIndexLimit(): void +// { +// $this->assertEquals(59, $this->getDatabase()->getLimitForIndexes()); +// } +// +// public function testGetId(): void +// { +// $this->assertEquals(20, strlen(ID::unique())); +// $this->assertEquals(13, strlen(ID::unique(0))); +// $this->assertEquals(13, strlen(ID::unique(-1))); +// $this->assertEquals(23, strlen(ID::unique(10))); +// +// // ensure two sequential calls to getId do not give the same result +// $this->assertNotEquals(ID::unique(10), ID::unique(10)); +// } +// +// public function testRenameIndex(): void +// { +// $database = static::getDatabase(); +// +// $numbers = $database->createCollection('numbers'); +// $database->createAttribute('numbers', 'verbose', Database::VAR_STRING, 128, true); +// $database->createAttribute('numbers', 'symbol', Database::VAR_INTEGER, 0, true); +// +// $database->createIndex('numbers', 'index1', Database::INDEX_KEY, ['verbose'], [128], [Database::ORDER_ASC]); +// $database->createIndex('numbers', 'index2', Database::INDEX_KEY, ['symbol'], [0], [Database::ORDER_ASC]); +// +// $index = $database->renameIndex('numbers', 'index1', 'index3'); +// +// $this->assertTrue($index); +// +// $numbers = $database->getCollection('numbers'); +// +// $this->assertEquals('index2', $numbers->getAttribute('indexes')[1]['$id']); +// $this->assertEquals('index3', $numbers->getAttribute('indexes')[0]['$id']); +// $this->assertCount(2, $numbers->getAttribute('indexes')); +// } +// +// /** +// * @depends testRenameIndex +// * @expectedException Exception +// */ +// public function testRenameIndexMissing(): void +// { +// $database = static::getDatabase(); +// $this->expectExceptionMessage('Index not found'); +// $index = $database->renameIndex('numbers', 'index1', 'index4'); +// } +// +// /** +// * @depends testRenameIndex +// * @expectedException Exception +// */ +// public function testRenameIndexExisting(): void +// { +// $database = static::getDatabase(); +// $this->expectExceptionMessage('Index name already used'); +// $index = $database->renameIndex('numbers', 'index3', 'index2'); +// } +// +// public function testRenameAttribute(): void +// { +// $database = static::getDatabase(); +// +// $colors = $database->createCollection('colors'); +// $database->createAttribute('colors', 'name', Database::VAR_STRING, 128, true); +// $database->createAttribute('colors', 'hex', Database::VAR_STRING, 128, true); +// +// $database->createIndex('colors', 'index1', Database::INDEX_KEY, ['name'], [128], [Database::ORDER_ASC]); +// +// $database->createDocument('colors', new Document([ +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'black', +// 'hex' => '#000000' +// ])); +// +// $attribute = $database->renameAttribute('colors', 'name', 'verbose'); +// +// $this->assertTrue($attribute); +// +// $colors = $database->getCollection('colors'); +// $this->assertEquals('hex', $colors->getAttribute('attributes')[1]['$id']); +// $this->assertEquals('verbose', $colors->getAttribute('attributes')[0]['$id']); +// $this->assertCount(2, $colors->getAttribute('attributes')); +// +// // Attribute in index is renamed automatically on adapter-level. What we need to check is if metadata is properly updated +// $this->assertEquals('verbose', $colors->getAttribute('indexes')[0]->getAttribute("attributes")[0]); +// $this->assertCount(1, $colors->getAttribute('indexes')); +// +// // Document should be there if adapter migrated properly +// $document = $database->findOne('colors'); +// $this->assertTrue($document instanceof Document); +// $this->assertEquals('black', $document->getAttribute('verbose')); +// $this->assertEquals('#000000', $document->getAttribute('hex')); +// $this->assertEquals(null, $document->getAttribute('name')); +// } +// +// /** +// * @depends testRenameAttribute +// * @expectedException Exception +// */ +// public function textRenameAttributeMissing(): void +// { +// $database = static::getDatabase(); +// $this->expectExceptionMessage('Attribute not found'); +// $database->renameAttribute('colors', 'name2', 'name3'); +// } +// +// /** +// * @depends testRenameAttribute +// * @expectedException Exception +// */ +// public function testRenameAttributeExisting(): void +// { +// $database = static::getDatabase(); +// $this->expectExceptionMessage('Attribute name already used'); +// $database->renameAttribute('colors', 'verbose', 'hex'); +// } +// +// public function testUpdateAttributeDefault(): void +// { +// $database = static::getDatabase(); +// +// $flowers = $database->createCollection('flowers'); +// $database->createAttribute('flowers', 'name', Database::VAR_STRING, 128, true); +// $database->createAttribute('flowers', 'inStock', Database::VAR_INTEGER, 0, false); +// $database->createAttribute('flowers', 'date', Database::VAR_STRING, 128, false); +// +// $database->createDocument('flowers', new Document([ +// '$id' => 'flowerWithDate', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Violet', +// 'inStock' => 51, +// 'date' => '2000-06-12 14:12:55.000' +// ])); +// +// $doc = $database->createDocument('flowers', new Document([ +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Lily' +// ])); +// +// $this->assertNull($doc->getAttribute('inStock')); +// +// $database->updateAttributeDefault('flowers', 'inStock', 100); +// +// $doc = $database->createDocument('flowers', new Document([ +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Iris' +// ])); +// +// $this->assertIsNumeric($doc->getAttribute('inStock')); +// $this->assertEquals(100, $doc->getAttribute('inStock')); +// +// $database->updateAttributeDefault('flowers', 'inStock', null); +// } +// +// /** +// * @depends testUpdateAttributeDefault +// */ +// public function testUpdateAttributeRequired(): void +// { +// $database = static::getDatabase(); +// +// $database->updateAttributeRequired('flowers', 'inStock', true); +// +// $this->expectExceptionMessage('Invalid document structure: Missing required attribute "inStock"'); +// +// $doc = $database->createDocument('flowers', new Document([ +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Lily With Missing Stocks' +// ])); +// } +// +// /** +// * @depends testUpdateAttributeDefault +// */ +// public function testUpdateAttributeFilter(): void +// { +// $database = static::getDatabase(); +// +// $database->createAttribute('flowers', 'cartModel', Database::VAR_STRING, 2000, false); +// +// $doc = $database->createDocument('flowers', new Document([ +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Lily With CartData', +// 'inStock' => 50, +// 'cartModel' => '{"color":"string","size":"number"}' +// ])); +// +// $this->assertIsString($doc->getAttribute('cartModel')); +// $this->assertEquals('{"color":"string","size":"number"}', $doc->getAttribute('cartModel')); +// +// $database->updateAttributeFilters('flowers', 'cartModel', ['json']); +// +// $doc = $database->getDocument('flowers', $doc->getId()); +// $this->assertIsArray($doc->getAttribute('cartModel')); +// $this->assertCount(2, $doc->getAttribute('cartModel')); +// $this->assertEquals('string', $doc->getAttribute('cartModel')['color']); +// $this->assertEquals('number', $doc->getAttribute('cartModel')['size']); +// } +// +// /** +// * @depends testUpdateAttributeDefault +// */ +// public function testUpdateAttributeFormat(): void +// { +// $database = static::getDatabase(); +// +// $database->createAttribute('flowers', 'price', Database::VAR_INTEGER, 0, false); +// +// $doc = $database->createDocument('flowers', new Document([ +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// '$id' => ID::custom('LiliPriced'), +// 'name' => 'Lily Priced', +// 'inStock' => 50, +// 'cartModel' => '{}', +// 'price' => 500 +// ])); +// +// $this->assertIsNumeric($doc->getAttribute('price')); +// $this->assertEquals(500, $doc->getAttribute('price')); +// +// Structure::addFormat('priceRange', function ($attribute) { +// $min = $attribute['formatOptions']['min']; +// $max = $attribute['formatOptions']['max']; +// +// return new Range($min, $max); +// }, Database::VAR_INTEGER); +// +// $database->updateAttributeFormat('flowers', 'price', 'priceRange'); +// $database->updateAttributeFormatOptions('flowers', 'price', ['min' => 1, 'max' => 10000]); +// +// $this->expectExceptionMessage('Invalid document structure: Attribute "price" has invalid format. Value must be a valid range between 1 and 10,000'); +// +// $doc = $database->createDocument('flowers', new Document([ +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Lily Overpriced', +// 'inStock' => 50, +// 'cartModel' => '{}', +// 'price' => 15000 +// ])); +// } +// +// /** +// * @depends testUpdateAttributeDefault +// * @depends testUpdateAttributeFormat +// */ +// public function testUpdateAttributeStructure(): void +// { +// // TODO: When this becomes relevant, add many more tests (from all types to all types, chaging size up&down, switchign between array/non-array... +// +// Structure::addFormat('priceRangeNew', function ($attribute) { +// $min = $attribute['formatOptions']['min']; +// $max = $attribute['formatOptions']['max']; +// return new Range($min, $max); +// }, Database::VAR_INTEGER); +// +// $database = static::getDatabase(); +// +// // price attribute +// $collection = $database->getCollection('flowers'); +// $attribute = $collection->getAttribute('attributes')[4]; +// $this->assertEquals(true, $attribute['signed']); +// $this->assertEquals(0, $attribute['size']); +// $this->assertEquals(null, $attribute['default']); +// $this->assertEquals(false, $attribute['array']); +// $this->assertEquals(false, $attribute['required']); +// $this->assertEquals('priceRange', $attribute['format']); +// $this->assertEquals(['min'=>1, 'max'=>10000], $attribute['formatOptions']); +// +// $database->updateAttribute('flowers', 'price', default: 100); +// $collection = $database->getCollection('flowers'); +// $attribute = $collection->getAttribute('attributes')[4]; +// $this->assertEquals('integer', $attribute['type']); +// $this->assertEquals(true, $attribute['signed']); +// $this->assertEquals(0, $attribute['size']); +// $this->assertEquals(100, $attribute['default']); +// $this->assertEquals(false, $attribute['array']); +// $this->assertEquals(false, $attribute['required']); +// $this->assertEquals('priceRange', $attribute['format']); +// $this->assertEquals(['min'=>1, 'max'=>10000], $attribute['formatOptions']); +// +// $database->updateAttribute('flowers', 'price', format: 'priceRangeNew'); +// $collection = $database->getCollection('flowers'); +// $attribute = $collection->getAttribute('attributes')[4]; +// $this->assertEquals('integer', $attribute['type']); +// $this->assertEquals(true, $attribute['signed']); +// $this->assertEquals(0, $attribute['size']); +// $this->assertEquals(100, $attribute['default']); +// $this->assertEquals(false, $attribute['array']); +// $this->assertEquals(false, $attribute['required']); +// $this->assertEquals('priceRangeNew', $attribute['format']); +// $this->assertEquals(['min'=>1, 'max'=>10000], $attribute['formatOptions']); +// +// $database->updateAttribute('flowers', 'price', format: ''); +// $collection = $database->getCollection('flowers'); +// $attribute = $collection->getAttribute('attributes')[4]; +// $this->assertEquals('integer', $attribute['type']); +// $this->assertEquals(true, $attribute['signed']); +// $this->assertEquals(0, $attribute['size']); +// $this->assertEquals(100, $attribute['default']); +// $this->assertEquals(false, $attribute['array']); +// $this->assertEquals(false, $attribute['required']); +// $this->assertEquals('', $attribute['format']); +// $this->assertEquals(['min'=>1, 'max'=>10000], $attribute['formatOptions']); +// +// $database->updateAttribute('flowers', 'price', formatOptions: ['min' => 1, 'max' => 999]); +// $collection = $database->getCollection('flowers'); +// $attribute = $collection->getAttribute('attributes')[4]; +// $this->assertEquals('integer', $attribute['type']); +// $this->assertEquals(true, $attribute['signed']); +// $this->assertEquals(0, $attribute['size']); +// $this->assertEquals(100, $attribute['default']); +// $this->assertEquals(false, $attribute['array']); +// $this->assertEquals(false, $attribute['required']); +// $this->assertEquals('', $attribute['format']); +// $this->assertEquals(['min'=>1, 'max'=>999], $attribute['formatOptions']); +// +// $database->updateAttribute('flowers', 'price', formatOptions: []); +// $collection = $database->getCollection('flowers'); +// $attribute = $collection->getAttribute('attributes')[4]; +// $this->assertEquals('integer', $attribute['type']); +// $this->assertEquals(true, $attribute['signed']); +// $this->assertEquals(0, $attribute['size']); +// $this->assertEquals(100, $attribute['default']); +// $this->assertEquals(false, $attribute['array']); +// $this->assertEquals(false, $attribute['required']); +// $this->assertEquals('', $attribute['format']); +// $this->assertEquals([], $attribute['formatOptions']); +// +// $database->updateAttribute('flowers', 'price', signed: false); +// $collection = $database->getCollection('flowers'); +// $attribute = $collection->getAttribute('attributes')[4]; +// $this->assertEquals('integer', $attribute['type']); +// $this->assertEquals(false, $attribute['signed']); +// $this->assertEquals(0, $attribute['size']); +// $this->assertEquals(100, $attribute['default']); +// $this->assertEquals(false, $attribute['array']); +// $this->assertEquals(false, $attribute['required']); +// $this->assertEquals('', $attribute['format']); +// $this->assertEquals([], $attribute['formatOptions']); +// +// $database->updateAttribute('flowers', 'price', required: true); +// $collection = $database->getCollection('flowers'); +// $attribute = $collection->getAttribute('attributes')[4]; +// $this->assertEquals('integer', $attribute['type']); +// $this->assertEquals(false, $attribute['signed']); +// $this->assertEquals(0, $attribute['size']); +// $this->assertEquals(null, $attribute['default']); +// $this->assertEquals(false, $attribute['array']); +// $this->assertEquals(true, $attribute['required']); +// $this->assertEquals('', $attribute['format']); +// $this->assertEquals([], $attribute['formatOptions']); +// +// $database->updateAttribute('flowers', 'price', type: Database::VAR_STRING, size: Database::LENGTH_KEY, format: ''); +// $collection = $database->getCollection('flowers'); +// $attribute = $collection->getAttribute('attributes')[4]; +// $this->assertEquals('string', $attribute['type']); +// $this->assertEquals(false, $attribute['signed']); +// $this->assertEquals(255, $attribute['size']); +// $this->assertEquals(null, $attribute['default']); +// $this->assertEquals(false, $attribute['array']); +// $this->assertEquals(true, $attribute['required']); +// $this->assertEquals('', $attribute['format']); +// $this->assertEquals([], $collection->getAttribute('attributes')[4]['formatOptions']); +// +// // Date attribute +// $attribute = $collection->getAttribute('attributes')[2]; +// $this->assertEquals('date', $attribute['key']); +// $this->assertEquals('string', $attribute['type']); +// $this->assertEquals(null, $attribute['default']); +// +// $database->updateAttribute('flowers', 'date', type: Database::VAR_DATETIME, size: 0, filters: ['datetime']); +// $collection = $database->getCollection('flowers'); +// $attribute = $collection->getAttribute('attributes')[2]; +// $this->assertEquals('datetime', $attribute['type']); +// $this->assertEquals(0, $attribute['size']); +// $this->assertEquals(null, $attribute['default']); +// $this->assertEquals(false, $attribute['required']); +// $this->assertEquals(true, $attribute['signed']); +// $this->assertEquals(false, $attribute['array']); +// $this->assertEquals('', $attribute['format']); +// $this->assertEquals([], $attribute['formatOptions']); +// +// $doc = $database->getDocument('flowers', 'LiliPriced'); +// $this->assertIsString($doc->getAttribute('price')); +// $this->assertEquals('500', $doc->getAttribute('price')); +// +// $doc = $database->getDocument('flowers', 'flowerWithDate'); +// $this->assertEquals('2000-06-12T14:12:55.000+00:00', $doc->getAttribute('date')); +// } +// +// /** +// * @depends testCreatedAtUpdatedAt +// */ +// public function testCreatedAtUpdatedAtAssert(): void +// { +// $document = static::getDatabase()->getDocument('created_at', 'uid123'); +// $this->assertEquals(true, !$document->isEmpty()); +// sleep(1); +// $document->setAttribute('title', 'new title'); +// static::getDatabase()->updateDocument('created_at', 'uid123', $document); +// $document = static::getDatabase()->getDocument('created_at', 'uid123'); +// +// $this->assertGreaterThan($document->getCreatedAt(), $document->getUpdatedAt()); +// $this->expectException(DuplicateException::class); +// +// static::getDatabase()->createCollection('created_at'); +// } +// +// public function testCreateDatetime(): void +// { +// static::getDatabase()->createCollection('datetime'); +// +// $this->assertEquals(true, static::getDatabase()->createAttribute('datetime', 'date', Database::VAR_DATETIME, 0, true, null, true, false, null, [], ['datetime'])); +// $this->assertEquals(true, static::getDatabase()->createAttribute('datetime', 'date2', Database::VAR_DATETIME, 0, false, null, true, false, null, [], ['datetime'])); +// +// $doc = static::getDatabase()->createDocument('datetime', new Document([ +// '$id' => ID::custom('id1234'), +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'date' => DateTime::now(), +// ])); +// +// $this->assertEquals(29, strlen($doc->getCreatedAt())); +// $this->assertEquals(29, strlen($doc->getUpdatedAt())); +// $this->assertEquals('+00:00', substr($doc->getCreatedAt(), -6)); +// $this->assertEquals('+00:00', substr($doc->getUpdatedAt(), -6)); +// $this->assertGreaterThan('2020-08-16T19:30:08.363+00:00', $doc->getCreatedAt()); +// $this->assertGreaterThan('2020-08-16T19:30:08.363+00:00', $doc->getUpdatedAt()); +// +// $document = static::getDatabase()->getDocument('datetime', 'id1234'); +// $dateValidator = new DatetimeValidator(); +// $this->assertEquals(null, $document->getAttribute('date2')); +// $this->assertEquals(true, $dateValidator->isValid($document->getAttribute('date'))); +// $this->assertEquals(false, $dateValidator->isValid($document->getAttribute('date2'))); +// +// $documents = static::getDatabase()->find('datetime', [ +// Query::greaterThan('date', '1975-12-06 10:00:00+01:00'), +// Query::lessThan('date', '2030-12-06 10:00:00-01:00'), +// ]); +// +// $this->assertEquals(1, count($documents)); +// +// $this->expectException(StructureException::class); +// static::getDatabase()->createDocument('datetime', new Document([ +// '$permissions' => [ +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'date' => "1975-12-06 00:00:61" +// ])); +// } +// +// public function testCreateDateTimeAttributeFailure(): void +// { +// static::getDatabase()->createCollection('datetime_fail'); +// +// /** Test for FAILURE */ +// $this->expectException(Exception::class); +// static::getDatabase()->createAttribute('datetime_fail', 'date_fail', Database::VAR_DATETIME, 0, false); +// } +// +// public function testKeywords(): void +// { +// $database = static::getDatabase(); +// $keywords = $database->getKeywords(); +// +// // Collection name tests +// $attributes = [ +// new Document([ +// '$id' => ID::custom('attribute1'), +// 'type' => Database::VAR_STRING, +// 'size' => 256, +// 'required' => false, +// 'signed' => true, +// 'array' => false, +// 'filters' => [], +// ]), +// ]; +// +// $indexes = [ +// new Document([ +// '$id' => ID::custom('index1'), +// 'type' => Database::INDEX_KEY, +// 'attributes' => ['attribute1'], +// 'lengths' => [256], +// 'orders' => ['ASC'], +// ]), +// ]; +// +// foreach ($keywords as $keyword) { +// $collection = $database->createCollection($keyword, $attributes, $indexes); +// $this->assertEquals($keyword, $collection->getId()); +// +// $document = $database->createDocument($keyword, new Document([ +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// '$id' => ID::custom('helloWorld'), +// 'attribute1' => 'Hello World', +// ])); +// $this->assertEquals('helloWorld', $document->getId()); +// +// $document = $database->getDocument($keyword, 'helloWorld'); +// $this->assertEquals('helloWorld', $document->getId()); +// +// $documents = $database->find($keyword); +// $this->assertCount(1, $documents); +// $this->assertEquals('helloWorld', $documents[0]->getId()); +// +// $collection = $database->deleteCollection($keyword); +// $this->assertTrue($collection); +// } +// +// // TODO: updateCollection name tests +// +// // Attribute name tests +// foreach ($keywords as $keyword) { +// $collectionName = 'rk' . $keyword; // rk is short-hand for reserved-keyword. We do this sicne there are some limits (64 chars max) +// +// $collection = $database->createCollection($collectionName); +// $this->assertEquals($collectionName, $collection->getId()); +// +// $attribute = static::getDatabase()->createAttribute($collectionName, $keyword, Database::VAR_STRING, 128, true); +// $this->assertEquals(true, $attribute); +// +// $document = new Document([ +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// '$id' => 'reservedKeyDocument' +// ]); +// $document->setAttribute($keyword, 'Reserved:' . $keyword); +// +// $document = $database->createDocument($collectionName, $document); +// $this->assertEquals('reservedKeyDocument', $document->getId()); +// $this->assertEquals('Reserved:' . $keyword, $document->getAttribute($keyword)); +// +// $document = $database->getDocument($collectionName, 'reservedKeyDocument'); +// $this->assertEquals('reservedKeyDocument', $document->getId()); +// $this->assertEquals('Reserved:' . $keyword, $document->getAttribute($keyword)); +// +// $documents = $database->find($collectionName); +// $this->assertCount(1, $documents); +// $this->assertEquals('reservedKeyDocument', $documents[0]->getId()); +// $this->assertEquals('Reserved:' . $keyword, $documents[0]->getAttribute($keyword)); +// +// $documents = $database->find($collectionName, [Query::equal($keyword, ["Reserved:${keyword}"])]); +// $this->assertCount(1, $documents); +// $this->assertEquals('reservedKeyDocument', $documents[0]->getId()); +// +// $documents = $database->find($collectionName, [ +// Query::orderDesc($keyword) +// ]); +// $this->assertCount(1, $documents); +// $this->assertEquals('reservedKeyDocument', $documents[0]->getId()); +// +// +// $collection = $database->deleteCollection($collectionName); +// $this->assertTrue($collection); +// +// // TODO: updateAttribute name tests +// } +// +// // TODO: Index name tests +// } +// +// public function testWritePermissions(): void +// { +// Authorization::setRole(Role::any()->toString()); +// $database = static::getDatabase(); +// +// $database->createCollection('animals', permissions: [ +// Permission::create(Role::any()), +// ], documentSecurity: true); +// +// $database->createAttribute('animals', 'type', Database::VAR_STRING, 128, true); +// +// $dog = $database->createDocument('animals', new Document([ +// '$id' => 'dog', +// '$permissions' => [ +// Permission::delete(Role::any()), +// ], +// 'type' => 'Dog' +// ])); +// +// $cat = $database->createDocument('animals', new Document([ +// '$id' => 'cat', +// '$permissions' => [ +// Permission::update(Role::any()), +// ], +// 'type' => 'Cat' +// ])); +// +// // No read permissions: +// +// $docs = $database->find('animals'); +// $this->assertCount(0, $docs); +// +// $doc = $database->getDocument('animals', 'dog'); +// $this->assertTrue($doc->isEmpty()); +// +// $doc = $database->getDocument('animals', 'cat'); +// $this->assertTrue($doc->isEmpty()); +// +// // Cannot delete with update permission: +// $didFail = false; +// +// try { +// $database->deleteDocument('animals', 'cat'); +// } catch (AuthorizationException) { +// $didFail = true; +// } +// +// $this->assertTrue($didFail); +// +// // Cannot update with delete permission: +// $didFail = false; +// +// try { +// $newDog = $dog->setAttribute('type', 'newDog'); +// $database->updateDocument('animals', 'dog', $newDog); +// } catch (AuthorizationException) { +// $didFail = true; +// } +// +// $this->assertTrue($didFail); +// +// // Can delete: +// $database->deleteDocument('animals', 'dog'); +// +// // Can update: +// $newCat = $cat->setAttribute('type', 'newCat'); +// $database->updateDocument('animals', 'cat', $newCat); +// +// $docs = Authorization::skip(fn () => $database->find('animals')); +// $this->assertCount(1, $docs); +// $this->assertEquals('cat', $docs[0]['$id']); +// $this->assertEquals('newCat', $docs[0]['type']); +// } +// +// public function testNoInvalidKeysWithRelationships(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// static::getDatabase()->createCollection('species'); +// static::getDatabase()->createCollection('creatures'); +// static::getDatabase()->createCollection('characterstics'); +// +// static::getDatabase()->createAttribute('species', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('creatures', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('characterstics', 'name', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'species', +// relatedCollection: 'creatures', +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// id: 'creature', +// twoWayKey:'species' +// ); +// static::getDatabase()->createRelationship( +// collection: 'creatures', +// relatedCollection: 'characterstics', +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// id: 'characterstic', +// twoWayKey:'creature' +// ); +// +// $species = static::getDatabase()->createDocument('species', new Document([ +// '$id' => ID::custom('1'), +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Canine', +// 'creature' => [ +// '$id' => ID::custom('1'), +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Dog', +// 'characterstic' => [ +// '$id' => ID::custom('1'), +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// ], +// 'name' => 'active', +// ] +// ] +// ])); +// static::getDatabase()->updateDocument('species', $species->getId(), new Document([ +// '$id' => ID::custom('1'), +// '$collection' => 'species', +// 'creature' => [ +// '$id' => ID::custom('1'), +// '$collection' => 'creatures', +// 'characterstic' => [ +// '$id' => ID::custom('1'), +// 'name' => 'active', +// '$collection' => 'characterstics', +// ] +// ] +// ])); +// $updatedSpecies = static::getDatabase()->getDocument('species', $species->getId()); +// $this->assertEquals($species, $updatedSpecies); +// } +// +// // Relationships +// public function testOneToOneOneWayRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('person'); +// static::getDatabase()->createCollection('library'); +// +// static::getDatabase()->createAttribute('person', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('library', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('library', 'area', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'person', +// relatedCollection: 'library', +// type: Database::RELATION_ONE_TO_ONE +// ); +// +// // Check metadata for collection +// $collection = static::getDatabase()->getCollection('person'); +// $attributes = $collection->getAttribute('attributes', []); +// +// foreach ($attributes as $attribute) { +// if ($attribute['key'] === 'library') { +// $this->assertEquals('relationship', $attribute['type']); +// $this->assertEquals('library', $attribute['$id']); +// $this->assertEquals('library', $attribute['key']); +// $this->assertEquals('library', $attribute['options']['relatedCollection']); +// $this->assertEquals(Database::RELATION_ONE_TO_ONE, $attribute['options']['relationType']); +// $this->assertEquals(false, $attribute['options']['twoWay']); +// $this->assertEquals('person', $attribute['options']['twoWayKey']); +// } +// } +// +// try { +// static::getDatabase()->deleteAttribute('person', 'library'); +// $this->fail('Failed to throw Exception'); +// } catch (Exception $e) { +// $this->assertEquals('Cannot delete relationship as an attribute', $e->getMessage()); +// } +// +// // Create document with relationship with nested data +// $person1 = static::getDatabase()->createDocument('person', new Document([ +// '$id' => 'person1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Person 1', +// 'library' => [ +// '$id' => 'library1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Library 1', +// 'area' => 'Area 1', +// ], +// ])); +// +// // Update a document with non existing related document. It should not get added to the list. +// static::getDatabase()->updateDocument( +// 'person', +// 'person1', +// $person1->setAttribute('library', 'no-library') +// ); +// +// $person1Document = static::getDatabase()->getDocument('person', 'person1'); +// // Assert document does not contain non existing relation document. +// $this->assertEquals(null, $person1Document->getAttribute('library')); +// +// static::getDatabase()->updateDocument( +// 'person', +// 'person1', +// $person1->setAttribute('library', 'library1') +// ); +// +// // Update through create +// $library10 = static::getDatabase()->createDocument('library', new Document([ +// '$id' => 'library10', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// ], +// 'name' => 'Library 10', +// 'area' => 'Area 10', +// ])); +// $person10 = static::getDatabase()->createDocument('person', new Document([ +// '$id' => 'person10', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Person 10', +// 'library' => [ +// '$id' => $library10->getId(), +// 'name' => 'Library 10 Updated', +// 'area' => 'Area 10 Updated', +// ], +// ])); +// $this->assertEquals('Library 10 Updated', $person10->getAttribute('library')->getAttribute('name')); +// $library10 = static::getDatabase()->getDocument('library', $library10->getId()); +// $this->assertEquals('Library 10 Updated', $library10->getAttribute('name')); +// +// // Create document with relationship with related ID +// static::getDatabase()->createDocument('library', new Document([ +// '$id' => 'library2', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Library 2', +// 'area' => 'Area 2', +// ])); +// static::getDatabase()->createDocument('person', new Document([ +// '$id' => 'person2', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Person 2', +// 'library' => 'library2', +// ])); +// +// // Get documents with relationship +// $person1 = static::getDatabase()->getDocument('person', 'person1'); +// $library = $person1->getAttribute('library'); +// $this->assertEquals('library1', $library['$id']); +// $this->assertArrayNotHasKey('person', $library); +// +// $person = static::getDatabase()->getDocument('person', 'person2'); +// $library = $person->getAttribute('library'); +// $this->assertEquals('library2', $library['$id']); +// $this->assertArrayNotHasKey('person', $library); +// +// // Get related documents +// $library = static::getDatabase()->getDocument('library', 'library1'); +// $this->assertArrayNotHasKey('person', $library); +// +// $library = static::getDatabase()->getDocument('library', 'library2'); +// $this->assertArrayNotHasKey('person', $library); +// +// $people = static::getDatabase()->find('person', [ +// Query::select(['name']) +// ]); +// +// $this->assertArrayNotHasKey('library', $people[0]); +// +// $people = static::getDatabase()->find('person'); +// $this->assertEquals(3, \count($people)); +// +// // Select related document attributes +// $person = static::getDatabase()->findOne('person', [ +// Query::select(['*', 'library.name']) +// ]); +// +// if (!$person instanceof Document) { +// throw new Exception('Person not found'); +// } +// +// $this->assertEquals('Library 1', $person->getAttribute('library')->getAttribute('name')); +// $this->assertArrayNotHasKey('area', $person->getAttribute('library')); +// +// $person = static::getDatabase()->getDocument('person', 'person1', [ +// Query::select(['*', 'library.name', '$id']) +// ]); +// +// $this->assertEquals('Library 1', $person->getAttribute('library')->getAttribute('name')); +// $this->assertArrayNotHasKey('area', $person->getAttribute('library')); +// +// +// +// $document = static::getDatabase()->getDocument('person', $person->getId(), [ +// Query::select(['name']), +// ]); +// $this->assertArrayNotHasKey('library', $document); +// $this->assertEquals('Person 1', $document['name']); +// +// $document = static::getDatabase()->getDocument('person', $person->getId(), [ +// Query::select(['*']), +// ]); +// $this->assertEquals('library1', $document['library']); +// +// $document = static::getDatabase()->getDocument('person', $person->getId(), [ +// Query::select(['library.*']), +// ]); +// $this->assertEquals('Library 1', $document['library']['name']); +// $this->assertArrayNotHasKey('name', $document); +// +// // Update root document attribute without altering relationship +// $person1 = static::getDatabase()->updateDocument( +// 'person', +// $person1->getId(), +// $person1->setAttribute('name', 'Person 1 Updated') +// ); +// +// $this->assertEquals('Person 1 Updated', $person1->getAttribute('name')); +// $person1 = static::getDatabase()->getDocument('person', 'person1'); +// $this->assertEquals('Person 1 Updated', $person1->getAttribute('name')); +// +// // Update nested document attribute +// $person1 = static::getDatabase()->updateDocument( +// 'person', +// $person1->getId(), +// $person1->setAttribute( +// 'library', +// $person1 +// ->getAttribute('library') +// ->setAttribute('name', 'Library 1 Updated') +// ) +// ); +// +// $this->assertEquals('Library 1 Updated', $person1->getAttribute('library')->getAttribute('name')); +// $person1 = static::getDatabase()->getDocument('person', 'person1'); +// $this->assertEquals('Library 1 Updated', $person1->getAttribute('library')->getAttribute('name')); +// +// // Create new document with no relationship +// $person3 = static::getDatabase()->createDocument('person', new Document([ +// '$id' => 'person3', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Person 3', +// ])); +// +// // Update to relate to created document +// $person3 = static::getDatabase()->updateDocument( +// 'person', +// $person3->getId(), +// $person3->setAttribute('library', new Document([ +// '$id' => 'library3', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// ], +// 'name' => 'Library 3', +// 'area' => 'Area 3', +// ])) +// ); +// +// $this->assertEquals('library3', $person3->getAttribute('library')['$id']); +// $person3 = static::getDatabase()->getDocument('person', 'person3'); +// $this->assertEquals('Library 3', $person3['library']['name']); +// +// $libraryDocument = static::getDatabase()->getDocument('library', 'library3'); +// $libraryDocument->setAttribute('name', 'Library 3 updated'); +// static::getDatabase()->updateDocument('library', 'library3', $libraryDocument); +// $libraryDocument = static::getDatabase()->getDocument('library', 'library3'); +// $this->assertEquals('Library 3 updated', $libraryDocument['name']); +// +// $person3 = static::getDatabase()->getDocument('person', 'person3'); +// // Todo: This is failing +// $this->assertEquals($libraryDocument['name'], $person3['library']['name']); +// $this->assertEquals('library3', $person3->getAttribute('library')['$id']); +// +// // One to one can't relate to multiple documents, unique index throws duplicate +// try { +// static::getDatabase()->updateDocument( +// 'person', +// $person1->getId(), +// $person1->setAttribute('library', 'library2') +// ); +// $this->fail('Failed to throw duplicate exception'); +// } catch (Exception $e) { +// $this->assertInstanceOf(DuplicateException::class, $e); +// } +// +// // Create new document +// $library4 = static::getDatabase()->createDocument('library', new Document([ +// '$id' => 'library4', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Library 4', +// 'area' => 'Area 4', +// ])); +// +// // Relate existing document to new document +// static::getDatabase()->updateDocument( +// 'person', +// $person1->getId(), +// $person1->setAttribute('library', 'library4') +// ); +// +// // Relate existing document to new document as nested data +// static::getDatabase()->updateDocument( +// 'person', +// $person1->getId(), +// $person1->setAttribute('library', $library4) +// ); +// +// // Rename relationship key +// static::getDatabase()->updateRelationship( +// collection: 'person', +// id: 'library', +// newKey: 'newLibrary' +// ); +// +// // Get document with again +// $person = static::getDatabase()->getDocument('person', 'person1'); +// $library = $person->getAttribute('newLibrary'); +// $this->assertEquals('library4', $library['$id']); +// +// // Create person with no relationship +// static::getDatabase()->createDocument('person', new Document([ +// '$id' => 'person4', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Person 4', +// ])); +// +// // Can delete parent document with no relation with on delete set to restrict +// $deleted = static::getDatabase()->deleteDocument('person', 'person4'); +// $this->assertEquals(true, $deleted); +// +// $person4 = static::getDatabase()->getDocument('person', 'person4'); +// $this->assertEquals(true, $person4->isEmpty()); +// +// // Cannot delete document while still related to another with on delete set to restrict +// try { +// static::getDatabase()->deleteDocument('person', 'person1'); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); +// } +// +// // Can delete child document while still related to another with on delete set to restrict +// $person5 = static::getDatabase()->createDocument('person', new Document([ +// '$id' => 'person5', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Person 5', +// 'newLibrary' => [ +// '$id' => 'library5', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Library 5', +// 'area' => 'Area 5', +// ], +// ])); +// $deleted = static::getDatabase()->deleteDocument('library', 'library5'); +// $this->assertEquals(true, $deleted); +// $person5 = static::getDatabase()->getDocument('person', 'person5'); +// $this->assertEquals(null, $person5->getAttribute('newLibrary')); +// +// // Change on delete to set null +// static::getDatabase()->updateRelationship( +// collection: 'person', +// id: 'newLibrary', +// onDelete: Database::RELATION_MUTATE_SET_NULL +// ); +// +// // Delete parent, no effect on children for one-way +// static::getDatabase()->deleteDocument('person', 'person1'); +// +// // Delete child, set parent relating attribute to null for one-way +// static::getDatabase()->deleteDocument('library', 'library2'); +// +// // Check relation was set to null +// $person2 = static::getDatabase()->getDocument('person', 'person2'); +// $this->assertEquals(null, $person2->getAttribute('newLibrary', '')); +// +// // Relate to another document +// static::getDatabase()->updateDocument( +// 'person', +// $person2->getId(), +// $person2->setAttribute('newLibrary', 'library4') +// ); +// +// // Change on delete to cascade +// static::getDatabase()->updateRelationship( +// collection: 'person', +// id: 'newLibrary', +// onDelete: Database::RELATION_MUTATE_CASCADE +// ); +// +// // Delete parent, will delete child +// static::getDatabase()->deleteDocument('person', 'person2'); +// +// // Check parent and child were deleted +// $person = static::getDatabase()->getDocument('person', 'person2'); +// $this->assertEquals(true, $person->isEmpty()); +// +// $library = static::getDatabase()->getDocument('library', 'library4'); +// $this->assertEquals(true, $library->isEmpty()); +// +// // Delete relationship +// static::getDatabase()->deleteRelationship( +// 'person', +// 'newLibrary' +// ); +// +// // Check parent doesn't have relationship anymore +// $person = static::getDatabase()->getDocument('person', 'person1'); +// $library = $person->getAttribute('newLibrary', ''); +// $this->assertEquals(null, $library); +// } +// +// /** +// * @throws AuthorizationException +// * @throws LimitException +// * @throws DuplicateException +// * @throws StructureException +// * @throws Throwable +// */ +// public function testOneToOneTwoWayRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('country'); +// static::getDatabase()->createCollection('city'); +// +// static::getDatabase()->createAttribute('country', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('city', 'code', Database::VAR_STRING, 3, true); +// static::getDatabase()->createAttribute('city', 'name', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'country', +// relatedCollection: 'city', +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true +// ); +// +// $collection = static::getDatabase()->getCollection('country'); +// $attributes = $collection->getAttribute('attributes', []); +// foreach ($attributes as $attribute) { +// if ($attribute['key'] === 'city') { +// $this->assertEquals('relationship', $attribute['type']); +// $this->assertEquals('city', $attribute['$id']); +// $this->assertEquals('city', $attribute['key']); +// $this->assertEquals('city', $attribute['options']['relatedCollection']); +// $this->assertEquals(Database::RELATION_ONE_TO_ONE, $attribute['options']['relationType']); +// $this->assertEquals(true, $attribute['options']['twoWay']); +// $this->assertEquals('country', $attribute['options']['twoWayKey']); +// } +// } +// +// $collection = static::getDatabase()->getCollection('city'); +// $attributes = $collection->getAttribute('attributes', []); +// foreach ($attributes as $attribute) { +// if ($attribute['key'] === 'country') { +// $this->assertEquals('relationship', $attribute['type']); +// $this->assertEquals('country', $attribute['$id']); +// $this->assertEquals('country', $attribute['key']); +// $this->assertEquals('country', $attribute['options']['relatedCollection']); +// $this->assertEquals(Database::RELATION_ONE_TO_ONE, $attribute['options']['relationType']); +// $this->assertEquals(true, $attribute['options']['twoWay']); +// $this->assertEquals('city', $attribute['options']['twoWayKey']); +// } +// } +// +// // Create document with relationship with nested data +// $doc = new Document([ +// '$id' => 'country1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'England', +// 'city' => [ +// '$id' => 'city1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'London', +// 'code' => 'LON', +// ], +// ]); +// +// static::getDatabase()->createDocument('country', $doc); +// $country1 = static::getDatabase()->getDocument('country', 'country1'); +// $this->assertEquals('London', $country1->getAttribute('city')->getAttribute('name')); +// +// // Update a document with non existing related document. It should not get added to the list. +// static::getDatabase()->updateDocument('country', 'country1', (new Document($doc->getArrayCopy()))->setAttribute('city', 'no-city')); +// +// $country1Document = static::getDatabase()->getDocument('country', 'country1'); +// // Assert document does not contain non existing relation document. +// $this->assertEquals(null, $country1Document->getAttribute('city')); +// static::getDatabase()->updateDocument('country', 'country1', (new Document($doc->getArrayCopy()))->setAttribute('city', 'city1')); +// try { +// static::getDatabase()->deleteDocument('country', 'country1'); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertInstanceOf(RestrictedException::class, $e); +// } +// +// $this->assertTrue(static::getDatabase()->deleteDocument('city', 'city1')); +// +// $city1 = static::getDatabase()->getDocument('city', 'city1'); +// $this->assertTrue($city1->isEmpty()); +// +// $country1 = static::getDatabase()->getDocument('country', 'country1'); +// $this->assertTrue($country1->getAttribute('city')->isEmpty()); +// +// $this->assertTrue(static::getDatabase()->deleteDocument('country', 'country1')); +// +// static::getDatabase()->createDocument('country', $doc); +// $country1 = static::getDatabase()->getDocument('country', 'country1'); +// $this->assertEquals('London', $country1->getAttribute('city')->getAttribute('name')); +// +// // Create document with relationship with related ID +// static::getDatabase()->createDocument('city', new Document([ +// '$id' => 'city2', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Paris', +// 'code' => 'PAR', +// ])); +// static::getDatabase()->createDocument('country', new Document([ +// '$id' => 'country2', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'France', +// 'city' => 'city2', +// ])); +// +// // Create from child side +// static::getDatabase()->createDocument('city', new Document([ +// '$id' => 'city3', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Christchurch', +// 'code' => 'CHC', +// 'country' => [ +// '$id' => 'country3', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'New Zealand', +// ], +// ])); +// static::getDatabase()->createDocument('country', new Document([ +// '$id' => 'country4', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Australia', +// ])); +// static::getDatabase()->createDocument('city', new Document([ +// '$id' => 'city4', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Sydney', +// 'code' => 'SYD', +// 'country' => 'country4', +// ])); +// +// // Get document with relationship +// $city = static::getDatabase()->getDocument('city', 'city1'); +// $country = $city->getAttribute('country'); +// $this->assertEquals('country1', $country['$id']); +// $this->assertArrayNotHasKey('city', $country); +// +// $city = static::getDatabase()->getDocument('city', 'city2'); +// $country = $city->getAttribute('country'); +// $this->assertEquals('country2', $country['$id']); +// $this->assertArrayNotHasKey('city', $country); +// +// $city = static::getDatabase()->getDocument('city', 'city3'); +// $country = $city->getAttribute('country'); +// $this->assertEquals('country3', $country['$id']); +// $this->assertArrayNotHasKey('city', $country); +// +// $city = static::getDatabase()->getDocument('city', 'city4'); +// $country = $city->getAttribute('country'); +// $this->assertEquals('country4', $country['$id']); +// $this->assertArrayNotHasKey('city', $country); +// +// // Get inverse document with relationship +// $country = static::getDatabase()->getDocument('country', 'country1'); +// $city = $country->getAttribute('city'); +// $this->assertEquals('city1', $city['$id']); +// $this->assertArrayNotHasKey('country', $city); +// +// $country = static::getDatabase()->getDocument('country', 'country2'); +// $city = $country->getAttribute('city'); +// $this->assertEquals('city2', $city['$id']); +// $this->assertArrayNotHasKey('country', $city); +// +// $country = static::getDatabase()->getDocument('country', 'country3'); +// $city = $country->getAttribute('city'); +// $this->assertEquals('city3', $city['$id']); +// $this->assertArrayNotHasKey('country', $city); +// +// $country = static::getDatabase()->getDocument('country', 'country4'); +// $city = $country->getAttribute('city'); +// $this->assertEquals('city4', $city['$id']); +// $this->assertArrayNotHasKey('country', $city); +// +// $countries = static::getDatabase()->find('country'); +// +// $this->assertEquals(4, \count($countries)); +// +// // Select related document attributes +// $country = static::getDatabase()->findOne('country', [ +// Query::select(['*', 'city.name']) +// ]); +// +// if (!$country instanceof Document) { +// throw new Exception('Country not found'); +// } +// +// $this->assertEquals('London', $country->getAttribute('city')->getAttribute('name')); +// $this->assertArrayNotHasKey('code', $country->getAttribute('city')); +// +// $country = static::getDatabase()->getDocument('country', 'country1', [ +// Query::select(['*', 'city.name']) +// ]); +// +// $this->assertEquals('London', $country->getAttribute('city')->getAttribute('name')); +// $this->assertArrayNotHasKey('code', $country->getAttribute('city')); +// +// $country1 = static::getDatabase()->getDocument('country', 'country1'); +// +// // Update root document attribute without altering relationship +// $country1 = static::getDatabase()->updateDocument( +// 'country', +// $country1->getId(), +// $country1->setAttribute('name', 'Country 1 Updated') +// ); +// +// $this->assertEquals('Country 1 Updated', $country1->getAttribute('name')); +// $country1 = static::getDatabase()->getDocument('country', 'country1'); +// $this->assertEquals('Country 1 Updated', $country1->getAttribute('name')); +// +// $city2 = static::getDatabase()->getDocument('city', 'city2'); +// +// // Update inverse root document attribute without altering relationship +// $city2 = static::getDatabase()->updateDocument( +// 'city', +// $city2->getId(), +// $city2->setAttribute('name', 'City 2 Updated') +// ); +// +// $this->assertEquals('City 2 Updated', $city2->getAttribute('name')); +// $city2 = static::getDatabase()->getDocument('city', 'city2'); +// $this->assertEquals('City 2 Updated', $city2->getAttribute('name')); +// +// // Update nested document attribute +// $country1 = static::getDatabase()->updateDocument( +// 'country', +// $country1->getId(), +// $country1->setAttribute( +// 'city', +// $country1 +// ->getAttribute('city') +// ->setAttribute('name', 'City 1 Updated') +// ) +// ); +// +// $this->assertEquals('City 1 Updated', $country1->getAttribute('city')->getAttribute('name')); +// $country1 = static::getDatabase()->getDocument('country', 'country1'); +// $this->assertEquals('City 1 Updated', $country1->getAttribute('city')->getAttribute('name')); +// +// // Update inverse nested document attribute +// $city2 = static::getDatabase()->updateDocument( +// 'city', +// $city2->getId(), +// $city2->setAttribute( +// 'country', +// $city2 +// ->getAttribute('country') +// ->setAttribute('name', 'Country 2 Updated') +// ) +// ); +// +// $this->assertEquals('Country 2 Updated', $city2->getAttribute('country')->getAttribute('name')); +// $city2 = static::getDatabase()->getDocument('city', 'city2'); +// $this->assertEquals('Country 2 Updated', $city2->getAttribute('country')->getAttribute('name')); +// +// // Create new document with no relationship +// $country5 = static::getDatabase()->createDocument('country', new Document([ +// '$id' => 'country5', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Country 5', +// ])); +// +// // Update to relate to created document +// $country5 = static::getDatabase()->updateDocument( +// 'country', +// $country5->getId(), +// $country5->setAttribute('city', new Document([ +// '$id' => 'city5', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// ], +// 'name' => 'City 5', +// 'code' => 'C5', +// ])) +// ); +// +// $this->assertEquals('city5', $country5->getAttribute('city')['$id']); +// $country5 = static::getDatabase()->getDocument('country', 'country5'); +// $this->assertEquals('city5', $country5->getAttribute('city')['$id']); +// +// // Create new document with no relationship +// $city6 = static::getDatabase()->createDocument('city', new Document([ +// '$id' => 'city6', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'City6', +// 'code' => 'C6', +// ])); +// +// // Update to relate to created document +// $city6 = static::getDatabase()->updateDocument( +// 'city', +// $city6->getId(), +// $city6->setAttribute('country', new Document([ +// '$id' => 'country6', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// ], +// 'name' => 'Country 6', +// ])) +// ); +// +// $this->assertEquals('country6', $city6->getAttribute('country')['$id']); +// $city6 = static::getDatabase()->getDocument('city', 'city6'); +// $this->assertEquals('country6', $city6->getAttribute('country')['$id']); +// +// // One to one can't relate to multiple documents, unique index throws duplicate +// try { +// static::getDatabase()->updateDocument( +// 'country', +// $country1->getId(), +// $country1->setAttribute('city', 'city2') +// ); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertInstanceOf(DuplicateException::class, $e); +// } +// +// $city1 = static::getDatabase()->getDocument('city', 'city1'); +// +// // Set relationship to null +// $city1 = static::getDatabase()->updateDocument( +// 'city', +// $city1->getId(), +// $city1->setAttribute('country', null) +// ); +// +// $this->assertEquals(null, $city1->getAttribute('country')); +// $city1 = static::getDatabase()->getDocument('city', 'city1'); +// $this->assertEquals(null, $city1->getAttribute('country')); +// +// // Create a new city with no relation +// $city7 = static::getDatabase()->createDocument('city', new Document([ +// '$id' => 'city7', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Copenhagen', +// 'code' => 'CPH', +// ])); +// +// // Update document with relation to new document +// static::getDatabase()->updateDocument( +// 'country', +// $country1->getId(), +// $country1->setAttribute('city', 'city7') +// ); +// +// // Relate existing document to new document as nested data +// static::getDatabase()->updateDocument( +// 'country', +// $country1->getId(), +// $country1->setAttribute('city', $city7) +// ); +// +// // Create a new country with no relation +// static::getDatabase()->createDocument('country', new Document([ +// '$id' => 'country7', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Denmark' +// ])); +// +// // Update inverse document with new related document +// static::getDatabase()->updateDocument( +// 'city', +// $city1->getId(), +// $city1->setAttribute('country', 'country7') +// ); +// +// // Rename relationship keys on both sides +// static::getDatabase()->updateRelationship( +// 'country', +// 'city', +// 'newCity', +// 'newCountry' +// ); +// +// // Get document with new relationship key +// $city = static::getDatabase()->getDocument('city', 'city1'); +// $country = $city->getAttribute('newCountry'); +// $this->assertEquals('country7', $country['$id']); +// +// // Get inverse document with new relationship key +// $country = static::getDatabase()->getDocument('country', 'country7'); +// $city = $country->getAttribute('newCity'); +// $this->assertEquals('city1', $city['$id']); +// +// // Create a new country with no relation +// static::getDatabase()->createDocument('country', new Document([ +// '$id' => 'country8', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Denmark' +// ])); +// +// // Can delete parent document with no relation with on delete set to restrict +// $deleted = static::getDatabase()->deleteDocument('country', 'country8'); +// $this->assertEquals(1, $deleted); +// +// $country8 = static::getDatabase()->getDocument('country', 'country8'); +// $this->assertEquals(true, $country8->isEmpty()); +// +// +// // Cannot delete document while still related to another with on delete set to restrict +// try { +// static::getDatabase()->deleteDocument('country', 'country1'); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); +// } +// +// // Change on delete to set null +// static::getDatabase()->updateRelationship( +// collection: 'country', +// id: 'newCity', +// onDelete: Database::RELATION_MUTATE_SET_NULL +// ); +// +// // Delete parent, will set child relationship to null for two-way +// static::getDatabase()->deleteDocument('country', 'country1'); +// +// // Check relation was set to null +// $city7 = static::getDatabase()->getDocument('city', 'city7'); +// $this->assertEquals(null, $city7->getAttribute('country', '')); +// +// // Delete child, set parent relationship to null for two-way +// static::getDatabase()->deleteDocument('city', 'city2'); +// +// // Check relation was set to null +// $country2 = static::getDatabase()->getDocument('country', 'country2'); +// $this->assertEquals(null, $country2->getAttribute('city', '')); +// +// // Relate again +// static::getDatabase()->updateDocument( +// 'city', +// $city7->getId(), +// $city7->setAttribute('newCountry', 'country2') +// ); +// +// // Change on delete to cascade +// static::getDatabase()->updateRelationship( +// collection: 'country', +// id: 'newCity', +// onDelete: Database::RELATION_MUTATE_CASCADE +// ); +// +// // Delete parent, will delete child +// static::getDatabase()->deleteDocument('country', 'country7'); +// +// // Check parent and child were deleted +// $library = static::getDatabase()->getDocument('country', 'country7'); +// $this->assertEquals(true, $library->isEmpty()); +// +// $library = static::getDatabase()->getDocument('city', 'city1'); +// $this->assertEquals(true, $library->isEmpty()); +// +// // Delete child, will delete parent for two-way +// static::getDatabase()->deleteDocument('city', 'city7'); +// +// // Check parent and child were deleted +// $library = static::getDatabase()->getDocument('city', 'city7'); +// $this->assertEquals(true, $library->isEmpty()); +// +// $library = static::getDatabase()->getDocument('country', 'country2'); +// $this->assertEquals(true, $library->isEmpty()); +// +// // Create new document to check after deleting relationship +// static::getDatabase()->createDocument('city', new Document([ +// '$id' => 'city7', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Munich', +// 'code' => 'MUC', +// 'newCountry' => [ +// '$id' => 'country7', +// 'name' => 'Germany' +// ] +// ])); +// +// // Delete relationship +// static::getDatabase()->deleteRelationship( +// 'country', +// 'newCity' +// ); +// +// // Try to get document again +// $country = static::getDatabase()->getDocument('country', 'country4'); +// $city = $country->getAttribute('newCity'); +// $this->assertEquals(null, $city); +// +// // Try to get inverse document again +// $city = static::getDatabase()->getDocument('city', 'city7'); +// $country = $city->getAttribute('newCountry'); +// $this->assertEquals(null, $country); +// } +// +// public function testIdenticalTwoWayKeyRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('parent'); +// static::getDatabase()->createCollection('child'); +// +// static::getDatabase()->createRelationship( +// collection: 'parent', +// relatedCollection: 'child', +// type: Database::RELATION_ONE_TO_ONE, +// id: 'child1' +// ); +// +// try { +// static::getDatabase()->createRelationship( +// collection: 'parent', +// relatedCollection: 'child', +// type: Database::RELATION_ONE_TO_MANY, +// id: 'children', +// ); +// $this->fail('Failed to throw Exception'); +// } catch (Exception $e) { +// $this->assertEquals('Related attribute already exists', $e->getMessage()); +// } +// +// static::getDatabase()->createRelationship( +// collection: 'parent', +// relatedCollection: 'child', +// type: Database::RELATION_ONE_TO_MANY, +// id: 'children', +// twoWayKey: 'parent_id' +// ); +// +// $collection = static::getDatabase()->getCollection('parent'); +// $attributes = $collection->getAttribute('attributes', []); +// foreach ($attributes as $attribute) { +// if ($attribute['key'] === 'child1') { +// $this->assertEquals('parent', $attribute['options']['twoWayKey']); +// } +// +// if ($attribute['key'] === 'children') { +// $this->assertEquals('parent_id', $attribute['options']['twoWayKey']); +// } +// } +// +// static::getDatabase()->createDocument('parent', new Document([ +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'child1' => [ +// '$id' => 'foo', +// '$permissions' => [Permission::read(Role::any())], +// ], +// 'children' => [ +// [ +// '$id' => 'bar', +// '$permissions' => [Permission::read(Role::any())], +// ], +// ], +// ])); +// +// $documents = static::getDatabase()->find('parent', []); +// $document = array_pop($documents); +// $this->assertArrayHasKey('child1', $document); +// $this->assertEquals('foo', $document->getAttribute('child1')->getId()); +// $this->assertArrayHasKey('children', $document); +// $this->assertEquals('bar', $document->getAttribute('children')[0]->getId()); +// +// try { +// static::getDatabase()->updateRelationship( +// collection: 'parent', +// id: 'children', +// newKey: 'child1' +// ); +// $this->fail('Failed to throw Exception'); +// } catch (Exception $e) { +// $this->assertEquals('Attribute already exists', $e->getMessage()); +// } +// +// try { +// static::getDatabase()->updateRelationship( +// collection: 'parent', +// id: 'children', +// newTwoWayKey: 'parent' +// ); +// $this->fail('Failed to throw Exception'); +// } catch (Exception $e) { +// $this->assertEquals('Related attribute already exists', $e->getMessage()); +// } +// } +// +// public function testOneToManyOneWayRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('artist'); +// static::getDatabase()->createCollection('album'); +// +// static::getDatabase()->createAttribute('artist', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('album', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('album', 'price', Database::VAR_FLOAT, 0, true); +// +// static::getDatabase()->createRelationship( +// collection: 'artist', +// relatedCollection: 'album', +// type: Database::RELATION_ONE_TO_MANY, +// id: 'albums' +// ); +// +// // Check metadata for collection +// $collection = static::getDatabase()->getCollection('artist'); +// $attributes = $collection->getAttribute('attributes', []); +// +// foreach ($attributes as $attribute) { +// if ($attribute['key'] === 'albums') { +// $this->assertEquals('relationship', $attribute['type']); +// $this->assertEquals('albums', $attribute['$id']); +// $this->assertEquals('albums', $attribute['key']); +// $this->assertEquals('album', $attribute['options']['relatedCollection']); +// $this->assertEquals(Database::RELATION_ONE_TO_MANY, $attribute['options']['relationType']); +// $this->assertEquals(false, $attribute['options']['twoWay']); +// $this->assertEquals('artist', $attribute['options']['twoWayKey']); +// } +// } +// +// // Create document with relationship with nested data +// $artist1 = static::getDatabase()->createDocument('artist', new Document([ +// '$id' => 'artist1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Artist 1', +// 'albums' => [ +// [ +// '$id' => 'album1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()) +// ], +// 'name' => 'Album 1', +// 'price' => 9.99, +// ], +// ], +// ])); +// +// // Update a document with non existing related document. It should not get added to the list. +// static::getDatabase()->updateDocument('artist', 'artist1', $artist1->setAttribute('albums', ['album1', 'no-album'])); +// +// $artist1Document = static::getDatabase()->getDocument('artist', 'artist1'); +// // Assert document does not contain non existing relation document. +// $this->assertEquals(1, \count($artist1Document->getAttribute('albums'))); +// +// // Create document with relationship with related ID +// static::getDatabase()->createDocument('album', new Document([ +// '$id' => 'album2', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Album 2', +// 'price' => 19.99, +// ])); +// static::getDatabase()->createDocument('artist', new Document([ +// '$id' => 'artist2', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Artist 2', +// 'albums' => [ +// 'album2', +// [ +// '$id' => 'album33', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Album 3', +// 'price' => 33.33, +// ] +// ] +// ])); +// +// $documents = static::getDatabase()->find('artist', [ +// Query::select(['name']), +// Query::limit(1) +// ]); +// $this->assertArrayNotHasKey('albums', $documents[0]); +// +// // Get document with relationship +// $artist = static::getDatabase()->getDocument('artist', 'artist1'); +// $albums = $artist->getAttribute('albums', []); +// $this->assertEquals('album1', $albums[0]['$id']); +// $this->assertArrayNotHasKey('artist', $albums[0]); +// +// $artist = static::getDatabase()->getDocument('artist', 'artist2'); +// $albums = $artist->getAttribute('albums', []); +// $this->assertEquals('album2', $albums[0]['$id']); +// $this->assertArrayNotHasKey('artist', $albums[0]); +// $this->assertEquals('album33', $albums[1]['$id']); +// $this->assertCount(2, $albums); +// +// // Get related document +// $album = static::getDatabase()->getDocument('album', 'album1'); +// $this->assertArrayNotHasKey('artist', $album); +// +// $album = static::getDatabase()->getDocument('album', 'album2'); +// $this->assertArrayNotHasKey('artist', $album); +// +// $artists = static::getDatabase()->find('artist'); +// +// $this->assertEquals(2, \count($artists)); +// +// // Select related document attributes +// $artist = static::getDatabase()->findOne('artist', [ +// Query::select(['*', 'albums.name']) +// ]); +// +// if (!$artist instanceof Document) { +// $this->fail('Artist not found'); +// } +// +// $this->assertEquals('Album 1', $artist->getAttribute('albums')[0]->getAttribute('name')); +// $this->assertArrayNotHasKey('price', $artist->getAttribute('albums')[0]); +// +// $artist = static::getDatabase()->getDocument('artist', 'artist1', [ +// Query::select(['*', 'albums.name']) +// ]); +// +// $this->assertEquals('Album 1', $artist->getAttribute('albums')[0]->getAttribute('name')); +// $this->assertArrayNotHasKey('price', $artist->getAttribute('albums')[0]); +// +// // Update root document attribute without altering relationship +// $artist1 = static::getDatabase()->updateDocument( +// 'artist', +// $artist1->getId(), +// $artist1->setAttribute('name', 'Artist 1 Updated') +// ); +// +// $this->assertEquals('Artist 1 Updated', $artist1->getAttribute('name')); +// $artist1 = static::getDatabase()->getDocument('artist', 'artist1'); +// $this->assertEquals('Artist 1 Updated', $artist1->getAttribute('name')); +// +// // Update nested document attribute +// $albums = $artist1->getAttribute('albums', []); +// $albums[0]->setAttribute('name', 'Album 1 Updated'); +// +// $artist1 = static::getDatabase()->updateDocument( +// 'artist', +// $artist1->getId(), +// $artist1->setAttribute('albums', $albums) +// ); +// +// $this->assertEquals('Album 1 Updated', $artist1->getAttribute('albums')[0]->getAttribute('name')); +// $artist1 = static::getDatabase()->getDocument('artist', 'artist1'); +// $this->assertEquals('Album 1 Updated', $artist1->getAttribute('albums')[0]->getAttribute('name')); +// +// $albumId = $artist1->getAttribute('albums')[0]->getAttribute('$id'); +// $albumDocument = static::getDatabase()->getDocument('album', $albumId); +// $albumDocument->setAttribute('name', 'Album 1 Updated!!!'); +// static::getDatabase()->updateDocument('album', $albumDocument->getId(), $albumDocument); +// $albumDocument = static::getDatabase()->getDocument('album', $albumDocument->getId()); +// $artist1 = static::getDatabase()->getDocument('artist', $artist1->getId()); +// +// $this->assertEquals('Album 1 Updated!!!', $albumDocument['name']); +// $this->assertEquals($albumDocument->getId(), $artist1->getAttribute('albums')[0]->getId()); +// //Todo: This is failing +// $this->assertEquals($albumDocument->getAttribute('name'), $artist1->getAttribute('albums')[0]->getAttribute('name')); +// +// // Create new document with no relationship +// $artist3 = static::getDatabase()->createDocument('artist', new Document([ +// '$id' => 'artist3', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Artist 3', +// ])); +// +// // Update to relate to created document +// $artist3 = static::getDatabase()->updateDocument( +// 'artist', +// $artist3->getId(), +// $artist3->setAttribute('albums', [new Document([ +// '$id' => 'album3', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Album 3', +// 'price' => 29.99, +// ])]) +// ); +// +// $this->assertEquals('Album 3', $artist3->getAttribute('albums')[0]->getAttribute('name')); +// $artist3 = static::getDatabase()->getDocument('artist', 'artist3'); +// $this->assertEquals('Album 3', $artist3->getAttribute('albums')[0]->getAttribute('name')); +// +// // Update document with new related documents, will remove existing relations +// static::getDatabase()->updateDocument( +// 'artist', +// $artist1->getId(), +// $artist1->setAttribute('albums', ['album2']) +// ); +// +// // Update document with new related documents, will remove existing relations +// static::getDatabase()->updateDocument( +// 'artist', +// $artist1->getId(), +// $artist1->setAttribute('albums', ['album1', 'album2']) +// ); +// +// // Rename relationship key +// static::getDatabase()->updateRelationship( +// 'artist', +// 'albums', +// 'newAlbums' +// ); +// +// // Get document with new relationship key +// $artist = static::getDatabase()->getDocument('artist', 'artist1'); +// $albums = $artist->getAttribute('newAlbums'); +// $this->assertEquals('album1', $albums[0]['$id']); +// +// // Create new document with no relationship +// static::getDatabase()->createDocument('artist', new Document([ +// '$id' => 'artist4', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Artist 4', +// ])); +// +// // Can delete document with no relationship when on delete is set to restrict +// $deleted = static::getDatabase()->deleteDocument('artist', 'artist4'); +// $this->assertEquals(true, $deleted); +// +// $artist4 = static::getDatabase()->getDocument('artist', 'artist4'); +// $this->assertEquals(true, $artist4->isEmpty()); +// +// // Try to delete document while still related to another with on delete: restrict +// try { +// static::getDatabase()->deleteDocument('artist', 'artist1'); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); +// } +// +// // Change on delete to set null +// static::getDatabase()->updateRelationship( +// collection: 'artist', +// id: 'newAlbums', +// onDelete: Database::RELATION_MUTATE_SET_NULL +// ); +// +// // Delete parent, set child relationship to null +// static::getDatabase()->deleteDocument('artist', 'artist1'); +// +// // Check relation was set to null +// $album2 = static::getDatabase()->getDocument('album', 'album2'); +// $this->assertEquals(null, $album2->getAttribute('artist', '')); +// +// // Relate again +// static::getDatabase()->updateDocument( +// 'album', +// $album2->getId(), +// $album2->setAttribute('artist', 'artist2') +// ); +// +// // Change on delete to cascade +// static::getDatabase()->updateRelationship( +// collection: 'artist', +// id: 'newAlbums', +// onDelete: Database::RELATION_MUTATE_CASCADE +// ); +// +// // Delete parent, will delete child +// static::getDatabase()->deleteDocument('artist', 'artist2'); +// +// // Check parent and child were deleted +// $library = static::getDatabase()->getDocument('artist', 'artist2'); +// $this->assertEquals(true, $library->isEmpty()); +// +// $library = static::getDatabase()->getDocument('album', 'album2'); +// $this->assertEquals(true, $library->isEmpty()); +// +// $albums = []; +// for ($i = 1 ; $i <= 50 ; $i++) { +// $albums[] = [ +// '$id' => 'album_' . $i, +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'album ' . $i . ' ' . 'Artist 100', +// 'price' => 100, +// ]; +// } +// +// $artist = static::getDatabase()->createDocument('artist', new Document([ +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Artist 100', +// 'newAlbums' => $albums +// ])); +// +// $artist = static::getDatabase()->getDocument('artist', $artist->getId()); +// $this->assertCount(50, $artist->getAttribute('newAlbums')); +// +// $albums = static::getDatabase()->find('album', [ +// Query::equal('artist', [$artist->getId()]), +// Query::limit(999) +// ]); +// +// $this->assertCount(50, $albums); +// +// $count = static::getDatabase()->count('album', [ +// Query::equal('artist', [$artist->getId()]), +// ]); +// +// $this->assertEquals(50, $count); +// +// static::getDatabase()->deleteDocument('album', 'album_1'); +// $artist = static::getDatabase()->getDocument('artist', $artist->getId()); +// $this->assertCount(49, $artist->getAttribute('newAlbums')); +// +// static::getDatabase()->deleteDocument('artist', $artist->getId()); +// +// $albums = static::getDatabase()->find('album', [ +// Query::equal('artist', [$artist->getId()]), +// Query::limit(999) +// ]); +// +// $this->assertCount(0, $albums); +// +// // Delete relationship +// static::getDatabase()->deleteRelationship( +// 'artist', +// 'newAlbums' +// ); +// +// // Try to get document again +// $artist = static::getDatabase()->getDocument('artist', 'artist1'); +// $albums = $artist->getAttribute('newAlbums', ''); +// $this->assertEquals(null, $albums); +// } +// +// public function testOneToManyTwoWayRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('customer'); +// static::getDatabase()->createCollection('account'); +// +// static::getDatabase()->createAttribute('customer', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('account', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('account', 'number', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'customer', +// relatedCollection: 'account', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// id: 'accounts' +// ); +// +// // Check metadata for collection +// $collection = static::getDatabase()->getCollection('customer'); +// $attributes = $collection->getAttribute('attributes', []); +// foreach ($attributes as $attribute) { +// if ($attribute['key'] === 'accounts') { +// $this->assertEquals('relationship', $attribute['type']); +// $this->assertEquals('accounts', $attribute['$id']); +// $this->assertEquals('accounts', $attribute['key']); +// $this->assertEquals('account', $attribute['options']['relatedCollection']); +// $this->assertEquals(Database::RELATION_ONE_TO_MANY, $attribute['options']['relationType']); +// $this->assertEquals(true, $attribute['options']['twoWay']); +// $this->assertEquals('customer', $attribute['options']['twoWayKey']); +// } +// } +// +// // Check metadata for related collection +// $collection = static::getDatabase()->getCollection('account'); +// $attributes = $collection->getAttribute('attributes', []); +// foreach ($attributes as $attribute) { +// if ($attribute['key'] === 'customer') { +// $this->assertEquals('relationship', $attribute['type']); +// $this->assertEquals('customer', $attribute['$id']); +// $this->assertEquals('customer', $attribute['key']); +// $this->assertEquals('customer', $attribute['options']['relatedCollection']); +// $this->assertEquals(Database::RELATION_ONE_TO_MANY, $attribute['options']['relationType']); +// $this->assertEquals(true, $attribute['options']['twoWay']); +// $this->assertEquals('accounts', $attribute['options']['twoWayKey']); +// } +// } +// +// // Create document with relationship with nested data +// $customer1 = static::getDatabase()->createDocument('customer', new Document([ +// '$id' => 'customer1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Customer 1', +// 'accounts' => [ +// [ +// '$id' => 'account1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Account 1', +// 'number' => '123456789', +// ], +// ], +// ])); +// +// // Update a document with non existing related document. It should not get added to the list. +// static::getDatabase()->updateDocument('customer', 'customer1', $customer1->setAttribute('accounts', ['account1','no-account'])); +// +// $customer1Document = static::getDatabase()->getDocument('customer', 'customer1'); +// // Assert document does not contain non existing relation document. +// $this->assertEquals(1, \count($customer1Document->getAttribute('accounts'))); +// +// // Create document with relationship with related ID +// $account2 = static::getDatabase()->createDocument('account', new Document([ +// '$id' => 'account2', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Account 2', +// 'number' => '987654321', +// ])); +// static::getDatabase()->createDocument('customer', new Document([ +// '$id' => 'customer2', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Customer 2', +// 'accounts' => [ +// 'account2' +// ] +// ])); +// +// // Create from child side +// static::getDatabase()->createDocument('account', new Document([ +// '$id' => 'account3', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Account 3', +// 'number' => '123456789', +// 'customer' => [ +// '$id' => 'customer3', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Customer 3' +// ] +// ])); +// static::getDatabase()->createDocument('customer', new Document([ +// '$id' => 'customer4', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Customer 4', +// ])); +// static::getDatabase()->createDocument('account', new Document([ +// '$id' => 'account4', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Account 4', +// 'number' => '123456789', +// 'customer' => 'customer4' +// ])); +// +// // Get documents with relationship +// $customer = static::getDatabase()->getDocument('customer', 'customer1'); +// $accounts = $customer->getAttribute('accounts', []); +// $this->assertEquals('account1', $accounts[0]['$id']); +// $this->assertArrayNotHasKey('customer', $accounts[0]); +// +// $customer = static::getDatabase()->getDocument('customer', 'customer2'); +// $accounts = $customer->getAttribute('accounts', []); +// $this->assertEquals('account2', $accounts[0]['$id']); +// $this->assertArrayNotHasKey('customer', $accounts[0]); +// +// $customer = static::getDatabase()->getDocument('customer', 'customer3'); +// $accounts = $customer->getAttribute('accounts', []); +// $this->assertEquals('account3', $accounts[0]['$id']); +// $this->assertArrayNotHasKey('customer', $accounts[0]); +// +// $customer = static::getDatabase()->getDocument('customer', 'customer4'); +// $accounts = $customer->getAttribute('accounts', []); +// $this->assertEquals('account4', $accounts[0]['$id']); +// $this->assertArrayNotHasKey('customer', $accounts[0]); +// +// // Get related documents +// $account = static::getDatabase()->getDocument('account', 'account1'); +// $customer = $account->getAttribute('customer'); +// $this->assertEquals('customer1', $customer['$id']); +// $this->assertArrayNotHasKey('accounts', $customer); +// +// $account = static::getDatabase()->getDocument('account', 'account2'); +// $customer = $account->getAttribute('customer'); +// $this->assertEquals('customer2', $customer['$id']); +// $this->assertArrayNotHasKey('accounts', $customer); +// +// $account = static::getDatabase()->getDocument('account', 'account3'); +// $customer = $account->getAttribute('customer'); +// $this->assertEquals('customer3', $customer['$id']); +// $this->assertArrayNotHasKey('accounts', $customer); +// +// $account = static::getDatabase()->getDocument('account', 'account4'); +// $customer = $account->getAttribute('customer'); +// $this->assertEquals('customer4', $customer['$id']); +// $this->assertArrayNotHasKey('accounts', $customer); +// +// $customers = static::getDatabase()->find('customer'); +// +// $this->assertEquals(4, \count($customers)); +// +// // Select related document attributes +// $customer = static::getDatabase()->findOne('customer', [ +// Query::select(['*', 'accounts.name']) +// ]); +// +// if (!$customer instanceof Document) { +// throw new Exception('Customer not found'); +// } +// +// $this->assertEquals('Account 1', $customer->getAttribute('accounts')[0]->getAttribute('name')); +// $this->assertArrayNotHasKey('number', $customer->getAttribute('accounts')[0]); +// +// $customer = static::getDatabase()->getDocument('customer', 'customer1', [ +// Query::select(['*', 'accounts.name']) +// ]); +// +// $this->assertEquals('Account 1', $customer->getAttribute('accounts')[0]->getAttribute('name')); +// $this->assertArrayNotHasKey('number', $customer->getAttribute('accounts')[0]); +// +// // Update root document attribute without altering relationship +// $customer1 = static::getDatabase()->updateDocument( +// 'customer', +// $customer1->getId(), +// $customer1->setAttribute('name', 'Customer 1 Updated') +// ); +// +// $this->assertEquals('Customer 1 Updated', $customer1->getAttribute('name')); +// $customer1 = static::getDatabase()->getDocument('customer', 'customer1'); +// $this->assertEquals('Customer 1 Updated', $customer1->getAttribute('name')); +// +// $account2 = static::getDatabase()->getDocument('account', 'account2'); +// +// // Update inverse root document attribute without altering relationship +// $account2 = static::getDatabase()->updateDocument( +// 'account', +// $account2->getId(), +// $account2->setAttribute('name', 'Account 2 Updated') +// ); +// +// $this->assertEquals('Account 2 Updated', $account2->getAttribute('name')); +// $account2 = static::getDatabase()->getDocument('account', 'account2'); +// $this->assertEquals('Account 2 Updated', $account2->getAttribute('name')); +// +// // Update nested document attribute +// $accounts = $customer1->getAttribute('accounts', []); +// $accounts[0]->setAttribute('name', 'Account 1 Updated'); +// +// $customer1 = static::getDatabase()->updateDocument( +// 'customer', +// $customer1->getId(), +// $customer1->setAttribute('accounts', $accounts) +// ); +// +// $this->assertEquals('Account 1 Updated', $customer1->getAttribute('accounts')[0]->getAttribute('name')); +// $customer1 = static::getDatabase()->getDocument('customer', 'customer1'); +// $this->assertEquals('Account 1 Updated', $customer1->getAttribute('accounts')[0]->getAttribute('name')); +// +// // Update inverse nested document attribute +// $account2 = static::getDatabase()->updateDocument( +// 'account', +// $account2->getId(), +// $account2->setAttribute( +// 'customer', +// $account2 +// ->getAttribute('customer') +// ->setAttribute('name', 'Customer 2 Updated') +// ) +// ); +// +// $this->assertEquals('Customer 2 Updated', $account2->getAttribute('customer')->getAttribute('name')); +// $account2 = static::getDatabase()->getDocument('account', 'account2'); +// $this->assertEquals('Customer 2 Updated', $account2->getAttribute('customer')->getAttribute('name')); +// +// // Create new document with no relationship +// $customer5 = static::getDatabase()->createDocument('customer', new Document([ +// '$id' => 'customer5', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Customer 5', +// ])); +// +// // Update to relate to created document +// $customer5 = static::getDatabase()->updateDocument( +// 'customer', +// $customer5->getId(), +// $customer5->setAttribute('accounts', [new Document([ +// '$id' => 'account5', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Account 5', +// 'number' => '123456789', +// ])]) +// ); +// +// $this->assertEquals('Account 5', $customer5->getAttribute('accounts')[0]->getAttribute('name')); +// $customer5 = static::getDatabase()->getDocument('customer', 'customer5'); +// $this->assertEquals('Account 5', $customer5->getAttribute('accounts')[0]->getAttribute('name')); +// +// // Create new child document with no relationship +// $account6 = static::getDatabase()->createDocument('account', new Document([ +// '$id' => 'account6', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Account 6', +// 'number' => '123456789', +// ])); +// +// // Update inverse to relate to created document +// $account6 = static::getDatabase()->updateDocument( +// 'account', +// $account6->getId(), +// $account6->setAttribute('customer', new Document([ +// '$id' => 'customer6', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Customer 6', +// ])) +// ); +// +// $this->assertEquals('Customer 6', $account6->getAttribute('customer')->getAttribute('name')); +// $account6 = static::getDatabase()->getDocument('account', 'account6'); +// $this->assertEquals('Customer 6', $account6->getAttribute('customer')->getAttribute('name')); +// +// // Update document with new related document, will remove existing relations +// static::getDatabase()->updateDocument( +// 'customer', +// $customer1->getId(), +// $customer1->setAttribute('accounts', ['account2']) +// ); +// +// // Update document with new related document +// static::getDatabase()->updateDocument( +// 'customer', +// $customer1->getId(), +// $customer1->setAttribute('accounts', ['account1', 'account2']) +// ); +// +// // Update inverse document +// static::getDatabase()->updateDocument( +// 'account', +// $account2->getId(), +// $account2->setAttribute('customer', 'customer2') +// ); +// +// // Rename relationship keys on both sides +// static::getDatabase()->updateRelationship( +// 'customer', +// 'accounts', +// 'newAccounts', +// 'newCustomer' +// ); +// +// // Get document with new relationship key +// $customer = static::getDatabase()->getDocument('customer', 'customer1'); +// $accounts = $customer->getAttribute('newAccounts'); +// $this->assertEquals('account1', $accounts[0]['$id']); +// +// // Get inverse document with new relationship key +// $account = static::getDatabase()->getDocument('account', 'account1'); +// $customer = $account->getAttribute('newCustomer'); +// $this->assertEquals('customer1', $customer['$id']); +// +// // Create new document with no relationship +// static::getDatabase()->createDocument('customer', new Document([ +// '$id' => 'customer7', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Customer 7', +// ])); +// +// // Can delete document with no relationship when on delete is set to restrict +// $deleted = static::getDatabase()->deleteDocument('customer', 'customer7'); +// $this->assertEquals(true, $deleted); +// +// $customer7 = static::getDatabase()->getDocument('customer', 'customer7'); +// $this->assertEquals(true, $customer7->isEmpty()); +// +// // Try to delete document while still related to another with on delete: restrict +// try { +// static::getDatabase()->deleteDocument('customer', 'customer1'); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); +// } +// +// // Change on delete to set null +// static::getDatabase()->updateRelationship( +// collection: 'customer', +// id: 'newAccounts', +// onDelete: Database::RELATION_MUTATE_SET_NULL +// ); +// +// // Delete parent, set child relationship to null +// static::getDatabase()->deleteDocument('customer', 'customer1'); +// +// // Check relation was set to null +// $account1 = static::getDatabase()->getDocument('account', 'account1'); +// $this->assertEquals(null, $account2->getAttribute('newCustomer', '')); +// +// // Relate again +// static::getDatabase()->updateDocument( +// 'account', +// $account1->getId(), +// $account1->setAttribute('newCustomer', 'customer2') +// ); +// +// // Change on delete to cascade +// static::getDatabase()->updateRelationship( +// collection: 'customer', +// id: 'newAccounts', +// onDelete: Database::RELATION_MUTATE_CASCADE +// ); +// +// // Delete parent, will delete child +// static::getDatabase()->deleteDocument('customer', 'customer2'); +// +// // Check parent and child were deleted +// $library = static::getDatabase()->getDocument('customer', 'customer2'); +// $this->assertEquals(true, $library->isEmpty()); +// +// $library = static::getDatabase()->getDocument('account', 'account2'); +// $this->assertEquals(true, $library->isEmpty()); +// +// // Delete relationship +// static::getDatabase()->deleteRelationship( +// 'customer', +// 'newAccounts' +// ); +// +// // Try to get document again +// $customer = static::getDatabase()->getDocument('customer', 'customer1'); +// $accounts = $customer->getAttribute('newAccounts'); +// $this->assertEquals(null, $accounts); +// +// // Try to get inverse document again +// $accounts = static::getDatabase()->getDocument('account', 'account1'); +// $customer = $accounts->getAttribute('newCustomer'); +// $this->assertEquals(null, $customer); +// } +// +// public function testManyToOneOneWayRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('review'); +// static::getDatabase()->createCollection('movie'); +// +// static::getDatabase()->createAttribute('review', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('movie', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('movie', 'length', Database::VAR_INTEGER, 0, true, formatOptions: ['min' => 0, 'max' => 999]); +// static::getDatabase()->createAttribute('movie', 'date', Database::VAR_DATETIME, 0, false, filters: ['datetime']); +// static::getDatabase()->createAttribute('review', 'date', Database::VAR_DATETIME, 0, false, filters: ['datetime']); +// static::getDatabase()->createRelationship( +// collection: 'review', +// relatedCollection: 'movie', +// type: Database::RELATION_MANY_TO_ONE, +// twoWayKey: 'reviews' +// ); +// +// // Check metadata for collection +// $collection = static::getDatabase()->getCollection('review'); +// $attributes = $collection->getAttribute('attributes', []); +// foreach ($attributes as $attribute) { +// if ($attribute['key'] === 'movie') { +// $this->assertEquals('relationship', $attribute['type']); +// $this->assertEquals('movie', $attribute['$id']); +// $this->assertEquals('movie', $attribute['key']); +// $this->assertEquals('movie', $attribute['options']['relatedCollection']); +// $this->assertEquals(Database::RELATION_MANY_TO_ONE, $attribute['options']['relationType']); +// $this->assertEquals(false, $attribute['options']['twoWay']); +// $this->assertEquals('reviews', $attribute['options']['twoWayKey']); +// } +// } +// +// // Check metadata for related collection +// $collection = static::getDatabase()->getCollection('movie'); +// $attributes = $collection->getAttribute('attributes', []); +// foreach ($attributes as $attribute) { +// if ($attribute['key'] === 'reviews') { +// $this->assertEquals('relationship', $attribute['type']); +// $this->assertEquals('reviews', $attribute['$id']); +// $this->assertEquals('reviews', $attribute['key']); +// $this->assertEquals('review', $attribute['options']['relatedCollection']); +// $this->assertEquals(Database::RELATION_MANY_TO_ONE, $attribute['options']['relationType']); +// $this->assertEquals(false, $attribute['options']['twoWay']); +// $this->assertEquals('movie', $attribute['options']['twoWayKey']); +// } +// } +// +// // Create document with relationship with nested data +// $review1 = static::getDatabase()->createDocument('review', new Document([ +// '$id' => 'review1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Review 1', +// 'date' => '2023-04-03 10:35:27.390', +// 'movie' => [ +// '$id' => 'movie1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Movie 1', +// 'date' => '2023-04-03 10:35:27.390', +// 'length' => 120, +// ], +// ])); +// +// // Update a document with non existing related document. It should not get added to the list. +// static::getDatabase()->updateDocument('review', 'review1', $review1->setAttribute('movie', 'no-movie')); +// +// $review1Document = static::getDatabase()->getDocument('review', 'review1'); +// // Assert document does not contain non existing relation document. +// $this->assertEquals(null, $review1Document->getAttribute('movie')); +// +// static::getDatabase()->updateDocument('review', 'review1', $review1->setAttribute('movie', 'movie1')); +// +// // Create document with relationship to existing document by ID +// $review10 = static::getDatabase()->createDocument('review', new Document([ +// '$id' => 'review10', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Review 10', +// 'movie' => 'movie1', +// 'date' => '2023-04-03 10:35:27.390', +// ])); +// +// // Create document with relationship with related ID +// static::getDatabase()->createDocument('movie', new Document([ +// '$id' => 'movie2', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Movie 2', +// 'length' => 90, +// 'date' => '2023-04-03 10:35:27.390', +// ])); +// static::getDatabase()->createDocument('review', new Document([ +// '$id' => 'review2', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Review 2', +// 'movie' => 'movie2', +// 'date' => '2023-04-03 10:35:27.390', +// ])); +// +// // Get document with relationship +// $review = static::getDatabase()->getDocument('review', 'review1'); +// $movie = $review->getAttribute('movie', []); +// $this->assertEquals('movie1', $movie['$id']); +// $this->assertArrayNotHasKey('reviews', $movie); +// +// $documents = static::getDatabase()->find('review', [ +// Query::select(['date', 'movie.date']) +// ]); +// +// $this->assertCount(3, $documents); +// +// $document = $documents[0]; +// $this->assertArrayHasKey('date', $document); +// $this->assertArrayHasKey('movie', $document); +// $this->assertArrayHasKey('date', $document->getAttribute('movie')); +// $this->assertArrayNotHasKey('name', $document); +// $this->assertEquals(29, strlen($document['date'])); // checks filter +// $this->assertEquals(29, strlen($document['movie']['date'])); +// +// $review = static::getDatabase()->getDocument('review', 'review2'); +// $movie = $review->getAttribute('movie', []); +// $this->assertEquals('movie2', $movie['$id']); +// $this->assertArrayNotHasKey('reviews', $movie); +// +// // Get related document +// $movie = static::getDatabase()->getDocument('movie', 'movie1'); +// $this->assertArrayNotHasKey('reviews', $movie); +// +// $movie = static::getDatabase()->getDocument('movie', 'movie2'); +// $this->assertArrayNotHasKey('reviews', $movie); +// +// $reviews = static::getDatabase()->find('review'); +// +// $this->assertEquals(3, \count($reviews)); +// +// // Select related document attributes +// $review = static::getDatabase()->findOne('review', [ +// Query::select(['*', 'movie.name']) +// ]); +// +// if (!$review instanceof Document) { +// throw new Exception('Review not found'); +// } +// +// $this->assertEquals('Movie 1', $review->getAttribute('movie')->getAttribute('name')); +// $this->assertArrayNotHasKey('length', $review->getAttribute('movie')); +// +// $review = static::getDatabase()->getDocument('review', 'review1', [ +// Query::select(['*', 'movie.name']) +// ]); +// +// $this->assertEquals('Movie 1', $review->getAttribute('movie')->getAttribute('name')); +// $this->assertArrayNotHasKey('length', $review->getAttribute('movie')); +// +// // Update root document attribute without altering relationship +// $review1 = static::getDatabase()->updateDocument( +// 'review', +// $review1->getId(), +// $review1->setAttribute('name', 'Review 1 Updated') +// ); +// +// $this->assertEquals('Review 1 Updated', $review1->getAttribute('name')); +// $review1 = static::getDatabase()->getDocument('review', 'review1'); +// $this->assertEquals('Review 1 Updated', $review1->getAttribute('name')); +// +// // Update nested document attribute +// $movie = $review1->getAttribute('movie'); +// $movie->setAttribute('name', 'Movie 1 Updated'); +// +// $review1 = static::getDatabase()->updateDocument( +// 'review', +// $review1->getId(), +// $review1->setAttribute('movie', $movie) +// ); +// +// $this->assertEquals('Movie 1 Updated', $review1->getAttribute('movie')->getAttribute('name')); +// $review1 = static::getDatabase()->getDocument('review', 'review1'); +// $this->assertEquals('Movie 1 Updated', $review1->getAttribute('movie')->getAttribute('name')); +// +// // Create new document with no relationship +// $review5 = static::getDatabase()->createDocument('review', new Document([ +// '$id' => 'review5', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Review 5', +// ])); +// +// // Update to relate to created document +// $review5 = static::getDatabase()->updateDocument( +// 'review', +// $review5->getId(), +// $review5->setAttribute('movie', new Document([ +// '$id' => 'movie5', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Movie 5', +// 'length' => 90, +// ])) +// ); +// +// $this->assertEquals('Movie 5', $review5->getAttribute('movie')->getAttribute('name')); +// $review5 = static::getDatabase()->getDocument('review', 'review5'); +// $this->assertEquals('Movie 5', $review5->getAttribute('movie')->getAttribute('name')); +// +// // Update document with new related document +// static::getDatabase()->updateDocument( +// 'review', +// $review1->getId(), +// $review1->setAttribute('movie', 'movie2') +// ); +// +// // Rename relationship keys on both sides +// static::getDatabase()->updateRelationship( +// 'review', +// 'movie', +// 'newMovie', +// ); +// +// // Get document with new relationship key +// $review = static::getDatabase()->getDocument('review', 'review1'); +// $movie = $review->getAttribute('newMovie'); +// $this->assertEquals('movie2', $movie['$id']); +// +// // Reset values +// $review1 = static::getDatabase()->getDocument('review', 'review1'); +// +// static::getDatabase()->updateDocument( +// 'review', +// $review1->getId(), +// $review1->setAttribute('newMovie', 'movie1') +// ); +// +// // Create new document with no relationship +// static::getDatabase()->createDocument('movie', new Document([ +// '$id' => 'movie3', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Movie 3', +// 'length' => 90, +// ])); +// +// // Can delete document with no relationship when on delete is set to restrict +// $deleted = static::getDatabase()->deleteDocument('movie', 'movie3'); +// $this->assertEquals(true, $deleted); +// +// $movie3 = static::getDatabase()->getDocument('movie', 'movie3'); +// $this->assertEquals(true, $movie3->isEmpty()); +// +// // Try to delete document while still related to another with on delete: restrict +// try { +// static::getDatabase()->deleteDocument('movie', 'movie1'); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); +// } +// +// // Change on delete to set null +// static::getDatabase()->updateRelationship( +// collection: 'review', +// id: 'newMovie', +// onDelete: Database::RELATION_MUTATE_SET_NULL +// ); +// +// // Delete child, set parent relationship to null +// static::getDatabase()->deleteDocument('movie', 'movie1'); +// +// // Check relation was set to null +// $review1 = static::getDatabase()->getDocument('review', 'review1'); +// $this->assertEquals(null, $review1->getAttribute('newMovie')); +// +// // Change on delete to cascade +// static::getDatabase()->updateRelationship( +// collection: 'review', +// id: 'newMovie', +// onDelete: Database::RELATION_MUTATE_CASCADE +// ); +// +// // Delete child, will delete parent +// static::getDatabase()->deleteDocument('movie', 'movie2'); +// +// // Check parent and child were deleted +// $library = static::getDatabase()->getDocument('movie', 'movie2'); +// $this->assertEquals(true, $library->isEmpty()); +// +// $library = static::getDatabase()->getDocument('review', 'review2'); +// $this->assertEquals(true, $library->isEmpty()); +// +// +// // Delete relationship +// static::getDatabase()->deleteRelationship( +// 'review', +// 'newMovie' +// ); +// +// // Try to get document again +// $review = static::getDatabase()->getDocument('review', 'review1'); +// $movie = $review->getAttribute('newMovie'); +// $this->assertEquals(null, $movie); +// } +// +// public function testManyToOneTwoWayRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('product'); +// static::getDatabase()->createCollection('store'); +// +// static::getDatabase()->createAttribute('store', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('store', 'opensAt', Database::VAR_STRING, 5, true); +// +// static::getDatabase()->createAttribute( +// collection: 'product', +// id: 'name', +// type: Database::VAR_STRING, +// size: 255, +// required: true +// ); +// +// static::getDatabase()->createRelationship( +// collection: 'product', +// relatedCollection: 'store', +// type: Database::RELATION_MANY_TO_ONE, +// twoWay: true, +// twoWayKey: 'products' +// ); +// +// // Check metadata for collection +// $collection = static::getDatabase()->getCollection('product'); +// $attributes = $collection->getAttribute('attributes', []); +// foreach ($attributes as $attribute) { +// if ($attribute['key'] === 'store') { +// $this->assertEquals('relationship', $attribute['type']); +// $this->assertEquals('store', $attribute['$id']); +// $this->assertEquals('store', $attribute['key']); +// $this->assertEquals('store', $attribute['options']['relatedCollection']); +// $this->assertEquals(Database::RELATION_MANY_TO_ONE, $attribute['options']['relationType']); +// $this->assertEquals(true, $attribute['options']['twoWay']); +// $this->assertEquals('products', $attribute['options']['twoWayKey']); +// } +// } +// +// // Check metadata for related collection +// $collection = static::getDatabase()->getCollection('store'); +// $attributes = $collection->getAttribute('attributes', []); +// foreach ($attributes as $attribute) { +// if ($attribute['key'] === 'products') { +// $this->assertEquals('relationship', $attribute['type']); +// $this->assertEquals('products', $attribute['$id']); +// $this->assertEquals('products', $attribute['key']); +// $this->assertEquals('product', $attribute['options']['relatedCollection']); +// $this->assertEquals(Database::RELATION_MANY_TO_ONE, $attribute['options']['relationType']); +// $this->assertEquals(true, $attribute['options']['twoWay']); +// $this->assertEquals('store', $attribute['options']['twoWayKey']); +// } +// } +// +// // Create document with relationship with nested data +// $product1 = static::getDatabase()->createDocument('product', new Document([ +// '$id' => 'product1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Product 1', +// 'store' => [ +// '$id' => 'store1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Store 1', +// 'opensAt' => '09:00', +// ], +// ])); +// +// // Update a document with non existing related document. It should not get added to the list. +// static::getDatabase()->updateDocument('product', 'product1', $product1->setAttribute('store', 'no-store')); +// +// $product1Document = static::getDatabase()->getDocument('product', 'product1'); +// // Assert document does not contain non existing relation document. +// $this->assertEquals(null, $product1Document->getAttribute('store')); +// +// static::getDatabase()->updateDocument('product', 'product1', $product1->setAttribute('store', 'store1')); +// +// // Create document with relationship with related ID +// static::getDatabase()->createDocument('store', new Document([ +// '$id' => 'store2', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Store 2', +// 'opensAt' => '09:30', +// ])); +// static::getDatabase()->createDocument('product', new Document([ +// '$id' => 'product2', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Product 2', +// 'store' => 'store2', +// ])); +// +// // Create from child side +// static::getDatabase()->createDocument('store', new Document([ +// '$id' => 'store3', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Store 3', +// 'opensAt' => '11:30', +// 'products' => [ +// [ +// '$id' => 'product3', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Product 3', +// ], +// ], +// ])); +// +// static::getDatabase()->createDocument('product', new Document([ +// '$id' => 'product4', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Product 4', +// ])); +// static::getDatabase()->createDocument('store', new Document([ +// '$id' => 'store4', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Store 4', +// 'opensAt' => '11:30', +// 'products' => [ +// 'product4', +// ], +// ])); +// +// // Get document with relationship +// $product = static::getDatabase()->getDocument('product', 'product1'); +// $store = $product->getAttribute('store', []); +// $this->assertEquals('store1', $store['$id']); +// $this->assertArrayNotHasKey('products', $store); +// +// $product = static::getDatabase()->getDocument('product', 'product2'); +// $store = $product->getAttribute('store', []); +// $this->assertEquals('store2', $store['$id']); +// $this->assertArrayNotHasKey('products', $store); +// +// $product = static::getDatabase()->getDocument('product', 'product3'); +// $store = $product->getAttribute('store', []); +// $this->assertEquals('store3', $store['$id']); +// $this->assertArrayNotHasKey('products', $store); +// +// $product = static::getDatabase()->getDocument('product', 'product4'); +// $store = $product->getAttribute('store', []); +// $this->assertEquals('store4', $store['$id']); +// $this->assertArrayNotHasKey('products', $store); +// +// // Get related document +// $store = static::getDatabase()->getDocument('store', 'store1'); +// $products = $store->getAttribute('products'); +// $this->assertEquals('product1', $products[0]['$id']); +// $this->assertArrayNotHasKey('store', $products[0]); +// +// $store = static::getDatabase()->getDocument('store', 'store2'); +// $products = $store->getAttribute('products'); +// $this->assertEquals('product2', $products[0]['$id']); +// $this->assertArrayNotHasKey('store', $products[0]); +// +// $store = static::getDatabase()->getDocument('store', 'store3'); +// $products = $store->getAttribute('products'); +// $this->assertEquals('product3', $products[0]['$id']); +// $this->assertArrayNotHasKey('store', $products[0]); +// +// $store = static::getDatabase()->getDocument('store', 'store4'); +// $products = $store->getAttribute('products'); +// $this->assertEquals('product4', $products[0]['$id']); +// $this->assertArrayNotHasKey('store', $products[0]); +// +// $products = static::getDatabase()->find('product'); +// +// $this->assertEquals(4, \count($products)); +// +// // Select related document attributes +// $product = static::getDatabase()->findOne('product', [ +// Query::select(['*', 'store.name']) +// ]); +// +// if (!$product instanceof Document) { +// throw new Exception('Product not found'); +// } +// +// $this->assertEquals('Store 1', $product->getAttribute('store')->getAttribute('name')); +// $this->assertArrayNotHasKey('opensAt', $product->getAttribute('store')); +// +// $product = static::getDatabase()->getDocument('product', 'product1', [ +// Query::select(['*', 'store.name']) +// ]); +// +// $this->assertEquals('Store 1', $product->getAttribute('store')->getAttribute('name')); +// $this->assertArrayNotHasKey('opensAt', $product->getAttribute('store')); +// +// // Update root document attribute without altering relationship +// $product1 = static::getDatabase()->updateDocument( +// 'product', +// $product1->getId(), +// $product1->setAttribute('name', 'Product 1 Updated') +// ); +// +// $this->assertEquals('Product 1 Updated', $product1->getAttribute('name')); +// $product1 = static::getDatabase()->getDocument('product', 'product1'); +// $this->assertEquals('Product 1 Updated', $product1->getAttribute('name')); +// +// // Update inverse document attribute without altering relationship +// $store1 = static::getDatabase()->getDocument('store', 'store1'); +// $store1 = static::getDatabase()->updateDocument( +// 'store', +// $store1->getId(), +// $store1->setAttribute('name', 'Store 1 Updated') +// ); +// +// $this->assertEquals('Store 1 Updated', $store1->getAttribute('name')); +// $store1 = static::getDatabase()->getDocument('store', 'store1'); +// $this->assertEquals('Store 1 Updated', $store1->getAttribute('name')); +// +// // Update nested document attribute +// $store = $product1->getAttribute('store'); +// $store->setAttribute('name', 'Store 1 Updated'); +// +// $product1 = static::getDatabase()->updateDocument( +// 'product', +// $product1->getId(), +// $product1->setAttribute('store', $store) +// ); +// +// $this->assertEquals('Store 1 Updated', $product1->getAttribute('store')->getAttribute('name')); +// $product1 = static::getDatabase()->getDocument('product', 'product1'); +// $this->assertEquals('Store 1 Updated', $product1->getAttribute('store')->getAttribute('name')); +// +// // Update inverse nested document attribute +// $product = $store1->getAttribute('products')[0]; +// $product->setAttribute('name', 'Product 1 Updated'); +// +// $store1 = static::getDatabase()->updateDocument( +// 'store', +// $store1->getId(), +// $store1->setAttribute('products', [$product]) +// ); +// +// $this->assertEquals('Product 1 Updated', $store1->getAttribute('products')[0]->getAttribute('name')); +// $store1 = static::getDatabase()->getDocument('store', 'store1'); +// $this->assertEquals('Product 1 Updated', $store1->getAttribute('products')[0]->getAttribute('name')); +// +// // Create new document with no relationship +// $product5 = static::getDatabase()->createDocument('product', new Document([ +// '$id' => 'product5', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Product 5', +// ])); +// +// // Update to relate to created document +// $product5 = static::getDatabase()->updateDocument( +// 'product', +// $product5->getId(), +// $product5->setAttribute('store', new Document([ +// '$id' => 'store5', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Store 5', +// 'opensAt' => '09:00', +// ])) +// ); +// +// $this->assertEquals('Store 5', $product5->getAttribute('store')->getAttribute('name')); +// $product5 = static::getDatabase()->getDocument('product', 'product5'); +// $this->assertEquals('Store 5', $product5->getAttribute('store')->getAttribute('name')); +// +// // Create new child document with no relationship +// $store6 = static::getDatabase()->createDocument('store', new Document([ +// '$id' => 'store6', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Store 6', +// 'opensAt' => '09:00', +// ])); +// +// // Update inverse to related to newly created document +// $store6 = static::getDatabase()->updateDocument( +// 'store', +// $store6->getId(), +// $store6->setAttribute('products', [new Document([ +// '$id' => 'product6', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Product 6', +// ])]) +// ); +// +// $this->assertEquals('Product 6', $store6->getAttribute('products')[0]->getAttribute('name')); +// $store6 = static::getDatabase()->getDocument('store', 'store6'); +// $this->assertEquals('Product 6', $store6->getAttribute('products')[0]->getAttribute('name')); +// +// // Update document with new related document +// static::getDatabase()->updateDocument( +// 'product', +// $product1->getId(), +// $product1->setAttribute('store', 'store2') +// ); +// +// $store1 = static::getDatabase()->getDocument('store', 'store1'); +// +// // Update inverse document +// static::getDatabase()->updateDocument( +// 'store', +// $store1->getId(), +// $store1->setAttribute('products', ['product1']) +// ); +// +// $store2 = static::getDatabase()->getDocument('store', 'store2'); +// +// // Update inverse document +// static::getDatabase()->updateDocument( +// 'store', +// $store2->getId(), +// $store2->setAttribute('products', ['product1', 'product2']) +// ); +// +// // Rename relationship keys on both sides +// static::getDatabase()->updateRelationship( +// 'product', +// 'store', +// 'newStore', +// 'newProducts' +// ); +// +// // Get document with new relationship key +// $store = static::getDatabase()->getDocument('store', 'store2'); +// $products = $store->getAttribute('newProducts'); +// $this->assertEquals('product1', $products[0]['$id']); +// +// // Get inverse document with new relationship key +// $product = static::getDatabase()->getDocument('product', 'product1'); +// $store = $product->getAttribute('newStore'); +// $this->assertEquals('store2', $store['$id']); +// +// // Reset relationships +// $store1 = static::getDatabase()->getDocument('store', 'store1'); +// static::getDatabase()->updateDocument( +// 'store', +// $store1->getId(), +// $store1->setAttribute('newProducts', ['product1']) +// ); +// +// // Create new document with no relationship +// static::getDatabase()->createDocument('store', new Document([ +// '$id' => 'store7', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Store 7', +// 'opensAt' => '09:00', +// ])); +// +// // Can delete document with no relationship when on delete is set to restrict +// $deleted = static::getDatabase()->deleteDocument('store', 'store7'); +// $this->assertEquals(true, $deleted); +// +// $store7 = static::getDatabase()->getDocument('store', 'store7'); +// $this->assertEquals(true, $store7->isEmpty()); +// +// // Try to delete child while still related to another with on delete: restrict +// try { +// static::getDatabase()->deleteDocument('store', 'store1'); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); +// } +// +// // Delete parent while still related to another with on delete: restrict +// $result = static::getDatabase()->deleteDocument('product', 'product5'); +// $this->assertEquals(true, $result); +// +// // Change on delete to set null +// static::getDatabase()->updateRelationship( +// collection: 'product', +// id: 'newStore', +// onDelete: Database::RELATION_MUTATE_SET_NULL +// ); +// +// // Delete child, set parent relationship to null +// static::getDatabase()->deleteDocument('store', 'store1'); +// +// // Check relation was set to null +// static::getDatabase()->getDocument('product', 'product1'); +// $this->assertEquals(null, $product1->getAttribute('newStore')); +// +// // Change on delete to cascade +// static::getDatabase()->updateRelationship( +// collection: 'product', +// id: 'newStore', +// onDelete: Database::RELATION_MUTATE_CASCADE +// ); +// +// // Delete child, will delete parent +// static::getDatabase()->deleteDocument('store', 'store2'); +// +// // Check parent and child were deleted +// $library = static::getDatabase()->getDocument('store', 'store2'); +// $this->assertEquals(true, $library->isEmpty()); +// +// $library = static::getDatabase()->getDocument('product', 'product2'); +// $this->assertEquals(true, $library->isEmpty()); +// +// // Delete relationship +// static::getDatabase()->deleteRelationship( +// 'product', +// 'newStore' +// ); +// +// // Try to get document again +// $products = static::getDatabase()->getDocument('product', 'product1'); +// $store = $products->getAttribute('newStore'); +// $this->assertEquals(null, $store); +// +// // Try to get inverse document again +// $store = static::getDatabase()->getDocument('store', 'store1'); +// $products = $store->getAttribute('newProducts'); +// $this->assertEquals(null, $products); +// } +// +// public function testManyToManyOneWayRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('playlist'); +// static::getDatabase()->createCollection('song'); +// +// static::getDatabase()->createAttribute('playlist', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('song', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('song', 'length', Database::VAR_INTEGER, 0, true); +// +// static::getDatabase()->createRelationship( +// collection: 'playlist', +// relatedCollection: 'song', +// type: Database::RELATION_MANY_TO_MANY, +// id: 'songs' +// ); +// +// // Check metadata for collection +// $collection = static::getDatabase()->getCollection('playlist'); +// $attributes = $collection->getAttribute('attributes', []); +// +// foreach ($attributes as $attribute) { +// if ($attribute['key'] === 'songs') { +// $this->assertEquals('relationship', $attribute['type']); +// $this->assertEquals('songs', $attribute['$id']); +// $this->assertEquals('songs', $attribute['key']); +// $this->assertEquals('song', $attribute['options']['relatedCollection']); +// $this->assertEquals(Database::RELATION_MANY_TO_MANY, $attribute['options']['relationType']); +// $this->assertEquals(false, $attribute['options']['twoWay']); +// $this->assertEquals('playlist', $attribute['options']['twoWayKey']); +// } +// } +// +// // Create document with relationship with nested data +// $playlist1 = static::getDatabase()->createDocument('playlist', new Document([ +// '$id' => 'playlist1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Playlist 1', +// 'songs' => [ +// [ +// '$id' => 'song1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Song 1', +// 'length' => 180, +// ], +// ], +// ])); +// +// // Create document with relationship with related ID +// static::getDatabase()->createDocument('song', new Document([ +// '$id' => 'song2', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Song 2', +// 'length' => 140, +// ])); +// static::getDatabase()->createDocument('playlist', new Document([ +// '$id' => 'playlist2', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Playlist 2', +// 'songs' => [ +// 'song2' +// ] +// ])); +// +// // Update a document with non existing related document. It should not get added to the list. +// static::getDatabase()->updateDocument('playlist', 'playlist1', $playlist1->setAttribute('songs', ['song1','no-song'])); +// +// $playlist1Document = static::getDatabase()->getDocument('playlist', 'playlist1'); +// // Assert document does not contain non existing relation document. +// $this->assertEquals(1, \count($playlist1Document->getAttribute('songs'))); +// +// $documents = static::getDatabase()->find('playlist', [ +// Query::select(['name']), +// Query::limit(1) +// ]); +// +// $this->assertArrayNotHasKey('songs', $documents[0]); +// +// // Get document with relationship +// $playlist = static::getDatabase()->getDocument('playlist', 'playlist1'); +// $songs = $playlist->getAttribute('songs', []); +// $this->assertEquals('song1', $songs[0]['$id']); +// $this->assertArrayNotHasKey('playlist', $songs[0]); +// +// $playlist = static::getDatabase()->getDocument('playlist', 'playlist2'); +// $songs = $playlist->getAttribute('songs', []); +// $this->assertEquals('song2', $songs[0]['$id']); +// $this->assertArrayNotHasKey('playlist', $songs[0]); +// +// // Get related document +// $library = static::getDatabase()->getDocument('song', 'song1'); +// $this->assertArrayNotHasKey('songs', $library); +// +// $library = static::getDatabase()->getDocument('song', 'song2'); +// $this->assertArrayNotHasKey('songs', $library); +// +// $playlists = static::getDatabase()->find('playlist'); +// +// $this->assertEquals(2, \count($playlists)); +// +// // Select related document attributes +// $playlist = static::getDatabase()->findOne('playlist', [ +// Query::select(['*', 'songs.name']) +// ]); +// +// if (!$playlist instanceof Document) { +// throw new Exception('Playlist not found'); +// } +// +// $this->assertEquals('Song 1', $playlist->getAttribute('songs')[0]->getAttribute('name')); +// $this->assertArrayNotHasKey('length', $playlist->getAttribute('songs')[0]); +// +// $playlist = static::getDatabase()->getDocument('playlist', 'playlist1', [ +// Query::select(['*', 'songs.name']) +// ]); +// +// $this->assertEquals('Song 1', $playlist->getAttribute('songs')[0]->getAttribute('name')); +// $this->assertArrayNotHasKey('length', $playlist->getAttribute('songs')[0]); +// +// // Update root document attribute without altering relationship +// $playlist1 = static::getDatabase()->updateDocument( +// 'playlist', +// $playlist1->getId(), +// $playlist1->setAttribute('name', 'Playlist 1 Updated') +// ); +// +// $this->assertEquals('Playlist 1 Updated', $playlist1->getAttribute('name')); +// $playlist1 = static::getDatabase()->getDocument('playlist', 'playlist1'); +// $this->assertEquals('Playlist 1 Updated', $playlist1->getAttribute('name')); +// +// // Update nested document attribute +// $songs = $playlist1->getAttribute('songs', []); +// $songs[0]->setAttribute('name', 'Song 1 Updated'); +// +// $playlist1 = static::getDatabase()->updateDocument( +// 'playlist', +// $playlist1->getId(), +// $playlist1->setAttribute('songs', $songs) +// ); +// +// $this->assertEquals('Song 1 Updated', $playlist1->getAttribute('songs')[0]->getAttribute('name')); +// $playlist1 = static::getDatabase()->getDocument('playlist', 'playlist1'); +// $this->assertEquals('Song 1 Updated', $playlist1->getAttribute('songs')[0]->getAttribute('name')); +// +// // Create new document with no relationship +// $playlist5 = static::getDatabase()->createDocument('playlist', new Document([ +// '$id' => 'playlist5', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Playlist 5', +// ])); +// +// // Update to relate to created document +// $playlist5 = static::getDatabase()->updateDocument( +// 'playlist', +// $playlist5->getId(), +// $playlist5->setAttribute('songs', [new Document([ +// '$id' => 'song5', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Song 5', +// 'length' => 180, +// ])]) +// ); +// +// // Playlist relating to existing songs that belong to other playlists +// static::getDatabase()->createDocument('playlist', new Document([ +// '$id' => 'playlist6', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Playlist 6', +// 'songs' => [ +// 'song1', +// 'song2', +// 'song5' +// ] +// ])); +// +// $this->assertEquals('Song 5', $playlist5->getAttribute('songs')[0]->getAttribute('name')); +// $playlist5 = static::getDatabase()->getDocument('playlist', 'playlist5'); +// $this->assertEquals('Song 5', $playlist5->getAttribute('songs')[0]->getAttribute('name')); +// +// // Update document with new related document +// static::getDatabase()->updateDocument( +// 'playlist', +// $playlist1->getId(), +// $playlist1->setAttribute('songs', ['song2']) +// ); +// +// // Rename relationship key +// static::getDatabase()->updateRelationship( +// 'playlist', +// 'songs', +// 'newSongs' +// ); +// +// // Get document with new relationship key +// $playlist = static::getDatabase()->getDocument('playlist', 'playlist1'); +// $songs = $playlist->getAttribute('newSongs'); +// $this->assertEquals('song2', $songs[0]['$id']); +// +// // Create new document with no relationship +// static::getDatabase()->createDocument('playlist', new Document([ +// '$id' => 'playlist3', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Playlist 3', +// ])); +// +// // Can delete document with no relationship when on delete is set to restrict +// $deleted = static::getDatabase()->deleteDocument('playlist', 'playlist3'); +// $this->assertEquals(true, $deleted); +// +// $playlist3 = static::getDatabase()->getDocument('playlist', 'playlist3'); +// $this->assertEquals(true, $playlist3->isEmpty()); +// +// // Try to delete document while still related to another with on delete: restrict +// try { +// static::getDatabase()->deleteDocument('playlist', 'playlist1'); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); +// } +// +// // Change on delete to set null +// static::getDatabase()->updateRelationship( +// collection: 'playlist', +// id: 'newSongs', +// onDelete: Database::RELATION_MUTATE_SET_NULL +// ); +// +// $playlist1 = static::getDatabase()->getDocument('playlist', 'playlist1'); +// +// // Reset relationships +// static::getDatabase()->updateDocument( +// 'playlist', +// $playlist1->getId(), +// $playlist1->setAttribute('newSongs', ['song1']) +// ); +// +// // Delete child, will delete junction +// static::getDatabase()->deleteDocument('song', 'song1'); +// +// // Check relation was set to null +// $playlist1 = static::getDatabase()->getDocument('playlist', 'playlist1'); +// $this->assertEquals(0, \count($playlist1->getAttribute('newSongs'))); +// +// // Change on delete to cascade +// static::getDatabase()->updateRelationship( +// collection: 'playlist', +// id: 'newSongs', +// onDelete: Database::RELATION_MUTATE_CASCADE +// ); +// +// // Delete parent, will delete child +// static::getDatabase()->deleteDocument('playlist', 'playlist2'); +// +// // Check parent and child were deleted +// $library = static::getDatabase()->getDocument('playlist', 'playlist2'); +// $this->assertEquals(true, $library->isEmpty()); +// +// $library = static::getDatabase()->getDocument('song', 'song2'); +// $this->assertEquals(true, $library->isEmpty()); +// +// // Delete relationship +// static::getDatabase()->deleteRelationship( +// 'playlist', +// 'newSongs' +// ); +// +// // Try to get document again +// $playlist = static::getDatabase()->getDocument('playlist', 'playlist1'); +// $songs = $playlist->getAttribute('newSongs'); +// $this->assertEquals(null, $songs); +// } +// +// public function testManyToManyTwoWayRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('students'); +// static::getDatabase()->createCollection('classes'); +// +// static::getDatabase()->createAttribute('students', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('classes', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('classes', 'number', Database::VAR_INTEGER, 0, true); +// +// static::getDatabase()->createRelationship( +// collection: 'students', +// relatedCollection: 'classes', +// type: Database::RELATION_MANY_TO_MANY, +// twoWay: true, +// ); +// +// // Check metadata for collection +// $collection = static::getDatabase()->getCollection('students'); +// $attributes = $collection->getAttribute('attributes', []); +// foreach ($attributes as $attribute) { +// if ($attribute['key'] === 'students') { +// $this->assertEquals('relationship', $attribute['type']); +// $this->assertEquals('students', $attribute['$id']); +// $this->assertEquals('students', $attribute['key']); +// $this->assertEquals('students', $attribute['options']['relatedCollection']); +// $this->assertEquals(Database::RELATION_MANY_TO_MANY, $attribute['options']['relationType']); +// $this->assertEquals(true, $attribute['options']['twoWay']); +// $this->assertEquals('classes', $attribute['options']['twoWayKey']); +// } +// } +// +// // Check metadata for related collection +// $collection = static::getDatabase()->getCollection('classes'); +// $attributes = $collection->getAttribute('attributes', []); +// foreach ($attributes as $attribute) { +// if ($attribute['key'] === 'classes') { +// $this->assertEquals('relationship', $attribute['type']); +// $this->assertEquals('classes', $attribute['$id']); +// $this->assertEquals('classes', $attribute['key']); +// $this->assertEquals('classes', $attribute['options']['relatedCollection']); +// $this->assertEquals(Database::RELATION_MANY_TO_MANY, $attribute['options']['relationType']); +// $this->assertEquals(true, $attribute['options']['twoWay']); +// $this->assertEquals('students', $attribute['options']['twoWayKey']); +// } +// } +// +// // Create document with relationship with nested data +// $student1 = static::getDatabase()->createDocument('students', new Document([ +// '$id' => 'student1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Student 1', +// 'classes' => [ +// [ +// '$id' => 'class1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Class 1', +// 'number' => 1, +// ], +// ], +// ])); +// +// // Update a document with non existing related document. It should not get added to the list. +// static::getDatabase()->updateDocument('students', 'student1', $student1->setAttribute('classes', ['class1', 'no-class'])); +// +// $student1Document = static::getDatabase()->getDocument('students', 'student1'); +// // Assert document does not contain non existing relation document. +// $this->assertEquals(1, \count($student1Document->getAttribute('classes'))); +// +// // Create document with relationship with related ID +// static::getDatabase()->createDocument('classes', new Document([ +// '$id' => 'class2', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// +// ], +// 'name' => 'Class 2', +// 'number' => 2, +// ])); +// static::getDatabase()->createDocument('students', new Document([ +// '$id' => 'student2', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Student 2', +// 'classes' => [ +// 'class2' +// ], +// ])); +// +// // Create from child side +// static::getDatabase()->createDocument('classes', new Document([ +// '$id' => 'class3', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Class 3', +// 'number' => 3, +// 'students' => [ +// [ +// '$id' => 'student3', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Student 3', +// ] +// ], +// ])); +// static::getDatabase()->createDocument('students', new Document([ +// '$id' => 'student4', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Student 4' +// ])); +// static::getDatabase()->createDocument('classes', new Document([ +// '$id' => 'class4', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// +// ], +// 'name' => 'Class 4', +// 'number' => 4, +// 'students' => [ +// 'student4' +// ], +// ])); +// +// // Get document with relationship +// $student = static::getDatabase()->getDocument('students', 'student1'); +// $classes = $student->getAttribute('classes', []); +// $this->assertEquals('class1', $classes[0]['$id']); +// $this->assertArrayNotHasKey('students', $classes[0]); +// +// $student = static::getDatabase()->getDocument('students', 'student2'); +// $classes = $student->getAttribute('classes', []); +// $this->assertEquals('class2', $classes[0]['$id']); +// $this->assertArrayNotHasKey('students', $classes[0]); +// +// $student = static::getDatabase()->getDocument('students', 'student3'); +// $classes = $student->getAttribute('classes', []); +// $this->assertEquals('class3', $classes[0]['$id']); +// $this->assertArrayNotHasKey('students', $classes[0]); +// +// $student = static::getDatabase()->getDocument('students', 'student4'); +// $classes = $student->getAttribute('classes', []); +// $this->assertEquals('class4', $classes[0]['$id']); +// $this->assertArrayNotHasKey('students', $classes[0]); +// +// // Get related document +// $class = static::getDatabase()->getDocument('classes', 'class1'); +// $student = $class->getAttribute('students'); +// $this->assertEquals('student1', $student[0]['$id']); +// $this->assertArrayNotHasKey('classes', $student[0]); +// +// $class = static::getDatabase()->getDocument('classes', 'class2'); +// $student = $class->getAttribute('students'); +// $this->assertEquals('student2', $student[0]['$id']); +// $this->assertArrayNotHasKey('classes', $student[0]); +// +// $class = static::getDatabase()->getDocument('classes', 'class3'); +// $student = $class->getAttribute('students'); +// $this->assertEquals('student3', $student[0]['$id']); +// $this->assertArrayNotHasKey('classes', $student[0]); +// +// $class = static::getDatabase()->getDocument('classes', 'class4'); +// $student = $class->getAttribute('students'); +// $this->assertEquals('student4', $student[0]['$id']); +// $this->assertArrayNotHasKey('classes', $student[0]); +// +// // Select related document attributes +// $student = static::getDatabase()->findOne('students', [ +// Query::select(['*', 'classes.name']) +// ]); +// +// if (!$student instanceof Document) { +// throw new Exception('Student not found'); +// } +// +// $this->assertEquals('Class 1', $student->getAttribute('classes')[0]->getAttribute('name')); +// $this->assertArrayNotHasKey('number', $student->getAttribute('classes')[0]); +// +// $student = static::getDatabase()->getDocument('students', 'student1', [ +// Query::select(['*', 'classes.name']) +// ]); +// +// $this->assertEquals('Class 1', $student->getAttribute('classes')[0]->getAttribute('name')); +// $this->assertArrayNotHasKey('number', $student->getAttribute('classes')[0]); +// +// // Update root document attribute without altering relationship +// $student1 = static::getDatabase()->updateDocument( +// 'students', +// $student1->getId(), +// $student1->setAttribute('name', 'Student 1 Updated') +// ); +// +// $this->assertEquals('Student 1 Updated', $student1->getAttribute('name')); +// $student1 = static::getDatabase()->getDocument('students', 'student1'); +// $this->assertEquals('Student 1 Updated', $student1->getAttribute('name')); +// +// // Update inverse root document attribute without altering relationship +// $class2 = static::getDatabase()->getDocument('classes', 'class2'); +// $class2 = static::getDatabase()->updateDocument( +// 'classes', +// $class2->getId(), +// $class2->setAttribute('name', 'Class 2 Updated') +// ); +// +// $this->assertEquals('Class 2 Updated', $class2->getAttribute('name')); +// $class2 = static::getDatabase()->getDocument('classes', 'class2'); +// $this->assertEquals('Class 2 Updated', $class2->getAttribute('name')); +// +// // Update nested document attribute +// $classes = $student1->getAttribute('classes', []); +// $classes[0]->setAttribute('name', 'Class 1 Updated'); +// +// $student1 = static::getDatabase()->updateDocument( +// 'students', +// $student1->getId(), +// $student1->setAttribute('classes', $classes) +// ); +// +// $this->assertEquals('Class 1 Updated', $student1->getAttribute('classes')[0]->getAttribute('name')); +// $student1 = static::getDatabase()->getDocument('students', 'student1'); +// $this->assertEquals('Class 1 Updated', $student1->getAttribute('classes')[0]->getAttribute('name')); +// +// // Update inverse nested document attribute +// $students = $class2->getAttribute('students', []); +// $students[0]->setAttribute('name', 'Student 2 Updated'); +// +// $class2 = static::getDatabase()->updateDocument( +// 'classes', +// $class2->getId(), +// $class2->setAttribute('students', $students) +// ); +// +// $this->assertEquals('Student 2 Updated', $class2->getAttribute('students')[0]->getAttribute('name')); +// $class2 = static::getDatabase()->getDocument('classes', 'class2'); +// $this->assertEquals('Student 2 Updated', $class2->getAttribute('students')[0]->getAttribute('name')); +// +// // Create new document with no relationship +// $student5 = static::getDatabase()->createDocument('students', new Document([ +// '$id' => 'student5', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Student 5', +// ])); +// +// // Update to relate to created document +// $student5 = static::getDatabase()->updateDocument( +// 'students', +// $student5->getId(), +// $student5->setAttribute('classes', [new Document([ +// '$id' => 'class5', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Class 5', +// 'number' => 5, +// ])]) +// ); +// +// $this->assertEquals('Class 5', $student5->getAttribute('classes')[0]->getAttribute('name')); +// $student5 = static::getDatabase()->getDocument('students', 'student5'); +// $this->assertEquals('Class 5', $student5->getAttribute('classes')[0]->getAttribute('name')); +// +// // Create child document with no relationship +// $class6 = static::getDatabase()->createDocument('classes', new Document([ +// '$id' => 'class6', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Class 6', +// 'number' => 6, +// ])); +// +// // Update to relate to created document +// $class6 = static::getDatabase()->updateDocument( +// 'classes', +// $class6->getId(), +// $class6->setAttribute('students', [new Document([ +// '$id' => 'student6', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Student 6', +// ])]) +// ); +// +// $this->assertEquals('Student 6', $class6->getAttribute('students')[0]->getAttribute('name')); +// $class6 = static::getDatabase()->getDocument('classes', 'class6'); +// $this->assertEquals('Student 6', $class6->getAttribute('students')[0]->getAttribute('name')); +// +// // Update document with new related document +// static::getDatabase()->updateDocument( +// 'students', +// $student1->getId(), +// $student1->setAttribute('classes', ['class2']) +// ); +// +// $class1 = static::getDatabase()->getDocument('classes', 'class1'); +// +// // Update inverse document +// static::getDatabase()->updateDocument( +// 'classes', +// $class1->getId(), +// $class1->setAttribute('students', ['student1']) +// ); +// +// // Rename relationship keys on both sides +// static::getDatabase()->updateRelationship( +// 'students', +// 'classes', +// 'newClasses', +// 'newStudents' +// ); +// +// // Get document with new relationship key +// $students = static::getDatabase()->getDocument('students', 'student1'); +// $classes = $students->getAttribute('newClasses'); +// $this->assertEquals('class2', $classes[0]['$id']); +// +// // Get inverse document with new relationship key +// $class = static::getDatabase()->getDocument('classes', 'class1'); +// $students = $class->getAttribute('newStudents'); +// $this->assertEquals('student1', $students[0]['$id']); +// +// // Create new document with no relationship +// static::getDatabase()->createDocument('students', new Document([ +// '$id' => 'student7', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()), +// ], +// 'name' => 'Student 7', +// ])); +// +// // Can delete document with no relationship when on delete is set to restrict +// $deleted = static::getDatabase()->deleteDocument('students', 'student7'); +// $this->assertEquals(true, $deleted); +// +// $student6 = static::getDatabase()->getDocument('students', 'student7'); +// $this->assertEquals(true, $student6->isEmpty()); +// +// // Try to delete document while still related to another with on delete: restrict +// try { +// static::getDatabase()->deleteDocument('students', 'student1'); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); +// } +// +// // Change on delete to set null +// static::getDatabase()->updateRelationship( +// collection: 'students', +// id: 'newClasses', +// onDelete: Database::RELATION_MUTATE_SET_NULL +// ); +// +// $student1 = static::getDatabase()->getDocument('students', 'student1'); +// +// // Reset relationships +// static::getDatabase()->updateDocument( +// 'students', +// $student1->getId(), +// $student1->setAttribute('newClasses', ['class1']) +// ); +// +// // Delete child, will delete junction +// static::getDatabase()->deleteDocument('classes', 'class1'); +// +// // Check relation was set to null +// $student1 = static::getDatabase()->getDocument('students', 'student1'); +// $this->assertEquals(0, \count($student1->getAttribute('newClasses'))); +// +// // Change on delete to cascade +// static::getDatabase()->updateRelationship( +// collection: 'students', +// id: 'newClasses', +// onDelete: Database::RELATION_MUTATE_CASCADE +// ); +// +// // Delete parent, will delete child +// static::getDatabase()->deleteDocument('students', 'student2'); +// +// // Check parent and child were deleted +// $library = static::getDatabase()->getDocument('students', 'student2'); +// $this->assertEquals(true, $library->isEmpty()); +// +// // Delete child, should not delete parent +// static::getDatabase()->deleteDocument('classes', 'class6'); +// +// // Check only child was deleted +// $student6 = static::getDatabase()->getDocument('students', 'student6'); +// $this->assertEquals(false, $student6->isEmpty()); +// $this->assertEmpty($student6->getAttribute('newClasses')); +// +// $library = static::getDatabase()->getDocument('classes', 'class2'); +// $this->assertEquals(true, $library->isEmpty()); +// +// // Delete relationship +// static::getDatabase()->deleteRelationship( +// 'students', +// 'newClasses' +// ); +// +// // Try to get documents again +// $student = static::getDatabase()->getDocument('students', 'student1'); +// $classes = $student->getAttribute('newClasses'); +// $this->assertEquals(null, $classes); +// +// // Try to get inverse documents again +// $classes = static::getDatabase()->getDocument('classes', 'class1'); +// $students = $classes->getAttribute('newStudents'); +// $this->assertEquals(null, $students); +// } +// +// public function testSelectRelationshipAttributes(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('make'); +// static::getDatabase()->createCollection('model'); +// +// static::getDatabase()->createAttribute('make', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('model', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('model', 'year', Database::VAR_INTEGER, 0, true); +// +// static::getDatabase()->createRelationship( +// collection: 'make', +// relatedCollection: 'model', +// type: Database::RELATION_ONE_TO_MANY, +// id: 'models' +// ); +// +// static::getDatabase()->createDocument('make', new Document([ +// '$id' => 'ford', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Ford', +// 'models' => [ +// [ +// '$id' => 'fiesta', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Fiesta', +// 'year' => 2010, +// ], +// [ +// '$id' => 'focus', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Focus', +// 'year' => 2011, +// ], +// ], +// ])); +// +// // Select some parent attributes, some child attributes +// $make = static::getDatabase()->findOne('make', [ +// Query::select(['name', 'models.name']), +// ]); +// +// if (!$make instanceof Document) { +// throw new Exception('Make not found'); +// } +// +// $this->assertEquals('Ford', $make['name']); +// $this->assertEquals(2, \count($make['models'])); +// $this->assertEquals('Fiesta', $make['models'][0]['name']); +// $this->assertEquals('Focus', $make['models'][1]['name']); +// $this->assertArrayNotHasKey('year', $make['models'][0]); +// $this->assertArrayNotHasKey('year', $make['models'][1]); +// $this->assertArrayNotHasKey('$id', $make); +// $this->assertArrayNotHasKey('$internalId', $make); +// $this->assertArrayNotHasKey('$permissions', $make); +// $this->assertArrayNotHasKey('$collection', $make); +// $this->assertArrayNotHasKey('$createdAt', $make); +// $this->assertArrayNotHasKey('$updatedAt', $make); +// +// // Select internal attributes +// $make = static::getDatabase()->findOne('make', [ +// Query::select(['name', '$id']), +// ]); +// +// if (!$make instanceof Document) { +// throw new Exception('Make not found'); +// } +// +// $this->assertArrayHasKey('$id', $make); +// $this->assertArrayNotHasKey('$internalId', $make); +// $this->assertArrayNotHasKey('$collection', $make); +// $this->assertArrayNotHasKey('$createdAt', $make); +// $this->assertArrayNotHasKey('$updatedAt', $make); +// $this->assertArrayNotHasKey('$permissions', $make); +// +// $make = static::getDatabase()->findOne('make', [ +// Query::select(['name', '$internalId']), +// ]); +// +// if (!$make instanceof Document) { +// throw new Exception('Make not found'); +// } +// +// $this->assertArrayNotHasKey('$id', $make); +// $this->assertArrayHasKey('$internalId', $make); +// $this->assertArrayNotHasKey('$collection', $make); +// $this->assertArrayNotHasKey('$createdAt', $make); +// $this->assertArrayNotHasKey('$updatedAt', $make); +// $this->assertArrayNotHasKey('$permissions', $make); +// +// $make = static::getDatabase()->findOne('make', [ +// Query::select(['name', '$collection']), +// ]); +// +// if (!$make instanceof Document) { +// throw new Exception('Make not found'); +// } +// +// $this->assertArrayNotHasKey('$id', $make); +// $this->assertArrayNotHasKey('$internalId', $make); +// $this->assertArrayHasKey('$collection', $make); +// $this->assertArrayNotHasKey('$createdAt', $make); +// $this->assertArrayNotHasKey('$updatedAt', $make); +// $this->assertArrayNotHasKey('$permissions', $make); +// +// $make = static::getDatabase()->findOne('make', [ +// Query::select(['name', '$createdAt']), +// ]); +// +// if (!$make instanceof Document) { +// throw new Exception('Make not found'); +// } +// +// $this->assertArrayNotHasKey('$id', $make); +// $this->assertArrayNotHasKey('$internalId', $make); +// $this->assertArrayNotHasKey('$collection', $make); +// $this->assertArrayHasKey('$createdAt', $make); +// $this->assertArrayNotHasKey('$updatedAt', $make); +// $this->assertArrayNotHasKey('$permissions', $make); +// +// $make = static::getDatabase()->findOne('make', [ +// Query::select(['name', '$updatedAt']), +// ]); +// +// if (!$make instanceof Document) { +// throw new Exception('Make not found'); +// } +// +// $this->assertArrayNotHasKey('$id', $make); +// $this->assertArrayNotHasKey('$internalId', $make); +// $this->assertArrayNotHasKey('$collection', $make); +// $this->assertArrayNotHasKey('$createdAt', $make); +// $this->assertArrayHasKey('$updatedAt', $make); +// $this->assertArrayNotHasKey('$permissions', $make); +// +// $make = static::getDatabase()->findOne('make', [ +// Query::select(['name', '$permissions']), +// ]); +// +// if (!$make instanceof Document) { +// throw new Exception('Make not found'); +// } +// +// $this->assertArrayNotHasKey('$id', $make); +// $this->assertArrayNotHasKey('$internalId', $make); +// $this->assertArrayNotHasKey('$collection', $make); +// $this->assertArrayNotHasKey('$createdAt', $make); +// $this->assertArrayNotHasKey('$updatedAt', $make); +// $this->assertArrayHasKey('$permissions', $make); +// +// // Select all parent attributes, some child attributes +// $make = static::getDatabase()->findOne('make', [ +// Query::select(['*', 'models.year']), +// ]); +// +// if (!$make instanceof Document) { +// throw new Exception('Make not found'); +// } +// +// $this->assertEquals('Ford', $make['name']); +// $this->assertEquals(2, \count($make['models'])); +// $this->assertArrayNotHasKey('name', $make['models'][0]); +// $this->assertArrayNotHasKey('name', $make['models'][1]); +// $this->assertEquals(2010, $make['models'][0]['year']); +// $this->assertEquals(2011, $make['models'][1]['year']); +// +// // Select all parent attributes, all child attributes +// $make = static::getDatabase()->findOne('make', [ +// Query::select(['*', 'models.*']), +// ]); +// +// if (!$make instanceof Document) { +// throw new Exception('Make not found'); +// } +// +// $this->assertEquals('Ford', $make['name']); +// $this->assertEquals(2, \count($make['models'])); +// $this->assertEquals('Fiesta', $make['models'][0]['name']); +// $this->assertEquals('Focus', $make['models'][1]['name']); +// $this->assertEquals(2010, $make['models'][0]['year']); +// $this->assertEquals(2011, $make['models'][1]['year']); +// +// // Select all parent attributes, all child attributes +// // Must select parent if selecting children +// $make = static::getDatabase()->findOne('make', [ +// Query::select(['models.*']), +// ]); +// +// if (!$make instanceof Document) { +// throw new Exception('Make not found'); +// } +// +// $this->assertEquals('Ford', $make['name']); +// $this->assertEquals(2, \count($make['models'])); +// $this->assertEquals('Fiesta', $make['models'][0]['name']); +// $this->assertEquals('Focus', $make['models'][1]['name']); +// $this->assertEquals(2010, $make['models'][0]['year']); +// $this->assertEquals(2011, $make['models'][1]['year']); +// +// // Select all parent attributes, no child attributes +// $make = static::getDatabase()->findOne('make', [ +// Query::select(['name']), +// ]); +// +// if (!$make instanceof Document) { +// throw new Exception('Make not found'); +// } +// +// $this->assertEquals('Ford', $make['name']); +// $this->assertArrayNotHasKey('models', $make); +// } +// +// public function testNestedOneToOne_OneToOneRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('pattern'); +// static::getDatabase()->createCollection('shirt'); +// static::getDatabase()->createCollection('team'); +// +// static::getDatabase()->createAttribute('pattern', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('shirt', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('team', 'name', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'pattern', +// relatedCollection: 'shirt', +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// id: 'shirt', +// twoWayKey: 'pattern' +// ); +// static::getDatabase()->createRelationship( +// collection: 'shirt', +// relatedCollection: 'team', +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// id: 'team', +// twoWayKey: 'shirt' +// ); +// +// static::getDatabase()->createDocument('pattern', new Document([ +// '$id' => 'stripes', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Stripes', +// 'shirt' => [ +// '$id' => 'red', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Red', +// 'team' => [ +// '$id' => 'reds', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Reds', +// ], +// ], +// ])); +// +// $pattern = static::getDatabase()->getDocument('pattern', 'stripes'); +// $this->assertEquals('red', $pattern['shirt']['$id']); +// $this->assertArrayNotHasKey('pattern', $pattern['shirt']); +// $this->assertEquals('reds', $pattern['shirt']['team']['$id']); +// $this->assertArrayNotHasKey('shirt', $pattern['shirt']['team']); +// +// static::getDatabase()->createDocument('team', new Document([ +// '$id' => 'blues', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Blues', +// 'shirt' => [ +// '$id' => 'blue', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Blue', +// 'pattern' => [ +// '$id' => 'plain', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Plain', +// ], +// ], +// ])); +// +// $team = static::getDatabase()->getDocument('team', 'blues'); +// $this->assertEquals('blue', $team['shirt']['$id']); +// $this->assertArrayNotHasKey('team', $team['shirt']); +// $this->assertEquals('plain', $team['shirt']['pattern']['$id']); +// $this->assertArrayNotHasKey('shirt', $team['shirt']['pattern']); +// } +// +// public function testNestedOneToOne_OneToManyRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('teachers'); +// static::getDatabase()->createCollection('classrooms'); +// static::getDatabase()->createCollection('children'); +// +// static::getDatabase()->createAttribute('children', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('teachers', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('classrooms', 'name', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'teachers', +// relatedCollection: 'classrooms', +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// id: 'classroom', +// twoWayKey: 'teacher' +// ); +// static::getDatabase()->createRelationship( +// collection: 'classrooms', +// relatedCollection: 'children', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// twoWayKey: 'classroom' +// ); +// +// static::getDatabase()->createDocument('teachers', new Document([ +// '$id' => 'teacher1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Teacher 1', +// 'classroom' => [ +// '$id' => 'classroom1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Classroom 1', +// 'children' => [ +// [ +// '$id' => 'child1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Child 1', +// ], +// [ +// '$id' => 'child2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Child 2', +// ], +// ], +// ], +// ])); +// +// $teacher1 = static::getDatabase()->getDocument('teachers', 'teacher1'); +// $this->assertEquals('classroom1', $teacher1['classroom']['$id']); +// $this->assertArrayNotHasKey('teacher', $teacher1['classroom']); +// $this->assertEquals(2, \count($teacher1['classroom']['children'])); +// $this->assertEquals('Child 1', $teacher1['classroom']['children'][0]['name']); +// $this->assertEquals('Child 2', $teacher1['classroom']['children'][1]['name']); +// +// static::getDatabase()->createDocument('children', new Document([ +// '$id' => 'child3', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Child 3', +// 'classroom' => [ +// '$id' => 'classroom2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Classroom 2', +// 'teacher' => [ +// '$id' => 'teacher2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Teacher 2', +// ], +// ], +// ])); +// +// $child3 = static::getDatabase()->getDocument('children', 'child3'); +// $this->assertEquals('classroom2', $child3['classroom']['$id']); +// $this->assertArrayNotHasKey('children', $child3['classroom']); +// $this->assertEquals('teacher2', $child3['classroom']['teacher']['$id']); +// $this->assertArrayNotHasKey('classroom', $child3['classroom']['teacher']); +// } +// +// public function testNestedOneToOne_ManyToOneRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('users'); +// static::getDatabase()->createCollection('profiles'); +// static::getDatabase()->createCollection('avatars'); +// +// static::getDatabase()->createAttribute('users', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('profiles', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('avatars', 'name', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'users', +// relatedCollection: 'profiles', +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// id: 'profile', +// twoWayKey: 'user' +// ); +// static::getDatabase()->createRelationship( +// collection: 'profiles', +// relatedCollection: 'avatars', +// type: Database::RELATION_MANY_TO_ONE, +// twoWay: true, +// id: 'avatar', +// ); +// +// static::getDatabase()->createDocument('users', new Document([ +// '$id' => 'user1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'User 1', +// 'profile' => [ +// '$id' => 'profile1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Profile 1', +// 'avatar' => [ +// '$id' => 'avatar1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Avatar 1', +// ], +// ], +// ])); +// +// $user1 = static::getDatabase()->getDocument('users', 'user1'); +// $this->assertEquals('profile1', $user1['profile']['$id']); +// $this->assertArrayNotHasKey('user', $user1['profile']); +// $this->assertEquals('avatar1', $user1['profile']['avatar']['$id']); +// $this->assertArrayNotHasKey('profile', $user1['profile']['avatar']); +// +// static::getDatabase()->createDocument('avatars', new Document([ +// '$id' => 'avatar2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Avatar 2', +// 'profiles' => [ +// [ +// '$id' => 'profile2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Profile 2', +// 'user' => [ +// '$id' => 'user2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'User 2', +// ], +// ] +// ], +// ])); +// +// $avatar2 = static::getDatabase()->getDocument('avatars', 'avatar2'); +// $this->assertEquals('profile2', $avatar2['profiles'][0]['$id']); +// $this->assertArrayNotHasKey('avatars', $avatar2['profiles'][0]); +// $this->assertEquals('user2', $avatar2['profiles'][0]['user']['$id']); +// $this->assertArrayNotHasKey('profiles', $avatar2['profiles'][0]['user']); +// } +// +// public function testNestedOneToOne_ManyToManyRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('addresses'); +// static::getDatabase()->createCollection('houses'); +// static::getDatabase()->createCollection('buildings'); +// +// static::getDatabase()->createAttribute('addresses', 'street', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('houses', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('buildings', 'name', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'addresses', +// relatedCollection: 'houses', +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// id: 'house', +// twoWayKey: 'address' +// ); +// static::getDatabase()->createRelationship( +// collection: 'houses', +// relatedCollection: 'buildings', +// type: Database::RELATION_MANY_TO_MANY, +// twoWay: true, +// ); +// +// static::getDatabase()->createDocument('addresses', new Document([ +// '$id' => 'address1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'street' => 'Street 1', +// 'house' => [ +// '$id' => 'house1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'House 1', +// 'buildings' => [ +// [ +// '$id' => 'building1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Building 1', +// ], +// [ +// '$id' => 'building2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Building 2', +// ], +// ], +// ], +// ])); +// +// $address1 = static::getDatabase()->getDocument('addresses', 'address1'); +// $this->assertEquals('house1', $address1['house']['$id']); +// $this->assertArrayNotHasKey('address', $address1['house']); +// $this->assertEquals('building1', $address1['house']['buildings'][0]['$id']); +// $this->assertEquals('building2', $address1['house']['buildings'][1]['$id']); +// $this->assertArrayNotHasKey('houses', $address1['house']['buildings'][0]); +// $this->assertArrayNotHasKey('houses', $address1['house']['buildings'][1]); +// +// static::getDatabase()->createDocument('buildings', new Document([ +// '$id' => 'building3', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Building 3', +// 'houses' => [ +// [ +// '$id' => 'house2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'House 2', +// 'address' => [ +// '$id' => 'address2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'street' => 'Street 2', +// ], +// ], +// ], +// ])); +// } +// +// public function testNestedOneToMany_OneToOneRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('countries'); +// static::getDatabase()->createCollection('cities'); +// static::getDatabase()->createCollection('mayors'); +// +// static::getDatabase()->createAttribute('cities', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('countries', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('mayors', 'name', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'countries', +// relatedCollection: 'cities', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// twoWayKey: 'country' +// ); +// static::getDatabase()->createRelationship( +// collection: 'cities', +// relatedCollection: 'mayors', +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// id: 'mayor', +// twoWayKey: 'city' +// ); +// +// static::getDatabase()->createDocument('countries', new Document([ +// '$id' => 'country1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// ], +// 'name' => 'Country 1', +// 'cities' => [ +// [ +// '$id' => 'city1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// ], +// 'name' => 'City 1', +// 'mayor' => [ +// '$id' => 'mayor1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// ], +// 'name' => 'Mayor 1', +// ], +// ], +// [ +// '$id' => 'city2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'City 2', +// 'mayor' => [ +// '$id' => 'mayor2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Mayor 2', +// ], +// ], +// ], +// ])); +// +// $documents = static::getDatabase()->find('countries', [ +// Query::limit(1) +// ]); +// $this->assertEquals('Mayor 1', $documents[0]['cities'][0]['mayor']['name']); +// +// $documents = static::getDatabase()->find('countries', [ +// Query::select(['name']), +// Query::limit(1) +// ]); +// $this->assertArrayHasKey('name', $documents[0]); +// $this->assertArrayNotHasKey('cities', $documents[0]); +// +// $documents = static::getDatabase()->find('countries', [ +// Query::select(['*']), +// Query::limit(1) +// ]); +// $this->assertArrayHasKey('name', $documents[0]); +// $this->assertArrayNotHasKey('cities', $documents[0]); +// +// $documents = static::getDatabase()->find('countries', [ +// Query::select(['*', 'cities.*', 'cities.mayor.*']), +// Query::limit(1) +// ]); +// +// $this->assertEquals('Mayor 1', $documents[0]['cities'][0]['mayor']['name']); +// +// // Insert docs to cache: +// $country1 = static::getDatabase()->getDocument('countries', 'country1'); +// $mayor1 = static::getDatabase()->getDocument('mayors', 'mayor1'); +// $this->assertEquals('City 1', $mayor1['city']['name']); +// $this->assertEquals('City 1', $country1['cities'][0]['name']); +// +// static::getDatabase()->updateDocument('cities', 'city1', new Document([ +// '$id' => 'city1', +// '$collection' => 'cities', +// 'name' => 'City 1 updated', +// 'mayor' => 'mayor1', // we don't support partial updates at the moment +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// ], +// ])); +// +// $mayor1 = static::getDatabase()->getDocument('mayors', 'mayor1'); +// $country1 = static::getDatabase()->getDocument('countries', 'country1'); +// +// $this->assertEquals('City 1 updated', $mayor1['city']['name']); +// $this->assertEquals('City 1 updated', $country1['cities'][0]['name']); +// $this->assertEquals('city1', $country1['cities'][0]['$id']); +// $this->assertEquals('city2', $country1['cities'][1]['$id']); +// $this->assertEquals('mayor1', $country1['cities'][0]['mayor']['$id']); +// $this->assertEquals('mayor2', $country1['cities'][1]['mayor']['$id']); +// $this->assertArrayNotHasKey('city', $country1['cities'][0]['mayor']); +// $this->assertArrayNotHasKey('city', $country1['cities'][1]['mayor']); +// +// static::getDatabase()->createDocument('mayors', new Document([ +// '$id' => 'mayor3', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Mayor 3', +// 'city' => [ +// '$id' => 'city3', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'City 3', +// 'country' => [ +// '$id' => 'country2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Country 2', +// ], +// ], +// ])); +// +// $country2 = static::getDatabase()->getDocument('countries', 'country2'); +// $this->assertEquals('city3', $country2['cities'][0]['$id']); +// $this->assertEquals('mayor3', $country2['cities'][0]['mayor']['$id']); +// $this->assertArrayNotHasKey('country', $country2['cities'][0]); +// $this->assertArrayNotHasKey('city', $country2['cities'][0]['mayor']); +// } +// +// public function testNestedOneToMany_OneToManyRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('dormitories'); +// static::getDatabase()->createCollection('occupants'); +// static::getDatabase()->createCollection('pets'); +// +// static::getDatabase()->createAttribute('dormitories', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('occupants', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('pets', 'name', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'dormitories', +// relatedCollection: 'occupants', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// twoWayKey: 'dormitory' +// ); +// static::getDatabase()->createRelationship( +// collection: 'occupants', +// relatedCollection: 'pets', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// twoWayKey: 'occupant' +// ); +// +// static::getDatabase()->createDocument('dormitories', new Document([ +// '$id' => 'dormitory1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'House 1', +// 'occupants' => [ +// [ +// '$id' => 'occupant1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Occupant 1', +// 'pets' => [ +// [ +// '$id' => 'pet1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Pet 1', +// ], +// [ +// '$id' => 'pet2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Pet 2', +// ], +// ], +// ], +// [ +// '$id' => 'occupant2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Occupant 2', +// 'pets' => [ +// [ +// '$id' => 'pet3', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Pet 3', +// ], +// [ +// '$id' => 'pet4', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Pet 4', +// ], +// ], +// ], +// ], +// ])); +// +// $dormitory1 = static::getDatabase()->getDocument('dormitories', 'dormitory1'); +// $this->assertEquals('occupant1', $dormitory1['occupants'][0]['$id']); +// $this->assertEquals('occupant2', $dormitory1['occupants'][1]['$id']); +// $this->assertEquals('pet1', $dormitory1['occupants'][0]['pets'][0]['$id']); +// $this->assertEquals('pet2', $dormitory1['occupants'][0]['pets'][1]['$id']); +// $this->assertEquals('pet3', $dormitory1['occupants'][1]['pets'][0]['$id']); +// $this->assertEquals('pet4', $dormitory1['occupants'][1]['pets'][1]['$id']); +// $this->assertArrayNotHasKey('dormitory', $dormitory1['occupants'][0]); +// $this->assertArrayNotHasKey('dormitory', $dormitory1['occupants'][1]); +// $this->assertArrayNotHasKey('occupant', $dormitory1['occupants'][0]['pets'][0]); +// $this->assertArrayNotHasKey('occupant', $dormitory1['occupants'][0]['pets'][1]); +// $this->assertArrayNotHasKey('occupant', $dormitory1['occupants'][1]['pets'][0]); +// $this->assertArrayNotHasKey('occupant', $dormitory1['occupants'][1]['pets'][1]); +// +// static::getDatabase()->createDocument('pets', new Document([ +// '$id' => 'pet5', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Pet 5', +// 'occupant' => [ +// '$id' => 'occupant3', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Occupant 3', +// 'dormitory' => [ +// '$id' => 'dormitory2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'House 2', +// ], +// ], +// ])); +// +// $pet5 = static::getDatabase()->getDocument('pets', 'pet5'); +// $this->assertEquals('occupant3', $pet5['occupant']['$id']); +// $this->assertEquals('dormitory2', $pet5['occupant']['dormitory']['$id']); +// $this->assertArrayNotHasKey('pets', $pet5['occupant']); +// $this->assertArrayNotHasKey('occupant', $pet5['occupant']['dormitory']); +// } +// +// public function testNestedOneToMany_ManyToOneRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('home'); +// static::getDatabase()->createCollection('renters'); +// static::getDatabase()->createCollection('floors'); +// +// static::getDatabase()->createAttribute('home', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('renters', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('floors', 'name', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'home', +// relatedCollection: 'renters', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true +// ); +// static::getDatabase()->createRelationship( +// collection: 'renters', +// relatedCollection: 'floors', +// type: Database::RELATION_MANY_TO_ONE, +// twoWay: true, +// id: 'floor' +// ); +// +// static::getDatabase()->createDocument('home', new Document([ +// '$id' => 'home1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'House 1', +// 'renters' => [ +// [ +// '$id' => 'renter1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Occupant 1', +// 'floor' => [ +// '$id' => 'floor1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Floor 1', +// ], +// ], +// ], +// ])); +// +// $home1 = static::getDatabase()->getDocument('home', 'home1'); +// $this->assertEquals('renter1', $home1['renters'][0]['$id']); +// $this->assertEquals('floor1', $home1['renters'][0]['floor']['$id']); +// $this->assertArrayNotHasKey('home', $home1['renters'][0]); +// $this->assertArrayNotHasKey('renters', $home1['renters'][0]['floor']); +// +// static::getDatabase()->createDocument('floors', new Document([ +// '$id' => 'floor2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Floor 2', +// 'renters' => [ +// [ +// '$id' => 'renter2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Occupant 2', +// 'home' => [ +// '$id' => 'home2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'House 2', +// ], +// ], +// ], +// ])); +// +// $floor2 = static::getDatabase()->getDocument('floors', 'floor2'); +// $this->assertEquals('renter2', $floor2['renters'][0]['$id']); +// $this->assertArrayNotHasKey('floor', $floor2['renters'][0]); +// $this->assertEquals('home2', $floor2['renters'][0]['home']['$id']); +// $this->assertArrayNotHasKey('renter', $floor2['renters'][0]['home']); +// } +// +// public function testNestedOneToMany_ManyToManyRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('owners'); +// static::getDatabase()->createCollection('cats'); +// static::getDatabase()->createCollection('toys'); +// +// static::getDatabase()->createAttribute('owners', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('cats', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('toys', 'name', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'owners', +// relatedCollection: 'cats', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// twoWayKey: 'owner' +// ); +// static::getDatabase()->createRelationship( +// collection: 'cats', +// relatedCollection: 'toys', +// type: Database::RELATION_MANY_TO_MANY, +// twoWay: true +// ); +// +// static::getDatabase()->createDocument('owners', new Document([ +// '$id' => 'owner1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Owner 1', +// 'cats' => [ +// [ +// '$id' => 'cat1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Pet 1', +// 'toys' => [ +// [ +// '$id' => 'toy1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Toy 1', +// ], +// ], +// ], +// ], +// ])); +// +// $owner1 = static::getDatabase()->getDocument('owners', 'owner1'); +// $this->assertEquals('cat1', $owner1['cats'][0]['$id']); +// $this->assertArrayNotHasKey('owner', $owner1['cats'][0]); +// $this->assertEquals('toy1', $owner1['cats'][0]['toys'][0]['$id']); +// $this->assertArrayNotHasKey('cats', $owner1['cats'][0]['toys'][0]); +// +// static::getDatabase()->createDocument('toys', new Document([ +// '$id' => 'toy2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Toy 2', +// 'cats' => [ +// [ +// '$id' => 'cat2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Pet 2', +// 'owner' => [ +// '$id' => 'owner2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Owner 2', +// ], +// ], +// ], +// ])); +// +// $toy2 = static::getDatabase()->getDocument('toys', 'toy2'); +// $this->assertEquals('cat2', $toy2['cats'][0]['$id']); +// $this->assertArrayNotHasKey('toys', $toy2['cats'][0]); +// $this->assertEquals('owner2', $toy2['cats'][0]['owner']['$id']); +// $this->assertArrayNotHasKey('cats', $toy2['cats'][0]['owner']); +// } +// +// public function testNestedManyToOne_OneToOneRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('towns'); +// static::getDatabase()->createCollection('homelands'); +// static::getDatabase()->createCollection('capitals'); +// +// static::getDatabase()->createAttribute('towns', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('homelands', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('capitals', 'name', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'towns', +// relatedCollection: 'homelands', +// type: Database::RELATION_MANY_TO_ONE, +// twoWay: true, +// id: 'homeland' +// ); +// static::getDatabase()->createRelationship( +// collection: 'homelands', +// relatedCollection: 'capitals', +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// id: 'capital', +// twoWayKey: 'homeland' +// ); +// +// static::getDatabase()->createDocument('towns', new Document([ +// '$id' => 'town1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'City 1', +// 'homeland' => [ +// '$id' => 'homeland1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Country 1', +// 'capital' => [ +// '$id' => 'capital1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Flag 1', +// ], +// ], +// ])); +// +// $town1 = static::getDatabase()->getDocument('towns', 'town1'); +// $this->assertEquals('homeland1', $town1['homeland']['$id']); +// $this->assertArrayNotHasKey('towns', $town1['homeland']); +// $this->assertEquals('capital1', $town1['homeland']['capital']['$id']); +// $this->assertArrayNotHasKey('homeland', $town1['homeland']['capital']); +// +// static::getDatabase()->createDocument('capitals', new Document([ +// '$id' => 'capital2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Flag 2', +// 'homeland' => [ +// '$id' => 'homeland2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Country 2', +// 'towns' => [ +// [ +// '$id' => 'town2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Town 2', +// ], +// [ +// '$id' => 'town3', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Town 3', +// ], +// ], +// ], +// ])); +// +// $capital2 = static::getDatabase()->getDocument('capitals', 'capital2'); +// $this->assertEquals('homeland2', $capital2['homeland']['$id']); +// $this->assertArrayNotHasKey('capital', $capital2['homeland']); +// $this->assertEquals(2, \count($capital2['homeland']['towns'])); +// $this->assertEquals('town2', $capital2['homeland']['towns'][0]['$id']); +// $this->assertEquals('town3', $capital2['homeland']['towns'][1]['$id']); +// } +// +// public function testNestedManyToOne_OneToManyRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('players'); +// static::getDatabase()->createCollection('teams'); +// static::getDatabase()->createCollection('supporters'); +// +// static::getDatabase()->createAttribute('players', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('teams', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('supporters', 'name', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'players', +// relatedCollection: 'teams', +// type: Database::RELATION_MANY_TO_ONE, +// twoWay: true, +// id: 'team' +// ); +// static::getDatabase()->createRelationship( +// collection: 'teams', +// relatedCollection: 'supporters', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// id: 'supporters', +// twoWayKey: 'team' +// ); +// +// static::getDatabase()->createDocument('players', new Document([ +// '$id' => 'player1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Player 1', +// 'team' => [ +// '$id' => 'team1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Team 1', +// 'supporters' => [ +// [ +// '$id' => 'supporter1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Supporter 1', +// ], +// [ +// '$id' => 'supporter2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Supporter 2', +// ], +// ], +// ], +// ])); +// +// $player1 = static::getDatabase()->getDocument('players', 'player1'); +// $this->assertEquals('team1', $player1['team']['$id']); +// $this->assertArrayNotHasKey('players', $player1['team']); +// $this->assertEquals(2, \count($player1['team']['supporters'])); +// $this->assertEquals('supporter1', $player1['team']['supporters'][0]['$id']); +// $this->assertEquals('supporter2', $player1['team']['supporters'][1]['$id']); +// +// static::getDatabase()->createDocument('supporters', new Document([ +// '$id' => 'supporter3', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Supporter 3', +// 'team' => [ +// '$id' => 'team2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Team 2', +// 'players' => [ +// [ +// '$id' => 'player2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Player 2', +// ], +// [ +// '$id' => 'player3', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Player 3', +// ], +// ], +// ], +// ])); +// +// $supporter3 = static::getDatabase()->getDocument('supporters', 'supporter3'); +// $this->assertEquals('team2', $supporter3['team']['$id']); +// $this->assertArrayNotHasKey('supporters', $supporter3['team']); +// $this->assertEquals(2, \count($supporter3['team']['players'])); +// $this->assertEquals('player2', $supporter3['team']['players'][0]['$id']); +// $this->assertEquals('player3', $supporter3['team']['players'][1]['$id']); +// } +// +// public function testNestedManyToOne_ManyToOne(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('cows'); +// static::getDatabase()->createCollection('farms'); +// static::getDatabase()->createCollection('farmer'); +// +// static::getDatabase()->createAttribute('cows', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('farms', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('farmer', 'name', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'cows', +// relatedCollection: 'farms', +// type: Database::RELATION_MANY_TO_ONE, +// twoWay: true, +// id: 'farm' +// ); +// static::getDatabase()->createRelationship( +// collection: 'farms', +// relatedCollection: 'farmer', +// type: Database::RELATION_MANY_TO_ONE, +// twoWay: true, +// id: 'farmer' +// ); +// +// static::getDatabase()->createDocument('cows', new Document([ +// '$id' => 'cow1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Cow 1', +// 'farm' => [ +// '$id' => 'farm1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Farm 1', +// 'farmer' => [ +// '$id' => 'farmer1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Farmer 1', +// ], +// ], +// ])); +// +// $cow1 = static::getDatabase()->getDocument('cows', 'cow1'); +// $this->assertEquals('farm1', $cow1['farm']['$id']); +// $this->assertArrayNotHasKey('cows', $cow1['farm']); +// $this->assertEquals('farmer1', $cow1['farm']['farmer']['$id']); +// $this->assertArrayNotHasKey('farms', $cow1['farm']['farmer']); +// +// static::getDatabase()->createDocument('farmer', new Document([ +// '$id' => 'farmer2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Farmer 2', +// 'farms' => [ +// [ +// '$id' => 'farm2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Farm 2', +// 'cows' => [ +// [ +// '$id' => 'cow2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Cow 2', +// ], +// [ +// '$id' => 'cow3', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Cow 3', +// ], +// ], +// ], +// ], +// ])); +// +// $farmer2 = static::getDatabase()->getDocument('farmer', 'farmer2'); +// $this->assertEquals('farm2', $farmer2['farms'][0]['$id']); +// $this->assertArrayNotHasKey('farmer', $farmer2['farms'][0]); +// $this->assertEquals(2, \count($farmer2['farms'][0]['cows'])); +// $this->assertEquals('cow2', $farmer2['farms'][0]['cows'][0]['$id']); +// $this->assertEquals('cow3', $farmer2['farms'][0]['cows'][1]['$id']); +// } +// +// public function testNestedManyToOne_ManyToManyRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('books'); +// static::getDatabase()->createCollection('entrants'); +// static::getDatabase()->createCollection('rooms'); +// +// static::getDatabase()->createAttribute('books', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('entrants', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('rooms', 'name', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'books', +// relatedCollection: 'entrants', +// type: Database::RELATION_MANY_TO_ONE, +// twoWay: true, +// id: 'entrant' +// ); +// static::getDatabase()->createRelationship( +// collection: 'entrants', +// relatedCollection: 'rooms', +// type: Database::RELATION_MANY_TO_MANY, +// twoWay: true, +// ); +// +// static::getDatabase()->createDocument('books', new Document([ +// '$id' => 'book1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Book 1', +// 'entrant' => [ +// '$id' => 'entrant1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Entrant 1', +// 'rooms' => [ +// [ +// '$id' => 'class1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Class 1', +// ], +// [ +// '$id' => 'class2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Class 2', +// ], +// ], +// ], +// ])); +// +// $book1 = static::getDatabase()->getDocument('books', 'book1'); +// $this->assertEquals('entrant1', $book1['entrant']['$id']); +// $this->assertArrayNotHasKey('books', $book1['entrant']); +// $this->assertEquals(2, \count($book1['entrant']['rooms'])); +// $this->assertEquals('class1', $book1['entrant']['rooms'][0]['$id']); +// $this->assertEquals('class2', $book1['entrant']['rooms'][1]['$id']); +// } +// +// public function testNestedManyToMany_OneToOneRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('stones'); +// static::getDatabase()->createCollection('hearths'); +// static::getDatabase()->createCollection('plots'); +// +// static::getDatabase()->createAttribute('stones', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('hearths', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('plots', 'name', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'stones', +// relatedCollection: 'hearths', +// type: Database::RELATION_MANY_TO_MANY, +// twoWay: true, +// ); +// static::getDatabase()->createRelationship( +// collection: 'hearths', +// relatedCollection: 'plots', +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// id: 'plot', +// twoWayKey: 'hearth' +// ); +// +// static::getDatabase()->createDocument('stones', new Document([ +// '$id' => 'stone1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Building 1', +// 'hearths' => [ +// [ +// '$id' => 'hearth1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'House 1', +// 'plot' => [ +// '$id' => 'plot1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Address 1', +// ], +// ], +// [ +// '$id' => 'hearth2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'House 2', +// 'plot' => [ +// '$id' => 'plot2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Address 2', +// ], +// ], +// ], +// ])); +// +// $stone1 = static::getDatabase()->getDocument('stones', 'stone1'); +// $this->assertEquals(2, \count($stone1['hearths'])); +// $this->assertEquals('hearth1', $stone1['hearths'][0]['$id']); +// $this->assertEquals('hearth2', $stone1['hearths'][1]['$id']); +// $this->assertArrayNotHasKey('stone', $stone1['hearths'][0]); +// $this->assertEquals('plot1', $stone1['hearths'][0]['plot']['$id']); +// $this->assertEquals('plot2', $stone1['hearths'][1]['plot']['$id']); +// $this->assertArrayNotHasKey('hearth', $stone1['hearths'][0]['plot']); +// +// static::getDatabase()->createDocument('plots', new Document([ +// '$id' => 'plot3', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Address 3', +// 'hearth' => [ +// '$id' => 'hearth3', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Hearth 3', +// 'stones' => [ +// [ +// '$id' => 'stone2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Stone 2', +// ], +// ], +// ], +// ])); +// +// $plot3 = static::getDatabase()->getDocument('plots', 'plot3'); +// $this->assertEquals('hearth3', $plot3['hearth']['$id']); +// $this->assertArrayNotHasKey('plot', $plot3['hearth']); +// $this->assertEquals('stone2', $plot3['hearth']['stones'][0]['$id']); +// $this->assertArrayNotHasKey('hearths', $plot3['hearth']['stones'][0]); +// } +// +// public function testNestedManyToMany_OneToManyRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('groups'); +// static::getDatabase()->createCollection('tounaments'); +// static::getDatabase()->createCollection('prizes'); +// +// static::getDatabase()->createAttribute('groups', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('tounaments', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('prizes', 'name', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'groups', +// relatedCollection: 'tounaments', +// type: Database::RELATION_MANY_TO_MANY, +// twoWay: true, +// ); +// static::getDatabase()->createRelationship( +// collection: 'tounaments', +// relatedCollection: 'prizes', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// id: 'prizes', +// twoWayKey: 'tounament' +// ); +// +// static::getDatabase()->createDocument('groups', new Document([ +// '$id' => 'group1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Group 1', +// 'tounaments' => [ +// [ +// '$id' => 'tounament1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Tounament 1', +// 'prizes' => [ +// [ +// '$id' => 'prize1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Prize 1', +// ], +// [ +// '$id' => 'prize2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Prize 2', +// ], +// ], +// ], +// [ +// '$id' => 'tounament2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Tounament 2', +// 'prizes' => [ +// [ +// '$id' => 'prize3', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Prize 3', +// ], +// [ +// '$id' => 'prize4', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Prize 4', +// ], +// ], +// ], +// ], +// ])); +// +// $group1 = static::getDatabase()->getDocument('groups', 'group1'); +// $this->assertEquals(2, \count($group1['tounaments'])); +// $this->assertEquals('tounament1', $group1['tounaments'][0]['$id']); +// $this->assertEquals('tounament2', $group1['tounaments'][1]['$id']); +// $this->assertArrayNotHasKey('group', $group1['tounaments'][0]); +// $this->assertEquals(2, \count($group1['tounaments'][0]['prizes'])); +// $this->assertEquals('prize1', $group1['tounaments'][0]['prizes'][0]['$id']); +// $this->assertEquals('prize2', $group1['tounaments'][0]['prizes'][1]['$id']); +// $this->assertArrayNotHasKey('tounament', $group1['tounaments'][0]['prizes'][0]); +// } +// +// public function testNestedManyToMany_ManyToOneRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('platforms'); +// static::getDatabase()->createCollection('games'); +// static::getDatabase()->createCollection('publishers'); +// +// static::getDatabase()->createAttribute('platforms', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('games', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('publishers', 'name', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'platforms', +// relatedCollection: 'games', +// type: Database::RELATION_MANY_TO_MANY, +// twoWay: true, +// ); +// static::getDatabase()->createRelationship( +// collection: 'games', +// relatedCollection: 'publishers', +// type: Database::RELATION_MANY_TO_ONE, +// twoWay: true, +// id: 'publisher', +// twoWayKey: 'games' +// ); +// +// static::getDatabase()->createDocument('platforms', new Document([ +// '$id' => 'platform1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Platform 1', +// 'games' => [ +// [ +// '$id' => 'game1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Game 1', +// 'publisher' => [ +// '$id' => 'publisher1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Publisher 1', +// ], +// ], +// [ +// '$id' => 'game2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Game 2', +// 'publisher' => [ +// '$id' => 'publisher2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Publisher 2', +// ], +// ], +// ] +// ])); +// +// $platform1 = static::getDatabase()->getDocument('platforms', 'platform1'); +// $this->assertEquals(2, \count($platform1['games'])); +// $this->assertEquals('game1', $platform1['games'][0]['$id']); +// $this->assertEquals('game2', $platform1['games'][1]['$id']); +// $this->assertArrayNotHasKey('platforms', $platform1['games'][0]); +// $this->assertEquals('publisher1', $platform1['games'][0]['publisher']['$id']); +// $this->assertEquals('publisher2', $platform1['games'][1]['publisher']['$id']); +// $this->assertArrayNotHasKey('games', $platform1['games'][0]['publisher']); +// +// static::getDatabase()->createDocument('publishers', new Document([ +// '$id' => 'publisher3', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Publisher 3', +// 'games' => [ +// [ +// '$id' => 'game3', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Game 3', +// 'platforms' => [ +// [ +// '$id' => 'platform2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Platform 2', +// ] +// ], +// ], +// ], +// ])); +// +// $publisher3 = static::getDatabase()->getDocument('publishers', 'publisher3'); +// $this->assertEquals(1, \count($publisher3['games'])); +// $this->assertEquals('game3', $publisher3['games'][0]['$id']); +// $this->assertArrayNotHasKey('publisher', $publisher3['games'][0]); +// $this->assertEquals('platform2', $publisher3['games'][0]['platforms'][0]['$id']); +// $this->assertArrayNotHasKey('games', $publisher3['games'][0]['platforms'][0]); +// } +// +// public function testNestedManyToMany_ManyToManyRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('sauces'); +// static::getDatabase()->createCollection('pizzas'); +// static::getDatabase()->createCollection('toppings'); +// +// static::getDatabase()->createAttribute('sauces', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('pizzas', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('toppings', 'name', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'sauces', +// relatedCollection: 'pizzas', +// type: Database::RELATION_MANY_TO_MANY, +// twoWay: true, +// ); +// static::getDatabase()->createRelationship( +// collection: 'pizzas', +// relatedCollection: 'toppings', +// type: Database::RELATION_MANY_TO_MANY, +// twoWay: true, +// id: 'toppings', +// twoWayKey: 'pizzas' +// ); +// +// static::getDatabase()->createDocument('sauces', new Document([ +// '$id' => 'sauce1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Sauce 1', +// 'pizzas' => [ +// [ +// '$id' => 'pizza1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Pizza 1', +// 'toppings' => [ +// [ +// '$id' => 'topping1', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Topping 1', +// ], +// [ +// '$id' => 'topping2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Topping 2', +// ], +// ], +// ], +// [ +// '$id' => 'pizza2', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Pizza 2', +// 'toppings' => [ +// [ +// '$id' => 'topping3', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Topping 3', +// ], +// [ +// '$id' => 'topping4', +// '$permissions' => [ +// Permission::read(Role::any()), +// ], +// 'name' => 'Topping 4', +// ], +// ], +// ], +// ] +// ])); +// +// $sauce1 = static::getDatabase()->getDocument('sauces', 'sauce1'); +// $this->assertEquals(2, \count($sauce1['pizzas'])); +// $this->assertEquals('pizza1', $sauce1['pizzas'][0]['$id']); +// $this->assertEquals('pizza2', $sauce1['pizzas'][1]['$id']); +// $this->assertArrayNotHasKey('sauces', $sauce1['pizzas'][0]); +// $this->assertEquals(2, \count($sauce1['pizzas'][0]['toppings'])); +// $this->assertEquals('topping1', $sauce1['pizzas'][0]['toppings'][0]['$id']); +// $this->assertEquals('topping2', $sauce1['pizzas'][0]['toppings'][1]['$id']); +// $this->assertArrayNotHasKey('pizzas', $sauce1['pizzas'][0]['toppings'][0]); +// $this->assertEquals(2, \count($sauce1['pizzas'][1]['toppings'])); +// $this->assertEquals('topping3', $sauce1['pizzas'][1]['toppings'][0]['$id']); +// $this->assertEquals('topping4', $sauce1['pizzas'][1]['toppings'][1]['$id']); +// $this->assertArrayNotHasKey('pizzas', $sauce1['pizzas'][1]['toppings'][0]); +// } +// +// public function testInheritRelationshipPermissions(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('lawns', permissions: [Permission::create(Role::any())], documentSecurity: true); +// static::getDatabase()->createCollection('trees', permissions: [Permission::create(Role::any())], documentSecurity: true); +// static::getDatabase()->createCollection('birds', permissions: [Permission::create(Role::any())], documentSecurity: true); +// +// static::getDatabase()->createAttribute('lawns', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('trees', 'name', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('birds', 'name', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'lawns', +// relatedCollection: 'trees', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// twoWayKey: 'lawn', +// onDelete: Database::RELATION_MUTATE_CASCADE, +// ); +// static::getDatabase()->createRelationship( +// collection: 'trees', +// relatedCollection: 'birds', +// type: Database::RELATION_MANY_TO_MANY, +// twoWay: true, +// onDelete: Database::RELATION_MUTATE_SET_NULL, +// ); +// +// $permissions = [ +// Permission::read(Role::any()), +// Permission::read(Role::user('user1')), +// Permission::update(Role::user('user1')), +// Permission::delete(Role::user('user2')), +// ]; +// +// static::getDatabase()->createDocument('lawns', new Document([ +// '$id' => 'lawn1', +// '$permissions' => $permissions, +// 'name' => 'Lawn 1', +// 'trees' => [ +// [ +// '$id' => 'tree1', +// 'name' => 'Tree 1', +// 'birds' => [ +// [ +// '$id' => 'bird1', +// 'name' => 'Bird 1', +// ], +// [ +// '$id' => 'bird2', +// 'name' => 'Bird 2', +// ], +// ], +// ], +// ], +// ])); +// +// $lawn1 = static::getDatabase()->getDocument('lawns', 'lawn1'); +// $this->assertEquals($permissions, $lawn1->getPermissions()); +// $this->assertEquals($permissions, $lawn1['trees'][0]->getPermissions()); +// $this->assertEquals($permissions, $lawn1['trees'][0]['birds'][0]->getPermissions()); +// $this->assertEquals($permissions, $lawn1['trees'][0]['birds'][1]->getPermissions()); +// +// $tree1 = static::getDatabase()->getDocument('trees', 'tree1'); +// $this->assertEquals($permissions, $tree1->getPermissions()); +// $this->assertEquals($permissions, $tree1['lawn']->getPermissions()); +// $this->assertEquals($permissions, $tree1['birds'][0]->getPermissions()); +// $this->assertEquals($permissions, $tree1['birds'][1]->getPermissions()); +// } +// +// /** +// * @depends testInheritRelationshipPermissions +// */ +// public function testEnforceRelationshipPermissions(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// Authorization::cleanRoles(); +// Authorization::setRole(Role::any()->toString()); +// $lawn1 = static::getDatabase()->getDocument('lawns', 'lawn1'); +// $this->assertEquals('Lawn 1', $lawn1['name']); +// +// // Try update root document +// try { +// static::getDatabase()->updateDocument( +// 'lawns', +// $lawn1->getId(), +// $lawn1->setAttribute('name', 'Lawn 1 Updated') +// ); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertEquals('Missing "update" permission for role "user:user1". Only "["any"]" scopes are allowed and "["user:user1"]" was given.', $e->getMessage()); +// } +// +// // Try delete root document +// try { +// static::getDatabase()->deleteDocument( +// 'lawns', +// $lawn1->getId(), +// ); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertEquals('Missing "delete" permission for role "user:user2". Only "["any"]" scopes are allowed and "["user:user2"]" was given.', $e->getMessage()); +// } +// +// $tree1 = static::getDatabase()->getDocument('trees', 'tree1'); +// +// // Try update nested document +// try { +// static::getDatabase()->updateDocument( +// 'trees', +// $tree1->getId(), +// $tree1->setAttribute('name', 'Tree 1 Updated') +// ); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertEquals('Missing "update" permission for role "user:user1". Only "["any"]" scopes are allowed and "["user:user1"]" was given.', $e->getMessage()); +// } +// +// // Try delete nested document +// try { +// static::getDatabase()->deleteDocument( +// 'trees', +// $tree1->getId(), +// ); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertEquals('Missing "delete" permission for role "user:user2". Only "["any"]" scopes are allowed and "["user:user2"]" was given.', $e->getMessage()); +// } +// +// $bird1 = static::getDatabase()->getDocument('birds', 'bird1'); +// +// // Try update multi-level nested document +// try { +// static::getDatabase()->updateDocument( +// 'birds', +// $bird1->getId(), +// $bird1->setAttribute('name', 'Bird 1 Updated') +// ); +// $this->fail('Failed to throw exception when updating document with missing permissions'); +// } catch (Exception $e) { +// $this->assertEquals('Missing "update" permission for role "user:user1". Only "["any"]" scopes are allowed and "["user:user1"]" was given.', $e->getMessage()); +// } +// +// // Try delete multi-level nested document +// try { +// static::getDatabase()->deleteDocument( +// 'birds', +// $bird1->getId(), +// ); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertEquals('Missing "delete" permission for role "user:user2". Only "["any"]" scopes are allowed and "["user:user2"]" was given.', $e->getMessage()); +// } +// +// Authorization::setRole(Role::user('user1')->toString()); +// +// $bird1 = static::getDatabase()->getDocument('birds', 'bird1'); +// +// // Try update multi-level nested document +// $bird1 = static::getDatabase()->updateDocument( +// 'birds', +// $bird1->getId(), +// $bird1->setAttribute('name', 'Bird 1 Updated') +// ); +// +// $this->assertEquals('Bird 1 Updated', $bird1['name']); +// +// Authorization::setRole(Role::user('user2')->toString()); +// +// // Try delete multi-level nested document +// $deleted = static::getDatabase()->deleteDocument( +// 'birds', +// $bird1->getId(), +// ); +// +// $this->assertEquals(true, $deleted); +// $tree1 = static::getDatabase()->getDocument('trees', 'tree1'); +// $this->assertEquals(1, count($tree1['birds'])); +// +// // Try update nested document +// $tree1 = static::getDatabase()->updateDocument( +// 'trees', +// $tree1->getId(), +// $tree1->setAttribute('name', 'Tree 1 Updated') +// ); +// +// $this->assertEquals('Tree 1 Updated', $tree1['name']); +// +// // Try delete nested document +// $deleted = static::getDatabase()->deleteDocument( +// 'trees', +// $tree1->getId(), +// ); +// +// $this->assertEquals(true, $deleted); +// $lawn1 = static::getDatabase()->getDocument('lawns', 'lawn1'); +// $this->assertEquals(0, count($lawn1['trees'])); +// +// // Create document with no permissions +// static::getDatabase()->createDocument('lawns', new Document([ +// '$id' => 'lawn2', +// 'name' => 'Lawn 2', +// 'trees' => [ +// [ +// '$id' => 'tree2', +// 'name' => 'Tree 2', +// 'birds' => [ +// [ +// '$id' => 'bird3', +// 'name' => 'Bird 3', +// ], +// ], +// ], +// ], +// ])); +// +// $lawn2 = static::getDatabase()->getDocument('lawns', 'lawn2'); +// $this->assertEquals(true, $lawn2->isEmpty()); +// +// $tree2 = static::getDatabase()->getDocument('trees', 'tree2'); +// $this->assertEquals(true, $tree2->isEmpty()); +// +// $bird3 = static::getDatabase()->getDocument('birds', 'bird3'); +// $this->assertEquals(true, $bird3->isEmpty()); +// } +// +// public function testExceedMaxDepthOneToMany(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// $level1Collection = 'level1OneToMany'; +// $level2Collection = 'level2OneToMany'; +// $level3Collection = 'level3OneToMany'; +// $level4Collection = 'level4OneToMany'; +// +// static::getDatabase()->createCollection($level1Collection); +// static::getDatabase()->createCollection($level2Collection); +// static::getDatabase()->createCollection($level3Collection); +// static::getDatabase()->createCollection($level4Collection); +// +// static::getDatabase()->createRelationship( +// collection: $level1Collection, +// relatedCollection: $level2Collection, +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// ); +// static::getDatabase()->createRelationship( +// collection: $level2Collection, +// relatedCollection: $level3Collection, +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// ); +// static::getDatabase()->createRelationship( +// collection: $level3Collection, +// relatedCollection: $level4Collection, +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// ); +// +// // Exceed create depth +// $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ +// '$id' => 'level1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// ], +// $level2Collection => [ +// [ +// '$id' => 'level2', +// $level3Collection => [ +// [ +// '$id' => 'level3', +// $level4Collection => [ +// [ +// '$id' => 'level4', +// ], +// ], +// ], +// ], +// ], +// ], +// ])); +// $this->assertEquals(1, count($level1[$level2Collection])); +// $this->assertEquals('level2', $level1[$level2Collection][0]->getId()); +// $this->assertEquals(1, count($level1[$level2Collection][0][$level3Collection])); +// $this->assertEquals('level3', $level1[$level2Collection][0][$level3Collection][0]->getId()); +// $this->assertArrayNotHasKey('level4', $level1[$level2Collection][0][$level3Collection][0]); +// +// // Make sure level 4 document was not created +// $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); +// $this->assertEquals(0, count($level3[$level4Collection])); +// $level4 = static::getDatabase()->getDocument($level4Collection, 'level4'); +// $this->assertTrue($level4->isEmpty()); +// +// // Exceed fetch depth +// $level1 = static::getDatabase()->getDocument($level1Collection, 'level1'); +// $this->assertEquals(1, count($level1[$level2Collection])); +// $this->assertEquals('level2', $level1[$level2Collection][0]->getId()); +// $this->assertEquals(1, count($level1[$level2Collection][0][$level3Collection])); +// $this->assertEquals('level3', $level1[$level2Collection][0][$level3Collection][0]->getId()); +// $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][0][$level3Collection][0]); +// +// +// // Exceed update depth +// $level1 = static::getDatabase()->updateDocument( +// $level1Collection, +// 'level1', +// $level1 +// ->setAttribute($level2Collection, [new Document([ +// '$id' => 'level2new', +// $level3Collection => [ +// [ +// '$id' => 'level3new', +// $level4Collection => [ +// [ +// '$id' => 'level4new', +// ], +// ], +// ], +// ], +// ])]) +// ); +// $this->assertEquals(1, count($level1[$level2Collection])); +// $this->assertEquals('level2new', $level1[$level2Collection][0]->getId()); +// $this->assertEquals(1, count($level1[$level2Collection][0][$level3Collection])); +// $this->assertEquals('level3new', $level1[$level2Collection][0][$level3Collection][0]->getId()); +// $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][0][$level3Collection][0]); +// +// // Make sure level 4 document was not created +// $level3 = static::getDatabase()->getDocument($level3Collection, 'level3new'); +// $this->assertEquals(0, count($level3[$level4Collection])); +// $level4 = static::getDatabase()->getDocument($level4Collection, 'level4new'); +// $this->assertTrue($level4->isEmpty()); +// } +// +// public function testExceedMaxDepthOneToOne(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// $level1Collection = 'level1OneToOne'; +// $level2Collection = 'level2OneToOne'; +// $level3Collection = 'level3OneToOne'; +// $level4Collection = 'level4OneToOne'; +// +// static::getDatabase()->createCollection($level1Collection); +// static::getDatabase()->createCollection($level2Collection); +// static::getDatabase()->createCollection($level3Collection); +// static::getDatabase()->createCollection($level4Collection); +// +// static::getDatabase()->createRelationship( +// collection: $level1Collection, +// relatedCollection: $level2Collection, +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// ); +// static::getDatabase()->createRelationship( +// collection: $level2Collection, +// relatedCollection: $level3Collection, +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// ); +// static::getDatabase()->createRelationship( +// collection: $level3Collection, +// relatedCollection: $level4Collection, +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// ); +// +// // Exceed create depth +// $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ +// '$id' => 'level1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// ], +// $level2Collection => [ +// '$id' => 'level2', +// $level3Collection => [ +// '$id' => 'level3', +// $level4Collection => [ +// '$id' => 'level4', +// ], +// ], +// ], +// ])); +// $this->assertArrayHasKey($level2Collection, $level1); +// $this->assertEquals('level2', $level1[$level2Collection]->getId()); +// $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); +// $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); +// $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); +// +// // Confirm the 4th level document does not exist +// $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); +// $this->assertNull($level3[$level4Collection]); +// +// // Create level 4 document +// $level3->setAttribute($level4Collection, new Document([ +// '$id' => 'level4', +// ])); +// $level3 = static::getDatabase()->updateDocument($level3Collection, $level3->getId(), $level3); +// $this->assertEquals('level4', $level3[$level4Collection]->getId()); +// +// // Exceed fetch depth +// $level1 = static::getDatabase()->getDocument($level1Collection, 'level1'); +// $this->assertArrayHasKey($level2Collection, $level1); +// $this->assertEquals('level2', $level1[$level2Collection]->getId()); +// $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); +// $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); +// $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); +// } +// +// public function testExceedMaxDepthOneToOneNull(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// $level1Collection = 'level1OneToOneNull'; +// $level2Collection = 'level2OneToOneNull'; +// $level3Collection = 'level3OneToOneNull'; +// $level4Collection = 'level4OneToOneNull'; +// +// static::getDatabase()->createCollection($level1Collection); +// static::getDatabase()->createCollection($level2Collection); +// static::getDatabase()->createCollection($level3Collection); +// static::getDatabase()->createCollection($level4Collection); +// +// static::getDatabase()->createRelationship( +// collection: $level1Collection, +// relatedCollection: $level2Collection, +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// ); +// static::getDatabase()->createRelationship( +// collection: $level2Collection, +// relatedCollection: $level3Collection, +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// ); +// static::getDatabase()->createRelationship( +// collection: $level3Collection, +// relatedCollection: $level4Collection, +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// ); +// +// $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ +// '$id' => 'level1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// ], +// $level2Collection => [ +// '$id' => 'level2', +// $level3Collection => [ +// '$id' => 'level3', +// $level4Collection => [ +// '$id' => 'level4', +// ], +// ], +// ], +// ])); +// $this->assertArrayHasKey($level2Collection, $level1); +// $this->assertEquals('level2', $level1[$level2Collection]->getId()); +// $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); +// $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); +// $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); +// +// // Confirm the 4th level document does not exist +// $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); +// $this->assertNull($level3[$level4Collection]); +// +// // Create level 4 document +// $level3->setAttribute($level4Collection, new Document([ +// '$id' => 'level4', +// ])); +// $level3 = static::getDatabase()->updateDocument($level3Collection, $level3->getId(), $level3); +// $this->assertEquals('level4', $level3[$level4Collection]->getId()); +// $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); +// $this->assertEquals('level4', $level3[$level4Collection]->getId()); +// +// // Exceed fetch depth +// $level1 = static::getDatabase()->getDocument($level1Collection, 'level1'); +// $this->assertArrayHasKey($level2Collection, $level1); +// $this->assertEquals('level2', $level1[$level2Collection]->getId()); +// $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); +// $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); +// $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); +// } +// +// public function testExceedMaxDepthManyToOneParent(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// $level1Collection = 'level1ManyToOneParent'; +// $level2Collection = 'level2ManyToOneParent'; +// $level3Collection = 'level3ManyToOneParent'; +// $level4Collection = 'level4ManyToOneParent'; +// +// static::getDatabase()->createCollection($level1Collection); +// static::getDatabase()->createCollection($level2Collection); +// static::getDatabase()->createCollection($level3Collection); +// static::getDatabase()->createCollection($level4Collection); +// +// static::getDatabase()->createRelationship( +// collection: $level1Collection, +// relatedCollection: $level2Collection, +// type: Database::RELATION_MANY_TO_ONE, +// twoWay: true, +// ); +// static::getDatabase()->createRelationship( +// collection: $level2Collection, +// relatedCollection: $level3Collection, +// type: Database::RELATION_MANY_TO_ONE, +// twoWay: true, +// ); +// static::getDatabase()->createRelationship( +// collection: $level3Collection, +// relatedCollection: $level4Collection, +// type: Database::RELATION_MANY_TO_ONE, +// twoWay: true, +// ); +// +// $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ +// '$id' => 'level1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// ], +// $level2Collection => [ +// '$id' => 'level2', +// $level3Collection => [ +// '$id' => 'level3', +// $level4Collection => [ +// '$id' => 'level4', +// ], +// ], +// ], +// ])); +// $this->assertArrayHasKey($level2Collection, $level1); +// $this->assertEquals('level2', $level1[$level2Collection]->getId()); +// $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); +// $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); +// $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); +// +// // Confirm the 4th level document does not exist +// $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); +// $this->assertNull($level3[$level4Collection]); +// +// // Create level 4 document +// $level3->setAttribute($level4Collection, new Document([ +// '$id' => 'level4', +// ])); +// $level3 = static::getDatabase()->updateDocument($level3Collection, $level3->getId(), $level3); +// $this->assertEquals('level4', $level3[$level4Collection]->getId()); +// $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); +// $this->assertEquals('level4', $level3[$level4Collection]->getId()); +// +// // Exceed fetch depth +// $level1 = static::getDatabase()->getDocument($level1Collection, 'level1'); +// $this->assertArrayHasKey($level2Collection, $level1); +// $this->assertEquals('level2', $level1[$level2Collection]->getId()); +// $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); +// $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); +// $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); +// } +// +// public function testExceedMaxDepthOneToManyChild(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// $level1Collection = 'level1OneToManyChild'; +// $level2Collection = 'level2OneToManyChild'; +// $level3Collection = 'level3OneToManyChild'; +// $level4Collection = 'level4OneToManyChild'; +// +// static::getDatabase()->createCollection($level1Collection); +// static::getDatabase()->createCollection($level2Collection); +// static::getDatabase()->createCollection($level3Collection); +// static::getDatabase()->createCollection($level4Collection); +// +// static::getDatabase()->createRelationship( +// collection: $level1Collection, +// relatedCollection: $level2Collection, +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// ); +// static::getDatabase()->createRelationship( +// collection: $level2Collection, +// relatedCollection: $level3Collection, +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// ); +// static::getDatabase()->createRelationship( +// collection: $level3Collection, +// relatedCollection: $level4Collection, +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// ); +// +// $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ +// '$id' => 'level1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// ], +// $level2Collection => [ +// [ +// '$id' => 'level2', +// $level3Collection => [ +// [ +// '$id' => 'level3', +// $level4Collection => [ +// [ +// '$id' => 'level4', +// ], +// ] +// ], +// ], +// ], +// ], +// ])); +// $this->assertArrayHasKey($level2Collection, $level1); +// $this->assertEquals('level2', $level1[$level2Collection][0]->getId()); +// $this->assertArrayHasKey($level3Collection, $level1[$level2Collection][0]); +// $this->assertEquals('level3', $level1[$level2Collection][0][$level3Collection][0]->getId()); +// $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][0][$level3Collection][0]); +// +// // Confirm the 4th level document does not exist +// $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); +// $this->assertEquals(0, count($level3[$level4Collection])); +// +// // Create level 4 document +// $level3->setAttribute($level4Collection, [new Document([ +// '$id' => 'level4', +// ])]); +// $level3 = static::getDatabase()->updateDocument($level3Collection, $level3->getId(), $level3); +// $this->assertEquals('level4', $level3[$level4Collection][0]->getId()); +// +// // Verify level 4 document is set +// $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); +// $this->assertArrayHasKey($level4Collection, $level3); +// $this->assertEquals('level4', $level3[$level4Collection][0]->getId()); +// +// // Exceed fetch depth +// $level4 = static::getDatabase()->getDocument($level4Collection, 'level4'); +// $this->assertArrayHasKey($level3Collection, $level4); +// $this->assertEquals('level3', $level4[$level3Collection]->getId()); +// $this->assertArrayHasKey($level2Collection, $level4[$level3Collection]); +// $this->assertEquals('level2', $level4[$level3Collection][$level2Collection]->getId()); +// $this->assertArrayNotHasKey($level1Collection, $level4[$level3Collection][$level2Collection]); +// } +// +// public function testCreateRelationshipMissingCollection(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// $this->expectException(Exception::class); +// $this->expectExceptionMessage('Collection not found'); +// +// static::getDatabase()->createRelationship( +// collection: 'missing', +// relatedCollection: 'missing', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// ); +// } +// +// public function testCreateRelationshipMissingRelatedCollection(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('test'); +// +// $this->expectException(Exception::class); +// $this->expectExceptionMessage('Related collection not found'); +// +// static::getDatabase()->createRelationship( +// collection: 'test', +// relatedCollection: 'missing', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// ); +// } +// +// public function testCreateDuplicateRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('test1'); +// static::getDatabase()->createCollection('test2'); +// +// static::getDatabase()->createRelationship( +// collection: 'test1', +// relatedCollection: 'test2', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// ); +// +// $this->expectException(Exception::class); +// $this->expectExceptionMessage('Attribute already exists'); +// +// static::getDatabase()->createRelationship( +// collection: 'test1', +// relatedCollection: 'test2', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// ); +// } +// +// public function testCreateInvalidRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('test3'); +// static::getDatabase()->createCollection('test4'); +// +// $this->expectException(Exception::class); +// $this->expectExceptionMessage('Invalid relationship type'); +// +// static::getDatabase()->createRelationship( +// collection: 'test3', +// relatedCollection: 'test4', +// type: 'invalid', +// twoWay: true, +// ); +// } +// +// public function testDeleteMissingRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// $this->expectException(Exception::class); +// $this->expectExceptionMessage('Attribute not found'); +// +// static::getDatabase()->deleteRelationship('test', 'test2'); +// } +// +// public function testCreateInvalidIntValueRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('invalid1'); +// static::getDatabase()->createCollection('invalid2'); +// +// static::getDatabase()->createRelationship( +// collection: 'invalid1', +// relatedCollection: 'invalid2', +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// ); +// +// $this->expectException(Exception::class); +// $this->expectExceptionMessage('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); +// +// static::getDatabase()->createDocument('invalid1', new Document([ +// '$id' => ID::unique(), +// 'invalid2' => 10, +// ])); +// } +// +// /** +// * @depends testCreateInvalidIntValueRelationship +// */ +// public function testCreateInvalidObjectValueRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// $this->expectException(Exception::class); +// $this->expectExceptionMessage('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); +// +// static::getDatabase()->createDocument('invalid1', new Document([ +// '$id' => ID::unique(), +// 'invalid2' => new \stdClass(), +// ])); +// } +// +// /** +// * @depends testCreateInvalidIntValueRelationship +// */ +// public function testCreateInvalidArrayIntValueRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createRelationship( +// collection: 'invalid1', +// relatedCollection: 'invalid2', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// id: 'invalid3', +// twoWayKey: 'invalid4', +// ); +// +// $this->expectException(Exception::class); +// $this->expectExceptionMessage('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); +// +// static::getDatabase()->createDocument('invalid1', new Document([ +// '$id' => ID::unique(), +// 'invalid3' => [10], +// ])); +// } +// +// public function testCreateEmptyValueRelationship(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('null1'); +// static::getDatabase()->createCollection('null2'); +// +// static::getDatabase()->createRelationship( +// collection: 'null1', +// relatedCollection: 'null2', +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// ); +// static::getDatabase()->createRelationship( +// collection: 'null1', +// relatedCollection: 'null2', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// id: 'null3', +// twoWayKey: 'null4', +// ); +// static::getDatabase()->createRelationship( +// collection: 'null1', +// relatedCollection: 'null2', +// type: Database::RELATION_MANY_TO_ONE, +// twoWay: true, +// id: 'null4', +// twoWayKey: 'null5', +// ); +// static::getDatabase()->createRelationship( +// collection: 'null1', +// relatedCollection: 'null2', +// type: Database::RELATION_MANY_TO_MANY, +// twoWay: true, +// id: 'null6', +// twoWayKey: 'null7', +// ); +// +// $document = static::getDatabase()->createDocument('null1', new Document([ +// '$id' => ID::unique(), +// 'null2' => null, +// ])); +// +// $this->assertEquals(null, $document->getAttribute('null2')); +// +// $document = static::getDatabase()->createDocument('null2', new Document([ +// '$id' => ID::unique(), +// 'null1' => null, +// ])); +// +// $this->assertEquals(null, $document->getAttribute('null1')); +// +// $document = static::getDatabase()->createDocument('null1', new Document([ +// '$id' => ID::unique(), +// 'null3' => null, +// ])); +// +// // One to many will be empty array instead of null +// $this->assertEquals([], $document->getAttribute('null3')); +// +// $document = static::getDatabase()->createDocument('null2', new Document([ +// '$id' => ID::unique(), +// 'null4' => null, +// ])); +// +// $this->assertEquals(null, $document->getAttribute('null4')); +// +// $document = static::getDatabase()->createDocument('null1', new Document([ +// '$id' => ID::unique(), +// 'null4' => null, +// ])); +// +// $this->assertEquals(null, $document->getAttribute('null4')); +// +// $document = static::getDatabase()->createDocument('null2', new Document([ +// '$id' => ID::unique(), +// 'null5' => null, +// ])); +// +// $this->assertEquals([], $document->getAttribute('null5')); +// +// $document = static::getDatabase()->createDocument('null1', new Document([ +// '$id' => ID::unique(), +// 'null6' => null, +// ])); +// +// $this->assertEquals([], $document->getAttribute('null6')); +// +// $document = static::getDatabase()->createDocument('null2', new Document([ +// '$id' => ID::unique(), +// 'null7' => null, +// ])); +// +// $this->assertEquals([], $document->getAttribute('null7')); +// } +// +// public function testDeleteCollectionDeletesRelationships(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('testers'); +// static::getDatabase()->createCollection('devices'); +// +// static::getDatabase()->createRelationship( +// collection: 'testers', +// relatedCollection: 'devices', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// twoWayKey: 'tester' +// ); +// +// $testers = static::getDatabase()->getCollection('testers'); +// $devices = static::getDatabase()->getCollection('devices'); +// +// $this->assertEquals(1, \count($testers->getAttribute('attributes'))); +// $this->assertEquals(1, \count($devices->getAttribute('attributes'))); +// $this->assertEquals(1, \count($devices->getAttribute('indexes'))); +// +// static::getDatabase()->deleteCollection('testers'); +// +// $testers = static::getDatabase()->getCollection('testers'); +// $devices = static::getDatabase()->getCollection('devices'); +// +// $this->assertEquals(true, $testers->isEmpty()); +// $this->assertEquals(0, \count($devices->getAttribute('attributes'))); +// $this->assertEquals(0, \count($devices->getAttribute('indexes'))); +// } +// +// public function testDeleteTwoWayRelationshipFromChild(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('drivers'); +// static::getDatabase()->createCollection('licenses'); +// +// static::getDatabase()->createRelationship( +// collection: 'drivers', +// relatedCollection: 'licenses', +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// id: 'license', +// twoWayKey: 'driver' +// ); +// +// $drivers = static::getDatabase()->getCollection('drivers'); +// $licenses = static::getDatabase()->getCollection('licenses'); +// +// $this->assertEquals(1, \count($drivers->getAttribute('attributes'))); +// $this->assertEquals(1, \count($drivers->getAttribute('indexes'))); +// $this->assertEquals(1, \count($licenses->getAttribute('attributes'))); +// $this->assertEquals(1, \count($licenses->getAttribute('indexes'))); +// +// static::getDatabase()->deleteRelationship('licenses', 'driver'); +// +// $drivers = static::getDatabase()->getCollection('drivers'); +// $licenses = static::getDatabase()->getCollection('licenses'); +// +// $this->assertEquals(0, \count($drivers->getAttribute('attributes'))); +// $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); +// $this->assertEquals(0, \count($licenses->getAttribute('attributes'))); +// $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); +// +// static::getDatabase()->createRelationship( +// collection: 'drivers', +// relatedCollection: 'licenses', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// id: 'licenses', +// twoWayKey: 'driver' +// ); +// +// $drivers = static::getDatabase()->getCollection('drivers'); +// $licenses = static::getDatabase()->getCollection('licenses'); +// +// $this->assertEquals(1, \count($drivers->getAttribute('attributes'))); +// $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); +// $this->assertEquals(1, \count($licenses->getAttribute('attributes'))); +// $this->assertEquals(1, \count($licenses->getAttribute('indexes'))); +// +// static::getDatabase()->deleteRelationship('licenses', 'driver'); +// +// $drivers = static::getDatabase()->getCollection('drivers'); +// $licenses = static::getDatabase()->getCollection('licenses'); +// +// $this->assertEquals(0, \count($drivers->getAttribute('attributes'))); +// $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); +// $this->assertEquals(0, \count($licenses->getAttribute('attributes'))); +// $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); +// +// static::getDatabase()->createRelationship( +// collection: 'licenses', +// relatedCollection: 'drivers', +// type: Database::RELATION_MANY_TO_ONE, +// twoWay: true, +// id: 'driver', +// twoWayKey: 'licenses' +// ); +// +// $drivers = static::getDatabase()->getCollection('drivers'); +// $licenses = static::getDatabase()->getCollection('licenses'); +// +// $this->assertEquals(1, \count($drivers->getAttribute('attributes'))); +// $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); +// $this->assertEquals(1, \count($licenses->getAttribute('attributes'))); +// $this->assertEquals(1, \count($licenses->getAttribute('indexes'))); +// +// static::getDatabase()->deleteRelationship('drivers', 'licenses'); +// +// $drivers = static::getDatabase()->getCollection('drivers'); +// $licenses = static::getDatabase()->getCollection('licenses'); +// +// $this->assertEquals(0, \count($drivers->getAttribute('attributes'))); +// $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); +// $this->assertEquals(0, \count($licenses->getAttribute('attributes'))); +// $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); +// +// static::getDatabase()->createRelationship( +// collection: 'licenses', +// relatedCollection: 'drivers', +// type: Database::RELATION_MANY_TO_MANY, +// twoWay: true, +// id: 'drivers', +// twoWayKey: 'licenses' +// ); +// +// $drivers = static::getDatabase()->getCollection('drivers'); +// $licenses = static::getDatabase()->getCollection('licenses'); +// $junction = static::getDatabase()->getCollection('_' . $licenses->getInternalId() . '_' . $drivers->getInternalId()); +// +// $this->assertEquals(1, \count($drivers->getAttribute('attributes'))); +// $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); +// $this->assertEquals(1, \count($licenses->getAttribute('attributes'))); +// $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); +// $this->assertEquals(2, \count($junction->getAttribute('attributes'))); +// $this->assertEquals(2, \count($junction->getAttribute('indexes'))); +// +// static::getDatabase()->deleteRelationship('drivers', 'licenses'); +// +// $drivers = static::getDatabase()->getCollection('drivers'); +// $licenses = static::getDatabase()->getCollection('licenses'); +// $junction = static::getDatabase()->getCollection('_licenses_drivers'); +// +// $this->assertEquals(0, \count($drivers->getAttribute('attributes'))); +// $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); +// $this->assertEquals(0, \count($licenses->getAttribute('attributes'))); +// $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); +// +// $this->assertEquals(true, $junction->isEmpty()); +// } +// +// public function testUpdateRelationshipToExistingKey(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('ovens'); +// static::getDatabase()->createCollection('cakes'); +// +// static::getDatabase()->createAttribute('ovens', 'maxTemp', Database::VAR_INTEGER, 0, true); +// static::getDatabase()->createAttribute('ovens', 'owner', Database::VAR_STRING, 255, true); +// static::getDatabase()->createAttribute('cakes', 'height', Database::VAR_INTEGER, 0, true); +// static::getDatabase()->createAttribute('cakes', 'colour', Database::VAR_STRING, 255, true); +// +// static::getDatabase()->createRelationship( +// collection: 'ovens', +// relatedCollection: 'cakes', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// id: 'cakes', +// twoWayKey: 'oven' +// ); +// +// try { +// static::getDatabase()->updateRelationship('ovens', 'cakes', newKey: 'owner'); +// $this->fail('Failed to throw exception'); +// } catch (DuplicateException $e) { +// $this->assertEquals('Attribute already exists', $e->getMessage()); +// } +// +// try { +// static::getDatabase()->updateRelationship('ovens', 'cakes', newTwoWayKey: 'height'); +// $this->fail('Failed to throw exception'); +// } catch (DuplicateException $e) { +// $this->assertEquals('Related attribute already exists', $e->getMessage()); +// } +// } +// +// public function testOneToOneRelationshipKeyWithSymbols(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('$symbols_coll.ection1'); +// static::getDatabase()->createCollection('$symbols_coll.ection2'); +// +// static::getDatabase()->createRelationship( +// collection: '$symbols_coll.ection1', +// relatedCollection: '$symbols_coll.ection2', +// type: Database::RELATION_ONE_TO_ONE, +// twoWay: true, +// ); +// +// $doc1 = static::getDatabase()->createDocument('$symbols_coll.ection2', new Document([ +// '$id' => ID::unique(), +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()) +// ] +// ])); +// $doc2 = static::getDatabase()->createDocument('$symbols_coll.ection1', new Document([ +// '$id' => ID::unique(), +// '$symbols_coll.ection2' => $doc1->getId(), +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()) +// ] +// ])); +// +// $doc1 = static::getDatabase()->getDocument('$symbols_coll.ection2', $doc1->getId()); +// $doc2 = static::getDatabase()->getDocument('$symbols_coll.ection1', $doc2->getId()); +// +// $this->assertEquals($doc2->getId(), $doc1->getAttribute('$symbols_coll.ection1')->getId()); +// $this->assertEquals($doc1->getId(), $doc2->getAttribute('$symbols_coll.ection2')->getId()); +// } +// +// public function testOneToManyRelationshipKeyWithSymbols(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('$symbols_coll.ection3'); +// static::getDatabase()->createCollection('$symbols_coll.ection4'); +// +// static::getDatabase()->createRelationship( +// collection: '$symbols_coll.ection3', +// relatedCollection: '$symbols_coll.ection4', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// ); +// +// $doc1 = static::getDatabase()->createDocument('$symbols_coll.ection4', new Document([ +// '$id' => ID::unique(), +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()) +// ] +// ])); +// $doc2 = static::getDatabase()->createDocument('$symbols_coll.ection3', new Document([ +// '$id' => ID::unique(), +// '$symbols_coll.ection4' => [$doc1->getId()], +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()) +// ] +// ])); +// +// $doc1 = static::getDatabase()->getDocument('$symbols_coll.ection4', $doc1->getId()); +// $doc2 = static::getDatabase()->getDocument('$symbols_coll.ection3', $doc2->getId()); +// +// $this->assertEquals($doc2->getId(), $doc1->getAttribute('$symbols_coll.ection3')->getId()); +// $this->assertEquals($doc1->getId(), $doc2->getAttribute('$symbols_coll.ection4')[0]->getId()); +// } +// +// public function testManyToOneRelationshipKeyWithSymbols(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('$symbols_coll.ection5'); +// static::getDatabase()->createCollection('$symbols_coll.ection6'); +// +// static::getDatabase()->createRelationship( +// collection: '$symbols_coll.ection5', +// relatedCollection: '$symbols_coll.ection6', +// type: Database::RELATION_MANY_TO_ONE, +// twoWay: true, +// ); +// +// $doc1 = static::getDatabase()->createDocument('$symbols_coll.ection6', new Document([ +// '$id' => ID::unique(), +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()) +// ] +// ])); +// $doc2 = static::getDatabase()->createDocument('$symbols_coll.ection5', new Document([ +// '$id' => ID::unique(), +// '$symbols_coll.ection6' => $doc1->getId(), +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()) +// ] +// ])); +// +// $doc1 = static::getDatabase()->getDocument('$symbols_coll.ection6', $doc1->getId()); +// $doc2 = static::getDatabase()->getDocument('$symbols_coll.ection5', $doc2->getId()); +// +// $this->assertEquals($doc2->getId(), $doc1->getAttribute('$symbols_coll.ection5')[0]->getId()); +// $this->assertEquals($doc1->getId(), $doc2->getAttribute('$symbols_coll.ection6')->getId()); +// } +// +// public function testManyToManyRelationshipKeyWithSymbols(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('$symbols_coll.ection7'); +// static::getDatabase()->createCollection('$symbols_coll.ection8'); +// +// static::getDatabase()->createRelationship( +// collection: '$symbols_coll.ection7', +// relatedCollection: '$symbols_coll.ection8', +// type: Database::RELATION_MANY_TO_MANY, +// twoWay: true, +// ); +// +// $doc1 = static::getDatabase()->createDocument('$symbols_coll.ection8', new Document([ +// '$id' => ID::unique(), +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()) +// ] +// ])); +// $doc2 = static::getDatabase()->createDocument('$symbols_coll.ection7', new Document([ +// '$id' => ID::unique(), +// '$symbols_coll.ection8' => [$doc1->getId()], +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()) +// ] +// ])); +// +// $doc1 = static::getDatabase()->getDocument('$symbols_coll.ection8', $doc1->getId()); +// $doc2 = static::getDatabase()->getDocument('$symbols_coll.ection7', $doc2->getId()); +// +// $this->assertEquals($doc2->getId(), $doc1->getAttribute('$symbols_coll.ection7')[0]->getId()); +// $this->assertEquals($doc1->getId(), $doc2->getAttribute('$symbols_coll.ection8')[0]->getId()); +// } +// +// public function testCascadeMultiDelete(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// static::getDatabase()->createCollection('cascadeMultiDelete1'); +// static::getDatabase()->createCollection('cascadeMultiDelete2'); +// static::getDatabase()->createCollection('cascadeMultiDelete3'); +// +// static::getDatabase()->createRelationship( +// collection: 'cascadeMultiDelete1', +// relatedCollection: 'cascadeMultiDelete2', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// onDelete: Database::RELATION_MUTATE_CASCADE +// ); +// +// static::getDatabase()->createRelationship( +// collection: 'cascadeMultiDelete2', +// relatedCollection: 'cascadeMultiDelete3', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// onDelete: Database::RELATION_MUTATE_CASCADE +// ); +// +// $root = static::getDatabase()->createDocument('cascadeMultiDelete1', new Document([ +// '$id' => 'cascadeMultiDelete1', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::delete(Role::any()) +// ], +// 'cascadeMultiDelete2' => [ +// [ +// '$id' => 'cascadeMultiDelete2', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::delete(Role::any()) +// ], +// 'cascadeMultiDelete3' => [ +// [ +// '$id' => 'cascadeMultiDelete3', +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::delete(Role::any()) +// ], +// ], +// ], +// ], +// ], +// ])); +// +// $this->assertCount(1, $root->getAttribute('cascadeMultiDelete2')); +// $this->assertCount(1, $root->getAttribute('cascadeMultiDelete2')[0]->getAttribute('cascadeMultiDelete3')); +// +// $this->assertEquals(true, static::getDatabase()->deleteDocument('cascadeMultiDelete1', $root->getId())); +// +// $multi2 = static::getDatabase()->getDocument('cascadeMultiDelete2', 'cascadeMultiDelete2'); +// $this->assertEquals(true, $multi2->isEmpty()); +// +// $multi3 = static::getDatabase()->getDocument('cascadeMultiDelete3', 'cascadeMultiDelete3'); +// $this->assertEquals(true, $multi3->isEmpty()); +// } +// +// public function testCollectionUpdate(): Document +// { +// $collection = static::getDatabase()->createCollection('collectionUpdate', permissions: [ +// Permission::create(Role::users()), +// Permission::read(Role::users()), +// Permission::update(Role::users()), +// Permission::delete(Role::users()) +// ], documentSecurity: false); +// +// $this->assertInstanceOf(Document::class, $collection); +// +// $collection = static::getDatabase()->getCollection('collectionUpdate'); +// +// $this->assertFalse($collection->getAttribute('documentSecurity')); +// $this->assertIsArray($collection->getPermissions()); +// $this->assertCount(4, $collection->getPermissions()); +// +// $collection = static::getDatabase()->updateCollection('collectionUpdate', [], true); +// +// $this->assertTrue($collection->getAttribute('documentSecurity')); +// $this->assertIsArray($collection->getPermissions()); +// $this->assertEmpty($collection->getPermissions()); +// +// $collection = static::getDatabase()->getCollection('collectionUpdate'); +// +// $this->assertTrue($collection->getAttribute('documentSecurity')); +// $this->assertIsArray($collection->getPermissions()); +// $this->assertEmpty($collection->getPermissions()); +// +// return $collection; +// } +// +// /** +// * @depends testCollectionUpdate +// */ +// public function testCollectionUpdatePermissionsThrowException(Document $collection): void +// { +// $this->expectException(InvalidArgumentException::class); +// static::getDatabase()->updateCollection($collection->getId(), permissions: [ +// 'i dont work' +// ], documentSecurity: false); +// } +// +// public function testCollectionPermissions(): Document +// { +// $collection = static::getDatabase()->createCollection('collectionSecurity', permissions: [ +// Permission::create(Role::users()), +// Permission::read(Role::users()), +// Permission::update(Role::users()), +// Permission::delete(Role::users()) +// ], documentSecurity: false); +// +// $this->assertInstanceOf(Document::class, $collection); +// +// $this->assertTrue(static::getDatabase()->createAttribute( +// collection: $collection->getId(), +// id: 'test', +// type: Database::VAR_STRING, +// size: 255, +// required: false +// )); +// +// return $collection; +// } +// +// public function testCollectionPermissionsExceptions(): void +// { +// $this->expectException(InvalidArgumentException::class); +// static::getDatabase()->createCollection('collectionSecurity', permissions: [ +// 'i dont work' +// ]); +// } +// +// /** +// * @depends testCollectionPermissions +// * @return array +// */ +// public function testCollectionPermissionsCreateWorks(Document $collection): array +// { +// Authorization::cleanRoles(); +// Authorization::setRole(Role::users()->toString()); +// +// $document = static::getDatabase()->createDocument($collection->getId(), new Document([ +// '$id' => ID::unique(), +// '$permissions' => [ +// Permission::read(Role::user('random')), +// Permission::update(Role::user('random')), +// Permission::delete(Role::user('random')) +// ], +// 'test' => 'lorem' +// ])); +// $this->assertInstanceOf(Document::class, $document); +// +// return [$collection, $document]; +// } +// +// +// /** +// * @depends testCollectionPermissions +// */ +// public function testCollectionPermissionsCreateThrowsException(Document $collection): void +// { +// Authorization::cleanRoles(); +// Authorization::setRole(Role::any()->toString()); +// $this->expectException(AuthorizationException::class); +// +// static::getDatabase()->createDocument($collection->getId(), new Document([ +// '$id' => ID::unique(), +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()) +// ], +// 'test' => 'lorem ipsum' +// ])); +// } +// +// /** +// * @depends testCollectionPermissionsCreateWorks +// * @param array $data +// * @return array +// */ +// public function testCollectionPermissionsGetWorks(array $data): array +// { +// [$collection, $document] = $data; +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::users()->toString()); +// +// $document = static::getDatabase()->getDocument( +// $collection->getId(), +// $document->getId() +// ); +// $this->assertInstanceOf(Document::class, $document); +// $this->assertFalse($document->isEmpty()); +// +// return $data; +// } +// +// /** +// * @depends testCollectionPermissionsCreateWorks +// * @param array $data +// */ +// public function testCollectionPermissionsGetThrowsException(array $data): void +// { +// [$collection, $document] = $data; +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::any()->toString()); +// +// $document = static::getDatabase()->getDocument( +// $collection->getId(), +// $document->getId(), +// ); +// $this->assertInstanceOf(Document::class, $document); +// $this->assertTrue($document->isEmpty()); +// } +// +// /** +// * @depends testCollectionPermissionsCreateWorks +// * @param array $data +// * @return array +// */ +// public function testCollectionPermissionsFindWorks(array $data): array +// { +// [$collection, $document] = $data; +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::users()->toString()); +// +// $documents = static::getDatabase()->find($collection->getId()); +// $this->assertNotEmpty($documents); +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::user('random')->toString()); +// +// try { +// static::getDatabase()->find($collection->getId()); +// $this->fail('Failed to throw exception'); +// } catch (AuthorizationException) { +// } +// +// return $data; +// } +// +// /** +// * @param array $data +// * @depends testCollectionPermissionsCreateWorks +// */ +// public function testCollectionPermissionsFindThrowsException(array $data): void +// { +// [$collection, $document] = $data; +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::any()->toString()); +// +// $this->expectException(AuthorizationException::class); +// static::getDatabase()->find($collection->getId()); +// } +// +// /** +// * @depends testCollectionPermissionsCreateWorks +// * @param array $data +// * @return array +// */ +// public function testCollectionPermissionsCountWorks(array $data): array +// { +// [$collection, $document] = $data; +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::users()->toString()); +// +// $count = static::getDatabase()->count( +// $collection->getId() +// ); +// +// $this->assertNotEmpty($count); +// +// return $data; +// } +// +// /** +// * @param array $data +// * @depends testCollectionPermissionsCreateWorks +// */ +// public function testCollectionPermissionsCountThrowsException(array $data): void +// { +// [$collection, $document] = $data; +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::any()->toString()); +// +// $count = static::getDatabase()->count( +// $collection->getId() +// ); +// $this->assertEmpty($count); +// } +// +// /** +// * @depends testCollectionPermissionsCreateWorks +// * @param array $data +// * @return array +// */ +// public function testCollectionPermissionsUpdateWorks(array $data): array +// { +// [$collection, $document] = $data; +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::users()->toString()); +// +// $this->assertInstanceOf(Document::class, static::getDatabase()->updateDocument( +// $collection->getId(), +// $document->getId(), +// $document->setAttribute('test', 'ipsum') +// )); +// +// return $data; +// } +// +// /** +// * @param array $data +// * @depends testCollectionPermissionsCreateWorks +// */ +// public function testCollectionPermissionsUpdateThrowsException(array $data): void +// { +// [$collection, $document] = $data; +// Authorization::cleanRoles(); +// Authorization::setRole(Role::any()->toString()); +// +// $this->expectException(AuthorizationException::class); +// $document = static::getDatabase()->updateDocument( +// $collection->getId(), +// $document->getId(), +// $document->setAttribute('test', 'lorem') +// ); +// } +// +// /** +// * @param array $data +// * @depends testCollectionPermissionsUpdateWorks +// */ +// public function testCollectionPermissionsDeleteThrowsException(array $data): void +// { +// [$collection, $document] = $data; +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::any()->toString()); +// +// $this->expectException(AuthorizationException::class); +// static::getDatabase()->deleteDocument( +// $collection->getId(), +// $document->getId() +// ); +// } +// +// /** +// * @param array $data +// * @depends testCollectionPermissionsUpdateWorks +// */ +// public function testCollectionPermissionsDeleteWorks(array $data): void +// { +// [$collection, $document] = $data; +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::users()->toString()); +// +// $this->assertTrue(static::getDatabase()->deleteDocument( +// $collection->getId(), +// $document->getId() +// )); +// } +// +// /** +// * @return array +// */ +// public function testCollectionPermissionsRelationships(): array +// { +// $collection = static::getDatabase()->createCollection('collectionSecurity.Parent', permissions: [ +// Permission::create(Role::users()), +// Permission::read(Role::users()), +// Permission::update(Role::users()), +// Permission::delete(Role::users()) +// ], documentSecurity: true); +// +// $this->assertInstanceOf(Document::class, $collection); +// +// $this->assertTrue(static::getDatabase()->createAttribute( +// collection: $collection->getId(), +// id: 'test', +// type: Database::VAR_STRING, +// size: 255, +// required: false +// )); +// +// $collectionOneToOne = static::getDatabase()->createCollection('collectionSecurity.OneToOne', permissions: [ +// Permission::create(Role::users()), +// Permission::read(Role::users()), +// Permission::update(Role::users()), +// Permission::delete(Role::users()) +// ], documentSecurity: true); +// +// $this->assertInstanceOf(Document::class, $collectionOneToOne); +// +// $this->assertTrue(static::getDatabase()->createAttribute( +// collection: $collectionOneToOne->getId(), +// id: 'test', +// type: Database::VAR_STRING, +// size: 255, +// required: false +// )); +// +// $this->assertTrue(static::getDatabase()->createRelationship( +// collection: $collection->getId(), +// relatedCollection: $collectionOneToOne->getId(), +// type: Database::RELATION_ONE_TO_ONE, +// id: Database::RELATION_ONE_TO_ONE, +// onDelete: Database::RELATION_MUTATE_CASCADE +// )); +// +// $collectionOneToMany = static::getDatabase()->createCollection('collectionSecurity.OneToMany', permissions: [ +// Permission::create(Role::users()), +// Permission::read(Role::users()), +// Permission::update(Role::users()), +// Permission::delete(Role::users()) +// ], documentSecurity: true); +// +// $this->assertInstanceOf(Document::class, $collectionOneToMany); +// +// $this->assertTrue(static::getDatabase()->createAttribute( +// collection: $collectionOneToMany->getId(), +// id: 'test', +// type: Database::VAR_STRING, +// size: 255, +// required: false +// )); +// +// $this->assertTrue(static::getDatabase()->createRelationship( +// collection: $collection->getId(), +// relatedCollection: $collectionOneToMany->getId(), +// type: Database::RELATION_ONE_TO_MANY, +// id: Database::RELATION_ONE_TO_MANY, +// onDelete: Database::RELATION_MUTATE_CASCADE +// )); +// +// return [$collection, $collectionOneToOne, $collectionOneToMany]; +// } +// +// /** +// * @depends testCollectionPermissionsRelationships +// * @param array $data +// * @return array +// */ +// public function testCollectionPermissionsRelationshipsCreateWorks(array $data): array +// { +// [$collection, $collectionOneToOne, $collectionOneToMany] = $data; +// Authorization::cleanRoles(); +// Authorization::setRole(Role::users()->toString()); +// +// $document = static::getDatabase()->createDocument($collection->getId(), new Document([ +// '$id' => ID::unique(), +// '$permissions' => [ +// Permission::read(Role::user('random')), +// Permission::update(Role::user('random')), +// Permission::delete(Role::user('random')) +// ], +// 'test' => 'lorem', +// Database::RELATION_ONE_TO_ONE => [ +// '$id' => ID::unique(), +// '$permissions' => [ +// Permission::read(Role::user('random')), +// Permission::update(Role::user('random')), +// Permission::delete(Role::user('random')) +// ], +// 'test' => 'lorem ipsum' +// ], +// Database::RELATION_ONE_TO_MANY => [ +// [ +// '$id' => ID::unique(), +// '$permissions' => [ +// Permission::read(Role::user('random')), +// Permission::update(Role::user('random')), +// Permission::delete(Role::user('random')) +// ], +// 'test' => 'lorem ipsum' +// ], [ +// '$id' => ID::unique(), +// '$permissions' => [ +// Permission::read(Role::user('torsten')), +// Permission::update(Role::user('random')), +// Permission::delete(Role::user('random')) +// ], +// 'test' => 'dolor' +// ] +// ], +// ])); +// $this->assertInstanceOf(Document::class, $document); +// +// return [...$data, $document]; +// } +// +// /** +// * @depends testCollectionPermissionsRelationships +// * @param array $data +// */ +// public function testCollectionPermissionsRelationshipsCreateThrowsException(array $data): void +// { +// [$collection, $collectionOneToOne, $collectionOneToMany] = $data; +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::any()->toString()); +// $this->expectException(AuthorizationException::class); +// +// static::getDatabase()->createDocument($collection->getId(), new Document([ +// '$id' => ID::unique(), +// '$permissions' => [ +// Permission::read(Role::any()), +// Permission::update(Role::any()) +// ], +// 'test' => 'lorem ipsum' +// ])); +// } +// +// /** +// * @depends testCollectionPermissionsRelationshipsCreateWorks +// * @param array $data +// * @return array +// */ +// public function testCollectionPermissionsRelationshipsGetWorks(array $data): array +// { +// [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::users()->toString()); +// +// $document = static::getDatabase()->getDocument( +// $collection->getId(), +// $document->getId() +// ); +// +// $this->assertInstanceOf(Document::class, $document); +// $this->assertInstanceOf(Document::class, $document->getAttribute(Database::RELATION_ONE_TO_ONE)); +// $this->assertIsArray($document->getAttribute(Database::RELATION_ONE_TO_MANY)); +// $this->assertCount(2, $document->getAttribute(Database::RELATION_ONE_TO_MANY)); +// $this->assertFalse($document->isEmpty()); +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::user('random')->toString()); +// +// $document = static::getDatabase()->getDocument( +// $collection->getId(), +// $document->getId() +// ); +// +// $this->assertInstanceOf(Document::class, $document); +// $this->assertInstanceOf(Document::class, $document->getAttribute(Database::RELATION_ONE_TO_ONE)); +// $this->assertIsArray($document->getAttribute(Database::RELATION_ONE_TO_MANY)); +// $this->assertCount(1, $document->getAttribute(Database::RELATION_ONE_TO_MANY)); +// $this->assertFalse($document->isEmpty()); +// +// return $data; +// } +// +// /** +// * @param array $data +// * @depends testCollectionPermissionsRelationshipsCreateWorks +// */ +// public function testCollectionPermissionsRelationshipsGetThrowsException(array $data): void +// { +// [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::any()->toString()); +// +// $document = static::getDatabase()->getDocument( +// $collection->getId(), +// $document->getId(), +// ); +// $this->assertInstanceOf(Document::class, $document); +// $this->assertTrue($document->isEmpty()); +// } +// +// /** +// * @depends testCollectionPermissionsRelationshipsCreateWorks +// * @param array $data +// */ +// public function testCollectionPermissionsRelationshipsFindWorks(array $data): void +// { +// [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::users()->toString()); +// +// $documents = static::getDatabase()->find( +// $collection->getId() +// ); +// +// $this->assertIsArray($documents); +// $this->assertCount(1, $documents); +// $document = $documents[0]; +// $this->assertInstanceOf(Document::class, $document); +// $this->assertInstanceOf(Document::class, $document->getAttribute(Database::RELATION_ONE_TO_ONE)); +// $this->assertIsArray($document->getAttribute(Database::RELATION_ONE_TO_MANY)); +// $this->assertCount(2, $document->getAttribute(Database::RELATION_ONE_TO_MANY)); +// $this->assertFalse($document->isEmpty()); +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::user('random')->toString()); +// +// $documents = static::getDatabase()->find( +// $collection->getId() +// ); +// +// $this->assertIsArray($documents); +// $this->assertCount(1, $documents); +// $document = $documents[0]; +// $this->assertInstanceOf(Document::class, $document); +// $this->assertInstanceOf(Document::class, $document->getAttribute(Database::RELATION_ONE_TO_ONE)); +// $this->assertIsArray($document->getAttribute(Database::RELATION_ONE_TO_MANY)); +// $this->assertCount(1, $document->getAttribute(Database::RELATION_ONE_TO_MANY)); +// $this->assertFalse($document->isEmpty()); +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::user('unknown')->toString()); +// +// $documents = static::getDatabase()->find( +// $collection->getId() +// ); +// +// $this->assertIsArray($documents); +// $this->assertCount(0, $documents); +// } +// +// /** +// * @depends testCollectionPermissionsRelationshipsCreateWorks +// * @param array $data +// */ +// public function testCollectionPermissionsRelationshipsCountWorks(array $data): void +// { +// [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::users()->toString()); +// +// $documents = static::getDatabase()->count( +// $collection->getId() +// ); +// +// $this->assertEquals(1, $documents); +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::user('random')->toString()); +// +// $documents = static::getDatabase()->count( +// $collection->getId() +// ); +// +// $this->assertEquals(1, $documents); +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::user('unknown')->toString()); +// +// $documents = static::getDatabase()->count( +// $collection->getId() +// ); +// +// $this->assertEquals(0, $documents); +// } +// +// /** +// * @depends testCollectionPermissionsRelationshipsCreateWorks +// * @param array $data +// * @return array +// */ +// public function testCollectionPermissionsRelationshipsUpdateWorks(array $data): array +// { +// [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::users()->toString()); +// static::getDatabase()->updateDocument( +// $collection->getId(), +// $document->getId(), +// $document +// ); +// +// $this->assertTrue(true); +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::user('random')->toString()); +// +// static::getDatabase()->updateDocument( +// $collection->getId(), +// $document->getId(), +// $document->setAttribute('test', 'ipsum') +// ); +// +// $this->assertTrue(true); +// +// return $data; +// } +// +// /** +// * @param array $data +// * @depends testCollectionPermissionsRelationshipsCreateWorks +// */ +// public function testCollectionPermissionsRelationshipsUpdateThrowsException(array $data): void +// { +// [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::any()->toString()); +// +// $this->expectException(AuthorizationException::class); +// $document = static::getDatabase()->updateDocument( +// $collection->getId(), +// $document->getId(), +// $document->setAttribute('test', $document->getAttribute('test').'new_value') +// ); +// } +// +// /** +// * @param array $data +// * @depends testCollectionPermissionsRelationshipsUpdateWorks +// */ +// public function testCollectionPermissionsRelationshipsDeleteThrowsException(array $data): void +// { +// [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::any()->toString()); +// +// $this->expectException(AuthorizationException::class); +// $document = static::getDatabase()->deleteDocument( +// $collection->getId(), +// $document->getId() +// ); +// } +// +// /** +// * @param array $data +// * @depends testCollectionPermissionsRelationshipsUpdateWorks +// */ +// public function testCollectionPermissionsRelationshipsDeleteWorks(array $data): void +// { +// [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::users()->toString()); +// +// $this->assertTrue(static::getDatabase()->deleteDocument( +// $collection->getId(), +// $document->getId() +// )); +// } +// +// public function testCreateRelationDocumentWithoutUpdatePermission(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// +// Authorization::cleanRoles(); +// Authorization::setRole(Role::user('a')->toString()); +// +// static::getDatabase()->createCollection('parentRelationTest', [], [], [ +// Permission::read(Role::user('a')), +// Permission::create(Role::user('a')), +// Permission::update(Role::user('a')), +// Permission::delete(Role::user('a')) +// ]); +// static::getDatabase()->createCollection('childRelationTest', [], [], [ +// Permission::create(Role::user('a')), +// Permission::read(Role::user('a')), +// ]); +// static::getDatabase()->createAttribute('parentRelationTest', 'name', Database::VAR_STRING, 255, false); +// static::getDatabase()->createAttribute('childRelationTest', 'name', Database::VAR_STRING, 255, false); +// +// static::getDatabase()->createRelationship( +// collection: 'parentRelationTest', +// relatedCollection: 'childRelationTest', +// type: Database::RELATION_ONE_TO_MANY, +// id: 'children' +// ); +// +// // Create document with relationship with nested data +// $parent = static::getDatabase()->createDocument('parentRelationTest', new Document([ +// '$id' => 'parent1', +// 'name' => 'Parent 1', +// 'children' => [ +// [ +// '$id' => 'child1', +// 'name' => 'Child 1', +// ], +// ], +// ])); +// $this->assertEquals('child1', $parent->getAttribute('children')[0]->getId()); +// $parent->setAttribute('children', [ +// [ +// '$id' => 'child2', +// ], +// ]); +// $updatedParent = static::getDatabase()->updateDocument('parentRelationTest', 'parent1', $parent); +// +// $this->assertEquals('child2', $updatedParent->getAttribute('children')[0]->getId()); +// +// static::getDatabase()->deleteCollection('parentRelationTest'); +// static::getDatabase()->deleteCollection('childRelationTest'); +// } +// +// public function testUpdateDocumentWithRelationships(): void +// { +// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { +// $this->expectNotToPerformAssertions(); +// return; +// } +// static::getDatabase()->createCollection('userProfiles', [ +// new Document([ +// '$id' => ID::custom('username'), +// 'type' => Database::VAR_STRING, +// 'format' => '', +// 'size' => 700, +// 'signed' => true, +// 'required' => false, +// 'default' => null, +// 'array' => false, +// 'filters' => [], +// ]), +// ], [], [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()) +// ]); +// static::getDatabase()->createCollection('links', [ +// new Document([ +// '$id' => ID::custom('title'), +// 'type' => Database::VAR_STRING, +// 'format' => '', +// 'size' => 700, +// 'signed' => true, +// 'required' => false, +// 'default' => null, +// 'array' => false, +// 'filters' => [], +// ]), +// ], [], [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()) +// ]); +// static::getDatabase()->createCollection('videos', [ +// new Document([ +// '$id' => ID::custom('title'), +// 'type' => Database::VAR_STRING, +// 'format' => '', +// 'size' => 700, +// 'signed' => true, +// 'required' => false, +// 'default' => null, +// 'array' => false, +// 'filters' => [], +// ]), +// ], [], [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()) +// ]); +// static::getDatabase()->createCollection('products', [ +// new Document([ +// '$id' => ID::custom('title'), +// 'type' => Database::VAR_STRING, +// 'format' => '', +// 'size' => 700, +// 'signed' => true, +// 'required' => false, +// 'default' => null, +// 'array' => false, +// 'filters' => [], +// ]), +// ], [], [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()) +// ]); +// static::getDatabase()->createCollection('settings', [ +// new Document([ +// '$id' => ID::custom('metaTitle'), +// 'type' => Database::VAR_STRING, +// 'format' => '', +// 'size' => 700, +// 'signed' => true, +// 'required' => false, +// 'default' => null, +// 'array' => false, +// 'filters' => [], +// ]), +// ], [], [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()) +// ]); +// static::getDatabase()->createCollection('appearance', [ +// new Document([ +// '$id' => ID::custom('metaTitle'), +// 'type' => Database::VAR_STRING, +// 'format' => '', +// 'size' => 700, +// 'signed' => true, +// 'required' => false, +// 'default' => null, +// 'array' => false, +// 'filters' => [], +// ]), +// ], [], [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()) +// ]); +// static::getDatabase()->createCollection('group', [ +// new Document([ +// '$id' => ID::custom('name'), +// 'type' => Database::VAR_STRING, +// 'format' => '', +// 'size' => 700, +// 'signed' => true, +// 'required' => false, +// 'default' => null, +// 'array' => false, +// 'filters' => [], +// ]), +// ], [], [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()) +// ]); +// static::getDatabase()->createCollection('community', [ +// new Document([ +// '$id' => ID::custom('name'), +// 'type' => Database::VAR_STRING, +// 'format' => '', +// 'size' => 700, +// 'signed' => true, +// 'required' => false, +// 'default' => null, +// 'array' => false, +// 'filters' => [], +// ]), +// ], [], [ +// Permission::read(Role::any()), +// Permission::create(Role::any()), +// Permission::update(Role::any()), +// Permission::delete(Role::any()) +// ]); +// +// static::getDatabase()->createRelationship( +// collection: 'userProfiles', +// relatedCollection: 'links', +// type: Database::RELATION_ONE_TO_MANY, +// id: 'links' +// ); +// +// static::getDatabase()->createRelationship( +// collection: 'userProfiles', +// relatedCollection: 'videos', +// type: Database::RELATION_ONE_TO_MANY, +// id: 'videos' +// ); +// +// static::getDatabase()->createRelationship( +// collection: 'userProfiles', +// relatedCollection: 'products', +// type: Database::RELATION_ONE_TO_MANY, +// twoWay: true, +// id: 'products', +// twoWayKey: 'userProfile', +// ); +// +// static::getDatabase()->createRelationship( +// collection: 'userProfiles', +// relatedCollection: 'settings', +// type: Database::RELATION_ONE_TO_ONE, +// id: 'settings' +// ); +// +// static::getDatabase()->createRelationship( +// collection: 'userProfiles', +// relatedCollection: 'appearance', +// type: Database::RELATION_ONE_TO_ONE, +// id: 'appearance' +// ); +// +// static::getDatabase()->createRelationship( +// collection: 'userProfiles', +// relatedCollection: 'group', +// type: Database::RELATION_MANY_TO_ONE, +// id: 'group' +// ); +// +// static::getDatabase()->createRelationship( +// collection: 'userProfiles', +// relatedCollection: 'community', +// type: Database::RELATION_MANY_TO_ONE, +// id: 'community' +// ); +// +// $profile = static::getDatabase()->createDocument('userProfiles', new Document([ +// '$id' => '1', +// 'username' => 'user1', +// 'links' => [ +// [ +// '$id' => 'link1', +// 'title' => 'Link 1', +// ], +// ], +// 'videos' => [ +// [ +// '$id' => 'video1', +// 'title' => 'Video 1', +// ], +// ], +// 'products' => [ +// [ +// '$id' => 'product1', +// 'title' => 'Product 1', +// ], +// ], +// 'settings' => [ +// '$id' => 'settings1', +// 'metaTitle' => 'Meta Title', +// ], +// 'appearance' => [ +// '$id' => 'appearance1', +// 'metaTitle' => 'Meta Title', +// ], +// 'group' => [ +// '$id' => 'group1', +// 'name' => 'Group 1', +// ], +// 'community' => [ +// '$id' => 'community1', +// 'name' => 'Community 1', +// ], +// ])); +// $this->assertEquals('link1', $profile->getAttribute('links')[0]->getId()); +// $this->assertEquals('settings1', $profile->getAttribute('settings')->getId()); +// $this->assertEquals('group1', $profile->getAttribute('group')->getId()); +// $this->assertEquals('community1', $profile->getAttribute('community')->getId()); +// $this->assertEquals('video1', $profile->getAttribute('videos')[0]->getId()); +// $this->assertEquals('product1', $profile->getAttribute('products')[0]->getId()); +// $this->assertEquals('appearance1', $profile->getAttribute('appearance')->getId()); +// +// $profile->setAttribute('links', [ +// [ +// '$id' => 'link1', +// 'title' => 'New Link Value', +// ], +// ]); +// +// $profile->setAttribute('settings', [ +// '$id' => 'settings1', +// 'metaTitle' => 'New Meta Title', +// ]); +// +// $profile->setAttribute('group', [ +// '$id' => 'group1', +// 'name' => 'New Group Name', +// ]); +// +// $updatedProfile = static::getDatabase()->updateDocument('userProfiles', '1', $profile); +// +// $this->assertEquals('New Link Value', $updatedProfile->getAttribute('links')[0]->getAttribute('title')); +// $this->assertEquals('New Meta Title', $updatedProfile->getAttribute('settings')->getAttribute('metaTitle')); +// $this->assertEquals('New Group Name', $updatedProfile->getAttribute('group')->getAttribute('name')); +// +// // This is the point of test, related documents should be present if they are not updated +// $this->assertEquals('Video 1', $updatedProfile->getAttribute('videos')[0]->getAttribute('title')); +// $this->assertEquals('Product 1', $updatedProfile->getAttribute('products')[0]->getAttribute('title')); +// $this->assertEquals('Meta Title', $updatedProfile->getAttribute('appearance')->getAttribute('metaTitle')); +// $this->assertEquals('Community 1', $updatedProfile->getAttribute('community')->getAttribute('name')); +// +// // updating document using two way key in one to many relationship +// $product = static::getDatabase()->getDocument('products', 'product1'); +// $product->setAttribute('userProfile', [ +// '$id' => '1', +// 'username' => 'updated user value', +// ]); +// $updatedProduct = static::getDatabase()->updateDocument('products', 'product1', $product); +// $this->assertEquals('updated user value', $updatedProduct->getAttribute('userProfile')->getAttribute('username')); +// $this->assertEquals('Product 1', $updatedProduct->getAttribute('title')); +// $this->assertEquals('product1', $updatedProduct->getId()); +// $this->assertEquals('1', $updatedProduct->getAttribute('userProfile')->getId()); +// +// static::getDatabase()->deleteCollection('userProfiles'); +// static::getDatabase()->deleteCollection('links'); +// static::getDatabase()->deleteCollection('settings'); +// static::getDatabase()->deleteCollection('group'); +// static::getDatabase()->deleteCollection('community'); +// static::getDatabase()->deleteCollection('videos'); +// static::getDatabase()->deleteCollection('products'); +// static::getDatabase()->deleteCollection('appearance'); +// } +// +// public function testLabels(): void +// { +// $this->assertInstanceOf('Utopia\Database\Document', static::getDatabase()->createCollection( +// 'labels_test', +// )); +// static::getDatabase()->createAttribute('labels_test', 'attr1', Database::VAR_STRING, 10, false); +// +// static::getDatabase()->createDocument('labels_test', new Document([ +// '$id' => 'doc1', +// 'attr1' => 'value1', +// '$permissions' => [ +// Permission::read(Role::label('reader')), +// ], +// ])); +// +// $documents = static::getDatabase()->find('labels_test'); +// +// $this->assertEmpty($documents); +// +// Authorization::setRole(Role::label('reader')->toString()); +// +// $documents = static::getDatabase()->find('labels_test'); +// +// $this->assertCount(1, $documents); +// } +// +// public function testEmptyOperatorValues(): void +// { +// try { +// static::getDatabase()->findOne('documents', [ +// Query::equal('string', []), +// ]); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertInstanceOf(Exception::class, $e); +// $this->assertEquals('Invalid query: Equal queries require at least one value.', $e->getMessage()); +// } +// +// try { +// static::getDatabase()->findOne('documents', [ +// Query::search('string', null), +// ]); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertInstanceOf(Exception::class, $e); +// $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); +// } +// +// try { +// static::getDatabase()->findOne('documents', [ +// Query::notEqual('string', []), +// ]); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertInstanceOf(Exception::class, $e); +// $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); +// } +// +// try { +// static::getDatabase()->findOne('documents', [ +// Query::lessThan('string', []), +// ]); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertInstanceOf(Exception::class, $e); +// $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); +// } +// +// try { +// static::getDatabase()->findOne('documents', [ +// Query::lessThanEqual('string', []), +// ]); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertInstanceOf(Exception::class, $e); +// $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); +// } +// +// try { +// static::getDatabase()->findOne('documents', [ +// Query::greaterThan('string', []), +// ]); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertInstanceOf(Exception::class, $e); +// $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); +// } +// +// try { +// static::getDatabase()->findOne('documents', [ +// Query::greaterThanEqual('string', []), +// ]); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertInstanceOf(Exception::class, $e); +// $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); +// } +// +// try { +// static::getDatabase()->findOne('documents', [ +// Query::contains('string', []), +// ]); +// $this->fail('Failed to throw exception'); +// } catch (Exception $e) { +// $this->assertInstanceOf(Exception::class, $e); +// $this->assertEquals('Invalid query: Contains queries require at least one value.', $e->getMessage()); +// } +// } +// +// public function testEvents(): void +// { +// Authorization::skip(function () { +// $database = static::getDatabase(); +// +// $events = [ +// Database::EVENT_DATABASE_CREATE, +// Database::EVENT_DATABASE_LIST, +// Database::EVENT_COLLECTION_CREATE, +// Database::EVENT_COLLECTION_LIST, +// Database::EVENT_COLLECTION_READ, +// Database::EVENT_ATTRIBUTE_CREATE, +// Database::EVENT_ATTRIBUTE_UPDATE, +// Database::EVENT_INDEX_CREATE, +// Database::EVENT_DOCUMENT_CREATE, +// Database::EVENT_DOCUMENT_UPDATE, +// Database::EVENT_DOCUMENT_READ, +// Database::EVENT_DOCUMENT_FIND, +// Database::EVENT_DOCUMENT_FIND, +// Database::EVENT_DOCUMENT_COUNT, +// Database::EVENT_DOCUMENT_SUM, +// Database::EVENT_DOCUMENT_INCREASE, +// Database::EVENT_DOCUMENT_DECREASE, +// Database::EVENT_INDEX_DELETE, +// Database::EVENT_DOCUMENT_DELETE, +// Database::EVENT_ATTRIBUTE_DELETE, +// Database::EVENT_COLLECTION_DELETE, +// Database::EVENT_DATABASE_DELETE, +// ]; +// +// $database->on(Database::EVENT_ALL, 'test', function ($event, $data) use (&$events) { +// $shifted = array_shift($events); +// +// $this->assertEquals($shifted, $event); +// }); +// +// if ($this->getDatabase()->getAdapter()->getSupportForSchemas()) { +// $database->setDefaultDatabase('hellodb'); +// $database->create(); +// } else { +// array_shift($events); +// } +// +// $database->list(); +// +// $database->setDefaultDatabase($this->testDatabase); +// +// $collectionId = ID::unique(); +// $database->createCollection($collectionId); +// $database->listCollections(); +// $database->getCollection($collectionId); +// $database->createAttribute($collectionId, 'attr1', Database::VAR_INTEGER, 2, false); +// $database->updateAttributeRequired($collectionId, 'attr1', true); +// $indexId1 = 'index2_' . uniqid(); +// $database->createIndex($collectionId, $indexId1, Database::INDEX_KEY, ['attr1']); +// +// $document = $database->createDocument($collectionId, new Document([ +// '$id' => 'doc1', +// 'attr1' => 10, +// '$permissions' => [ +// Permission::delete(Role::any()), +// Permission::update(Role::any()), +// Permission::read(Role::any()), +// ], +// ])); +// +// $executed = false; +// $database->on(Database::EVENT_ALL, 'should-not-execute', function ($event, $data) use (&$executed) { +// $executed = true; +// }); +// +// $database->silent(function () use ($database, $collectionId, $document) { +// $database->updateDocument($collectionId, 'doc1', $document->setAttribute('attr1', 15)); +// $database->getDocument($collectionId, 'doc1'); +// $database->find($collectionId); +// $database->findOne($collectionId); +// $database->count($collectionId); +// $database->sum($collectionId, 'attr1'); +// $database->increaseDocumentAttribute($collectionId, $document->getId(), 'attr1'); +// $database->decreaseDocumentAttribute($collectionId, $document->getId(), 'attr1'); +// }, ['should-not-execute']); +// +// $this->assertFalse($executed); +// +// $database->deleteIndex($collectionId, $indexId1); +// $database->deleteDocument($collectionId, 'doc1'); +// $database->deleteAttribute($collectionId, 'attr1'); +// $database->deleteCollection($collectionId); +// $database->delete('hellodb'); +// }); +// } } From fe25d3c9fb26b6bf93c126208e82943edcbd0e5a Mon Sep 17 00:00:00 2001 From: fogelito Date: Tue, 17 Oct 2023 12:26:54 +0300 Subject: [PATCH 6/9] Query OR --- src/Database/Adapter/MariaDB.php | 24 +- src/Database/Adapter/SQL.php | 36 +- src/Database/Validator/Query/Filter.php | 2 + tests/Database/Adapter/MariaDBTest.php | 120 +- tests/Database/Adapter/MySQLTest.php | 142 +- tests/Database/Base.php | 19585 +++++++++++----------- 6 files changed, 9971 insertions(+), 9938 deletions(-) diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index fe73f0c9d..b2aeb26d0 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -1032,13 +1032,17 @@ public function find(string $collection, array $queries = [], ?int $limit = 25, } } - foreach ($queries as $query) { - if ($query->getMethod() === Query::TYPE_SELECT) { - continue; - } - $where[] = $this->getSQLCondition($query); - } - +// $filters = $this->getSQLConditions($queries); +// if(!empty($filters)){ +// $where[] = $filters; +// } + +// foreach ($queries as $query) { +// if ($query->getMethod() === Query::TYPE_SELECT) { +// continue; +// } +// $where[] = $this->getSQLCondition($query); +// } if (Authorization::$status) { $where[] = $this->getSQLPermissionsCondition($name, $roles); @@ -1059,11 +1063,13 @@ public function find(string $collection, array $queries = [], ?int $limit = 25, {$sqlLimit}; "; + var_dump($sql); + + if ($timeout || static::$timeout) { $sql = $this->setTimeoutForQuery($sql, $timeout ? $timeout : static::$timeout); } - var_dump($sql); $stmt = $this->getPDO()->prepare($sql); foreach ($queries as $query) { $this->bindConditionValue($stmt, $query); @@ -1331,7 +1337,7 @@ protected function getSQLCondition(Query $query): string $conditions[] = $this->getSQLCondition($q); } - return ' OR ' . implode(' ', $conditions); + return empty($condition) ? '' : ' OR (' . implode(' AND ', $conditions) . ')'; case Query::TYPE_SEARCH: return "MATCH(table_main.{$attribute}) AGAINST (:{$placeholder}_0 IN BOOLEAN MODE)"; diff --git a/src/Database/Adapter/SQL.php b/src/Database/Adapter/SQL.php index f3c430ed8..b60ea61ae 100644 --- a/src/Database/Adapter/SQL.php +++ b/src/Database/Adapter/SQL.php @@ -706,16 +706,12 @@ protected function bindConditionValue(mixed $stmt, Query $query): void } if($query->getMethod() === Query::TYPE_OR){ - var_dump($query); + foreach ($query->getValue() as $value) { + $this->bindConditionValue($stmt, $value); + } + return; } - -// var_dump($query); -// if(is_array($query->getValue())){ -// $this->bindConditionValue($stmt, $query); -// return; -// } - foreach ($query->getValues() as $key => $value) { $value = match ($query->getMethod()) { Query::TYPE_STARTS_WITH => $this->escapeWildcards($value) . '%', @@ -963,29 +959,35 @@ public function bindNestedConditionValue($stmt, array $queries = []){ } }} - /** - * @throws Exception - */ + public function getSQLConditions(array $queries = []): string { - $separator = 'and'; + $separator = 'AND'; $conditions = []; foreach ($queries as $query) { + + if ($query->getMethod() === Query::TYPE_SELECT) { + continue; + } + /* @var $query Query */ if($query->getMethod() === Query::TYPE_OR){ $separator = 'or'; - foreach ($query->getValues() as $queriesArray) { - $conditions[] = $this->getSQLConditions($queriesArray); - } + $conditions[] = $this->getSQLConditions($query->getValue()); + +// foreach ($query->getValue() as $queriesArray) { +// var_dump($queriesArray); +// +// $conditions[] = $this->getSQLConditions($queriesArray); +// } } else $conditions[] = $this->getSQLCondition($query); } - $tmp = implode(" {$separator} ", $conditions); + $tmp = implode(' ' . $separator . ' ', $conditions); return empty($tmp) ? '' : '(' . $tmp . ')'; } - /** * @param $stmt * @param Query[] $queries diff --git a/src/Database/Validator/Query/Filter.php b/src/Database/Validator/Query/Filter.php index ef4e75814..5ba16e715 100644 --- a/src/Database/Validator/Query/Filter.php +++ b/src/Database/Validator/Query/Filter.php @@ -173,6 +173,8 @@ public function isValid($value): bool return $this->isValidAttributeAndValues($attribute, $value->getValues()); case Query::TYPE_OR: + var_dump($value); + // if (count($value->getValues()) != 2) { // $this->message = \ucfirst($method) . ' queries require exactly two values.'; // return false; diff --git a/tests/Database/Adapter/MariaDBTest.php b/tests/Database/Adapter/MariaDBTest.php index af616f260..8831aef4c 100644 --- a/tests/Database/Adapter/MariaDBTest.php +++ b/tests/Database/Adapter/MariaDBTest.php @@ -1,61 +1,61 @@ connect('redis', 6379); - $redis->flushAll(); - $cache = new Cache(new RedisAdapter($redis)); - - $database = new Database(new MariaDB($pdo), $cache); - $database->setDefaultDatabase('utopiaTests'); - $database->setNamespace('myapp_'.uniqid()); - - if ($database->exists('utopiaTests')) { - $database->delete('utopiaTests'); - } - - $database->create(); - - return self::$database = $database; - } -} +// +//namespace Utopia\Tests\Adapter; +// +//use PDO; +//use Redis; +//use Utopia\Database\Database; +//use Utopia\Database\Adapter\MariaDB; +//use Utopia\Cache\Cache; +//use Utopia\Cache\Adapter\Redis as RedisAdapter; +//use Utopia\Tests\Base; +// +//class MariaDBTest extends Base +//{ +// public static ?Database $database = null; +// +// // TODO@kodumbeats hacky way to identify adapters for tests +// // Remove once all methods are implemented +// /** +// * Return name of adapter +// * +// * @return string +// */ +// public static function getAdapterName(): string +// { +// return "mariadb"; +// } +// +// /** +// * @return Database +// */ +// public static function getDatabase(): Database +// { +// if (!is_null(self::$database)) { +// return self::$database; +// } +// +// $dbHost = 'mariadb'; +// $dbPort = '3306'; +// $dbUser = 'root'; +// $dbPass = 'password'; +// +// $pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, MariaDB::getPDOAttributes()); +// $redis = new Redis(); +// $redis->connect('redis', 6379); +// $redis->flushAll(); +// $cache = new Cache(new RedisAdapter($redis)); +// +// $database = new Database(new MariaDB($pdo), $cache); +// $database->setDefaultDatabase('utopiaTests'); +// $database->setNamespace('myapp_'.uniqid()); +// +// if ($database->exists('utopiaTests')) { +// $database->delete('utopiaTests'); +// } +// +// $database->create(); +// +// return self::$database = $database; +// } +//} diff --git a/tests/Database/Adapter/MySQLTest.php b/tests/Database/Adapter/MySQLTest.php index e1a181792..0faefca61 100644 --- a/tests/Database/Adapter/MySQLTest.php +++ b/tests/Database/Adapter/MySQLTest.php @@ -1,72 +1,72 @@ connect('redis', 6379); -// $redis->flushAll(); -// -// $cache = new Cache(new RedisAdapter($redis)); -// -// $database = new Database(new MySQL($pdo), $cache); -// $database->setDefaultDatabase('utopiaTests'); -// $database->setNamespace('myapp_'.uniqid()); -// -// if ($database->exists('utopiaTests')) { -// $database->delete('utopiaTests'); -// } -// -// $database->create(); -// -// return self::$database = $database; -// } -//} + +namespace Utopia\Tests\Adapter; + +use PDO; +use Redis; +use Utopia\Cache\Cache; +use Utopia\Database\Database; +use Utopia\Database\Adapter\MySQL; +use Utopia\Cache\Adapter\Redis as RedisAdapter; +use Utopia\Tests\Base; + +class MySQLTest extends Base +{ + public static ?Database $database = null; + + // TODO@kodumbeats hacky way to identify adapters for tests + // Remove once all methods are implemented + /** + * Return name of adapter + * + * @return string + */ + public static function getAdapterName(): string + { + return "mysql"; + } + + /** + * + * @return int + */ + public static function getUsedIndexes(): int + { + return MySQL::getCountOfDefaultIndexes(); + } + + /** + * @return Database + */ + public static function getDatabase(): Database + { + if (!is_null(self::$database)) { + return self::$database; + } + + $dbHost = 'mysql'; + $dbPort = '3307'; + $dbUser = 'root'; + $dbPass = 'password'; + + $pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, MySQL::getPDOAttributes()); + + $redis = new Redis(); + $redis->connect('redis', 6379); + $redis->flushAll(); + + $cache = new Cache(new RedisAdapter($redis)); + + $database = new Database(new MySQL($pdo), $cache); + $database->setDefaultDatabase('utopiaTests'); + $database->setNamespace('myapp_'.uniqid()); + + if ($database->exists('utopiaTests')) { + $database->delete('utopiaTests'); + } + + $database->create(); + + return self::$database = $database; + } +} diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 30c92b2d1..9f18c6357 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -2746,9795 +2746,9818 @@ public function testFindEdgeCases(Document $document): void } } - public function testFindOr(){ + public function testOr(){ $documents = static::getDatabase()->find('movies', [ - Query::equal('active', [true]), + Query::equal('director', ['TBD']), Query::or([ - Query::equal('name', ['shmuel']), - ]), - Query::or( + Query::equal('director', ['Joe Johnston']) + ]) + ]); + + $this->assertEquals(3, count($documents)); + } + + public function testOrFirstQuery(){ + try { + static::getDatabase()->find('movies', [ + Query::or([ + Query::equal('director', ['Joe Johnston']) + ]) + ]); + $this->fail('Failed to throw exception'); + } catch(Exception $e) { + // $this->assertEquals('Please throw exception', $e->getMessage()); + } + } + + public function testOrMultipleQueries(){ + $documents = static::getDatabase()->find('movies', [ + Query::equal('director', ['Joe Johnston']), + Query::or([ + Query::equal('director', ['TBD']), + Query::equal('year', [2026]) + ]) + ]); + + $this->assertEquals(2, count($documents)); + } + + public function testOrNested(){ + $documents = static::getDatabase()->find('movies', [ + Query::select(['director']), + Query::equal('director', ['Joe Johnston']), + Query::or([ + Query::equal('name', ['Frozen']), + Query::or([ + Query::equal('name', ['Frozen II']), + ]) + ]) + ]); + + var_dump($documents); + $this->assertEquals(3, count($documents)); + $this->assertArrayNotHasKey('name', $documents[0]); + die; + } + + /** + * @depends testFind + */ + public function testFindOne(): void + { + $document = static::getDatabase()->findOne('movies', [ + Query::offset(2), + Query::orderAsc('name') + ]); + + $this->assertTrue($document instanceof Document); + $this->assertEquals('Frozen', $document->getAttribute('name')); + + $document = static::getDatabase()->findOne('movies', [ + Query::offset(10) + ]); + $this->assertEquals(false, $document); + } + + public function testFindNull(): void + { + $documents = static::getDatabase()->find('movies', [ + Query::isNull('nullable'), + ]); + + $this->assertEquals(5, count($documents)); + } + + public function testFindNotNull(): void + { + $documents = static::getDatabase()->find('movies', [ + Query::isNotNull('nullable'), + ]); + + $this->assertEquals(1, count($documents)); + } + + public function testFindStartsWith(): void + { + $documents = static::getDatabase()->find('movies', [ + Query::startsWith('name', 'Work'), + ]); + + $this->assertEquals(2, count($documents)); + + if ($this->getDatabase()->getAdapter() instanceof SQL) { + $documents = static::getDatabase()->find('movies', [ + Query::startsWith('name', '%ork'), + ]); + } else { + $documents = static::getDatabase()->find('movies', [ + Query::startsWith('name', '.*ork'), + ]); + } + + $this->assertEquals(0, count($documents)); + } + + public function testFindStartsWithWords(): void + { + $documents = static::getDatabase()->find('movies', [ + Query::startsWith('name', 'Work in Progress'), + ]); + + $this->assertEquals(2, count($documents)); + } + + public function testFindEndsWith(): void + { + $documents = static::getDatabase()->find('movies', [ + Query::endsWith('name', 'Marvel'), + ]); + + $this->assertEquals(1, count($documents)); + } + + public function testFindSelect(): void + { + $documents = static::getDatabase()->find('movies', [ + Query::select(['name', 'year']) + ]); + + foreach ($documents as $document) { + $this->assertArrayHasKey('name', $document); + $this->assertArrayHasKey('year', $document); + $this->assertArrayNotHasKey('director', $document); + $this->assertArrayNotHasKey('price', $document); + $this->assertArrayNotHasKey('active', $document); + $this->assertArrayNotHasKey('$id', $document); + $this->assertArrayNotHasKey('$internalId', $document); + $this->assertArrayNotHasKey('$collection', $document); + $this->assertArrayNotHasKey('$createdAt', $document); + $this->assertArrayNotHasKey('$updatedAt', $document); + $this->assertArrayNotHasKey('$permissions', $document); + } + + $documents = static::getDatabase()->find('movies', [ + Query::select(['name', 'year', '$id']) + ]); + + foreach ($documents as $document) { + $this->assertArrayHasKey('name', $document); + $this->assertArrayHasKey('year', $document); + $this->assertArrayNotHasKey('director', $document); + $this->assertArrayNotHasKey('price', $document); + $this->assertArrayNotHasKey('active', $document); + $this->assertArrayHasKey('$id', $document); + $this->assertArrayNotHasKey('$internalId', $document); + $this->assertArrayNotHasKey('$collection', $document); + $this->assertArrayNotHasKey('$createdAt', $document); + $this->assertArrayNotHasKey('$updatedAt', $document); + $this->assertArrayNotHasKey('$permissions', $document); + } + + $documents = static::getDatabase()->find('movies', [ + Query::select(['name', 'year', '$internalId']) + ]); + + foreach ($documents as $document) { + $this->assertArrayHasKey('name', $document); + $this->assertArrayHasKey('year', $document); + $this->assertArrayNotHasKey('director', $document); + $this->assertArrayNotHasKey('price', $document); + $this->assertArrayNotHasKey('active', $document); + $this->assertArrayNotHasKey('$id', $document); + $this->assertArrayHasKey('$internalId', $document); + $this->assertArrayNotHasKey('$collection', $document); + $this->assertArrayNotHasKey('$createdAt', $document); + $this->assertArrayNotHasKey('$updatedAt', $document); + $this->assertArrayNotHasKey('$permissions', $document); + } + + $documents = static::getDatabase()->find('movies', [ + Query::select(['name', 'year', '$collection']) + ]); + + foreach ($documents as $document) { + $this->assertArrayHasKey('name', $document); + $this->assertArrayHasKey('year', $document); + $this->assertArrayNotHasKey('director', $document); + $this->assertArrayNotHasKey('price', $document); + $this->assertArrayNotHasKey('active', $document); + $this->assertArrayNotHasKey('$id', $document); + $this->assertArrayNotHasKey('$internalId', $document); + $this->assertArrayHasKey('$collection', $document); + $this->assertArrayNotHasKey('$createdAt', $document); + $this->assertArrayNotHasKey('$updatedAt', $document); + $this->assertArrayNotHasKey('$permissions', $document); + } + + $documents = static::getDatabase()->find('movies', [ + Query::select(['name', 'year', '$createdAt']) + ]); + + foreach ($documents as $document) { + $this->assertArrayHasKey('name', $document); + $this->assertArrayHasKey('year', $document); + $this->assertArrayNotHasKey('director', $document); + $this->assertArrayNotHasKey('price', $document); + $this->assertArrayNotHasKey('active', $document); + $this->assertArrayNotHasKey('$id', $document); + $this->assertArrayNotHasKey('$internalId', $document); + $this->assertArrayNotHasKey('$collection', $document); + $this->assertArrayHasKey('$createdAt', $document); + $this->assertArrayNotHasKey('$updatedAt', $document); + $this->assertArrayNotHasKey('$permissions', $document); + } + + $documents = static::getDatabase()->find('movies', [ + Query::select(['name', 'year', '$updatedAt']) + ]); + + foreach ($documents as $document) { + $this->assertArrayHasKey('name', $document); + $this->assertArrayHasKey('year', $document); + $this->assertArrayNotHasKey('director', $document); + $this->assertArrayNotHasKey('price', $document); + $this->assertArrayNotHasKey('active', $document); + $this->assertArrayNotHasKey('$id', $document); + $this->assertArrayNotHasKey('$internalId', $document); + $this->assertArrayNotHasKey('$collection', $document); + $this->assertArrayNotHasKey('$createdAt', $document); + $this->assertArrayHasKey('$updatedAt', $document); + $this->assertArrayNotHasKey('$permissions', $document); + } + + $documents = static::getDatabase()->find('movies', [ + Query::select(['name', 'year', '$permissions']) + ]); + + foreach ($documents as $document) { + $this->assertArrayHasKey('name', $document); + $this->assertArrayHasKey('year', $document); + $this->assertArrayNotHasKey('director', $document); + $this->assertArrayNotHasKey('price', $document); + $this->assertArrayNotHasKey('active', $document); + $this->assertArrayNotHasKey('$id', $document); + $this->assertArrayNotHasKey('$internalId', $document); + $this->assertArrayNotHasKey('$collection', $document); + $this->assertArrayNotHasKey('$createdAt', $document); + $this->assertArrayNotHasKey('$updatedAt', $document); + $this->assertArrayHasKey('$permissions', $document); + } + } + + /** + * @depends testFind + */ + public function testCount(): void + { + $count = static::getDatabase()->count('movies'); + $this->assertEquals(6, $count); + $count = static::getDatabase()->count('movies', [Query::equal('year', [2019])]); + $this->assertEquals(2, $count); + $count = static::getDatabase()->count('movies', [Query::equal('with-dash', ['Works'])]); + $this->assertEquals(2, $count); + $count = static::getDatabase()->count('movies', [Query::equal('with-dash', ['Works2', 'Works3'])]); + $this->assertEquals(4, $count); + + Authorization::unsetRole('user:x'); + $count = static::getDatabase()->count('movies'); + $this->assertEquals(5, $count); + + Authorization::disable(); + $count = static::getDatabase()->count('movies'); + $this->assertEquals(6, $count); + Authorization::reset(); + + Authorization::disable(); + $count = static::getDatabase()->count('movies', [], 3); + $this->assertEquals(3, $count); + Authorization::reset(); + + /** + * Test that OR queries are handled correctly + */ + Authorization::disable(); + $count = static::getDatabase()->count('movies', [ + Query::equal('director', ['TBD', 'Joe Johnston']), + Query::equal('year', [2025]), + ]); + $this->assertEquals(1, $count); + Authorization::reset(); + } + + /** + * @depends testFind + */ + public function testSum(): void + { + Authorization::setRole('user:x'); + $sum = static::getDatabase()->sum('movies', 'year', [Query::equal('year', [2019]),]); + $this->assertEquals(2019 + 2019, $sum); + $sum = static::getDatabase()->sum('movies', 'year'); + $this->assertEquals(2013 + 2019 + 2011 + 2019 + 2025 + 2026, $sum); + $sum = static::getDatabase()->sum('movies', 'price', [Query::equal('year', [2019]),]); + $this->assertEquals(round(39.50 + 25.99, 2), round($sum, 2)); + $sum = static::getDatabase()->sum('movies', 'price', [Query::equal('year', [2019]),]); + $this->assertEquals(round(39.50 + 25.99, 2), round($sum, 2)); + + $sum = static::getDatabase()->sum('movies', 'year', [Query::equal('year', [2019])], 1); + $this->assertEquals(2019, $sum); + + Authorization::unsetRole('user:x'); + Authorization::unsetRole('userx'); + $sum = static::getDatabase()->sum('movies', 'year', [Query::equal('year', [2019]),]); + $this->assertEquals(2019 + 2019, $sum); + $sum = static::getDatabase()->sum('movies', 'year'); + $this->assertEquals(2013 + 2019 + 2011 + 2019 + 2025, $sum); + $sum = static::getDatabase()->sum('movies', 'price', [Query::equal('year', [2019]),]); + $this->assertEquals(round(39.50 + 25.99, 2), round($sum, 2)); + $sum = static::getDatabase()->sum('movies', 'price', [Query::equal('year', [2019]),]); + $this->assertEquals(round(39.50 + 25.99, 2), round($sum, 2)); + } + + public function testEncodeDecode(): void + { + $collection = new Document([ + '$collection' => ID::custom(Database::METADATA), + '$id' => ID::custom('users'), + 'name' => 'Users', + 'attributes' => [ [ - Query::equal('name', ["Frozen II"]), - Query::equal('year', [2019]) - ] - ), - Query::or( + '$id' => ID::custom('name'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'array' => false, + 'filters' => [], + ], [ - Query::or( - [ - Query::equal('year', [2013]), - ] - ) + '$id' => ID::custom('email'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 1024, + 'signed' => true, + 'required' => false, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('status'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('password'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('passwordUpdate'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('registration'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('emailVerification'), + 'type' => Database::VAR_BOOLEAN, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('reset'), + 'type' => Database::VAR_BOOLEAN, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('prefs'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'array' => false, + 'filters' => ['json'] + ], + [ + '$id' => ID::custom('sessions'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'array' => false, + 'filters' => ['json'], + ], + [ + '$id' => ID::custom('tokens'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'array' => false, + 'filters' => ['json'], + ], + [ + '$id' => ID::custom('memberships'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'array' => false, + 'filters' => ['json'], + ], + [ + '$id' => ID::custom('roles'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 128, + 'signed' => true, + 'required' => false, + 'array' => true, + 'filters' => [], + ], + [ + '$id' => ID::custom('tags'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 128, + 'signed' => true, + 'required' => false, + 'array' => true, + 'filters' => ['json'], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_email'), + 'type' => Database::INDEX_UNIQUE, + 'attributes' => ['email'], + 'lengths' => [1024], + 'orders' => [Database::ORDER_ASC], ] - ), - Query::limit(25) + ], ]); - $this->assertEquals(4, count($documents)); - $this->assertEquals(true, false); + $document = new Document([ + '$id' => ID::custom('608fdbe51361a'), + '$permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::user('608fdbe51361a')), + Permission::update(Role::user('608fdbe51361a')), + Permission::delete(Role::user('608fdbe51361a')), + ], + 'email' => 'test@example.com', + 'emailVerification' => false, + 'status' => 1, + 'password' => 'randomhash', + 'passwordUpdate' => '2000-06-12 14:12:55', + 'registration' => '1975-06-12 14:12:55+01:00', + 'reset' => false, + 'name' => 'My Name', + 'prefs' => new \stdClass(), + 'sessions' => [], + 'tokens' => [], + 'memberships' => [], + 'roles' => [ + 'admin', + 'developer', + 'tester', + ], + 'tags' => [ + ['$id' => '1', 'label' => 'x'], + ['$id' => '2', 'label' => 'y'], + ['$id' => '3', 'label' => 'z'], + ], + ]); - die; + $result = static::getDatabase()->encode($collection, $document); + + $this->assertEquals('608fdbe51361a', $result->getAttribute('$id')); + $this->assertContains('read("any")', $result->getAttribute('$permissions')); + $this->assertContains('read("any")', $result->getPermissions()); + $this->assertContains('any', $result->getRead()); + $this->assertContains(Permission::create(Role::user(ID::custom('608fdbe51361a'))), $result->getPermissions()); + $this->assertContains('user:608fdbe51361a', $result->getCreate()); + $this->assertContains('user:608fdbe51361a', $result->getWrite()); + $this->assertEquals('test@example.com', $result->getAttribute('email')); + $this->assertEquals(false, $result->getAttribute('emailVerification')); + $this->assertEquals(1, $result->getAttribute('status')); + $this->assertEquals('randomhash', $result->getAttribute('password')); + $this->assertEquals('2000-06-12 14:12:55.000', $result->getAttribute('passwordUpdate')); + $this->assertEquals('1975-06-12 13:12:55.000', $result->getAttribute('registration')); + $this->assertEquals(false, $result->getAttribute('reset')); + $this->assertEquals('My Name', $result->getAttribute('name')); + $this->assertEquals('{}', $result->getAttribute('prefs')); + $this->assertEquals('[]', $result->getAttribute('sessions')); + $this->assertEquals('[]', $result->getAttribute('tokens')); + $this->assertEquals('[]', $result->getAttribute('memberships')); + $this->assertEquals(['admin', 'developer', 'tester',], $result->getAttribute('roles')); + $this->assertEquals(['{"$id":"1","label":"x"}', '{"$id":"2","label":"y"}', '{"$id":"3","label":"z"}',], $result->getAttribute('tags')); + + $result = static::getDatabase()->decode($collection, $document); + + $this->assertEquals('608fdbe51361a', $result->getAttribute('$id')); + $this->assertContains('read("any")', $result->getAttribute('$permissions')); + $this->assertContains('read("any")', $result->getPermissions()); + $this->assertContains('any', $result->getRead()); + $this->assertContains(Permission::create(Role::user('608fdbe51361a')), $result->getPermissions()); + $this->assertContains('user:608fdbe51361a', $result->getCreate()); + $this->assertContains('user:608fdbe51361a', $result->getWrite()); + $this->assertEquals('test@example.com', $result->getAttribute('email')); + $this->assertEquals(false, $result->getAttribute('emailVerification')); + $this->assertEquals(1, $result->getAttribute('status')); + $this->assertEquals('randomhash', $result->getAttribute('password')); + $this->assertEquals('2000-06-12T14:12:55.000+00:00', $result->getAttribute('passwordUpdate')); + $this->assertEquals('1975-06-12T13:12:55.000+00:00', $result->getAttribute('registration')); + $this->assertEquals(false, $result->getAttribute('reset')); + $this->assertEquals('My Name', $result->getAttribute('name')); + $this->assertEquals([], $result->getAttribute('prefs')); + $this->assertEquals([], $result->getAttribute('sessions')); + $this->assertEquals([], $result->getAttribute('tokens')); + $this->assertEquals([], $result->getAttribute('memberships')); + $this->assertEquals(['admin', 'developer', 'tester',], $result->getAttribute('roles')); + $this->assertEquals([ + new Document(['$id' => '1', 'label' => 'x']), + new Document(['$id' => '2', 'label' => 'y']), + new Document(['$id' => '3', 'label' => 'z']), + ], $result->getAttribute('tags')); + } + + /** + * @depends testCreateDocument + */ + public function testReadPermissionsSuccess(Document $document): Document + { + Authorization::cleanRoles(); + Authorization::setRole(Role::any()->toString()); + + $document = static::getDatabase()->createDocument('documents', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'string' => 'text📝', + 'integer' => 5, + 'bigint' => 8589934592, // 2^33 + 'float' => 5.55, + 'boolean' => true, + 'colors' => ['pink', 'green', 'blue'], + ])); + + $this->assertEquals(false, $document->isEmpty()); + + Authorization::cleanRoles(); + + $document = static::getDatabase()->getDocument($document->getCollection(), $document->getId()); + $this->assertEquals(true, $document->isEmpty()); + + Authorization::setRole(Role::any()->toString()); + + return $document; + } + public function testReadPermissionsFailure(): Document + { + Authorization::cleanRoles(); + Authorization::setRole(Role::any()->toString()); + + $document = static::getDatabase()->createDocument('documents', new Document([ + '$permissions' => [ + Permission::read(Role::user('1')), + Permission::create(Role::user('1')), + Permission::update(Role::user('1')), + Permission::delete(Role::user('1')), + ], + 'string' => 'text📝', + 'integer' => 5, + 'bigint' => 8589934592, // 2^33 + 'float' => 5.55, + 'boolean' => true, + 'colors' => ['pink', 'green', 'blue'], + ])); + + Authorization::cleanRoles(); + + $document = static::getDatabase()->getDocument($document->getCollection(), $document->getId()); + + $this->assertEquals(true, $document->isEmpty()); + + Authorization::setRole(Role::any()->toString()); + + return $document; + } + + /** + * @depends testCreateDocument + */ + public function testWritePermissionsSuccess(Document $document): void + { + Authorization::cleanRoles(); + + $this->expectException(AuthorizationException::class); + static::getDatabase()->createDocument('documents', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'string' => 'text📝', + 'integer' => 5, + 'bigint' => 8589934592, // 2^33 + 'float' => 5.55, + 'boolean' => true, + 'colors' => ['pink', 'green', 'blue'], + ])); + } + + /** + * @depends testCreateDocument + */ + public function testWritePermissionsUpdateFailure(Document $document): Document + { + $this->expectException(AuthorizationException::class); + + Authorization::cleanRoles(); + Authorization::setRole(Role::any()->toString()); + + $document = static::getDatabase()->createDocument('documents', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'string' => 'text📝', + 'integer' => 5, + 'bigint' => 8589934592, // 2^33 + 'float' => 5.55, + 'boolean' => true, + 'colors' => ['pink', 'green', 'blue'], + ])); + + Authorization::cleanRoles(); + + $document = static::getDatabase()->updateDocument('documents', $document->getId(), new Document([ + '$id' => ID::custom($document->getId()), + '$permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'string' => 'text📝', + 'integer' => 6, + 'bigint' => 8589934592, // 2^33 + 'float' => 5.55, + 'boolean' => true, + 'colors' => ['pink', 'green', 'blue'], + ])); + + return $document; + } + + /** + * @depends testCreateDocument + */ + public function testNoChangeUpdateDocumentWithoutPermission(Document $document): Document + { + $document = static::getDatabase()->createDocument('documents', new Document([ + '$id' => ID::unique(), + '$permissions' => [], + 'string' => 'text📝', + 'integer' => 5, + 'bigint' => 8589934592, // 2^33 + 'float' => 5.55, + 'boolean' => true, + 'colors' => ['pink', 'green', 'blue'], + ])); + + $updatedDocument = static::getDatabase()->updateDocument( + 'documents', + $document->getId(), + $document + ); + + // Document should not be updated as there is no change. + // It should also not throw any authorization exception without any permission because of no change. + $this->assertEquals($updatedDocument->getUpdatedAt(), $document->getUpdatedAt()); + + return $document; + } + + public function testNoChangeUpdateDocumentWithRelationWithoutPermission(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + $attribute = new Document([ + '$id' => ID::custom("name"), + 'type' => Database::VAR_STRING, + 'size' => 100, + 'required' => false, + 'default' => null, + 'signed' => false, + 'array' => false, + 'filters' => [], + ]); + + $permissions = [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::delete(Role::any()), + ]; + for ($i=1; $i < 6; $i++) { + static::getDatabase()->createCollection("level{$i}", [$attribute], [], $permissions); + } + + for ($i = 1; $i < 5; $i++) { + $collectionId = $i; + $relatedCollectionId = $i+1; + static::getDatabase()->createRelationship( + collection: "level{$collectionId}", + relatedCollection: "level{$relatedCollectionId}", + type: Database::RELATION_ONE_TO_ONE, + id: "level{$relatedCollectionId}" + ); + } + + // Create document with relationship with nested data + $level1 = static::getDatabase()->createDocument('level1', new Document([ + '$id' => 'level1', + '$permissions' => [], + 'name' => 'Level 1', + 'level2' => [ + '$id' => 'level2', + '$permissions' => [], + 'name' => 'Level 2', + 'level3' => [ + '$id' => 'level3', + '$permissions' => [], + 'name' => 'Level 3', + 'level4' => [ + '$id' => 'level4', + '$permissions' => [], + 'name' => 'Level 4', + 'level5' => [ + '$id' => 'level5', + '$permissions' => [], + 'name' => 'Level 5', + ] + ], + ], + ], + ])); + static::getDatabase()->updateDocument('level1', $level1->getId(), new Document($level1->getArrayCopy())); + $updatedLevel1 = static::getDatabase()->getDocument('level1', $level1->getId()); + $this->assertEquals($level1, $updatedLevel1); + + try { + static::getDatabase()->updateDocument('level1', $level1->getId(), $level1->setAttribute('name', 'haha')); + $this->fail('Failed to throw exception'); + } catch(Exception $e) { + $this->assertInstanceOf(AuthorizationException::class, $e); + } + $level1->setAttribute('name', 'Level 1'); + static::getDatabase()->updateCollection('level3', [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], false); + $level2 = $level1->getAttribute('level2'); + $level3 = $level2->getAttribute('level3'); + + $level3->setAttribute('name', 'updated value'); + $level2->setAttribute('level3', $level3); + $level1->setAttribute('level2', $level2); + + $level1 = static::getDatabase()->updateDocument('level1', $level1->getId(), $level1); + $this->assertEquals('updated value', $level1['level2']['level3']['name']); + + for ($i=1; $i < 6; $i++) { + static::getDatabase()->deleteCollection("level{$i}"); + } + } + + public function testExceptionAttributeLimit(): void + { + if ($this->getDatabase()->getLimitForAttributes() > 0) { + // Load the collection up to the limit + $attributes = []; + for ($i = 0; $i < $this->getDatabase()->getLimitForAttributes(); $i++) { + $attributes[] = new Document([ + '$id' => ID::custom("test{$i}"), + 'type' => Database::VAR_INTEGER, + 'size' => 0, + 'required' => false, + 'default' => null, + 'signed' => true, + 'array' => false, + 'filters' => [], + ]); + } + + static::getDatabase()->createCollection('attributeLimit', $attributes); + + $this->expectException(LimitException::class); + $this->assertEquals(false, static::getDatabase()->createAttribute('attributeLimit', "breaking", Database::VAR_INTEGER, 0, true)); + } + + // Default assertion for other adapters + $this->assertEquals(1, 1); + } + + /** + * @depends testExceptionAttributeLimit + */ + public function testCheckAttributeCountLimit(): void + { + if ($this->getDatabase()->getLimitForAttributes() > 0) { + $collection = static::getDatabase()->getCollection('attributeLimit'); + + // create same attribute in testExceptionAttributeLimit + $attribute = new Document([ + '$id' => ID::custom('breaking'), + 'type' => Database::VAR_INTEGER, + 'size' => 0, + 'required' => true, + 'default' => null, + 'signed' => true, + 'array' => false, + 'filters' => [], + ]); + + $this->expectException(LimitException::class); + $this->assertEquals(false, static::getDatabase()->checkAttribute($collection, $attribute)); + } + + // Default assertion for other adapters + $this->assertEquals(1, 1); + } + + /** + * Using phpunit dataProviders to check that all these combinations of types/sizes throw exceptions + * https://phpunit.de/manual/3.7/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers + * + * @return array> + */ + public function rowWidthExceedsMaximum(): array + { + return [ + // These combinations of attributes gets exactly to the 64k limit + // [$key, $stringSize, $stringCount, $intCount, $floatCount, $boolCount] + // [0, 1024, 15, 0, 731, 3], + // [1, 512, 31, 0, 0, 833], + // [2, 256, 62, 128, 0, 305], + // [3, 128, 125, 30, 24, 2], + // + // Taken 500 bytes off for tests + [0, 1024, 15, 0, 304, 3], + [1, 512, 31, 0, 0, 333], + [2, 256, 62, 103, 0, 5], + [3, 128, 124, 30, 12, 14], + ]; + } + + /** + * @dataProvider rowWidthExceedsMaximum + */ + public function testExceptionWidthLimit(int $key, int $stringSize, int $stringCount, int $intCount, int $floatCount, int $boolCount): void + { + if (static::getDatabase()->getAdapter()::getDocumentSizeLimit() > 0) { + $attributes = []; + + // Load the collection up to the limit + // Strings + for ($i = 0; $i < $stringCount; $i++) { + $attributes[] = new Document([ + '$id' => ID::custom("test_string{$i}"), + 'type' => Database::VAR_STRING, + 'size' => $stringSize, + 'required' => false, + 'default' => null, + 'signed' => true, + 'array' => false, + 'filters' => [], + ]); + } + + // Integers + for ($i = 0; $i < $intCount; $i++) { + $attributes[] = new Document([ + '$id' => ID::custom("test_int{$i}"), + 'type' => Database::VAR_INTEGER, + 'size' => 0, + 'required' => false, + 'default' => null, + 'signed' => true, + 'array' => false, + 'filters' => [], + ]); + } + + // Floats + for ($i = 0; $i < $floatCount; $i++) { + $attributes[] = new Document([ + '$id' => ID::custom("test_float{$i}"), + 'type' => Database::VAR_FLOAT, + 'size' => 0, + 'required' => false, + 'default' => null, + 'signed' => true, + 'array' => false, + 'filters' => [], + ]); + } + + // Booleans + for ($i = 0; $i < $boolCount; $i++) { + $attributes[] = new Document([ + '$id' => ID::custom("test_bool{$i}"), + 'type' => Database::VAR_BOOLEAN, + 'size' => 0, + 'required' => false, + 'default' => null, + 'signed' => true, + 'array' => false, + 'filters' => [], + ]); + } + + $collection = static::getDatabase()->createCollection("widthLimit{$key}", $attributes); + + $this->expectException(LimitException::class); + $this->assertEquals(false, static::getDatabase()->createAttribute("widthLimit{$key}", "breaking", Database::VAR_STRING, 100, true)); + } + + // Default assertion for other adapters + $this->assertEquals(1, 1); + } + + /** + * @dataProvider rowWidthExceedsMaximum + * @depends testExceptionWidthLimit + */ + public function testCheckAttributeWidthLimit(int $key, int $stringSize, int $stringCount, int $intCount, int $floatCount, int $boolCount): void + { + if (static::getDatabase()->getAdapter()::getDocumentSizeLimit()> 0) { + $collection = static::getDatabase()->getCollection("widthLimit{$key}"); + + // create same attribute in testExceptionWidthLimit + $attribute = new Document([ + '$id' => ID::custom('breaking'), + 'type' => Database::VAR_STRING, + 'size' => 100, + 'required' => true, + 'default' => null, + 'signed' => true, + 'array' => false, + 'filters' => [], + ]); + + $this->expectException(LimitException::class); + $this->assertEquals(false, static::getDatabase()->checkAttribute($collection, $attribute)); + } + + // Default assertion for other adapters + $this->assertEquals(1, 1); + } + + public function testExceptionIndexLimit(): void + { + static::getDatabase()->createCollection('indexLimit'); + + // add unique attributes for indexing + for ($i = 0; $i < 64; $i++) { + $this->assertEquals(true, static::getDatabase()->createAttribute('indexLimit', "test{$i}", Database::VAR_STRING, 16, true)); + } + + // Testing for indexLimit + // Add up to the limit, then check if the next index throws IndexLimitException + for ($i = 0; $i < ($this->getDatabase()->getLimitForIndexes()); $i++) { + $this->assertEquals(true, static::getDatabase()->createIndex('indexLimit', "index{$i}", Database::INDEX_KEY, ["test{$i}"], [16])); + } + $this->expectException(LimitException::class); + $this->assertEquals(false, static::getDatabase()->createIndex('indexLimit', "index64", Database::INDEX_KEY, ["test64"], [16])); + + static::getDatabase()->deleteCollection('indexLimit'); + } + + /** + * @depends testGetDocument + */ + public function testExceptionDuplicate(Document $document): void + { + $document->setAttribute('$id', 'duplicated'); + static::getDatabase()->createDocument($document->getCollection(), $document); + + $this->expectException(DuplicateException::class); + static::getDatabase()->createDocument($document->getCollection(), $document); + } + + /** + * @depends testGetDocument + */ + public function testExceptionCaseInsensitiveDuplicate(Document $document): Document + { + $document->setAttribute('$id', 'caseSensitive'); + $document->setAttribute('$internalId', '200'); + static::getDatabase()->createDocument($document->getCollection(), $document); + + $document->setAttribute('$id', 'CaseSensitive'); + + $this->expectException(DuplicateException::class); + static::getDatabase()->createDocument($document->getCollection(), $document); + + return $document; + } + + /** + * @depends testFind + */ + public function testUniqueIndexDuplicate(): void + { + $this->expectException(DuplicateException::class); + + $this->assertEquals(true, static::getDatabase()->createIndex('movies', 'uniqueIndex', Database::INDEX_UNIQUE, ['name'], [128], [Database::ORDER_ASC])); + + static::getDatabase()->createDocument('movies', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::read(Role::user('1')), + Permission::read(Role::user('2')), + Permission::create(Role::any()), + Permission::create(Role::user('1x')), + Permission::create(Role::user('2x')), + Permission::update(Role::any()), + Permission::update(Role::user('1x')), + Permission::update(Role::user('2x')), + Permission::delete(Role::any()), + Permission::delete(Role::user('1x')), + Permission::delete(Role::user('2x')), + ], + 'name' => 'Frozen', + 'director' => 'Chris Buck & Jennifer Lee', + 'year' => 2013, + 'price' => 39.50, + 'active' => true, + 'generes' => ['animation', 'kids'], + 'with-dash' => 'Works4' + ])); + } + + /** + * @depends testUniqueIndexDuplicate + */ + public function testUniqueIndexDuplicateUpdate(): void + { + Authorization::setRole(Role::users()->toString()); + // create document then update to conflict with index + $document = static::getDatabase()->createDocument('movies', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::read(Role::user('1')), + Permission::read(Role::user('2')), + Permission::create(Role::any()), + Permission::create(Role::user('1x')), + Permission::create(Role::user('2x')), + Permission::update(Role::any()), + Permission::update(Role::user('1x')), + Permission::update(Role::user('2x')), + Permission::delete(Role::any()), + Permission::delete(Role::user('1x')), + Permission::delete(Role::user('2x')), + ], + 'name' => 'Frozen 5', + 'director' => 'Chris Buck & Jennifer Lee', + 'year' => 2013, + 'price' => 39.50, + 'active' => true, + 'generes' => ['animation', 'kids'], + 'with-dash' => 'Works4' + ])); + + $this->expectException(DuplicateException::class); + + static::getDatabase()->updateDocument('movies', $document->getId(), $document->setAttribute('name', 'Frozen')); + } + + public function testGetAttributeLimit(): void + { + $this->assertIsInt($this->getDatabase()->getLimitForAttributes()); + } + + public function testGetIndexLimit(): void + { + $this->assertEquals(59, $this->getDatabase()->getLimitForIndexes()); + } + + public function testGetId(): void + { + $this->assertEquals(20, strlen(ID::unique())); + $this->assertEquals(13, strlen(ID::unique(0))); + $this->assertEquals(13, strlen(ID::unique(-1))); + $this->assertEquals(23, strlen(ID::unique(10))); + + // ensure two sequential calls to getId do not give the same result + $this->assertNotEquals(ID::unique(10), ID::unique(10)); + } + + public function testRenameIndex(): void + { + $database = static::getDatabase(); + + $numbers = $database->createCollection('numbers'); + $database->createAttribute('numbers', 'verbose', Database::VAR_STRING, 128, true); + $database->createAttribute('numbers', 'symbol', Database::VAR_INTEGER, 0, true); + + $database->createIndex('numbers', 'index1', Database::INDEX_KEY, ['verbose'], [128], [Database::ORDER_ASC]); + $database->createIndex('numbers', 'index2', Database::INDEX_KEY, ['symbol'], [0], [Database::ORDER_ASC]); + + $index = $database->renameIndex('numbers', 'index1', 'index3'); + + $this->assertTrue($index); + + $numbers = $database->getCollection('numbers'); + + $this->assertEquals('index2', $numbers->getAttribute('indexes')[1]['$id']); + $this->assertEquals('index3', $numbers->getAttribute('indexes')[0]['$id']); + $this->assertCount(2, $numbers->getAttribute('indexes')); + } + + /** + * @depends testRenameIndex + * @expectedException Exception + */ + public function testRenameIndexMissing(): void + { + $database = static::getDatabase(); + $this->expectExceptionMessage('Index not found'); + $index = $database->renameIndex('numbers', 'index1', 'index4'); + } + + /** + * @depends testRenameIndex + * @expectedException Exception + */ + public function testRenameIndexExisting(): void + { + $database = static::getDatabase(); + $this->expectExceptionMessage('Index name already used'); + $index = $database->renameIndex('numbers', 'index3', 'index2'); + } + + public function testRenameAttribute(): void + { + $database = static::getDatabase(); + + $colors = $database->createCollection('colors'); + $database->createAttribute('colors', 'name', Database::VAR_STRING, 128, true); + $database->createAttribute('colors', 'hex', Database::VAR_STRING, 128, true); + + $database->createIndex('colors', 'index1', Database::INDEX_KEY, ['name'], [128], [Database::ORDER_ASC]); + + $database->createDocument('colors', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'black', + 'hex' => '#000000' + ])); + + $attribute = $database->renameAttribute('colors', 'name', 'verbose'); + + $this->assertTrue($attribute); + + $colors = $database->getCollection('colors'); + $this->assertEquals('hex', $colors->getAttribute('attributes')[1]['$id']); + $this->assertEquals('verbose', $colors->getAttribute('attributes')[0]['$id']); + $this->assertCount(2, $colors->getAttribute('attributes')); + + // Attribute in index is renamed automatically on adapter-level. What we need to check is if metadata is properly updated + $this->assertEquals('verbose', $colors->getAttribute('indexes')[0]->getAttribute("attributes")[0]); + $this->assertCount(1, $colors->getAttribute('indexes')); + + // Document should be there if adapter migrated properly + $document = $database->findOne('colors'); + $this->assertTrue($document instanceof Document); + $this->assertEquals('black', $document->getAttribute('verbose')); + $this->assertEquals('#000000', $document->getAttribute('hex')); + $this->assertEquals(null, $document->getAttribute('name')); + } + + /** + * @depends testRenameAttribute + * @expectedException Exception + */ + public function textRenameAttributeMissing(): void + { + $database = static::getDatabase(); + $this->expectExceptionMessage('Attribute not found'); + $database->renameAttribute('colors', 'name2', 'name3'); + } + + /** + * @depends testRenameAttribute + * @expectedException Exception + */ + public function testRenameAttributeExisting(): void + { + $database = static::getDatabase(); + $this->expectExceptionMessage('Attribute name already used'); + $database->renameAttribute('colors', 'verbose', 'hex'); + } + + public function testUpdateAttributeDefault(): void + { + $database = static::getDatabase(); + + $flowers = $database->createCollection('flowers'); + $database->createAttribute('flowers', 'name', Database::VAR_STRING, 128, true); + $database->createAttribute('flowers', 'inStock', Database::VAR_INTEGER, 0, false); + $database->createAttribute('flowers', 'date', Database::VAR_STRING, 128, false); + + $database->createDocument('flowers', new Document([ + '$id' => 'flowerWithDate', + '$permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Violet', + 'inStock' => 51, + 'date' => '2000-06-12 14:12:55.000' + ])); + + $doc = $database->createDocument('flowers', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Lily' + ])); + + $this->assertNull($doc->getAttribute('inStock')); + + $database->updateAttributeDefault('flowers', 'inStock', 100); + + $doc = $database->createDocument('flowers', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Iris' + ])); + + $this->assertIsNumeric($doc->getAttribute('inStock')); + $this->assertEquals(100, $doc->getAttribute('inStock')); + + $database->updateAttributeDefault('flowers', 'inStock', null); + } + + /** + * @depends testUpdateAttributeDefault + */ + public function testUpdateAttributeRequired(): void + { + $database = static::getDatabase(); + + $database->updateAttributeRequired('flowers', 'inStock', true); + + $this->expectExceptionMessage('Invalid document structure: Missing required attribute "inStock"'); + + $doc = $database->createDocument('flowers', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Lily With Missing Stocks' + ])); + } + + /** + * @depends testUpdateAttributeDefault + */ + public function testUpdateAttributeFilter(): void + { + $database = static::getDatabase(); + + $database->createAttribute('flowers', 'cartModel', Database::VAR_STRING, 2000, false); + + $doc = $database->createDocument('flowers', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Lily With CartData', + 'inStock' => 50, + 'cartModel' => '{"color":"string","size":"number"}' + ])); + + $this->assertIsString($doc->getAttribute('cartModel')); + $this->assertEquals('{"color":"string","size":"number"}', $doc->getAttribute('cartModel')); + + $database->updateAttributeFilters('flowers', 'cartModel', ['json']); + + $doc = $database->getDocument('flowers', $doc->getId()); + $this->assertIsArray($doc->getAttribute('cartModel')); + $this->assertCount(2, $doc->getAttribute('cartModel')); + $this->assertEquals('string', $doc->getAttribute('cartModel')['color']); + $this->assertEquals('number', $doc->getAttribute('cartModel')['size']); + } + + /** + * @depends testUpdateAttributeDefault + */ + public function testUpdateAttributeFormat(): void + { + $database = static::getDatabase(); + + $database->createAttribute('flowers', 'price', Database::VAR_INTEGER, 0, false); + + $doc = $database->createDocument('flowers', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + '$id' => ID::custom('LiliPriced'), + 'name' => 'Lily Priced', + 'inStock' => 50, + 'cartModel' => '{}', + 'price' => 500 + ])); + + $this->assertIsNumeric($doc->getAttribute('price')); + $this->assertEquals(500, $doc->getAttribute('price')); + + Structure::addFormat('priceRange', function ($attribute) { + $min = $attribute['formatOptions']['min']; + $max = $attribute['formatOptions']['max']; + + return new Range($min, $max); + }, Database::VAR_INTEGER); + + $database->updateAttributeFormat('flowers', 'price', 'priceRange'); + $database->updateAttributeFormatOptions('flowers', 'price', ['min' => 1, 'max' => 10000]); + + $this->expectExceptionMessage('Invalid document structure: Attribute "price" has invalid format. Value must be a valid range between 1 and 10,000'); + + $doc = $database->createDocument('flowers', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Lily Overpriced', + 'inStock' => 50, + 'cartModel' => '{}', + 'price' => 15000 + ])); + } + + /** + * @depends testUpdateAttributeDefault + * @depends testUpdateAttributeFormat + */ + public function testUpdateAttributeStructure(): void + { + // TODO: When this becomes relevant, add many more tests (from all types to all types, chaging size up&down, switchign between array/non-array... + + Structure::addFormat('priceRangeNew', function ($attribute) { + $min = $attribute['formatOptions']['min']; + $max = $attribute['formatOptions']['max']; + return new Range($min, $max); + }, Database::VAR_INTEGER); + + $database = static::getDatabase(); + + // price attribute + $collection = $database->getCollection('flowers'); + $attribute = $collection->getAttribute('attributes')[4]; + $this->assertEquals(true, $attribute['signed']); + $this->assertEquals(0, $attribute['size']); + $this->assertEquals(null, $attribute['default']); + $this->assertEquals(false, $attribute['array']); + $this->assertEquals(false, $attribute['required']); + $this->assertEquals('priceRange', $attribute['format']); + $this->assertEquals(['min'=>1, 'max'=>10000], $attribute['formatOptions']); + + $database->updateAttribute('flowers', 'price', default: 100); + $collection = $database->getCollection('flowers'); + $attribute = $collection->getAttribute('attributes')[4]; + $this->assertEquals('integer', $attribute['type']); + $this->assertEquals(true, $attribute['signed']); + $this->assertEquals(0, $attribute['size']); + $this->assertEquals(100, $attribute['default']); + $this->assertEquals(false, $attribute['array']); + $this->assertEquals(false, $attribute['required']); + $this->assertEquals('priceRange', $attribute['format']); + $this->assertEquals(['min'=>1, 'max'=>10000], $attribute['formatOptions']); + + $database->updateAttribute('flowers', 'price', format: 'priceRangeNew'); + $collection = $database->getCollection('flowers'); + $attribute = $collection->getAttribute('attributes')[4]; + $this->assertEquals('integer', $attribute['type']); + $this->assertEquals(true, $attribute['signed']); + $this->assertEquals(0, $attribute['size']); + $this->assertEquals(100, $attribute['default']); + $this->assertEquals(false, $attribute['array']); + $this->assertEquals(false, $attribute['required']); + $this->assertEquals('priceRangeNew', $attribute['format']); + $this->assertEquals(['min'=>1, 'max'=>10000], $attribute['formatOptions']); + + $database->updateAttribute('flowers', 'price', format: ''); + $collection = $database->getCollection('flowers'); + $attribute = $collection->getAttribute('attributes')[4]; + $this->assertEquals('integer', $attribute['type']); + $this->assertEquals(true, $attribute['signed']); + $this->assertEquals(0, $attribute['size']); + $this->assertEquals(100, $attribute['default']); + $this->assertEquals(false, $attribute['array']); + $this->assertEquals(false, $attribute['required']); + $this->assertEquals('', $attribute['format']); + $this->assertEquals(['min'=>1, 'max'=>10000], $attribute['formatOptions']); + + $database->updateAttribute('flowers', 'price', formatOptions: ['min' => 1, 'max' => 999]); + $collection = $database->getCollection('flowers'); + $attribute = $collection->getAttribute('attributes')[4]; + $this->assertEquals('integer', $attribute['type']); + $this->assertEquals(true, $attribute['signed']); + $this->assertEquals(0, $attribute['size']); + $this->assertEquals(100, $attribute['default']); + $this->assertEquals(false, $attribute['array']); + $this->assertEquals(false, $attribute['required']); + $this->assertEquals('', $attribute['format']); + $this->assertEquals(['min'=>1, 'max'=>999], $attribute['formatOptions']); + + $database->updateAttribute('flowers', 'price', formatOptions: []); + $collection = $database->getCollection('flowers'); + $attribute = $collection->getAttribute('attributes')[4]; + $this->assertEquals('integer', $attribute['type']); + $this->assertEquals(true, $attribute['signed']); + $this->assertEquals(0, $attribute['size']); + $this->assertEquals(100, $attribute['default']); + $this->assertEquals(false, $attribute['array']); + $this->assertEquals(false, $attribute['required']); + $this->assertEquals('', $attribute['format']); + $this->assertEquals([], $attribute['formatOptions']); + + $database->updateAttribute('flowers', 'price', signed: false); + $collection = $database->getCollection('flowers'); + $attribute = $collection->getAttribute('attributes')[4]; + $this->assertEquals('integer', $attribute['type']); + $this->assertEquals(false, $attribute['signed']); + $this->assertEquals(0, $attribute['size']); + $this->assertEquals(100, $attribute['default']); + $this->assertEquals(false, $attribute['array']); + $this->assertEquals(false, $attribute['required']); + $this->assertEquals('', $attribute['format']); + $this->assertEquals([], $attribute['formatOptions']); + + $database->updateAttribute('flowers', 'price', required: true); + $collection = $database->getCollection('flowers'); + $attribute = $collection->getAttribute('attributes')[4]; + $this->assertEquals('integer', $attribute['type']); + $this->assertEquals(false, $attribute['signed']); + $this->assertEquals(0, $attribute['size']); + $this->assertEquals(null, $attribute['default']); + $this->assertEquals(false, $attribute['array']); + $this->assertEquals(true, $attribute['required']); + $this->assertEquals('', $attribute['format']); + $this->assertEquals([], $attribute['formatOptions']); + + $database->updateAttribute('flowers', 'price', type: Database::VAR_STRING, size: Database::LENGTH_KEY, format: ''); + $collection = $database->getCollection('flowers'); + $attribute = $collection->getAttribute('attributes')[4]; + $this->assertEquals('string', $attribute['type']); + $this->assertEquals(false, $attribute['signed']); + $this->assertEquals(255, $attribute['size']); + $this->assertEquals(null, $attribute['default']); + $this->assertEquals(false, $attribute['array']); + $this->assertEquals(true, $attribute['required']); + $this->assertEquals('', $attribute['format']); + $this->assertEquals([], $collection->getAttribute('attributes')[4]['formatOptions']); + + // Date attribute + $attribute = $collection->getAttribute('attributes')[2]; + $this->assertEquals('date', $attribute['key']); + $this->assertEquals('string', $attribute['type']); + $this->assertEquals(null, $attribute['default']); + + $database->updateAttribute('flowers', 'date', type: Database::VAR_DATETIME, size: 0, filters: ['datetime']); + $collection = $database->getCollection('flowers'); + $attribute = $collection->getAttribute('attributes')[2]; + $this->assertEquals('datetime', $attribute['type']); + $this->assertEquals(0, $attribute['size']); + $this->assertEquals(null, $attribute['default']); + $this->assertEquals(false, $attribute['required']); + $this->assertEquals(true, $attribute['signed']); + $this->assertEquals(false, $attribute['array']); + $this->assertEquals('', $attribute['format']); + $this->assertEquals([], $attribute['formatOptions']); + + $doc = $database->getDocument('flowers', 'LiliPriced'); + $this->assertIsString($doc->getAttribute('price')); + $this->assertEquals('500', $doc->getAttribute('price')); + + $doc = $database->getDocument('flowers', 'flowerWithDate'); + $this->assertEquals('2000-06-12T14:12:55.000+00:00', $doc->getAttribute('date')); + } + + /** + * @depends testCreatedAtUpdatedAt + */ + public function testCreatedAtUpdatedAtAssert(): void + { + $document = static::getDatabase()->getDocument('created_at', 'uid123'); + $this->assertEquals(true, !$document->isEmpty()); + sleep(1); + $document->setAttribute('title', 'new title'); + static::getDatabase()->updateDocument('created_at', 'uid123', $document); + $document = static::getDatabase()->getDocument('created_at', 'uid123'); + + $this->assertGreaterThan($document->getCreatedAt(), $document->getUpdatedAt()); + $this->expectException(DuplicateException::class); + + static::getDatabase()->createCollection('created_at'); + } + + public function testCreateDatetime(): void + { + static::getDatabase()->createCollection('datetime'); + + $this->assertEquals(true, static::getDatabase()->createAttribute('datetime', 'date', Database::VAR_DATETIME, 0, true, null, true, false, null, [], ['datetime'])); + $this->assertEquals(true, static::getDatabase()->createAttribute('datetime', 'date2', Database::VAR_DATETIME, 0, false, null, true, false, null, [], ['datetime'])); + + $doc = static::getDatabase()->createDocument('datetime', new Document([ + '$id' => ID::custom('id1234'), + '$permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'date' => DateTime::now(), + ])); + + $this->assertEquals(29, strlen($doc->getCreatedAt())); + $this->assertEquals(29, strlen($doc->getUpdatedAt())); + $this->assertEquals('+00:00', substr($doc->getCreatedAt(), -6)); + $this->assertEquals('+00:00', substr($doc->getUpdatedAt(), -6)); + $this->assertGreaterThan('2020-08-16T19:30:08.363+00:00', $doc->getCreatedAt()); + $this->assertGreaterThan('2020-08-16T19:30:08.363+00:00', $doc->getUpdatedAt()); + + $document = static::getDatabase()->getDocument('datetime', 'id1234'); + $dateValidator = new DatetimeValidator(); + $this->assertEquals(null, $document->getAttribute('date2')); + $this->assertEquals(true, $dateValidator->isValid($document->getAttribute('date'))); + $this->assertEquals(false, $dateValidator->isValid($document->getAttribute('date2'))); + + $documents = static::getDatabase()->find('datetime', [ + Query::greaterThan('date', '1975-12-06 10:00:00+01:00'), + Query::lessThan('date', '2030-12-06 10:00:00-01:00'), + ]); + + $this->assertEquals(1, count($documents)); + + $this->expectException(StructureException::class); + static::getDatabase()->createDocument('datetime', new Document([ + '$permissions' => [ + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'date' => "1975-12-06 00:00:61" + ])); + } + + public function testCreateDateTimeAttributeFailure(): void + { + static::getDatabase()->createCollection('datetime_fail'); + + /** Test for FAILURE */ + $this->expectException(Exception::class); + static::getDatabase()->createAttribute('datetime_fail', 'date_fail', Database::VAR_DATETIME, 0, false); + } + + public function testKeywords(): void + { + $database = static::getDatabase(); + $keywords = $database->getKeywords(); + + // Collection name tests + $attributes = [ + new Document([ + '$id' => ID::custom('attribute1'), + 'type' => Database::VAR_STRING, + 'size' => 256, + 'required' => false, + 'signed' => true, + 'array' => false, + 'filters' => [], + ]), + ]; + + $indexes = [ + new Document([ + '$id' => ID::custom('index1'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['attribute1'], + 'lengths' => [256], + 'orders' => ['ASC'], + ]), + ]; + + foreach ($keywords as $keyword) { + $collection = $database->createCollection($keyword, $attributes, $indexes); + $this->assertEquals($keyword, $collection->getId()); + + $document = $database->createDocument($keyword, new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + '$id' => ID::custom('helloWorld'), + 'attribute1' => 'Hello World', + ])); + $this->assertEquals('helloWorld', $document->getId()); + + $document = $database->getDocument($keyword, 'helloWorld'); + $this->assertEquals('helloWorld', $document->getId()); + + $documents = $database->find($keyword); + $this->assertCount(1, $documents); + $this->assertEquals('helloWorld', $documents[0]->getId()); + + $collection = $database->deleteCollection($keyword); + $this->assertTrue($collection); + } + + // TODO: updateCollection name tests + + // Attribute name tests + foreach ($keywords as $keyword) { + $collectionName = 'rk' . $keyword; // rk is short-hand for reserved-keyword. We do this sicne there are some limits (64 chars max) + + $collection = $database->createCollection($collectionName); + $this->assertEquals($collectionName, $collection->getId()); + + $attribute = static::getDatabase()->createAttribute($collectionName, $keyword, Database::VAR_STRING, 128, true); + $this->assertEquals(true, $attribute); + + $document = new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + '$id' => 'reservedKeyDocument' + ]); + $document->setAttribute($keyword, 'Reserved:' . $keyword); + + $document = $database->createDocument($collectionName, $document); + $this->assertEquals('reservedKeyDocument', $document->getId()); + $this->assertEquals('Reserved:' . $keyword, $document->getAttribute($keyword)); + + $document = $database->getDocument($collectionName, 'reservedKeyDocument'); + $this->assertEquals('reservedKeyDocument', $document->getId()); + $this->assertEquals('Reserved:' . $keyword, $document->getAttribute($keyword)); + + $documents = $database->find($collectionName); + $this->assertCount(1, $documents); + $this->assertEquals('reservedKeyDocument', $documents[0]->getId()); + $this->assertEquals('Reserved:' . $keyword, $documents[0]->getAttribute($keyword)); + + $documents = $database->find($collectionName, [Query::equal($keyword, ["Reserved:${keyword}"])]); + $this->assertCount(1, $documents); + $this->assertEquals('reservedKeyDocument', $documents[0]->getId()); + + $documents = $database->find($collectionName, [ + Query::orderDesc($keyword) + ]); + $this->assertCount(1, $documents); + $this->assertEquals('reservedKeyDocument', $documents[0]->getId()); + + + $collection = $database->deleteCollection($collectionName); + $this->assertTrue($collection); + + // TODO: updateAttribute name tests + } + + // TODO: Index name tests + } + + public function testWritePermissions(): void + { + Authorization::setRole(Role::any()->toString()); + $database = static::getDatabase(); + + $database->createCollection('animals', permissions: [ + Permission::create(Role::any()), + ], documentSecurity: true); + + $database->createAttribute('animals', 'type', Database::VAR_STRING, 128, true); + + $dog = $database->createDocument('animals', new Document([ + '$id' => 'dog', + '$permissions' => [ + Permission::delete(Role::any()), + ], + 'type' => 'Dog' + ])); + + $cat = $database->createDocument('animals', new Document([ + '$id' => 'cat', + '$permissions' => [ + Permission::update(Role::any()), + ], + 'type' => 'Cat' + ])); + + // No read permissions: + + $docs = $database->find('animals'); + $this->assertCount(0, $docs); + + $doc = $database->getDocument('animals', 'dog'); + $this->assertTrue($doc->isEmpty()); + + $doc = $database->getDocument('animals', 'cat'); + $this->assertTrue($doc->isEmpty()); + + // Cannot delete with update permission: + $didFail = false; + + try { + $database->deleteDocument('animals', 'cat'); + } catch (AuthorizationException) { + $didFail = true; + } + + $this->assertTrue($didFail); + + // Cannot update with delete permission: + $didFail = false; + + try { + $newDog = $dog->setAttribute('type', 'newDog'); + $database->updateDocument('animals', 'dog', $newDog); + } catch (AuthorizationException) { + $didFail = true; + } + + $this->assertTrue($didFail); + + // Can delete: + $database->deleteDocument('animals', 'dog'); + + // Can update: + $newCat = $cat->setAttribute('type', 'newCat'); + $database->updateDocument('animals', 'cat', $newCat); + + $docs = Authorization::skip(fn () => $database->find('animals')); + $this->assertCount(1, $docs); + $this->assertEquals('cat', $docs[0]['$id']); + $this->assertEquals('newCat', $docs[0]['type']); + } + + public function testNoInvalidKeysWithRelationships(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + static::getDatabase()->createCollection('species'); + static::getDatabase()->createCollection('creatures'); + static::getDatabase()->createCollection('characterstics'); + + static::getDatabase()->createAttribute('species', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('creatures', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('characterstics', 'name', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'species', + relatedCollection: 'creatures', + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + id: 'creature', + twoWayKey:'species' + ); + static::getDatabase()->createRelationship( + collection: 'creatures', + relatedCollection: 'characterstics', + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + id: 'characterstic', + twoWayKey:'creature' + ); + + $species = static::getDatabase()->createDocument('species', new Document([ + '$id' => ID::custom('1'), + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Canine', + 'creature' => [ + '$id' => ID::custom('1'), + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Dog', + 'characterstic' => [ + '$id' => ID::custom('1'), + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + 'name' => 'active', + ] + ] + ])); + static::getDatabase()->updateDocument('species', $species->getId(), new Document([ + '$id' => ID::custom('1'), + '$collection' => 'species', + 'creature' => [ + '$id' => ID::custom('1'), + '$collection' => 'creatures', + 'characterstic' => [ + '$id' => ID::custom('1'), + 'name' => 'active', + '$collection' => 'characterstics', + ] + ] + ])); + $updatedSpecies = static::getDatabase()->getDocument('species', $species->getId()); + $this->assertEquals($species, $updatedSpecies); + } + + // Relationships + public function testOneToOneOneWayRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('person'); + static::getDatabase()->createCollection('library'); + + static::getDatabase()->createAttribute('person', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('library', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('library', 'area', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'person', + relatedCollection: 'library', + type: Database::RELATION_ONE_TO_ONE + ); + + // Check metadata for collection + $collection = static::getDatabase()->getCollection('person'); + $attributes = $collection->getAttribute('attributes', []); + + foreach ($attributes as $attribute) { + if ($attribute['key'] === 'library') { + $this->assertEquals('relationship', $attribute['type']); + $this->assertEquals('library', $attribute['$id']); + $this->assertEquals('library', $attribute['key']); + $this->assertEquals('library', $attribute['options']['relatedCollection']); + $this->assertEquals(Database::RELATION_ONE_TO_ONE, $attribute['options']['relationType']); + $this->assertEquals(false, $attribute['options']['twoWay']); + $this->assertEquals('person', $attribute['options']['twoWayKey']); + } + } + + try { + static::getDatabase()->deleteAttribute('person', 'library'); + $this->fail('Failed to throw Exception'); + } catch (Exception $e) { + $this->assertEquals('Cannot delete relationship as an attribute', $e->getMessage()); + } + + // Create document with relationship with nested data + $person1 = static::getDatabase()->createDocument('person', new Document([ + '$id' => 'person1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Person 1', + 'library' => [ + '$id' => 'library1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Library 1', + 'area' => 'Area 1', + ], + ])); + + // Update a document with non existing related document. It should not get added to the list. + static::getDatabase()->updateDocument( + 'person', + 'person1', + $person1->setAttribute('library', 'no-library') + ); + + $person1Document = static::getDatabase()->getDocument('person', 'person1'); + // Assert document does not contain non existing relation document. + $this->assertEquals(null, $person1Document->getAttribute('library')); + + static::getDatabase()->updateDocument( + 'person', + 'person1', + $person1->setAttribute('library', 'library1') + ); + + // Update through create + $library10 = static::getDatabase()->createDocument('library', new Document([ + '$id' => 'library10', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + 'name' => 'Library 10', + 'area' => 'Area 10', + ])); + $person10 = static::getDatabase()->createDocument('person', new Document([ + '$id' => 'person10', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Person 10', + 'library' => [ + '$id' => $library10->getId(), + 'name' => 'Library 10 Updated', + 'area' => 'Area 10 Updated', + ], + ])); + $this->assertEquals('Library 10 Updated', $person10->getAttribute('library')->getAttribute('name')); + $library10 = static::getDatabase()->getDocument('library', $library10->getId()); + $this->assertEquals('Library 10 Updated', $library10->getAttribute('name')); + + // Create document with relationship with related ID + static::getDatabase()->createDocument('library', new Document([ + '$id' => 'library2', + '$permissions' => [ + Permission::read(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Library 2', + 'area' => 'Area 2', + ])); + static::getDatabase()->createDocument('person', new Document([ + '$id' => 'person2', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Person 2', + 'library' => 'library2', + ])); + + // Get documents with relationship + $person1 = static::getDatabase()->getDocument('person', 'person1'); + $library = $person1->getAttribute('library'); + $this->assertEquals('library1', $library['$id']); + $this->assertArrayNotHasKey('person', $library); + + $person = static::getDatabase()->getDocument('person', 'person2'); + $library = $person->getAttribute('library'); + $this->assertEquals('library2', $library['$id']); + $this->assertArrayNotHasKey('person', $library); + + // Get related documents + $library = static::getDatabase()->getDocument('library', 'library1'); + $this->assertArrayNotHasKey('person', $library); + + $library = static::getDatabase()->getDocument('library', 'library2'); + $this->assertArrayNotHasKey('person', $library); + + $people = static::getDatabase()->find('person', [ + Query::select(['name']) + ]); + + $this->assertArrayNotHasKey('library', $people[0]); + + $people = static::getDatabase()->find('person'); + $this->assertEquals(3, \count($people)); + + // Select related document attributes + $person = static::getDatabase()->findOne('person', [ + Query::select(['*', 'library.name']) + ]); + + if (!$person instanceof Document) { + throw new Exception('Person not found'); + } + + $this->assertEquals('Library 1', $person->getAttribute('library')->getAttribute('name')); + $this->assertArrayNotHasKey('area', $person->getAttribute('library')); + + $person = static::getDatabase()->getDocument('person', 'person1', [ + Query::select(['*', 'library.name', '$id']) + ]); + + $this->assertEquals('Library 1', $person->getAttribute('library')->getAttribute('name')); + $this->assertArrayNotHasKey('area', $person->getAttribute('library')); + + + + $document = static::getDatabase()->getDocument('person', $person->getId(), [ + Query::select(['name']), + ]); + $this->assertArrayNotHasKey('library', $document); + $this->assertEquals('Person 1', $document['name']); + + $document = static::getDatabase()->getDocument('person', $person->getId(), [ + Query::select(['*']), + ]); + $this->assertEquals('library1', $document['library']); + + $document = static::getDatabase()->getDocument('person', $person->getId(), [ + Query::select(['library.*']), + ]); + $this->assertEquals('Library 1', $document['library']['name']); + $this->assertArrayNotHasKey('name', $document); + + // Update root document attribute without altering relationship + $person1 = static::getDatabase()->updateDocument( + 'person', + $person1->getId(), + $person1->setAttribute('name', 'Person 1 Updated') + ); + + $this->assertEquals('Person 1 Updated', $person1->getAttribute('name')); + $person1 = static::getDatabase()->getDocument('person', 'person1'); + $this->assertEquals('Person 1 Updated', $person1->getAttribute('name')); + + // Update nested document attribute + $person1 = static::getDatabase()->updateDocument( + 'person', + $person1->getId(), + $person1->setAttribute( + 'library', + $person1 + ->getAttribute('library') + ->setAttribute('name', 'Library 1 Updated') + ) + ); + + $this->assertEquals('Library 1 Updated', $person1->getAttribute('library')->getAttribute('name')); + $person1 = static::getDatabase()->getDocument('person', 'person1'); + $this->assertEquals('Library 1 Updated', $person1->getAttribute('library')->getAttribute('name')); + + // Create new document with no relationship + $person3 = static::getDatabase()->createDocument('person', new Document([ + '$id' => 'person3', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Person 3', + ])); + + // Update to relate to created document + $person3 = static::getDatabase()->updateDocument( + 'person', + $person3->getId(), + $person3->setAttribute('library', new Document([ + '$id' => 'library3', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + 'name' => 'Library 3', + 'area' => 'Area 3', + ])) + ); + + $this->assertEquals('library3', $person3->getAttribute('library')['$id']); + $person3 = static::getDatabase()->getDocument('person', 'person3'); + $this->assertEquals('Library 3', $person3['library']['name']); + + $libraryDocument = static::getDatabase()->getDocument('library', 'library3'); + $libraryDocument->setAttribute('name', 'Library 3 updated'); + static::getDatabase()->updateDocument('library', 'library3', $libraryDocument); + $libraryDocument = static::getDatabase()->getDocument('library', 'library3'); + $this->assertEquals('Library 3 updated', $libraryDocument['name']); + + $person3 = static::getDatabase()->getDocument('person', 'person3'); + // Todo: This is failing + $this->assertEquals($libraryDocument['name'], $person3['library']['name']); + $this->assertEquals('library3', $person3->getAttribute('library')['$id']); + + // One to one can't relate to multiple documents, unique index throws duplicate + try { + static::getDatabase()->updateDocument( + 'person', + $person1->getId(), + $person1->setAttribute('library', 'library2') + ); + $this->fail('Failed to throw duplicate exception'); + } catch (Exception $e) { + $this->assertInstanceOf(DuplicateException::class, $e); + } + + // Create new document + $library4 = static::getDatabase()->createDocument('library', new Document([ + '$id' => 'library4', + '$permissions' => [ + Permission::read(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Library 4', + 'area' => 'Area 4', + ])); + + // Relate existing document to new document + static::getDatabase()->updateDocument( + 'person', + $person1->getId(), + $person1->setAttribute('library', 'library4') + ); + + // Relate existing document to new document as nested data + static::getDatabase()->updateDocument( + 'person', + $person1->getId(), + $person1->setAttribute('library', $library4) + ); + + // Rename relationship key + static::getDatabase()->updateRelationship( + collection: 'person', + id: 'library', + newKey: 'newLibrary' + ); + + // Get document with again + $person = static::getDatabase()->getDocument('person', 'person1'); + $library = $person->getAttribute('newLibrary'); + $this->assertEquals('library4', $library['$id']); + + // Create person with no relationship + static::getDatabase()->createDocument('person', new Document([ + '$id' => 'person4', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Person 4', + ])); + + // Can delete parent document with no relation with on delete set to restrict + $deleted = static::getDatabase()->deleteDocument('person', 'person4'); + $this->assertEquals(true, $deleted); + + $person4 = static::getDatabase()->getDocument('person', 'person4'); + $this->assertEquals(true, $person4->isEmpty()); + + // Cannot delete document while still related to another with on delete set to restrict + try { + static::getDatabase()->deleteDocument('person', 'person1'); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); + } + + // Can delete child document while still related to another with on delete set to restrict + $person5 = static::getDatabase()->createDocument('person', new Document([ + '$id' => 'person5', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Person 5', + 'newLibrary' => [ + '$id' => 'library5', + '$permissions' => [ + Permission::read(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Library 5', + 'area' => 'Area 5', + ], + ])); + $deleted = static::getDatabase()->deleteDocument('library', 'library5'); + $this->assertEquals(true, $deleted); + $person5 = static::getDatabase()->getDocument('person', 'person5'); + $this->assertEquals(null, $person5->getAttribute('newLibrary')); + + // Change on delete to set null + static::getDatabase()->updateRelationship( + collection: 'person', + id: 'newLibrary', + onDelete: Database::RELATION_MUTATE_SET_NULL + ); + + // Delete parent, no effect on children for one-way + static::getDatabase()->deleteDocument('person', 'person1'); + + // Delete child, set parent relating attribute to null for one-way + static::getDatabase()->deleteDocument('library', 'library2'); + + // Check relation was set to null + $person2 = static::getDatabase()->getDocument('person', 'person2'); + $this->assertEquals(null, $person2->getAttribute('newLibrary', '')); + + // Relate to another document + static::getDatabase()->updateDocument( + 'person', + $person2->getId(), + $person2->setAttribute('newLibrary', 'library4') + ); + + // Change on delete to cascade + static::getDatabase()->updateRelationship( + collection: 'person', + id: 'newLibrary', + onDelete: Database::RELATION_MUTATE_CASCADE + ); + + // Delete parent, will delete child + static::getDatabase()->deleteDocument('person', 'person2'); + + // Check parent and child were deleted + $person = static::getDatabase()->getDocument('person', 'person2'); + $this->assertEquals(true, $person->isEmpty()); + + $library = static::getDatabase()->getDocument('library', 'library4'); + $this->assertEquals(true, $library->isEmpty()); + + // Delete relationship + static::getDatabase()->deleteRelationship( + 'person', + 'newLibrary' + ); + + // Check parent doesn't have relationship anymore + $person = static::getDatabase()->getDocument('person', 'person1'); + $library = $person->getAttribute('newLibrary', ''); + $this->assertEquals(null, $library); + } + + /** + * @throws AuthorizationException + * @throws LimitException + * @throws DuplicateException + * @throws StructureException + * @throws Throwable + */ + public function testOneToOneTwoWayRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('country'); + static::getDatabase()->createCollection('city'); + + static::getDatabase()->createAttribute('country', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('city', 'code', Database::VAR_STRING, 3, true); + static::getDatabase()->createAttribute('city', 'name', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'country', + relatedCollection: 'city', + type: Database::RELATION_ONE_TO_ONE, + twoWay: true + ); + + $collection = static::getDatabase()->getCollection('country'); + $attributes = $collection->getAttribute('attributes', []); + foreach ($attributes as $attribute) { + if ($attribute['key'] === 'city') { + $this->assertEquals('relationship', $attribute['type']); + $this->assertEquals('city', $attribute['$id']); + $this->assertEquals('city', $attribute['key']); + $this->assertEquals('city', $attribute['options']['relatedCollection']); + $this->assertEquals(Database::RELATION_ONE_TO_ONE, $attribute['options']['relationType']); + $this->assertEquals(true, $attribute['options']['twoWay']); + $this->assertEquals('country', $attribute['options']['twoWayKey']); + } + } + + $collection = static::getDatabase()->getCollection('city'); + $attributes = $collection->getAttribute('attributes', []); + foreach ($attributes as $attribute) { + if ($attribute['key'] === 'country') { + $this->assertEquals('relationship', $attribute['type']); + $this->assertEquals('country', $attribute['$id']); + $this->assertEquals('country', $attribute['key']); + $this->assertEquals('country', $attribute['options']['relatedCollection']); + $this->assertEquals(Database::RELATION_ONE_TO_ONE, $attribute['options']['relationType']); + $this->assertEquals(true, $attribute['options']['twoWay']); + $this->assertEquals('city', $attribute['options']['twoWayKey']); + } + } + + // Create document with relationship with nested data + $doc = new Document([ + '$id' => 'country1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'England', + 'city' => [ + '$id' => 'city1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'London', + 'code' => 'LON', + ], + ]); + + static::getDatabase()->createDocument('country', $doc); + $country1 = static::getDatabase()->getDocument('country', 'country1'); + $this->assertEquals('London', $country1->getAttribute('city')->getAttribute('name')); + + // Update a document with non existing related document. It should not get added to the list. + static::getDatabase()->updateDocument('country', 'country1', (new Document($doc->getArrayCopy()))->setAttribute('city', 'no-city')); + + $country1Document = static::getDatabase()->getDocument('country', 'country1'); + // Assert document does not contain non existing relation document. + $this->assertEquals(null, $country1Document->getAttribute('city')); + static::getDatabase()->updateDocument('country', 'country1', (new Document($doc->getArrayCopy()))->setAttribute('city', 'city1')); + try { + static::getDatabase()->deleteDocument('country', 'country1'); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertInstanceOf(RestrictedException::class, $e); + } + + $this->assertTrue(static::getDatabase()->deleteDocument('city', 'city1')); + + $city1 = static::getDatabase()->getDocument('city', 'city1'); + $this->assertTrue($city1->isEmpty()); + + $country1 = static::getDatabase()->getDocument('country', 'country1'); + $this->assertTrue($country1->getAttribute('city')->isEmpty()); + + $this->assertTrue(static::getDatabase()->deleteDocument('country', 'country1')); + + static::getDatabase()->createDocument('country', $doc); + $country1 = static::getDatabase()->getDocument('country', 'country1'); + $this->assertEquals('London', $country1->getAttribute('city')->getAttribute('name')); + + // Create document with relationship with related ID + static::getDatabase()->createDocument('city', new Document([ + '$id' => 'city2', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Paris', + 'code' => 'PAR', + ])); + static::getDatabase()->createDocument('country', new Document([ + '$id' => 'country2', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'France', + 'city' => 'city2', + ])); + + // Create from child side + static::getDatabase()->createDocument('city', new Document([ + '$id' => 'city3', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Christchurch', + 'code' => 'CHC', + 'country' => [ + '$id' => 'country3', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'New Zealand', + ], + ])); + static::getDatabase()->createDocument('country', new Document([ + '$id' => 'country4', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Australia', + ])); + static::getDatabase()->createDocument('city', new Document([ + '$id' => 'city4', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Sydney', + 'code' => 'SYD', + 'country' => 'country4', + ])); + + // Get document with relationship + $city = static::getDatabase()->getDocument('city', 'city1'); + $country = $city->getAttribute('country'); + $this->assertEquals('country1', $country['$id']); + $this->assertArrayNotHasKey('city', $country); + + $city = static::getDatabase()->getDocument('city', 'city2'); + $country = $city->getAttribute('country'); + $this->assertEquals('country2', $country['$id']); + $this->assertArrayNotHasKey('city', $country); + + $city = static::getDatabase()->getDocument('city', 'city3'); + $country = $city->getAttribute('country'); + $this->assertEquals('country3', $country['$id']); + $this->assertArrayNotHasKey('city', $country); + + $city = static::getDatabase()->getDocument('city', 'city4'); + $country = $city->getAttribute('country'); + $this->assertEquals('country4', $country['$id']); + $this->assertArrayNotHasKey('city', $country); + + // Get inverse document with relationship + $country = static::getDatabase()->getDocument('country', 'country1'); + $city = $country->getAttribute('city'); + $this->assertEquals('city1', $city['$id']); + $this->assertArrayNotHasKey('country', $city); + + $country = static::getDatabase()->getDocument('country', 'country2'); + $city = $country->getAttribute('city'); + $this->assertEquals('city2', $city['$id']); + $this->assertArrayNotHasKey('country', $city); + + $country = static::getDatabase()->getDocument('country', 'country3'); + $city = $country->getAttribute('city'); + $this->assertEquals('city3', $city['$id']); + $this->assertArrayNotHasKey('country', $city); + + $country = static::getDatabase()->getDocument('country', 'country4'); + $city = $country->getAttribute('city'); + $this->assertEquals('city4', $city['$id']); + $this->assertArrayNotHasKey('country', $city); + + $countries = static::getDatabase()->find('country'); + + $this->assertEquals(4, \count($countries)); + + // Select related document attributes + $country = static::getDatabase()->findOne('country', [ + Query::select(['*', 'city.name']) + ]); + + if (!$country instanceof Document) { + throw new Exception('Country not found'); + } + + $this->assertEquals('London', $country->getAttribute('city')->getAttribute('name')); + $this->assertArrayNotHasKey('code', $country->getAttribute('city')); + + $country = static::getDatabase()->getDocument('country', 'country1', [ + Query::select(['*', 'city.name']) + ]); + + $this->assertEquals('London', $country->getAttribute('city')->getAttribute('name')); + $this->assertArrayNotHasKey('code', $country->getAttribute('city')); + + $country1 = static::getDatabase()->getDocument('country', 'country1'); + + // Update root document attribute without altering relationship + $country1 = static::getDatabase()->updateDocument( + 'country', + $country1->getId(), + $country1->setAttribute('name', 'Country 1 Updated') + ); + + $this->assertEquals('Country 1 Updated', $country1->getAttribute('name')); + $country1 = static::getDatabase()->getDocument('country', 'country1'); + $this->assertEquals('Country 1 Updated', $country1->getAttribute('name')); + + $city2 = static::getDatabase()->getDocument('city', 'city2'); + + // Update inverse root document attribute without altering relationship + $city2 = static::getDatabase()->updateDocument( + 'city', + $city2->getId(), + $city2->setAttribute('name', 'City 2 Updated') + ); + + $this->assertEquals('City 2 Updated', $city2->getAttribute('name')); + $city2 = static::getDatabase()->getDocument('city', 'city2'); + $this->assertEquals('City 2 Updated', $city2->getAttribute('name')); + + // Update nested document attribute + $country1 = static::getDatabase()->updateDocument( + 'country', + $country1->getId(), + $country1->setAttribute( + 'city', + $country1 + ->getAttribute('city') + ->setAttribute('name', 'City 1 Updated') + ) + ); + + $this->assertEquals('City 1 Updated', $country1->getAttribute('city')->getAttribute('name')); + $country1 = static::getDatabase()->getDocument('country', 'country1'); + $this->assertEquals('City 1 Updated', $country1->getAttribute('city')->getAttribute('name')); + + // Update inverse nested document attribute + $city2 = static::getDatabase()->updateDocument( + 'city', + $city2->getId(), + $city2->setAttribute( + 'country', + $city2 + ->getAttribute('country') + ->setAttribute('name', 'Country 2 Updated') + ) + ); + + $this->assertEquals('Country 2 Updated', $city2->getAttribute('country')->getAttribute('name')); + $city2 = static::getDatabase()->getDocument('city', 'city2'); + $this->assertEquals('Country 2 Updated', $city2->getAttribute('country')->getAttribute('name')); + + // Create new document with no relationship + $country5 = static::getDatabase()->createDocument('country', new Document([ + '$id' => 'country5', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Country 5', + ])); + + // Update to relate to created document + $country5 = static::getDatabase()->updateDocument( + 'country', + $country5->getId(), + $country5->setAttribute('city', new Document([ + '$id' => 'city5', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + 'name' => 'City 5', + 'code' => 'C5', + ])) + ); + + $this->assertEquals('city5', $country5->getAttribute('city')['$id']); + $country5 = static::getDatabase()->getDocument('country', 'country5'); + $this->assertEquals('city5', $country5->getAttribute('city')['$id']); + + // Create new document with no relationship + $city6 = static::getDatabase()->createDocument('city', new Document([ + '$id' => 'city6', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'City6', + 'code' => 'C6', + ])); + + // Update to relate to created document + $city6 = static::getDatabase()->updateDocument( + 'city', + $city6->getId(), + $city6->setAttribute('country', new Document([ + '$id' => 'country6', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + 'name' => 'Country 6', + ])) + ); + + $this->assertEquals('country6', $city6->getAttribute('country')['$id']); + $city6 = static::getDatabase()->getDocument('city', 'city6'); + $this->assertEquals('country6', $city6->getAttribute('country')['$id']); + + // One to one can't relate to multiple documents, unique index throws duplicate + try { + static::getDatabase()->updateDocument( + 'country', + $country1->getId(), + $country1->setAttribute('city', 'city2') + ); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertInstanceOf(DuplicateException::class, $e); + } + + $city1 = static::getDatabase()->getDocument('city', 'city1'); + + // Set relationship to null + $city1 = static::getDatabase()->updateDocument( + 'city', + $city1->getId(), + $city1->setAttribute('country', null) + ); + + $this->assertEquals(null, $city1->getAttribute('country')); + $city1 = static::getDatabase()->getDocument('city', 'city1'); + $this->assertEquals(null, $city1->getAttribute('country')); + + // Create a new city with no relation + $city7 = static::getDatabase()->createDocument('city', new Document([ + '$id' => 'city7', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Copenhagen', + 'code' => 'CPH', + ])); + + // Update document with relation to new document + static::getDatabase()->updateDocument( + 'country', + $country1->getId(), + $country1->setAttribute('city', 'city7') + ); + + // Relate existing document to new document as nested data + static::getDatabase()->updateDocument( + 'country', + $country1->getId(), + $country1->setAttribute('city', $city7) + ); + + // Create a new country with no relation + static::getDatabase()->createDocument('country', new Document([ + '$id' => 'country7', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Denmark' + ])); + + // Update inverse document with new related document + static::getDatabase()->updateDocument( + 'city', + $city1->getId(), + $city1->setAttribute('country', 'country7') + ); + + // Rename relationship keys on both sides + static::getDatabase()->updateRelationship( + 'country', + 'city', + 'newCity', + 'newCountry' + ); + + // Get document with new relationship key + $city = static::getDatabase()->getDocument('city', 'city1'); + $country = $city->getAttribute('newCountry'); + $this->assertEquals('country7', $country['$id']); + + // Get inverse document with new relationship key + $country = static::getDatabase()->getDocument('country', 'country7'); + $city = $country->getAttribute('newCity'); + $this->assertEquals('city1', $city['$id']); + + // Create a new country with no relation + static::getDatabase()->createDocument('country', new Document([ + '$id' => 'country8', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Denmark' + ])); + + // Can delete parent document with no relation with on delete set to restrict + $deleted = static::getDatabase()->deleteDocument('country', 'country8'); + $this->assertEquals(1, $deleted); + + $country8 = static::getDatabase()->getDocument('country', 'country8'); + $this->assertEquals(true, $country8->isEmpty()); + + + // Cannot delete document while still related to another with on delete set to restrict + try { + static::getDatabase()->deleteDocument('country', 'country1'); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); + } + + // Change on delete to set null + static::getDatabase()->updateRelationship( + collection: 'country', + id: 'newCity', + onDelete: Database::RELATION_MUTATE_SET_NULL + ); + + // Delete parent, will set child relationship to null for two-way + static::getDatabase()->deleteDocument('country', 'country1'); + + // Check relation was set to null + $city7 = static::getDatabase()->getDocument('city', 'city7'); + $this->assertEquals(null, $city7->getAttribute('country', '')); + + // Delete child, set parent relationship to null for two-way + static::getDatabase()->deleteDocument('city', 'city2'); + + // Check relation was set to null + $country2 = static::getDatabase()->getDocument('country', 'country2'); + $this->assertEquals(null, $country2->getAttribute('city', '')); + + // Relate again + static::getDatabase()->updateDocument( + 'city', + $city7->getId(), + $city7->setAttribute('newCountry', 'country2') + ); + + // Change on delete to cascade + static::getDatabase()->updateRelationship( + collection: 'country', + id: 'newCity', + onDelete: Database::RELATION_MUTATE_CASCADE + ); + + // Delete parent, will delete child + static::getDatabase()->deleteDocument('country', 'country7'); + + // Check parent and child were deleted + $library = static::getDatabase()->getDocument('country', 'country7'); + $this->assertEquals(true, $library->isEmpty()); + + $library = static::getDatabase()->getDocument('city', 'city1'); + $this->assertEquals(true, $library->isEmpty()); + + // Delete child, will delete parent for two-way + static::getDatabase()->deleteDocument('city', 'city7'); + + // Check parent and child were deleted + $library = static::getDatabase()->getDocument('city', 'city7'); + $this->assertEquals(true, $library->isEmpty()); + + $library = static::getDatabase()->getDocument('country', 'country2'); + $this->assertEquals(true, $library->isEmpty()); + + // Create new document to check after deleting relationship + static::getDatabase()->createDocument('city', new Document([ + '$id' => 'city7', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Munich', + 'code' => 'MUC', + 'newCountry' => [ + '$id' => 'country7', + 'name' => 'Germany' + ] + ])); + + // Delete relationship + static::getDatabase()->deleteRelationship( + 'country', + 'newCity' + ); + + // Try to get document again + $country = static::getDatabase()->getDocument('country', 'country4'); + $city = $country->getAttribute('newCity'); + $this->assertEquals(null, $city); + + // Try to get inverse document again + $city = static::getDatabase()->getDocument('city', 'city7'); + $country = $city->getAttribute('newCountry'); + $this->assertEquals(null, $country); + } + + public function testIdenticalTwoWayKeyRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('parent'); + static::getDatabase()->createCollection('child'); + + static::getDatabase()->createRelationship( + collection: 'parent', + relatedCollection: 'child', + type: Database::RELATION_ONE_TO_ONE, + id: 'child1' + ); + + try { + static::getDatabase()->createRelationship( + collection: 'parent', + relatedCollection: 'child', + type: Database::RELATION_ONE_TO_MANY, + id: 'children', + ); + $this->fail('Failed to throw Exception'); + } catch (Exception $e) { + $this->assertEquals('Related attribute already exists', $e->getMessage()); + } + + static::getDatabase()->createRelationship( + collection: 'parent', + relatedCollection: 'child', + type: Database::RELATION_ONE_TO_MANY, + id: 'children', + twoWayKey: 'parent_id' + ); + + $collection = static::getDatabase()->getCollection('parent'); + $attributes = $collection->getAttribute('attributes', []); + foreach ($attributes as $attribute) { + if ($attribute['key'] === 'child1') { + $this->assertEquals('parent', $attribute['options']['twoWayKey']); + } + + if ($attribute['key'] === 'children') { + $this->assertEquals('parent_id', $attribute['options']['twoWayKey']); + } + } + + static::getDatabase()->createDocument('parent', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'child1' => [ + '$id' => 'foo', + '$permissions' => [Permission::read(Role::any())], + ], + 'children' => [ + [ + '$id' => 'bar', + '$permissions' => [Permission::read(Role::any())], + ], + ], + ])); + + $documents = static::getDatabase()->find('parent', []); + $document = array_pop($documents); + $this->assertArrayHasKey('child1', $document); + $this->assertEquals('foo', $document->getAttribute('child1')->getId()); + $this->assertArrayHasKey('children', $document); + $this->assertEquals('bar', $document->getAttribute('children')[0]->getId()); + + try { + static::getDatabase()->updateRelationship( + collection: 'parent', + id: 'children', + newKey: 'child1' + ); + $this->fail('Failed to throw Exception'); + } catch (Exception $e) { + $this->assertEquals('Attribute already exists', $e->getMessage()); + } + + try { + static::getDatabase()->updateRelationship( + collection: 'parent', + id: 'children', + newTwoWayKey: 'parent' + ); + $this->fail('Failed to throw Exception'); + } catch (Exception $e) { + $this->assertEquals('Related attribute already exists', $e->getMessage()); + } + } + + public function testOneToManyOneWayRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('artist'); + static::getDatabase()->createCollection('album'); + + static::getDatabase()->createAttribute('artist', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('album', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('album', 'price', Database::VAR_FLOAT, 0, true); + + static::getDatabase()->createRelationship( + collection: 'artist', + relatedCollection: 'album', + type: Database::RELATION_ONE_TO_MANY, + id: 'albums' + ); + + // Check metadata for collection + $collection = static::getDatabase()->getCollection('artist'); + $attributes = $collection->getAttribute('attributes', []); + + foreach ($attributes as $attribute) { + if ($attribute['key'] === 'albums') { + $this->assertEquals('relationship', $attribute['type']); + $this->assertEquals('albums', $attribute['$id']); + $this->assertEquals('albums', $attribute['key']); + $this->assertEquals('album', $attribute['options']['relatedCollection']); + $this->assertEquals(Database::RELATION_ONE_TO_MANY, $attribute['options']['relationType']); + $this->assertEquals(false, $attribute['options']['twoWay']); + $this->assertEquals('artist', $attribute['options']['twoWayKey']); + } + } + + // Create document with relationship with nested data + $artist1 = static::getDatabase()->createDocument('artist', new Document([ + '$id' => 'artist1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Artist 1', + 'albums' => [ + [ + '$id' => 'album1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()) + ], + 'name' => 'Album 1', + 'price' => 9.99, + ], + ], + ])); + + // Update a document with non existing related document. It should not get added to the list. + static::getDatabase()->updateDocument('artist', 'artist1', $artist1->setAttribute('albums', ['album1', 'no-album'])); + + $artist1Document = static::getDatabase()->getDocument('artist', 'artist1'); + // Assert document does not contain non existing relation document. + $this->assertEquals(1, \count($artist1Document->getAttribute('albums'))); + + // Create document with relationship with related ID + static::getDatabase()->createDocument('album', new Document([ + '$id' => 'album2', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Album 2', + 'price' => 19.99, + ])); + static::getDatabase()->createDocument('artist', new Document([ + '$id' => 'artist2', + '$permissions' => [ + Permission::read(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Artist 2', + 'albums' => [ + 'album2', + [ + '$id' => 'album33', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Album 3', + 'price' => 33.33, + ] + ] + ])); + + $documents = static::getDatabase()->find('artist', [ + Query::select(['name']), + Query::limit(1) + ]); + $this->assertArrayNotHasKey('albums', $documents[0]); + + // Get document with relationship + $artist = static::getDatabase()->getDocument('artist', 'artist1'); + $albums = $artist->getAttribute('albums', []); + $this->assertEquals('album1', $albums[0]['$id']); + $this->assertArrayNotHasKey('artist', $albums[0]); + + $artist = static::getDatabase()->getDocument('artist', 'artist2'); + $albums = $artist->getAttribute('albums', []); + $this->assertEquals('album2', $albums[0]['$id']); + $this->assertArrayNotHasKey('artist', $albums[0]); + $this->assertEquals('album33', $albums[1]['$id']); + $this->assertCount(2, $albums); + + // Get related document + $album = static::getDatabase()->getDocument('album', 'album1'); + $this->assertArrayNotHasKey('artist', $album); + + $album = static::getDatabase()->getDocument('album', 'album2'); + $this->assertArrayNotHasKey('artist', $album); + + $artists = static::getDatabase()->find('artist'); + + $this->assertEquals(2, \count($artists)); + + // Select related document attributes + $artist = static::getDatabase()->findOne('artist', [ + Query::select(['*', 'albums.name']) + ]); + + if (!$artist instanceof Document) { + $this->fail('Artist not found'); + } + + $this->assertEquals('Album 1', $artist->getAttribute('albums')[0]->getAttribute('name')); + $this->assertArrayNotHasKey('price', $artist->getAttribute('albums')[0]); + + $artist = static::getDatabase()->getDocument('artist', 'artist1', [ + Query::select(['*', 'albums.name']) + ]); + + $this->assertEquals('Album 1', $artist->getAttribute('albums')[0]->getAttribute('name')); + $this->assertArrayNotHasKey('price', $artist->getAttribute('albums')[0]); + + // Update root document attribute without altering relationship + $artist1 = static::getDatabase()->updateDocument( + 'artist', + $artist1->getId(), + $artist1->setAttribute('name', 'Artist 1 Updated') + ); + + $this->assertEquals('Artist 1 Updated', $artist1->getAttribute('name')); + $artist1 = static::getDatabase()->getDocument('artist', 'artist1'); + $this->assertEquals('Artist 1 Updated', $artist1->getAttribute('name')); + + // Update nested document attribute + $albums = $artist1->getAttribute('albums', []); + $albums[0]->setAttribute('name', 'Album 1 Updated'); + + $artist1 = static::getDatabase()->updateDocument( + 'artist', + $artist1->getId(), + $artist1->setAttribute('albums', $albums) + ); + + $this->assertEquals('Album 1 Updated', $artist1->getAttribute('albums')[0]->getAttribute('name')); + $artist1 = static::getDatabase()->getDocument('artist', 'artist1'); + $this->assertEquals('Album 1 Updated', $artist1->getAttribute('albums')[0]->getAttribute('name')); + + $albumId = $artist1->getAttribute('albums')[0]->getAttribute('$id'); + $albumDocument = static::getDatabase()->getDocument('album', $albumId); + $albumDocument->setAttribute('name', 'Album 1 Updated!!!'); + static::getDatabase()->updateDocument('album', $albumDocument->getId(), $albumDocument); + $albumDocument = static::getDatabase()->getDocument('album', $albumDocument->getId()); + $artist1 = static::getDatabase()->getDocument('artist', $artist1->getId()); + + $this->assertEquals('Album 1 Updated!!!', $albumDocument['name']); + $this->assertEquals($albumDocument->getId(), $artist1->getAttribute('albums')[0]->getId()); + //Todo: This is failing + $this->assertEquals($albumDocument->getAttribute('name'), $artist1->getAttribute('albums')[0]->getAttribute('name')); + + // Create new document with no relationship + $artist3 = static::getDatabase()->createDocument('artist', new Document([ + '$id' => 'artist3', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Artist 3', + ])); + + // Update to relate to created document + $artist3 = static::getDatabase()->updateDocument( + 'artist', + $artist3->getId(), + $artist3->setAttribute('albums', [new Document([ + '$id' => 'album3', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Album 3', + 'price' => 29.99, + ])]) + ); + + $this->assertEquals('Album 3', $artist3->getAttribute('albums')[0]->getAttribute('name')); + $artist3 = static::getDatabase()->getDocument('artist', 'artist3'); + $this->assertEquals('Album 3', $artist3->getAttribute('albums')[0]->getAttribute('name')); + + // Update document with new related documents, will remove existing relations + static::getDatabase()->updateDocument( + 'artist', + $artist1->getId(), + $artist1->setAttribute('albums', ['album2']) + ); + + // Update document with new related documents, will remove existing relations + static::getDatabase()->updateDocument( + 'artist', + $artist1->getId(), + $artist1->setAttribute('albums', ['album1', 'album2']) + ); + + // Rename relationship key + static::getDatabase()->updateRelationship( + 'artist', + 'albums', + 'newAlbums' + ); + + // Get document with new relationship key + $artist = static::getDatabase()->getDocument('artist', 'artist1'); + $albums = $artist->getAttribute('newAlbums'); + $this->assertEquals('album1', $albums[0]['$id']); + + // Create new document with no relationship + static::getDatabase()->createDocument('artist', new Document([ + '$id' => 'artist4', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Artist 4', + ])); + + // Can delete document with no relationship when on delete is set to restrict + $deleted = static::getDatabase()->deleteDocument('artist', 'artist4'); + $this->assertEquals(true, $deleted); + + $artist4 = static::getDatabase()->getDocument('artist', 'artist4'); + $this->assertEquals(true, $artist4->isEmpty()); + + // Try to delete document while still related to another with on delete: restrict + try { + static::getDatabase()->deleteDocument('artist', 'artist1'); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); + } + + // Change on delete to set null + static::getDatabase()->updateRelationship( + collection: 'artist', + id: 'newAlbums', + onDelete: Database::RELATION_MUTATE_SET_NULL + ); + + // Delete parent, set child relationship to null + static::getDatabase()->deleteDocument('artist', 'artist1'); + + // Check relation was set to null + $album2 = static::getDatabase()->getDocument('album', 'album2'); + $this->assertEquals(null, $album2->getAttribute('artist', '')); + + // Relate again + static::getDatabase()->updateDocument( + 'album', + $album2->getId(), + $album2->setAttribute('artist', 'artist2') + ); + + // Change on delete to cascade + static::getDatabase()->updateRelationship( + collection: 'artist', + id: 'newAlbums', + onDelete: Database::RELATION_MUTATE_CASCADE + ); + + // Delete parent, will delete child + static::getDatabase()->deleteDocument('artist', 'artist2'); + + // Check parent and child were deleted + $library = static::getDatabase()->getDocument('artist', 'artist2'); + $this->assertEquals(true, $library->isEmpty()); + + $library = static::getDatabase()->getDocument('album', 'album2'); + $this->assertEquals(true, $library->isEmpty()); + + $albums = []; + for ($i = 1 ; $i <= 50 ; $i++) { + $albums[] = [ + '$id' => 'album_' . $i, + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'album ' . $i . ' ' . 'Artist 100', + 'price' => 100, + ]; + } + + $artist = static::getDatabase()->createDocument('artist', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Artist 100', + 'newAlbums' => $albums + ])); + + $artist = static::getDatabase()->getDocument('artist', $artist->getId()); + $this->assertCount(50, $artist->getAttribute('newAlbums')); + + $albums = static::getDatabase()->find('album', [ + Query::equal('artist', [$artist->getId()]), + Query::limit(999) + ]); + + $this->assertCount(50, $albums); + + $count = static::getDatabase()->count('album', [ + Query::equal('artist', [$artist->getId()]), + ]); + + $this->assertEquals(50, $count); + + static::getDatabase()->deleteDocument('album', 'album_1'); + $artist = static::getDatabase()->getDocument('artist', $artist->getId()); + $this->assertCount(49, $artist->getAttribute('newAlbums')); + + static::getDatabase()->deleteDocument('artist', $artist->getId()); + + $albums = static::getDatabase()->find('album', [ + Query::equal('artist', [$artist->getId()]), + Query::limit(999) + ]); + + $this->assertCount(0, $albums); + + // Delete relationship + static::getDatabase()->deleteRelationship( + 'artist', + 'newAlbums' + ); + + // Try to get document again + $artist = static::getDatabase()->getDocument('artist', 'artist1'); + $albums = $artist->getAttribute('newAlbums', ''); + $this->assertEquals(null, $albums); + } + + public function testOneToManyTwoWayRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('customer'); + static::getDatabase()->createCollection('account'); + + static::getDatabase()->createAttribute('customer', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('account', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('account', 'number', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'customer', + relatedCollection: 'account', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + id: 'accounts' + ); + + // Check metadata for collection + $collection = static::getDatabase()->getCollection('customer'); + $attributes = $collection->getAttribute('attributes', []); + foreach ($attributes as $attribute) { + if ($attribute['key'] === 'accounts') { + $this->assertEquals('relationship', $attribute['type']); + $this->assertEquals('accounts', $attribute['$id']); + $this->assertEquals('accounts', $attribute['key']); + $this->assertEquals('account', $attribute['options']['relatedCollection']); + $this->assertEquals(Database::RELATION_ONE_TO_MANY, $attribute['options']['relationType']); + $this->assertEquals(true, $attribute['options']['twoWay']); + $this->assertEquals('customer', $attribute['options']['twoWayKey']); + } + } + + // Check metadata for related collection + $collection = static::getDatabase()->getCollection('account'); + $attributes = $collection->getAttribute('attributes', []); + foreach ($attributes as $attribute) { + if ($attribute['key'] === 'customer') { + $this->assertEquals('relationship', $attribute['type']); + $this->assertEquals('customer', $attribute['$id']); + $this->assertEquals('customer', $attribute['key']); + $this->assertEquals('customer', $attribute['options']['relatedCollection']); + $this->assertEquals(Database::RELATION_ONE_TO_MANY, $attribute['options']['relationType']); + $this->assertEquals(true, $attribute['options']['twoWay']); + $this->assertEquals('accounts', $attribute['options']['twoWayKey']); + } + } + + // Create document with relationship with nested data + $customer1 = static::getDatabase()->createDocument('customer', new Document([ + '$id' => 'customer1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Customer 1', + 'accounts' => [ + [ + '$id' => 'account1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Account 1', + 'number' => '123456789', + ], + ], + ])); + + // Update a document with non existing related document. It should not get added to the list. + static::getDatabase()->updateDocument('customer', 'customer1', $customer1->setAttribute('accounts', ['account1','no-account'])); + + $customer1Document = static::getDatabase()->getDocument('customer', 'customer1'); + // Assert document does not contain non existing relation document. + $this->assertEquals(1, \count($customer1Document->getAttribute('accounts'))); + + // Create document with relationship with related ID + $account2 = static::getDatabase()->createDocument('account', new Document([ + '$id' => 'account2', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Account 2', + 'number' => '987654321', + ])); + static::getDatabase()->createDocument('customer', new Document([ + '$id' => 'customer2', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Customer 2', + 'accounts' => [ + 'account2' + ] + ])); + + // Create from child side + static::getDatabase()->createDocument('account', new Document([ + '$id' => 'account3', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Account 3', + 'number' => '123456789', + 'customer' => [ + '$id' => 'customer3', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Customer 3' + ] + ])); + static::getDatabase()->createDocument('customer', new Document([ + '$id' => 'customer4', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Customer 4', + ])); + static::getDatabase()->createDocument('account', new Document([ + '$id' => 'account4', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Account 4', + 'number' => '123456789', + 'customer' => 'customer4' + ])); + + // Get documents with relationship + $customer = static::getDatabase()->getDocument('customer', 'customer1'); + $accounts = $customer->getAttribute('accounts', []); + $this->assertEquals('account1', $accounts[0]['$id']); + $this->assertArrayNotHasKey('customer', $accounts[0]); + + $customer = static::getDatabase()->getDocument('customer', 'customer2'); + $accounts = $customer->getAttribute('accounts', []); + $this->assertEquals('account2', $accounts[0]['$id']); + $this->assertArrayNotHasKey('customer', $accounts[0]); + + $customer = static::getDatabase()->getDocument('customer', 'customer3'); + $accounts = $customer->getAttribute('accounts', []); + $this->assertEquals('account3', $accounts[0]['$id']); + $this->assertArrayNotHasKey('customer', $accounts[0]); + + $customer = static::getDatabase()->getDocument('customer', 'customer4'); + $accounts = $customer->getAttribute('accounts', []); + $this->assertEquals('account4', $accounts[0]['$id']); + $this->assertArrayNotHasKey('customer', $accounts[0]); + + // Get related documents + $account = static::getDatabase()->getDocument('account', 'account1'); + $customer = $account->getAttribute('customer'); + $this->assertEquals('customer1', $customer['$id']); + $this->assertArrayNotHasKey('accounts', $customer); + + $account = static::getDatabase()->getDocument('account', 'account2'); + $customer = $account->getAttribute('customer'); + $this->assertEquals('customer2', $customer['$id']); + $this->assertArrayNotHasKey('accounts', $customer); + + $account = static::getDatabase()->getDocument('account', 'account3'); + $customer = $account->getAttribute('customer'); + $this->assertEquals('customer3', $customer['$id']); + $this->assertArrayNotHasKey('accounts', $customer); + + $account = static::getDatabase()->getDocument('account', 'account4'); + $customer = $account->getAttribute('customer'); + $this->assertEquals('customer4', $customer['$id']); + $this->assertArrayNotHasKey('accounts', $customer); + + $customers = static::getDatabase()->find('customer'); + + $this->assertEquals(4, \count($customers)); + + // Select related document attributes + $customer = static::getDatabase()->findOne('customer', [ + Query::select(['*', 'accounts.name']) + ]); + + if (!$customer instanceof Document) { + throw new Exception('Customer not found'); + } + + $this->assertEquals('Account 1', $customer->getAttribute('accounts')[0]->getAttribute('name')); + $this->assertArrayNotHasKey('number', $customer->getAttribute('accounts')[0]); + + $customer = static::getDatabase()->getDocument('customer', 'customer1', [ + Query::select(['*', 'accounts.name']) + ]); + + $this->assertEquals('Account 1', $customer->getAttribute('accounts')[0]->getAttribute('name')); + $this->assertArrayNotHasKey('number', $customer->getAttribute('accounts')[0]); + + // Update root document attribute without altering relationship + $customer1 = static::getDatabase()->updateDocument( + 'customer', + $customer1->getId(), + $customer1->setAttribute('name', 'Customer 1 Updated') + ); + + $this->assertEquals('Customer 1 Updated', $customer1->getAttribute('name')); + $customer1 = static::getDatabase()->getDocument('customer', 'customer1'); + $this->assertEquals('Customer 1 Updated', $customer1->getAttribute('name')); + + $account2 = static::getDatabase()->getDocument('account', 'account2'); + + // Update inverse root document attribute without altering relationship + $account2 = static::getDatabase()->updateDocument( + 'account', + $account2->getId(), + $account2->setAttribute('name', 'Account 2 Updated') + ); + + $this->assertEquals('Account 2 Updated', $account2->getAttribute('name')); + $account2 = static::getDatabase()->getDocument('account', 'account2'); + $this->assertEquals('Account 2 Updated', $account2->getAttribute('name')); + + // Update nested document attribute + $accounts = $customer1->getAttribute('accounts', []); + $accounts[0]->setAttribute('name', 'Account 1 Updated'); + + $customer1 = static::getDatabase()->updateDocument( + 'customer', + $customer1->getId(), + $customer1->setAttribute('accounts', $accounts) + ); + + $this->assertEquals('Account 1 Updated', $customer1->getAttribute('accounts')[0]->getAttribute('name')); + $customer1 = static::getDatabase()->getDocument('customer', 'customer1'); + $this->assertEquals('Account 1 Updated', $customer1->getAttribute('accounts')[0]->getAttribute('name')); + + // Update inverse nested document attribute + $account2 = static::getDatabase()->updateDocument( + 'account', + $account2->getId(), + $account2->setAttribute( + 'customer', + $account2 + ->getAttribute('customer') + ->setAttribute('name', 'Customer 2 Updated') + ) + ); + + $this->assertEquals('Customer 2 Updated', $account2->getAttribute('customer')->getAttribute('name')); + $account2 = static::getDatabase()->getDocument('account', 'account2'); + $this->assertEquals('Customer 2 Updated', $account2->getAttribute('customer')->getAttribute('name')); + + // Create new document with no relationship + $customer5 = static::getDatabase()->createDocument('customer', new Document([ + '$id' => 'customer5', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Customer 5', + ])); + + // Update to relate to created document + $customer5 = static::getDatabase()->updateDocument( + 'customer', + $customer5->getId(), + $customer5->setAttribute('accounts', [new Document([ + '$id' => 'account5', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Account 5', + 'number' => '123456789', + ])]) + ); + + $this->assertEquals('Account 5', $customer5->getAttribute('accounts')[0]->getAttribute('name')); + $customer5 = static::getDatabase()->getDocument('customer', 'customer5'); + $this->assertEquals('Account 5', $customer5->getAttribute('accounts')[0]->getAttribute('name')); + + // Create new child document with no relationship + $account6 = static::getDatabase()->createDocument('account', new Document([ + '$id' => 'account6', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Account 6', + 'number' => '123456789', + ])); + + // Update inverse to relate to created document + $account6 = static::getDatabase()->updateDocument( + 'account', + $account6->getId(), + $account6->setAttribute('customer', new Document([ + '$id' => 'customer6', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Customer 6', + ])) + ); + + $this->assertEquals('Customer 6', $account6->getAttribute('customer')->getAttribute('name')); + $account6 = static::getDatabase()->getDocument('account', 'account6'); + $this->assertEquals('Customer 6', $account6->getAttribute('customer')->getAttribute('name')); + + // Update document with new related document, will remove existing relations + static::getDatabase()->updateDocument( + 'customer', + $customer1->getId(), + $customer1->setAttribute('accounts', ['account2']) + ); + + // Update document with new related document + static::getDatabase()->updateDocument( + 'customer', + $customer1->getId(), + $customer1->setAttribute('accounts', ['account1', 'account2']) + ); + + // Update inverse document + static::getDatabase()->updateDocument( + 'account', + $account2->getId(), + $account2->setAttribute('customer', 'customer2') + ); + + // Rename relationship keys on both sides + static::getDatabase()->updateRelationship( + 'customer', + 'accounts', + 'newAccounts', + 'newCustomer' + ); + + // Get document with new relationship key + $customer = static::getDatabase()->getDocument('customer', 'customer1'); + $accounts = $customer->getAttribute('newAccounts'); + $this->assertEquals('account1', $accounts[0]['$id']); + + // Get inverse document with new relationship key + $account = static::getDatabase()->getDocument('account', 'account1'); + $customer = $account->getAttribute('newCustomer'); + $this->assertEquals('customer1', $customer['$id']); + + // Create new document with no relationship + static::getDatabase()->createDocument('customer', new Document([ + '$id' => 'customer7', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Customer 7', + ])); + + // Can delete document with no relationship when on delete is set to restrict + $deleted = static::getDatabase()->deleteDocument('customer', 'customer7'); + $this->assertEquals(true, $deleted); + + $customer7 = static::getDatabase()->getDocument('customer', 'customer7'); + $this->assertEquals(true, $customer7->isEmpty()); + + // Try to delete document while still related to another with on delete: restrict + try { + static::getDatabase()->deleteDocument('customer', 'customer1'); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); + } + + // Change on delete to set null + static::getDatabase()->updateRelationship( + collection: 'customer', + id: 'newAccounts', + onDelete: Database::RELATION_MUTATE_SET_NULL + ); + + // Delete parent, set child relationship to null + static::getDatabase()->deleteDocument('customer', 'customer1'); + + // Check relation was set to null + $account1 = static::getDatabase()->getDocument('account', 'account1'); + $this->assertEquals(null, $account2->getAttribute('newCustomer', '')); + + // Relate again + static::getDatabase()->updateDocument( + 'account', + $account1->getId(), + $account1->setAttribute('newCustomer', 'customer2') + ); + + // Change on delete to cascade + static::getDatabase()->updateRelationship( + collection: 'customer', + id: 'newAccounts', + onDelete: Database::RELATION_MUTATE_CASCADE + ); + + // Delete parent, will delete child + static::getDatabase()->deleteDocument('customer', 'customer2'); + + // Check parent and child were deleted + $library = static::getDatabase()->getDocument('customer', 'customer2'); + $this->assertEquals(true, $library->isEmpty()); + + $library = static::getDatabase()->getDocument('account', 'account2'); + $this->assertEquals(true, $library->isEmpty()); + + // Delete relationship + static::getDatabase()->deleteRelationship( + 'customer', + 'newAccounts' + ); + + // Try to get document again + $customer = static::getDatabase()->getDocument('customer', 'customer1'); + $accounts = $customer->getAttribute('newAccounts'); + $this->assertEquals(null, $accounts); + + // Try to get inverse document again + $accounts = static::getDatabase()->getDocument('account', 'account1'); + $customer = $accounts->getAttribute('newCustomer'); + $this->assertEquals(null, $customer); + } + + public function testManyToOneOneWayRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('review'); + static::getDatabase()->createCollection('movie'); + + static::getDatabase()->createAttribute('review', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('movie', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('movie', 'length', Database::VAR_INTEGER, 0, true, formatOptions: ['min' => 0, 'max' => 999]); + static::getDatabase()->createAttribute('movie', 'date', Database::VAR_DATETIME, 0, false, filters: ['datetime']); + static::getDatabase()->createAttribute('review', 'date', Database::VAR_DATETIME, 0, false, filters: ['datetime']); + static::getDatabase()->createRelationship( + collection: 'review', + relatedCollection: 'movie', + type: Database::RELATION_MANY_TO_ONE, + twoWayKey: 'reviews' + ); + + // Check metadata for collection + $collection = static::getDatabase()->getCollection('review'); + $attributes = $collection->getAttribute('attributes', []); + foreach ($attributes as $attribute) { + if ($attribute['key'] === 'movie') { + $this->assertEquals('relationship', $attribute['type']); + $this->assertEquals('movie', $attribute['$id']); + $this->assertEquals('movie', $attribute['key']); + $this->assertEquals('movie', $attribute['options']['relatedCollection']); + $this->assertEquals(Database::RELATION_MANY_TO_ONE, $attribute['options']['relationType']); + $this->assertEquals(false, $attribute['options']['twoWay']); + $this->assertEquals('reviews', $attribute['options']['twoWayKey']); + } + } + + // Check metadata for related collection + $collection = static::getDatabase()->getCollection('movie'); + $attributes = $collection->getAttribute('attributes', []); + foreach ($attributes as $attribute) { + if ($attribute['key'] === 'reviews') { + $this->assertEquals('relationship', $attribute['type']); + $this->assertEquals('reviews', $attribute['$id']); + $this->assertEquals('reviews', $attribute['key']); + $this->assertEquals('review', $attribute['options']['relatedCollection']); + $this->assertEquals(Database::RELATION_MANY_TO_ONE, $attribute['options']['relationType']); + $this->assertEquals(false, $attribute['options']['twoWay']); + $this->assertEquals('movie', $attribute['options']['twoWayKey']); + } + } + + // Create document with relationship with nested data + $review1 = static::getDatabase()->createDocument('review', new Document([ + '$id' => 'review1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Review 1', + 'date' => '2023-04-03 10:35:27.390', + 'movie' => [ + '$id' => 'movie1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Movie 1', + 'date' => '2023-04-03 10:35:27.390', + 'length' => 120, + ], + ])); + + // Update a document with non existing related document. It should not get added to the list. + static::getDatabase()->updateDocument('review', 'review1', $review1->setAttribute('movie', 'no-movie')); + + $review1Document = static::getDatabase()->getDocument('review', 'review1'); + // Assert document does not contain non existing relation document. + $this->assertEquals(null, $review1Document->getAttribute('movie')); + + static::getDatabase()->updateDocument('review', 'review1', $review1->setAttribute('movie', 'movie1')); + + // Create document with relationship to existing document by ID + $review10 = static::getDatabase()->createDocument('review', new Document([ + '$id' => 'review10', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Review 10', + 'movie' => 'movie1', + 'date' => '2023-04-03 10:35:27.390', + ])); + + // Create document with relationship with related ID + static::getDatabase()->createDocument('movie', new Document([ + '$id' => 'movie2', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Movie 2', + 'length' => 90, + 'date' => '2023-04-03 10:35:27.390', + ])); + static::getDatabase()->createDocument('review', new Document([ + '$id' => 'review2', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Review 2', + 'movie' => 'movie2', + 'date' => '2023-04-03 10:35:27.390', + ])); + + // Get document with relationship + $review = static::getDatabase()->getDocument('review', 'review1'); + $movie = $review->getAttribute('movie', []); + $this->assertEquals('movie1', $movie['$id']); + $this->assertArrayNotHasKey('reviews', $movie); + + $documents = static::getDatabase()->find('review', [ + Query::select(['date', 'movie.date']) + ]); + + $this->assertCount(3, $documents); + + $document = $documents[0]; + $this->assertArrayHasKey('date', $document); + $this->assertArrayHasKey('movie', $document); + $this->assertArrayHasKey('date', $document->getAttribute('movie')); + $this->assertArrayNotHasKey('name', $document); + $this->assertEquals(29, strlen($document['date'])); // checks filter + $this->assertEquals(29, strlen($document['movie']['date'])); + + $review = static::getDatabase()->getDocument('review', 'review2'); + $movie = $review->getAttribute('movie', []); + $this->assertEquals('movie2', $movie['$id']); + $this->assertArrayNotHasKey('reviews', $movie); + + // Get related document + $movie = static::getDatabase()->getDocument('movie', 'movie1'); + $this->assertArrayNotHasKey('reviews', $movie); + + $movie = static::getDatabase()->getDocument('movie', 'movie2'); + $this->assertArrayNotHasKey('reviews', $movie); + + $reviews = static::getDatabase()->find('review'); + + $this->assertEquals(3, \count($reviews)); + + // Select related document attributes + $review = static::getDatabase()->findOne('review', [ + Query::select(['*', 'movie.name']) + ]); + + if (!$review instanceof Document) { + throw new Exception('Review not found'); + } + + $this->assertEquals('Movie 1', $review->getAttribute('movie')->getAttribute('name')); + $this->assertArrayNotHasKey('length', $review->getAttribute('movie')); + + $review = static::getDatabase()->getDocument('review', 'review1', [ + Query::select(['*', 'movie.name']) + ]); + + $this->assertEquals('Movie 1', $review->getAttribute('movie')->getAttribute('name')); + $this->assertArrayNotHasKey('length', $review->getAttribute('movie')); + + // Update root document attribute without altering relationship + $review1 = static::getDatabase()->updateDocument( + 'review', + $review1->getId(), + $review1->setAttribute('name', 'Review 1 Updated') + ); + + $this->assertEquals('Review 1 Updated', $review1->getAttribute('name')); + $review1 = static::getDatabase()->getDocument('review', 'review1'); + $this->assertEquals('Review 1 Updated', $review1->getAttribute('name')); + + // Update nested document attribute + $movie = $review1->getAttribute('movie'); + $movie->setAttribute('name', 'Movie 1 Updated'); + + $review1 = static::getDatabase()->updateDocument( + 'review', + $review1->getId(), + $review1->setAttribute('movie', $movie) + ); + + $this->assertEquals('Movie 1 Updated', $review1->getAttribute('movie')->getAttribute('name')); + $review1 = static::getDatabase()->getDocument('review', 'review1'); + $this->assertEquals('Movie 1 Updated', $review1->getAttribute('movie')->getAttribute('name')); + + // Create new document with no relationship + $review5 = static::getDatabase()->createDocument('review', new Document([ + '$id' => 'review5', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Review 5', + ])); + + // Update to relate to created document + $review5 = static::getDatabase()->updateDocument( + 'review', + $review5->getId(), + $review5->setAttribute('movie', new Document([ + '$id' => 'movie5', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Movie 5', + 'length' => 90, + ])) + ); + + $this->assertEquals('Movie 5', $review5->getAttribute('movie')->getAttribute('name')); + $review5 = static::getDatabase()->getDocument('review', 'review5'); + $this->assertEquals('Movie 5', $review5->getAttribute('movie')->getAttribute('name')); + + // Update document with new related document + static::getDatabase()->updateDocument( + 'review', + $review1->getId(), + $review1->setAttribute('movie', 'movie2') + ); + + // Rename relationship keys on both sides + static::getDatabase()->updateRelationship( + 'review', + 'movie', + 'newMovie', + ); + + // Get document with new relationship key + $review = static::getDatabase()->getDocument('review', 'review1'); + $movie = $review->getAttribute('newMovie'); + $this->assertEquals('movie2', $movie['$id']); + + // Reset values + $review1 = static::getDatabase()->getDocument('review', 'review1'); + + static::getDatabase()->updateDocument( + 'review', + $review1->getId(), + $review1->setAttribute('newMovie', 'movie1') + ); + + // Create new document with no relationship + static::getDatabase()->createDocument('movie', new Document([ + '$id' => 'movie3', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Movie 3', + 'length' => 90, + ])); + + // Can delete document with no relationship when on delete is set to restrict + $deleted = static::getDatabase()->deleteDocument('movie', 'movie3'); + $this->assertEquals(true, $deleted); + + $movie3 = static::getDatabase()->getDocument('movie', 'movie3'); + $this->assertEquals(true, $movie3->isEmpty()); + + // Try to delete document while still related to another with on delete: restrict + try { + static::getDatabase()->deleteDocument('movie', 'movie1'); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); + } + + // Change on delete to set null + static::getDatabase()->updateRelationship( + collection: 'review', + id: 'newMovie', + onDelete: Database::RELATION_MUTATE_SET_NULL + ); + + // Delete child, set parent relationship to null + static::getDatabase()->deleteDocument('movie', 'movie1'); + + // Check relation was set to null + $review1 = static::getDatabase()->getDocument('review', 'review1'); + $this->assertEquals(null, $review1->getAttribute('newMovie')); + + // Change on delete to cascade + static::getDatabase()->updateRelationship( + collection: 'review', + id: 'newMovie', + onDelete: Database::RELATION_MUTATE_CASCADE + ); + + // Delete child, will delete parent + static::getDatabase()->deleteDocument('movie', 'movie2'); + + // Check parent and child were deleted + $library = static::getDatabase()->getDocument('movie', 'movie2'); + $this->assertEquals(true, $library->isEmpty()); + + $library = static::getDatabase()->getDocument('review', 'review2'); + $this->assertEquals(true, $library->isEmpty()); + + + // Delete relationship + static::getDatabase()->deleteRelationship( + 'review', + 'newMovie' + ); + + // Try to get document again + $review = static::getDatabase()->getDocument('review', 'review1'); + $movie = $review->getAttribute('newMovie'); + $this->assertEquals(null, $movie); + } + + public function testManyToOneTwoWayRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('product'); + static::getDatabase()->createCollection('store'); + + static::getDatabase()->createAttribute('store', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('store', 'opensAt', Database::VAR_STRING, 5, true); + + static::getDatabase()->createAttribute( + collection: 'product', + id: 'name', + type: Database::VAR_STRING, + size: 255, + required: true + ); + + static::getDatabase()->createRelationship( + collection: 'product', + relatedCollection: 'store', + type: Database::RELATION_MANY_TO_ONE, + twoWay: true, + twoWayKey: 'products' + ); + + // Check metadata for collection + $collection = static::getDatabase()->getCollection('product'); + $attributes = $collection->getAttribute('attributes', []); + foreach ($attributes as $attribute) { + if ($attribute['key'] === 'store') { + $this->assertEquals('relationship', $attribute['type']); + $this->assertEquals('store', $attribute['$id']); + $this->assertEquals('store', $attribute['key']); + $this->assertEquals('store', $attribute['options']['relatedCollection']); + $this->assertEquals(Database::RELATION_MANY_TO_ONE, $attribute['options']['relationType']); + $this->assertEquals(true, $attribute['options']['twoWay']); + $this->assertEquals('products', $attribute['options']['twoWayKey']); + } + } + + // Check metadata for related collection + $collection = static::getDatabase()->getCollection('store'); + $attributes = $collection->getAttribute('attributes', []); + foreach ($attributes as $attribute) { + if ($attribute['key'] === 'products') { + $this->assertEquals('relationship', $attribute['type']); + $this->assertEquals('products', $attribute['$id']); + $this->assertEquals('products', $attribute['key']); + $this->assertEquals('product', $attribute['options']['relatedCollection']); + $this->assertEquals(Database::RELATION_MANY_TO_ONE, $attribute['options']['relationType']); + $this->assertEquals(true, $attribute['options']['twoWay']); + $this->assertEquals('store', $attribute['options']['twoWayKey']); + } + } + + // Create document with relationship with nested data + $product1 = static::getDatabase()->createDocument('product', new Document([ + '$id' => 'product1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Product 1', + 'store' => [ + '$id' => 'store1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Store 1', + 'opensAt' => '09:00', + ], + ])); + + // Update a document with non existing related document. It should not get added to the list. + static::getDatabase()->updateDocument('product', 'product1', $product1->setAttribute('store', 'no-store')); + + $product1Document = static::getDatabase()->getDocument('product', 'product1'); + // Assert document does not contain non existing relation document. + $this->assertEquals(null, $product1Document->getAttribute('store')); + + static::getDatabase()->updateDocument('product', 'product1', $product1->setAttribute('store', 'store1')); + + // Create document with relationship with related ID + static::getDatabase()->createDocument('store', new Document([ + '$id' => 'store2', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Store 2', + 'opensAt' => '09:30', + ])); + static::getDatabase()->createDocument('product', new Document([ + '$id' => 'product2', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Product 2', + 'store' => 'store2', + ])); + + // Create from child side + static::getDatabase()->createDocument('store', new Document([ + '$id' => 'store3', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Store 3', + 'opensAt' => '11:30', + 'products' => [ + [ + '$id' => 'product3', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Product 3', + ], + ], + ])); + + static::getDatabase()->createDocument('product', new Document([ + '$id' => 'product4', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Product 4', + ])); + static::getDatabase()->createDocument('store', new Document([ + '$id' => 'store4', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Store 4', + 'opensAt' => '11:30', + 'products' => [ + 'product4', + ], + ])); + + // Get document with relationship + $product = static::getDatabase()->getDocument('product', 'product1'); + $store = $product->getAttribute('store', []); + $this->assertEquals('store1', $store['$id']); + $this->assertArrayNotHasKey('products', $store); + + $product = static::getDatabase()->getDocument('product', 'product2'); + $store = $product->getAttribute('store', []); + $this->assertEquals('store2', $store['$id']); + $this->assertArrayNotHasKey('products', $store); + + $product = static::getDatabase()->getDocument('product', 'product3'); + $store = $product->getAttribute('store', []); + $this->assertEquals('store3', $store['$id']); + $this->assertArrayNotHasKey('products', $store); + + $product = static::getDatabase()->getDocument('product', 'product4'); + $store = $product->getAttribute('store', []); + $this->assertEquals('store4', $store['$id']); + $this->assertArrayNotHasKey('products', $store); + + // Get related document + $store = static::getDatabase()->getDocument('store', 'store1'); + $products = $store->getAttribute('products'); + $this->assertEquals('product1', $products[0]['$id']); + $this->assertArrayNotHasKey('store', $products[0]); + + $store = static::getDatabase()->getDocument('store', 'store2'); + $products = $store->getAttribute('products'); + $this->assertEquals('product2', $products[0]['$id']); + $this->assertArrayNotHasKey('store', $products[0]); + + $store = static::getDatabase()->getDocument('store', 'store3'); + $products = $store->getAttribute('products'); + $this->assertEquals('product3', $products[0]['$id']); + $this->assertArrayNotHasKey('store', $products[0]); + + $store = static::getDatabase()->getDocument('store', 'store4'); + $products = $store->getAttribute('products'); + $this->assertEquals('product4', $products[0]['$id']); + $this->assertArrayNotHasKey('store', $products[0]); + + $products = static::getDatabase()->find('product'); + + $this->assertEquals(4, \count($products)); + + // Select related document attributes + $product = static::getDatabase()->findOne('product', [ + Query::select(['*', 'store.name']) + ]); + + if (!$product instanceof Document) { + throw new Exception('Product not found'); + } + + $this->assertEquals('Store 1', $product->getAttribute('store')->getAttribute('name')); + $this->assertArrayNotHasKey('opensAt', $product->getAttribute('store')); + + $product = static::getDatabase()->getDocument('product', 'product1', [ + Query::select(['*', 'store.name']) + ]); + + $this->assertEquals('Store 1', $product->getAttribute('store')->getAttribute('name')); + $this->assertArrayNotHasKey('opensAt', $product->getAttribute('store')); + + // Update root document attribute without altering relationship + $product1 = static::getDatabase()->updateDocument( + 'product', + $product1->getId(), + $product1->setAttribute('name', 'Product 1 Updated') + ); + + $this->assertEquals('Product 1 Updated', $product1->getAttribute('name')); + $product1 = static::getDatabase()->getDocument('product', 'product1'); + $this->assertEquals('Product 1 Updated', $product1->getAttribute('name')); + + // Update inverse document attribute without altering relationship + $store1 = static::getDatabase()->getDocument('store', 'store1'); + $store1 = static::getDatabase()->updateDocument( + 'store', + $store1->getId(), + $store1->setAttribute('name', 'Store 1 Updated') + ); + + $this->assertEquals('Store 1 Updated', $store1->getAttribute('name')); + $store1 = static::getDatabase()->getDocument('store', 'store1'); + $this->assertEquals('Store 1 Updated', $store1->getAttribute('name')); + + // Update nested document attribute + $store = $product1->getAttribute('store'); + $store->setAttribute('name', 'Store 1 Updated'); + + $product1 = static::getDatabase()->updateDocument( + 'product', + $product1->getId(), + $product1->setAttribute('store', $store) + ); + + $this->assertEquals('Store 1 Updated', $product1->getAttribute('store')->getAttribute('name')); + $product1 = static::getDatabase()->getDocument('product', 'product1'); + $this->assertEquals('Store 1 Updated', $product1->getAttribute('store')->getAttribute('name')); + + // Update inverse nested document attribute + $product = $store1->getAttribute('products')[0]; + $product->setAttribute('name', 'Product 1 Updated'); + + $store1 = static::getDatabase()->updateDocument( + 'store', + $store1->getId(), + $store1->setAttribute('products', [$product]) + ); + + $this->assertEquals('Product 1 Updated', $store1->getAttribute('products')[0]->getAttribute('name')); + $store1 = static::getDatabase()->getDocument('store', 'store1'); + $this->assertEquals('Product 1 Updated', $store1->getAttribute('products')[0]->getAttribute('name')); + + // Create new document with no relationship + $product5 = static::getDatabase()->createDocument('product', new Document([ + '$id' => 'product5', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Product 5', + ])); + + // Update to relate to created document + $product5 = static::getDatabase()->updateDocument( + 'product', + $product5->getId(), + $product5->setAttribute('store', new Document([ + '$id' => 'store5', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Store 5', + 'opensAt' => '09:00', + ])) + ); + + $this->assertEquals('Store 5', $product5->getAttribute('store')->getAttribute('name')); + $product5 = static::getDatabase()->getDocument('product', 'product5'); + $this->assertEquals('Store 5', $product5->getAttribute('store')->getAttribute('name')); + + // Create new child document with no relationship + $store6 = static::getDatabase()->createDocument('store', new Document([ + '$id' => 'store6', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Store 6', + 'opensAt' => '09:00', + ])); + + // Update inverse to related to newly created document + $store6 = static::getDatabase()->updateDocument( + 'store', + $store6->getId(), + $store6->setAttribute('products', [new Document([ + '$id' => 'product6', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Product 6', + ])]) + ); + + $this->assertEquals('Product 6', $store6->getAttribute('products')[0]->getAttribute('name')); + $store6 = static::getDatabase()->getDocument('store', 'store6'); + $this->assertEquals('Product 6', $store6->getAttribute('products')[0]->getAttribute('name')); + + // Update document with new related document + static::getDatabase()->updateDocument( + 'product', + $product1->getId(), + $product1->setAttribute('store', 'store2') + ); + + $store1 = static::getDatabase()->getDocument('store', 'store1'); + + // Update inverse document + static::getDatabase()->updateDocument( + 'store', + $store1->getId(), + $store1->setAttribute('products', ['product1']) + ); + + $store2 = static::getDatabase()->getDocument('store', 'store2'); + + // Update inverse document + static::getDatabase()->updateDocument( + 'store', + $store2->getId(), + $store2->setAttribute('products', ['product1', 'product2']) + ); + + // Rename relationship keys on both sides + static::getDatabase()->updateRelationship( + 'product', + 'store', + 'newStore', + 'newProducts' + ); + + // Get document with new relationship key + $store = static::getDatabase()->getDocument('store', 'store2'); + $products = $store->getAttribute('newProducts'); + $this->assertEquals('product1', $products[0]['$id']); + + // Get inverse document with new relationship key + $product = static::getDatabase()->getDocument('product', 'product1'); + $store = $product->getAttribute('newStore'); + $this->assertEquals('store2', $store['$id']); + + // Reset relationships + $store1 = static::getDatabase()->getDocument('store', 'store1'); + static::getDatabase()->updateDocument( + 'store', + $store1->getId(), + $store1->setAttribute('newProducts', ['product1']) + ); + + // Create new document with no relationship + static::getDatabase()->createDocument('store', new Document([ + '$id' => 'store7', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Store 7', + 'opensAt' => '09:00', + ])); + + // Can delete document with no relationship when on delete is set to restrict + $deleted = static::getDatabase()->deleteDocument('store', 'store7'); + $this->assertEquals(true, $deleted); + + $store7 = static::getDatabase()->getDocument('store', 'store7'); + $this->assertEquals(true, $store7->isEmpty()); + + // Try to delete child while still related to another with on delete: restrict + try { + static::getDatabase()->deleteDocument('store', 'store1'); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); + } + + // Delete parent while still related to another with on delete: restrict + $result = static::getDatabase()->deleteDocument('product', 'product5'); + $this->assertEquals(true, $result); + + // Change on delete to set null + static::getDatabase()->updateRelationship( + collection: 'product', + id: 'newStore', + onDelete: Database::RELATION_MUTATE_SET_NULL + ); + + // Delete child, set parent relationship to null + static::getDatabase()->deleteDocument('store', 'store1'); + + // Check relation was set to null + static::getDatabase()->getDocument('product', 'product1'); + $this->assertEquals(null, $product1->getAttribute('newStore')); + + // Change on delete to cascade + static::getDatabase()->updateRelationship( + collection: 'product', + id: 'newStore', + onDelete: Database::RELATION_MUTATE_CASCADE + ); + + // Delete child, will delete parent + static::getDatabase()->deleteDocument('store', 'store2'); + + // Check parent and child were deleted + $library = static::getDatabase()->getDocument('store', 'store2'); + $this->assertEquals(true, $library->isEmpty()); + + $library = static::getDatabase()->getDocument('product', 'product2'); + $this->assertEquals(true, $library->isEmpty()); + + // Delete relationship + static::getDatabase()->deleteRelationship( + 'product', + 'newStore' + ); + + // Try to get document again + $products = static::getDatabase()->getDocument('product', 'product1'); + $store = $products->getAttribute('newStore'); + $this->assertEquals(null, $store); + + // Try to get inverse document again + $store = static::getDatabase()->getDocument('store', 'store1'); + $products = $store->getAttribute('newProducts'); + $this->assertEquals(null, $products); + } + + public function testManyToManyOneWayRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('playlist'); + static::getDatabase()->createCollection('song'); + + static::getDatabase()->createAttribute('playlist', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('song', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('song', 'length', Database::VAR_INTEGER, 0, true); + + static::getDatabase()->createRelationship( + collection: 'playlist', + relatedCollection: 'song', + type: Database::RELATION_MANY_TO_MANY, + id: 'songs' + ); + + // Check metadata for collection + $collection = static::getDatabase()->getCollection('playlist'); + $attributes = $collection->getAttribute('attributes', []); + + foreach ($attributes as $attribute) { + if ($attribute['key'] === 'songs') { + $this->assertEquals('relationship', $attribute['type']); + $this->assertEquals('songs', $attribute['$id']); + $this->assertEquals('songs', $attribute['key']); + $this->assertEquals('song', $attribute['options']['relatedCollection']); + $this->assertEquals(Database::RELATION_MANY_TO_MANY, $attribute['options']['relationType']); + $this->assertEquals(false, $attribute['options']['twoWay']); + $this->assertEquals('playlist', $attribute['options']['twoWayKey']); + } + } + + // Create document with relationship with nested data + $playlist1 = static::getDatabase()->createDocument('playlist', new Document([ + '$id' => 'playlist1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Playlist 1', + 'songs' => [ + [ + '$id' => 'song1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Song 1', + 'length' => 180, + ], + ], + ])); + + // Create document with relationship with related ID + static::getDatabase()->createDocument('song', new Document([ + '$id' => 'song2', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Song 2', + 'length' => 140, + ])); + static::getDatabase()->createDocument('playlist', new Document([ + '$id' => 'playlist2', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Playlist 2', + 'songs' => [ + 'song2' + ] + ])); + + // Update a document with non existing related document. It should not get added to the list. + static::getDatabase()->updateDocument('playlist', 'playlist1', $playlist1->setAttribute('songs', ['song1','no-song'])); + + $playlist1Document = static::getDatabase()->getDocument('playlist', 'playlist1'); + // Assert document does not contain non existing relation document. + $this->assertEquals(1, \count($playlist1Document->getAttribute('songs'))); + + $documents = static::getDatabase()->find('playlist', [ + Query::select(['name']), + Query::limit(1) + ]); + + $this->assertArrayNotHasKey('songs', $documents[0]); + + // Get document with relationship + $playlist = static::getDatabase()->getDocument('playlist', 'playlist1'); + $songs = $playlist->getAttribute('songs', []); + $this->assertEquals('song1', $songs[0]['$id']); + $this->assertArrayNotHasKey('playlist', $songs[0]); + + $playlist = static::getDatabase()->getDocument('playlist', 'playlist2'); + $songs = $playlist->getAttribute('songs', []); + $this->assertEquals('song2', $songs[0]['$id']); + $this->assertArrayNotHasKey('playlist', $songs[0]); + + // Get related document + $library = static::getDatabase()->getDocument('song', 'song1'); + $this->assertArrayNotHasKey('songs', $library); + + $library = static::getDatabase()->getDocument('song', 'song2'); + $this->assertArrayNotHasKey('songs', $library); + + $playlists = static::getDatabase()->find('playlist'); + + $this->assertEquals(2, \count($playlists)); + + // Select related document attributes + $playlist = static::getDatabase()->findOne('playlist', [ + Query::select(['*', 'songs.name']) + ]); + + if (!$playlist instanceof Document) { + throw new Exception('Playlist not found'); + } + + $this->assertEquals('Song 1', $playlist->getAttribute('songs')[0]->getAttribute('name')); + $this->assertArrayNotHasKey('length', $playlist->getAttribute('songs')[0]); + + $playlist = static::getDatabase()->getDocument('playlist', 'playlist1', [ + Query::select(['*', 'songs.name']) + ]); + + $this->assertEquals('Song 1', $playlist->getAttribute('songs')[0]->getAttribute('name')); + $this->assertArrayNotHasKey('length', $playlist->getAttribute('songs')[0]); + + // Update root document attribute without altering relationship + $playlist1 = static::getDatabase()->updateDocument( + 'playlist', + $playlist1->getId(), + $playlist1->setAttribute('name', 'Playlist 1 Updated') + ); + + $this->assertEquals('Playlist 1 Updated', $playlist1->getAttribute('name')); + $playlist1 = static::getDatabase()->getDocument('playlist', 'playlist1'); + $this->assertEquals('Playlist 1 Updated', $playlist1->getAttribute('name')); + + // Update nested document attribute + $songs = $playlist1->getAttribute('songs', []); + $songs[0]->setAttribute('name', 'Song 1 Updated'); + + $playlist1 = static::getDatabase()->updateDocument( + 'playlist', + $playlist1->getId(), + $playlist1->setAttribute('songs', $songs) + ); + + $this->assertEquals('Song 1 Updated', $playlist1->getAttribute('songs')[0]->getAttribute('name')); + $playlist1 = static::getDatabase()->getDocument('playlist', 'playlist1'); + $this->assertEquals('Song 1 Updated', $playlist1->getAttribute('songs')[0]->getAttribute('name')); + + // Create new document with no relationship + $playlist5 = static::getDatabase()->createDocument('playlist', new Document([ + '$id' => 'playlist5', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Playlist 5', + ])); + + // Update to relate to created document + $playlist5 = static::getDatabase()->updateDocument( + 'playlist', + $playlist5->getId(), + $playlist5->setAttribute('songs', [new Document([ + '$id' => 'song5', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Song 5', + 'length' => 180, + ])]) + ); + + // Playlist relating to existing songs that belong to other playlists + static::getDatabase()->createDocument('playlist', new Document([ + '$id' => 'playlist6', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Playlist 6', + 'songs' => [ + 'song1', + 'song2', + 'song5' + ] + ])); + + $this->assertEquals('Song 5', $playlist5->getAttribute('songs')[0]->getAttribute('name')); + $playlist5 = static::getDatabase()->getDocument('playlist', 'playlist5'); + $this->assertEquals('Song 5', $playlist5->getAttribute('songs')[0]->getAttribute('name')); + + // Update document with new related document + static::getDatabase()->updateDocument( + 'playlist', + $playlist1->getId(), + $playlist1->setAttribute('songs', ['song2']) + ); + + // Rename relationship key + static::getDatabase()->updateRelationship( + 'playlist', + 'songs', + 'newSongs' + ); + + // Get document with new relationship key + $playlist = static::getDatabase()->getDocument('playlist', 'playlist1'); + $songs = $playlist->getAttribute('newSongs'); + $this->assertEquals('song2', $songs[0]['$id']); + + // Create new document with no relationship + static::getDatabase()->createDocument('playlist', new Document([ + '$id' => 'playlist3', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Playlist 3', + ])); + + // Can delete document with no relationship when on delete is set to restrict + $deleted = static::getDatabase()->deleteDocument('playlist', 'playlist3'); + $this->assertEquals(true, $deleted); + + $playlist3 = static::getDatabase()->getDocument('playlist', 'playlist3'); + $this->assertEquals(true, $playlist3->isEmpty()); + + // Try to delete document while still related to another with on delete: restrict + try { + static::getDatabase()->deleteDocument('playlist', 'playlist1'); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); + } + + // Change on delete to set null + static::getDatabase()->updateRelationship( + collection: 'playlist', + id: 'newSongs', + onDelete: Database::RELATION_MUTATE_SET_NULL + ); + + $playlist1 = static::getDatabase()->getDocument('playlist', 'playlist1'); + + // Reset relationships + static::getDatabase()->updateDocument( + 'playlist', + $playlist1->getId(), + $playlist1->setAttribute('newSongs', ['song1']) + ); + + // Delete child, will delete junction + static::getDatabase()->deleteDocument('song', 'song1'); + + // Check relation was set to null + $playlist1 = static::getDatabase()->getDocument('playlist', 'playlist1'); + $this->assertEquals(0, \count($playlist1->getAttribute('newSongs'))); + + // Change on delete to cascade + static::getDatabase()->updateRelationship( + collection: 'playlist', + id: 'newSongs', + onDelete: Database::RELATION_MUTATE_CASCADE + ); + + // Delete parent, will delete child + static::getDatabase()->deleteDocument('playlist', 'playlist2'); + + // Check parent and child were deleted + $library = static::getDatabase()->getDocument('playlist', 'playlist2'); + $this->assertEquals(true, $library->isEmpty()); + + $library = static::getDatabase()->getDocument('song', 'song2'); + $this->assertEquals(true, $library->isEmpty()); + + // Delete relationship + static::getDatabase()->deleteRelationship( + 'playlist', + 'newSongs' + ); + + // Try to get document again + $playlist = static::getDatabase()->getDocument('playlist', 'playlist1'); + $songs = $playlist->getAttribute('newSongs'); + $this->assertEquals(null, $songs); + } + + public function testManyToManyTwoWayRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('students'); + static::getDatabase()->createCollection('classes'); + + static::getDatabase()->createAttribute('students', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('classes', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('classes', 'number', Database::VAR_INTEGER, 0, true); + + static::getDatabase()->createRelationship( + collection: 'students', + relatedCollection: 'classes', + type: Database::RELATION_MANY_TO_MANY, + twoWay: true, + ); + + // Check metadata for collection + $collection = static::getDatabase()->getCollection('students'); + $attributes = $collection->getAttribute('attributes', []); + foreach ($attributes as $attribute) { + if ($attribute['key'] === 'students') { + $this->assertEquals('relationship', $attribute['type']); + $this->assertEquals('students', $attribute['$id']); + $this->assertEquals('students', $attribute['key']); + $this->assertEquals('students', $attribute['options']['relatedCollection']); + $this->assertEquals(Database::RELATION_MANY_TO_MANY, $attribute['options']['relationType']); + $this->assertEquals(true, $attribute['options']['twoWay']); + $this->assertEquals('classes', $attribute['options']['twoWayKey']); + } + } + + // Check metadata for related collection + $collection = static::getDatabase()->getCollection('classes'); + $attributes = $collection->getAttribute('attributes', []); + foreach ($attributes as $attribute) { + if ($attribute['key'] === 'classes') { + $this->assertEquals('relationship', $attribute['type']); + $this->assertEquals('classes', $attribute['$id']); + $this->assertEquals('classes', $attribute['key']); + $this->assertEquals('classes', $attribute['options']['relatedCollection']); + $this->assertEquals(Database::RELATION_MANY_TO_MANY, $attribute['options']['relationType']); + $this->assertEquals(true, $attribute['options']['twoWay']); + $this->assertEquals('students', $attribute['options']['twoWayKey']); + } + } + + // Create document with relationship with nested data + $student1 = static::getDatabase()->createDocument('students', new Document([ + '$id' => 'student1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Student 1', + 'classes' => [ + [ + '$id' => 'class1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Class 1', + 'number' => 1, + ], + ], + ])); + + // Update a document with non existing related document. It should not get added to the list. + static::getDatabase()->updateDocument('students', 'student1', $student1->setAttribute('classes', ['class1', 'no-class'])); + + $student1Document = static::getDatabase()->getDocument('students', 'student1'); + // Assert document does not contain non existing relation document. + $this->assertEquals(1, \count($student1Document->getAttribute('classes'))); + + // Create document with relationship with related ID + static::getDatabase()->createDocument('classes', new Document([ + '$id' => 'class2', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + + ], + 'name' => 'Class 2', + 'number' => 2, + ])); + static::getDatabase()->createDocument('students', new Document([ + '$id' => 'student2', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Student 2', + 'classes' => [ + 'class2' + ], + ])); + + // Create from child side + static::getDatabase()->createDocument('classes', new Document([ + '$id' => 'class3', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Class 3', + 'number' => 3, + 'students' => [ + [ + '$id' => 'student3', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Student 3', + ] + ], + ])); + static::getDatabase()->createDocument('students', new Document([ + '$id' => 'student4', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Student 4' + ])); + static::getDatabase()->createDocument('classes', new Document([ + '$id' => 'class4', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + + ], + 'name' => 'Class 4', + 'number' => 4, + 'students' => [ + 'student4' + ], + ])); + + // Get document with relationship + $student = static::getDatabase()->getDocument('students', 'student1'); + $classes = $student->getAttribute('classes', []); + $this->assertEquals('class1', $classes[0]['$id']); + $this->assertArrayNotHasKey('students', $classes[0]); + + $student = static::getDatabase()->getDocument('students', 'student2'); + $classes = $student->getAttribute('classes', []); + $this->assertEquals('class2', $classes[0]['$id']); + $this->assertArrayNotHasKey('students', $classes[0]); + + $student = static::getDatabase()->getDocument('students', 'student3'); + $classes = $student->getAttribute('classes', []); + $this->assertEquals('class3', $classes[0]['$id']); + $this->assertArrayNotHasKey('students', $classes[0]); + + $student = static::getDatabase()->getDocument('students', 'student4'); + $classes = $student->getAttribute('classes', []); + $this->assertEquals('class4', $classes[0]['$id']); + $this->assertArrayNotHasKey('students', $classes[0]); + + // Get related document + $class = static::getDatabase()->getDocument('classes', 'class1'); + $student = $class->getAttribute('students'); + $this->assertEquals('student1', $student[0]['$id']); + $this->assertArrayNotHasKey('classes', $student[0]); + + $class = static::getDatabase()->getDocument('classes', 'class2'); + $student = $class->getAttribute('students'); + $this->assertEquals('student2', $student[0]['$id']); + $this->assertArrayNotHasKey('classes', $student[0]); + + $class = static::getDatabase()->getDocument('classes', 'class3'); + $student = $class->getAttribute('students'); + $this->assertEquals('student3', $student[0]['$id']); + $this->assertArrayNotHasKey('classes', $student[0]); + + $class = static::getDatabase()->getDocument('classes', 'class4'); + $student = $class->getAttribute('students'); + $this->assertEquals('student4', $student[0]['$id']); + $this->assertArrayNotHasKey('classes', $student[0]); + + // Select related document attributes + $student = static::getDatabase()->findOne('students', [ + Query::select(['*', 'classes.name']) + ]); + + if (!$student instanceof Document) { + throw new Exception('Student not found'); + } + + $this->assertEquals('Class 1', $student->getAttribute('classes')[0]->getAttribute('name')); + $this->assertArrayNotHasKey('number', $student->getAttribute('classes')[0]); + + $student = static::getDatabase()->getDocument('students', 'student1', [ + Query::select(['*', 'classes.name']) + ]); + + $this->assertEquals('Class 1', $student->getAttribute('classes')[0]->getAttribute('name')); + $this->assertArrayNotHasKey('number', $student->getAttribute('classes')[0]); + + // Update root document attribute without altering relationship + $student1 = static::getDatabase()->updateDocument( + 'students', + $student1->getId(), + $student1->setAttribute('name', 'Student 1 Updated') + ); + + $this->assertEquals('Student 1 Updated', $student1->getAttribute('name')); + $student1 = static::getDatabase()->getDocument('students', 'student1'); + $this->assertEquals('Student 1 Updated', $student1->getAttribute('name')); + + // Update inverse root document attribute without altering relationship + $class2 = static::getDatabase()->getDocument('classes', 'class2'); + $class2 = static::getDatabase()->updateDocument( + 'classes', + $class2->getId(), + $class2->setAttribute('name', 'Class 2 Updated') + ); + + $this->assertEquals('Class 2 Updated', $class2->getAttribute('name')); + $class2 = static::getDatabase()->getDocument('classes', 'class2'); + $this->assertEquals('Class 2 Updated', $class2->getAttribute('name')); + + // Update nested document attribute + $classes = $student1->getAttribute('classes', []); + $classes[0]->setAttribute('name', 'Class 1 Updated'); + + $student1 = static::getDatabase()->updateDocument( + 'students', + $student1->getId(), + $student1->setAttribute('classes', $classes) + ); + + $this->assertEquals('Class 1 Updated', $student1->getAttribute('classes')[0]->getAttribute('name')); + $student1 = static::getDatabase()->getDocument('students', 'student1'); + $this->assertEquals('Class 1 Updated', $student1->getAttribute('classes')[0]->getAttribute('name')); + + // Update inverse nested document attribute + $students = $class2->getAttribute('students', []); + $students[0]->setAttribute('name', 'Student 2 Updated'); + + $class2 = static::getDatabase()->updateDocument( + 'classes', + $class2->getId(), + $class2->setAttribute('students', $students) + ); + + $this->assertEquals('Student 2 Updated', $class2->getAttribute('students')[0]->getAttribute('name')); + $class2 = static::getDatabase()->getDocument('classes', 'class2'); + $this->assertEquals('Student 2 Updated', $class2->getAttribute('students')[0]->getAttribute('name')); + + // Create new document with no relationship + $student5 = static::getDatabase()->createDocument('students', new Document([ + '$id' => 'student5', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Student 5', + ])); + + // Update to relate to created document + $student5 = static::getDatabase()->updateDocument( + 'students', + $student5->getId(), + $student5->setAttribute('classes', [new Document([ + '$id' => 'class5', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Class 5', + 'number' => 5, + ])]) + ); + + $this->assertEquals('Class 5', $student5->getAttribute('classes')[0]->getAttribute('name')); + $student5 = static::getDatabase()->getDocument('students', 'student5'); + $this->assertEquals('Class 5', $student5->getAttribute('classes')[0]->getAttribute('name')); + + // Create child document with no relationship + $class6 = static::getDatabase()->createDocument('classes', new Document([ + '$id' => 'class6', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Class 6', + 'number' => 6, + ])); + + // Update to relate to created document + $class6 = static::getDatabase()->updateDocument( + 'classes', + $class6->getId(), + $class6->setAttribute('students', [new Document([ + '$id' => 'student6', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Student 6', + ])]) + ); + + $this->assertEquals('Student 6', $class6->getAttribute('students')[0]->getAttribute('name')); + $class6 = static::getDatabase()->getDocument('classes', 'class6'); + $this->assertEquals('Student 6', $class6->getAttribute('students')[0]->getAttribute('name')); + + // Update document with new related document + static::getDatabase()->updateDocument( + 'students', + $student1->getId(), + $student1->setAttribute('classes', ['class2']) + ); + + $class1 = static::getDatabase()->getDocument('classes', 'class1'); + + // Update inverse document + static::getDatabase()->updateDocument( + 'classes', + $class1->getId(), + $class1->setAttribute('students', ['student1']) + ); + + // Rename relationship keys on both sides + static::getDatabase()->updateRelationship( + 'students', + 'classes', + 'newClasses', + 'newStudents' + ); + + // Get document with new relationship key + $students = static::getDatabase()->getDocument('students', 'student1'); + $classes = $students->getAttribute('newClasses'); + $this->assertEquals('class2', $classes[0]['$id']); + + // Get inverse document with new relationship key + $class = static::getDatabase()->getDocument('classes', 'class1'); + $students = $class->getAttribute('newStudents'); + $this->assertEquals('student1', $students[0]['$id']); + + // Create new document with no relationship + static::getDatabase()->createDocument('students', new Document([ + '$id' => 'student7', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'name' => 'Student 7', + ])); + + // Can delete document with no relationship when on delete is set to restrict + $deleted = static::getDatabase()->deleteDocument('students', 'student7'); + $this->assertEquals(true, $deleted); + + $student6 = static::getDatabase()->getDocument('students', 'student7'); + $this->assertEquals(true, $student6->isEmpty()); + + // Try to delete document while still related to another with on delete: restrict + try { + static::getDatabase()->deleteDocument('students', 'student1'); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); + } + + // Change on delete to set null + static::getDatabase()->updateRelationship( + collection: 'students', + id: 'newClasses', + onDelete: Database::RELATION_MUTATE_SET_NULL + ); + + $student1 = static::getDatabase()->getDocument('students', 'student1'); + + // Reset relationships + static::getDatabase()->updateDocument( + 'students', + $student1->getId(), + $student1->setAttribute('newClasses', ['class1']) + ); + + // Delete child, will delete junction + static::getDatabase()->deleteDocument('classes', 'class1'); + + // Check relation was set to null + $student1 = static::getDatabase()->getDocument('students', 'student1'); + $this->assertEquals(0, \count($student1->getAttribute('newClasses'))); + + // Change on delete to cascade + static::getDatabase()->updateRelationship( + collection: 'students', + id: 'newClasses', + onDelete: Database::RELATION_MUTATE_CASCADE + ); + + // Delete parent, will delete child + static::getDatabase()->deleteDocument('students', 'student2'); + + // Check parent and child were deleted + $library = static::getDatabase()->getDocument('students', 'student2'); + $this->assertEquals(true, $library->isEmpty()); + + // Delete child, should not delete parent + static::getDatabase()->deleteDocument('classes', 'class6'); + + // Check only child was deleted + $student6 = static::getDatabase()->getDocument('students', 'student6'); + $this->assertEquals(false, $student6->isEmpty()); + $this->assertEmpty($student6->getAttribute('newClasses')); + + $library = static::getDatabase()->getDocument('classes', 'class2'); + $this->assertEquals(true, $library->isEmpty()); + + // Delete relationship + static::getDatabase()->deleteRelationship( + 'students', + 'newClasses' + ); + + // Try to get documents again + $student = static::getDatabase()->getDocument('students', 'student1'); + $classes = $student->getAttribute('newClasses'); + $this->assertEquals(null, $classes); + + // Try to get inverse documents again + $classes = static::getDatabase()->getDocument('classes', 'class1'); + $students = $classes->getAttribute('newStudents'); + $this->assertEquals(null, $students); + } + + public function testSelectRelationshipAttributes(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('make'); + static::getDatabase()->createCollection('model'); + + static::getDatabase()->createAttribute('make', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('model', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('model', 'year', Database::VAR_INTEGER, 0, true); + + static::getDatabase()->createRelationship( + collection: 'make', + relatedCollection: 'model', + type: Database::RELATION_ONE_TO_MANY, + id: 'models' + ); + + static::getDatabase()->createDocument('make', new Document([ + '$id' => 'ford', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Ford', + 'models' => [ + [ + '$id' => 'fiesta', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Fiesta', + 'year' => 2010, + ], + [ + '$id' => 'focus', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Focus', + 'year' => 2011, + ], + ], + ])); + + // Select some parent attributes, some child attributes + $make = static::getDatabase()->findOne('make', [ + Query::select(['name', 'models.name']), + ]); + + if (!$make instanceof Document) { + throw new Exception('Make not found'); + } + + $this->assertEquals('Ford', $make['name']); + $this->assertEquals(2, \count($make['models'])); + $this->assertEquals('Fiesta', $make['models'][0]['name']); + $this->assertEquals('Focus', $make['models'][1]['name']); + $this->assertArrayNotHasKey('year', $make['models'][0]); + $this->assertArrayNotHasKey('year', $make['models'][1]); + $this->assertArrayNotHasKey('$id', $make); + $this->assertArrayNotHasKey('$internalId', $make); + $this->assertArrayNotHasKey('$permissions', $make); + $this->assertArrayNotHasKey('$collection', $make); + $this->assertArrayNotHasKey('$createdAt', $make); + $this->assertArrayNotHasKey('$updatedAt', $make); + + // Select internal attributes + $make = static::getDatabase()->findOne('make', [ + Query::select(['name', '$id']), + ]); + + if (!$make instanceof Document) { + throw new Exception('Make not found'); + } + + $this->assertArrayHasKey('$id', $make); + $this->assertArrayNotHasKey('$internalId', $make); + $this->assertArrayNotHasKey('$collection', $make); + $this->assertArrayNotHasKey('$createdAt', $make); + $this->assertArrayNotHasKey('$updatedAt', $make); + $this->assertArrayNotHasKey('$permissions', $make); + + $make = static::getDatabase()->findOne('make', [ + Query::select(['name', '$internalId']), + ]); + + if (!$make instanceof Document) { + throw new Exception('Make not found'); + } + + $this->assertArrayNotHasKey('$id', $make); + $this->assertArrayHasKey('$internalId', $make); + $this->assertArrayNotHasKey('$collection', $make); + $this->assertArrayNotHasKey('$createdAt', $make); + $this->assertArrayNotHasKey('$updatedAt', $make); + $this->assertArrayNotHasKey('$permissions', $make); + + $make = static::getDatabase()->findOne('make', [ + Query::select(['name', '$collection']), + ]); + + if (!$make instanceof Document) { + throw new Exception('Make not found'); + } + + $this->assertArrayNotHasKey('$id', $make); + $this->assertArrayNotHasKey('$internalId', $make); + $this->assertArrayHasKey('$collection', $make); + $this->assertArrayNotHasKey('$createdAt', $make); + $this->assertArrayNotHasKey('$updatedAt', $make); + $this->assertArrayNotHasKey('$permissions', $make); + + $make = static::getDatabase()->findOne('make', [ + Query::select(['name', '$createdAt']), + ]); + + if (!$make instanceof Document) { + throw new Exception('Make not found'); + } + + $this->assertArrayNotHasKey('$id', $make); + $this->assertArrayNotHasKey('$internalId', $make); + $this->assertArrayNotHasKey('$collection', $make); + $this->assertArrayHasKey('$createdAt', $make); + $this->assertArrayNotHasKey('$updatedAt', $make); + $this->assertArrayNotHasKey('$permissions', $make); + + $make = static::getDatabase()->findOne('make', [ + Query::select(['name', '$updatedAt']), + ]); + + if (!$make instanceof Document) { + throw new Exception('Make not found'); + } + + $this->assertArrayNotHasKey('$id', $make); + $this->assertArrayNotHasKey('$internalId', $make); + $this->assertArrayNotHasKey('$collection', $make); + $this->assertArrayNotHasKey('$createdAt', $make); + $this->assertArrayHasKey('$updatedAt', $make); + $this->assertArrayNotHasKey('$permissions', $make); + + $make = static::getDatabase()->findOne('make', [ + Query::select(['name', '$permissions']), + ]); + + if (!$make instanceof Document) { + throw new Exception('Make not found'); + } + + $this->assertArrayNotHasKey('$id', $make); + $this->assertArrayNotHasKey('$internalId', $make); + $this->assertArrayNotHasKey('$collection', $make); + $this->assertArrayNotHasKey('$createdAt', $make); + $this->assertArrayNotHasKey('$updatedAt', $make); + $this->assertArrayHasKey('$permissions', $make); + + // Select all parent attributes, some child attributes + $make = static::getDatabase()->findOne('make', [ + Query::select(['*', 'models.year']), + ]); + + if (!$make instanceof Document) { + throw new Exception('Make not found'); + } + + $this->assertEquals('Ford', $make['name']); + $this->assertEquals(2, \count($make['models'])); + $this->assertArrayNotHasKey('name', $make['models'][0]); + $this->assertArrayNotHasKey('name', $make['models'][1]); + $this->assertEquals(2010, $make['models'][0]['year']); + $this->assertEquals(2011, $make['models'][1]['year']); + + // Select all parent attributes, all child attributes + $make = static::getDatabase()->findOne('make', [ + Query::select(['*', 'models.*']), + ]); + + if (!$make instanceof Document) { + throw new Exception('Make not found'); + } + + $this->assertEquals('Ford', $make['name']); + $this->assertEquals(2, \count($make['models'])); + $this->assertEquals('Fiesta', $make['models'][0]['name']); + $this->assertEquals('Focus', $make['models'][1]['name']); + $this->assertEquals(2010, $make['models'][0]['year']); + $this->assertEquals(2011, $make['models'][1]['year']); + + // Select all parent attributes, all child attributes + // Must select parent if selecting children + $make = static::getDatabase()->findOne('make', [ + Query::select(['models.*']), + ]); + + if (!$make instanceof Document) { + throw new Exception('Make not found'); + } + + $this->assertEquals('Ford', $make['name']); + $this->assertEquals(2, \count($make['models'])); + $this->assertEquals('Fiesta', $make['models'][0]['name']); + $this->assertEquals('Focus', $make['models'][1]['name']); + $this->assertEquals(2010, $make['models'][0]['year']); + $this->assertEquals(2011, $make['models'][1]['year']); + + // Select all parent attributes, no child attributes + $make = static::getDatabase()->findOne('make', [ + Query::select(['name']), + ]); + + if (!$make instanceof Document) { + throw new Exception('Make not found'); + } + + $this->assertEquals('Ford', $make['name']); + $this->assertArrayNotHasKey('models', $make); + } + + public function testNestedOneToOne_OneToOneRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('pattern'); + static::getDatabase()->createCollection('shirt'); + static::getDatabase()->createCollection('team'); + + static::getDatabase()->createAttribute('pattern', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('shirt', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('team', 'name', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'pattern', + relatedCollection: 'shirt', + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + id: 'shirt', + twoWayKey: 'pattern' + ); + static::getDatabase()->createRelationship( + collection: 'shirt', + relatedCollection: 'team', + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + id: 'team', + twoWayKey: 'shirt' + ); + + static::getDatabase()->createDocument('pattern', new Document([ + '$id' => 'stripes', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Stripes', + 'shirt' => [ + '$id' => 'red', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Red', + 'team' => [ + '$id' => 'reds', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Reds', + ], + ], + ])); + + $pattern = static::getDatabase()->getDocument('pattern', 'stripes'); + $this->assertEquals('red', $pattern['shirt']['$id']); + $this->assertArrayNotHasKey('pattern', $pattern['shirt']); + $this->assertEquals('reds', $pattern['shirt']['team']['$id']); + $this->assertArrayNotHasKey('shirt', $pattern['shirt']['team']); + + static::getDatabase()->createDocument('team', new Document([ + '$id' => 'blues', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Blues', + 'shirt' => [ + '$id' => 'blue', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Blue', + 'pattern' => [ + '$id' => 'plain', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Plain', + ], + ], + ])); + + $team = static::getDatabase()->getDocument('team', 'blues'); + $this->assertEquals('blue', $team['shirt']['$id']); + $this->assertArrayNotHasKey('team', $team['shirt']); + $this->assertEquals('plain', $team['shirt']['pattern']['$id']); + $this->assertArrayNotHasKey('shirt', $team['shirt']['pattern']); + } + + public function testNestedOneToOne_OneToManyRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('teachers'); + static::getDatabase()->createCollection('classrooms'); + static::getDatabase()->createCollection('children'); + + static::getDatabase()->createAttribute('children', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('teachers', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('classrooms', 'name', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'teachers', + relatedCollection: 'classrooms', + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + id: 'classroom', + twoWayKey: 'teacher' + ); + static::getDatabase()->createRelationship( + collection: 'classrooms', + relatedCollection: 'children', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + twoWayKey: 'classroom' + ); + + static::getDatabase()->createDocument('teachers', new Document([ + '$id' => 'teacher1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Teacher 1', + 'classroom' => [ + '$id' => 'classroom1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Classroom 1', + 'children' => [ + [ + '$id' => 'child1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Child 1', + ], + [ + '$id' => 'child2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Child 2', + ], + ], + ], + ])); + + $teacher1 = static::getDatabase()->getDocument('teachers', 'teacher1'); + $this->assertEquals('classroom1', $teacher1['classroom']['$id']); + $this->assertArrayNotHasKey('teacher', $teacher1['classroom']); + $this->assertEquals(2, \count($teacher1['classroom']['children'])); + $this->assertEquals('Child 1', $teacher1['classroom']['children'][0]['name']); + $this->assertEquals('Child 2', $teacher1['classroom']['children'][1]['name']); + + static::getDatabase()->createDocument('children', new Document([ + '$id' => 'child3', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Child 3', + 'classroom' => [ + '$id' => 'classroom2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Classroom 2', + 'teacher' => [ + '$id' => 'teacher2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Teacher 2', + ], + ], + ])); + + $child3 = static::getDatabase()->getDocument('children', 'child3'); + $this->assertEquals('classroom2', $child3['classroom']['$id']); + $this->assertArrayNotHasKey('children', $child3['classroom']); + $this->assertEquals('teacher2', $child3['classroom']['teacher']['$id']); + $this->assertArrayNotHasKey('classroom', $child3['classroom']['teacher']); + } + + public function testNestedOneToOne_ManyToOneRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('users'); + static::getDatabase()->createCollection('profiles'); + static::getDatabase()->createCollection('avatars'); + + static::getDatabase()->createAttribute('users', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('profiles', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('avatars', 'name', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'users', + relatedCollection: 'profiles', + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + id: 'profile', + twoWayKey: 'user' + ); + static::getDatabase()->createRelationship( + collection: 'profiles', + relatedCollection: 'avatars', + type: Database::RELATION_MANY_TO_ONE, + twoWay: true, + id: 'avatar', + ); + + static::getDatabase()->createDocument('users', new Document([ + '$id' => 'user1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'User 1', + 'profile' => [ + '$id' => 'profile1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Profile 1', + 'avatar' => [ + '$id' => 'avatar1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Avatar 1', + ], + ], + ])); + + $user1 = static::getDatabase()->getDocument('users', 'user1'); + $this->assertEquals('profile1', $user1['profile']['$id']); + $this->assertArrayNotHasKey('user', $user1['profile']); + $this->assertEquals('avatar1', $user1['profile']['avatar']['$id']); + $this->assertArrayNotHasKey('profile', $user1['profile']['avatar']); + + static::getDatabase()->createDocument('avatars', new Document([ + '$id' => 'avatar2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Avatar 2', + 'profiles' => [ + [ + '$id' => 'profile2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Profile 2', + 'user' => [ + '$id' => 'user2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'User 2', + ], + ] + ], + ])); + + $avatar2 = static::getDatabase()->getDocument('avatars', 'avatar2'); + $this->assertEquals('profile2', $avatar2['profiles'][0]['$id']); + $this->assertArrayNotHasKey('avatars', $avatar2['profiles'][0]); + $this->assertEquals('user2', $avatar2['profiles'][0]['user']['$id']); + $this->assertArrayNotHasKey('profiles', $avatar2['profiles'][0]['user']); + } + + public function testNestedOneToOne_ManyToManyRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('addresses'); + static::getDatabase()->createCollection('houses'); + static::getDatabase()->createCollection('buildings'); + + static::getDatabase()->createAttribute('addresses', 'street', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('houses', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('buildings', 'name', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'addresses', + relatedCollection: 'houses', + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + id: 'house', + twoWayKey: 'address' + ); + static::getDatabase()->createRelationship( + collection: 'houses', + relatedCollection: 'buildings', + type: Database::RELATION_MANY_TO_MANY, + twoWay: true, + ); + + static::getDatabase()->createDocument('addresses', new Document([ + '$id' => 'address1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'street' => 'Street 1', + 'house' => [ + '$id' => 'house1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'House 1', + 'buildings' => [ + [ + '$id' => 'building1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Building 1', + ], + [ + '$id' => 'building2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Building 2', + ], + ], + ], + ])); + + $address1 = static::getDatabase()->getDocument('addresses', 'address1'); + $this->assertEquals('house1', $address1['house']['$id']); + $this->assertArrayNotHasKey('address', $address1['house']); + $this->assertEquals('building1', $address1['house']['buildings'][0]['$id']); + $this->assertEquals('building2', $address1['house']['buildings'][1]['$id']); + $this->assertArrayNotHasKey('houses', $address1['house']['buildings'][0]); + $this->assertArrayNotHasKey('houses', $address1['house']['buildings'][1]); + + static::getDatabase()->createDocument('buildings', new Document([ + '$id' => 'building3', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Building 3', + 'houses' => [ + [ + '$id' => 'house2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'House 2', + 'address' => [ + '$id' => 'address2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'street' => 'Street 2', + ], + ], + ], + ])); + } + + public function testNestedOneToMany_OneToOneRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('countries'); + static::getDatabase()->createCollection('cities'); + static::getDatabase()->createCollection('mayors'); + + static::getDatabase()->createAttribute('cities', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('countries', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('mayors', 'name', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'countries', + relatedCollection: 'cities', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + twoWayKey: 'country' + ); + static::getDatabase()->createRelationship( + collection: 'cities', + relatedCollection: 'mayors', + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + id: 'mayor', + twoWayKey: 'city' + ); + + static::getDatabase()->createDocument('countries', new Document([ + '$id' => 'country1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + 'name' => 'Country 1', + 'cities' => [ + [ + '$id' => 'city1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + 'name' => 'City 1', + 'mayor' => [ + '$id' => 'mayor1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + 'name' => 'Mayor 1', + ], + ], + [ + '$id' => 'city2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'City 2', + 'mayor' => [ + '$id' => 'mayor2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Mayor 2', + ], + ], + ], + ])); + + $documents = static::getDatabase()->find('countries', [ + Query::limit(1) + ]); + $this->assertEquals('Mayor 1', $documents[0]['cities'][0]['mayor']['name']); + + $documents = static::getDatabase()->find('countries', [ + Query::select(['name']), + Query::limit(1) + ]); + $this->assertArrayHasKey('name', $documents[0]); + $this->assertArrayNotHasKey('cities', $documents[0]); + + $documents = static::getDatabase()->find('countries', [ + Query::select(['*']), + Query::limit(1) + ]); + $this->assertArrayHasKey('name', $documents[0]); + $this->assertArrayNotHasKey('cities', $documents[0]); + + $documents = static::getDatabase()->find('countries', [ + Query::select(['*', 'cities.*', 'cities.mayor.*']), + Query::limit(1) + ]); + + $this->assertEquals('Mayor 1', $documents[0]['cities'][0]['mayor']['name']); + + // Insert docs to cache: + $country1 = static::getDatabase()->getDocument('countries', 'country1'); + $mayor1 = static::getDatabase()->getDocument('mayors', 'mayor1'); + $this->assertEquals('City 1', $mayor1['city']['name']); + $this->assertEquals('City 1', $country1['cities'][0]['name']); + + static::getDatabase()->updateDocument('cities', 'city1', new Document([ + '$id' => 'city1', + '$collection' => 'cities', + 'name' => 'City 1 updated', + 'mayor' => 'mayor1', // we don't support partial updates at the moment + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ])); + + $mayor1 = static::getDatabase()->getDocument('mayors', 'mayor1'); + $country1 = static::getDatabase()->getDocument('countries', 'country1'); + + $this->assertEquals('City 1 updated', $mayor1['city']['name']); + $this->assertEquals('City 1 updated', $country1['cities'][0]['name']); + $this->assertEquals('city1', $country1['cities'][0]['$id']); + $this->assertEquals('city2', $country1['cities'][1]['$id']); + $this->assertEquals('mayor1', $country1['cities'][0]['mayor']['$id']); + $this->assertEquals('mayor2', $country1['cities'][1]['mayor']['$id']); + $this->assertArrayNotHasKey('city', $country1['cities'][0]['mayor']); + $this->assertArrayNotHasKey('city', $country1['cities'][1]['mayor']); + + static::getDatabase()->createDocument('mayors', new Document([ + '$id' => 'mayor3', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Mayor 3', + 'city' => [ + '$id' => 'city3', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'City 3', + 'country' => [ + '$id' => 'country2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Country 2', + ], + ], + ])); + + $country2 = static::getDatabase()->getDocument('countries', 'country2'); + $this->assertEquals('city3', $country2['cities'][0]['$id']); + $this->assertEquals('mayor3', $country2['cities'][0]['mayor']['$id']); + $this->assertArrayNotHasKey('country', $country2['cities'][0]); + $this->assertArrayNotHasKey('city', $country2['cities'][0]['mayor']); + } + + public function testNestedOneToMany_OneToManyRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('dormitories'); + static::getDatabase()->createCollection('occupants'); + static::getDatabase()->createCollection('pets'); + + static::getDatabase()->createAttribute('dormitories', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('occupants', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('pets', 'name', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'dormitories', + relatedCollection: 'occupants', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + twoWayKey: 'dormitory' + ); + static::getDatabase()->createRelationship( + collection: 'occupants', + relatedCollection: 'pets', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + twoWayKey: 'occupant' + ); + + static::getDatabase()->createDocument('dormitories', new Document([ + '$id' => 'dormitory1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'House 1', + 'occupants' => [ + [ + '$id' => 'occupant1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Occupant 1', + 'pets' => [ + [ + '$id' => 'pet1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Pet 1', + ], + [ + '$id' => 'pet2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Pet 2', + ], + ], + ], + [ + '$id' => 'occupant2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Occupant 2', + 'pets' => [ + [ + '$id' => 'pet3', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Pet 3', + ], + [ + '$id' => 'pet4', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Pet 4', + ], + ], + ], + ], + ])); + + $dormitory1 = static::getDatabase()->getDocument('dormitories', 'dormitory1'); + $this->assertEquals('occupant1', $dormitory1['occupants'][0]['$id']); + $this->assertEquals('occupant2', $dormitory1['occupants'][1]['$id']); + $this->assertEquals('pet1', $dormitory1['occupants'][0]['pets'][0]['$id']); + $this->assertEquals('pet2', $dormitory1['occupants'][0]['pets'][1]['$id']); + $this->assertEquals('pet3', $dormitory1['occupants'][1]['pets'][0]['$id']); + $this->assertEquals('pet4', $dormitory1['occupants'][1]['pets'][1]['$id']); + $this->assertArrayNotHasKey('dormitory', $dormitory1['occupants'][0]); + $this->assertArrayNotHasKey('dormitory', $dormitory1['occupants'][1]); + $this->assertArrayNotHasKey('occupant', $dormitory1['occupants'][0]['pets'][0]); + $this->assertArrayNotHasKey('occupant', $dormitory1['occupants'][0]['pets'][1]); + $this->assertArrayNotHasKey('occupant', $dormitory1['occupants'][1]['pets'][0]); + $this->assertArrayNotHasKey('occupant', $dormitory1['occupants'][1]['pets'][1]); + + static::getDatabase()->createDocument('pets', new Document([ + '$id' => 'pet5', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Pet 5', + 'occupant' => [ + '$id' => 'occupant3', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Occupant 3', + 'dormitory' => [ + '$id' => 'dormitory2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'House 2', + ], + ], + ])); + + $pet5 = static::getDatabase()->getDocument('pets', 'pet5'); + $this->assertEquals('occupant3', $pet5['occupant']['$id']); + $this->assertEquals('dormitory2', $pet5['occupant']['dormitory']['$id']); + $this->assertArrayNotHasKey('pets', $pet5['occupant']); + $this->assertArrayNotHasKey('occupant', $pet5['occupant']['dormitory']); + } + + public function testNestedOneToMany_ManyToOneRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('home'); + static::getDatabase()->createCollection('renters'); + static::getDatabase()->createCollection('floors'); + + static::getDatabase()->createAttribute('home', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('renters', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('floors', 'name', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'home', + relatedCollection: 'renters', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true + ); + static::getDatabase()->createRelationship( + collection: 'renters', + relatedCollection: 'floors', + type: Database::RELATION_MANY_TO_ONE, + twoWay: true, + id: 'floor' + ); + + static::getDatabase()->createDocument('home', new Document([ + '$id' => 'home1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'House 1', + 'renters' => [ + [ + '$id' => 'renter1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Occupant 1', + 'floor' => [ + '$id' => 'floor1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Floor 1', + ], + ], + ], + ])); + + $home1 = static::getDatabase()->getDocument('home', 'home1'); + $this->assertEquals('renter1', $home1['renters'][0]['$id']); + $this->assertEquals('floor1', $home1['renters'][0]['floor']['$id']); + $this->assertArrayNotHasKey('home', $home1['renters'][0]); + $this->assertArrayNotHasKey('renters', $home1['renters'][0]['floor']); + + static::getDatabase()->createDocument('floors', new Document([ + '$id' => 'floor2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Floor 2', + 'renters' => [ + [ + '$id' => 'renter2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Occupant 2', + 'home' => [ + '$id' => 'home2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'House 2', + ], + ], + ], + ])); + + $floor2 = static::getDatabase()->getDocument('floors', 'floor2'); + $this->assertEquals('renter2', $floor2['renters'][0]['$id']); + $this->assertArrayNotHasKey('floor', $floor2['renters'][0]); + $this->assertEquals('home2', $floor2['renters'][0]['home']['$id']); + $this->assertArrayNotHasKey('renter', $floor2['renters'][0]['home']); + } + + public function testNestedOneToMany_ManyToManyRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('owners'); + static::getDatabase()->createCollection('cats'); + static::getDatabase()->createCollection('toys'); + + static::getDatabase()->createAttribute('owners', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('cats', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('toys', 'name', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'owners', + relatedCollection: 'cats', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + twoWayKey: 'owner' + ); + static::getDatabase()->createRelationship( + collection: 'cats', + relatedCollection: 'toys', + type: Database::RELATION_MANY_TO_MANY, + twoWay: true + ); + + static::getDatabase()->createDocument('owners', new Document([ + '$id' => 'owner1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Owner 1', + 'cats' => [ + [ + '$id' => 'cat1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Pet 1', + 'toys' => [ + [ + '$id' => 'toy1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Toy 1', + ], + ], + ], + ], + ])); + + $owner1 = static::getDatabase()->getDocument('owners', 'owner1'); + $this->assertEquals('cat1', $owner1['cats'][0]['$id']); + $this->assertArrayNotHasKey('owner', $owner1['cats'][0]); + $this->assertEquals('toy1', $owner1['cats'][0]['toys'][0]['$id']); + $this->assertArrayNotHasKey('cats', $owner1['cats'][0]['toys'][0]); + + static::getDatabase()->createDocument('toys', new Document([ + '$id' => 'toy2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Toy 2', + 'cats' => [ + [ + '$id' => 'cat2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Pet 2', + 'owner' => [ + '$id' => 'owner2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Owner 2', + ], + ], + ], + ])); + + $toy2 = static::getDatabase()->getDocument('toys', 'toy2'); + $this->assertEquals('cat2', $toy2['cats'][0]['$id']); + $this->assertArrayNotHasKey('toys', $toy2['cats'][0]); + $this->assertEquals('owner2', $toy2['cats'][0]['owner']['$id']); + $this->assertArrayNotHasKey('cats', $toy2['cats'][0]['owner']); + } + + public function testNestedManyToOne_OneToOneRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('towns'); + static::getDatabase()->createCollection('homelands'); + static::getDatabase()->createCollection('capitals'); + + static::getDatabase()->createAttribute('towns', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('homelands', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('capitals', 'name', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'towns', + relatedCollection: 'homelands', + type: Database::RELATION_MANY_TO_ONE, + twoWay: true, + id: 'homeland' + ); + static::getDatabase()->createRelationship( + collection: 'homelands', + relatedCollection: 'capitals', + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + id: 'capital', + twoWayKey: 'homeland' + ); + + static::getDatabase()->createDocument('towns', new Document([ + '$id' => 'town1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'City 1', + 'homeland' => [ + '$id' => 'homeland1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Country 1', + 'capital' => [ + '$id' => 'capital1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Flag 1', + ], + ], + ])); + + $town1 = static::getDatabase()->getDocument('towns', 'town1'); + $this->assertEquals('homeland1', $town1['homeland']['$id']); + $this->assertArrayNotHasKey('towns', $town1['homeland']); + $this->assertEquals('capital1', $town1['homeland']['capital']['$id']); + $this->assertArrayNotHasKey('homeland', $town1['homeland']['capital']); + + static::getDatabase()->createDocument('capitals', new Document([ + '$id' => 'capital2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Flag 2', + 'homeland' => [ + '$id' => 'homeland2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Country 2', + 'towns' => [ + [ + '$id' => 'town2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Town 2', + ], + [ + '$id' => 'town3', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Town 3', + ], + ], + ], + ])); + + $capital2 = static::getDatabase()->getDocument('capitals', 'capital2'); + $this->assertEquals('homeland2', $capital2['homeland']['$id']); + $this->assertArrayNotHasKey('capital', $capital2['homeland']); + $this->assertEquals(2, \count($capital2['homeland']['towns'])); + $this->assertEquals('town2', $capital2['homeland']['towns'][0]['$id']); + $this->assertEquals('town3', $capital2['homeland']['towns'][1]['$id']); + } + + public function testNestedManyToOne_OneToManyRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('players'); + static::getDatabase()->createCollection('teams'); + static::getDatabase()->createCollection('supporters'); + + static::getDatabase()->createAttribute('players', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('teams', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('supporters', 'name', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'players', + relatedCollection: 'teams', + type: Database::RELATION_MANY_TO_ONE, + twoWay: true, + id: 'team' + ); + static::getDatabase()->createRelationship( + collection: 'teams', + relatedCollection: 'supporters', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + id: 'supporters', + twoWayKey: 'team' + ); + + static::getDatabase()->createDocument('players', new Document([ + '$id' => 'player1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Player 1', + 'team' => [ + '$id' => 'team1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Team 1', + 'supporters' => [ + [ + '$id' => 'supporter1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Supporter 1', + ], + [ + '$id' => 'supporter2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Supporter 2', + ], + ], + ], + ])); + + $player1 = static::getDatabase()->getDocument('players', 'player1'); + $this->assertEquals('team1', $player1['team']['$id']); + $this->assertArrayNotHasKey('players', $player1['team']); + $this->assertEquals(2, \count($player1['team']['supporters'])); + $this->assertEquals('supporter1', $player1['team']['supporters'][0]['$id']); + $this->assertEquals('supporter2', $player1['team']['supporters'][1]['$id']); + + static::getDatabase()->createDocument('supporters', new Document([ + '$id' => 'supporter3', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Supporter 3', + 'team' => [ + '$id' => 'team2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Team 2', + 'players' => [ + [ + '$id' => 'player2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Player 2', + ], + [ + '$id' => 'player3', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Player 3', + ], + ], + ], + ])); + + $supporter3 = static::getDatabase()->getDocument('supporters', 'supporter3'); + $this->assertEquals('team2', $supporter3['team']['$id']); + $this->assertArrayNotHasKey('supporters', $supporter3['team']); + $this->assertEquals(2, \count($supporter3['team']['players'])); + $this->assertEquals('player2', $supporter3['team']['players'][0]['$id']); + $this->assertEquals('player3', $supporter3['team']['players'][1]['$id']); + } + + public function testNestedManyToOne_ManyToOne(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('cows'); + static::getDatabase()->createCollection('farms'); + static::getDatabase()->createCollection('farmer'); + + static::getDatabase()->createAttribute('cows', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('farms', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('farmer', 'name', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'cows', + relatedCollection: 'farms', + type: Database::RELATION_MANY_TO_ONE, + twoWay: true, + id: 'farm' + ); + static::getDatabase()->createRelationship( + collection: 'farms', + relatedCollection: 'farmer', + type: Database::RELATION_MANY_TO_ONE, + twoWay: true, + id: 'farmer' + ); + + static::getDatabase()->createDocument('cows', new Document([ + '$id' => 'cow1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Cow 1', + 'farm' => [ + '$id' => 'farm1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Farm 1', + 'farmer' => [ + '$id' => 'farmer1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Farmer 1', + ], + ], + ])); + + $cow1 = static::getDatabase()->getDocument('cows', 'cow1'); + $this->assertEquals('farm1', $cow1['farm']['$id']); + $this->assertArrayNotHasKey('cows', $cow1['farm']); + $this->assertEquals('farmer1', $cow1['farm']['farmer']['$id']); + $this->assertArrayNotHasKey('farms', $cow1['farm']['farmer']); + + static::getDatabase()->createDocument('farmer', new Document([ + '$id' => 'farmer2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Farmer 2', + 'farms' => [ + [ + '$id' => 'farm2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Farm 2', + 'cows' => [ + [ + '$id' => 'cow2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Cow 2', + ], + [ + '$id' => 'cow3', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Cow 3', + ], + ], + ], + ], + ])); + + $farmer2 = static::getDatabase()->getDocument('farmer', 'farmer2'); + $this->assertEquals('farm2', $farmer2['farms'][0]['$id']); + $this->assertArrayNotHasKey('farmer', $farmer2['farms'][0]); + $this->assertEquals(2, \count($farmer2['farms'][0]['cows'])); + $this->assertEquals('cow2', $farmer2['farms'][0]['cows'][0]['$id']); + $this->assertEquals('cow3', $farmer2['farms'][0]['cows'][1]['$id']); + } + + public function testNestedManyToOne_ManyToManyRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('books'); + static::getDatabase()->createCollection('entrants'); + static::getDatabase()->createCollection('rooms'); + + static::getDatabase()->createAttribute('books', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('entrants', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('rooms', 'name', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'books', + relatedCollection: 'entrants', + type: Database::RELATION_MANY_TO_ONE, + twoWay: true, + id: 'entrant' + ); + static::getDatabase()->createRelationship( + collection: 'entrants', + relatedCollection: 'rooms', + type: Database::RELATION_MANY_TO_MANY, + twoWay: true, + ); + + static::getDatabase()->createDocument('books', new Document([ + '$id' => 'book1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Book 1', + 'entrant' => [ + '$id' => 'entrant1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Entrant 1', + 'rooms' => [ + [ + '$id' => 'class1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Class 1', + ], + [ + '$id' => 'class2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Class 2', + ], + ], + ], + ])); + + $book1 = static::getDatabase()->getDocument('books', 'book1'); + $this->assertEquals('entrant1', $book1['entrant']['$id']); + $this->assertArrayNotHasKey('books', $book1['entrant']); + $this->assertEquals(2, \count($book1['entrant']['rooms'])); + $this->assertEquals('class1', $book1['entrant']['rooms'][0]['$id']); + $this->assertEquals('class2', $book1['entrant']['rooms'][1]['$id']); + } + + public function testNestedManyToMany_OneToOneRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('stones'); + static::getDatabase()->createCollection('hearths'); + static::getDatabase()->createCollection('plots'); + + static::getDatabase()->createAttribute('stones', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('hearths', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('plots', 'name', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'stones', + relatedCollection: 'hearths', + type: Database::RELATION_MANY_TO_MANY, + twoWay: true, + ); + static::getDatabase()->createRelationship( + collection: 'hearths', + relatedCollection: 'plots', + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + id: 'plot', + twoWayKey: 'hearth' + ); + + static::getDatabase()->createDocument('stones', new Document([ + '$id' => 'stone1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Building 1', + 'hearths' => [ + [ + '$id' => 'hearth1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'House 1', + 'plot' => [ + '$id' => 'plot1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Address 1', + ], + ], + [ + '$id' => 'hearth2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'House 2', + 'plot' => [ + '$id' => 'plot2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Address 2', + ], + ], + ], + ])); + + $stone1 = static::getDatabase()->getDocument('stones', 'stone1'); + $this->assertEquals(2, \count($stone1['hearths'])); + $this->assertEquals('hearth1', $stone1['hearths'][0]['$id']); + $this->assertEquals('hearth2', $stone1['hearths'][1]['$id']); + $this->assertArrayNotHasKey('stone', $stone1['hearths'][0]); + $this->assertEquals('plot1', $stone1['hearths'][0]['plot']['$id']); + $this->assertEquals('plot2', $stone1['hearths'][1]['plot']['$id']); + $this->assertArrayNotHasKey('hearth', $stone1['hearths'][0]['plot']); + + static::getDatabase()->createDocument('plots', new Document([ + '$id' => 'plot3', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Address 3', + 'hearth' => [ + '$id' => 'hearth3', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Hearth 3', + 'stones' => [ + [ + '$id' => 'stone2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Stone 2', + ], + ], + ], + ])); + + $plot3 = static::getDatabase()->getDocument('plots', 'plot3'); + $this->assertEquals('hearth3', $plot3['hearth']['$id']); + $this->assertArrayNotHasKey('plot', $plot3['hearth']); + $this->assertEquals('stone2', $plot3['hearth']['stones'][0]['$id']); + $this->assertArrayNotHasKey('hearths', $plot3['hearth']['stones'][0]); + } + + public function testNestedManyToMany_OneToManyRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('groups'); + static::getDatabase()->createCollection('tounaments'); + static::getDatabase()->createCollection('prizes'); + + static::getDatabase()->createAttribute('groups', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('tounaments', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('prizes', 'name', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'groups', + relatedCollection: 'tounaments', + type: Database::RELATION_MANY_TO_MANY, + twoWay: true, + ); + static::getDatabase()->createRelationship( + collection: 'tounaments', + relatedCollection: 'prizes', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + id: 'prizes', + twoWayKey: 'tounament' + ); + + static::getDatabase()->createDocument('groups', new Document([ + '$id' => 'group1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Group 1', + 'tounaments' => [ + [ + '$id' => 'tounament1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Tounament 1', + 'prizes' => [ + [ + '$id' => 'prize1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Prize 1', + ], + [ + '$id' => 'prize2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Prize 2', + ], + ], + ], + [ + '$id' => 'tounament2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Tounament 2', + 'prizes' => [ + [ + '$id' => 'prize3', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Prize 3', + ], + [ + '$id' => 'prize4', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Prize 4', + ], + ], + ], + ], + ])); + + $group1 = static::getDatabase()->getDocument('groups', 'group1'); + $this->assertEquals(2, \count($group1['tounaments'])); + $this->assertEquals('tounament1', $group1['tounaments'][0]['$id']); + $this->assertEquals('tounament2', $group1['tounaments'][1]['$id']); + $this->assertArrayNotHasKey('group', $group1['tounaments'][0]); + $this->assertEquals(2, \count($group1['tounaments'][0]['prizes'])); + $this->assertEquals('prize1', $group1['tounaments'][0]['prizes'][0]['$id']); + $this->assertEquals('prize2', $group1['tounaments'][0]['prizes'][1]['$id']); + $this->assertArrayNotHasKey('tounament', $group1['tounaments'][0]['prizes'][0]); + } + + public function testNestedManyToMany_ManyToOneRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('platforms'); + static::getDatabase()->createCollection('games'); + static::getDatabase()->createCollection('publishers'); + + static::getDatabase()->createAttribute('platforms', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('games', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('publishers', 'name', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'platforms', + relatedCollection: 'games', + type: Database::RELATION_MANY_TO_MANY, + twoWay: true, + ); + static::getDatabase()->createRelationship( + collection: 'games', + relatedCollection: 'publishers', + type: Database::RELATION_MANY_TO_ONE, + twoWay: true, + id: 'publisher', + twoWayKey: 'games' + ); + + static::getDatabase()->createDocument('platforms', new Document([ + '$id' => 'platform1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Platform 1', + 'games' => [ + [ + '$id' => 'game1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Game 1', + 'publisher' => [ + '$id' => 'publisher1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Publisher 1', + ], + ], + [ + '$id' => 'game2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Game 2', + 'publisher' => [ + '$id' => 'publisher2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Publisher 2', + ], + ], + ] + ])); + + $platform1 = static::getDatabase()->getDocument('platforms', 'platform1'); + $this->assertEquals(2, \count($platform1['games'])); + $this->assertEquals('game1', $platform1['games'][0]['$id']); + $this->assertEquals('game2', $platform1['games'][1]['$id']); + $this->assertArrayNotHasKey('platforms', $platform1['games'][0]); + $this->assertEquals('publisher1', $platform1['games'][0]['publisher']['$id']); + $this->assertEquals('publisher2', $platform1['games'][1]['publisher']['$id']); + $this->assertArrayNotHasKey('games', $platform1['games'][0]['publisher']); + + static::getDatabase()->createDocument('publishers', new Document([ + '$id' => 'publisher3', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Publisher 3', + 'games' => [ + [ + '$id' => 'game3', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Game 3', + 'platforms' => [ + [ + '$id' => 'platform2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Platform 2', + ] + ], + ], + ], + ])); + + $publisher3 = static::getDatabase()->getDocument('publishers', 'publisher3'); + $this->assertEquals(1, \count($publisher3['games'])); + $this->assertEquals('game3', $publisher3['games'][0]['$id']); + $this->assertArrayNotHasKey('publisher', $publisher3['games'][0]); + $this->assertEquals('platform2', $publisher3['games'][0]['platforms'][0]['$id']); + $this->assertArrayNotHasKey('games', $publisher3['games'][0]['platforms'][0]); + } + + public function testNestedManyToMany_ManyToManyRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('sauces'); + static::getDatabase()->createCollection('pizzas'); + static::getDatabase()->createCollection('toppings'); + + static::getDatabase()->createAttribute('sauces', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('pizzas', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('toppings', 'name', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'sauces', + relatedCollection: 'pizzas', + type: Database::RELATION_MANY_TO_MANY, + twoWay: true, + ); + static::getDatabase()->createRelationship( + collection: 'pizzas', + relatedCollection: 'toppings', + type: Database::RELATION_MANY_TO_MANY, + twoWay: true, + id: 'toppings', + twoWayKey: 'pizzas' + ); + + static::getDatabase()->createDocument('sauces', new Document([ + '$id' => 'sauce1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Sauce 1', + 'pizzas' => [ + [ + '$id' => 'pizza1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Pizza 1', + 'toppings' => [ + [ + '$id' => 'topping1', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Topping 1', + ], + [ + '$id' => 'topping2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Topping 2', + ], + ], + ], + [ + '$id' => 'pizza2', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Pizza 2', + 'toppings' => [ + [ + '$id' => 'topping3', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Topping 3', + ], + [ + '$id' => 'topping4', + '$permissions' => [ + Permission::read(Role::any()), + ], + 'name' => 'Topping 4', + ], + ], + ], + ] + ])); + + $sauce1 = static::getDatabase()->getDocument('sauces', 'sauce1'); + $this->assertEquals(2, \count($sauce1['pizzas'])); + $this->assertEquals('pizza1', $sauce1['pizzas'][0]['$id']); + $this->assertEquals('pizza2', $sauce1['pizzas'][1]['$id']); + $this->assertArrayNotHasKey('sauces', $sauce1['pizzas'][0]); + $this->assertEquals(2, \count($sauce1['pizzas'][0]['toppings'])); + $this->assertEquals('topping1', $sauce1['pizzas'][0]['toppings'][0]['$id']); + $this->assertEquals('topping2', $sauce1['pizzas'][0]['toppings'][1]['$id']); + $this->assertArrayNotHasKey('pizzas', $sauce1['pizzas'][0]['toppings'][0]); + $this->assertEquals(2, \count($sauce1['pizzas'][1]['toppings'])); + $this->assertEquals('topping3', $sauce1['pizzas'][1]['toppings'][0]['$id']); + $this->assertEquals('topping4', $sauce1['pizzas'][1]['toppings'][1]['$id']); + $this->assertArrayNotHasKey('pizzas', $sauce1['pizzas'][1]['toppings'][0]); + } + + public function testInheritRelationshipPermissions(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('lawns', permissions: [Permission::create(Role::any())], documentSecurity: true); + static::getDatabase()->createCollection('trees', permissions: [Permission::create(Role::any())], documentSecurity: true); + static::getDatabase()->createCollection('birds', permissions: [Permission::create(Role::any())], documentSecurity: true); + + static::getDatabase()->createAttribute('lawns', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('trees', 'name', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('birds', 'name', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'lawns', + relatedCollection: 'trees', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + twoWayKey: 'lawn', + onDelete: Database::RELATION_MUTATE_CASCADE, + ); + static::getDatabase()->createRelationship( + collection: 'trees', + relatedCollection: 'birds', + type: Database::RELATION_MANY_TO_MANY, + twoWay: true, + onDelete: Database::RELATION_MUTATE_SET_NULL, + ); + + $permissions = [ + Permission::read(Role::any()), + Permission::read(Role::user('user1')), + Permission::update(Role::user('user1')), + Permission::delete(Role::user('user2')), + ]; + + static::getDatabase()->createDocument('lawns', new Document([ + '$id' => 'lawn1', + '$permissions' => $permissions, + 'name' => 'Lawn 1', + 'trees' => [ + [ + '$id' => 'tree1', + 'name' => 'Tree 1', + 'birds' => [ + [ + '$id' => 'bird1', + 'name' => 'Bird 1', + ], + [ + '$id' => 'bird2', + 'name' => 'Bird 2', + ], + ], + ], + ], + ])); + + $lawn1 = static::getDatabase()->getDocument('lawns', 'lawn1'); + $this->assertEquals($permissions, $lawn1->getPermissions()); + $this->assertEquals($permissions, $lawn1['trees'][0]->getPermissions()); + $this->assertEquals($permissions, $lawn1['trees'][0]['birds'][0]->getPermissions()); + $this->assertEquals($permissions, $lawn1['trees'][0]['birds'][1]->getPermissions()); + + $tree1 = static::getDatabase()->getDocument('trees', 'tree1'); + $this->assertEquals($permissions, $tree1->getPermissions()); + $this->assertEquals($permissions, $tree1['lawn']->getPermissions()); + $this->assertEquals($permissions, $tree1['birds'][0]->getPermissions()); + $this->assertEquals($permissions, $tree1['birds'][1]->getPermissions()); + } + + /** + * @depends testInheritRelationshipPermissions + */ + public function testEnforceRelationshipPermissions(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + Authorization::cleanRoles(); + Authorization::setRole(Role::any()->toString()); + $lawn1 = static::getDatabase()->getDocument('lawns', 'lawn1'); + $this->assertEquals('Lawn 1', $lawn1['name']); + + // Try update root document + try { + static::getDatabase()->updateDocument( + 'lawns', + $lawn1->getId(), + $lawn1->setAttribute('name', 'Lawn 1 Updated') + ); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertEquals('Missing "update" permission for role "user:user1". Only "["any"]" scopes are allowed and "["user:user1"]" was given.', $e->getMessage()); + } + + // Try delete root document + try { + static::getDatabase()->deleteDocument( + 'lawns', + $lawn1->getId(), + ); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertEquals('Missing "delete" permission for role "user:user2". Only "["any"]" scopes are allowed and "["user:user2"]" was given.', $e->getMessage()); + } + + $tree1 = static::getDatabase()->getDocument('trees', 'tree1'); + + // Try update nested document + try { + static::getDatabase()->updateDocument( + 'trees', + $tree1->getId(), + $tree1->setAttribute('name', 'Tree 1 Updated') + ); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertEquals('Missing "update" permission for role "user:user1". Only "["any"]" scopes are allowed and "["user:user1"]" was given.', $e->getMessage()); + } + + // Try delete nested document + try { + static::getDatabase()->deleteDocument( + 'trees', + $tree1->getId(), + ); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertEquals('Missing "delete" permission for role "user:user2". Only "["any"]" scopes are allowed and "["user:user2"]" was given.', $e->getMessage()); + } + + $bird1 = static::getDatabase()->getDocument('birds', 'bird1'); + + // Try update multi-level nested document + try { + static::getDatabase()->updateDocument( + 'birds', + $bird1->getId(), + $bird1->setAttribute('name', 'Bird 1 Updated') + ); + $this->fail('Failed to throw exception when updating document with missing permissions'); + } catch (Exception $e) { + $this->assertEquals('Missing "update" permission for role "user:user1". Only "["any"]" scopes are allowed and "["user:user1"]" was given.', $e->getMessage()); + } + + // Try delete multi-level nested document + try { + static::getDatabase()->deleteDocument( + 'birds', + $bird1->getId(), + ); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertEquals('Missing "delete" permission for role "user:user2". Only "["any"]" scopes are allowed and "["user:user2"]" was given.', $e->getMessage()); + } + + Authorization::setRole(Role::user('user1')->toString()); + + $bird1 = static::getDatabase()->getDocument('birds', 'bird1'); + + // Try update multi-level nested document + $bird1 = static::getDatabase()->updateDocument( + 'birds', + $bird1->getId(), + $bird1->setAttribute('name', 'Bird 1 Updated') + ); + + $this->assertEquals('Bird 1 Updated', $bird1['name']); + + Authorization::setRole(Role::user('user2')->toString()); + + // Try delete multi-level nested document + $deleted = static::getDatabase()->deleteDocument( + 'birds', + $bird1->getId(), + ); + + $this->assertEquals(true, $deleted); + $tree1 = static::getDatabase()->getDocument('trees', 'tree1'); + $this->assertEquals(1, count($tree1['birds'])); + + // Try update nested document + $tree1 = static::getDatabase()->updateDocument( + 'trees', + $tree1->getId(), + $tree1->setAttribute('name', 'Tree 1 Updated') + ); + + $this->assertEquals('Tree 1 Updated', $tree1['name']); + + // Try delete nested document + $deleted = static::getDatabase()->deleteDocument( + 'trees', + $tree1->getId(), + ); + + $this->assertEquals(true, $deleted); + $lawn1 = static::getDatabase()->getDocument('lawns', 'lawn1'); + $this->assertEquals(0, count($lawn1['trees'])); + + // Create document with no permissions + static::getDatabase()->createDocument('lawns', new Document([ + '$id' => 'lawn2', + 'name' => 'Lawn 2', + 'trees' => [ + [ + '$id' => 'tree2', + 'name' => 'Tree 2', + 'birds' => [ + [ + '$id' => 'bird3', + 'name' => 'Bird 3', + ], + ], + ], + ], + ])); + + $lawn2 = static::getDatabase()->getDocument('lawns', 'lawn2'); + $this->assertEquals(true, $lawn2->isEmpty()); + + $tree2 = static::getDatabase()->getDocument('trees', 'tree2'); + $this->assertEquals(true, $tree2->isEmpty()); + + $bird3 = static::getDatabase()->getDocument('birds', 'bird3'); + $this->assertEquals(true, $bird3->isEmpty()); + } + + public function testExceedMaxDepthOneToMany(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + $level1Collection = 'level1OneToMany'; + $level2Collection = 'level2OneToMany'; + $level3Collection = 'level3OneToMany'; + $level4Collection = 'level4OneToMany'; + + static::getDatabase()->createCollection($level1Collection); + static::getDatabase()->createCollection($level2Collection); + static::getDatabase()->createCollection($level3Collection); + static::getDatabase()->createCollection($level4Collection); + + static::getDatabase()->createRelationship( + collection: $level1Collection, + relatedCollection: $level2Collection, + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + ); + static::getDatabase()->createRelationship( + collection: $level2Collection, + relatedCollection: $level3Collection, + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + ); + static::getDatabase()->createRelationship( + collection: $level3Collection, + relatedCollection: $level4Collection, + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + ); + + // Exceed create depth + $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ + '$id' => 'level1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + $level2Collection => [ + [ + '$id' => 'level2', + $level3Collection => [ + [ + '$id' => 'level3', + $level4Collection => [ + [ + '$id' => 'level4', + ], + ], + ], + ], + ], + ], + ])); + $this->assertEquals(1, count($level1[$level2Collection])); + $this->assertEquals('level2', $level1[$level2Collection][0]->getId()); + $this->assertEquals(1, count($level1[$level2Collection][0][$level3Collection])); + $this->assertEquals('level3', $level1[$level2Collection][0][$level3Collection][0]->getId()); + $this->assertArrayNotHasKey('level4', $level1[$level2Collection][0][$level3Collection][0]); + + // Make sure level 4 document was not created + $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); + $this->assertEquals(0, count($level3[$level4Collection])); + $level4 = static::getDatabase()->getDocument($level4Collection, 'level4'); + $this->assertTrue($level4->isEmpty()); + + // Exceed fetch depth + $level1 = static::getDatabase()->getDocument($level1Collection, 'level1'); + $this->assertEquals(1, count($level1[$level2Collection])); + $this->assertEquals('level2', $level1[$level2Collection][0]->getId()); + $this->assertEquals(1, count($level1[$level2Collection][0][$level3Collection])); + $this->assertEquals('level3', $level1[$level2Collection][0][$level3Collection][0]->getId()); + $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][0][$level3Collection][0]); + + + // Exceed update depth + $level1 = static::getDatabase()->updateDocument( + $level1Collection, + 'level1', + $level1 + ->setAttribute($level2Collection, [new Document([ + '$id' => 'level2new', + $level3Collection => [ + [ + '$id' => 'level3new', + $level4Collection => [ + [ + '$id' => 'level4new', + ], + ], + ], + ], + ])]) + ); + $this->assertEquals(1, count($level1[$level2Collection])); + $this->assertEquals('level2new', $level1[$level2Collection][0]->getId()); + $this->assertEquals(1, count($level1[$level2Collection][0][$level3Collection])); + $this->assertEquals('level3new', $level1[$level2Collection][0][$level3Collection][0]->getId()); + $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][0][$level3Collection][0]); + + // Make sure level 4 document was not created + $level3 = static::getDatabase()->getDocument($level3Collection, 'level3new'); + $this->assertEquals(0, count($level3[$level4Collection])); + $level4 = static::getDatabase()->getDocument($level4Collection, 'level4new'); + $this->assertTrue($level4->isEmpty()); + } + + public function testExceedMaxDepthOneToOne(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + $level1Collection = 'level1OneToOne'; + $level2Collection = 'level2OneToOne'; + $level3Collection = 'level3OneToOne'; + $level4Collection = 'level4OneToOne'; + + static::getDatabase()->createCollection($level1Collection); + static::getDatabase()->createCollection($level2Collection); + static::getDatabase()->createCollection($level3Collection); + static::getDatabase()->createCollection($level4Collection); + + static::getDatabase()->createRelationship( + collection: $level1Collection, + relatedCollection: $level2Collection, + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + ); + static::getDatabase()->createRelationship( + collection: $level2Collection, + relatedCollection: $level3Collection, + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + ); + static::getDatabase()->createRelationship( + collection: $level3Collection, + relatedCollection: $level4Collection, + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + ); + + // Exceed create depth + $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ + '$id' => 'level1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + $level2Collection => [ + '$id' => 'level2', + $level3Collection => [ + '$id' => 'level3', + $level4Collection => [ + '$id' => 'level4', + ], + ], + ], + ])); + $this->assertArrayHasKey($level2Collection, $level1); + $this->assertEquals('level2', $level1[$level2Collection]->getId()); + $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); + $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); + $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); + + // Confirm the 4th level document does not exist + $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); + $this->assertNull($level3[$level4Collection]); + + // Create level 4 document + $level3->setAttribute($level4Collection, new Document([ + '$id' => 'level4', + ])); + $level3 = static::getDatabase()->updateDocument($level3Collection, $level3->getId(), $level3); + $this->assertEquals('level4', $level3[$level4Collection]->getId()); + + // Exceed fetch depth + $level1 = static::getDatabase()->getDocument($level1Collection, 'level1'); + $this->assertArrayHasKey($level2Collection, $level1); + $this->assertEquals('level2', $level1[$level2Collection]->getId()); + $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); + $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); + $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); + } + + public function testExceedMaxDepthOneToOneNull(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + $level1Collection = 'level1OneToOneNull'; + $level2Collection = 'level2OneToOneNull'; + $level3Collection = 'level3OneToOneNull'; + $level4Collection = 'level4OneToOneNull'; + + static::getDatabase()->createCollection($level1Collection); + static::getDatabase()->createCollection($level2Collection); + static::getDatabase()->createCollection($level3Collection); + static::getDatabase()->createCollection($level4Collection); + + static::getDatabase()->createRelationship( + collection: $level1Collection, + relatedCollection: $level2Collection, + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + ); + static::getDatabase()->createRelationship( + collection: $level2Collection, + relatedCollection: $level3Collection, + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + ); + static::getDatabase()->createRelationship( + collection: $level3Collection, + relatedCollection: $level4Collection, + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + ); + + $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ + '$id' => 'level1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + $level2Collection => [ + '$id' => 'level2', + $level3Collection => [ + '$id' => 'level3', + $level4Collection => [ + '$id' => 'level4', + ], + ], + ], + ])); + $this->assertArrayHasKey($level2Collection, $level1); + $this->assertEquals('level2', $level1[$level2Collection]->getId()); + $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); + $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); + $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); + + // Confirm the 4th level document does not exist + $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); + $this->assertNull($level3[$level4Collection]); + + // Create level 4 document + $level3->setAttribute($level4Collection, new Document([ + '$id' => 'level4', + ])); + $level3 = static::getDatabase()->updateDocument($level3Collection, $level3->getId(), $level3); + $this->assertEquals('level4', $level3[$level4Collection]->getId()); + $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); + $this->assertEquals('level4', $level3[$level4Collection]->getId()); + + // Exceed fetch depth + $level1 = static::getDatabase()->getDocument($level1Collection, 'level1'); + $this->assertArrayHasKey($level2Collection, $level1); + $this->assertEquals('level2', $level1[$level2Collection]->getId()); + $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); + $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); + $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); + } + + public function testExceedMaxDepthManyToOneParent(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + $level1Collection = 'level1ManyToOneParent'; + $level2Collection = 'level2ManyToOneParent'; + $level3Collection = 'level3ManyToOneParent'; + $level4Collection = 'level4ManyToOneParent'; + + static::getDatabase()->createCollection($level1Collection); + static::getDatabase()->createCollection($level2Collection); + static::getDatabase()->createCollection($level3Collection); + static::getDatabase()->createCollection($level4Collection); + + static::getDatabase()->createRelationship( + collection: $level1Collection, + relatedCollection: $level2Collection, + type: Database::RELATION_MANY_TO_ONE, + twoWay: true, + ); + static::getDatabase()->createRelationship( + collection: $level2Collection, + relatedCollection: $level3Collection, + type: Database::RELATION_MANY_TO_ONE, + twoWay: true, + ); + static::getDatabase()->createRelationship( + collection: $level3Collection, + relatedCollection: $level4Collection, + type: Database::RELATION_MANY_TO_ONE, + twoWay: true, + ); + + $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ + '$id' => 'level1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + $level2Collection => [ + '$id' => 'level2', + $level3Collection => [ + '$id' => 'level3', + $level4Collection => [ + '$id' => 'level4', + ], + ], + ], + ])); + $this->assertArrayHasKey($level2Collection, $level1); + $this->assertEquals('level2', $level1[$level2Collection]->getId()); + $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); + $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); + $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); + + // Confirm the 4th level document does not exist + $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); + $this->assertNull($level3[$level4Collection]); + + // Create level 4 document + $level3->setAttribute($level4Collection, new Document([ + '$id' => 'level4', + ])); + $level3 = static::getDatabase()->updateDocument($level3Collection, $level3->getId(), $level3); + $this->assertEquals('level4', $level3[$level4Collection]->getId()); + $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); + $this->assertEquals('level4', $level3[$level4Collection]->getId()); + + // Exceed fetch depth + $level1 = static::getDatabase()->getDocument($level1Collection, 'level1'); + $this->assertArrayHasKey($level2Collection, $level1); + $this->assertEquals('level2', $level1[$level2Collection]->getId()); + $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); + $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); + $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); + } + + public function testExceedMaxDepthOneToManyChild(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + $level1Collection = 'level1OneToManyChild'; + $level2Collection = 'level2OneToManyChild'; + $level3Collection = 'level3OneToManyChild'; + $level4Collection = 'level4OneToManyChild'; + + static::getDatabase()->createCollection($level1Collection); + static::getDatabase()->createCollection($level2Collection); + static::getDatabase()->createCollection($level3Collection); + static::getDatabase()->createCollection($level4Collection); + + static::getDatabase()->createRelationship( + collection: $level1Collection, + relatedCollection: $level2Collection, + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + ); + static::getDatabase()->createRelationship( + collection: $level2Collection, + relatedCollection: $level3Collection, + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + ); + static::getDatabase()->createRelationship( + collection: $level3Collection, + relatedCollection: $level4Collection, + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + ); + + $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ + '$id' => 'level1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + $level2Collection => [ + [ + '$id' => 'level2', + $level3Collection => [ + [ + '$id' => 'level3', + $level4Collection => [ + [ + '$id' => 'level4', + ], + ] + ], + ], + ], + ], + ])); + $this->assertArrayHasKey($level2Collection, $level1); + $this->assertEquals('level2', $level1[$level2Collection][0]->getId()); + $this->assertArrayHasKey($level3Collection, $level1[$level2Collection][0]); + $this->assertEquals('level3', $level1[$level2Collection][0][$level3Collection][0]->getId()); + $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][0][$level3Collection][0]); + + // Confirm the 4th level document does not exist + $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); + $this->assertEquals(0, count($level3[$level4Collection])); + + // Create level 4 document + $level3->setAttribute($level4Collection, [new Document([ + '$id' => 'level4', + ])]); + $level3 = static::getDatabase()->updateDocument($level3Collection, $level3->getId(), $level3); + $this->assertEquals('level4', $level3[$level4Collection][0]->getId()); + + // Verify level 4 document is set + $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); + $this->assertArrayHasKey($level4Collection, $level3); + $this->assertEquals('level4', $level3[$level4Collection][0]->getId()); + + // Exceed fetch depth + $level4 = static::getDatabase()->getDocument($level4Collection, 'level4'); + $this->assertArrayHasKey($level3Collection, $level4); + $this->assertEquals('level3', $level4[$level3Collection]->getId()); + $this->assertArrayHasKey($level2Collection, $level4[$level3Collection]); + $this->assertEquals('level2', $level4[$level3Collection][$level2Collection]->getId()); + $this->assertArrayNotHasKey($level1Collection, $level4[$level3Collection][$level2Collection]); + } + + public function testCreateRelationshipMissingCollection(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + $this->expectException(Exception::class); + $this->expectExceptionMessage('Collection not found'); + + static::getDatabase()->createRelationship( + collection: 'missing', + relatedCollection: 'missing', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + ); + } + + public function testCreateRelationshipMissingRelatedCollection(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('test'); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('Related collection not found'); + + static::getDatabase()->createRelationship( + collection: 'test', + relatedCollection: 'missing', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + ); + } + + public function testCreateDuplicateRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('test1'); + static::getDatabase()->createCollection('test2'); + + static::getDatabase()->createRelationship( + collection: 'test1', + relatedCollection: 'test2', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + ); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('Attribute already exists'); + + static::getDatabase()->createRelationship( + collection: 'test1', + relatedCollection: 'test2', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + ); + } + + public function testCreateInvalidRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('test3'); + static::getDatabase()->createCollection('test4'); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('Invalid relationship type'); + + static::getDatabase()->createRelationship( + collection: 'test3', + relatedCollection: 'test4', + type: 'invalid', + twoWay: true, + ); + } + + public function testDeleteMissingRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + $this->expectException(Exception::class); + $this->expectExceptionMessage('Attribute not found'); + + static::getDatabase()->deleteRelationship('test', 'test2'); + } + + public function testCreateInvalidIntValueRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('invalid1'); + static::getDatabase()->createCollection('invalid2'); + + static::getDatabase()->createRelationship( + collection: 'invalid1', + relatedCollection: 'invalid2', + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + ); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); + + static::getDatabase()->createDocument('invalid1', new Document([ + '$id' => ID::unique(), + 'invalid2' => 10, + ])); + } + + /** + * @depends testCreateInvalidIntValueRelationship + */ + public function testCreateInvalidObjectValueRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + $this->expectException(Exception::class); + $this->expectExceptionMessage('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); + + static::getDatabase()->createDocument('invalid1', new Document([ + '$id' => ID::unique(), + 'invalid2' => new \stdClass(), + ])); + } + + /** + * @depends testCreateInvalidIntValueRelationship + */ + public function testCreateInvalidArrayIntValueRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createRelationship( + collection: 'invalid1', + relatedCollection: 'invalid2', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + id: 'invalid3', + twoWayKey: 'invalid4', + ); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); + + static::getDatabase()->createDocument('invalid1', new Document([ + '$id' => ID::unique(), + 'invalid3' => [10], + ])); + } + + public function testCreateEmptyValueRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('null1'); + static::getDatabase()->createCollection('null2'); + + static::getDatabase()->createRelationship( + collection: 'null1', + relatedCollection: 'null2', + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + ); + static::getDatabase()->createRelationship( + collection: 'null1', + relatedCollection: 'null2', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + id: 'null3', + twoWayKey: 'null4', + ); + static::getDatabase()->createRelationship( + collection: 'null1', + relatedCollection: 'null2', + type: Database::RELATION_MANY_TO_ONE, + twoWay: true, + id: 'null4', + twoWayKey: 'null5', + ); + static::getDatabase()->createRelationship( + collection: 'null1', + relatedCollection: 'null2', + type: Database::RELATION_MANY_TO_MANY, + twoWay: true, + id: 'null6', + twoWayKey: 'null7', + ); + + $document = static::getDatabase()->createDocument('null1', new Document([ + '$id' => ID::unique(), + 'null2' => null, + ])); + + $this->assertEquals(null, $document->getAttribute('null2')); + + $document = static::getDatabase()->createDocument('null2', new Document([ + '$id' => ID::unique(), + 'null1' => null, + ])); + + $this->assertEquals(null, $document->getAttribute('null1')); + + $document = static::getDatabase()->createDocument('null1', new Document([ + '$id' => ID::unique(), + 'null3' => null, + ])); + + // One to many will be empty array instead of null + $this->assertEquals([], $document->getAttribute('null3')); + + $document = static::getDatabase()->createDocument('null2', new Document([ + '$id' => ID::unique(), + 'null4' => null, + ])); + + $this->assertEquals(null, $document->getAttribute('null4')); + + $document = static::getDatabase()->createDocument('null1', new Document([ + '$id' => ID::unique(), + 'null4' => null, + ])); + + $this->assertEquals(null, $document->getAttribute('null4')); + + $document = static::getDatabase()->createDocument('null2', new Document([ + '$id' => ID::unique(), + 'null5' => null, + ])); + + $this->assertEquals([], $document->getAttribute('null5')); + + $document = static::getDatabase()->createDocument('null1', new Document([ + '$id' => ID::unique(), + 'null6' => null, + ])); + + $this->assertEquals([], $document->getAttribute('null6')); + + $document = static::getDatabase()->createDocument('null2', new Document([ + '$id' => ID::unique(), + 'null7' => null, + ])); + + $this->assertEquals([], $document->getAttribute('null7')); + } + + public function testDeleteCollectionDeletesRelationships(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('testers'); + static::getDatabase()->createCollection('devices'); + + static::getDatabase()->createRelationship( + collection: 'testers', + relatedCollection: 'devices', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + twoWayKey: 'tester' + ); + + $testers = static::getDatabase()->getCollection('testers'); + $devices = static::getDatabase()->getCollection('devices'); + + $this->assertEquals(1, \count($testers->getAttribute('attributes'))); + $this->assertEquals(1, \count($devices->getAttribute('attributes'))); + $this->assertEquals(1, \count($devices->getAttribute('indexes'))); + + static::getDatabase()->deleteCollection('testers'); + + $testers = static::getDatabase()->getCollection('testers'); + $devices = static::getDatabase()->getCollection('devices'); + + $this->assertEquals(true, $testers->isEmpty()); + $this->assertEquals(0, \count($devices->getAttribute('attributes'))); + $this->assertEquals(0, \count($devices->getAttribute('indexes'))); + } + + public function testDeleteTwoWayRelationshipFromChild(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('drivers'); + static::getDatabase()->createCollection('licenses'); + + static::getDatabase()->createRelationship( + collection: 'drivers', + relatedCollection: 'licenses', + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + id: 'license', + twoWayKey: 'driver' + ); + + $drivers = static::getDatabase()->getCollection('drivers'); + $licenses = static::getDatabase()->getCollection('licenses'); + + $this->assertEquals(1, \count($drivers->getAttribute('attributes'))); + $this->assertEquals(1, \count($drivers->getAttribute('indexes'))); + $this->assertEquals(1, \count($licenses->getAttribute('attributes'))); + $this->assertEquals(1, \count($licenses->getAttribute('indexes'))); + + static::getDatabase()->deleteRelationship('licenses', 'driver'); + + $drivers = static::getDatabase()->getCollection('drivers'); + $licenses = static::getDatabase()->getCollection('licenses'); + + $this->assertEquals(0, \count($drivers->getAttribute('attributes'))); + $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); + $this->assertEquals(0, \count($licenses->getAttribute('attributes'))); + $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); + + static::getDatabase()->createRelationship( + collection: 'drivers', + relatedCollection: 'licenses', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + id: 'licenses', + twoWayKey: 'driver' + ); + + $drivers = static::getDatabase()->getCollection('drivers'); + $licenses = static::getDatabase()->getCollection('licenses'); + + $this->assertEquals(1, \count($drivers->getAttribute('attributes'))); + $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); + $this->assertEquals(1, \count($licenses->getAttribute('attributes'))); + $this->assertEquals(1, \count($licenses->getAttribute('indexes'))); + + static::getDatabase()->deleteRelationship('licenses', 'driver'); + + $drivers = static::getDatabase()->getCollection('drivers'); + $licenses = static::getDatabase()->getCollection('licenses'); + + $this->assertEquals(0, \count($drivers->getAttribute('attributes'))); + $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); + $this->assertEquals(0, \count($licenses->getAttribute('attributes'))); + $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); + + static::getDatabase()->createRelationship( + collection: 'licenses', + relatedCollection: 'drivers', + type: Database::RELATION_MANY_TO_ONE, + twoWay: true, + id: 'driver', + twoWayKey: 'licenses' + ); + + $drivers = static::getDatabase()->getCollection('drivers'); + $licenses = static::getDatabase()->getCollection('licenses'); + + $this->assertEquals(1, \count($drivers->getAttribute('attributes'))); + $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); + $this->assertEquals(1, \count($licenses->getAttribute('attributes'))); + $this->assertEquals(1, \count($licenses->getAttribute('indexes'))); + + static::getDatabase()->deleteRelationship('drivers', 'licenses'); + + $drivers = static::getDatabase()->getCollection('drivers'); + $licenses = static::getDatabase()->getCollection('licenses'); + + $this->assertEquals(0, \count($drivers->getAttribute('attributes'))); + $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); + $this->assertEquals(0, \count($licenses->getAttribute('attributes'))); + $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); + + static::getDatabase()->createRelationship( + collection: 'licenses', + relatedCollection: 'drivers', + type: Database::RELATION_MANY_TO_MANY, + twoWay: true, + id: 'drivers', + twoWayKey: 'licenses' + ); + + $drivers = static::getDatabase()->getCollection('drivers'); + $licenses = static::getDatabase()->getCollection('licenses'); + $junction = static::getDatabase()->getCollection('_' . $licenses->getInternalId() . '_' . $drivers->getInternalId()); + + $this->assertEquals(1, \count($drivers->getAttribute('attributes'))); + $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); + $this->assertEquals(1, \count($licenses->getAttribute('attributes'))); + $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); + $this->assertEquals(2, \count($junction->getAttribute('attributes'))); + $this->assertEquals(2, \count($junction->getAttribute('indexes'))); + + static::getDatabase()->deleteRelationship('drivers', 'licenses'); + + $drivers = static::getDatabase()->getCollection('drivers'); + $licenses = static::getDatabase()->getCollection('licenses'); + $junction = static::getDatabase()->getCollection('_licenses_drivers'); + + $this->assertEquals(0, \count($drivers->getAttribute('attributes'))); + $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); + $this->assertEquals(0, \count($licenses->getAttribute('attributes'))); + $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); + + $this->assertEquals(true, $junction->isEmpty()); + } + + public function testUpdateRelationshipToExistingKey(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('ovens'); + static::getDatabase()->createCollection('cakes'); + + static::getDatabase()->createAttribute('ovens', 'maxTemp', Database::VAR_INTEGER, 0, true); + static::getDatabase()->createAttribute('ovens', 'owner', Database::VAR_STRING, 255, true); + static::getDatabase()->createAttribute('cakes', 'height', Database::VAR_INTEGER, 0, true); + static::getDatabase()->createAttribute('cakes', 'colour', Database::VAR_STRING, 255, true); + + static::getDatabase()->createRelationship( + collection: 'ovens', + relatedCollection: 'cakes', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + id: 'cakes', + twoWayKey: 'oven' + ); + + try { + static::getDatabase()->updateRelationship('ovens', 'cakes', newKey: 'owner'); + $this->fail('Failed to throw exception'); + } catch (DuplicateException $e) { + $this->assertEquals('Attribute already exists', $e->getMessage()); + } + + try { + static::getDatabase()->updateRelationship('ovens', 'cakes', newTwoWayKey: 'height'); + $this->fail('Failed to throw exception'); + } catch (DuplicateException $e) { + $this->assertEquals('Related attribute already exists', $e->getMessage()); + } + } + + public function testOneToOneRelationshipKeyWithSymbols(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('$symbols_coll.ection1'); + static::getDatabase()->createCollection('$symbols_coll.ection2'); + + static::getDatabase()->createRelationship( + collection: '$symbols_coll.ection1', + relatedCollection: '$symbols_coll.ection2', + type: Database::RELATION_ONE_TO_ONE, + twoWay: true, + ); + + $doc1 = static::getDatabase()->createDocument('$symbols_coll.ection2', new Document([ + '$id' => ID::unique(), + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()) + ] + ])); + $doc2 = static::getDatabase()->createDocument('$symbols_coll.ection1', new Document([ + '$id' => ID::unique(), + '$symbols_coll.ection2' => $doc1->getId(), + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()) + ] + ])); + + $doc1 = static::getDatabase()->getDocument('$symbols_coll.ection2', $doc1->getId()); + $doc2 = static::getDatabase()->getDocument('$symbols_coll.ection1', $doc2->getId()); + + $this->assertEquals($doc2->getId(), $doc1->getAttribute('$symbols_coll.ection1')->getId()); + $this->assertEquals($doc1->getId(), $doc2->getAttribute('$symbols_coll.ection2')->getId()); + } + + public function testOneToManyRelationshipKeyWithSymbols(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('$symbols_coll.ection3'); + static::getDatabase()->createCollection('$symbols_coll.ection4'); + + static::getDatabase()->createRelationship( + collection: '$symbols_coll.ection3', + relatedCollection: '$symbols_coll.ection4', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + ); + + $doc1 = static::getDatabase()->createDocument('$symbols_coll.ection4', new Document([ + '$id' => ID::unique(), + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()) + ] + ])); + $doc2 = static::getDatabase()->createDocument('$symbols_coll.ection3', new Document([ + '$id' => ID::unique(), + '$symbols_coll.ection4' => [$doc1->getId()], + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()) + ] + ])); + + $doc1 = static::getDatabase()->getDocument('$symbols_coll.ection4', $doc1->getId()); + $doc2 = static::getDatabase()->getDocument('$symbols_coll.ection3', $doc2->getId()); + + $this->assertEquals($doc2->getId(), $doc1->getAttribute('$symbols_coll.ection3')->getId()); + $this->assertEquals($doc1->getId(), $doc2->getAttribute('$symbols_coll.ection4')[0]->getId()); + } + + public function testManyToOneRelationshipKeyWithSymbols(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('$symbols_coll.ection5'); + static::getDatabase()->createCollection('$symbols_coll.ection6'); + + static::getDatabase()->createRelationship( + collection: '$symbols_coll.ection5', + relatedCollection: '$symbols_coll.ection6', + type: Database::RELATION_MANY_TO_ONE, + twoWay: true, + ); + + $doc1 = static::getDatabase()->createDocument('$symbols_coll.ection6', new Document([ + '$id' => ID::unique(), + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()) + ] + ])); + $doc2 = static::getDatabase()->createDocument('$symbols_coll.ection5', new Document([ + '$id' => ID::unique(), + '$symbols_coll.ection6' => $doc1->getId(), + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()) + ] + ])); + + $doc1 = static::getDatabase()->getDocument('$symbols_coll.ection6', $doc1->getId()); + $doc2 = static::getDatabase()->getDocument('$symbols_coll.ection5', $doc2->getId()); + + $this->assertEquals($doc2->getId(), $doc1->getAttribute('$symbols_coll.ection5')[0]->getId()); + $this->assertEquals($doc1->getId(), $doc2->getAttribute('$symbols_coll.ection6')->getId()); + } + + public function testManyToManyRelationshipKeyWithSymbols(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('$symbols_coll.ection7'); + static::getDatabase()->createCollection('$symbols_coll.ection8'); + + static::getDatabase()->createRelationship( + collection: '$symbols_coll.ection7', + relatedCollection: '$symbols_coll.ection8', + type: Database::RELATION_MANY_TO_MANY, + twoWay: true, + ); + + $doc1 = static::getDatabase()->createDocument('$symbols_coll.ection8', new Document([ + '$id' => ID::unique(), + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()) + ] + ])); + $doc2 = static::getDatabase()->createDocument('$symbols_coll.ection7', new Document([ + '$id' => ID::unique(), + '$symbols_coll.ection8' => [$doc1->getId()], + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()) + ] + ])); + + $doc1 = static::getDatabase()->getDocument('$symbols_coll.ection8', $doc1->getId()); + $doc2 = static::getDatabase()->getDocument('$symbols_coll.ection7', $doc2->getId()); + + $this->assertEquals($doc2->getId(), $doc1->getAttribute('$symbols_coll.ection7')[0]->getId()); + $this->assertEquals($doc1->getId(), $doc2->getAttribute('$symbols_coll.ection8')[0]->getId()); + } + + public function testCascadeMultiDelete(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('cascadeMultiDelete1'); + static::getDatabase()->createCollection('cascadeMultiDelete2'); + static::getDatabase()->createCollection('cascadeMultiDelete3'); + + static::getDatabase()->createRelationship( + collection: 'cascadeMultiDelete1', + relatedCollection: 'cascadeMultiDelete2', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + onDelete: Database::RELATION_MUTATE_CASCADE + ); + + static::getDatabase()->createRelationship( + collection: 'cascadeMultiDelete2', + relatedCollection: 'cascadeMultiDelete3', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + onDelete: Database::RELATION_MUTATE_CASCADE + ); + + $root = static::getDatabase()->createDocument('cascadeMultiDelete1', new Document([ + '$id' => 'cascadeMultiDelete1', + '$permissions' => [ + Permission::read(Role::any()), + Permission::delete(Role::any()) + ], + 'cascadeMultiDelete2' => [ + [ + '$id' => 'cascadeMultiDelete2', + '$permissions' => [ + Permission::read(Role::any()), + Permission::delete(Role::any()) + ], + 'cascadeMultiDelete3' => [ + [ + '$id' => 'cascadeMultiDelete3', + '$permissions' => [ + Permission::read(Role::any()), + Permission::delete(Role::any()) + ], + ], + ], + ], + ], + ])); + + $this->assertCount(1, $root->getAttribute('cascadeMultiDelete2')); + $this->assertCount(1, $root->getAttribute('cascadeMultiDelete2')[0]->getAttribute('cascadeMultiDelete3')); + + $this->assertEquals(true, static::getDatabase()->deleteDocument('cascadeMultiDelete1', $root->getId())); + + $multi2 = static::getDatabase()->getDocument('cascadeMultiDelete2', 'cascadeMultiDelete2'); + $this->assertEquals(true, $multi2->isEmpty()); + + $multi3 = static::getDatabase()->getDocument('cascadeMultiDelete3', 'cascadeMultiDelete3'); + $this->assertEquals(true, $multi3->isEmpty()); + } + + public function testCollectionUpdate(): Document + { + $collection = static::getDatabase()->createCollection('collectionUpdate', permissions: [ + Permission::create(Role::users()), + Permission::read(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()) + ], documentSecurity: false); + + $this->assertInstanceOf(Document::class, $collection); + + $collection = static::getDatabase()->getCollection('collectionUpdate'); + + $this->assertFalse($collection->getAttribute('documentSecurity')); + $this->assertIsArray($collection->getPermissions()); + $this->assertCount(4, $collection->getPermissions()); + + $collection = static::getDatabase()->updateCollection('collectionUpdate', [], true); + + $this->assertTrue($collection->getAttribute('documentSecurity')); + $this->assertIsArray($collection->getPermissions()); + $this->assertEmpty($collection->getPermissions()); + + $collection = static::getDatabase()->getCollection('collectionUpdate'); + + $this->assertTrue($collection->getAttribute('documentSecurity')); + $this->assertIsArray($collection->getPermissions()); + $this->assertEmpty($collection->getPermissions()); + + return $collection; + } + + /** + * @depends testCollectionUpdate + */ + public function testCollectionUpdatePermissionsThrowException(Document $collection): void + { + $this->expectException(InvalidArgumentException::class); + static::getDatabase()->updateCollection($collection->getId(), permissions: [ + 'i dont work' + ], documentSecurity: false); + } + + public function testCollectionPermissions(): Document + { + $collection = static::getDatabase()->createCollection('collectionSecurity', permissions: [ + Permission::create(Role::users()), + Permission::read(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()) + ], documentSecurity: false); + + $this->assertInstanceOf(Document::class, $collection); + + $this->assertTrue(static::getDatabase()->createAttribute( + collection: $collection->getId(), + id: 'test', + type: Database::VAR_STRING, + size: 255, + required: false + )); + + return $collection; + } + + public function testCollectionPermissionsExceptions(): void + { + $this->expectException(InvalidArgumentException::class); + static::getDatabase()->createCollection('collectionSecurity', permissions: [ + 'i dont work' + ]); + } + + /** + * @depends testCollectionPermissions + * @return array + */ + public function testCollectionPermissionsCreateWorks(Document $collection): array + { + Authorization::cleanRoles(); + Authorization::setRole(Role::users()->toString()); + + $document = static::getDatabase()->createDocument($collection->getId(), new Document([ + '$id' => ID::unique(), + '$permissions' => [ + Permission::read(Role::user('random')), + Permission::update(Role::user('random')), + Permission::delete(Role::user('random')) + ], + 'test' => 'lorem' + ])); + $this->assertInstanceOf(Document::class, $document); + + return [$collection, $document]; + } + + + /** + * @depends testCollectionPermissions + */ + public function testCollectionPermissionsCreateThrowsException(Document $collection): void + { + Authorization::cleanRoles(); + Authorization::setRole(Role::any()->toString()); + $this->expectException(AuthorizationException::class); + + static::getDatabase()->createDocument($collection->getId(), new Document([ + '$id' => ID::unique(), + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()) + ], + 'test' => 'lorem ipsum' + ])); + } + + /** + * @depends testCollectionPermissionsCreateWorks + * @param array $data + * @return array + */ + public function testCollectionPermissionsGetWorks(array $data): array + { + [$collection, $document] = $data; + + Authorization::cleanRoles(); + Authorization::setRole(Role::users()->toString()); + + $document = static::getDatabase()->getDocument( + $collection->getId(), + $document->getId() + ); + $this->assertInstanceOf(Document::class, $document); + $this->assertFalse($document->isEmpty()); + + return $data; + } + + /** + * @depends testCollectionPermissionsCreateWorks + * @param array $data + */ + public function testCollectionPermissionsGetThrowsException(array $data): void + { + [$collection, $document] = $data; + + Authorization::cleanRoles(); + Authorization::setRole(Role::any()->toString()); + + $document = static::getDatabase()->getDocument( + $collection->getId(), + $document->getId(), + ); + $this->assertInstanceOf(Document::class, $document); + $this->assertTrue($document->isEmpty()); + } + + /** + * @depends testCollectionPermissionsCreateWorks + * @param array $data + * @return array + */ + public function testCollectionPermissionsFindWorks(array $data): array + { + [$collection, $document] = $data; + + Authorization::cleanRoles(); + Authorization::setRole(Role::users()->toString()); + + $documents = static::getDatabase()->find($collection->getId()); + $this->assertNotEmpty($documents); + + Authorization::cleanRoles(); + Authorization::setRole(Role::user('random')->toString()); + + try { + static::getDatabase()->find($collection->getId()); + $this->fail('Failed to throw exception'); + } catch (AuthorizationException) { + } + + return $data; + } + + /** + * @param array $data + * @depends testCollectionPermissionsCreateWorks + */ + public function testCollectionPermissionsFindThrowsException(array $data): void + { + [$collection, $document] = $data; + + Authorization::cleanRoles(); + Authorization::setRole(Role::any()->toString()); + + $this->expectException(AuthorizationException::class); + static::getDatabase()->find($collection->getId()); + } + + /** + * @depends testCollectionPermissionsCreateWorks + * @param array $data + * @return array + */ + public function testCollectionPermissionsCountWorks(array $data): array + { + [$collection, $document] = $data; + + Authorization::cleanRoles(); + Authorization::setRole(Role::users()->toString()); + + $count = static::getDatabase()->count( + $collection->getId() + ); + + $this->assertNotEmpty($count); + + return $data; + } + + /** + * @param array $data + * @depends testCollectionPermissionsCreateWorks + */ + public function testCollectionPermissionsCountThrowsException(array $data): void + { + [$collection, $document] = $data; + + Authorization::cleanRoles(); + Authorization::setRole(Role::any()->toString()); + + $count = static::getDatabase()->count( + $collection->getId() + ); + $this->assertEmpty($count); + } + + /** + * @depends testCollectionPermissionsCreateWorks + * @param array $data + * @return array + */ + public function testCollectionPermissionsUpdateWorks(array $data): array + { + [$collection, $document] = $data; + + Authorization::cleanRoles(); + Authorization::setRole(Role::users()->toString()); + + $this->assertInstanceOf(Document::class, static::getDatabase()->updateDocument( + $collection->getId(), + $document->getId(), + $document->setAttribute('test', 'ipsum') + )); + + return $data; + } + + /** + * @param array $data + * @depends testCollectionPermissionsCreateWorks + */ + public function testCollectionPermissionsUpdateThrowsException(array $data): void + { + [$collection, $document] = $data; + Authorization::cleanRoles(); + Authorization::setRole(Role::any()->toString()); + + $this->expectException(AuthorizationException::class); + $document = static::getDatabase()->updateDocument( + $collection->getId(), + $document->getId(), + $document->setAttribute('test', 'lorem') + ); + } + + /** + * @param array $data + * @depends testCollectionPermissionsUpdateWorks + */ + public function testCollectionPermissionsDeleteThrowsException(array $data): void + { + [$collection, $document] = $data; + + Authorization::cleanRoles(); + Authorization::setRole(Role::any()->toString()); + + $this->expectException(AuthorizationException::class); + static::getDatabase()->deleteDocument( + $collection->getId(), + $document->getId() + ); + } + + /** + * @param array $data + * @depends testCollectionPermissionsUpdateWorks + */ + public function testCollectionPermissionsDeleteWorks(array $data): void + { + [$collection, $document] = $data; + + Authorization::cleanRoles(); + Authorization::setRole(Role::users()->toString()); + + $this->assertTrue(static::getDatabase()->deleteDocument( + $collection->getId(), + $document->getId() + )); + } + + /** + * @return array + */ + public function testCollectionPermissionsRelationships(): array + { + $collection = static::getDatabase()->createCollection('collectionSecurity.Parent', permissions: [ + Permission::create(Role::users()), + Permission::read(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()) + ], documentSecurity: true); + + $this->assertInstanceOf(Document::class, $collection); + + $this->assertTrue(static::getDatabase()->createAttribute( + collection: $collection->getId(), + id: 'test', + type: Database::VAR_STRING, + size: 255, + required: false + )); + + $collectionOneToOne = static::getDatabase()->createCollection('collectionSecurity.OneToOne', permissions: [ + Permission::create(Role::users()), + Permission::read(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()) + ], documentSecurity: true); + + $this->assertInstanceOf(Document::class, $collectionOneToOne); + + $this->assertTrue(static::getDatabase()->createAttribute( + collection: $collectionOneToOne->getId(), + id: 'test', + type: Database::VAR_STRING, + size: 255, + required: false + )); + + $this->assertTrue(static::getDatabase()->createRelationship( + collection: $collection->getId(), + relatedCollection: $collectionOneToOne->getId(), + type: Database::RELATION_ONE_TO_ONE, + id: Database::RELATION_ONE_TO_ONE, + onDelete: Database::RELATION_MUTATE_CASCADE + )); + + $collectionOneToMany = static::getDatabase()->createCollection('collectionSecurity.OneToMany', permissions: [ + Permission::create(Role::users()), + Permission::read(Role::users()), + Permission::update(Role::users()), + Permission::delete(Role::users()) + ], documentSecurity: true); + + $this->assertInstanceOf(Document::class, $collectionOneToMany); + + $this->assertTrue(static::getDatabase()->createAttribute( + collection: $collectionOneToMany->getId(), + id: 'test', + type: Database::VAR_STRING, + size: 255, + required: false + )); + + $this->assertTrue(static::getDatabase()->createRelationship( + collection: $collection->getId(), + relatedCollection: $collectionOneToMany->getId(), + type: Database::RELATION_ONE_TO_MANY, + id: Database::RELATION_ONE_TO_MANY, + onDelete: Database::RELATION_MUTATE_CASCADE + )); + + return [$collection, $collectionOneToOne, $collectionOneToMany]; + } + + /** + * @depends testCollectionPermissionsRelationships + * @param array $data + * @return array + */ + public function testCollectionPermissionsRelationshipsCreateWorks(array $data): array + { + [$collection, $collectionOneToOne, $collectionOneToMany] = $data; + Authorization::cleanRoles(); + Authorization::setRole(Role::users()->toString()); + + $document = static::getDatabase()->createDocument($collection->getId(), new Document([ + '$id' => ID::unique(), + '$permissions' => [ + Permission::read(Role::user('random')), + Permission::update(Role::user('random')), + Permission::delete(Role::user('random')) + ], + 'test' => 'lorem', + Database::RELATION_ONE_TO_ONE => [ + '$id' => ID::unique(), + '$permissions' => [ + Permission::read(Role::user('random')), + Permission::update(Role::user('random')), + Permission::delete(Role::user('random')) + ], + 'test' => 'lorem ipsum' + ], + Database::RELATION_ONE_TO_MANY => [ + [ + '$id' => ID::unique(), + '$permissions' => [ + Permission::read(Role::user('random')), + Permission::update(Role::user('random')), + Permission::delete(Role::user('random')) + ], + 'test' => 'lorem ipsum' + ], [ + '$id' => ID::unique(), + '$permissions' => [ + Permission::read(Role::user('torsten')), + Permission::update(Role::user('random')), + Permission::delete(Role::user('random')) + ], + 'test' => 'dolor' + ] + ], + ])); + $this->assertInstanceOf(Document::class, $document); + + return [...$data, $document]; + } + + /** + * @depends testCollectionPermissionsRelationships + * @param array $data + */ + public function testCollectionPermissionsRelationshipsCreateThrowsException(array $data): void + { + [$collection, $collectionOneToOne, $collectionOneToMany] = $data; + + Authorization::cleanRoles(); + Authorization::setRole(Role::any()->toString()); + $this->expectException(AuthorizationException::class); + + static::getDatabase()->createDocument($collection->getId(), new Document([ + '$id' => ID::unique(), + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()) + ], + 'test' => 'lorem ipsum' + ])); + } + + /** + * @depends testCollectionPermissionsRelationshipsCreateWorks + * @param array $data + * @return array + */ + public function testCollectionPermissionsRelationshipsGetWorks(array $data): array + { + [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; + + Authorization::cleanRoles(); + Authorization::setRole(Role::users()->toString()); + + $document = static::getDatabase()->getDocument( + $collection->getId(), + $document->getId() + ); + + $this->assertInstanceOf(Document::class, $document); + $this->assertInstanceOf(Document::class, $document->getAttribute(Database::RELATION_ONE_TO_ONE)); + $this->assertIsArray($document->getAttribute(Database::RELATION_ONE_TO_MANY)); + $this->assertCount(2, $document->getAttribute(Database::RELATION_ONE_TO_MANY)); + $this->assertFalse($document->isEmpty()); + + Authorization::cleanRoles(); + Authorization::setRole(Role::user('random')->toString()); + + $document = static::getDatabase()->getDocument( + $collection->getId(), + $document->getId() + ); + + $this->assertInstanceOf(Document::class, $document); + $this->assertInstanceOf(Document::class, $document->getAttribute(Database::RELATION_ONE_TO_ONE)); + $this->assertIsArray($document->getAttribute(Database::RELATION_ONE_TO_MANY)); + $this->assertCount(1, $document->getAttribute(Database::RELATION_ONE_TO_MANY)); + $this->assertFalse($document->isEmpty()); + + return $data; + } + + /** + * @param array $data + * @depends testCollectionPermissionsRelationshipsCreateWorks + */ + public function testCollectionPermissionsRelationshipsGetThrowsException(array $data): void + { + [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; + + Authorization::cleanRoles(); + Authorization::setRole(Role::any()->toString()); + + $document = static::getDatabase()->getDocument( + $collection->getId(), + $document->getId(), + ); + $this->assertInstanceOf(Document::class, $document); + $this->assertTrue($document->isEmpty()); + } + + /** + * @depends testCollectionPermissionsRelationshipsCreateWorks + * @param array $data + */ + public function testCollectionPermissionsRelationshipsFindWorks(array $data): void + { + [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; + + Authorization::cleanRoles(); + Authorization::setRole(Role::users()->toString()); + + $documents = static::getDatabase()->find( + $collection->getId() + ); + + $this->assertIsArray($documents); + $this->assertCount(1, $documents); + $document = $documents[0]; + $this->assertInstanceOf(Document::class, $document); + $this->assertInstanceOf(Document::class, $document->getAttribute(Database::RELATION_ONE_TO_ONE)); + $this->assertIsArray($document->getAttribute(Database::RELATION_ONE_TO_MANY)); + $this->assertCount(2, $document->getAttribute(Database::RELATION_ONE_TO_MANY)); + $this->assertFalse($document->isEmpty()); + + Authorization::cleanRoles(); + Authorization::setRole(Role::user('random')->toString()); + + $documents = static::getDatabase()->find( + $collection->getId() + ); + + $this->assertIsArray($documents); + $this->assertCount(1, $documents); + $document = $documents[0]; + $this->assertInstanceOf(Document::class, $document); + $this->assertInstanceOf(Document::class, $document->getAttribute(Database::RELATION_ONE_TO_ONE)); + $this->assertIsArray($document->getAttribute(Database::RELATION_ONE_TO_MANY)); + $this->assertCount(1, $document->getAttribute(Database::RELATION_ONE_TO_MANY)); + $this->assertFalse($document->isEmpty()); + + Authorization::cleanRoles(); + Authorization::setRole(Role::user('unknown')->toString()); + + $documents = static::getDatabase()->find( + $collection->getId() + ); + + $this->assertIsArray($documents); + $this->assertCount(0, $documents); + } + + /** + * @depends testCollectionPermissionsRelationshipsCreateWorks + * @param array $data + */ + public function testCollectionPermissionsRelationshipsCountWorks(array $data): void + { + [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; + + Authorization::cleanRoles(); + Authorization::setRole(Role::users()->toString()); + + $documents = static::getDatabase()->count( + $collection->getId() + ); + + $this->assertEquals(1, $documents); + + Authorization::cleanRoles(); + Authorization::setRole(Role::user('random')->toString()); + + $documents = static::getDatabase()->count( + $collection->getId() + ); + + $this->assertEquals(1, $documents); + + Authorization::cleanRoles(); + Authorization::setRole(Role::user('unknown')->toString()); + + $documents = static::getDatabase()->count( + $collection->getId() + ); + + $this->assertEquals(0, $documents); + } + + /** + * @depends testCollectionPermissionsRelationshipsCreateWorks + * @param array $data + * @return array + */ + public function testCollectionPermissionsRelationshipsUpdateWorks(array $data): array + { + [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; + + Authorization::cleanRoles(); + Authorization::setRole(Role::users()->toString()); + static::getDatabase()->updateDocument( + $collection->getId(), + $document->getId(), + $document + ); + + $this->assertTrue(true); + + Authorization::cleanRoles(); + Authorization::setRole(Role::user('random')->toString()); + + static::getDatabase()->updateDocument( + $collection->getId(), + $document->getId(), + $document->setAttribute('test', 'ipsum') + ); + + $this->assertTrue(true); + + return $data; + } + + /** + * @param array $data + * @depends testCollectionPermissionsRelationshipsCreateWorks + */ + public function testCollectionPermissionsRelationshipsUpdateThrowsException(array $data): void + { + [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; + + Authorization::cleanRoles(); + Authorization::setRole(Role::any()->toString()); + + $this->expectException(AuthorizationException::class); + $document = static::getDatabase()->updateDocument( + $collection->getId(), + $document->getId(), + $document->setAttribute('test', $document->getAttribute('test').'new_value') + ); + } + + /** + * @param array $data + * @depends testCollectionPermissionsRelationshipsUpdateWorks + */ + public function testCollectionPermissionsRelationshipsDeleteThrowsException(array $data): void + { + [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; + + Authorization::cleanRoles(); + Authorization::setRole(Role::any()->toString()); + + $this->expectException(AuthorizationException::class); + $document = static::getDatabase()->deleteDocument( + $collection->getId(), + $document->getId() + ); + } + + /** + * @param array $data + * @depends testCollectionPermissionsRelationshipsUpdateWorks + */ + public function testCollectionPermissionsRelationshipsDeleteWorks(array $data): void + { + [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; + + Authorization::cleanRoles(); + Authorization::setRole(Role::users()->toString()); + + $this->assertTrue(static::getDatabase()->deleteDocument( + $collection->getId(), + $document->getId() + )); + } + + public function testCreateRelationDocumentWithoutUpdatePermission(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + Authorization::cleanRoles(); + Authorization::setRole(Role::user('a')->toString()); + + static::getDatabase()->createCollection('parentRelationTest', [], [], [ + Permission::read(Role::user('a')), + Permission::create(Role::user('a')), + Permission::update(Role::user('a')), + Permission::delete(Role::user('a')) + ]); + static::getDatabase()->createCollection('childRelationTest', [], [], [ + Permission::create(Role::user('a')), + Permission::read(Role::user('a')), + ]); + static::getDatabase()->createAttribute('parentRelationTest', 'name', Database::VAR_STRING, 255, false); + static::getDatabase()->createAttribute('childRelationTest', 'name', Database::VAR_STRING, 255, false); + + static::getDatabase()->createRelationship( + collection: 'parentRelationTest', + relatedCollection: 'childRelationTest', + type: Database::RELATION_ONE_TO_MANY, + id: 'children' + ); + + // Create document with relationship with nested data + $parent = static::getDatabase()->createDocument('parentRelationTest', new Document([ + '$id' => 'parent1', + 'name' => 'Parent 1', + 'children' => [ + [ + '$id' => 'child1', + 'name' => 'Child 1', + ], + ], + ])); + $this->assertEquals('child1', $parent->getAttribute('children')[0]->getId()); + $parent->setAttribute('children', [ + [ + '$id' => 'child2', + ], + ]); + $updatedParent = static::getDatabase()->updateDocument('parentRelationTest', 'parent1', $parent); + + $this->assertEquals('child2', $updatedParent->getAttribute('children')[0]->getId()); + + static::getDatabase()->deleteCollection('parentRelationTest'); + static::getDatabase()->deleteCollection('childRelationTest'); + } + + public function testUpdateDocumentWithRelationships(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + static::getDatabase()->createCollection('userProfiles', [ + new Document([ + '$id' => ID::custom('username'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 700, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ]), + ], [], [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()) + ]); + static::getDatabase()->createCollection('links', [ + new Document([ + '$id' => ID::custom('title'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 700, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ]), + ], [], [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()) + ]); + static::getDatabase()->createCollection('videos', [ + new Document([ + '$id' => ID::custom('title'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 700, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ]), + ], [], [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()) + ]); + static::getDatabase()->createCollection('products', [ + new Document([ + '$id' => ID::custom('title'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 700, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ]), + ], [], [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()) + ]); + static::getDatabase()->createCollection('settings', [ + new Document([ + '$id' => ID::custom('metaTitle'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 700, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ]), + ], [], [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()) + ]); + static::getDatabase()->createCollection('appearance', [ + new Document([ + '$id' => ID::custom('metaTitle'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 700, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ]), + ], [], [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()) + ]); + static::getDatabase()->createCollection('group', [ + new Document([ + '$id' => ID::custom('name'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 700, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ]), + ], [], [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()) + ]); + static::getDatabase()->createCollection('community', [ + new Document([ + '$id' => ID::custom('name'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 700, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ]), + ], [], [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()) + ]); + + static::getDatabase()->createRelationship( + collection: 'userProfiles', + relatedCollection: 'links', + type: Database::RELATION_ONE_TO_MANY, + id: 'links' + ); + + static::getDatabase()->createRelationship( + collection: 'userProfiles', + relatedCollection: 'videos', + type: Database::RELATION_ONE_TO_MANY, + id: 'videos' + ); + + static::getDatabase()->createRelationship( + collection: 'userProfiles', + relatedCollection: 'products', + type: Database::RELATION_ONE_TO_MANY, + twoWay: true, + id: 'products', + twoWayKey: 'userProfile', + ); + + static::getDatabase()->createRelationship( + collection: 'userProfiles', + relatedCollection: 'settings', + type: Database::RELATION_ONE_TO_ONE, + id: 'settings' + ); + + static::getDatabase()->createRelationship( + collection: 'userProfiles', + relatedCollection: 'appearance', + type: Database::RELATION_ONE_TO_ONE, + id: 'appearance' + ); + + static::getDatabase()->createRelationship( + collection: 'userProfiles', + relatedCollection: 'group', + type: Database::RELATION_MANY_TO_ONE, + id: 'group' + ); + + static::getDatabase()->createRelationship( + collection: 'userProfiles', + relatedCollection: 'community', + type: Database::RELATION_MANY_TO_ONE, + id: 'community' + ); + + $profile = static::getDatabase()->createDocument('userProfiles', new Document([ + '$id' => '1', + 'username' => 'user1', + 'links' => [ + [ + '$id' => 'link1', + 'title' => 'Link 1', + ], + ], + 'videos' => [ + [ + '$id' => 'video1', + 'title' => 'Video 1', + ], + ], + 'products' => [ + [ + '$id' => 'product1', + 'title' => 'Product 1', + ], + ], + 'settings' => [ + '$id' => 'settings1', + 'metaTitle' => 'Meta Title', + ], + 'appearance' => [ + '$id' => 'appearance1', + 'metaTitle' => 'Meta Title', + ], + 'group' => [ + '$id' => 'group1', + 'name' => 'Group 1', + ], + 'community' => [ + '$id' => 'community1', + 'name' => 'Community 1', + ], + ])); + $this->assertEquals('link1', $profile->getAttribute('links')[0]->getId()); + $this->assertEquals('settings1', $profile->getAttribute('settings')->getId()); + $this->assertEquals('group1', $profile->getAttribute('group')->getId()); + $this->assertEquals('community1', $profile->getAttribute('community')->getId()); + $this->assertEquals('video1', $profile->getAttribute('videos')[0]->getId()); + $this->assertEquals('product1', $profile->getAttribute('products')[0]->getId()); + $this->assertEquals('appearance1', $profile->getAttribute('appearance')->getId()); + + $profile->setAttribute('links', [ + [ + '$id' => 'link1', + 'title' => 'New Link Value', + ], + ]); + + $profile->setAttribute('settings', [ + '$id' => 'settings1', + 'metaTitle' => 'New Meta Title', + ]); + + $profile->setAttribute('group', [ + '$id' => 'group1', + 'name' => 'New Group Name', + ]); + + $updatedProfile = static::getDatabase()->updateDocument('userProfiles', '1', $profile); + + $this->assertEquals('New Link Value', $updatedProfile->getAttribute('links')[0]->getAttribute('title')); + $this->assertEquals('New Meta Title', $updatedProfile->getAttribute('settings')->getAttribute('metaTitle')); + $this->assertEquals('New Group Name', $updatedProfile->getAttribute('group')->getAttribute('name')); + + // This is the point of test, related documents should be present if they are not updated + $this->assertEquals('Video 1', $updatedProfile->getAttribute('videos')[0]->getAttribute('title')); + $this->assertEquals('Product 1', $updatedProfile->getAttribute('products')[0]->getAttribute('title')); + $this->assertEquals('Meta Title', $updatedProfile->getAttribute('appearance')->getAttribute('metaTitle')); + $this->assertEquals('Community 1', $updatedProfile->getAttribute('community')->getAttribute('name')); + + // updating document using two way key in one to many relationship + $product = static::getDatabase()->getDocument('products', 'product1'); + $product->setAttribute('userProfile', [ + '$id' => '1', + 'username' => 'updated user value', + ]); + $updatedProduct = static::getDatabase()->updateDocument('products', 'product1', $product); + $this->assertEquals('updated user value', $updatedProduct->getAttribute('userProfile')->getAttribute('username')); + $this->assertEquals('Product 1', $updatedProduct->getAttribute('title')); + $this->assertEquals('product1', $updatedProduct->getId()); + $this->assertEquals('1', $updatedProduct->getAttribute('userProfile')->getId()); + + static::getDatabase()->deleteCollection('userProfiles'); + static::getDatabase()->deleteCollection('links'); + static::getDatabase()->deleteCollection('settings'); + static::getDatabase()->deleteCollection('group'); + static::getDatabase()->deleteCollection('community'); + static::getDatabase()->deleteCollection('videos'); + static::getDatabase()->deleteCollection('products'); + static::getDatabase()->deleteCollection('appearance'); + } + + public function testLabels(): void + { + $this->assertInstanceOf('Utopia\Database\Document', static::getDatabase()->createCollection( + 'labels_test', + )); + static::getDatabase()->createAttribute('labels_test', 'attr1', Database::VAR_STRING, 10, false); + + static::getDatabase()->createDocument('labels_test', new Document([ + '$id' => 'doc1', + 'attr1' => 'value1', + '$permissions' => [ + Permission::read(Role::label('reader')), + ], + ])); + + $documents = static::getDatabase()->find('labels_test'); + + $this->assertEmpty($documents); + + Authorization::setRole(Role::label('reader')->toString()); + + $documents = static::getDatabase()->find('labels_test'); + + $this->assertCount(1, $documents); + } + + public function testEmptyOperatorValues(): void + { + try { + static::getDatabase()->findOne('documents', [ + Query::equal('string', []), + ]); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertInstanceOf(Exception::class, $e); + $this->assertEquals('Invalid query: Equal queries require at least one value.', $e->getMessage()); + } + + try { + static::getDatabase()->findOne('documents', [ + Query::search('string', null), + ]); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertInstanceOf(Exception::class, $e); + $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); + } + + try { + static::getDatabase()->findOne('documents', [ + Query::notEqual('string', []), + ]); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertInstanceOf(Exception::class, $e); + $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); + } + + try { + static::getDatabase()->findOne('documents', [ + Query::lessThan('string', []), + ]); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertInstanceOf(Exception::class, $e); + $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); + } + + try { + static::getDatabase()->findOne('documents', [ + Query::lessThanEqual('string', []), + ]); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertInstanceOf(Exception::class, $e); + $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); + } + + try { + static::getDatabase()->findOne('documents', [ + Query::greaterThan('string', []), + ]); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertInstanceOf(Exception::class, $e); + $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); + } + + try { + static::getDatabase()->findOne('documents', [ + Query::greaterThanEqual('string', []), + ]); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertInstanceOf(Exception::class, $e); + $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); + } + + try { + static::getDatabase()->findOne('documents', [ + Query::contains('string', []), + ]); + $this->fail('Failed to throw exception'); + } catch (Exception $e) { + $this->assertInstanceOf(Exception::class, $e); + $this->assertEquals('Invalid query: Contains queries require at least one value.', $e->getMessage()); + } + } + + public function testEvents(): void + { + Authorization::skip(function () { + $database = static::getDatabase(); + + $events = [ + Database::EVENT_DATABASE_CREATE, + Database::EVENT_DATABASE_LIST, + Database::EVENT_COLLECTION_CREATE, + Database::EVENT_COLLECTION_LIST, + Database::EVENT_COLLECTION_READ, + Database::EVENT_ATTRIBUTE_CREATE, + Database::EVENT_ATTRIBUTE_UPDATE, + Database::EVENT_INDEX_CREATE, + Database::EVENT_DOCUMENT_CREATE, + Database::EVENT_DOCUMENT_UPDATE, + Database::EVENT_DOCUMENT_READ, + Database::EVENT_DOCUMENT_FIND, + Database::EVENT_DOCUMENT_FIND, + Database::EVENT_DOCUMENT_COUNT, + Database::EVENT_DOCUMENT_SUM, + Database::EVENT_DOCUMENT_INCREASE, + Database::EVENT_DOCUMENT_DECREASE, + Database::EVENT_INDEX_DELETE, + Database::EVENT_DOCUMENT_DELETE, + Database::EVENT_ATTRIBUTE_DELETE, + Database::EVENT_COLLECTION_DELETE, + Database::EVENT_DATABASE_DELETE, + ]; + + $database->on(Database::EVENT_ALL, 'test', function ($event, $data) use (&$events) { + $shifted = array_shift($events); + + $this->assertEquals($shifted, $event); + }); + + if ($this->getDatabase()->getAdapter()->getSupportForSchemas()) { + $database->setDefaultDatabase('hellodb'); + $database->create(); + } else { + array_shift($events); + } + + $database->list(); + + $database->setDefaultDatabase($this->testDatabase); + + $collectionId = ID::unique(); + $database->createCollection($collectionId); + $database->listCollections(); + $database->getCollection($collectionId); + $database->createAttribute($collectionId, 'attr1', Database::VAR_INTEGER, 2, false); + $database->updateAttributeRequired($collectionId, 'attr1', true); + $indexId1 = 'index2_' . uniqid(); + $database->createIndex($collectionId, $indexId1, Database::INDEX_KEY, ['attr1']); + + $document = $database->createDocument($collectionId, new Document([ + '$id' => 'doc1', + 'attr1' => 10, + '$permissions' => [ + Permission::delete(Role::any()), + Permission::update(Role::any()), + Permission::read(Role::any()), + ], + ])); + + $executed = false; + $database->on(Database::EVENT_ALL, 'should-not-execute', function ($event, $data) use (&$executed) { + $executed = true; + }); + + $database->silent(function () use ($database, $collectionId, $document) { + $database->updateDocument($collectionId, 'doc1', $document->setAttribute('attr1', 15)); + $database->getDocument($collectionId, 'doc1'); + $database->find($collectionId); + $database->findOne($collectionId); + $database->count($collectionId); + $database->sum($collectionId, 'attr1'); + $database->increaseDocumentAttribute($collectionId, $document->getId(), 'attr1'); + $database->decreaseDocumentAttribute($collectionId, $document->getId(), 'attr1'); + }, ['should-not-execute']); + + $this->assertFalse($executed); + + $database->deleteIndex($collectionId, $indexId1); + $database->deleteDocument($collectionId, 'doc1'); + $database->deleteAttribute($collectionId, 'attr1'); + $database->deleteCollection($collectionId); + $database->delete('hellodb'); + }); } -// -// /** -// * @depends testFind -// */ -// public function testFindOne(): void -// { -// $document = static::getDatabase()->findOne('movies', [ -// Query::offset(2), -// Query::orderAsc('name') -// ]); -// -// $this->assertTrue($document instanceof Document); -// $this->assertEquals('Frozen', $document->getAttribute('name')); -// -// $document = static::getDatabase()->findOne('movies', [ -// Query::offset(10) -// ]); -// $this->assertEquals(false, $document); -// } -// -// public function testFindNull(): void -// { -// $documents = static::getDatabase()->find('movies', [ -// Query::isNull('nullable'), -// ]); -// -// $this->assertEquals(5, count($documents)); -// } -// -// public function testFindNotNull(): void -// { -// $documents = static::getDatabase()->find('movies', [ -// Query::isNotNull('nullable'), -// ]); -// -// $this->assertEquals(1, count($documents)); -// } -// -// public function testFindStartsWith(): void -// { -// $documents = static::getDatabase()->find('movies', [ -// Query::startsWith('name', 'Work'), -// ]); -// -// $this->assertEquals(2, count($documents)); -// -// if ($this->getDatabase()->getAdapter() instanceof SQL) { -// $documents = static::getDatabase()->find('movies', [ -// Query::startsWith('name', '%ork'), -// ]); -// } else { -// $documents = static::getDatabase()->find('movies', [ -// Query::startsWith('name', '.*ork'), -// ]); -// } -// -// $this->assertEquals(0, count($documents)); -// } -// -// public function testFindStartsWithWords(): void -// { -// $documents = static::getDatabase()->find('movies', [ -// Query::startsWith('name', 'Work in Progress'), -// ]); -// -// $this->assertEquals(2, count($documents)); -// } -// -// public function testFindEndsWith(): void -// { -// $documents = static::getDatabase()->find('movies', [ -// Query::endsWith('name', 'Marvel'), -// ]); -// -// $this->assertEquals(1, count($documents)); -// } -// -// public function testFindSelect(): void -// { -// $documents = static::getDatabase()->find('movies', [ -// Query::select(['name', 'year']) -// ]); -// -// foreach ($documents as $document) { -// $this->assertArrayHasKey('name', $document); -// $this->assertArrayHasKey('year', $document); -// $this->assertArrayNotHasKey('director', $document); -// $this->assertArrayNotHasKey('price', $document); -// $this->assertArrayNotHasKey('active', $document); -// $this->assertArrayNotHasKey('$id', $document); -// $this->assertArrayNotHasKey('$internalId', $document); -// $this->assertArrayNotHasKey('$collection', $document); -// $this->assertArrayNotHasKey('$createdAt', $document); -// $this->assertArrayNotHasKey('$updatedAt', $document); -// $this->assertArrayNotHasKey('$permissions', $document); -// } -// -// $documents = static::getDatabase()->find('movies', [ -// Query::select(['name', 'year', '$id']) -// ]); -// -// foreach ($documents as $document) { -// $this->assertArrayHasKey('name', $document); -// $this->assertArrayHasKey('year', $document); -// $this->assertArrayNotHasKey('director', $document); -// $this->assertArrayNotHasKey('price', $document); -// $this->assertArrayNotHasKey('active', $document); -// $this->assertArrayHasKey('$id', $document); -// $this->assertArrayNotHasKey('$internalId', $document); -// $this->assertArrayNotHasKey('$collection', $document); -// $this->assertArrayNotHasKey('$createdAt', $document); -// $this->assertArrayNotHasKey('$updatedAt', $document); -// $this->assertArrayNotHasKey('$permissions', $document); -// } -// -// $documents = static::getDatabase()->find('movies', [ -// Query::select(['name', 'year', '$internalId']) -// ]); -// -// foreach ($documents as $document) { -// $this->assertArrayHasKey('name', $document); -// $this->assertArrayHasKey('year', $document); -// $this->assertArrayNotHasKey('director', $document); -// $this->assertArrayNotHasKey('price', $document); -// $this->assertArrayNotHasKey('active', $document); -// $this->assertArrayNotHasKey('$id', $document); -// $this->assertArrayHasKey('$internalId', $document); -// $this->assertArrayNotHasKey('$collection', $document); -// $this->assertArrayNotHasKey('$createdAt', $document); -// $this->assertArrayNotHasKey('$updatedAt', $document); -// $this->assertArrayNotHasKey('$permissions', $document); -// } -// -// $documents = static::getDatabase()->find('movies', [ -// Query::select(['name', 'year', '$collection']) -// ]); -// -// foreach ($documents as $document) { -// $this->assertArrayHasKey('name', $document); -// $this->assertArrayHasKey('year', $document); -// $this->assertArrayNotHasKey('director', $document); -// $this->assertArrayNotHasKey('price', $document); -// $this->assertArrayNotHasKey('active', $document); -// $this->assertArrayNotHasKey('$id', $document); -// $this->assertArrayNotHasKey('$internalId', $document); -// $this->assertArrayHasKey('$collection', $document); -// $this->assertArrayNotHasKey('$createdAt', $document); -// $this->assertArrayNotHasKey('$updatedAt', $document); -// $this->assertArrayNotHasKey('$permissions', $document); -// } -// -// $documents = static::getDatabase()->find('movies', [ -// Query::select(['name', 'year', '$createdAt']) -// ]); -// -// foreach ($documents as $document) { -// $this->assertArrayHasKey('name', $document); -// $this->assertArrayHasKey('year', $document); -// $this->assertArrayNotHasKey('director', $document); -// $this->assertArrayNotHasKey('price', $document); -// $this->assertArrayNotHasKey('active', $document); -// $this->assertArrayNotHasKey('$id', $document); -// $this->assertArrayNotHasKey('$internalId', $document); -// $this->assertArrayNotHasKey('$collection', $document); -// $this->assertArrayHasKey('$createdAt', $document); -// $this->assertArrayNotHasKey('$updatedAt', $document); -// $this->assertArrayNotHasKey('$permissions', $document); -// } -// -// $documents = static::getDatabase()->find('movies', [ -// Query::select(['name', 'year', '$updatedAt']) -// ]); -// -// foreach ($documents as $document) { -// $this->assertArrayHasKey('name', $document); -// $this->assertArrayHasKey('year', $document); -// $this->assertArrayNotHasKey('director', $document); -// $this->assertArrayNotHasKey('price', $document); -// $this->assertArrayNotHasKey('active', $document); -// $this->assertArrayNotHasKey('$id', $document); -// $this->assertArrayNotHasKey('$internalId', $document); -// $this->assertArrayNotHasKey('$collection', $document); -// $this->assertArrayNotHasKey('$createdAt', $document); -// $this->assertArrayHasKey('$updatedAt', $document); -// $this->assertArrayNotHasKey('$permissions', $document); -// } -// -// $documents = static::getDatabase()->find('movies', [ -// Query::select(['name', 'year', '$permissions']) -// ]); -// -// foreach ($documents as $document) { -// $this->assertArrayHasKey('name', $document); -// $this->assertArrayHasKey('year', $document); -// $this->assertArrayNotHasKey('director', $document); -// $this->assertArrayNotHasKey('price', $document); -// $this->assertArrayNotHasKey('active', $document); -// $this->assertArrayNotHasKey('$id', $document); -// $this->assertArrayNotHasKey('$internalId', $document); -// $this->assertArrayNotHasKey('$collection', $document); -// $this->assertArrayNotHasKey('$createdAt', $document); -// $this->assertArrayNotHasKey('$updatedAt', $document); -// $this->assertArrayHasKey('$permissions', $document); -// } -// } -// -// /** -// * @depends testFind -// */ -// public function testCount(): void -// { -// $count = static::getDatabase()->count('movies'); -// $this->assertEquals(6, $count); -// $count = static::getDatabase()->count('movies', [Query::equal('year', [2019])]); -// $this->assertEquals(2, $count); -// $count = static::getDatabase()->count('movies', [Query::equal('with-dash', ['Works'])]); -// $this->assertEquals(2, $count); -// $count = static::getDatabase()->count('movies', [Query::equal('with-dash', ['Works2', 'Works3'])]); -// $this->assertEquals(4, $count); -// -// Authorization::unsetRole('user:x'); -// $count = static::getDatabase()->count('movies'); -// $this->assertEquals(5, $count); -// -// Authorization::disable(); -// $count = static::getDatabase()->count('movies'); -// $this->assertEquals(6, $count); -// Authorization::reset(); -// -// Authorization::disable(); -// $count = static::getDatabase()->count('movies', [], 3); -// $this->assertEquals(3, $count); -// Authorization::reset(); -// -// /** -// * Test that OR queries are handled correctly -// */ -// Authorization::disable(); -// $count = static::getDatabase()->count('movies', [ -// Query::equal('director', ['TBD', 'Joe Johnston']), -// Query::equal('year', [2025]), -// ]); -// $this->assertEquals(1, $count); -// Authorization::reset(); -// } -// -// /** -// * @depends testFind -// */ -// public function testSum(): void -// { -// Authorization::setRole('user:x'); -// $sum = static::getDatabase()->sum('movies', 'year', [Query::equal('year', [2019]),]); -// $this->assertEquals(2019 + 2019, $sum); -// $sum = static::getDatabase()->sum('movies', 'year'); -// $this->assertEquals(2013 + 2019 + 2011 + 2019 + 2025 + 2026, $sum); -// $sum = static::getDatabase()->sum('movies', 'price', [Query::equal('year', [2019]),]); -// $this->assertEquals(round(39.50 + 25.99, 2), round($sum, 2)); -// $sum = static::getDatabase()->sum('movies', 'price', [Query::equal('year', [2019]),]); -// $this->assertEquals(round(39.50 + 25.99, 2), round($sum, 2)); -// -// $sum = static::getDatabase()->sum('movies', 'year', [Query::equal('year', [2019])], 1); -// $this->assertEquals(2019, $sum); -// -// Authorization::unsetRole('user:x'); -// Authorization::unsetRole('userx'); -// $sum = static::getDatabase()->sum('movies', 'year', [Query::equal('year', [2019]),]); -// $this->assertEquals(2019 + 2019, $sum); -// $sum = static::getDatabase()->sum('movies', 'year'); -// $this->assertEquals(2013 + 2019 + 2011 + 2019 + 2025, $sum); -// $sum = static::getDatabase()->sum('movies', 'price', [Query::equal('year', [2019]),]); -// $this->assertEquals(round(39.50 + 25.99, 2), round($sum, 2)); -// $sum = static::getDatabase()->sum('movies', 'price', [Query::equal('year', [2019]),]); -// $this->assertEquals(round(39.50 + 25.99, 2), round($sum, 2)); -// } -// -// public function testEncodeDecode(): void -// { -// $collection = new Document([ -// '$collection' => ID::custom(Database::METADATA), -// '$id' => ID::custom('users'), -// 'name' => 'Users', -// 'attributes' => [ -// [ -// '$id' => ID::custom('name'), -// 'type' => Database::VAR_STRING, -// 'format' => '', -// 'size' => 256, -// 'signed' => true, -// 'required' => false, -// 'array' => false, -// 'filters' => [], -// ], -// [ -// '$id' => ID::custom('email'), -// 'type' => Database::VAR_STRING, -// 'format' => '', -// 'size' => 1024, -// 'signed' => true, -// 'required' => false, -// 'array' => false, -// 'filters' => [], -// ], -// [ -// '$id' => ID::custom('status'), -// 'type' => Database::VAR_INTEGER, -// 'format' => '', -// 'size' => 0, -// 'signed' => true, -// 'required' => false, -// 'array' => false, -// 'filters' => [], -// ], -// [ -// '$id' => ID::custom('password'), -// 'type' => Database::VAR_STRING, -// 'format' => '', -// 'size' => 16384, -// 'signed' => true, -// 'required' => false, -// 'array' => false, -// 'filters' => [], -// ], -// [ -// '$id' => ID::custom('passwordUpdate'), -// 'type' => Database::VAR_DATETIME, -// 'format' => '', -// 'size' => 0, -// 'signed' => true, -// 'required' => false, -// 'array' => false, -// 'filters' => ['datetime'], -// ], -// [ -// '$id' => ID::custom('registration'), -// 'type' => Database::VAR_DATETIME, -// 'format' => '', -// 'size' => 0, -// 'signed' => true, -// 'required' => false, -// 'array' => false, -// 'filters' => ['datetime'], -// ], -// [ -// '$id' => ID::custom('emailVerification'), -// 'type' => Database::VAR_BOOLEAN, -// 'format' => '', -// 'size' => 0, -// 'signed' => true, -// 'required' => false, -// 'array' => false, -// 'filters' => [], -// ], -// [ -// '$id' => ID::custom('reset'), -// 'type' => Database::VAR_BOOLEAN, -// 'format' => '', -// 'size' => 0, -// 'signed' => true, -// 'required' => false, -// 'array' => false, -// 'filters' => [], -// ], -// [ -// '$id' => ID::custom('prefs'), -// 'type' => Database::VAR_STRING, -// 'format' => '', -// 'size' => 16384, -// 'signed' => true, -// 'required' => false, -// 'array' => false, -// 'filters' => ['json'] -// ], -// [ -// '$id' => ID::custom('sessions'), -// 'type' => Database::VAR_STRING, -// 'format' => '', -// 'size' => 16384, -// 'signed' => true, -// 'required' => false, -// 'array' => false, -// 'filters' => ['json'], -// ], -// [ -// '$id' => ID::custom('tokens'), -// 'type' => Database::VAR_STRING, -// 'format' => '', -// 'size' => 16384, -// 'signed' => true, -// 'required' => false, -// 'array' => false, -// 'filters' => ['json'], -// ], -// [ -// '$id' => ID::custom('memberships'), -// 'type' => Database::VAR_STRING, -// 'format' => '', -// 'size' => 16384, -// 'signed' => true, -// 'required' => false, -// 'array' => false, -// 'filters' => ['json'], -// ], -// [ -// '$id' => ID::custom('roles'), -// 'type' => Database::VAR_STRING, -// 'format' => '', -// 'size' => 128, -// 'signed' => true, -// 'required' => false, -// 'array' => true, -// 'filters' => [], -// ], -// [ -// '$id' => ID::custom('tags'), -// 'type' => Database::VAR_STRING, -// 'format' => '', -// 'size' => 128, -// 'signed' => true, -// 'required' => false, -// 'array' => true, -// 'filters' => ['json'], -// ], -// ], -// 'indexes' => [ -// [ -// '$id' => ID::custom('_key_email'), -// 'type' => Database::INDEX_UNIQUE, -// 'attributes' => ['email'], -// 'lengths' => [1024], -// 'orders' => [Database::ORDER_ASC], -// ] -// ], -// ]); -// -// $document = new Document([ -// '$id' => ID::custom('608fdbe51361a'), -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::create(Role::user('608fdbe51361a')), -// Permission::update(Role::user('608fdbe51361a')), -// Permission::delete(Role::user('608fdbe51361a')), -// ], -// 'email' => 'test@example.com', -// 'emailVerification' => false, -// 'status' => 1, -// 'password' => 'randomhash', -// 'passwordUpdate' => '2000-06-12 14:12:55', -// 'registration' => '1975-06-12 14:12:55+01:00', -// 'reset' => false, -// 'name' => 'My Name', -// 'prefs' => new \stdClass(), -// 'sessions' => [], -// 'tokens' => [], -// 'memberships' => [], -// 'roles' => [ -// 'admin', -// 'developer', -// 'tester', -// ], -// 'tags' => [ -// ['$id' => '1', 'label' => 'x'], -// ['$id' => '2', 'label' => 'y'], -// ['$id' => '3', 'label' => 'z'], -// ], -// ]); -// -// $result = static::getDatabase()->encode($collection, $document); -// -// $this->assertEquals('608fdbe51361a', $result->getAttribute('$id')); -// $this->assertContains('read("any")', $result->getAttribute('$permissions')); -// $this->assertContains('read("any")', $result->getPermissions()); -// $this->assertContains('any', $result->getRead()); -// $this->assertContains(Permission::create(Role::user(ID::custom('608fdbe51361a'))), $result->getPermissions()); -// $this->assertContains('user:608fdbe51361a', $result->getCreate()); -// $this->assertContains('user:608fdbe51361a', $result->getWrite()); -// $this->assertEquals('test@example.com', $result->getAttribute('email')); -// $this->assertEquals(false, $result->getAttribute('emailVerification')); -// $this->assertEquals(1, $result->getAttribute('status')); -// $this->assertEquals('randomhash', $result->getAttribute('password')); -// $this->assertEquals('2000-06-12 14:12:55.000', $result->getAttribute('passwordUpdate')); -// $this->assertEquals('1975-06-12 13:12:55.000', $result->getAttribute('registration')); -// $this->assertEquals(false, $result->getAttribute('reset')); -// $this->assertEquals('My Name', $result->getAttribute('name')); -// $this->assertEquals('{}', $result->getAttribute('prefs')); -// $this->assertEquals('[]', $result->getAttribute('sessions')); -// $this->assertEquals('[]', $result->getAttribute('tokens')); -// $this->assertEquals('[]', $result->getAttribute('memberships')); -// $this->assertEquals(['admin', 'developer', 'tester',], $result->getAttribute('roles')); -// $this->assertEquals(['{"$id":"1","label":"x"}', '{"$id":"2","label":"y"}', '{"$id":"3","label":"z"}',], $result->getAttribute('tags')); -// -// $result = static::getDatabase()->decode($collection, $document); -// -// $this->assertEquals('608fdbe51361a', $result->getAttribute('$id')); -// $this->assertContains('read("any")', $result->getAttribute('$permissions')); -// $this->assertContains('read("any")', $result->getPermissions()); -// $this->assertContains('any', $result->getRead()); -// $this->assertContains(Permission::create(Role::user('608fdbe51361a')), $result->getPermissions()); -// $this->assertContains('user:608fdbe51361a', $result->getCreate()); -// $this->assertContains('user:608fdbe51361a', $result->getWrite()); -// $this->assertEquals('test@example.com', $result->getAttribute('email')); -// $this->assertEquals(false, $result->getAttribute('emailVerification')); -// $this->assertEquals(1, $result->getAttribute('status')); -// $this->assertEquals('randomhash', $result->getAttribute('password')); -// $this->assertEquals('2000-06-12T14:12:55.000+00:00', $result->getAttribute('passwordUpdate')); -// $this->assertEquals('1975-06-12T13:12:55.000+00:00', $result->getAttribute('registration')); -// $this->assertEquals(false, $result->getAttribute('reset')); -// $this->assertEquals('My Name', $result->getAttribute('name')); -// $this->assertEquals([], $result->getAttribute('prefs')); -// $this->assertEquals([], $result->getAttribute('sessions')); -// $this->assertEquals([], $result->getAttribute('tokens')); -// $this->assertEquals([], $result->getAttribute('memberships')); -// $this->assertEquals(['admin', 'developer', 'tester',], $result->getAttribute('roles')); -// $this->assertEquals([ -// new Document(['$id' => '1', 'label' => 'x']), -// new Document(['$id' => '2', 'label' => 'y']), -// new Document(['$id' => '3', 'label' => 'z']), -// ], $result->getAttribute('tags')); -// } -// -// /** -// * @depends testCreateDocument -// */ -// public function testReadPermissionsSuccess(Document $document): Document -// { -// Authorization::cleanRoles(); -// Authorization::setRole(Role::any()->toString()); -// -// $document = static::getDatabase()->createDocument('documents', new Document([ -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'string' => 'text📝', -// 'integer' => 5, -// 'bigint' => 8589934592, // 2^33 -// 'float' => 5.55, -// 'boolean' => true, -// 'colors' => ['pink', 'green', 'blue'], -// ])); -// -// $this->assertEquals(false, $document->isEmpty()); -// -// Authorization::cleanRoles(); -// -// $document = static::getDatabase()->getDocument($document->getCollection(), $document->getId()); -// $this->assertEquals(true, $document->isEmpty()); -// -// Authorization::setRole(Role::any()->toString()); -// -// return $document; -// } -// -// public function testReadPermissionsFailure(): Document -// { -// Authorization::cleanRoles(); -// Authorization::setRole(Role::any()->toString()); -// -// $document = static::getDatabase()->createDocument('documents', new Document([ -// '$permissions' => [ -// Permission::read(Role::user('1')), -// Permission::create(Role::user('1')), -// Permission::update(Role::user('1')), -// Permission::delete(Role::user('1')), -// ], -// 'string' => 'text📝', -// 'integer' => 5, -// 'bigint' => 8589934592, // 2^33 -// 'float' => 5.55, -// 'boolean' => true, -// 'colors' => ['pink', 'green', 'blue'], -// ])); -// -// Authorization::cleanRoles(); -// -// $document = static::getDatabase()->getDocument($document->getCollection(), $document->getId()); -// -// $this->assertEquals(true, $document->isEmpty()); -// -// Authorization::setRole(Role::any()->toString()); -// -// return $document; -// } -// -// /** -// * @depends testCreateDocument -// */ -// public function testWritePermissionsSuccess(Document $document): void -// { -// Authorization::cleanRoles(); -// -// $this->expectException(AuthorizationException::class); -// static::getDatabase()->createDocument('documents', new Document([ -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'string' => 'text📝', -// 'integer' => 5, -// 'bigint' => 8589934592, // 2^33 -// 'float' => 5.55, -// 'boolean' => true, -// 'colors' => ['pink', 'green', 'blue'], -// ])); -// } -// -// /** -// * @depends testCreateDocument -// */ -// public function testWritePermissionsUpdateFailure(Document $document): Document -// { -// $this->expectException(AuthorizationException::class); -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::any()->toString()); -// -// $document = static::getDatabase()->createDocument('documents', new Document([ -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'string' => 'text📝', -// 'integer' => 5, -// 'bigint' => 8589934592, // 2^33 -// 'float' => 5.55, -// 'boolean' => true, -// 'colors' => ['pink', 'green', 'blue'], -// ])); -// -// Authorization::cleanRoles(); -// -// $document = static::getDatabase()->updateDocument('documents', $document->getId(), new Document([ -// '$id' => ID::custom($document->getId()), -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'string' => 'text📝', -// 'integer' => 6, -// 'bigint' => 8589934592, // 2^33 -// 'float' => 5.55, -// 'boolean' => true, -// 'colors' => ['pink', 'green', 'blue'], -// ])); -// -// return $document; -// } -// -// /** -// * @depends testCreateDocument -// */ -// public function testNoChangeUpdateDocumentWithoutPermission(Document $document): Document -// { -// $document = static::getDatabase()->createDocument('documents', new Document([ -// '$id' => ID::unique(), -// '$permissions' => [], -// 'string' => 'text📝', -// 'integer' => 5, -// 'bigint' => 8589934592, // 2^33 -// 'float' => 5.55, -// 'boolean' => true, -// 'colors' => ['pink', 'green', 'blue'], -// ])); -// -// $updatedDocument = static::getDatabase()->updateDocument( -// 'documents', -// $document->getId(), -// $document -// ); -// -// // Document should not be updated as there is no change. -// // It should also not throw any authorization exception without any permission because of no change. -// $this->assertEquals($updatedDocument->getUpdatedAt(), $document->getUpdatedAt()); -// -// return $document; -// } -// -// public function testNoChangeUpdateDocumentWithRelationWithoutPermission(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// $attribute = new Document([ -// '$id' => ID::custom("name"), -// 'type' => Database::VAR_STRING, -// 'size' => 100, -// 'required' => false, -// 'default' => null, -// 'signed' => false, -// 'array' => false, -// 'filters' => [], -// ]); -// -// $permissions = [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::delete(Role::any()), -// ]; -// for ($i=1; $i < 6; $i++) { -// static::getDatabase()->createCollection("level{$i}", [$attribute], [], $permissions); -// } -// -// for ($i = 1; $i < 5; $i++) { -// $collectionId = $i; -// $relatedCollectionId = $i+1; -// static::getDatabase()->createRelationship( -// collection: "level{$collectionId}", -// relatedCollection: "level{$relatedCollectionId}", -// type: Database::RELATION_ONE_TO_ONE, -// id: "level{$relatedCollectionId}" -// ); -// } -// -// // Create document with relationship with nested data -// $level1 = static::getDatabase()->createDocument('level1', new Document([ -// '$id' => 'level1', -// '$permissions' => [], -// 'name' => 'Level 1', -// 'level2' => [ -// '$id' => 'level2', -// '$permissions' => [], -// 'name' => 'Level 2', -// 'level3' => [ -// '$id' => 'level3', -// '$permissions' => [], -// 'name' => 'Level 3', -// 'level4' => [ -// '$id' => 'level4', -// '$permissions' => [], -// 'name' => 'Level 4', -// 'level5' => [ -// '$id' => 'level5', -// '$permissions' => [], -// 'name' => 'Level 5', -// ] -// ], -// ], -// ], -// ])); -// static::getDatabase()->updateDocument('level1', $level1->getId(), new Document($level1->getArrayCopy())); -// $updatedLevel1 = static::getDatabase()->getDocument('level1', $level1->getId()); -// $this->assertEquals($level1, $updatedLevel1); -// -// try { -// static::getDatabase()->updateDocument('level1', $level1->getId(), $level1->setAttribute('name', 'haha')); -// $this->fail('Failed to throw exception'); -// } catch(Exception $e) { -// $this->assertInstanceOf(AuthorizationException::class, $e); -// } -// $level1->setAttribute('name', 'Level 1'); -// static::getDatabase()->updateCollection('level3', [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], false); -// $level2 = $level1->getAttribute('level2'); -// $level3 = $level2->getAttribute('level3'); -// -// $level3->setAttribute('name', 'updated value'); -// $level2->setAttribute('level3', $level3); -// $level1->setAttribute('level2', $level2); -// -// $level1 = static::getDatabase()->updateDocument('level1', $level1->getId(), $level1); -// $this->assertEquals('updated value', $level1['level2']['level3']['name']); -// -// for ($i=1; $i < 6; $i++) { -// static::getDatabase()->deleteCollection("level{$i}"); -// } -// } -// -// public function testExceptionAttributeLimit(): void -// { -// if ($this->getDatabase()->getLimitForAttributes() > 0) { -// // Load the collection up to the limit -// $attributes = []; -// for ($i = 0; $i < $this->getDatabase()->getLimitForAttributes(); $i++) { -// $attributes[] = new Document([ -// '$id' => ID::custom("test{$i}"), -// 'type' => Database::VAR_INTEGER, -// 'size' => 0, -// 'required' => false, -// 'default' => null, -// 'signed' => true, -// 'array' => false, -// 'filters' => [], -// ]); -// } -// -// static::getDatabase()->createCollection('attributeLimit', $attributes); -// -// $this->expectException(LimitException::class); -// $this->assertEquals(false, static::getDatabase()->createAttribute('attributeLimit', "breaking", Database::VAR_INTEGER, 0, true)); -// } -// -// // Default assertion for other adapters -// $this->assertEquals(1, 1); -// } -// -// /** -// * @depends testExceptionAttributeLimit -// */ -// public function testCheckAttributeCountLimit(): void -// { -// if ($this->getDatabase()->getLimitForAttributes() > 0) { -// $collection = static::getDatabase()->getCollection('attributeLimit'); -// -// // create same attribute in testExceptionAttributeLimit -// $attribute = new Document([ -// '$id' => ID::custom('breaking'), -// 'type' => Database::VAR_INTEGER, -// 'size' => 0, -// 'required' => true, -// 'default' => null, -// 'signed' => true, -// 'array' => false, -// 'filters' => [], -// ]); -// -// $this->expectException(LimitException::class); -// $this->assertEquals(false, static::getDatabase()->checkAttribute($collection, $attribute)); -// } -// -// // Default assertion for other adapters -// $this->assertEquals(1, 1); -// } -// -// /** -// * Using phpunit dataProviders to check that all these combinations of types/sizes throw exceptions -// * https://phpunit.de/manual/3.7/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers -// * -// * @return array> -// */ -// public function rowWidthExceedsMaximum(): array -// { -// return [ -// // These combinations of attributes gets exactly to the 64k limit -// // [$key, $stringSize, $stringCount, $intCount, $floatCount, $boolCount] -// // [0, 1024, 15, 0, 731, 3], -// // [1, 512, 31, 0, 0, 833], -// // [2, 256, 62, 128, 0, 305], -// // [3, 128, 125, 30, 24, 2], -// // -// // Taken 500 bytes off for tests -// [0, 1024, 15, 0, 304, 3], -// [1, 512, 31, 0, 0, 333], -// [2, 256, 62, 103, 0, 5], -// [3, 128, 124, 30, 12, 14], -// ]; -// } -// -// /** -// * @dataProvider rowWidthExceedsMaximum -// */ -// public function testExceptionWidthLimit(int $key, int $stringSize, int $stringCount, int $intCount, int $floatCount, int $boolCount): void -// { -// if (static::getDatabase()->getAdapter()::getDocumentSizeLimit() > 0) { -// $attributes = []; -// -// // Load the collection up to the limit -// // Strings -// for ($i = 0; $i < $stringCount; $i++) { -// $attributes[] = new Document([ -// '$id' => ID::custom("test_string{$i}"), -// 'type' => Database::VAR_STRING, -// 'size' => $stringSize, -// 'required' => false, -// 'default' => null, -// 'signed' => true, -// 'array' => false, -// 'filters' => [], -// ]); -// } -// -// // Integers -// for ($i = 0; $i < $intCount; $i++) { -// $attributes[] = new Document([ -// '$id' => ID::custom("test_int{$i}"), -// 'type' => Database::VAR_INTEGER, -// 'size' => 0, -// 'required' => false, -// 'default' => null, -// 'signed' => true, -// 'array' => false, -// 'filters' => [], -// ]); -// } -// -// // Floats -// for ($i = 0; $i < $floatCount; $i++) { -// $attributes[] = new Document([ -// '$id' => ID::custom("test_float{$i}"), -// 'type' => Database::VAR_FLOAT, -// 'size' => 0, -// 'required' => false, -// 'default' => null, -// 'signed' => true, -// 'array' => false, -// 'filters' => [], -// ]); -// } -// -// // Booleans -// for ($i = 0; $i < $boolCount; $i++) { -// $attributes[] = new Document([ -// '$id' => ID::custom("test_bool{$i}"), -// 'type' => Database::VAR_BOOLEAN, -// 'size' => 0, -// 'required' => false, -// 'default' => null, -// 'signed' => true, -// 'array' => false, -// 'filters' => [], -// ]); -// } -// -// $collection = static::getDatabase()->createCollection("widthLimit{$key}", $attributes); -// -// $this->expectException(LimitException::class); -// $this->assertEquals(false, static::getDatabase()->createAttribute("widthLimit{$key}", "breaking", Database::VAR_STRING, 100, true)); -// } -// -// // Default assertion for other adapters -// $this->assertEquals(1, 1); -// } -// -// /** -// * @dataProvider rowWidthExceedsMaximum -// * @depends testExceptionWidthLimit -// */ -// public function testCheckAttributeWidthLimit(int $key, int $stringSize, int $stringCount, int $intCount, int $floatCount, int $boolCount): void -// { -// if (static::getDatabase()->getAdapter()::getDocumentSizeLimit()> 0) { -// $collection = static::getDatabase()->getCollection("widthLimit{$key}"); -// -// // create same attribute in testExceptionWidthLimit -// $attribute = new Document([ -// '$id' => ID::custom('breaking'), -// 'type' => Database::VAR_STRING, -// 'size' => 100, -// 'required' => true, -// 'default' => null, -// 'signed' => true, -// 'array' => false, -// 'filters' => [], -// ]); -// -// $this->expectException(LimitException::class); -// $this->assertEquals(false, static::getDatabase()->checkAttribute($collection, $attribute)); -// } -// -// // Default assertion for other adapters -// $this->assertEquals(1, 1); -// } -// -// public function testExceptionIndexLimit(): void -// { -// static::getDatabase()->createCollection('indexLimit'); -// -// // add unique attributes for indexing -// for ($i = 0; $i < 64; $i++) { -// $this->assertEquals(true, static::getDatabase()->createAttribute('indexLimit', "test{$i}", Database::VAR_STRING, 16, true)); -// } -// -// // Testing for indexLimit -// // Add up to the limit, then check if the next index throws IndexLimitException -// for ($i = 0; $i < ($this->getDatabase()->getLimitForIndexes()); $i++) { -// $this->assertEquals(true, static::getDatabase()->createIndex('indexLimit', "index{$i}", Database::INDEX_KEY, ["test{$i}"], [16])); -// } -// $this->expectException(LimitException::class); -// $this->assertEquals(false, static::getDatabase()->createIndex('indexLimit', "index64", Database::INDEX_KEY, ["test64"], [16])); -// -// static::getDatabase()->deleteCollection('indexLimit'); -// } -// -// /** -// * @depends testGetDocument -// */ -// public function testExceptionDuplicate(Document $document): void -// { -// $document->setAttribute('$id', 'duplicated'); -// static::getDatabase()->createDocument($document->getCollection(), $document); -// -// $this->expectException(DuplicateException::class); -// static::getDatabase()->createDocument($document->getCollection(), $document); -// } -// -// /** -// * @depends testGetDocument -// */ -// public function testExceptionCaseInsensitiveDuplicate(Document $document): Document -// { -// $document->setAttribute('$id', 'caseSensitive'); -// $document->setAttribute('$internalId', '200'); -// static::getDatabase()->createDocument($document->getCollection(), $document); -// -// $document->setAttribute('$id', 'CaseSensitive'); -// -// $this->expectException(DuplicateException::class); -// static::getDatabase()->createDocument($document->getCollection(), $document); -// -// return $document; -// } -// -// /** -// * @depends testFind -// */ -// public function testUniqueIndexDuplicate(): void -// { -// $this->expectException(DuplicateException::class); -// -// $this->assertEquals(true, static::getDatabase()->createIndex('movies', 'uniqueIndex', Database::INDEX_UNIQUE, ['name'], [128], [Database::ORDER_ASC])); -// -// static::getDatabase()->createDocument('movies', new Document([ -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::read(Role::user('1')), -// Permission::read(Role::user('2')), -// Permission::create(Role::any()), -// Permission::create(Role::user('1x')), -// Permission::create(Role::user('2x')), -// Permission::update(Role::any()), -// Permission::update(Role::user('1x')), -// Permission::update(Role::user('2x')), -// Permission::delete(Role::any()), -// Permission::delete(Role::user('1x')), -// Permission::delete(Role::user('2x')), -// ], -// 'name' => 'Frozen', -// 'director' => 'Chris Buck & Jennifer Lee', -// 'year' => 2013, -// 'price' => 39.50, -// 'active' => true, -// 'generes' => ['animation', 'kids'], -// 'with-dash' => 'Works4' -// ])); -// } -// -// /** -// * @depends testUniqueIndexDuplicate -// */ -// public function testUniqueIndexDuplicateUpdate(): void -// { -// Authorization::setRole(Role::users()->toString()); -// // create document then update to conflict with index -// $document = static::getDatabase()->createDocument('movies', new Document([ -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::read(Role::user('1')), -// Permission::read(Role::user('2')), -// Permission::create(Role::any()), -// Permission::create(Role::user('1x')), -// Permission::create(Role::user('2x')), -// Permission::update(Role::any()), -// Permission::update(Role::user('1x')), -// Permission::update(Role::user('2x')), -// Permission::delete(Role::any()), -// Permission::delete(Role::user('1x')), -// Permission::delete(Role::user('2x')), -// ], -// 'name' => 'Frozen 5', -// 'director' => 'Chris Buck & Jennifer Lee', -// 'year' => 2013, -// 'price' => 39.50, -// 'active' => true, -// 'generes' => ['animation', 'kids'], -// 'with-dash' => 'Works4' -// ])); -// -// $this->expectException(DuplicateException::class); -// -// static::getDatabase()->updateDocument('movies', $document->getId(), $document->setAttribute('name', 'Frozen')); -// } -// -// public function testGetAttributeLimit(): void -// { -// $this->assertIsInt($this->getDatabase()->getLimitForAttributes()); -// } -// -// public function testGetIndexLimit(): void -// { -// $this->assertEquals(59, $this->getDatabase()->getLimitForIndexes()); -// } -// -// public function testGetId(): void -// { -// $this->assertEquals(20, strlen(ID::unique())); -// $this->assertEquals(13, strlen(ID::unique(0))); -// $this->assertEquals(13, strlen(ID::unique(-1))); -// $this->assertEquals(23, strlen(ID::unique(10))); -// -// // ensure two sequential calls to getId do not give the same result -// $this->assertNotEquals(ID::unique(10), ID::unique(10)); -// } -// -// public function testRenameIndex(): void -// { -// $database = static::getDatabase(); -// -// $numbers = $database->createCollection('numbers'); -// $database->createAttribute('numbers', 'verbose', Database::VAR_STRING, 128, true); -// $database->createAttribute('numbers', 'symbol', Database::VAR_INTEGER, 0, true); -// -// $database->createIndex('numbers', 'index1', Database::INDEX_KEY, ['verbose'], [128], [Database::ORDER_ASC]); -// $database->createIndex('numbers', 'index2', Database::INDEX_KEY, ['symbol'], [0], [Database::ORDER_ASC]); -// -// $index = $database->renameIndex('numbers', 'index1', 'index3'); -// -// $this->assertTrue($index); -// -// $numbers = $database->getCollection('numbers'); -// -// $this->assertEquals('index2', $numbers->getAttribute('indexes')[1]['$id']); -// $this->assertEquals('index3', $numbers->getAttribute('indexes')[0]['$id']); -// $this->assertCount(2, $numbers->getAttribute('indexes')); -// } -// -// /** -// * @depends testRenameIndex -// * @expectedException Exception -// */ -// public function testRenameIndexMissing(): void -// { -// $database = static::getDatabase(); -// $this->expectExceptionMessage('Index not found'); -// $index = $database->renameIndex('numbers', 'index1', 'index4'); -// } -// -// /** -// * @depends testRenameIndex -// * @expectedException Exception -// */ -// public function testRenameIndexExisting(): void -// { -// $database = static::getDatabase(); -// $this->expectExceptionMessage('Index name already used'); -// $index = $database->renameIndex('numbers', 'index3', 'index2'); -// } -// -// public function testRenameAttribute(): void -// { -// $database = static::getDatabase(); -// -// $colors = $database->createCollection('colors'); -// $database->createAttribute('colors', 'name', Database::VAR_STRING, 128, true); -// $database->createAttribute('colors', 'hex', Database::VAR_STRING, 128, true); -// -// $database->createIndex('colors', 'index1', Database::INDEX_KEY, ['name'], [128], [Database::ORDER_ASC]); -// -// $database->createDocument('colors', new Document([ -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'black', -// 'hex' => '#000000' -// ])); -// -// $attribute = $database->renameAttribute('colors', 'name', 'verbose'); -// -// $this->assertTrue($attribute); -// -// $colors = $database->getCollection('colors'); -// $this->assertEquals('hex', $colors->getAttribute('attributes')[1]['$id']); -// $this->assertEquals('verbose', $colors->getAttribute('attributes')[0]['$id']); -// $this->assertCount(2, $colors->getAttribute('attributes')); -// -// // Attribute in index is renamed automatically on adapter-level. What we need to check is if metadata is properly updated -// $this->assertEquals('verbose', $colors->getAttribute('indexes')[0]->getAttribute("attributes")[0]); -// $this->assertCount(1, $colors->getAttribute('indexes')); -// -// // Document should be there if adapter migrated properly -// $document = $database->findOne('colors'); -// $this->assertTrue($document instanceof Document); -// $this->assertEquals('black', $document->getAttribute('verbose')); -// $this->assertEquals('#000000', $document->getAttribute('hex')); -// $this->assertEquals(null, $document->getAttribute('name')); -// } -// -// /** -// * @depends testRenameAttribute -// * @expectedException Exception -// */ -// public function textRenameAttributeMissing(): void -// { -// $database = static::getDatabase(); -// $this->expectExceptionMessage('Attribute not found'); -// $database->renameAttribute('colors', 'name2', 'name3'); -// } -// -// /** -// * @depends testRenameAttribute -// * @expectedException Exception -// */ -// public function testRenameAttributeExisting(): void -// { -// $database = static::getDatabase(); -// $this->expectExceptionMessage('Attribute name already used'); -// $database->renameAttribute('colors', 'verbose', 'hex'); -// } -// -// public function testUpdateAttributeDefault(): void -// { -// $database = static::getDatabase(); -// -// $flowers = $database->createCollection('flowers'); -// $database->createAttribute('flowers', 'name', Database::VAR_STRING, 128, true); -// $database->createAttribute('flowers', 'inStock', Database::VAR_INTEGER, 0, false); -// $database->createAttribute('flowers', 'date', Database::VAR_STRING, 128, false); -// -// $database->createDocument('flowers', new Document([ -// '$id' => 'flowerWithDate', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Violet', -// 'inStock' => 51, -// 'date' => '2000-06-12 14:12:55.000' -// ])); -// -// $doc = $database->createDocument('flowers', new Document([ -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Lily' -// ])); -// -// $this->assertNull($doc->getAttribute('inStock')); -// -// $database->updateAttributeDefault('flowers', 'inStock', 100); -// -// $doc = $database->createDocument('flowers', new Document([ -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Iris' -// ])); -// -// $this->assertIsNumeric($doc->getAttribute('inStock')); -// $this->assertEquals(100, $doc->getAttribute('inStock')); -// -// $database->updateAttributeDefault('flowers', 'inStock', null); -// } -// -// /** -// * @depends testUpdateAttributeDefault -// */ -// public function testUpdateAttributeRequired(): void -// { -// $database = static::getDatabase(); -// -// $database->updateAttributeRequired('flowers', 'inStock', true); -// -// $this->expectExceptionMessage('Invalid document structure: Missing required attribute "inStock"'); -// -// $doc = $database->createDocument('flowers', new Document([ -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Lily With Missing Stocks' -// ])); -// } -// -// /** -// * @depends testUpdateAttributeDefault -// */ -// public function testUpdateAttributeFilter(): void -// { -// $database = static::getDatabase(); -// -// $database->createAttribute('flowers', 'cartModel', Database::VAR_STRING, 2000, false); -// -// $doc = $database->createDocument('flowers', new Document([ -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Lily With CartData', -// 'inStock' => 50, -// 'cartModel' => '{"color":"string","size":"number"}' -// ])); -// -// $this->assertIsString($doc->getAttribute('cartModel')); -// $this->assertEquals('{"color":"string","size":"number"}', $doc->getAttribute('cartModel')); -// -// $database->updateAttributeFilters('flowers', 'cartModel', ['json']); -// -// $doc = $database->getDocument('flowers', $doc->getId()); -// $this->assertIsArray($doc->getAttribute('cartModel')); -// $this->assertCount(2, $doc->getAttribute('cartModel')); -// $this->assertEquals('string', $doc->getAttribute('cartModel')['color']); -// $this->assertEquals('number', $doc->getAttribute('cartModel')['size']); -// } -// -// /** -// * @depends testUpdateAttributeDefault -// */ -// public function testUpdateAttributeFormat(): void -// { -// $database = static::getDatabase(); -// -// $database->createAttribute('flowers', 'price', Database::VAR_INTEGER, 0, false); -// -// $doc = $database->createDocument('flowers', new Document([ -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// '$id' => ID::custom('LiliPriced'), -// 'name' => 'Lily Priced', -// 'inStock' => 50, -// 'cartModel' => '{}', -// 'price' => 500 -// ])); -// -// $this->assertIsNumeric($doc->getAttribute('price')); -// $this->assertEquals(500, $doc->getAttribute('price')); -// -// Structure::addFormat('priceRange', function ($attribute) { -// $min = $attribute['formatOptions']['min']; -// $max = $attribute['formatOptions']['max']; -// -// return new Range($min, $max); -// }, Database::VAR_INTEGER); -// -// $database->updateAttributeFormat('flowers', 'price', 'priceRange'); -// $database->updateAttributeFormatOptions('flowers', 'price', ['min' => 1, 'max' => 10000]); -// -// $this->expectExceptionMessage('Invalid document structure: Attribute "price" has invalid format. Value must be a valid range between 1 and 10,000'); -// -// $doc = $database->createDocument('flowers', new Document([ -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Lily Overpriced', -// 'inStock' => 50, -// 'cartModel' => '{}', -// 'price' => 15000 -// ])); -// } -// -// /** -// * @depends testUpdateAttributeDefault -// * @depends testUpdateAttributeFormat -// */ -// public function testUpdateAttributeStructure(): void -// { -// // TODO: When this becomes relevant, add many more tests (from all types to all types, chaging size up&down, switchign between array/non-array... -// -// Structure::addFormat('priceRangeNew', function ($attribute) { -// $min = $attribute['formatOptions']['min']; -// $max = $attribute['formatOptions']['max']; -// return new Range($min, $max); -// }, Database::VAR_INTEGER); -// -// $database = static::getDatabase(); -// -// // price attribute -// $collection = $database->getCollection('flowers'); -// $attribute = $collection->getAttribute('attributes')[4]; -// $this->assertEquals(true, $attribute['signed']); -// $this->assertEquals(0, $attribute['size']); -// $this->assertEquals(null, $attribute['default']); -// $this->assertEquals(false, $attribute['array']); -// $this->assertEquals(false, $attribute['required']); -// $this->assertEquals('priceRange', $attribute['format']); -// $this->assertEquals(['min'=>1, 'max'=>10000], $attribute['formatOptions']); -// -// $database->updateAttribute('flowers', 'price', default: 100); -// $collection = $database->getCollection('flowers'); -// $attribute = $collection->getAttribute('attributes')[4]; -// $this->assertEquals('integer', $attribute['type']); -// $this->assertEquals(true, $attribute['signed']); -// $this->assertEquals(0, $attribute['size']); -// $this->assertEquals(100, $attribute['default']); -// $this->assertEquals(false, $attribute['array']); -// $this->assertEquals(false, $attribute['required']); -// $this->assertEquals('priceRange', $attribute['format']); -// $this->assertEquals(['min'=>1, 'max'=>10000], $attribute['formatOptions']); -// -// $database->updateAttribute('flowers', 'price', format: 'priceRangeNew'); -// $collection = $database->getCollection('flowers'); -// $attribute = $collection->getAttribute('attributes')[4]; -// $this->assertEquals('integer', $attribute['type']); -// $this->assertEquals(true, $attribute['signed']); -// $this->assertEquals(0, $attribute['size']); -// $this->assertEquals(100, $attribute['default']); -// $this->assertEquals(false, $attribute['array']); -// $this->assertEquals(false, $attribute['required']); -// $this->assertEquals('priceRangeNew', $attribute['format']); -// $this->assertEquals(['min'=>1, 'max'=>10000], $attribute['formatOptions']); -// -// $database->updateAttribute('flowers', 'price', format: ''); -// $collection = $database->getCollection('flowers'); -// $attribute = $collection->getAttribute('attributes')[4]; -// $this->assertEquals('integer', $attribute['type']); -// $this->assertEquals(true, $attribute['signed']); -// $this->assertEquals(0, $attribute['size']); -// $this->assertEquals(100, $attribute['default']); -// $this->assertEquals(false, $attribute['array']); -// $this->assertEquals(false, $attribute['required']); -// $this->assertEquals('', $attribute['format']); -// $this->assertEquals(['min'=>1, 'max'=>10000], $attribute['formatOptions']); -// -// $database->updateAttribute('flowers', 'price', formatOptions: ['min' => 1, 'max' => 999]); -// $collection = $database->getCollection('flowers'); -// $attribute = $collection->getAttribute('attributes')[4]; -// $this->assertEquals('integer', $attribute['type']); -// $this->assertEquals(true, $attribute['signed']); -// $this->assertEquals(0, $attribute['size']); -// $this->assertEquals(100, $attribute['default']); -// $this->assertEquals(false, $attribute['array']); -// $this->assertEquals(false, $attribute['required']); -// $this->assertEquals('', $attribute['format']); -// $this->assertEquals(['min'=>1, 'max'=>999], $attribute['formatOptions']); -// -// $database->updateAttribute('flowers', 'price', formatOptions: []); -// $collection = $database->getCollection('flowers'); -// $attribute = $collection->getAttribute('attributes')[4]; -// $this->assertEquals('integer', $attribute['type']); -// $this->assertEquals(true, $attribute['signed']); -// $this->assertEquals(0, $attribute['size']); -// $this->assertEquals(100, $attribute['default']); -// $this->assertEquals(false, $attribute['array']); -// $this->assertEquals(false, $attribute['required']); -// $this->assertEquals('', $attribute['format']); -// $this->assertEquals([], $attribute['formatOptions']); -// -// $database->updateAttribute('flowers', 'price', signed: false); -// $collection = $database->getCollection('flowers'); -// $attribute = $collection->getAttribute('attributes')[4]; -// $this->assertEquals('integer', $attribute['type']); -// $this->assertEquals(false, $attribute['signed']); -// $this->assertEquals(0, $attribute['size']); -// $this->assertEquals(100, $attribute['default']); -// $this->assertEquals(false, $attribute['array']); -// $this->assertEquals(false, $attribute['required']); -// $this->assertEquals('', $attribute['format']); -// $this->assertEquals([], $attribute['formatOptions']); -// -// $database->updateAttribute('flowers', 'price', required: true); -// $collection = $database->getCollection('flowers'); -// $attribute = $collection->getAttribute('attributes')[4]; -// $this->assertEquals('integer', $attribute['type']); -// $this->assertEquals(false, $attribute['signed']); -// $this->assertEquals(0, $attribute['size']); -// $this->assertEquals(null, $attribute['default']); -// $this->assertEquals(false, $attribute['array']); -// $this->assertEquals(true, $attribute['required']); -// $this->assertEquals('', $attribute['format']); -// $this->assertEquals([], $attribute['formatOptions']); -// -// $database->updateAttribute('flowers', 'price', type: Database::VAR_STRING, size: Database::LENGTH_KEY, format: ''); -// $collection = $database->getCollection('flowers'); -// $attribute = $collection->getAttribute('attributes')[4]; -// $this->assertEquals('string', $attribute['type']); -// $this->assertEquals(false, $attribute['signed']); -// $this->assertEquals(255, $attribute['size']); -// $this->assertEquals(null, $attribute['default']); -// $this->assertEquals(false, $attribute['array']); -// $this->assertEquals(true, $attribute['required']); -// $this->assertEquals('', $attribute['format']); -// $this->assertEquals([], $collection->getAttribute('attributes')[4]['formatOptions']); -// -// // Date attribute -// $attribute = $collection->getAttribute('attributes')[2]; -// $this->assertEquals('date', $attribute['key']); -// $this->assertEquals('string', $attribute['type']); -// $this->assertEquals(null, $attribute['default']); -// -// $database->updateAttribute('flowers', 'date', type: Database::VAR_DATETIME, size: 0, filters: ['datetime']); -// $collection = $database->getCollection('flowers'); -// $attribute = $collection->getAttribute('attributes')[2]; -// $this->assertEquals('datetime', $attribute['type']); -// $this->assertEquals(0, $attribute['size']); -// $this->assertEquals(null, $attribute['default']); -// $this->assertEquals(false, $attribute['required']); -// $this->assertEquals(true, $attribute['signed']); -// $this->assertEquals(false, $attribute['array']); -// $this->assertEquals('', $attribute['format']); -// $this->assertEquals([], $attribute['formatOptions']); -// -// $doc = $database->getDocument('flowers', 'LiliPriced'); -// $this->assertIsString($doc->getAttribute('price')); -// $this->assertEquals('500', $doc->getAttribute('price')); -// -// $doc = $database->getDocument('flowers', 'flowerWithDate'); -// $this->assertEquals('2000-06-12T14:12:55.000+00:00', $doc->getAttribute('date')); -// } -// -// /** -// * @depends testCreatedAtUpdatedAt -// */ -// public function testCreatedAtUpdatedAtAssert(): void -// { -// $document = static::getDatabase()->getDocument('created_at', 'uid123'); -// $this->assertEquals(true, !$document->isEmpty()); -// sleep(1); -// $document->setAttribute('title', 'new title'); -// static::getDatabase()->updateDocument('created_at', 'uid123', $document); -// $document = static::getDatabase()->getDocument('created_at', 'uid123'); -// -// $this->assertGreaterThan($document->getCreatedAt(), $document->getUpdatedAt()); -// $this->expectException(DuplicateException::class); -// -// static::getDatabase()->createCollection('created_at'); -// } -// -// public function testCreateDatetime(): void -// { -// static::getDatabase()->createCollection('datetime'); -// -// $this->assertEquals(true, static::getDatabase()->createAttribute('datetime', 'date', Database::VAR_DATETIME, 0, true, null, true, false, null, [], ['datetime'])); -// $this->assertEquals(true, static::getDatabase()->createAttribute('datetime', 'date2', Database::VAR_DATETIME, 0, false, null, true, false, null, [], ['datetime'])); -// -// $doc = static::getDatabase()->createDocument('datetime', new Document([ -// '$id' => ID::custom('id1234'), -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'date' => DateTime::now(), -// ])); -// -// $this->assertEquals(29, strlen($doc->getCreatedAt())); -// $this->assertEquals(29, strlen($doc->getUpdatedAt())); -// $this->assertEquals('+00:00', substr($doc->getCreatedAt(), -6)); -// $this->assertEquals('+00:00', substr($doc->getUpdatedAt(), -6)); -// $this->assertGreaterThan('2020-08-16T19:30:08.363+00:00', $doc->getCreatedAt()); -// $this->assertGreaterThan('2020-08-16T19:30:08.363+00:00', $doc->getUpdatedAt()); -// -// $document = static::getDatabase()->getDocument('datetime', 'id1234'); -// $dateValidator = new DatetimeValidator(); -// $this->assertEquals(null, $document->getAttribute('date2')); -// $this->assertEquals(true, $dateValidator->isValid($document->getAttribute('date'))); -// $this->assertEquals(false, $dateValidator->isValid($document->getAttribute('date2'))); -// -// $documents = static::getDatabase()->find('datetime', [ -// Query::greaterThan('date', '1975-12-06 10:00:00+01:00'), -// Query::lessThan('date', '2030-12-06 10:00:00-01:00'), -// ]); -// -// $this->assertEquals(1, count($documents)); -// -// $this->expectException(StructureException::class); -// static::getDatabase()->createDocument('datetime', new Document([ -// '$permissions' => [ -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'date' => "1975-12-06 00:00:61" -// ])); -// } -// -// public function testCreateDateTimeAttributeFailure(): void -// { -// static::getDatabase()->createCollection('datetime_fail'); -// -// /** Test for FAILURE */ -// $this->expectException(Exception::class); -// static::getDatabase()->createAttribute('datetime_fail', 'date_fail', Database::VAR_DATETIME, 0, false); -// } -// -// public function testKeywords(): void -// { -// $database = static::getDatabase(); -// $keywords = $database->getKeywords(); -// -// // Collection name tests -// $attributes = [ -// new Document([ -// '$id' => ID::custom('attribute1'), -// 'type' => Database::VAR_STRING, -// 'size' => 256, -// 'required' => false, -// 'signed' => true, -// 'array' => false, -// 'filters' => [], -// ]), -// ]; -// -// $indexes = [ -// new Document([ -// '$id' => ID::custom('index1'), -// 'type' => Database::INDEX_KEY, -// 'attributes' => ['attribute1'], -// 'lengths' => [256], -// 'orders' => ['ASC'], -// ]), -// ]; -// -// foreach ($keywords as $keyword) { -// $collection = $database->createCollection($keyword, $attributes, $indexes); -// $this->assertEquals($keyword, $collection->getId()); -// -// $document = $database->createDocument($keyword, new Document([ -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// '$id' => ID::custom('helloWorld'), -// 'attribute1' => 'Hello World', -// ])); -// $this->assertEquals('helloWorld', $document->getId()); -// -// $document = $database->getDocument($keyword, 'helloWorld'); -// $this->assertEquals('helloWorld', $document->getId()); -// -// $documents = $database->find($keyword); -// $this->assertCount(1, $documents); -// $this->assertEquals('helloWorld', $documents[0]->getId()); -// -// $collection = $database->deleteCollection($keyword); -// $this->assertTrue($collection); -// } -// -// // TODO: updateCollection name tests -// -// // Attribute name tests -// foreach ($keywords as $keyword) { -// $collectionName = 'rk' . $keyword; // rk is short-hand for reserved-keyword. We do this sicne there are some limits (64 chars max) -// -// $collection = $database->createCollection($collectionName); -// $this->assertEquals($collectionName, $collection->getId()); -// -// $attribute = static::getDatabase()->createAttribute($collectionName, $keyword, Database::VAR_STRING, 128, true); -// $this->assertEquals(true, $attribute); -// -// $document = new Document([ -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// '$id' => 'reservedKeyDocument' -// ]); -// $document->setAttribute($keyword, 'Reserved:' . $keyword); -// -// $document = $database->createDocument($collectionName, $document); -// $this->assertEquals('reservedKeyDocument', $document->getId()); -// $this->assertEquals('Reserved:' . $keyword, $document->getAttribute($keyword)); -// -// $document = $database->getDocument($collectionName, 'reservedKeyDocument'); -// $this->assertEquals('reservedKeyDocument', $document->getId()); -// $this->assertEquals('Reserved:' . $keyword, $document->getAttribute($keyword)); -// -// $documents = $database->find($collectionName); -// $this->assertCount(1, $documents); -// $this->assertEquals('reservedKeyDocument', $documents[0]->getId()); -// $this->assertEquals('Reserved:' . $keyword, $documents[0]->getAttribute($keyword)); -// -// $documents = $database->find($collectionName, [Query::equal($keyword, ["Reserved:${keyword}"])]); -// $this->assertCount(1, $documents); -// $this->assertEquals('reservedKeyDocument', $documents[0]->getId()); -// -// $documents = $database->find($collectionName, [ -// Query::orderDesc($keyword) -// ]); -// $this->assertCount(1, $documents); -// $this->assertEquals('reservedKeyDocument', $documents[0]->getId()); -// -// -// $collection = $database->deleteCollection($collectionName); -// $this->assertTrue($collection); -// -// // TODO: updateAttribute name tests -// } -// -// // TODO: Index name tests -// } -// -// public function testWritePermissions(): void -// { -// Authorization::setRole(Role::any()->toString()); -// $database = static::getDatabase(); -// -// $database->createCollection('animals', permissions: [ -// Permission::create(Role::any()), -// ], documentSecurity: true); -// -// $database->createAttribute('animals', 'type', Database::VAR_STRING, 128, true); -// -// $dog = $database->createDocument('animals', new Document([ -// '$id' => 'dog', -// '$permissions' => [ -// Permission::delete(Role::any()), -// ], -// 'type' => 'Dog' -// ])); -// -// $cat = $database->createDocument('animals', new Document([ -// '$id' => 'cat', -// '$permissions' => [ -// Permission::update(Role::any()), -// ], -// 'type' => 'Cat' -// ])); -// -// // No read permissions: -// -// $docs = $database->find('animals'); -// $this->assertCount(0, $docs); -// -// $doc = $database->getDocument('animals', 'dog'); -// $this->assertTrue($doc->isEmpty()); -// -// $doc = $database->getDocument('animals', 'cat'); -// $this->assertTrue($doc->isEmpty()); -// -// // Cannot delete with update permission: -// $didFail = false; -// -// try { -// $database->deleteDocument('animals', 'cat'); -// } catch (AuthorizationException) { -// $didFail = true; -// } -// -// $this->assertTrue($didFail); -// -// // Cannot update with delete permission: -// $didFail = false; -// -// try { -// $newDog = $dog->setAttribute('type', 'newDog'); -// $database->updateDocument('animals', 'dog', $newDog); -// } catch (AuthorizationException) { -// $didFail = true; -// } -// -// $this->assertTrue($didFail); -// -// // Can delete: -// $database->deleteDocument('animals', 'dog'); -// -// // Can update: -// $newCat = $cat->setAttribute('type', 'newCat'); -// $database->updateDocument('animals', 'cat', $newCat); -// -// $docs = Authorization::skip(fn () => $database->find('animals')); -// $this->assertCount(1, $docs); -// $this->assertEquals('cat', $docs[0]['$id']); -// $this->assertEquals('newCat', $docs[0]['type']); -// } -// -// public function testNoInvalidKeysWithRelationships(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// static::getDatabase()->createCollection('species'); -// static::getDatabase()->createCollection('creatures'); -// static::getDatabase()->createCollection('characterstics'); -// -// static::getDatabase()->createAttribute('species', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('creatures', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('characterstics', 'name', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'species', -// relatedCollection: 'creatures', -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// id: 'creature', -// twoWayKey:'species' -// ); -// static::getDatabase()->createRelationship( -// collection: 'creatures', -// relatedCollection: 'characterstics', -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// id: 'characterstic', -// twoWayKey:'creature' -// ); -// -// $species = static::getDatabase()->createDocument('species', new Document([ -// '$id' => ID::custom('1'), -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Canine', -// 'creature' => [ -// '$id' => ID::custom('1'), -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Dog', -// 'characterstic' => [ -// '$id' => ID::custom('1'), -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// ], -// 'name' => 'active', -// ] -// ] -// ])); -// static::getDatabase()->updateDocument('species', $species->getId(), new Document([ -// '$id' => ID::custom('1'), -// '$collection' => 'species', -// 'creature' => [ -// '$id' => ID::custom('1'), -// '$collection' => 'creatures', -// 'characterstic' => [ -// '$id' => ID::custom('1'), -// 'name' => 'active', -// '$collection' => 'characterstics', -// ] -// ] -// ])); -// $updatedSpecies = static::getDatabase()->getDocument('species', $species->getId()); -// $this->assertEquals($species, $updatedSpecies); -// } -// -// // Relationships -// public function testOneToOneOneWayRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('person'); -// static::getDatabase()->createCollection('library'); -// -// static::getDatabase()->createAttribute('person', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('library', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('library', 'area', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'person', -// relatedCollection: 'library', -// type: Database::RELATION_ONE_TO_ONE -// ); -// -// // Check metadata for collection -// $collection = static::getDatabase()->getCollection('person'); -// $attributes = $collection->getAttribute('attributes', []); -// -// foreach ($attributes as $attribute) { -// if ($attribute['key'] === 'library') { -// $this->assertEquals('relationship', $attribute['type']); -// $this->assertEquals('library', $attribute['$id']); -// $this->assertEquals('library', $attribute['key']); -// $this->assertEquals('library', $attribute['options']['relatedCollection']); -// $this->assertEquals(Database::RELATION_ONE_TO_ONE, $attribute['options']['relationType']); -// $this->assertEquals(false, $attribute['options']['twoWay']); -// $this->assertEquals('person', $attribute['options']['twoWayKey']); -// } -// } -// -// try { -// static::getDatabase()->deleteAttribute('person', 'library'); -// $this->fail('Failed to throw Exception'); -// } catch (Exception $e) { -// $this->assertEquals('Cannot delete relationship as an attribute', $e->getMessage()); -// } -// -// // Create document with relationship with nested data -// $person1 = static::getDatabase()->createDocument('person', new Document([ -// '$id' => 'person1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Person 1', -// 'library' => [ -// '$id' => 'library1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Library 1', -// 'area' => 'Area 1', -// ], -// ])); -// -// // Update a document with non existing related document. It should not get added to the list. -// static::getDatabase()->updateDocument( -// 'person', -// 'person1', -// $person1->setAttribute('library', 'no-library') -// ); -// -// $person1Document = static::getDatabase()->getDocument('person', 'person1'); -// // Assert document does not contain non existing relation document. -// $this->assertEquals(null, $person1Document->getAttribute('library')); -// -// static::getDatabase()->updateDocument( -// 'person', -// 'person1', -// $person1->setAttribute('library', 'library1') -// ); -// -// // Update through create -// $library10 = static::getDatabase()->createDocument('library', new Document([ -// '$id' => 'library10', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// ], -// 'name' => 'Library 10', -// 'area' => 'Area 10', -// ])); -// $person10 = static::getDatabase()->createDocument('person', new Document([ -// '$id' => 'person10', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Person 10', -// 'library' => [ -// '$id' => $library10->getId(), -// 'name' => 'Library 10 Updated', -// 'area' => 'Area 10 Updated', -// ], -// ])); -// $this->assertEquals('Library 10 Updated', $person10->getAttribute('library')->getAttribute('name')); -// $library10 = static::getDatabase()->getDocument('library', $library10->getId()); -// $this->assertEquals('Library 10 Updated', $library10->getAttribute('name')); -// -// // Create document with relationship with related ID -// static::getDatabase()->createDocument('library', new Document([ -// '$id' => 'library2', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Library 2', -// 'area' => 'Area 2', -// ])); -// static::getDatabase()->createDocument('person', new Document([ -// '$id' => 'person2', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Person 2', -// 'library' => 'library2', -// ])); -// -// // Get documents with relationship -// $person1 = static::getDatabase()->getDocument('person', 'person1'); -// $library = $person1->getAttribute('library'); -// $this->assertEquals('library1', $library['$id']); -// $this->assertArrayNotHasKey('person', $library); -// -// $person = static::getDatabase()->getDocument('person', 'person2'); -// $library = $person->getAttribute('library'); -// $this->assertEquals('library2', $library['$id']); -// $this->assertArrayNotHasKey('person', $library); -// -// // Get related documents -// $library = static::getDatabase()->getDocument('library', 'library1'); -// $this->assertArrayNotHasKey('person', $library); -// -// $library = static::getDatabase()->getDocument('library', 'library2'); -// $this->assertArrayNotHasKey('person', $library); -// -// $people = static::getDatabase()->find('person', [ -// Query::select(['name']) -// ]); -// -// $this->assertArrayNotHasKey('library', $people[0]); -// -// $people = static::getDatabase()->find('person'); -// $this->assertEquals(3, \count($people)); -// -// // Select related document attributes -// $person = static::getDatabase()->findOne('person', [ -// Query::select(['*', 'library.name']) -// ]); -// -// if (!$person instanceof Document) { -// throw new Exception('Person not found'); -// } -// -// $this->assertEquals('Library 1', $person->getAttribute('library')->getAttribute('name')); -// $this->assertArrayNotHasKey('area', $person->getAttribute('library')); -// -// $person = static::getDatabase()->getDocument('person', 'person1', [ -// Query::select(['*', 'library.name', '$id']) -// ]); -// -// $this->assertEquals('Library 1', $person->getAttribute('library')->getAttribute('name')); -// $this->assertArrayNotHasKey('area', $person->getAttribute('library')); -// -// -// -// $document = static::getDatabase()->getDocument('person', $person->getId(), [ -// Query::select(['name']), -// ]); -// $this->assertArrayNotHasKey('library', $document); -// $this->assertEquals('Person 1', $document['name']); -// -// $document = static::getDatabase()->getDocument('person', $person->getId(), [ -// Query::select(['*']), -// ]); -// $this->assertEquals('library1', $document['library']); -// -// $document = static::getDatabase()->getDocument('person', $person->getId(), [ -// Query::select(['library.*']), -// ]); -// $this->assertEquals('Library 1', $document['library']['name']); -// $this->assertArrayNotHasKey('name', $document); -// -// // Update root document attribute without altering relationship -// $person1 = static::getDatabase()->updateDocument( -// 'person', -// $person1->getId(), -// $person1->setAttribute('name', 'Person 1 Updated') -// ); -// -// $this->assertEquals('Person 1 Updated', $person1->getAttribute('name')); -// $person1 = static::getDatabase()->getDocument('person', 'person1'); -// $this->assertEquals('Person 1 Updated', $person1->getAttribute('name')); -// -// // Update nested document attribute -// $person1 = static::getDatabase()->updateDocument( -// 'person', -// $person1->getId(), -// $person1->setAttribute( -// 'library', -// $person1 -// ->getAttribute('library') -// ->setAttribute('name', 'Library 1 Updated') -// ) -// ); -// -// $this->assertEquals('Library 1 Updated', $person1->getAttribute('library')->getAttribute('name')); -// $person1 = static::getDatabase()->getDocument('person', 'person1'); -// $this->assertEquals('Library 1 Updated', $person1->getAttribute('library')->getAttribute('name')); -// -// // Create new document with no relationship -// $person3 = static::getDatabase()->createDocument('person', new Document([ -// '$id' => 'person3', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Person 3', -// ])); -// -// // Update to relate to created document -// $person3 = static::getDatabase()->updateDocument( -// 'person', -// $person3->getId(), -// $person3->setAttribute('library', new Document([ -// '$id' => 'library3', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// ], -// 'name' => 'Library 3', -// 'area' => 'Area 3', -// ])) -// ); -// -// $this->assertEquals('library3', $person3->getAttribute('library')['$id']); -// $person3 = static::getDatabase()->getDocument('person', 'person3'); -// $this->assertEquals('Library 3', $person3['library']['name']); -// -// $libraryDocument = static::getDatabase()->getDocument('library', 'library3'); -// $libraryDocument->setAttribute('name', 'Library 3 updated'); -// static::getDatabase()->updateDocument('library', 'library3', $libraryDocument); -// $libraryDocument = static::getDatabase()->getDocument('library', 'library3'); -// $this->assertEquals('Library 3 updated', $libraryDocument['name']); -// -// $person3 = static::getDatabase()->getDocument('person', 'person3'); -// // Todo: This is failing -// $this->assertEquals($libraryDocument['name'], $person3['library']['name']); -// $this->assertEquals('library3', $person3->getAttribute('library')['$id']); -// -// // One to one can't relate to multiple documents, unique index throws duplicate -// try { -// static::getDatabase()->updateDocument( -// 'person', -// $person1->getId(), -// $person1->setAttribute('library', 'library2') -// ); -// $this->fail('Failed to throw duplicate exception'); -// } catch (Exception $e) { -// $this->assertInstanceOf(DuplicateException::class, $e); -// } -// -// // Create new document -// $library4 = static::getDatabase()->createDocument('library', new Document([ -// '$id' => 'library4', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Library 4', -// 'area' => 'Area 4', -// ])); -// -// // Relate existing document to new document -// static::getDatabase()->updateDocument( -// 'person', -// $person1->getId(), -// $person1->setAttribute('library', 'library4') -// ); -// -// // Relate existing document to new document as nested data -// static::getDatabase()->updateDocument( -// 'person', -// $person1->getId(), -// $person1->setAttribute('library', $library4) -// ); -// -// // Rename relationship key -// static::getDatabase()->updateRelationship( -// collection: 'person', -// id: 'library', -// newKey: 'newLibrary' -// ); -// -// // Get document with again -// $person = static::getDatabase()->getDocument('person', 'person1'); -// $library = $person->getAttribute('newLibrary'); -// $this->assertEquals('library4', $library['$id']); -// -// // Create person with no relationship -// static::getDatabase()->createDocument('person', new Document([ -// '$id' => 'person4', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Person 4', -// ])); -// -// // Can delete parent document with no relation with on delete set to restrict -// $deleted = static::getDatabase()->deleteDocument('person', 'person4'); -// $this->assertEquals(true, $deleted); -// -// $person4 = static::getDatabase()->getDocument('person', 'person4'); -// $this->assertEquals(true, $person4->isEmpty()); -// -// // Cannot delete document while still related to another with on delete set to restrict -// try { -// static::getDatabase()->deleteDocument('person', 'person1'); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); -// } -// -// // Can delete child document while still related to another with on delete set to restrict -// $person5 = static::getDatabase()->createDocument('person', new Document([ -// '$id' => 'person5', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Person 5', -// 'newLibrary' => [ -// '$id' => 'library5', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Library 5', -// 'area' => 'Area 5', -// ], -// ])); -// $deleted = static::getDatabase()->deleteDocument('library', 'library5'); -// $this->assertEquals(true, $deleted); -// $person5 = static::getDatabase()->getDocument('person', 'person5'); -// $this->assertEquals(null, $person5->getAttribute('newLibrary')); -// -// // Change on delete to set null -// static::getDatabase()->updateRelationship( -// collection: 'person', -// id: 'newLibrary', -// onDelete: Database::RELATION_MUTATE_SET_NULL -// ); -// -// // Delete parent, no effect on children for one-way -// static::getDatabase()->deleteDocument('person', 'person1'); -// -// // Delete child, set parent relating attribute to null for one-way -// static::getDatabase()->deleteDocument('library', 'library2'); -// -// // Check relation was set to null -// $person2 = static::getDatabase()->getDocument('person', 'person2'); -// $this->assertEquals(null, $person2->getAttribute('newLibrary', '')); -// -// // Relate to another document -// static::getDatabase()->updateDocument( -// 'person', -// $person2->getId(), -// $person2->setAttribute('newLibrary', 'library4') -// ); -// -// // Change on delete to cascade -// static::getDatabase()->updateRelationship( -// collection: 'person', -// id: 'newLibrary', -// onDelete: Database::RELATION_MUTATE_CASCADE -// ); -// -// // Delete parent, will delete child -// static::getDatabase()->deleteDocument('person', 'person2'); -// -// // Check parent and child were deleted -// $person = static::getDatabase()->getDocument('person', 'person2'); -// $this->assertEquals(true, $person->isEmpty()); -// -// $library = static::getDatabase()->getDocument('library', 'library4'); -// $this->assertEquals(true, $library->isEmpty()); -// -// // Delete relationship -// static::getDatabase()->deleteRelationship( -// 'person', -// 'newLibrary' -// ); -// -// // Check parent doesn't have relationship anymore -// $person = static::getDatabase()->getDocument('person', 'person1'); -// $library = $person->getAttribute('newLibrary', ''); -// $this->assertEquals(null, $library); -// } -// -// /** -// * @throws AuthorizationException -// * @throws LimitException -// * @throws DuplicateException -// * @throws StructureException -// * @throws Throwable -// */ -// public function testOneToOneTwoWayRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('country'); -// static::getDatabase()->createCollection('city'); -// -// static::getDatabase()->createAttribute('country', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('city', 'code', Database::VAR_STRING, 3, true); -// static::getDatabase()->createAttribute('city', 'name', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'country', -// relatedCollection: 'city', -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true -// ); -// -// $collection = static::getDatabase()->getCollection('country'); -// $attributes = $collection->getAttribute('attributes', []); -// foreach ($attributes as $attribute) { -// if ($attribute['key'] === 'city') { -// $this->assertEquals('relationship', $attribute['type']); -// $this->assertEquals('city', $attribute['$id']); -// $this->assertEquals('city', $attribute['key']); -// $this->assertEquals('city', $attribute['options']['relatedCollection']); -// $this->assertEquals(Database::RELATION_ONE_TO_ONE, $attribute['options']['relationType']); -// $this->assertEquals(true, $attribute['options']['twoWay']); -// $this->assertEquals('country', $attribute['options']['twoWayKey']); -// } -// } -// -// $collection = static::getDatabase()->getCollection('city'); -// $attributes = $collection->getAttribute('attributes', []); -// foreach ($attributes as $attribute) { -// if ($attribute['key'] === 'country') { -// $this->assertEquals('relationship', $attribute['type']); -// $this->assertEquals('country', $attribute['$id']); -// $this->assertEquals('country', $attribute['key']); -// $this->assertEquals('country', $attribute['options']['relatedCollection']); -// $this->assertEquals(Database::RELATION_ONE_TO_ONE, $attribute['options']['relationType']); -// $this->assertEquals(true, $attribute['options']['twoWay']); -// $this->assertEquals('city', $attribute['options']['twoWayKey']); -// } -// } -// -// // Create document with relationship with nested data -// $doc = new Document([ -// '$id' => 'country1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'England', -// 'city' => [ -// '$id' => 'city1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'London', -// 'code' => 'LON', -// ], -// ]); -// -// static::getDatabase()->createDocument('country', $doc); -// $country1 = static::getDatabase()->getDocument('country', 'country1'); -// $this->assertEquals('London', $country1->getAttribute('city')->getAttribute('name')); -// -// // Update a document with non existing related document. It should not get added to the list. -// static::getDatabase()->updateDocument('country', 'country1', (new Document($doc->getArrayCopy()))->setAttribute('city', 'no-city')); -// -// $country1Document = static::getDatabase()->getDocument('country', 'country1'); -// // Assert document does not contain non existing relation document. -// $this->assertEquals(null, $country1Document->getAttribute('city')); -// static::getDatabase()->updateDocument('country', 'country1', (new Document($doc->getArrayCopy()))->setAttribute('city', 'city1')); -// try { -// static::getDatabase()->deleteDocument('country', 'country1'); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertInstanceOf(RestrictedException::class, $e); -// } -// -// $this->assertTrue(static::getDatabase()->deleteDocument('city', 'city1')); -// -// $city1 = static::getDatabase()->getDocument('city', 'city1'); -// $this->assertTrue($city1->isEmpty()); -// -// $country1 = static::getDatabase()->getDocument('country', 'country1'); -// $this->assertTrue($country1->getAttribute('city')->isEmpty()); -// -// $this->assertTrue(static::getDatabase()->deleteDocument('country', 'country1')); -// -// static::getDatabase()->createDocument('country', $doc); -// $country1 = static::getDatabase()->getDocument('country', 'country1'); -// $this->assertEquals('London', $country1->getAttribute('city')->getAttribute('name')); -// -// // Create document with relationship with related ID -// static::getDatabase()->createDocument('city', new Document([ -// '$id' => 'city2', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Paris', -// 'code' => 'PAR', -// ])); -// static::getDatabase()->createDocument('country', new Document([ -// '$id' => 'country2', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'France', -// 'city' => 'city2', -// ])); -// -// // Create from child side -// static::getDatabase()->createDocument('city', new Document([ -// '$id' => 'city3', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Christchurch', -// 'code' => 'CHC', -// 'country' => [ -// '$id' => 'country3', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'New Zealand', -// ], -// ])); -// static::getDatabase()->createDocument('country', new Document([ -// '$id' => 'country4', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Australia', -// ])); -// static::getDatabase()->createDocument('city', new Document([ -// '$id' => 'city4', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Sydney', -// 'code' => 'SYD', -// 'country' => 'country4', -// ])); -// -// // Get document with relationship -// $city = static::getDatabase()->getDocument('city', 'city1'); -// $country = $city->getAttribute('country'); -// $this->assertEquals('country1', $country['$id']); -// $this->assertArrayNotHasKey('city', $country); -// -// $city = static::getDatabase()->getDocument('city', 'city2'); -// $country = $city->getAttribute('country'); -// $this->assertEquals('country2', $country['$id']); -// $this->assertArrayNotHasKey('city', $country); -// -// $city = static::getDatabase()->getDocument('city', 'city3'); -// $country = $city->getAttribute('country'); -// $this->assertEquals('country3', $country['$id']); -// $this->assertArrayNotHasKey('city', $country); -// -// $city = static::getDatabase()->getDocument('city', 'city4'); -// $country = $city->getAttribute('country'); -// $this->assertEquals('country4', $country['$id']); -// $this->assertArrayNotHasKey('city', $country); -// -// // Get inverse document with relationship -// $country = static::getDatabase()->getDocument('country', 'country1'); -// $city = $country->getAttribute('city'); -// $this->assertEquals('city1', $city['$id']); -// $this->assertArrayNotHasKey('country', $city); -// -// $country = static::getDatabase()->getDocument('country', 'country2'); -// $city = $country->getAttribute('city'); -// $this->assertEquals('city2', $city['$id']); -// $this->assertArrayNotHasKey('country', $city); -// -// $country = static::getDatabase()->getDocument('country', 'country3'); -// $city = $country->getAttribute('city'); -// $this->assertEquals('city3', $city['$id']); -// $this->assertArrayNotHasKey('country', $city); -// -// $country = static::getDatabase()->getDocument('country', 'country4'); -// $city = $country->getAttribute('city'); -// $this->assertEquals('city4', $city['$id']); -// $this->assertArrayNotHasKey('country', $city); -// -// $countries = static::getDatabase()->find('country'); -// -// $this->assertEquals(4, \count($countries)); -// -// // Select related document attributes -// $country = static::getDatabase()->findOne('country', [ -// Query::select(['*', 'city.name']) -// ]); -// -// if (!$country instanceof Document) { -// throw new Exception('Country not found'); -// } -// -// $this->assertEquals('London', $country->getAttribute('city')->getAttribute('name')); -// $this->assertArrayNotHasKey('code', $country->getAttribute('city')); -// -// $country = static::getDatabase()->getDocument('country', 'country1', [ -// Query::select(['*', 'city.name']) -// ]); -// -// $this->assertEquals('London', $country->getAttribute('city')->getAttribute('name')); -// $this->assertArrayNotHasKey('code', $country->getAttribute('city')); -// -// $country1 = static::getDatabase()->getDocument('country', 'country1'); -// -// // Update root document attribute without altering relationship -// $country1 = static::getDatabase()->updateDocument( -// 'country', -// $country1->getId(), -// $country1->setAttribute('name', 'Country 1 Updated') -// ); -// -// $this->assertEquals('Country 1 Updated', $country1->getAttribute('name')); -// $country1 = static::getDatabase()->getDocument('country', 'country1'); -// $this->assertEquals('Country 1 Updated', $country1->getAttribute('name')); -// -// $city2 = static::getDatabase()->getDocument('city', 'city2'); -// -// // Update inverse root document attribute without altering relationship -// $city2 = static::getDatabase()->updateDocument( -// 'city', -// $city2->getId(), -// $city2->setAttribute('name', 'City 2 Updated') -// ); -// -// $this->assertEquals('City 2 Updated', $city2->getAttribute('name')); -// $city2 = static::getDatabase()->getDocument('city', 'city2'); -// $this->assertEquals('City 2 Updated', $city2->getAttribute('name')); -// -// // Update nested document attribute -// $country1 = static::getDatabase()->updateDocument( -// 'country', -// $country1->getId(), -// $country1->setAttribute( -// 'city', -// $country1 -// ->getAttribute('city') -// ->setAttribute('name', 'City 1 Updated') -// ) -// ); -// -// $this->assertEquals('City 1 Updated', $country1->getAttribute('city')->getAttribute('name')); -// $country1 = static::getDatabase()->getDocument('country', 'country1'); -// $this->assertEquals('City 1 Updated', $country1->getAttribute('city')->getAttribute('name')); -// -// // Update inverse nested document attribute -// $city2 = static::getDatabase()->updateDocument( -// 'city', -// $city2->getId(), -// $city2->setAttribute( -// 'country', -// $city2 -// ->getAttribute('country') -// ->setAttribute('name', 'Country 2 Updated') -// ) -// ); -// -// $this->assertEquals('Country 2 Updated', $city2->getAttribute('country')->getAttribute('name')); -// $city2 = static::getDatabase()->getDocument('city', 'city2'); -// $this->assertEquals('Country 2 Updated', $city2->getAttribute('country')->getAttribute('name')); -// -// // Create new document with no relationship -// $country5 = static::getDatabase()->createDocument('country', new Document([ -// '$id' => 'country5', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Country 5', -// ])); -// -// // Update to relate to created document -// $country5 = static::getDatabase()->updateDocument( -// 'country', -// $country5->getId(), -// $country5->setAttribute('city', new Document([ -// '$id' => 'city5', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// ], -// 'name' => 'City 5', -// 'code' => 'C5', -// ])) -// ); -// -// $this->assertEquals('city5', $country5->getAttribute('city')['$id']); -// $country5 = static::getDatabase()->getDocument('country', 'country5'); -// $this->assertEquals('city5', $country5->getAttribute('city')['$id']); -// -// // Create new document with no relationship -// $city6 = static::getDatabase()->createDocument('city', new Document([ -// '$id' => 'city6', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'City6', -// 'code' => 'C6', -// ])); -// -// // Update to relate to created document -// $city6 = static::getDatabase()->updateDocument( -// 'city', -// $city6->getId(), -// $city6->setAttribute('country', new Document([ -// '$id' => 'country6', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// ], -// 'name' => 'Country 6', -// ])) -// ); -// -// $this->assertEquals('country6', $city6->getAttribute('country')['$id']); -// $city6 = static::getDatabase()->getDocument('city', 'city6'); -// $this->assertEquals('country6', $city6->getAttribute('country')['$id']); -// -// // One to one can't relate to multiple documents, unique index throws duplicate -// try { -// static::getDatabase()->updateDocument( -// 'country', -// $country1->getId(), -// $country1->setAttribute('city', 'city2') -// ); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertInstanceOf(DuplicateException::class, $e); -// } -// -// $city1 = static::getDatabase()->getDocument('city', 'city1'); -// -// // Set relationship to null -// $city1 = static::getDatabase()->updateDocument( -// 'city', -// $city1->getId(), -// $city1->setAttribute('country', null) -// ); -// -// $this->assertEquals(null, $city1->getAttribute('country')); -// $city1 = static::getDatabase()->getDocument('city', 'city1'); -// $this->assertEquals(null, $city1->getAttribute('country')); -// -// // Create a new city with no relation -// $city7 = static::getDatabase()->createDocument('city', new Document([ -// '$id' => 'city7', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Copenhagen', -// 'code' => 'CPH', -// ])); -// -// // Update document with relation to new document -// static::getDatabase()->updateDocument( -// 'country', -// $country1->getId(), -// $country1->setAttribute('city', 'city7') -// ); -// -// // Relate existing document to new document as nested data -// static::getDatabase()->updateDocument( -// 'country', -// $country1->getId(), -// $country1->setAttribute('city', $city7) -// ); -// -// // Create a new country with no relation -// static::getDatabase()->createDocument('country', new Document([ -// '$id' => 'country7', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Denmark' -// ])); -// -// // Update inverse document with new related document -// static::getDatabase()->updateDocument( -// 'city', -// $city1->getId(), -// $city1->setAttribute('country', 'country7') -// ); -// -// // Rename relationship keys on both sides -// static::getDatabase()->updateRelationship( -// 'country', -// 'city', -// 'newCity', -// 'newCountry' -// ); -// -// // Get document with new relationship key -// $city = static::getDatabase()->getDocument('city', 'city1'); -// $country = $city->getAttribute('newCountry'); -// $this->assertEquals('country7', $country['$id']); -// -// // Get inverse document with new relationship key -// $country = static::getDatabase()->getDocument('country', 'country7'); -// $city = $country->getAttribute('newCity'); -// $this->assertEquals('city1', $city['$id']); -// -// // Create a new country with no relation -// static::getDatabase()->createDocument('country', new Document([ -// '$id' => 'country8', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Denmark' -// ])); -// -// // Can delete parent document with no relation with on delete set to restrict -// $deleted = static::getDatabase()->deleteDocument('country', 'country8'); -// $this->assertEquals(1, $deleted); -// -// $country8 = static::getDatabase()->getDocument('country', 'country8'); -// $this->assertEquals(true, $country8->isEmpty()); -// -// -// // Cannot delete document while still related to another with on delete set to restrict -// try { -// static::getDatabase()->deleteDocument('country', 'country1'); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); -// } -// -// // Change on delete to set null -// static::getDatabase()->updateRelationship( -// collection: 'country', -// id: 'newCity', -// onDelete: Database::RELATION_MUTATE_SET_NULL -// ); -// -// // Delete parent, will set child relationship to null for two-way -// static::getDatabase()->deleteDocument('country', 'country1'); -// -// // Check relation was set to null -// $city7 = static::getDatabase()->getDocument('city', 'city7'); -// $this->assertEquals(null, $city7->getAttribute('country', '')); -// -// // Delete child, set parent relationship to null for two-way -// static::getDatabase()->deleteDocument('city', 'city2'); -// -// // Check relation was set to null -// $country2 = static::getDatabase()->getDocument('country', 'country2'); -// $this->assertEquals(null, $country2->getAttribute('city', '')); -// -// // Relate again -// static::getDatabase()->updateDocument( -// 'city', -// $city7->getId(), -// $city7->setAttribute('newCountry', 'country2') -// ); -// -// // Change on delete to cascade -// static::getDatabase()->updateRelationship( -// collection: 'country', -// id: 'newCity', -// onDelete: Database::RELATION_MUTATE_CASCADE -// ); -// -// // Delete parent, will delete child -// static::getDatabase()->deleteDocument('country', 'country7'); -// -// // Check parent and child were deleted -// $library = static::getDatabase()->getDocument('country', 'country7'); -// $this->assertEquals(true, $library->isEmpty()); -// -// $library = static::getDatabase()->getDocument('city', 'city1'); -// $this->assertEquals(true, $library->isEmpty()); -// -// // Delete child, will delete parent for two-way -// static::getDatabase()->deleteDocument('city', 'city7'); -// -// // Check parent and child were deleted -// $library = static::getDatabase()->getDocument('city', 'city7'); -// $this->assertEquals(true, $library->isEmpty()); -// -// $library = static::getDatabase()->getDocument('country', 'country2'); -// $this->assertEquals(true, $library->isEmpty()); -// -// // Create new document to check after deleting relationship -// static::getDatabase()->createDocument('city', new Document([ -// '$id' => 'city7', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Munich', -// 'code' => 'MUC', -// 'newCountry' => [ -// '$id' => 'country7', -// 'name' => 'Germany' -// ] -// ])); -// -// // Delete relationship -// static::getDatabase()->deleteRelationship( -// 'country', -// 'newCity' -// ); -// -// // Try to get document again -// $country = static::getDatabase()->getDocument('country', 'country4'); -// $city = $country->getAttribute('newCity'); -// $this->assertEquals(null, $city); -// -// // Try to get inverse document again -// $city = static::getDatabase()->getDocument('city', 'city7'); -// $country = $city->getAttribute('newCountry'); -// $this->assertEquals(null, $country); -// } -// -// public function testIdenticalTwoWayKeyRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('parent'); -// static::getDatabase()->createCollection('child'); -// -// static::getDatabase()->createRelationship( -// collection: 'parent', -// relatedCollection: 'child', -// type: Database::RELATION_ONE_TO_ONE, -// id: 'child1' -// ); -// -// try { -// static::getDatabase()->createRelationship( -// collection: 'parent', -// relatedCollection: 'child', -// type: Database::RELATION_ONE_TO_MANY, -// id: 'children', -// ); -// $this->fail('Failed to throw Exception'); -// } catch (Exception $e) { -// $this->assertEquals('Related attribute already exists', $e->getMessage()); -// } -// -// static::getDatabase()->createRelationship( -// collection: 'parent', -// relatedCollection: 'child', -// type: Database::RELATION_ONE_TO_MANY, -// id: 'children', -// twoWayKey: 'parent_id' -// ); -// -// $collection = static::getDatabase()->getCollection('parent'); -// $attributes = $collection->getAttribute('attributes', []); -// foreach ($attributes as $attribute) { -// if ($attribute['key'] === 'child1') { -// $this->assertEquals('parent', $attribute['options']['twoWayKey']); -// } -// -// if ($attribute['key'] === 'children') { -// $this->assertEquals('parent_id', $attribute['options']['twoWayKey']); -// } -// } -// -// static::getDatabase()->createDocument('parent', new Document([ -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'child1' => [ -// '$id' => 'foo', -// '$permissions' => [Permission::read(Role::any())], -// ], -// 'children' => [ -// [ -// '$id' => 'bar', -// '$permissions' => [Permission::read(Role::any())], -// ], -// ], -// ])); -// -// $documents = static::getDatabase()->find('parent', []); -// $document = array_pop($documents); -// $this->assertArrayHasKey('child1', $document); -// $this->assertEquals('foo', $document->getAttribute('child1')->getId()); -// $this->assertArrayHasKey('children', $document); -// $this->assertEquals('bar', $document->getAttribute('children')[0]->getId()); -// -// try { -// static::getDatabase()->updateRelationship( -// collection: 'parent', -// id: 'children', -// newKey: 'child1' -// ); -// $this->fail('Failed to throw Exception'); -// } catch (Exception $e) { -// $this->assertEquals('Attribute already exists', $e->getMessage()); -// } -// -// try { -// static::getDatabase()->updateRelationship( -// collection: 'parent', -// id: 'children', -// newTwoWayKey: 'parent' -// ); -// $this->fail('Failed to throw Exception'); -// } catch (Exception $e) { -// $this->assertEquals('Related attribute already exists', $e->getMessage()); -// } -// } -// -// public function testOneToManyOneWayRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('artist'); -// static::getDatabase()->createCollection('album'); -// -// static::getDatabase()->createAttribute('artist', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('album', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('album', 'price', Database::VAR_FLOAT, 0, true); -// -// static::getDatabase()->createRelationship( -// collection: 'artist', -// relatedCollection: 'album', -// type: Database::RELATION_ONE_TO_MANY, -// id: 'albums' -// ); -// -// // Check metadata for collection -// $collection = static::getDatabase()->getCollection('artist'); -// $attributes = $collection->getAttribute('attributes', []); -// -// foreach ($attributes as $attribute) { -// if ($attribute['key'] === 'albums') { -// $this->assertEquals('relationship', $attribute['type']); -// $this->assertEquals('albums', $attribute['$id']); -// $this->assertEquals('albums', $attribute['key']); -// $this->assertEquals('album', $attribute['options']['relatedCollection']); -// $this->assertEquals(Database::RELATION_ONE_TO_MANY, $attribute['options']['relationType']); -// $this->assertEquals(false, $attribute['options']['twoWay']); -// $this->assertEquals('artist', $attribute['options']['twoWayKey']); -// } -// } -// -// // Create document with relationship with nested data -// $artist1 = static::getDatabase()->createDocument('artist', new Document([ -// '$id' => 'artist1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Artist 1', -// 'albums' => [ -// [ -// '$id' => 'album1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()) -// ], -// 'name' => 'Album 1', -// 'price' => 9.99, -// ], -// ], -// ])); -// -// // Update a document with non existing related document. It should not get added to the list. -// static::getDatabase()->updateDocument('artist', 'artist1', $artist1->setAttribute('albums', ['album1', 'no-album'])); -// -// $artist1Document = static::getDatabase()->getDocument('artist', 'artist1'); -// // Assert document does not contain non existing relation document. -// $this->assertEquals(1, \count($artist1Document->getAttribute('albums'))); -// -// // Create document with relationship with related ID -// static::getDatabase()->createDocument('album', new Document([ -// '$id' => 'album2', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Album 2', -// 'price' => 19.99, -// ])); -// static::getDatabase()->createDocument('artist', new Document([ -// '$id' => 'artist2', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Artist 2', -// 'albums' => [ -// 'album2', -// [ -// '$id' => 'album33', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Album 3', -// 'price' => 33.33, -// ] -// ] -// ])); -// -// $documents = static::getDatabase()->find('artist', [ -// Query::select(['name']), -// Query::limit(1) -// ]); -// $this->assertArrayNotHasKey('albums', $documents[0]); -// -// // Get document with relationship -// $artist = static::getDatabase()->getDocument('artist', 'artist1'); -// $albums = $artist->getAttribute('albums', []); -// $this->assertEquals('album1', $albums[0]['$id']); -// $this->assertArrayNotHasKey('artist', $albums[0]); -// -// $artist = static::getDatabase()->getDocument('artist', 'artist2'); -// $albums = $artist->getAttribute('albums', []); -// $this->assertEquals('album2', $albums[0]['$id']); -// $this->assertArrayNotHasKey('artist', $albums[0]); -// $this->assertEquals('album33', $albums[1]['$id']); -// $this->assertCount(2, $albums); -// -// // Get related document -// $album = static::getDatabase()->getDocument('album', 'album1'); -// $this->assertArrayNotHasKey('artist', $album); -// -// $album = static::getDatabase()->getDocument('album', 'album2'); -// $this->assertArrayNotHasKey('artist', $album); -// -// $artists = static::getDatabase()->find('artist'); -// -// $this->assertEquals(2, \count($artists)); -// -// // Select related document attributes -// $artist = static::getDatabase()->findOne('artist', [ -// Query::select(['*', 'albums.name']) -// ]); -// -// if (!$artist instanceof Document) { -// $this->fail('Artist not found'); -// } -// -// $this->assertEquals('Album 1', $artist->getAttribute('albums')[0]->getAttribute('name')); -// $this->assertArrayNotHasKey('price', $artist->getAttribute('albums')[0]); -// -// $artist = static::getDatabase()->getDocument('artist', 'artist1', [ -// Query::select(['*', 'albums.name']) -// ]); -// -// $this->assertEquals('Album 1', $artist->getAttribute('albums')[0]->getAttribute('name')); -// $this->assertArrayNotHasKey('price', $artist->getAttribute('albums')[0]); -// -// // Update root document attribute without altering relationship -// $artist1 = static::getDatabase()->updateDocument( -// 'artist', -// $artist1->getId(), -// $artist1->setAttribute('name', 'Artist 1 Updated') -// ); -// -// $this->assertEquals('Artist 1 Updated', $artist1->getAttribute('name')); -// $artist1 = static::getDatabase()->getDocument('artist', 'artist1'); -// $this->assertEquals('Artist 1 Updated', $artist1->getAttribute('name')); -// -// // Update nested document attribute -// $albums = $artist1->getAttribute('albums', []); -// $albums[0]->setAttribute('name', 'Album 1 Updated'); -// -// $artist1 = static::getDatabase()->updateDocument( -// 'artist', -// $artist1->getId(), -// $artist1->setAttribute('albums', $albums) -// ); -// -// $this->assertEquals('Album 1 Updated', $artist1->getAttribute('albums')[0]->getAttribute('name')); -// $artist1 = static::getDatabase()->getDocument('artist', 'artist1'); -// $this->assertEquals('Album 1 Updated', $artist1->getAttribute('albums')[0]->getAttribute('name')); -// -// $albumId = $artist1->getAttribute('albums')[0]->getAttribute('$id'); -// $albumDocument = static::getDatabase()->getDocument('album', $albumId); -// $albumDocument->setAttribute('name', 'Album 1 Updated!!!'); -// static::getDatabase()->updateDocument('album', $albumDocument->getId(), $albumDocument); -// $albumDocument = static::getDatabase()->getDocument('album', $albumDocument->getId()); -// $artist1 = static::getDatabase()->getDocument('artist', $artist1->getId()); -// -// $this->assertEquals('Album 1 Updated!!!', $albumDocument['name']); -// $this->assertEquals($albumDocument->getId(), $artist1->getAttribute('albums')[0]->getId()); -// //Todo: This is failing -// $this->assertEquals($albumDocument->getAttribute('name'), $artist1->getAttribute('albums')[0]->getAttribute('name')); -// -// // Create new document with no relationship -// $artist3 = static::getDatabase()->createDocument('artist', new Document([ -// '$id' => 'artist3', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Artist 3', -// ])); -// -// // Update to relate to created document -// $artist3 = static::getDatabase()->updateDocument( -// 'artist', -// $artist3->getId(), -// $artist3->setAttribute('albums', [new Document([ -// '$id' => 'album3', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Album 3', -// 'price' => 29.99, -// ])]) -// ); -// -// $this->assertEquals('Album 3', $artist3->getAttribute('albums')[0]->getAttribute('name')); -// $artist3 = static::getDatabase()->getDocument('artist', 'artist3'); -// $this->assertEquals('Album 3', $artist3->getAttribute('albums')[0]->getAttribute('name')); -// -// // Update document with new related documents, will remove existing relations -// static::getDatabase()->updateDocument( -// 'artist', -// $artist1->getId(), -// $artist1->setAttribute('albums', ['album2']) -// ); -// -// // Update document with new related documents, will remove existing relations -// static::getDatabase()->updateDocument( -// 'artist', -// $artist1->getId(), -// $artist1->setAttribute('albums', ['album1', 'album2']) -// ); -// -// // Rename relationship key -// static::getDatabase()->updateRelationship( -// 'artist', -// 'albums', -// 'newAlbums' -// ); -// -// // Get document with new relationship key -// $artist = static::getDatabase()->getDocument('artist', 'artist1'); -// $albums = $artist->getAttribute('newAlbums'); -// $this->assertEquals('album1', $albums[0]['$id']); -// -// // Create new document with no relationship -// static::getDatabase()->createDocument('artist', new Document([ -// '$id' => 'artist4', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Artist 4', -// ])); -// -// // Can delete document with no relationship when on delete is set to restrict -// $deleted = static::getDatabase()->deleteDocument('artist', 'artist4'); -// $this->assertEquals(true, $deleted); -// -// $artist4 = static::getDatabase()->getDocument('artist', 'artist4'); -// $this->assertEquals(true, $artist4->isEmpty()); -// -// // Try to delete document while still related to another with on delete: restrict -// try { -// static::getDatabase()->deleteDocument('artist', 'artist1'); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); -// } -// -// // Change on delete to set null -// static::getDatabase()->updateRelationship( -// collection: 'artist', -// id: 'newAlbums', -// onDelete: Database::RELATION_MUTATE_SET_NULL -// ); -// -// // Delete parent, set child relationship to null -// static::getDatabase()->deleteDocument('artist', 'artist1'); -// -// // Check relation was set to null -// $album2 = static::getDatabase()->getDocument('album', 'album2'); -// $this->assertEquals(null, $album2->getAttribute('artist', '')); -// -// // Relate again -// static::getDatabase()->updateDocument( -// 'album', -// $album2->getId(), -// $album2->setAttribute('artist', 'artist2') -// ); -// -// // Change on delete to cascade -// static::getDatabase()->updateRelationship( -// collection: 'artist', -// id: 'newAlbums', -// onDelete: Database::RELATION_MUTATE_CASCADE -// ); -// -// // Delete parent, will delete child -// static::getDatabase()->deleteDocument('artist', 'artist2'); -// -// // Check parent and child were deleted -// $library = static::getDatabase()->getDocument('artist', 'artist2'); -// $this->assertEquals(true, $library->isEmpty()); -// -// $library = static::getDatabase()->getDocument('album', 'album2'); -// $this->assertEquals(true, $library->isEmpty()); -// -// $albums = []; -// for ($i = 1 ; $i <= 50 ; $i++) { -// $albums[] = [ -// '$id' => 'album_' . $i, -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'album ' . $i . ' ' . 'Artist 100', -// 'price' => 100, -// ]; -// } -// -// $artist = static::getDatabase()->createDocument('artist', new Document([ -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Artist 100', -// 'newAlbums' => $albums -// ])); -// -// $artist = static::getDatabase()->getDocument('artist', $artist->getId()); -// $this->assertCount(50, $artist->getAttribute('newAlbums')); -// -// $albums = static::getDatabase()->find('album', [ -// Query::equal('artist', [$artist->getId()]), -// Query::limit(999) -// ]); -// -// $this->assertCount(50, $albums); -// -// $count = static::getDatabase()->count('album', [ -// Query::equal('artist', [$artist->getId()]), -// ]); -// -// $this->assertEquals(50, $count); -// -// static::getDatabase()->deleteDocument('album', 'album_1'); -// $artist = static::getDatabase()->getDocument('artist', $artist->getId()); -// $this->assertCount(49, $artist->getAttribute('newAlbums')); -// -// static::getDatabase()->deleteDocument('artist', $artist->getId()); -// -// $albums = static::getDatabase()->find('album', [ -// Query::equal('artist', [$artist->getId()]), -// Query::limit(999) -// ]); -// -// $this->assertCount(0, $albums); -// -// // Delete relationship -// static::getDatabase()->deleteRelationship( -// 'artist', -// 'newAlbums' -// ); -// -// // Try to get document again -// $artist = static::getDatabase()->getDocument('artist', 'artist1'); -// $albums = $artist->getAttribute('newAlbums', ''); -// $this->assertEquals(null, $albums); -// } -// -// public function testOneToManyTwoWayRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('customer'); -// static::getDatabase()->createCollection('account'); -// -// static::getDatabase()->createAttribute('customer', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('account', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('account', 'number', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'customer', -// relatedCollection: 'account', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// id: 'accounts' -// ); -// -// // Check metadata for collection -// $collection = static::getDatabase()->getCollection('customer'); -// $attributes = $collection->getAttribute('attributes', []); -// foreach ($attributes as $attribute) { -// if ($attribute['key'] === 'accounts') { -// $this->assertEquals('relationship', $attribute['type']); -// $this->assertEquals('accounts', $attribute['$id']); -// $this->assertEquals('accounts', $attribute['key']); -// $this->assertEquals('account', $attribute['options']['relatedCollection']); -// $this->assertEquals(Database::RELATION_ONE_TO_MANY, $attribute['options']['relationType']); -// $this->assertEquals(true, $attribute['options']['twoWay']); -// $this->assertEquals('customer', $attribute['options']['twoWayKey']); -// } -// } -// -// // Check metadata for related collection -// $collection = static::getDatabase()->getCollection('account'); -// $attributes = $collection->getAttribute('attributes', []); -// foreach ($attributes as $attribute) { -// if ($attribute['key'] === 'customer') { -// $this->assertEquals('relationship', $attribute['type']); -// $this->assertEquals('customer', $attribute['$id']); -// $this->assertEquals('customer', $attribute['key']); -// $this->assertEquals('customer', $attribute['options']['relatedCollection']); -// $this->assertEquals(Database::RELATION_ONE_TO_MANY, $attribute['options']['relationType']); -// $this->assertEquals(true, $attribute['options']['twoWay']); -// $this->assertEquals('accounts', $attribute['options']['twoWayKey']); -// } -// } -// -// // Create document with relationship with nested data -// $customer1 = static::getDatabase()->createDocument('customer', new Document([ -// '$id' => 'customer1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Customer 1', -// 'accounts' => [ -// [ -// '$id' => 'account1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Account 1', -// 'number' => '123456789', -// ], -// ], -// ])); -// -// // Update a document with non existing related document. It should not get added to the list. -// static::getDatabase()->updateDocument('customer', 'customer1', $customer1->setAttribute('accounts', ['account1','no-account'])); -// -// $customer1Document = static::getDatabase()->getDocument('customer', 'customer1'); -// // Assert document does not contain non existing relation document. -// $this->assertEquals(1, \count($customer1Document->getAttribute('accounts'))); -// -// // Create document with relationship with related ID -// $account2 = static::getDatabase()->createDocument('account', new Document([ -// '$id' => 'account2', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Account 2', -// 'number' => '987654321', -// ])); -// static::getDatabase()->createDocument('customer', new Document([ -// '$id' => 'customer2', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Customer 2', -// 'accounts' => [ -// 'account2' -// ] -// ])); -// -// // Create from child side -// static::getDatabase()->createDocument('account', new Document([ -// '$id' => 'account3', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Account 3', -// 'number' => '123456789', -// 'customer' => [ -// '$id' => 'customer3', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Customer 3' -// ] -// ])); -// static::getDatabase()->createDocument('customer', new Document([ -// '$id' => 'customer4', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Customer 4', -// ])); -// static::getDatabase()->createDocument('account', new Document([ -// '$id' => 'account4', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Account 4', -// 'number' => '123456789', -// 'customer' => 'customer4' -// ])); -// -// // Get documents with relationship -// $customer = static::getDatabase()->getDocument('customer', 'customer1'); -// $accounts = $customer->getAttribute('accounts', []); -// $this->assertEquals('account1', $accounts[0]['$id']); -// $this->assertArrayNotHasKey('customer', $accounts[0]); -// -// $customer = static::getDatabase()->getDocument('customer', 'customer2'); -// $accounts = $customer->getAttribute('accounts', []); -// $this->assertEquals('account2', $accounts[0]['$id']); -// $this->assertArrayNotHasKey('customer', $accounts[0]); -// -// $customer = static::getDatabase()->getDocument('customer', 'customer3'); -// $accounts = $customer->getAttribute('accounts', []); -// $this->assertEquals('account3', $accounts[0]['$id']); -// $this->assertArrayNotHasKey('customer', $accounts[0]); -// -// $customer = static::getDatabase()->getDocument('customer', 'customer4'); -// $accounts = $customer->getAttribute('accounts', []); -// $this->assertEquals('account4', $accounts[0]['$id']); -// $this->assertArrayNotHasKey('customer', $accounts[0]); -// -// // Get related documents -// $account = static::getDatabase()->getDocument('account', 'account1'); -// $customer = $account->getAttribute('customer'); -// $this->assertEquals('customer1', $customer['$id']); -// $this->assertArrayNotHasKey('accounts', $customer); -// -// $account = static::getDatabase()->getDocument('account', 'account2'); -// $customer = $account->getAttribute('customer'); -// $this->assertEquals('customer2', $customer['$id']); -// $this->assertArrayNotHasKey('accounts', $customer); -// -// $account = static::getDatabase()->getDocument('account', 'account3'); -// $customer = $account->getAttribute('customer'); -// $this->assertEquals('customer3', $customer['$id']); -// $this->assertArrayNotHasKey('accounts', $customer); -// -// $account = static::getDatabase()->getDocument('account', 'account4'); -// $customer = $account->getAttribute('customer'); -// $this->assertEquals('customer4', $customer['$id']); -// $this->assertArrayNotHasKey('accounts', $customer); -// -// $customers = static::getDatabase()->find('customer'); -// -// $this->assertEquals(4, \count($customers)); -// -// // Select related document attributes -// $customer = static::getDatabase()->findOne('customer', [ -// Query::select(['*', 'accounts.name']) -// ]); -// -// if (!$customer instanceof Document) { -// throw new Exception('Customer not found'); -// } -// -// $this->assertEquals('Account 1', $customer->getAttribute('accounts')[0]->getAttribute('name')); -// $this->assertArrayNotHasKey('number', $customer->getAttribute('accounts')[0]); -// -// $customer = static::getDatabase()->getDocument('customer', 'customer1', [ -// Query::select(['*', 'accounts.name']) -// ]); -// -// $this->assertEquals('Account 1', $customer->getAttribute('accounts')[0]->getAttribute('name')); -// $this->assertArrayNotHasKey('number', $customer->getAttribute('accounts')[0]); -// -// // Update root document attribute without altering relationship -// $customer1 = static::getDatabase()->updateDocument( -// 'customer', -// $customer1->getId(), -// $customer1->setAttribute('name', 'Customer 1 Updated') -// ); -// -// $this->assertEquals('Customer 1 Updated', $customer1->getAttribute('name')); -// $customer1 = static::getDatabase()->getDocument('customer', 'customer1'); -// $this->assertEquals('Customer 1 Updated', $customer1->getAttribute('name')); -// -// $account2 = static::getDatabase()->getDocument('account', 'account2'); -// -// // Update inverse root document attribute without altering relationship -// $account2 = static::getDatabase()->updateDocument( -// 'account', -// $account2->getId(), -// $account2->setAttribute('name', 'Account 2 Updated') -// ); -// -// $this->assertEquals('Account 2 Updated', $account2->getAttribute('name')); -// $account2 = static::getDatabase()->getDocument('account', 'account2'); -// $this->assertEquals('Account 2 Updated', $account2->getAttribute('name')); -// -// // Update nested document attribute -// $accounts = $customer1->getAttribute('accounts', []); -// $accounts[0]->setAttribute('name', 'Account 1 Updated'); -// -// $customer1 = static::getDatabase()->updateDocument( -// 'customer', -// $customer1->getId(), -// $customer1->setAttribute('accounts', $accounts) -// ); -// -// $this->assertEquals('Account 1 Updated', $customer1->getAttribute('accounts')[0]->getAttribute('name')); -// $customer1 = static::getDatabase()->getDocument('customer', 'customer1'); -// $this->assertEquals('Account 1 Updated', $customer1->getAttribute('accounts')[0]->getAttribute('name')); -// -// // Update inverse nested document attribute -// $account2 = static::getDatabase()->updateDocument( -// 'account', -// $account2->getId(), -// $account2->setAttribute( -// 'customer', -// $account2 -// ->getAttribute('customer') -// ->setAttribute('name', 'Customer 2 Updated') -// ) -// ); -// -// $this->assertEquals('Customer 2 Updated', $account2->getAttribute('customer')->getAttribute('name')); -// $account2 = static::getDatabase()->getDocument('account', 'account2'); -// $this->assertEquals('Customer 2 Updated', $account2->getAttribute('customer')->getAttribute('name')); -// -// // Create new document with no relationship -// $customer5 = static::getDatabase()->createDocument('customer', new Document([ -// '$id' => 'customer5', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Customer 5', -// ])); -// -// // Update to relate to created document -// $customer5 = static::getDatabase()->updateDocument( -// 'customer', -// $customer5->getId(), -// $customer5->setAttribute('accounts', [new Document([ -// '$id' => 'account5', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Account 5', -// 'number' => '123456789', -// ])]) -// ); -// -// $this->assertEquals('Account 5', $customer5->getAttribute('accounts')[0]->getAttribute('name')); -// $customer5 = static::getDatabase()->getDocument('customer', 'customer5'); -// $this->assertEquals('Account 5', $customer5->getAttribute('accounts')[0]->getAttribute('name')); -// -// // Create new child document with no relationship -// $account6 = static::getDatabase()->createDocument('account', new Document([ -// '$id' => 'account6', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Account 6', -// 'number' => '123456789', -// ])); -// -// // Update inverse to relate to created document -// $account6 = static::getDatabase()->updateDocument( -// 'account', -// $account6->getId(), -// $account6->setAttribute('customer', new Document([ -// '$id' => 'customer6', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Customer 6', -// ])) -// ); -// -// $this->assertEquals('Customer 6', $account6->getAttribute('customer')->getAttribute('name')); -// $account6 = static::getDatabase()->getDocument('account', 'account6'); -// $this->assertEquals('Customer 6', $account6->getAttribute('customer')->getAttribute('name')); -// -// // Update document with new related document, will remove existing relations -// static::getDatabase()->updateDocument( -// 'customer', -// $customer1->getId(), -// $customer1->setAttribute('accounts', ['account2']) -// ); -// -// // Update document with new related document -// static::getDatabase()->updateDocument( -// 'customer', -// $customer1->getId(), -// $customer1->setAttribute('accounts', ['account1', 'account2']) -// ); -// -// // Update inverse document -// static::getDatabase()->updateDocument( -// 'account', -// $account2->getId(), -// $account2->setAttribute('customer', 'customer2') -// ); -// -// // Rename relationship keys on both sides -// static::getDatabase()->updateRelationship( -// 'customer', -// 'accounts', -// 'newAccounts', -// 'newCustomer' -// ); -// -// // Get document with new relationship key -// $customer = static::getDatabase()->getDocument('customer', 'customer1'); -// $accounts = $customer->getAttribute('newAccounts'); -// $this->assertEquals('account1', $accounts[0]['$id']); -// -// // Get inverse document with new relationship key -// $account = static::getDatabase()->getDocument('account', 'account1'); -// $customer = $account->getAttribute('newCustomer'); -// $this->assertEquals('customer1', $customer['$id']); -// -// // Create new document with no relationship -// static::getDatabase()->createDocument('customer', new Document([ -// '$id' => 'customer7', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Customer 7', -// ])); -// -// // Can delete document with no relationship when on delete is set to restrict -// $deleted = static::getDatabase()->deleteDocument('customer', 'customer7'); -// $this->assertEquals(true, $deleted); -// -// $customer7 = static::getDatabase()->getDocument('customer', 'customer7'); -// $this->assertEquals(true, $customer7->isEmpty()); -// -// // Try to delete document while still related to another with on delete: restrict -// try { -// static::getDatabase()->deleteDocument('customer', 'customer1'); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); -// } -// -// // Change on delete to set null -// static::getDatabase()->updateRelationship( -// collection: 'customer', -// id: 'newAccounts', -// onDelete: Database::RELATION_MUTATE_SET_NULL -// ); -// -// // Delete parent, set child relationship to null -// static::getDatabase()->deleteDocument('customer', 'customer1'); -// -// // Check relation was set to null -// $account1 = static::getDatabase()->getDocument('account', 'account1'); -// $this->assertEquals(null, $account2->getAttribute('newCustomer', '')); -// -// // Relate again -// static::getDatabase()->updateDocument( -// 'account', -// $account1->getId(), -// $account1->setAttribute('newCustomer', 'customer2') -// ); -// -// // Change on delete to cascade -// static::getDatabase()->updateRelationship( -// collection: 'customer', -// id: 'newAccounts', -// onDelete: Database::RELATION_MUTATE_CASCADE -// ); -// -// // Delete parent, will delete child -// static::getDatabase()->deleteDocument('customer', 'customer2'); -// -// // Check parent and child were deleted -// $library = static::getDatabase()->getDocument('customer', 'customer2'); -// $this->assertEquals(true, $library->isEmpty()); -// -// $library = static::getDatabase()->getDocument('account', 'account2'); -// $this->assertEquals(true, $library->isEmpty()); -// -// // Delete relationship -// static::getDatabase()->deleteRelationship( -// 'customer', -// 'newAccounts' -// ); -// -// // Try to get document again -// $customer = static::getDatabase()->getDocument('customer', 'customer1'); -// $accounts = $customer->getAttribute('newAccounts'); -// $this->assertEquals(null, $accounts); -// -// // Try to get inverse document again -// $accounts = static::getDatabase()->getDocument('account', 'account1'); -// $customer = $accounts->getAttribute('newCustomer'); -// $this->assertEquals(null, $customer); -// } -// -// public function testManyToOneOneWayRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('review'); -// static::getDatabase()->createCollection('movie'); -// -// static::getDatabase()->createAttribute('review', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('movie', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('movie', 'length', Database::VAR_INTEGER, 0, true, formatOptions: ['min' => 0, 'max' => 999]); -// static::getDatabase()->createAttribute('movie', 'date', Database::VAR_DATETIME, 0, false, filters: ['datetime']); -// static::getDatabase()->createAttribute('review', 'date', Database::VAR_DATETIME, 0, false, filters: ['datetime']); -// static::getDatabase()->createRelationship( -// collection: 'review', -// relatedCollection: 'movie', -// type: Database::RELATION_MANY_TO_ONE, -// twoWayKey: 'reviews' -// ); -// -// // Check metadata for collection -// $collection = static::getDatabase()->getCollection('review'); -// $attributes = $collection->getAttribute('attributes', []); -// foreach ($attributes as $attribute) { -// if ($attribute['key'] === 'movie') { -// $this->assertEquals('relationship', $attribute['type']); -// $this->assertEquals('movie', $attribute['$id']); -// $this->assertEquals('movie', $attribute['key']); -// $this->assertEquals('movie', $attribute['options']['relatedCollection']); -// $this->assertEquals(Database::RELATION_MANY_TO_ONE, $attribute['options']['relationType']); -// $this->assertEquals(false, $attribute['options']['twoWay']); -// $this->assertEquals('reviews', $attribute['options']['twoWayKey']); -// } -// } -// -// // Check metadata for related collection -// $collection = static::getDatabase()->getCollection('movie'); -// $attributes = $collection->getAttribute('attributes', []); -// foreach ($attributes as $attribute) { -// if ($attribute['key'] === 'reviews') { -// $this->assertEquals('relationship', $attribute['type']); -// $this->assertEquals('reviews', $attribute['$id']); -// $this->assertEquals('reviews', $attribute['key']); -// $this->assertEquals('review', $attribute['options']['relatedCollection']); -// $this->assertEquals(Database::RELATION_MANY_TO_ONE, $attribute['options']['relationType']); -// $this->assertEquals(false, $attribute['options']['twoWay']); -// $this->assertEquals('movie', $attribute['options']['twoWayKey']); -// } -// } -// -// // Create document with relationship with nested data -// $review1 = static::getDatabase()->createDocument('review', new Document([ -// '$id' => 'review1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Review 1', -// 'date' => '2023-04-03 10:35:27.390', -// 'movie' => [ -// '$id' => 'movie1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Movie 1', -// 'date' => '2023-04-03 10:35:27.390', -// 'length' => 120, -// ], -// ])); -// -// // Update a document with non existing related document. It should not get added to the list. -// static::getDatabase()->updateDocument('review', 'review1', $review1->setAttribute('movie', 'no-movie')); -// -// $review1Document = static::getDatabase()->getDocument('review', 'review1'); -// // Assert document does not contain non existing relation document. -// $this->assertEquals(null, $review1Document->getAttribute('movie')); -// -// static::getDatabase()->updateDocument('review', 'review1', $review1->setAttribute('movie', 'movie1')); -// -// // Create document with relationship to existing document by ID -// $review10 = static::getDatabase()->createDocument('review', new Document([ -// '$id' => 'review10', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Review 10', -// 'movie' => 'movie1', -// 'date' => '2023-04-03 10:35:27.390', -// ])); -// -// // Create document with relationship with related ID -// static::getDatabase()->createDocument('movie', new Document([ -// '$id' => 'movie2', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Movie 2', -// 'length' => 90, -// 'date' => '2023-04-03 10:35:27.390', -// ])); -// static::getDatabase()->createDocument('review', new Document([ -// '$id' => 'review2', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Review 2', -// 'movie' => 'movie2', -// 'date' => '2023-04-03 10:35:27.390', -// ])); -// -// // Get document with relationship -// $review = static::getDatabase()->getDocument('review', 'review1'); -// $movie = $review->getAttribute('movie', []); -// $this->assertEquals('movie1', $movie['$id']); -// $this->assertArrayNotHasKey('reviews', $movie); -// -// $documents = static::getDatabase()->find('review', [ -// Query::select(['date', 'movie.date']) -// ]); -// -// $this->assertCount(3, $documents); -// -// $document = $documents[0]; -// $this->assertArrayHasKey('date', $document); -// $this->assertArrayHasKey('movie', $document); -// $this->assertArrayHasKey('date', $document->getAttribute('movie')); -// $this->assertArrayNotHasKey('name', $document); -// $this->assertEquals(29, strlen($document['date'])); // checks filter -// $this->assertEquals(29, strlen($document['movie']['date'])); -// -// $review = static::getDatabase()->getDocument('review', 'review2'); -// $movie = $review->getAttribute('movie', []); -// $this->assertEquals('movie2', $movie['$id']); -// $this->assertArrayNotHasKey('reviews', $movie); -// -// // Get related document -// $movie = static::getDatabase()->getDocument('movie', 'movie1'); -// $this->assertArrayNotHasKey('reviews', $movie); -// -// $movie = static::getDatabase()->getDocument('movie', 'movie2'); -// $this->assertArrayNotHasKey('reviews', $movie); -// -// $reviews = static::getDatabase()->find('review'); -// -// $this->assertEquals(3, \count($reviews)); -// -// // Select related document attributes -// $review = static::getDatabase()->findOne('review', [ -// Query::select(['*', 'movie.name']) -// ]); -// -// if (!$review instanceof Document) { -// throw new Exception('Review not found'); -// } -// -// $this->assertEquals('Movie 1', $review->getAttribute('movie')->getAttribute('name')); -// $this->assertArrayNotHasKey('length', $review->getAttribute('movie')); -// -// $review = static::getDatabase()->getDocument('review', 'review1', [ -// Query::select(['*', 'movie.name']) -// ]); -// -// $this->assertEquals('Movie 1', $review->getAttribute('movie')->getAttribute('name')); -// $this->assertArrayNotHasKey('length', $review->getAttribute('movie')); -// -// // Update root document attribute without altering relationship -// $review1 = static::getDatabase()->updateDocument( -// 'review', -// $review1->getId(), -// $review1->setAttribute('name', 'Review 1 Updated') -// ); -// -// $this->assertEquals('Review 1 Updated', $review1->getAttribute('name')); -// $review1 = static::getDatabase()->getDocument('review', 'review1'); -// $this->assertEquals('Review 1 Updated', $review1->getAttribute('name')); -// -// // Update nested document attribute -// $movie = $review1->getAttribute('movie'); -// $movie->setAttribute('name', 'Movie 1 Updated'); -// -// $review1 = static::getDatabase()->updateDocument( -// 'review', -// $review1->getId(), -// $review1->setAttribute('movie', $movie) -// ); -// -// $this->assertEquals('Movie 1 Updated', $review1->getAttribute('movie')->getAttribute('name')); -// $review1 = static::getDatabase()->getDocument('review', 'review1'); -// $this->assertEquals('Movie 1 Updated', $review1->getAttribute('movie')->getAttribute('name')); -// -// // Create new document with no relationship -// $review5 = static::getDatabase()->createDocument('review', new Document([ -// '$id' => 'review5', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Review 5', -// ])); -// -// // Update to relate to created document -// $review5 = static::getDatabase()->updateDocument( -// 'review', -// $review5->getId(), -// $review5->setAttribute('movie', new Document([ -// '$id' => 'movie5', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Movie 5', -// 'length' => 90, -// ])) -// ); -// -// $this->assertEquals('Movie 5', $review5->getAttribute('movie')->getAttribute('name')); -// $review5 = static::getDatabase()->getDocument('review', 'review5'); -// $this->assertEquals('Movie 5', $review5->getAttribute('movie')->getAttribute('name')); -// -// // Update document with new related document -// static::getDatabase()->updateDocument( -// 'review', -// $review1->getId(), -// $review1->setAttribute('movie', 'movie2') -// ); -// -// // Rename relationship keys on both sides -// static::getDatabase()->updateRelationship( -// 'review', -// 'movie', -// 'newMovie', -// ); -// -// // Get document with new relationship key -// $review = static::getDatabase()->getDocument('review', 'review1'); -// $movie = $review->getAttribute('newMovie'); -// $this->assertEquals('movie2', $movie['$id']); -// -// // Reset values -// $review1 = static::getDatabase()->getDocument('review', 'review1'); -// -// static::getDatabase()->updateDocument( -// 'review', -// $review1->getId(), -// $review1->setAttribute('newMovie', 'movie1') -// ); -// -// // Create new document with no relationship -// static::getDatabase()->createDocument('movie', new Document([ -// '$id' => 'movie3', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Movie 3', -// 'length' => 90, -// ])); -// -// // Can delete document with no relationship when on delete is set to restrict -// $deleted = static::getDatabase()->deleteDocument('movie', 'movie3'); -// $this->assertEquals(true, $deleted); -// -// $movie3 = static::getDatabase()->getDocument('movie', 'movie3'); -// $this->assertEquals(true, $movie3->isEmpty()); -// -// // Try to delete document while still related to another with on delete: restrict -// try { -// static::getDatabase()->deleteDocument('movie', 'movie1'); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); -// } -// -// // Change on delete to set null -// static::getDatabase()->updateRelationship( -// collection: 'review', -// id: 'newMovie', -// onDelete: Database::RELATION_MUTATE_SET_NULL -// ); -// -// // Delete child, set parent relationship to null -// static::getDatabase()->deleteDocument('movie', 'movie1'); -// -// // Check relation was set to null -// $review1 = static::getDatabase()->getDocument('review', 'review1'); -// $this->assertEquals(null, $review1->getAttribute('newMovie')); -// -// // Change on delete to cascade -// static::getDatabase()->updateRelationship( -// collection: 'review', -// id: 'newMovie', -// onDelete: Database::RELATION_MUTATE_CASCADE -// ); -// -// // Delete child, will delete parent -// static::getDatabase()->deleteDocument('movie', 'movie2'); -// -// // Check parent and child were deleted -// $library = static::getDatabase()->getDocument('movie', 'movie2'); -// $this->assertEquals(true, $library->isEmpty()); -// -// $library = static::getDatabase()->getDocument('review', 'review2'); -// $this->assertEquals(true, $library->isEmpty()); -// -// -// // Delete relationship -// static::getDatabase()->deleteRelationship( -// 'review', -// 'newMovie' -// ); -// -// // Try to get document again -// $review = static::getDatabase()->getDocument('review', 'review1'); -// $movie = $review->getAttribute('newMovie'); -// $this->assertEquals(null, $movie); -// } -// -// public function testManyToOneTwoWayRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('product'); -// static::getDatabase()->createCollection('store'); -// -// static::getDatabase()->createAttribute('store', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('store', 'opensAt', Database::VAR_STRING, 5, true); -// -// static::getDatabase()->createAttribute( -// collection: 'product', -// id: 'name', -// type: Database::VAR_STRING, -// size: 255, -// required: true -// ); -// -// static::getDatabase()->createRelationship( -// collection: 'product', -// relatedCollection: 'store', -// type: Database::RELATION_MANY_TO_ONE, -// twoWay: true, -// twoWayKey: 'products' -// ); -// -// // Check metadata for collection -// $collection = static::getDatabase()->getCollection('product'); -// $attributes = $collection->getAttribute('attributes', []); -// foreach ($attributes as $attribute) { -// if ($attribute['key'] === 'store') { -// $this->assertEquals('relationship', $attribute['type']); -// $this->assertEquals('store', $attribute['$id']); -// $this->assertEquals('store', $attribute['key']); -// $this->assertEquals('store', $attribute['options']['relatedCollection']); -// $this->assertEquals(Database::RELATION_MANY_TO_ONE, $attribute['options']['relationType']); -// $this->assertEquals(true, $attribute['options']['twoWay']); -// $this->assertEquals('products', $attribute['options']['twoWayKey']); -// } -// } -// -// // Check metadata for related collection -// $collection = static::getDatabase()->getCollection('store'); -// $attributes = $collection->getAttribute('attributes', []); -// foreach ($attributes as $attribute) { -// if ($attribute['key'] === 'products') { -// $this->assertEquals('relationship', $attribute['type']); -// $this->assertEquals('products', $attribute['$id']); -// $this->assertEquals('products', $attribute['key']); -// $this->assertEquals('product', $attribute['options']['relatedCollection']); -// $this->assertEquals(Database::RELATION_MANY_TO_ONE, $attribute['options']['relationType']); -// $this->assertEquals(true, $attribute['options']['twoWay']); -// $this->assertEquals('store', $attribute['options']['twoWayKey']); -// } -// } -// -// // Create document with relationship with nested data -// $product1 = static::getDatabase()->createDocument('product', new Document([ -// '$id' => 'product1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Product 1', -// 'store' => [ -// '$id' => 'store1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Store 1', -// 'opensAt' => '09:00', -// ], -// ])); -// -// // Update a document with non existing related document. It should not get added to the list. -// static::getDatabase()->updateDocument('product', 'product1', $product1->setAttribute('store', 'no-store')); -// -// $product1Document = static::getDatabase()->getDocument('product', 'product1'); -// // Assert document does not contain non existing relation document. -// $this->assertEquals(null, $product1Document->getAttribute('store')); -// -// static::getDatabase()->updateDocument('product', 'product1', $product1->setAttribute('store', 'store1')); -// -// // Create document with relationship with related ID -// static::getDatabase()->createDocument('store', new Document([ -// '$id' => 'store2', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Store 2', -// 'opensAt' => '09:30', -// ])); -// static::getDatabase()->createDocument('product', new Document([ -// '$id' => 'product2', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Product 2', -// 'store' => 'store2', -// ])); -// -// // Create from child side -// static::getDatabase()->createDocument('store', new Document([ -// '$id' => 'store3', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Store 3', -// 'opensAt' => '11:30', -// 'products' => [ -// [ -// '$id' => 'product3', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Product 3', -// ], -// ], -// ])); -// -// static::getDatabase()->createDocument('product', new Document([ -// '$id' => 'product4', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Product 4', -// ])); -// static::getDatabase()->createDocument('store', new Document([ -// '$id' => 'store4', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Store 4', -// 'opensAt' => '11:30', -// 'products' => [ -// 'product4', -// ], -// ])); -// -// // Get document with relationship -// $product = static::getDatabase()->getDocument('product', 'product1'); -// $store = $product->getAttribute('store', []); -// $this->assertEquals('store1', $store['$id']); -// $this->assertArrayNotHasKey('products', $store); -// -// $product = static::getDatabase()->getDocument('product', 'product2'); -// $store = $product->getAttribute('store', []); -// $this->assertEquals('store2', $store['$id']); -// $this->assertArrayNotHasKey('products', $store); -// -// $product = static::getDatabase()->getDocument('product', 'product3'); -// $store = $product->getAttribute('store', []); -// $this->assertEquals('store3', $store['$id']); -// $this->assertArrayNotHasKey('products', $store); -// -// $product = static::getDatabase()->getDocument('product', 'product4'); -// $store = $product->getAttribute('store', []); -// $this->assertEquals('store4', $store['$id']); -// $this->assertArrayNotHasKey('products', $store); -// -// // Get related document -// $store = static::getDatabase()->getDocument('store', 'store1'); -// $products = $store->getAttribute('products'); -// $this->assertEquals('product1', $products[0]['$id']); -// $this->assertArrayNotHasKey('store', $products[0]); -// -// $store = static::getDatabase()->getDocument('store', 'store2'); -// $products = $store->getAttribute('products'); -// $this->assertEquals('product2', $products[0]['$id']); -// $this->assertArrayNotHasKey('store', $products[0]); -// -// $store = static::getDatabase()->getDocument('store', 'store3'); -// $products = $store->getAttribute('products'); -// $this->assertEquals('product3', $products[0]['$id']); -// $this->assertArrayNotHasKey('store', $products[0]); -// -// $store = static::getDatabase()->getDocument('store', 'store4'); -// $products = $store->getAttribute('products'); -// $this->assertEquals('product4', $products[0]['$id']); -// $this->assertArrayNotHasKey('store', $products[0]); -// -// $products = static::getDatabase()->find('product'); -// -// $this->assertEquals(4, \count($products)); -// -// // Select related document attributes -// $product = static::getDatabase()->findOne('product', [ -// Query::select(['*', 'store.name']) -// ]); -// -// if (!$product instanceof Document) { -// throw new Exception('Product not found'); -// } -// -// $this->assertEquals('Store 1', $product->getAttribute('store')->getAttribute('name')); -// $this->assertArrayNotHasKey('opensAt', $product->getAttribute('store')); -// -// $product = static::getDatabase()->getDocument('product', 'product1', [ -// Query::select(['*', 'store.name']) -// ]); -// -// $this->assertEquals('Store 1', $product->getAttribute('store')->getAttribute('name')); -// $this->assertArrayNotHasKey('opensAt', $product->getAttribute('store')); -// -// // Update root document attribute without altering relationship -// $product1 = static::getDatabase()->updateDocument( -// 'product', -// $product1->getId(), -// $product1->setAttribute('name', 'Product 1 Updated') -// ); -// -// $this->assertEquals('Product 1 Updated', $product1->getAttribute('name')); -// $product1 = static::getDatabase()->getDocument('product', 'product1'); -// $this->assertEquals('Product 1 Updated', $product1->getAttribute('name')); -// -// // Update inverse document attribute without altering relationship -// $store1 = static::getDatabase()->getDocument('store', 'store1'); -// $store1 = static::getDatabase()->updateDocument( -// 'store', -// $store1->getId(), -// $store1->setAttribute('name', 'Store 1 Updated') -// ); -// -// $this->assertEquals('Store 1 Updated', $store1->getAttribute('name')); -// $store1 = static::getDatabase()->getDocument('store', 'store1'); -// $this->assertEquals('Store 1 Updated', $store1->getAttribute('name')); -// -// // Update nested document attribute -// $store = $product1->getAttribute('store'); -// $store->setAttribute('name', 'Store 1 Updated'); -// -// $product1 = static::getDatabase()->updateDocument( -// 'product', -// $product1->getId(), -// $product1->setAttribute('store', $store) -// ); -// -// $this->assertEquals('Store 1 Updated', $product1->getAttribute('store')->getAttribute('name')); -// $product1 = static::getDatabase()->getDocument('product', 'product1'); -// $this->assertEquals('Store 1 Updated', $product1->getAttribute('store')->getAttribute('name')); -// -// // Update inverse nested document attribute -// $product = $store1->getAttribute('products')[0]; -// $product->setAttribute('name', 'Product 1 Updated'); -// -// $store1 = static::getDatabase()->updateDocument( -// 'store', -// $store1->getId(), -// $store1->setAttribute('products', [$product]) -// ); -// -// $this->assertEquals('Product 1 Updated', $store1->getAttribute('products')[0]->getAttribute('name')); -// $store1 = static::getDatabase()->getDocument('store', 'store1'); -// $this->assertEquals('Product 1 Updated', $store1->getAttribute('products')[0]->getAttribute('name')); -// -// // Create new document with no relationship -// $product5 = static::getDatabase()->createDocument('product', new Document([ -// '$id' => 'product5', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Product 5', -// ])); -// -// // Update to relate to created document -// $product5 = static::getDatabase()->updateDocument( -// 'product', -// $product5->getId(), -// $product5->setAttribute('store', new Document([ -// '$id' => 'store5', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Store 5', -// 'opensAt' => '09:00', -// ])) -// ); -// -// $this->assertEquals('Store 5', $product5->getAttribute('store')->getAttribute('name')); -// $product5 = static::getDatabase()->getDocument('product', 'product5'); -// $this->assertEquals('Store 5', $product5->getAttribute('store')->getAttribute('name')); -// -// // Create new child document with no relationship -// $store6 = static::getDatabase()->createDocument('store', new Document([ -// '$id' => 'store6', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Store 6', -// 'opensAt' => '09:00', -// ])); -// -// // Update inverse to related to newly created document -// $store6 = static::getDatabase()->updateDocument( -// 'store', -// $store6->getId(), -// $store6->setAttribute('products', [new Document([ -// '$id' => 'product6', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Product 6', -// ])]) -// ); -// -// $this->assertEquals('Product 6', $store6->getAttribute('products')[0]->getAttribute('name')); -// $store6 = static::getDatabase()->getDocument('store', 'store6'); -// $this->assertEquals('Product 6', $store6->getAttribute('products')[0]->getAttribute('name')); -// -// // Update document with new related document -// static::getDatabase()->updateDocument( -// 'product', -// $product1->getId(), -// $product1->setAttribute('store', 'store2') -// ); -// -// $store1 = static::getDatabase()->getDocument('store', 'store1'); -// -// // Update inverse document -// static::getDatabase()->updateDocument( -// 'store', -// $store1->getId(), -// $store1->setAttribute('products', ['product1']) -// ); -// -// $store2 = static::getDatabase()->getDocument('store', 'store2'); -// -// // Update inverse document -// static::getDatabase()->updateDocument( -// 'store', -// $store2->getId(), -// $store2->setAttribute('products', ['product1', 'product2']) -// ); -// -// // Rename relationship keys on both sides -// static::getDatabase()->updateRelationship( -// 'product', -// 'store', -// 'newStore', -// 'newProducts' -// ); -// -// // Get document with new relationship key -// $store = static::getDatabase()->getDocument('store', 'store2'); -// $products = $store->getAttribute('newProducts'); -// $this->assertEquals('product1', $products[0]['$id']); -// -// // Get inverse document with new relationship key -// $product = static::getDatabase()->getDocument('product', 'product1'); -// $store = $product->getAttribute('newStore'); -// $this->assertEquals('store2', $store['$id']); -// -// // Reset relationships -// $store1 = static::getDatabase()->getDocument('store', 'store1'); -// static::getDatabase()->updateDocument( -// 'store', -// $store1->getId(), -// $store1->setAttribute('newProducts', ['product1']) -// ); -// -// // Create new document with no relationship -// static::getDatabase()->createDocument('store', new Document([ -// '$id' => 'store7', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Store 7', -// 'opensAt' => '09:00', -// ])); -// -// // Can delete document with no relationship when on delete is set to restrict -// $deleted = static::getDatabase()->deleteDocument('store', 'store7'); -// $this->assertEquals(true, $deleted); -// -// $store7 = static::getDatabase()->getDocument('store', 'store7'); -// $this->assertEquals(true, $store7->isEmpty()); -// -// // Try to delete child while still related to another with on delete: restrict -// try { -// static::getDatabase()->deleteDocument('store', 'store1'); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); -// } -// -// // Delete parent while still related to another with on delete: restrict -// $result = static::getDatabase()->deleteDocument('product', 'product5'); -// $this->assertEquals(true, $result); -// -// // Change on delete to set null -// static::getDatabase()->updateRelationship( -// collection: 'product', -// id: 'newStore', -// onDelete: Database::RELATION_MUTATE_SET_NULL -// ); -// -// // Delete child, set parent relationship to null -// static::getDatabase()->deleteDocument('store', 'store1'); -// -// // Check relation was set to null -// static::getDatabase()->getDocument('product', 'product1'); -// $this->assertEquals(null, $product1->getAttribute('newStore')); -// -// // Change on delete to cascade -// static::getDatabase()->updateRelationship( -// collection: 'product', -// id: 'newStore', -// onDelete: Database::RELATION_MUTATE_CASCADE -// ); -// -// // Delete child, will delete parent -// static::getDatabase()->deleteDocument('store', 'store2'); -// -// // Check parent and child were deleted -// $library = static::getDatabase()->getDocument('store', 'store2'); -// $this->assertEquals(true, $library->isEmpty()); -// -// $library = static::getDatabase()->getDocument('product', 'product2'); -// $this->assertEquals(true, $library->isEmpty()); -// -// // Delete relationship -// static::getDatabase()->deleteRelationship( -// 'product', -// 'newStore' -// ); -// -// // Try to get document again -// $products = static::getDatabase()->getDocument('product', 'product1'); -// $store = $products->getAttribute('newStore'); -// $this->assertEquals(null, $store); -// -// // Try to get inverse document again -// $store = static::getDatabase()->getDocument('store', 'store1'); -// $products = $store->getAttribute('newProducts'); -// $this->assertEquals(null, $products); -// } -// -// public function testManyToManyOneWayRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('playlist'); -// static::getDatabase()->createCollection('song'); -// -// static::getDatabase()->createAttribute('playlist', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('song', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('song', 'length', Database::VAR_INTEGER, 0, true); -// -// static::getDatabase()->createRelationship( -// collection: 'playlist', -// relatedCollection: 'song', -// type: Database::RELATION_MANY_TO_MANY, -// id: 'songs' -// ); -// -// // Check metadata for collection -// $collection = static::getDatabase()->getCollection('playlist'); -// $attributes = $collection->getAttribute('attributes', []); -// -// foreach ($attributes as $attribute) { -// if ($attribute['key'] === 'songs') { -// $this->assertEquals('relationship', $attribute['type']); -// $this->assertEquals('songs', $attribute['$id']); -// $this->assertEquals('songs', $attribute['key']); -// $this->assertEquals('song', $attribute['options']['relatedCollection']); -// $this->assertEquals(Database::RELATION_MANY_TO_MANY, $attribute['options']['relationType']); -// $this->assertEquals(false, $attribute['options']['twoWay']); -// $this->assertEquals('playlist', $attribute['options']['twoWayKey']); -// } -// } -// -// // Create document with relationship with nested data -// $playlist1 = static::getDatabase()->createDocument('playlist', new Document([ -// '$id' => 'playlist1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Playlist 1', -// 'songs' => [ -// [ -// '$id' => 'song1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Song 1', -// 'length' => 180, -// ], -// ], -// ])); -// -// // Create document with relationship with related ID -// static::getDatabase()->createDocument('song', new Document([ -// '$id' => 'song2', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Song 2', -// 'length' => 140, -// ])); -// static::getDatabase()->createDocument('playlist', new Document([ -// '$id' => 'playlist2', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Playlist 2', -// 'songs' => [ -// 'song2' -// ] -// ])); -// -// // Update a document with non existing related document. It should not get added to the list. -// static::getDatabase()->updateDocument('playlist', 'playlist1', $playlist1->setAttribute('songs', ['song1','no-song'])); -// -// $playlist1Document = static::getDatabase()->getDocument('playlist', 'playlist1'); -// // Assert document does not contain non existing relation document. -// $this->assertEquals(1, \count($playlist1Document->getAttribute('songs'))); -// -// $documents = static::getDatabase()->find('playlist', [ -// Query::select(['name']), -// Query::limit(1) -// ]); -// -// $this->assertArrayNotHasKey('songs', $documents[0]); -// -// // Get document with relationship -// $playlist = static::getDatabase()->getDocument('playlist', 'playlist1'); -// $songs = $playlist->getAttribute('songs', []); -// $this->assertEquals('song1', $songs[0]['$id']); -// $this->assertArrayNotHasKey('playlist', $songs[0]); -// -// $playlist = static::getDatabase()->getDocument('playlist', 'playlist2'); -// $songs = $playlist->getAttribute('songs', []); -// $this->assertEquals('song2', $songs[0]['$id']); -// $this->assertArrayNotHasKey('playlist', $songs[0]); -// -// // Get related document -// $library = static::getDatabase()->getDocument('song', 'song1'); -// $this->assertArrayNotHasKey('songs', $library); -// -// $library = static::getDatabase()->getDocument('song', 'song2'); -// $this->assertArrayNotHasKey('songs', $library); -// -// $playlists = static::getDatabase()->find('playlist'); -// -// $this->assertEquals(2, \count($playlists)); -// -// // Select related document attributes -// $playlist = static::getDatabase()->findOne('playlist', [ -// Query::select(['*', 'songs.name']) -// ]); -// -// if (!$playlist instanceof Document) { -// throw new Exception('Playlist not found'); -// } -// -// $this->assertEquals('Song 1', $playlist->getAttribute('songs')[0]->getAttribute('name')); -// $this->assertArrayNotHasKey('length', $playlist->getAttribute('songs')[0]); -// -// $playlist = static::getDatabase()->getDocument('playlist', 'playlist1', [ -// Query::select(['*', 'songs.name']) -// ]); -// -// $this->assertEquals('Song 1', $playlist->getAttribute('songs')[0]->getAttribute('name')); -// $this->assertArrayNotHasKey('length', $playlist->getAttribute('songs')[0]); -// -// // Update root document attribute without altering relationship -// $playlist1 = static::getDatabase()->updateDocument( -// 'playlist', -// $playlist1->getId(), -// $playlist1->setAttribute('name', 'Playlist 1 Updated') -// ); -// -// $this->assertEquals('Playlist 1 Updated', $playlist1->getAttribute('name')); -// $playlist1 = static::getDatabase()->getDocument('playlist', 'playlist1'); -// $this->assertEquals('Playlist 1 Updated', $playlist1->getAttribute('name')); -// -// // Update nested document attribute -// $songs = $playlist1->getAttribute('songs', []); -// $songs[0]->setAttribute('name', 'Song 1 Updated'); -// -// $playlist1 = static::getDatabase()->updateDocument( -// 'playlist', -// $playlist1->getId(), -// $playlist1->setAttribute('songs', $songs) -// ); -// -// $this->assertEquals('Song 1 Updated', $playlist1->getAttribute('songs')[0]->getAttribute('name')); -// $playlist1 = static::getDatabase()->getDocument('playlist', 'playlist1'); -// $this->assertEquals('Song 1 Updated', $playlist1->getAttribute('songs')[0]->getAttribute('name')); -// -// // Create new document with no relationship -// $playlist5 = static::getDatabase()->createDocument('playlist', new Document([ -// '$id' => 'playlist5', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Playlist 5', -// ])); -// -// // Update to relate to created document -// $playlist5 = static::getDatabase()->updateDocument( -// 'playlist', -// $playlist5->getId(), -// $playlist5->setAttribute('songs', [new Document([ -// '$id' => 'song5', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Song 5', -// 'length' => 180, -// ])]) -// ); -// -// // Playlist relating to existing songs that belong to other playlists -// static::getDatabase()->createDocument('playlist', new Document([ -// '$id' => 'playlist6', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Playlist 6', -// 'songs' => [ -// 'song1', -// 'song2', -// 'song5' -// ] -// ])); -// -// $this->assertEquals('Song 5', $playlist5->getAttribute('songs')[0]->getAttribute('name')); -// $playlist5 = static::getDatabase()->getDocument('playlist', 'playlist5'); -// $this->assertEquals('Song 5', $playlist5->getAttribute('songs')[0]->getAttribute('name')); -// -// // Update document with new related document -// static::getDatabase()->updateDocument( -// 'playlist', -// $playlist1->getId(), -// $playlist1->setAttribute('songs', ['song2']) -// ); -// -// // Rename relationship key -// static::getDatabase()->updateRelationship( -// 'playlist', -// 'songs', -// 'newSongs' -// ); -// -// // Get document with new relationship key -// $playlist = static::getDatabase()->getDocument('playlist', 'playlist1'); -// $songs = $playlist->getAttribute('newSongs'); -// $this->assertEquals('song2', $songs[0]['$id']); -// -// // Create new document with no relationship -// static::getDatabase()->createDocument('playlist', new Document([ -// '$id' => 'playlist3', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Playlist 3', -// ])); -// -// // Can delete document with no relationship when on delete is set to restrict -// $deleted = static::getDatabase()->deleteDocument('playlist', 'playlist3'); -// $this->assertEquals(true, $deleted); -// -// $playlist3 = static::getDatabase()->getDocument('playlist', 'playlist3'); -// $this->assertEquals(true, $playlist3->isEmpty()); -// -// // Try to delete document while still related to another with on delete: restrict -// try { -// static::getDatabase()->deleteDocument('playlist', 'playlist1'); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); -// } -// -// // Change on delete to set null -// static::getDatabase()->updateRelationship( -// collection: 'playlist', -// id: 'newSongs', -// onDelete: Database::RELATION_MUTATE_SET_NULL -// ); -// -// $playlist1 = static::getDatabase()->getDocument('playlist', 'playlist1'); -// -// // Reset relationships -// static::getDatabase()->updateDocument( -// 'playlist', -// $playlist1->getId(), -// $playlist1->setAttribute('newSongs', ['song1']) -// ); -// -// // Delete child, will delete junction -// static::getDatabase()->deleteDocument('song', 'song1'); -// -// // Check relation was set to null -// $playlist1 = static::getDatabase()->getDocument('playlist', 'playlist1'); -// $this->assertEquals(0, \count($playlist1->getAttribute('newSongs'))); -// -// // Change on delete to cascade -// static::getDatabase()->updateRelationship( -// collection: 'playlist', -// id: 'newSongs', -// onDelete: Database::RELATION_MUTATE_CASCADE -// ); -// -// // Delete parent, will delete child -// static::getDatabase()->deleteDocument('playlist', 'playlist2'); -// -// // Check parent and child were deleted -// $library = static::getDatabase()->getDocument('playlist', 'playlist2'); -// $this->assertEquals(true, $library->isEmpty()); -// -// $library = static::getDatabase()->getDocument('song', 'song2'); -// $this->assertEquals(true, $library->isEmpty()); -// -// // Delete relationship -// static::getDatabase()->deleteRelationship( -// 'playlist', -// 'newSongs' -// ); -// -// // Try to get document again -// $playlist = static::getDatabase()->getDocument('playlist', 'playlist1'); -// $songs = $playlist->getAttribute('newSongs'); -// $this->assertEquals(null, $songs); -// } -// -// public function testManyToManyTwoWayRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('students'); -// static::getDatabase()->createCollection('classes'); -// -// static::getDatabase()->createAttribute('students', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('classes', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('classes', 'number', Database::VAR_INTEGER, 0, true); -// -// static::getDatabase()->createRelationship( -// collection: 'students', -// relatedCollection: 'classes', -// type: Database::RELATION_MANY_TO_MANY, -// twoWay: true, -// ); -// -// // Check metadata for collection -// $collection = static::getDatabase()->getCollection('students'); -// $attributes = $collection->getAttribute('attributes', []); -// foreach ($attributes as $attribute) { -// if ($attribute['key'] === 'students') { -// $this->assertEquals('relationship', $attribute['type']); -// $this->assertEquals('students', $attribute['$id']); -// $this->assertEquals('students', $attribute['key']); -// $this->assertEquals('students', $attribute['options']['relatedCollection']); -// $this->assertEquals(Database::RELATION_MANY_TO_MANY, $attribute['options']['relationType']); -// $this->assertEquals(true, $attribute['options']['twoWay']); -// $this->assertEquals('classes', $attribute['options']['twoWayKey']); -// } -// } -// -// // Check metadata for related collection -// $collection = static::getDatabase()->getCollection('classes'); -// $attributes = $collection->getAttribute('attributes', []); -// foreach ($attributes as $attribute) { -// if ($attribute['key'] === 'classes') { -// $this->assertEquals('relationship', $attribute['type']); -// $this->assertEquals('classes', $attribute['$id']); -// $this->assertEquals('classes', $attribute['key']); -// $this->assertEquals('classes', $attribute['options']['relatedCollection']); -// $this->assertEquals(Database::RELATION_MANY_TO_MANY, $attribute['options']['relationType']); -// $this->assertEquals(true, $attribute['options']['twoWay']); -// $this->assertEquals('students', $attribute['options']['twoWayKey']); -// } -// } -// -// // Create document with relationship with nested data -// $student1 = static::getDatabase()->createDocument('students', new Document([ -// '$id' => 'student1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Student 1', -// 'classes' => [ -// [ -// '$id' => 'class1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Class 1', -// 'number' => 1, -// ], -// ], -// ])); -// -// // Update a document with non existing related document. It should not get added to the list. -// static::getDatabase()->updateDocument('students', 'student1', $student1->setAttribute('classes', ['class1', 'no-class'])); -// -// $student1Document = static::getDatabase()->getDocument('students', 'student1'); -// // Assert document does not contain non existing relation document. -// $this->assertEquals(1, \count($student1Document->getAttribute('classes'))); -// -// // Create document with relationship with related ID -// static::getDatabase()->createDocument('classes', new Document([ -// '$id' => 'class2', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// -// ], -// 'name' => 'Class 2', -// 'number' => 2, -// ])); -// static::getDatabase()->createDocument('students', new Document([ -// '$id' => 'student2', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Student 2', -// 'classes' => [ -// 'class2' -// ], -// ])); -// -// // Create from child side -// static::getDatabase()->createDocument('classes', new Document([ -// '$id' => 'class3', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Class 3', -// 'number' => 3, -// 'students' => [ -// [ -// '$id' => 'student3', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Student 3', -// ] -// ], -// ])); -// static::getDatabase()->createDocument('students', new Document([ -// '$id' => 'student4', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Student 4' -// ])); -// static::getDatabase()->createDocument('classes', new Document([ -// '$id' => 'class4', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// -// ], -// 'name' => 'Class 4', -// 'number' => 4, -// 'students' => [ -// 'student4' -// ], -// ])); -// -// // Get document with relationship -// $student = static::getDatabase()->getDocument('students', 'student1'); -// $classes = $student->getAttribute('classes', []); -// $this->assertEquals('class1', $classes[0]['$id']); -// $this->assertArrayNotHasKey('students', $classes[0]); -// -// $student = static::getDatabase()->getDocument('students', 'student2'); -// $classes = $student->getAttribute('classes', []); -// $this->assertEquals('class2', $classes[0]['$id']); -// $this->assertArrayNotHasKey('students', $classes[0]); -// -// $student = static::getDatabase()->getDocument('students', 'student3'); -// $classes = $student->getAttribute('classes', []); -// $this->assertEquals('class3', $classes[0]['$id']); -// $this->assertArrayNotHasKey('students', $classes[0]); -// -// $student = static::getDatabase()->getDocument('students', 'student4'); -// $classes = $student->getAttribute('classes', []); -// $this->assertEquals('class4', $classes[0]['$id']); -// $this->assertArrayNotHasKey('students', $classes[0]); -// -// // Get related document -// $class = static::getDatabase()->getDocument('classes', 'class1'); -// $student = $class->getAttribute('students'); -// $this->assertEquals('student1', $student[0]['$id']); -// $this->assertArrayNotHasKey('classes', $student[0]); -// -// $class = static::getDatabase()->getDocument('classes', 'class2'); -// $student = $class->getAttribute('students'); -// $this->assertEquals('student2', $student[0]['$id']); -// $this->assertArrayNotHasKey('classes', $student[0]); -// -// $class = static::getDatabase()->getDocument('classes', 'class3'); -// $student = $class->getAttribute('students'); -// $this->assertEquals('student3', $student[0]['$id']); -// $this->assertArrayNotHasKey('classes', $student[0]); -// -// $class = static::getDatabase()->getDocument('classes', 'class4'); -// $student = $class->getAttribute('students'); -// $this->assertEquals('student4', $student[0]['$id']); -// $this->assertArrayNotHasKey('classes', $student[0]); -// -// // Select related document attributes -// $student = static::getDatabase()->findOne('students', [ -// Query::select(['*', 'classes.name']) -// ]); -// -// if (!$student instanceof Document) { -// throw new Exception('Student not found'); -// } -// -// $this->assertEquals('Class 1', $student->getAttribute('classes')[0]->getAttribute('name')); -// $this->assertArrayNotHasKey('number', $student->getAttribute('classes')[0]); -// -// $student = static::getDatabase()->getDocument('students', 'student1', [ -// Query::select(['*', 'classes.name']) -// ]); -// -// $this->assertEquals('Class 1', $student->getAttribute('classes')[0]->getAttribute('name')); -// $this->assertArrayNotHasKey('number', $student->getAttribute('classes')[0]); -// -// // Update root document attribute without altering relationship -// $student1 = static::getDatabase()->updateDocument( -// 'students', -// $student1->getId(), -// $student1->setAttribute('name', 'Student 1 Updated') -// ); -// -// $this->assertEquals('Student 1 Updated', $student1->getAttribute('name')); -// $student1 = static::getDatabase()->getDocument('students', 'student1'); -// $this->assertEquals('Student 1 Updated', $student1->getAttribute('name')); -// -// // Update inverse root document attribute without altering relationship -// $class2 = static::getDatabase()->getDocument('classes', 'class2'); -// $class2 = static::getDatabase()->updateDocument( -// 'classes', -// $class2->getId(), -// $class2->setAttribute('name', 'Class 2 Updated') -// ); -// -// $this->assertEquals('Class 2 Updated', $class2->getAttribute('name')); -// $class2 = static::getDatabase()->getDocument('classes', 'class2'); -// $this->assertEquals('Class 2 Updated', $class2->getAttribute('name')); -// -// // Update nested document attribute -// $classes = $student1->getAttribute('classes', []); -// $classes[0]->setAttribute('name', 'Class 1 Updated'); -// -// $student1 = static::getDatabase()->updateDocument( -// 'students', -// $student1->getId(), -// $student1->setAttribute('classes', $classes) -// ); -// -// $this->assertEquals('Class 1 Updated', $student1->getAttribute('classes')[0]->getAttribute('name')); -// $student1 = static::getDatabase()->getDocument('students', 'student1'); -// $this->assertEquals('Class 1 Updated', $student1->getAttribute('classes')[0]->getAttribute('name')); -// -// // Update inverse nested document attribute -// $students = $class2->getAttribute('students', []); -// $students[0]->setAttribute('name', 'Student 2 Updated'); -// -// $class2 = static::getDatabase()->updateDocument( -// 'classes', -// $class2->getId(), -// $class2->setAttribute('students', $students) -// ); -// -// $this->assertEquals('Student 2 Updated', $class2->getAttribute('students')[0]->getAttribute('name')); -// $class2 = static::getDatabase()->getDocument('classes', 'class2'); -// $this->assertEquals('Student 2 Updated', $class2->getAttribute('students')[0]->getAttribute('name')); -// -// // Create new document with no relationship -// $student5 = static::getDatabase()->createDocument('students', new Document([ -// '$id' => 'student5', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Student 5', -// ])); -// -// // Update to relate to created document -// $student5 = static::getDatabase()->updateDocument( -// 'students', -// $student5->getId(), -// $student5->setAttribute('classes', [new Document([ -// '$id' => 'class5', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Class 5', -// 'number' => 5, -// ])]) -// ); -// -// $this->assertEquals('Class 5', $student5->getAttribute('classes')[0]->getAttribute('name')); -// $student5 = static::getDatabase()->getDocument('students', 'student5'); -// $this->assertEquals('Class 5', $student5->getAttribute('classes')[0]->getAttribute('name')); -// -// // Create child document with no relationship -// $class6 = static::getDatabase()->createDocument('classes', new Document([ -// '$id' => 'class6', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Class 6', -// 'number' => 6, -// ])); -// -// // Update to relate to created document -// $class6 = static::getDatabase()->updateDocument( -// 'classes', -// $class6->getId(), -// $class6->setAttribute('students', [new Document([ -// '$id' => 'student6', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Student 6', -// ])]) -// ); -// -// $this->assertEquals('Student 6', $class6->getAttribute('students')[0]->getAttribute('name')); -// $class6 = static::getDatabase()->getDocument('classes', 'class6'); -// $this->assertEquals('Student 6', $class6->getAttribute('students')[0]->getAttribute('name')); -// -// // Update document with new related document -// static::getDatabase()->updateDocument( -// 'students', -// $student1->getId(), -// $student1->setAttribute('classes', ['class2']) -// ); -// -// $class1 = static::getDatabase()->getDocument('classes', 'class1'); -// -// // Update inverse document -// static::getDatabase()->updateDocument( -// 'classes', -// $class1->getId(), -// $class1->setAttribute('students', ['student1']) -// ); -// -// // Rename relationship keys on both sides -// static::getDatabase()->updateRelationship( -// 'students', -// 'classes', -// 'newClasses', -// 'newStudents' -// ); -// -// // Get document with new relationship key -// $students = static::getDatabase()->getDocument('students', 'student1'); -// $classes = $students->getAttribute('newClasses'); -// $this->assertEquals('class2', $classes[0]['$id']); -// -// // Get inverse document with new relationship key -// $class = static::getDatabase()->getDocument('classes', 'class1'); -// $students = $class->getAttribute('newStudents'); -// $this->assertEquals('student1', $students[0]['$id']); -// -// // Create new document with no relationship -// static::getDatabase()->createDocument('students', new Document([ -// '$id' => 'student7', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()), -// ], -// 'name' => 'Student 7', -// ])); -// -// // Can delete document with no relationship when on delete is set to restrict -// $deleted = static::getDatabase()->deleteDocument('students', 'student7'); -// $this->assertEquals(true, $deleted); -// -// $student6 = static::getDatabase()->getDocument('students', 'student7'); -// $this->assertEquals(true, $student6->isEmpty()); -// -// // Try to delete document while still related to another with on delete: restrict -// try { -// static::getDatabase()->deleteDocument('students', 'student1'); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertEquals('Cannot delete document because it has at least one related document.', $e->getMessage()); -// } -// -// // Change on delete to set null -// static::getDatabase()->updateRelationship( -// collection: 'students', -// id: 'newClasses', -// onDelete: Database::RELATION_MUTATE_SET_NULL -// ); -// -// $student1 = static::getDatabase()->getDocument('students', 'student1'); -// -// // Reset relationships -// static::getDatabase()->updateDocument( -// 'students', -// $student1->getId(), -// $student1->setAttribute('newClasses', ['class1']) -// ); -// -// // Delete child, will delete junction -// static::getDatabase()->deleteDocument('classes', 'class1'); -// -// // Check relation was set to null -// $student1 = static::getDatabase()->getDocument('students', 'student1'); -// $this->assertEquals(0, \count($student1->getAttribute('newClasses'))); -// -// // Change on delete to cascade -// static::getDatabase()->updateRelationship( -// collection: 'students', -// id: 'newClasses', -// onDelete: Database::RELATION_MUTATE_CASCADE -// ); -// -// // Delete parent, will delete child -// static::getDatabase()->deleteDocument('students', 'student2'); -// -// // Check parent and child were deleted -// $library = static::getDatabase()->getDocument('students', 'student2'); -// $this->assertEquals(true, $library->isEmpty()); -// -// // Delete child, should not delete parent -// static::getDatabase()->deleteDocument('classes', 'class6'); -// -// // Check only child was deleted -// $student6 = static::getDatabase()->getDocument('students', 'student6'); -// $this->assertEquals(false, $student6->isEmpty()); -// $this->assertEmpty($student6->getAttribute('newClasses')); -// -// $library = static::getDatabase()->getDocument('classes', 'class2'); -// $this->assertEquals(true, $library->isEmpty()); -// -// // Delete relationship -// static::getDatabase()->deleteRelationship( -// 'students', -// 'newClasses' -// ); -// -// // Try to get documents again -// $student = static::getDatabase()->getDocument('students', 'student1'); -// $classes = $student->getAttribute('newClasses'); -// $this->assertEquals(null, $classes); -// -// // Try to get inverse documents again -// $classes = static::getDatabase()->getDocument('classes', 'class1'); -// $students = $classes->getAttribute('newStudents'); -// $this->assertEquals(null, $students); -// } -// -// public function testSelectRelationshipAttributes(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('make'); -// static::getDatabase()->createCollection('model'); -// -// static::getDatabase()->createAttribute('make', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('model', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('model', 'year', Database::VAR_INTEGER, 0, true); -// -// static::getDatabase()->createRelationship( -// collection: 'make', -// relatedCollection: 'model', -// type: Database::RELATION_ONE_TO_MANY, -// id: 'models' -// ); -// -// static::getDatabase()->createDocument('make', new Document([ -// '$id' => 'ford', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Ford', -// 'models' => [ -// [ -// '$id' => 'fiesta', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Fiesta', -// 'year' => 2010, -// ], -// [ -// '$id' => 'focus', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Focus', -// 'year' => 2011, -// ], -// ], -// ])); -// -// // Select some parent attributes, some child attributes -// $make = static::getDatabase()->findOne('make', [ -// Query::select(['name', 'models.name']), -// ]); -// -// if (!$make instanceof Document) { -// throw new Exception('Make not found'); -// } -// -// $this->assertEquals('Ford', $make['name']); -// $this->assertEquals(2, \count($make['models'])); -// $this->assertEquals('Fiesta', $make['models'][0]['name']); -// $this->assertEquals('Focus', $make['models'][1]['name']); -// $this->assertArrayNotHasKey('year', $make['models'][0]); -// $this->assertArrayNotHasKey('year', $make['models'][1]); -// $this->assertArrayNotHasKey('$id', $make); -// $this->assertArrayNotHasKey('$internalId', $make); -// $this->assertArrayNotHasKey('$permissions', $make); -// $this->assertArrayNotHasKey('$collection', $make); -// $this->assertArrayNotHasKey('$createdAt', $make); -// $this->assertArrayNotHasKey('$updatedAt', $make); -// -// // Select internal attributes -// $make = static::getDatabase()->findOne('make', [ -// Query::select(['name', '$id']), -// ]); -// -// if (!$make instanceof Document) { -// throw new Exception('Make not found'); -// } -// -// $this->assertArrayHasKey('$id', $make); -// $this->assertArrayNotHasKey('$internalId', $make); -// $this->assertArrayNotHasKey('$collection', $make); -// $this->assertArrayNotHasKey('$createdAt', $make); -// $this->assertArrayNotHasKey('$updatedAt', $make); -// $this->assertArrayNotHasKey('$permissions', $make); -// -// $make = static::getDatabase()->findOne('make', [ -// Query::select(['name', '$internalId']), -// ]); -// -// if (!$make instanceof Document) { -// throw new Exception('Make not found'); -// } -// -// $this->assertArrayNotHasKey('$id', $make); -// $this->assertArrayHasKey('$internalId', $make); -// $this->assertArrayNotHasKey('$collection', $make); -// $this->assertArrayNotHasKey('$createdAt', $make); -// $this->assertArrayNotHasKey('$updatedAt', $make); -// $this->assertArrayNotHasKey('$permissions', $make); -// -// $make = static::getDatabase()->findOne('make', [ -// Query::select(['name', '$collection']), -// ]); -// -// if (!$make instanceof Document) { -// throw new Exception('Make not found'); -// } -// -// $this->assertArrayNotHasKey('$id', $make); -// $this->assertArrayNotHasKey('$internalId', $make); -// $this->assertArrayHasKey('$collection', $make); -// $this->assertArrayNotHasKey('$createdAt', $make); -// $this->assertArrayNotHasKey('$updatedAt', $make); -// $this->assertArrayNotHasKey('$permissions', $make); -// -// $make = static::getDatabase()->findOne('make', [ -// Query::select(['name', '$createdAt']), -// ]); -// -// if (!$make instanceof Document) { -// throw new Exception('Make not found'); -// } -// -// $this->assertArrayNotHasKey('$id', $make); -// $this->assertArrayNotHasKey('$internalId', $make); -// $this->assertArrayNotHasKey('$collection', $make); -// $this->assertArrayHasKey('$createdAt', $make); -// $this->assertArrayNotHasKey('$updatedAt', $make); -// $this->assertArrayNotHasKey('$permissions', $make); -// -// $make = static::getDatabase()->findOne('make', [ -// Query::select(['name', '$updatedAt']), -// ]); -// -// if (!$make instanceof Document) { -// throw new Exception('Make not found'); -// } -// -// $this->assertArrayNotHasKey('$id', $make); -// $this->assertArrayNotHasKey('$internalId', $make); -// $this->assertArrayNotHasKey('$collection', $make); -// $this->assertArrayNotHasKey('$createdAt', $make); -// $this->assertArrayHasKey('$updatedAt', $make); -// $this->assertArrayNotHasKey('$permissions', $make); -// -// $make = static::getDatabase()->findOne('make', [ -// Query::select(['name', '$permissions']), -// ]); -// -// if (!$make instanceof Document) { -// throw new Exception('Make not found'); -// } -// -// $this->assertArrayNotHasKey('$id', $make); -// $this->assertArrayNotHasKey('$internalId', $make); -// $this->assertArrayNotHasKey('$collection', $make); -// $this->assertArrayNotHasKey('$createdAt', $make); -// $this->assertArrayNotHasKey('$updatedAt', $make); -// $this->assertArrayHasKey('$permissions', $make); -// -// // Select all parent attributes, some child attributes -// $make = static::getDatabase()->findOne('make', [ -// Query::select(['*', 'models.year']), -// ]); -// -// if (!$make instanceof Document) { -// throw new Exception('Make not found'); -// } -// -// $this->assertEquals('Ford', $make['name']); -// $this->assertEquals(2, \count($make['models'])); -// $this->assertArrayNotHasKey('name', $make['models'][0]); -// $this->assertArrayNotHasKey('name', $make['models'][1]); -// $this->assertEquals(2010, $make['models'][0]['year']); -// $this->assertEquals(2011, $make['models'][1]['year']); -// -// // Select all parent attributes, all child attributes -// $make = static::getDatabase()->findOne('make', [ -// Query::select(['*', 'models.*']), -// ]); -// -// if (!$make instanceof Document) { -// throw new Exception('Make not found'); -// } -// -// $this->assertEquals('Ford', $make['name']); -// $this->assertEquals(2, \count($make['models'])); -// $this->assertEquals('Fiesta', $make['models'][0]['name']); -// $this->assertEquals('Focus', $make['models'][1]['name']); -// $this->assertEquals(2010, $make['models'][0]['year']); -// $this->assertEquals(2011, $make['models'][1]['year']); -// -// // Select all parent attributes, all child attributes -// // Must select parent if selecting children -// $make = static::getDatabase()->findOne('make', [ -// Query::select(['models.*']), -// ]); -// -// if (!$make instanceof Document) { -// throw new Exception('Make not found'); -// } -// -// $this->assertEquals('Ford', $make['name']); -// $this->assertEquals(2, \count($make['models'])); -// $this->assertEquals('Fiesta', $make['models'][0]['name']); -// $this->assertEquals('Focus', $make['models'][1]['name']); -// $this->assertEquals(2010, $make['models'][0]['year']); -// $this->assertEquals(2011, $make['models'][1]['year']); -// -// // Select all parent attributes, no child attributes -// $make = static::getDatabase()->findOne('make', [ -// Query::select(['name']), -// ]); -// -// if (!$make instanceof Document) { -// throw new Exception('Make not found'); -// } -// -// $this->assertEquals('Ford', $make['name']); -// $this->assertArrayNotHasKey('models', $make); -// } -// -// public function testNestedOneToOne_OneToOneRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('pattern'); -// static::getDatabase()->createCollection('shirt'); -// static::getDatabase()->createCollection('team'); -// -// static::getDatabase()->createAttribute('pattern', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('shirt', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('team', 'name', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'pattern', -// relatedCollection: 'shirt', -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// id: 'shirt', -// twoWayKey: 'pattern' -// ); -// static::getDatabase()->createRelationship( -// collection: 'shirt', -// relatedCollection: 'team', -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// id: 'team', -// twoWayKey: 'shirt' -// ); -// -// static::getDatabase()->createDocument('pattern', new Document([ -// '$id' => 'stripes', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Stripes', -// 'shirt' => [ -// '$id' => 'red', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Red', -// 'team' => [ -// '$id' => 'reds', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Reds', -// ], -// ], -// ])); -// -// $pattern = static::getDatabase()->getDocument('pattern', 'stripes'); -// $this->assertEquals('red', $pattern['shirt']['$id']); -// $this->assertArrayNotHasKey('pattern', $pattern['shirt']); -// $this->assertEquals('reds', $pattern['shirt']['team']['$id']); -// $this->assertArrayNotHasKey('shirt', $pattern['shirt']['team']); -// -// static::getDatabase()->createDocument('team', new Document([ -// '$id' => 'blues', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Blues', -// 'shirt' => [ -// '$id' => 'blue', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Blue', -// 'pattern' => [ -// '$id' => 'plain', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Plain', -// ], -// ], -// ])); -// -// $team = static::getDatabase()->getDocument('team', 'blues'); -// $this->assertEquals('blue', $team['shirt']['$id']); -// $this->assertArrayNotHasKey('team', $team['shirt']); -// $this->assertEquals('plain', $team['shirt']['pattern']['$id']); -// $this->assertArrayNotHasKey('shirt', $team['shirt']['pattern']); -// } -// -// public function testNestedOneToOne_OneToManyRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('teachers'); -// static::getDatabase()->createCollection('classrooms'); -// static::getDatabase()->createCollection('children'); -// -// static::getDatabase()->createAttribute('children', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('teachers', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('classrooms', 'name', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'teachers', -// relatedCollection: 'classrooms', -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// id: 'classroom', -// twoWayKey: 'teacher' -// ); -// static::getDatabase()->createRelationship( -// collection: 'classrooms', -// relatedCollection: 'children', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// twoWayKey: 'classroom' -// ); -// -// static::getDatabase()->createDocument('teachers', new Document([ -// '$id' => 'teacher1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Teacher 1', -// 'classroom' => [ -// '$id' => 'classroom1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Classroom 1', -// 'children' => [ -// [ -// '$id' => 'child1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Child 1', -// ], -// [ -// '$id' => 'child2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Child 2', -// ], -// ], -// ], -// ])); -// -// $teacher1 = static::getDatabase()->getDocument('teachers', 'teacher1'); -// $this->assertEquals('classroom1', $teacher1['classroom']['$id']); -// $this->assertArrayNotHasKey('teacher', $teacher1['classroom']); -// $this->assertEquals(2, \count($teacher1['classroom']['children'])); -// $this->assertEquals('Child 1', $teacher1['classroom']['children'][0]['name']); -// $this->assertEquals('Child 2', $teacher1['classroom']['children'][1]['name']); -// -// static::getDatabase()->createDocument('children', new Document([ -// '$id' => 'child3', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Child 3', -// 'classroom' => [ -// '$id' => 'classroom2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Classroom 2', -// 'teacher' => [ -// '$id' => 'teacher2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Teacher 2', -// ], -// ], -// ])); -// -// $child3 = static::getDatabase()->getDocument('children', 'child3'); -// $this->assertEquals('classroom2', $child3['classroom']['$id']); -// $this->assertArrayNotHasKey('children', $child3['classroom']); -// $this->assertEquals('teacher2', $child3['classroom']['teacher']['$id']); -// $this->assertArrayNotHasKey('classroom', $child3['classroom']['teacher']); -// } -// -// public function testNestedOneToOne_ManyToOneRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('users'); -// static::getDatabase()->createCollection('profiles'); -// static::getDatabase()->createCollection('avatars'); -// -// static::getDatabase()->createAttribute('users', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('profiles', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('avatars', 'name', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'users', -// relatedCollection: 'profiles', -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// id: 'profile', -// twoWayKey: 'user' -// ); -// static::getDatabase()->createRelationship( -// collection: 'profiles', -// relatedCollection: 'avatars', -// type: Database::RELATION_MANY_TO_ONE, -// twoWay: true, -// id: 'avatar', -// ); -// -// static::getDatabase()->createDocument('users', new Document([ -// '$id' => 'user1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'User 1', -// 'profile' => [ -// '$id' => 'profile1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Profile 1', -// 'avatar' => [ -// '$id' => 'avatar1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Avatar 1', -// ], -// ], -// ])); -// -// $user1 = static::getDatabase()->getDocument('users', 'user1'); -// $this->assertEquals('profile1', $user1['profile']['$id']); -// $this->assertArrayNotHasKey('user', $user1['profile']); -// $this->assertEquals('avatar1', $user1['profile']['avatar']['$id']); -// $this->assertArrayNotHasKey('profile', $user1['profile']['avatar']); -// -// static::getDatabase()->createDocument('avatars', new Document([ -// '$id' => 'avatar2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Avatar 2', -// 'profiles' => [ -// [ -// '$id' => 'profile2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Profile 2', -// 'user' => [ -// '$id' => 'user2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'User 2', -// ], -// ] -// ], -// ])); -// -// $avatar2 = static::getDatabase()->getDocument('avatars', 'avatar2'); -// $this->assertEquals('profile2', $avatar2['profiles'][0]['$id']); -// $this->assertArrayNotHasKey('avatars', $avatar2['profiles'][0]); -// $this->assertEquals('user2', $avatar2['profiles'][0]['user']['$id']); -// $this->assertArrayNotHasKey('profiles', $avatar2['profiles'][0]['user']); -// } -// -// public function testNestedOneToOne_ManyToManyRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('addresses'); -// static::getDatabase()->createCollection('houses'); -// static::getDatabase()->createCollection('buildings'); -// -// static::getDatabase()->createAttribute('addresses', 'street', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('houses', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('buildings', 'name', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'addresses', -// relatedCollection: 'houses', -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// id: 'house', -// twoWayKey: 'address' -// ); -// static::getDatabase()->createRelationship( -// collection: 'houses', -// relatedCollection: 'buildings', -// type: Database::RELATION_MANY_TO_MANY, -// twoWay: true, -// ); -// -// static::getDatabase()->createDocument('addresses', new Document([ -// '$id' => 'address1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'street' => 'Street 1', -// 'house' => [ -// '$id' => 'house1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'House 1', -// 'buildings' => [ -// [ -// '$id' => 'building1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Building 1', -// ], -// [ -// '$id' => 'building2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Building 2', -// ], -// ], -// ], -// ])); -// -// $address1 = static::getDatabase()->getDocument('addresses', 'address1'); -// $this->assertEquals('house1', $address1['house']['$id']); -// $this->assertArrayNotHasKey('address', $address1['house']); -// $this->assertEquals('building1', $address1['house']['buildings'][0]['$id']); -// $this->assertEquals('building2', $address1['house']['buildings'][1]['$id']); -// $this->assertArrayNotHasKey('houses', $address1['house']['buildings'][0]); -// $this->assertArrayNotHasKey('houses', $address1['house']['buildings'][1]); -// -// static::getDatabase()->createDocument('buildings', new Document([ -// '$id' => 'building3', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Building 3', -// 'houses' => [ -// [ -// '$id' => 'house2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'House 2', -// 'address' => [ -// '$id' => 'address2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'street' => 'Street 2', -// ], -// ], -// ], -// ])); -// } -// -// public function testNestedOneToMany_OneToOneRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('countries'); -// static::getDatabase()->createCollection('cities'); -// static::getDatabase()->createCollection('mayors'); -// -// static::getDatabase()->createAttribute('cities', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('countries', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('mayors', 'name', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'countries', -// relatedCollection: 'cities', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// twoWayKey: 'country' -// ); -// static::getDatabase()->createRelationship( -// collection: 'cities', -// relatedCollection: 'mayors', -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// id: 'mayor', -// twoWayKey: 'city' -// ); -// -// static::getDatabase()->createDocument('countries', new Document([ -// '$id' => 'country1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// ], -// 'name' => 'Country 1', -// 'cities' => [ -// [ -// '$id' => 'city1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// ], -// 'name' => 'City 1', -// 'mayor' => [ -// '$id' => 'mayor1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// ], -// 'name' => 'Mayor 1', -// ], -// ], -// [ -// '$id' => 'city2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'City 2', -// 'mayor' => [ -// '$id' => 'mayor2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Mayor 2', -// ], -// ], -// ], -// ])); -// -// $documents = static::getDatabase()->find('countries', [ -// Query::limit(1) -// ]); -// $this->assertEquals('Mayor 1', $documents[0]['cities'][0]['mayor']['name']); -// -// $documents = static::getDatabase()->find('countries', [ -// Query::select(['name']), -// Query::limit(1) -// ]); -// $this->assertArrayHasKey('name', $documents[0]); -// $this->assertArrayNotHasKey('cities', $documents[0]); -// -// $documents = static::getDatabase()->find('countries', [ -// Query::select(['*']), -// Query::limit(1) -// ]); -// $this->assertArrayHasKey('name', $documents[0]); -// $this->assertArrayNotHasKey('cities', $documents[0]); -// -// $documents = static::getDatabase()->find('countries', [ -// Query::select(['*', 'cities.*', 'cities.mayor.*']), -// Query::limit(1) -// ]); -// -// $this->assertEquals('Mayor 1', $documents[0]['cities'][0]['mayor']['name']); -// -// // Insert docs to cache: -// $country1 = static::getDatabase()->getDocument('countries', 'country1'); -// $mayor1 = static::getDatabase()->getDocument('mayors', 'mayor1'); -// $this->assertEquals('City 1', $mayor1['city']['name']); -// $this->assertEquals('City 1', $country1['cities'][0]['name']); -// -// static::getDatabase()->updateDocument('cities', 'city1', new Document([ -// '$id' => 'city1', -// '$collection' => 'cities', -// 'name' => 'City 1 updated', -// 'mayor' => 'mayor1', // we don't support partial updates at the moment -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// ], -// ])); -// -// $mayor1 = static::getDatabase()->getDocument('mayors', 'mayor1'); -// $country1 = static::getDatabase()->getDocument('countries', 'country1'); -// -// $this->assertEquals('City 1 updated', $mayor1['city']['name']); -// $this->assertEquals('City 1 updated', $country1['cities'][0]['name']); -// $this->assertEquals('city1', $country1['cities'][0]['$id']); -// $this->assertEquals('city2', $country1['cities'][1]['$id']); -// $this->assertEquals('mayor1', $country1['cities'][0]['mayor']['$id']); -// $this->assertEquals('mayor2', $country1['cities'][1]['mayor']['$id']); -// $this->assertArrayNotHasKey('city', $country1['cities'][0]['mayor']); -// $this->assertArrayNotHasKey('city', $country1['cities'][1]['mayor']); -// -// static::getDatabase()->createDocument('mayors', new Document([ -// '$id' => 'mayor3', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Mayor 3', -// 'city' => [ -// '$id' => 'city3', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'City 3', -// 'country' => [ -// '$id' => 'country2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Country 2', -// ], -// ], -// ])); -// -// $country2 = static::getDatabase()->getDocument('countries', 'country2'); -// $this->assertEquals('city3', $country2['cities'][0]['$id']); -// $this->assertEquals('mayor3', $country2['cities'][0]['mayor']['$id']); -// $this->assertArrayNotHasKey('country', $country2['cities'][0]); -// $this->assertArrayNotHasKey('city', $country2['cities'][0]['mayor']); -// } -// -// public function testNestedOneToMany_OneToManyRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('dormitories'); -// static::getDatabase()->createCollection('occupants'); -// static::getDatabase()->createCollection('pets'); -// -// static::getDatabase()->createAttribute('dormitories', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('occupants', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('pets', 'name', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'dormitories', -// relatedCollection: 'occupants', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// twoWayKey: 'dormitory' -// ); -// static::getDatabase()->createRelationship( -// collection: 'occupants', -// relatedCollection: 'pets', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// twoWayKey: 'occupant' -// ); -// -// static::getDatabase()->createDocument('dormitories', new Document([ -// '$id' => 'dormitory1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'House 1', -// 'occupants' => [ -// [ -// '$id' => 'occupant1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Occupant 1', -// 'pets' => [ -// [ -// '$id' => 'pet1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Pet 1', -// ], -// [ -// '$id' => 'pet2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Pet 2', -// ], -// ], -// ], -// [ -// '$id' => 'occupant2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Occupant 2', -// 'pets' => [ -// [ -// '$id' => 'pet3', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Pet 3', -// ], -// [ -// '$id' => 'pet4', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Pet 4', -// ], -// ], -// ], -// ], -// ])); -// -// $dormitory1 = static::getDatabase()->getDocument('dormitories', 'dormitory1'); -// $this->assertEquals('occupant1', $dormitory1['occupants'][0]['$id']); -// $this->assertEquals('occupant2', $dormitory1['occupants'][1]['$id']); -// $this->assertEquals('pet1', $dormitory1['occupants'][0]['pets'][0]['$id']); -// $this->assertEquals('pet2', $dormitory1['occupants'][0]['pets'][1]['$id']); -// $this->assertEquals('pet3', $dormitory1['occupants'][1]['pets'][0]['$id']); -// $this->assertEquals('pet4', $dormitory1['occupants'][1]['pets'][1]['$id']); -// $this->assertArrayNotHasKey('dormitory', $dormitory1['occupants'][0]); -// $this->assertArrayNotHasKey('dormitory', $dormitory1['occupants'][1]); -// $this->assertArrayNotHasKey('occupant', $dormitory1['occupants'][0]['pets'][0]); -// $this->assertArrayNotHasKey('occupant', $dormitory1['occupants'][0]['pets'][1]); -// $this->assertArrayNotHasKey('occupant', $dormitory1['occupants'][1]['pets'][0]); -// $this->assertArrayNotHasKey('occupant', $dormitory1['occupants'][1]['pets'][1]); -// -// static::getDatabase()->createDocument('pets', new Document([ -// '$id' => 'pet5', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Pet 5', -// 'occupant' => [ -// '$id' => 'occupant3', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Occupant 3', -// 'dormitory' => [ -// '$id' => 'dormitory2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'House 2', -// ], -// ], -// ])); -// -// $pet5 = static::getDatabase()->getDocument('pets', 'pet5'); -// $this->assertEquals('occupant3', $pet5['occupant']['$id']); -// $this->assertEquals('dormitory2', $pet5['occupant']['dormitory']['$id']); -// $this->assertArrayNotHasKey('pets', $pet5['occupant']); -// $this->assertArrayNotHasKey('occupant', $pet5['occupant']['dormitory']); -// } -// -// public function testNestedOneToMany_ManyToOneRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('home'); -// static::getDatabase()->createCollection('renters'); -// static::getDatabase()->createCollection('floors'); -// -// static::getDatabase()->createAttribute('home', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('renters', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('floors', 'name', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'home', -// relatedCollection: 'renters', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true -// ); -// static::getDatabase()->createRelationship( -// collection: 'renters', -// relatedCollection: 'floors', -// type: Database::RELATION_MANY_TO_ONE, -// twoWay: true, -// id: 'floor' -// ); -// -// static::getDatabase()->createDocument('home', new Document([ -// '$id' => 'home1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'House 1', -// 'renters' => [ -// [ -// '$id' => 'renter1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Occupant 1', -// 'floor' => [ -// '$id' => 'floor1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Floor 1', -// ], -// ], -// ], -// ])); -// -// $home1 = static::getDatabase()->getDocument('home', 'home1'); -// $this->assertEquals('renter1', $home1['renters'][0]['$id']); -// $this->assertEquals('floor1', $home1['renters'][0]['floor']['$id']); -// $this->assertArrayNotHasKey('home', $home1['renters'][0]); -// $this->assertArrayNotHasKey('renters', $home1['renters'][0]['floor']); -// -// static::getDatabase()->createDocument('floors', new Document([ -// '$id' => 'floor2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Floor 2', -// 'renters' => [ -// [ -// '$id' => 'renter2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Occupant 2', -// 'home' => [ -// '$id' => 'home2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'House 2', -// ], -// ], -// ], -// ])); -// -// $floor2 = static::getDatabase()->getDocument('floors', 'floor2'); -// $this->assertEquals('renter2', $floor2['renters'][0]['$id']); -// $this->assertArrayNotHasKey('floor', $floor2['renters'][0]); -// $this->assertEquals('home2', $floor2['renters'][0]['home']['$id']); -// $this->assertArrayNotHasKey('renter', $floor2['renters'][0]['home']); -// } -// -// public function testNestedOneToMany_ManyToManyRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('owners'); -// static::getDatabase()->createCollection('cats'); -// static::getDatabase()->createCollection('toys'); -// -// static::getDatabase()->createAttribute('owners', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('cats', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('toys', 'name', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'owners', -// relatedCollection: 'cats', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// twoWayKey: 'owner' -// ); -// static::getDatabase()->createRelationship( -// collection: 'cats', -// relatedCollection: 'toys', -// type: Database::RELATION_MANY_TO_MANY, -// twoWay: true -// ); -// -// static::getDatabase()->createDocument('owners', new Document([ -// '$id' => 'owner1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Owner 1', -// 'cats' => [ -// [ -// '$id' => 'cat1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Pet 1', -// 'toys' => [ -// [ -// '$id' => 'toy1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Toy 1', -// ], -// ], -// ], -// ], -// ])); -// -// $owner1 = static::getDatabase()->getDocument('owners', 'owner1'); -// $this->assertEquals('cat1', $owner1['cats'][0]['$id']); -// $this->assertArrayNotHasKey('owner', $owner1['cats'][0]); -// $this->assertEquals('toy1', $owner1['cats'][0]['toys'][0]['$id']); -// $this->assertArrayNotHasKey('cats', $owner1['cats'][0]['toys'][0]); -// -// static::getDatabase()->createDocument('toys', new Document([ -// '$id' => 'toy2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Toy 2', -// 'cats' => [ -// [ -// '$id' => 'cat2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Pet 2', -// 'owner' => [ -// '$id' => 'owner2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Owner 2', -// ], -// ], -// ], -// ])); -// -// $toy2 = static::getDatabase()->getDocument('toys', 'toy2'); -// $this->assertEquals('cat2', $toy2['cats'][0]['$id']); -// $this->assertArrayNotHasKey('toys', $toy2['cats'][0]); -// $this->assertEquals('owner2', $toy2['cats'][0]['owner']['$id']); -// $this->assertArrayNotHasKey('cats', $toy2['cats'][0]['owner']); -// } -// -// public function testNestedManyToOne_OneToOneRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('towns'); -// static::getDatabase()->createCollection('homelands'); -// static::getDatabase()->createCollection('capitals'); -// -// static::getDatabase()->createAttribute('towns', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('homelands', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('capitals', 'name', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'towns', -// relatedCollection: 'homelands', -// type: Database::RELATION_MANY_TO_ONE, -// twoWay: true, -// id: 'homeland' -// ); -// static::getDatabase()->createRelationship( -// collection: 'homelands', -// relatedCollection: 'capitals', -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// id: 'capital', -// twoWayKey: 'homeland' -// ); -// -// static::getDatabase()->createDocument('towns', new Document([ -// '$id' => 'town1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'City 1', -// 'homeland' => [ -// '$id' => 'homeland1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Country 1', -// 'capital' => [ -// '$id' => 'capital1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Flag 1', -// ], -// ], -// ])); -// -// $town1 = static::getDatabase()->getDocument('towns', 'town1'); -// $this->assertEquals('homeland1', $town1['homeland']['$id']); -// $this->assertArrayNotHasKey('towns', $town1['homeland']); -// $this->assertEquals('capital1', $town1['homeland']['capital']['$id']); -// $this->assertArrayNotHasKey('homeland', $town1['homeland']['capital']); -// -// static::getDatabase()->createDocument('capitals', new Document([ -// '$id' => 'capital2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Flag 2', -// 'homeland' => [ -// '$id' => 'homeland2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Country 2', -// 'towns' => [ -// [ -// '$id' => 'town2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Town 2', -// ], -// [ -// '$id' => 'town3', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Town 3', -// ], -// ], -// ], -// ])); -// -// $capital2 = static::getDatabase()->getDocument('capitals', 'capital2'); -// $this->assertEquals('homeland2', $capital2['homeland']['$id']); -// $this->assertArrayNotHasKey('capital', $capital2['homeland']); -// $this->assertEquals(2, \count($capital2['homeland']['towns'])); -// $this->assertEquals('town2', $capital2['homeland']['towns'][0]['$id']); -// $this->assertEquals('town3', $capital2['homeland']['towns'][1]['$id']); -// } -// -// public function testNestedManyToOne_OneToManyRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('players'); -// static::getDatabase()->createCollection('teams'); -// static::getDatabase()->createCollection('supporters'); -// -// static::getDatabase()->createAttribute('players', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('teams', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('supporters', 'name', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'players', -// relatedCollection: 'teams', -// type: Database::RELATION_MANY_TO_ONE, -// twoWay: true, -// id: 'team' -// ); -// static::getDatabase()->createRelationship( -// collection: 'teams', -// relatedCollection: 'supporters', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// id: 'supporters', -// twoWayKey: 'team' -// ); -// -// static::getDatabase()->createDocument('players', new Document([ -// '$id' => 'player1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Player 1', -// 'team' => [ -// '$id' => 'team1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Team 1', -// 'supporters' => [ -// [ -// '$id' => 'supporter1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Supporter 1', -// ], -// [ -// '$id' => 'supporter2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Supporter 2', -// ], -// ], -// ], -// ])); -// -// $player1 = static::getDatabase()->getDocument('players', 'player1'); -// $this->assertEquals('team1', $player1['team']['$id']); -// $this->assertArrayNotHasKey('players', $player1['team']); -// $this->assertEquals(2, \count($player1['team']['supporters'])); -// $this->assertEquals('supporter1', $player1['team']['supporters'][0]['$id']); -// $this->assertEquals('supporter2', $player1['team']['supporters'][1]['$id']); -// -// static::getDatabase()->createDocument('supporters', new Document([ -// '$id' => 'supporter3', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Supporter 3', -// 'team' => [ -// '$id' => 'team2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Team 2', -// 'players' => [ -// [ -// '$id' => 'player2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Player 2', -// ], -// [ -// '$id' => 'player3', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Player 3', -// ], -// ], -// ], -// ])); -// -// $supporter3 = static::getDatabase()->getDocument('supporters', 'supporter3'); -// $this->assertEquals('team2', $supporter3['team']['$id']); -// $this->assertArrayNotHasKey('supporters', $supporter3['team']); -// $this->assertEquals(2, \count($supporter3['team']['players'])); -// $this->assertEquals('player2', $supporter3['team']['players'][0]['$id']); -// $this->assertEquals('player3', $supporter3['team']['players'][1]['$id']); -// } -// -// public function testNestedManyToOne_ManyToOne(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('cows'); -// static::getDatabase()->createCollection('farms'); -// static::getDatabase()->createCollection('farmer'); -// -// static::getDatabase()->createAttribute('cows', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('farms', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('farmer', 'name', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'cows', -// relatedCollection: 'farms', -// type: Database::RELATION_MANY_TO_ONE, -// twoWay: true, -// id: 'farm' -// ); -// static::getDatabase()->createRelationship( -// collection: 'farms', -// relatedCollection: 'farmer', -// type: Database::RELATION_MANY_TO_ONE, -// twoWay: true, -// id: 'farmer' -// ); -// -// static::getDatabase()->createDocument('cows', new Document([ -// '$id' => 'cow1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Cow 1', -// 'farm' => [ -// '$id' => 'farm1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Farm 1', -// 'farmer' => [ -// '$id' => 'farmer1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Farmer 1', -// ], -// ], -// ])); -// -// $cow1 = static::getDatabase()->getDocument('cows', 'cow1'); -// $this->assertEquals('farm1', $cow1['farm']['$id']); -// $this->assertArrayNotHasKey('cows', $cow1['farm']); -// $this->assertEquals('farmer1', $cow1['farm']['farmer']['$id']); -// $this->assertArrayNotHasKey('farms', $cow1['farm']['farmer']); -// -// static::getDatabase()->createDocument('farmer', new Document([ -// '$id' => 'farmer2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Farmer 2', -// 'farms' => [ -// [ -// '$id' => 'farm2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Farm 2', -// 'cows' => [ -// [ -// '$id' => 'cow2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Cow 2', -// ], -// [ -// '$id' => 'cow3', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Cow 3', -// ], -// ], -// ], -// ], -// ])); -// -// $farmer2 = static::getDatabase()->getDocument('farmer', 'farmer2'); -// $this->assertEquals('farm2', $farmer2['farms'][0]['$id']); -// $this->assertArrayNotHasKey('farmer', $farmer2['farms'][0]); -// $this->assertEquals(2, \count($farmer2['farms'][0]['cows'])); -// $this->assertEquals('cow2', $farmer2['farms'][0]['cows'][0]['$id']); -// $this->assertEquals('cow3', $farmer2['farms'][0]['cows'][1]['$id']); -// } -// -// public function testNestedManyToOne_ManyToManyRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('books'); -// static::getDatabase()->createCollection('entrants'); -// static::getDatabase()->createCollection('rooms'); -// -// static::getDatabase()->createAttribute('books', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('entrants', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('rooms', 'name', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'books', -// relatedCollection: 'entrants', -// type: Database::RELATION_MANY_TO_ONE, -// twoWay: true, -// id: 'entrant' -// ); -// static::getDatabase()->createRelationship( -// collection: 'entrants', -// relatedCollection: 'rooms', -// type: Database::RELATION_MANY_TO_MANY, -// twoWay: true, -// ); -// -// static::getDatabase()->createDocument('books', new Document([ -// '$id' => 'book1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Book 1', -// 'entrant' => [ -// '$id' => 'entrant1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Entrant 1', -// 'rooms' => [ -// [ -// '$id' => 'class1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Class 1', -// ], -// [ -// '$id' => 'class2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Class 2', -// ], -// ], -// ], -// ])); -// -// $book1 = static::getDatabase()->getDocument('books', 'book1'); -// $this->assertEquals('entrant1', $book1['entrant']['$id']); -// $this->assertArrayNotHasKey('books', $book1['entrant']); -// $this->assertEquals(2, \count($book1['entrant']['rooms'])); -// $this->assertEquals('class1', $book1['entrant']['rooms'][0]['$id']); -// $this->assertEquals('class2', $book1['entrant']['rooms'][1]['$id']); -// } -// -// public function testNestedManyToMany_OneToOneRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('stones'); -// static::getDatabase()->createCollection('hearths'); -// static::getDatabase()->createCollection('plots'); -// -// static::getDatabase()->createAttribute('stones', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('hearths', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('plots', 'name', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'stones', -// relatedCollection: 'hearths', -// type: Database::RELATION_MANY_TO_MANY, -// twoWay: true, -// ); -// static::getDatabase()->createRelationship( -// collection: 'hearths', -// relatedCollection: 'plots', -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// id: 'plot', -// twoWayKey: 'hearth' -// ); -// -// static::getDatabase()->createDocument('stones', new Document([ -// '$id' => 'stone1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Building 1', -// 'hearths' => [ -// [ -// '$id' => 'hearth1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'House 1', -// 'plot' => [ -// '$id' => 'plot1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Address 1', -// ], -// ], -// [ -// '$id' => 'hearth2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'House 2', -// 'plot' => [ -// '$id' => 'plot2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Address 2', -// ], -// ], -// ], -// ])); -// -// $stone1 = static::getDatabase()->getDocument('stones', 'stone1'); -// $this->assertEquals(2, \count($stone1['hearths'])); -// $this->assertEquals('hearth1', $stone1['hearths'][0]['$id']); -// $this->assertEquals('hearth2', $stone1['hearths'][1]['$id']); -// $this->assertArrayNotHasKey('stone', $stone1['hearths'][0]); -// $this->assertEquals('plot1', $stone1['hearths'][0]['plot']['$id']); -// $this->assertEquals('plot2', $stone1['hearths'][1]['plot']['$id']); -// $this->assertArrayNotHasKey('hearth', $stone1['hearths'][0]['plot']); -// -// static::getDatabase()->createDocument('plots', new Document([ -// '$id' => 'plot3', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Address 3', -// 'hearth' => [ -// '$id' => 'hearth3', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Hearth 3', -// 'stones' => [ -// [ -// '$id' => 'stone2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Stone 2', -// ], -// ], -// ], -// ])); -// -// $plot3 = static::getDatabase()->getDocument('plots', 'plot3'); -// $this->assertEquals('hearth3', $plot3['hearth']['$id']); -// $this->assertArrayNotHasKey('plot', $plot3['hearth']); -// $this->assertEquals('stone2', $plot3['hearth']['stones'][0]['$id']); -// $this->assertArrayNotHasKey('hearths', $plot3['hearth']['stones'][0]); -// } -// -// public function testNestedManyToMany_OneToManyRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('groups'); -// static::getDatabase()->createCollection('tounaments'); -// static::getDatabase()->createCollection('prizes'); -// -// static::getDatabase()->createAttribute('groups', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('tounaments', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('prizes', 'name', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'groups', -// relatedCollection: 'tounaments', -// type: Database::RELATION_MANY_TO_MANY, -// twoWay: true, -// ); -// static::getDatabase()->createRelationship( -// collection: 'tounaments', -// relatedCollection: 'prizes', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// id: 'prizes', -// twoWayKey: 'tounament' -// ); -// -// static::getDatabase()->createDocument('groups', new Document([ -// '$id' => 'group1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Group 1', -// 'tounaments' => [ -// [ -// '$id' => 'tounament1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Tounament 1', -// 'prizes' => [ -// [ -// '$id' => 'prize1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Prize 1', -// ], -// [ -// '$id' => 'prize2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Prize 2', -// ], -// ], -// ], -// [ -// '$id' => 'tounament2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Tounament 2', -// 'prizes' => [ -// [ -// '$id' => 'prize3', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Prize 3', -// ], -// [ -// '$id' => 'prize4', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Prize 4', -// ], -// ], -// ], -// ], -// ])); -// -// $group1 = static::getDatabase()->getDocument('groups', 'group1'); -// $this->assertEquals(2, \count($group1['tounaments'])); -// $this->assertEquals('tounament1', $group1['tounaments'][0]['$id']); -// $this->assertEquals('tounament2', $group1['tounaments'][1]['$id']); -// $this->assertArrayNotHasKey('group', $group1['tounaments'][0]); -// $this->assertEquals(2, \count($group1['tounaments'][0]['prizes'])); -// $this->assertEquals('prize1', $group1['tounaments'][0]['prizes'][0]['$id']); -// $this->assertEquals('prize2', $group1['tounaments'][0]['prizes'][1]['$id']); -// $this->assertArrayNotHasKey('tounament', $group1['tounaments'][0]['prizes'][0]); -// } -// -// public function testNestedManyToMany_ManyToOneRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('platforms'); -// static::getDatabase()->createCollection('games'); -// static::getDatabase()->createCollection('publishers'); -// -// static::getDatabase()->createAttribute('platforms', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('games', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('publishers', 'name', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'platforms', -// relatedCollection: 'games', -// type: Database::RELATION_MANY_TO_MANY, -// twoWay: true, -// ); -// static::getDatabase()->createRelationship( -// collection: 'games', -// relatedCollection: 'publishers', -// type: Database::RELATION_MANY_TO_ONE, -// twoWay: true, -// id: 'publisher', -// twoWayKey: 'games' -// ); -// -// static::getDatabase()->createDocument('platforms', new Document([ -// '$id' => 'platform1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Platform 1', -// 'games' => [ -// [ -// '$id' => 'game1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Game 1', -// 'publisher' => [ -// '$id' => 'publisher1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Publisher 1', -// ], -// ], -// [ -// '$id' => 'game2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Game 2', -// 'publisher' => [ -// '$id' => 'publisher2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Publisher 2', -// ], -// ], -// ] -// ])); -// -// $platform1 = static::getDatabase()->getDocument('platforms', 'platform1'); -// $this->assertEquals(2, \count($platform1['games'])); -// $this->assertEquals('game1', $platform1['games'][0]['$id']); -// $this->assertEquals('game2', $platform1['games'][1]['$id']); -// $this->assertArrayNotHasKey('platforms', $platform1['games'][0]); -// $this->assertEquals('publisher1', $platform1['games'][0]['publisher']['$id']); -// $this->assertEquals('publisher2', $platform1['games'][1]['publisher']['$id']); -// $this->assertArrayNotHasKey('games', $platform1['games'][0]['publisher']); -// -// static::getDatabase()->createDocument('publishers', new Document([ -// '$id' => 'publisher3', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Publisher 3', -// 'games' => [ -// [ -// '$id' => 'game3', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Game 3', -// 'platforms' => [ -// [ -// '$id' => 'platform2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Platform 2', -// ] -// ], -// ], -// ], -// ])); -// -// $publisher3 = static::getDatabase()->getDocument('publishers', 'publisher3'); -// $this->assertEquals(1, \count($publisher3['games'])); -// $this->assertEquals('game3', $publisher3['games'][0]['$id']); -// $this->assertArrayNotHasKey('publisher', $publisher3['games'][0]); -// $this->assertEquals('platform2', $publisher3['games'][0]['platforms'][0]['$id']); -// $this->assertArrayNotHasKey('games', $publisher3['games'][0]['platforms'][0]); -// } -// -// public function testNestedManyToMany_ManyToManyRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('sauces'); -// static::getDatabase()->createCollection('pizzas'); -// static::getDatabase()->createCollection('toppings'); -// -// static::getDatabase()->createAttribute('sauces', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('pizzas', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('toppings', 'name', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'sauces', -// relatedCollection: 'pizzas', -// type: Database::RELATION_MANY_TO_MANY, -// twoWay: true, -// ); -// static::getDatabase()->createRelationship( -// collection: 'pizzas', -// relatedCollection: 'toppings', -// type: Database::RELATION_MANY_TO_MANY, -// twoWay: true, -// id: 'toppings', -// twoWayKey: 'pizzas' -// ); -// -// static::getDatabase()->createDocument('sauces', new Document([ -// '$id' => 'sauce1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Sauce 1', -// 'pizzas' => [ -// [ -// '$id' => 'pizza1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Pizza 1', -// 'toppings' => [ -// [ -// '$id' => 'topping1', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Topping 1', -// ], -// [ -// '$id' => 'topping2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Topping 2', -// ], -// ], -// ], -// [ -// '$id' => 'pizza2', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Pizza 2', -// 'toppings' => [ -// [ -// '$id' => 'topping3', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Topping 3', -// ], -// [ -// '$id' => 'topping4', -// '$permissions' => [ -// Permission::read(Role::any()), -// ], -// 'name' => 'Topping 4', -// ], -// ], -// ], -// ] -// ])); -// -// $sauce1 = static::getDatabase()->getDocument('sauces', 'sauce1'); -// $this->assertEquals(2, \count($sauce1['pizzas'])); -// $this->assertEquals('pizza1', $sauce1['pizzas'][0]['$id']); -// $this->assertEquals('pizza2', $sauce1['pizzas'][1]['$id']); -// $this->assertArrayNotHasKey('sauces', $sauce1['pizzas'][0]); -// $this->assertEquals(2, \count($sauce1['pizzas'][0]['toppings'])); -// $this->assertEquals('topping1', $sauce1['pizzas'][0]['toppings'][0]['$id']); -// $this->assertEquals('topping2', $sauce1['pizzas'][0]['toppings'][1]['$id']); -// $this->assertArrayNotHasKey('pizzas', $sauce1['pizzas'][0]['toppings'][0]); -// $this->assertEquals(2, \count($sauce1['pizzas'][1]['toppings'])); -// $this->assertEquals('topping3', $sauce1['pizzas'][1]['toppings'][0]['$id']); -// $this->assertEquals('topping4', $sauce1['pizzas'][1]['toppings'][1]['$id']); -// $this->assertArrayNotHasKey('pizzas', $sauce1['pizzas'][1]['toppings'][0]); -// } -// -// public function testInheritRelationshipPermissions(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('lawns', permissions: [Permission::create(Role::any())], documentSecurity: true); -// static::getDatabase()->createCollection('trees', permissions: [Permission::create(Role::any())], documentSecurity: true); -// static::getDatabase()->createCollection('birds', permissions: [Permission::create(Role::any())], documentSecurity: true); -// -// static::getDatabase()->createAttribute('lawns', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('trees', 'name', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('birds', 'name', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'lawns', -// relatedCollection: 'trees', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// twoWayKey: 'lawn', -// onDelete: Database::RELATION_MUTATE_CASCADE, -// ); -// static::getDatabase()->createRelationship( -// collection: 'trees', -// relatedCollection: 'birds', -// type: Database::RELATION_MANY_TO_MANY, -// twoWay: true, -// onDelete: Database::RELATION_MUTATE_SET_NULL, -// ); -// -// $permissions = [ -// Permission::read(Role::any()), -// Permission::read(Role::user('user1')), -// Permission::update(Role::user('user1')), -// Permission::delete(Role::user('user2')), -// ]; -// -// static::getDatabase()->createDocument('lawns', new Document([ -// '$id' => 'lawn1', -// '$permissions' => $permissions, -// 'name' => 'Lawn 1', -// 'trees' => [ -// [ -// '$id' => 'tree1', -// 'name' => 'Tree 1', -// 'birds' => [ -// [ -// '$id' => 'bird1', -// 'name' => 'Bird 1', -// ], -// [ -// '$id' => 'bird2', -// 'name' => 'Bird 2', -// ], -// ], -// ], -// ], -// ])); -// -// $lawn1 = static::getDatabase()->getDocument('lawns', 'lawn1'); -// $this->assertEquals($permissions, $lawn1->getPermissions()); -// $this->assertEquals($permissions, $lawn1['trees'][0]->getPermissions()); -// $this->assertEquals($permissions, $lawn1['trees'][0]['birds'][0]->getPermissions()); -// $this->assertEquals($permissions, $lawn1['trees'][0]['birds'][1]->getPermissions()); -// -// $tree1 = static::getDatabase()->getDocument('trees', 'tree1'); -// $this->assertEquals($permissions, $tree1->getPermissions()); -// $this->assertEquals($permissions, $tree1['lawn']->getPermissions()); -// $this->assertEquals($permissions, $tree1['birds'][0]->getPermissions()); -// $this->assertEquals($permissions, $tree1['birds'][1]->getPermissions()); -// } -// -// /** -// * @depends testInheritRelationshipPermissions -// */ -// public function testEnforceRelationshipPermissions(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// Authorization::cleanRoles(); -// Authorization::setRole(Role::any()->toString()); -// $lawn1 = static::getDatabase()->getDocument('lawns', 'lawn1'); -// $this->assertEquals('Lawn 1', $lawn1['name']); -// -// // Try update root document -// try { -// static::getDatabase()->updateDocument( -// 'lawns', -// $lawn1->getId(), -// $lawn1->setAttribute('name', 'Lawn 1 Updated') -// ); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertEquals('Missing "update" permission for role "user:user1". Only "["any"]" scopes are allowed and "["user:user1"]" was given.', $e->getMessage()); -// } -// -// // Try delete root document -// try { -// static::getDatabase()->deleteDocument( -// 'lawns', -// $lawn1->getId(), -// ); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertEquals('Missing "delete" permission for role "user:user2". Only "["any"]" scopes are allowed and "["user:user2"]" was given.', $e->getMessage()); -// } -// -// $tree1 = static::getDatabase()->getDocument('trees', 'tree1'); -// -// // Try update nested document -// try { -// static::getDatabase()->updateDocument( -// 'trees', -// $tree1->getId(), -// $tree1->setAttribute('name', 'Tree 1 Updated') -// ); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertEquals('Missing "update" permission for role "user:user1". Only "["any"]" scopes are allowed and "["user:user1"]" was given.', $e->getMessage()); -// } -// -// // Try delete nested document -// try { -// static::getDatabase()->deleteDocument( -// 'trees', -// $tree1->getId(), -// ); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertEquals('Missing "delete" permission for role "user:user2". Only "["any"]" scopes are allowed and "["user:user2"]" was given.', $e->getMessage()); -// } -// -// $bird1 = static::getDatabase()->getDocument('birds', 'bird1'); -// -// // Try update multi-level nested document -// try { -// static::getDatabase()->updateDocument( -// 'birds', -// $bird1->getId(), -// $bird1->setAttribute('name', 'Bird 1 Updated') -// ); -// $this->fail('Failed to throw exception when updating document with missing permissions'); -// } catch (Exception $e) { -// $this->assertEquals('Missing "update" permission for role "user:user1". Only "["any"]" scopes are allowed and "["user:user1"]" was given.', $e->getMessage()); -// } -// -// // Try delete multi-level nested document -// try { -// static::getDatabase()->deleteDocument( -// 'birds', -// $bird1->getId(), -// ); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertEquals('Missing "delete" permission for role "user:user2". Only "["any"]" scopes are allowed and "["user:user2"]" was given.', $e->getMessage()); -// } -// -// Authorization::setRole(Role::user('user1')->toString()); -// -// $bird1 = static::getDatabase()->getDocument('birds', 'bird1'); -// -// // Try update multi-level nested document -// $bird1 = static::getDatabase()->updateDocument( -// 'birds', -// $bird1->getId(), -// $bird1->setAttribute('name', 'Bird 1 Updated') -// ); -// -// $this->assertEquals('Bird 1 Updated', $bird1['name']); -// -// Authorization::setRole(Role::user('user2')->toString()); -// -// // Try delete multi-level nested document -// $deleted = static::getDatabase()->deleteDocument( -// 'birds', -// $bird1->getId(), -// ); -// -// $this->assertEquals(true, $deleted); -// $tree1 = static::getDatabase()->getDocument('trees', 'tree1'); -// $this->assertEquals(1, count($tree1['birds'])); -// -// // Try update nested document -// $tree1 = static::getDatabase()->updateDocument( -// 'trees', -// $tree1->getId(), -// $tree1->setAttribute('name', 'Tree 1 Updated') -// ); -// -// $this->assertEquals('Tree 1 Updated', $tree1['name']); -// -// // Try delete nested document -// $deleted = static::getDatabase()->deleteDocument( -// 'trees', -// $tree1->getId(), -// ); -// -// $this->assertEquals(true, $deleted); -// $lawn1 = static::getDatabase()->getDocument('lawns', 'lawn1'); -// $this->assertEquals(0, count($lawn1['trees'])); -// -// // Create document with no permissions -// static::getDatabase()->createDocument('lawns', new Document([ -// '$id' => 'lawn2', -// 'name' => 'Lawn 2', -// 'trees' => [ -// [ -// '$id' => 'tree2', -// 'name' => 'Tree 2', -// 'birds' => [ -// [ -// '$id' => 'bird3', -// 'name' => 'Bird 3', -// ], -// ], -// ], -// ], -// ])); -// -// $lawn2 = static::getDatabase()->getDocument('lawns', 'lawn2'); -// $this->assertEquals(true, $lawn2->isEmpty()); -// -// $tree2 = static::getDatabase()->getDocument('trees', 'tree2'); -// $this->assertEquals(true, $tree2->isEmpty()); -// -// $bird3 = static::getDatabase()->getDocument('birds', 'bird3'); -// $this->assertEquals(true, $bird3->isEmpty()); -// } -// -// public function testExceedMaxDepthOneToMany(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// $level1Collection = 'level1OneToMany'; -// $level2Collection = 'level2OneToMany'; -// $level3Collection = 'level3OneToMany'; -// $level4Collection = 'level4OneToMany'; -// -// static::getDatabase()->createCollection($level1Collection); -// static::getDatabase()->createCollection($level2Collection); -// static::getDatabase()->createCollection($level3Collection); -// static::getDatabase()->createCollection($level4Collection); -// -// static::getDatabase()->createRelationship( -// collection: $level1Collection, -// relatedCollection: $level2Collection, -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// ); -// static::getDatabase()->createRelationship( -// collection: $level2Collection, -// relatedCollection: $level3Collection, -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// ); -// static::getDatabase()->createRelationship( -// collection: $level3Collection, -// relatedCollection: $level4Collection, -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// ); -// -// // Exceed create depth -// $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ -// '$id' => 'level1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// ], -// $level2Collection => [ -// [ -// '$id' => 'level2', -// $level3Collection => [ -// [ -// '$id' => 'level3', -// $level4Collection => [ -// [ -// '$id' => 'level4', -// ], -// ], -// ], -// ], -// ], -// ], -// ])); -// $this->assertEquals(1, count($level1[$level2Collection])); -// $this->assertEquals('level2', $level1[$level2Collection][0]->getId()); -// $this->assertEquals(1, count($level1[$level2Collection][0][$level3Collection])); -// $this->assertEquals('level3', $level1[$level2Collection][0][$level3Collection][0]->getId()); -// $this->assertArrayNotHasKey('level4', $level1[$level2Collection][0][$level3Collection][0]); -// -// // Make sure level 4 document was not created -// $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); -// $this->assertEquals(0, count($level3[$level4Collection])); -// $level4 = static::getDatabase()->getDocument($level4Collection, 'level4'); -// $this->assertTrue($level4->isEmpty()); -// -// // Exceed fetch depth -// $level1 = static::getDatabase()->getDocument($level1Collection, 'level1'); -// $this->assertEquals(1, count($level1[$level2Collection])); -// $this->assertEquals('level2', $level1[$level2Collection][0]->getId()); -// $this->assertEquals(1, count($level1[$level2Collection][0][$level3Collection])); -// $this->assertEquals('level3', $level1[$level2Collection][0][$level3Collection][0]->getId()); -// $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][0][$level3Collection][0]); -// -// -// // Exceed update depth -// $level1 = static::getDatabase()->updateDocument( -// $level1Collection, -// 'level1', -// $level1 -// ->setAttribute($level2Collection, [new Document([ -// '$id' => 'level2new', -// $level3Collection => [ -// [ -// '$id' => 'level3new', -// $level4Collection => [ -// [ -// '$id' => 'level4new', -// ], -// ], -// ], -// ], -// ])]) -// ); -// $this->assertEquals(1, count($level1[$level2Collection])); -// $this->assertEquals('level2new', $level1[$level2Collection][0]->getId()); -// $this->assertEquals(1, count($level1[$level2Collection][0][$level3Collection])); -// $this->assertEquals('level3new', $level1[$level2Collection][0][$level3Collection][0]->getId()); -// $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][0][$level3Collection][0]); -// -// // Make sure level 4 document was not created -// $level3 = static::getDatabase()->getDocument($level3Collection, 'level3new'); -// $this->assertEquals(0, count($level3[$level4Collection])); -// $level4 = static::getDatabase()->getDocument($level4Collection, 'level4new'); -// $this->assertTrue($level4->isEmpty()); -// } -// -// public function testExceedMaxDepthOneToOne(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// $level1Collection = 'level1OneToOne'; -// $level2Collection = 'level2OneToOne'; -// $level3Collection = 'level3OneToOne'; -// $level4Collection = 'level4OneToOne'; -// -// static::getDatabase()->createCollection($level1Collection); -// static::getDatabase()->createCollection($level2Collection); -// static::getDatabase()->createCollection($level3Collection); -// static::getDatabase()->createCollection($level4Collection); -// -// static::getDatabase()->createRelationship( -// collection: $level1Collection, -// relatedCollection: $level2Collection, -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// ); -// static::getDatabase()->createRelationship( -// collection: $level2Collection, -// relatedCollection: $level3Collection, -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// ); -// static::getDatabase()->createRelationship( -// collection: $level3Collection, -// relatedCollection: $level4Collection, -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// ); -// -// // Exceed create depth -// $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ -// '$id' => 'level1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// ], -// $level2Collection => [ -// '$id' => 'level2', -// $level3Collection => [ -// '$id' => 'level3', -// $level4Collection => [ -// '$id' => 'level4', -// ], -// ], -// ], -// ])); -// $this->assertArrayHasKey($level2Collection, $level1); -// $this->assertEquals('level2', $level1[$level2Collection]->getId()); -// $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); -// $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); -// $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); -// -// // Confirm the 4th level document does not exist -// $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); -// $this->assertNull($level3[$level4Collection]); -// -// // Create level 4 document -// $level3->setAttribute($level4Collection, new Document([ -// '$id' => 'level4', -// ])); -// $level3 = static::getDatabase()->updateDocument($level3Collection, $level3->getId(), $level3); -// $this->assertEquals('level4', $level3[$level4Collection]->getId()); -// -// // Exceed fetch depth -// $level1 = static::getDatabase()->getDocument($level1Collection, 'level1'); -// $this->assertArrayHasKey($level2Collection, $level1); -// $this->assertEquals('level2', $level1[$level2Collection]->getId()); -// $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); -// $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); -// $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); -// } -// -// public function testExceedMaxDepthOneToOneNull(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// $level1Collection = 'level1OneToOneNull'; -// $level2Collection = 'level2OneToOneNull'; -// $level3Collection = 'level3OneToOneNull'; -// $level4Collection = 'level4OneToOneNull'; -// -// static::getDatabase()->createCollection($level1Collection); -// static::getDatabase()->createCollection($level2Collection); -// static::getDatabase()->createCollection($level3Collection); -// static::getDatabase()->createCollection($level4Collection); -// -// static::getDatabase()->createRelationship( -// collection: $level1Collection, -// relatedCollection: $level2Collection, -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// ); -// static::getDatabase()->createRelationship( -// collection: $level2Collection, -// relatedCollection: $level3Collection, -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// ); -// static::getDatabase()->createRelationship( -// collection: $level3Collection, -// relatedCollection: $level4Collection, -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// ); -// -// $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ -// '$id' => 'level1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// ], -// $level2Collection => [ -// '$id' => 'level2', -// $level3Collection => [ -// '$id' => 'level3', -// $level4Collection => [ -// '$id' => 'level4', -// ], -// ], -// ], -// ])); -// $this->assertArrayHasKey($level2Collection, $level1); -// $this->assertEquals('level2', $level1[$level2Collection]->getId()); -// $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); -// $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); -// $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); -// -// // Confirm the 4th level document does not exist -// $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); -// $this->assertNull($level3[$level4Collection]); -// -// // Create level 4 document -// $level3->setAttribute($level4Collection, new Document([ -// '$id' => 'level4', -// ])); -// $level3 = static::getDatabase()->updateDocument($level3Collection, $level3->getId(), $level3); -// $this->assertEquals('level4', $level3[$level4Collection]->getId()); -// $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); -// $this->assertEquals('level4', $level3[$level4Collection]->getId()); -// -// // Exceed fetch depth -// $level1 = static::getDatabase()->getDocument($level1Collection, 'level1'); -// $this->assertArrayHasKey($level2Collection, $level1); -// $this->assertEquals('level2', $level1[$level2Collection]->getId()); -// $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); -// $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); -// $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); -// } -// -// public function testExceedMaxDepthManyToOneParent(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// $level1Collection = 'level1ManyToOneParent'; -// $level2Collection = 'level2ManyToOneParent'; -// $level3Collection = 'level3ManyToOneParent'; -// $level4Collection = 'level4ManyToOneParent'; -// -// static::getDatabase()->createCollection($level1Collection); -// static::getDatabase()->createCollection($level2Collection); -// static::getDatabase()->createCollection($level3Collection); -// static::getDatabase()->createCollection($level4Collection); -// -// static::getDatabase()->createRelationship( -// collection: $level1Collection, -// relatedCollection: $level2Collection, -// type: Database::RELATION_MANY_TO_ONE, -// twoWay: true, -// ); -// static::getDatabase()->createRelationship( -// collection: $level2Collection, -// relatedCollection: $level3Collection, -// type: Database::RELATION_MANY_TO_ONE, -// twoWay: true, -// ); -// static::getDatabase()->createRelationship( -// collection: $level3Collection, -// relatedCollection: $level4Collection, -// type: Database::RELATION_MANY_TO_ONE, -// twoWay: true, -// ); -// -// $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ -// '$id' => 'level1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// ], -// $level2Collection => [ -// '$id' => 'level2', -// $level3Collection => [ -// '$id' => 'level3', -// $level4Collection => [ -// '$id' => 'level4', -// ], -// ], -// ], -// ])); -// $this->assertArrayHasKey($level2Collection, $level1); -// $this->assertEquals('level2', $level1[$level2Collection]->getId()); -// $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); -// $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); -// $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); -// -// // Confirm the 4th level document does not exist -// $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); -// $this->assertNull($level3[$level4Collection]); -// -// // Create level 4 document -// $level3->setAttribute($level4Collection, new Document([ -// '$id' => 'level4', -// ])); -// $level3 = static::getDatabase()->updateDocument($level3Collection, $level3->getId(), $level3); -// $this->assertEquals('level4', $level3[$level4Collection]->getId()); -// $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); -// $this->assertEquals('level4', $level3[$level4Collection]->getId()); -// -// // Exceed fetch depth -// $level1 = static::getDatabase()->getDocument($level1Collection, 'level1'); -// $this->assertArrayHasKey($level2Collection, $level1); -// $this->assertEquals('level2', $level1[$level2Collection]->getId()); -// $this->assertArrayHasKey($level3Collection, $level1[$level2Collection]); -// $this->assertEquals('level3', $level1[$level2Collection][$level3Collection]->getId()); -// $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][$level3Collection]); -// } -// -// public function testExceedMaxDepthOneToManyChild(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// $level1Collection = 'level1OneToManyChild'; -// $level2Collection = 'level2OneToManyChild'; -// $level3Collection = 'level3OneToManyChild'; -// $level4Collection = 'level4OneToManyChild'; -// -// static::getDatabase()->createCollection($level1Collection); -// static::getDatabase()->createCollection($level2Collection); -// static::getDatabase()->createCollection($level3Collection); -// static::getDatabase()->createCollection($level4Collection); -// -// static::getDatabase()->createRelationship( -// collection: $level1Collection, -// relatedCollection: $level2Collection, -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// ); -// static::getDatabase()->createRelationship( -// collection: $level2Collection, -// relatedCollection: $level3Collection, -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// ); -// static::getDatabase()->createRelationship( -// collection: $level3Collection, -// relatedCollection: $level4Collection, -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// ); -// -// $level1 = static::getDatabase()->createDocument($level1Collection, new Document([ -// '$id' => 'level1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// ], -// $level2Collection => [ -// [ -// '$id' => 'level2', -// $level3Collection => [ -// [ -// '$id' => 'level3', -// $level4Collection => [ -// [ -// '$id' => 'level4', -// ], -// ] -// ], -// ], -// ], -// ], -// ])); -// $this->assertArrayHasKey($level2Collection, $level1); -// $this->assertEquals('level2', $level1[$level2Collection][0]->getId()); -// $this->assertArrayHasKey($level3Collection, $level1[$level2Collection][0]); -// $this->assertEquals('level3', $level1[$level2Collection][0][$level3Collection][0]->getId()); -// $this->assertArrayNotHasKey($level4Collection, $level1[$level2Collection][0][$level3Collection][0]); -// -// // Confirm the 4th level document does not exist -// $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); -// $this->assertEquals(0, count($level3[$level4Collection])); -// -// // Create level 4 document -// $level3->setAttribute($level4Collection, [new Document([ -// '$id' => 'level4', -// ])]); -// $level3 = static::getDatabase()->updateDocument($level3Collection, $level3->getId(), $level3); -// $this->assertEquals('level4', $level3[$level4Collection][0]->getId()); -// -// // Verify level 4 document is set -// $level3 = static::getDatabase()->getDocument($level3Collection, 'level3'); -// $this->assertArrayHasKey($level4Collection, $level3); -// $this->assertEquals('level4', $level3[$level4Collection][0]->getId()); -// -// // Exceed fetch depth -// $level4 = static::getDatabase()->getDocument($level4Collection, 'level4'); -// $this->assertArrayHasKey($level3Collection, $level4); -// $this->assertEquals('level3', $level4[$level3Collection]->getId()); -// $this->assertArrayHasKey($level2Collection, $level4[$level3Collection]); -// $this->assertEquals('level2', $level4[$level3Collection][$level2Collection]->getId()); -// $this->assertArrayNotHasKey($level1Collection, $level4[$level3Collection][$level2Collection]); -// } -// -// public function testCreateRelationshipMissingCollection(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// $this->expectException(Exception::class); -// $this->expectExceptionMessage('Collection not found'); -// -// static::getDatabase()->createRelationship( -// collection: 'missing', -// relatedCollection: 'missing', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// ); -// } -// -// public function testCreateRelationshipMissingRelatedCollection(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('test'); -// -// $this->expectException(Exception::class); -// $this->expectExceptionMessage('Related collection not found'); -// -// static::getDatabase()->createRelationship( -// collection: 'test', -// relatedCollection: 'missing', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// ); -// } -// -// public function testCreateDuplicateRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('test1'); -// static::getDatabase()->createCollection('test2'); -// -// static::getDatabase()->createRelationship( -// collection: 'test1', -// relatedCollection: 'test2', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// ); -// -// $this->expectException(Exception::class); -// $this->expectExceptionMessage('Attribute already exists'); -// -// static::getDatabase()->createRelationship( -// collection: 'test1', -// relatedCollection: 'test2', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// ); -// } -// -// public function testCreateInvalidRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('test3'); -// static::getDatabase()->createCollection('test4'); -// -// $this->expectException(Exception::class); -// $this->expectExceptionMessage('Invalid relationship type'); -// -// static::getDatabase()->createRelationship( -// collection: 'test3', -// relatedCollection: 'test4', -// type: 'invalid', -// twoWay: true, -// ); -// } -// -// public function testDeleteMissingRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// $this->expectException(Exception::class); -// $this->expectExceptionMessage('Attribute not found'); -// -// static::getDatabase()->deleteRelationship('test', 'test2'); -// } -// -// public function testCreateInvalidIntValueRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('invalid1'); -// static::getDatabase()->createCollection('invalid2'); -// -// static::getDatabase()->createRelationship( -// collection: 'invalid1', -// relatedCollection: 'invalid2', -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// ); -// -// $this->expectException(Exception::class); -// $this->expectExceptionMessage('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); -// -// static::getDatabase()->createDocument('invalid1', new Document([ -// '$id' => ID::unique(), -// 'invalid2' => 10, -// ])); -// } -// -// /** -// * @depends testCreateInvalidIntValueRelationship -// */ -// public function testCreateInvalidObjectValueRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// $this->expectException(Exception::class); -// $this->expectExceptionMessage('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); -// -// static::getDatabase()->createDocument('invalid1', new Document([ -// '$id' => ID::unique(), -// 'invalid2' => new \stdClass(), -// ])); -// } -// -// /** -// * @depends testCreateInvalidIntValueRelationship -// */ -// public function testCreateInvalidArrayIntValueRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createRelationship( -// collection: 'invalid1', -// relatedCollection: 'invalid2', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// id: 'invalid3', -// twoWayKey: 'invalid4', -// ); -// -// $this->expectException(Exception::class); -// $this->expectExceptionMessage('Invalid relationship value. Must be either a document, document ID, or an array of documents or document IDs.'); -// -// static::getDatabase()->createDocument('invalid1', new Document([ -// '$id' => ID::unique(), -// 'invalid3' => [10], -// ])); -// } -// -// public function testCreateEmptyValueRelationship(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('null1'); -// static::getDatabase()->createCollection('null2'); -// -// static::getDatabase()->createRelationship( -// collection: 'null1', -// relatedCollection: 'null2', -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// ); -// static::getDatabase()->createRelationship( -// collection: 'null1', -// relatedCollection: 'null2', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// id: 'null3', -// twoWayKey: 'null4', -// ); -// static::getDatabase()->createRelationship( -// collection: 'null1', -// relatedCollection: 'null2', -// type: Database::RELATION_MANY_TO_ONE, -// twoWay: true, -// id: 'null4', -// twoWayKey: 'null5', -// ); -// static::getDatabase()->createRelationship( -// collection: 'null1', -// relatedCollection: 'null2', -// type: Database::RELATION_MANY_TO_MANY, -// twoWay: true, -// id: 'null6', -// twoWayKey: 'null7', -// ); -// -// $document = static::getDatabase()->createDocument('null1', new Document([ -// '$id' => ID::unique(), -// 'null2' => null, -// ])); -// -// $this->assertEquals(null, $document->getAttribute('null2')); -// -// $document = static::getDatabase()->createDocument('null2', new Document([ -// '$id' => ID::unique(), -// 'null1' => null, -// ])); -// -// $this->assertEquals(null, $document->getAttribute('null1')); -// -// $document = static::getDatabase()->createDocument('null1', new Document([ -// '$id' => ID::unique(), -// 'null3' => null, -// ])); -// -// // One to many will be empty array instead of null -// $this->assertEquals([], $document->getAttribute('null3')); -// -// $document = static::getDatabase()->createDocument('null2', new Document([ -// '$id' => ID::unique(), -// 'null4' => null, -// ])); -// -// $this->assertEquals(null, $document->getAttribute('null4')); -// -// $document = static::getDatabase()->createDocument('null1', new Document([ -// '$id' => ID::unique(), -// 'null4' => null, -// ])); -// -// $this->assertEquals(null, $document->getAttribute('null4')); -// -// $document = static::getDatabase()->createDocument('null2', new Document([ -// '$id' => ID::unique(), -// 'null5' => null, -// ])); -// -// $this->assertEquals([], $document->getAttribute('null5')); -// -// $document = static::getDatabase()->createDocument('null1', new Document([ -// '$id' => ID::unique(), -// 'null6' => null, -// ])); -// -// $this->assertEquals([], $document->getAttribute('null6')); -// -// $document = static::getDatabase()->createDocument('null2', new Document([ -// '$id' => ID::unique(), -// 'null7' => null, -// ])); -// -// $this->assertEquals([], $document->getAttribute('null7')); -// } -// -// public function testDeleteCollectionDeletesRelationships(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('testers'); -// static::getDatabase()->createCollection('devices'); -// -// static::getDatabase()->createRelationship( -// collection: 'testers', -// relatedCollection: 'devices', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// twoWayKey: 'tester' -// ); -// -// $testers = static::getDatabase()->getCollection('testers'); -// $devices = static::getDatabase()->getCollection('devices'); -// -// $this->assertEquals(1, \count($testers->getAttribute('attributes'))); -// $this->assertEquals(1, \count($devices->getAttribute('attributes'))); -// $this->assertEquals(1, \count($devices->getAttribute('indexes'))); -// -// static::getDatabase()->deleteCollection('testers'); -// -// $testers = static::getDatabase()->getCollection('testers'); -// $devices = static::getDatabase()->getCollection('devices'); -// -// $this->assertEquals(true, $testers->isEmpty()); -// $this->assertEquals(0, \count($devices->getAttribute('attributes'))); -// $this->assertEquals(0, \count($devices->getAttribute('indexes'))); -// } -// -// public function testDeleteTwoWayRelationshipFromChild(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('drivers'); -// static::getDatabase()->createCollection('licenses'); -// -// static::getDatabase()->createRelationship( -// collection: 'drivers', -// relatedCollection: 'licenses', -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// id: 'license', -// twoWayKey: 'driver' -// ); -// -// $drivers = static::getDatabase()->getCollection('drivers'); -// $licenses = static::getDatabase()->getCollection('licenses'); -// -// $this->assertEquals(1, \count($drivers->getAttribute('attributes'))); -// $this->assertEquals(1, \count($drivers->getAttribute('indexes'))); -// $this->assertEquals(1, \count($licenses->getAttribute('attributes'))); -// $this->assertEquals(1, \count($licenses->getAttribute('indexes'))); -// -// static::getDatabase()->deleteRelationship('licenses', 'driver'); -// -// $drivers = static::getDatabase()->getCollection('drivers'); -// $licenses = static::getDatabase()->getCollection('licenses'); -// -// $this->assertEquals(0, \count($drivers->getAttribute('attributes'))); -// $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); -// $this->assertEquals(0, \count($licenses->getAttribute('attributes'))); -// $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); -// -// static::getDatabase()->createRelationship( -// collection: 'drivers', -// relatedCollection: 'licenses', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// id: 'licenses', -// twoWayKey: 'driver' -// ); -// -// $drivers = static::getDatabase()->getCollection('drivers'); -// $licenses = static::getDatabase()->getCollection('licenses'); -// -// $this->assertEquals(1, \count($drivers->getAttribute('attributes'))); -// $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); -// $this->assertEquals(1, \count($licenses->getAttribute('attributes'))); -// $this->assertEquals(1, \count($licenses->getAttribute('indexes'))); -// -// static::getDatabase()->deleteRelationship('licenses', 'driver'); -// -// $drivers = static::getDatabase()->getCollection('drivers'); -// $licenses = static::getDatabase()->getCollection('licenses'); -// -// $this->assertEquals(0, \count($drivers->getAttribute('attributes'))); -// $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); -// $this->assertEquals(0, \count($licenses->getAttribute('attributes'))); -// $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); -// -// static::getDatabase()->createRelationship( -// collection: 'licenses', -// relatedCollection: 'drivers', -// type: Database::RELATION_MANY_TO_ONE, -// twoWay: true, -// id: 'driver', -// twoWayKey: 'licenses' -// ); -// -// $drivers = static::getDatabase()->getCollection('drivers'); -// $licenses = static::getDatabase()->getCollection('licenses'); -// -// $this->assertEquals(1, \count($drivers->getAttribute('attributes'))); -// $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); -// $this->assertEquals(1, \count($licenses->getAttribute('attributes'))); -// $this->assertEquals(1, \count($licenses->getAttribute('indexes'))); -// -// static::getDatabase()->deleteRelationship('drivers', 'licenses'); -// -// $drivers = static::getDatabase()->getCollection('drivers'); -// $licenses = static::getDatabase()->getCollection('licenses'); -// -// $this->assertEquals(0, \count($drivers->getAttribute('attributes'))); -// $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); -// $this->assertEquals(0, \count($licenses->getAttribute('attributes'))); -// $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); -// -// static::getDatabase()->createRelationship( -// collection: 'licenses', -// relatedCollection: 'drivers', -// type: Database::RELATION_MANY_TO_MANY, -// twoWay: true, -// id: 'drivers', -// twoWayKey: 'licenses' -// ); -// -// $drivers = static::getDatabase()->getCollection('drivers'); -// $licenses = static::getDatabase()->getCollection('licenses'); -// $junction = static::getDatabase()->getCollection('_' . $licenses->getInternalId() . '_' . $drivers->getInternalId()); -// -// $this->assertEquals(1, \count($drivers->getAttribute('attributes'))); -// $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); -// $this->assertEquals(1, \count($licenses->getAttribute('attributes'))); -// $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); -// $this->assertEquals(2, \count($junction->getAttribute('attributes'))); -// $this->assertEquals(2, \count($junction->getAttribute('indexes'))); -// -// static::getDatabase()->deleteRelationship('drivers', 'licenses'); -// -// $drivers = static::getDatabase()->getCollection('drivers'); -// $licenses = static::getDatabase()->getCollection('licenses'); -// $junction = static::getDatabase()->getCollection('_licenses_drivers'); -// -// $this->assertEquals(0, \count($drivers->getAttribute('attributes'))); -// $this->assertEquals(0, \count($drivers->getAttribute('indexes'))); -// $this->assertEquals(0, \count($licenses->getAttribute('attributes'))); -// $this->assertEquals(0, \count($licenses->getAttribute('indexes'))); -// -// $this->assertEquals(true, $junction->isEmpty()); -// } -// -// public function testUpdateRelationshipToExistingKey(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('ovens'); -// static::getDatabase()->createCollection('cakes'); -// -// static::getDatabase()->createAttribute('ovens', 'maxTemp', Database::VAR_INTEGER, 0, true); -// static::getDatabase()->createAttribute('ovens', 'owner', Database::VAR_STRING, 255, true); -// static::getDatabase()->createAttribute('cakes', 'height', Database::VAR_INTEGER, 0, true); -// static::getDatabase()->createAttribute('cakes', 'colour', Database::VAR_STRING, 255, true); -// -// static::getDatabase()->createRelationship( -// collection: 'ovens', -// relatedCollection: 'cakes', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// id: 'cakes', -// twoWayKey: 'oven' -// ); -// -// try { -// static::getDatabase()->updateRelationship('ovens', 'cakes', newKey: 'owner'); -// $this->fail('Failed to throw exception'); -// } catch (DuplicateException $e) { -// $this->assertEquals('Attribute already exists', $e->getMessage()); -// } -// -// try { -// static::getDatabase()->updateRelationship('ovens', 'cakes', newTwoWayKey: 'height'); -// $this->fail('Failed to throw exception'); -// } catch (DuplicateException $e) { -// $this->assertEquals('Related attribute already exists', $e->getMessage()); -// } -// } -// -// public function testOneToOneRelationshipKeyWithSymbols(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('$symbols_coll.ection1'); -// static::getDatabase()->createCollection('$symbols_coll.ection2'); -// -// static::getDatabase()->createRelationship( -// collection: '$symbols_coll.ection1', -// relatedCollection: '$symbols_coll.ection2', -// type: Database::RELATION_ONE_TO_ONE, -// twoWay: true, -// ); -// -// $doc1 = static::getDatabase()->createDocument('$symbols_coll.ection2', new Document([ -// '$id' => ID::unique(), -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()) -// ] -// ])); -// $doc2 = static::getDatabase()->createDocument('$symbols_coll.ection1', new Document([ -// '$id' => ID::unique(), -// '$symbols_coll.ection2' => $doc1->getId(), -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()) -// ] -// ])); -// -// $doc1 = static::getDatabase()->getDocument('$symbols_coll.ection2', $doc1->getId()); -// $doc2 = static::getDatabase()->getDocument('$symbols_coll.ection1', $doc2->getId()); -// -// $this->assertEquals($doc2->getId(), $doc1->getAttribute('$symbols_coll.ection1')->getId()); -// $this->assertEquals($doc1->getId(), $doc2->getAttribute('$symbols_coll.ection2')->getId()); -// } -// -// public function testOneToManyRelationshipKeyWithSymbols(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('$symbols_coll.ection3'); -// static::getDatabase()->createCollection('$symbols_coll.ection4'); -// -// static::getDatabase()->createRelationship( -// collection: '$symbols_coll.ection3', -// relatedCollection: '$symbols_coll.ection4', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// ); -// -// $doc1 = static::getDatabase()->createDocument('$symbols_coll.ection4', new Document([ -// '$id' => ID::unique(), -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()) -// ] -// ])); -// $doc2 = static::getDatabase()->createDocument('$symbols_coll.ection3', new Document([ -// '$id' => ID::unique(), -// '$symbols_coll.ection4' => [$doc1->getId()], -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()) -// ] -// ])); -// -// $doc1 = static::getDatabase()->getDocument('$symbols_coll.ection4', $doc1->getId()); -// $doc2 = static::getDatabase()->getDocument('$symbols_coll.ection3', $doc2->getId()); -// -// $this->assertEquals($doc2->getId(), $doc1->getAttribute('$symbols_coll.ection3')->getId()); -// $this->assertEquals($doc1->getId(), $doc2->getAttribute('$symbols_coll.ection4')[0]->getId()); -// } -// -// public function testManyToOneRelationshipKeyWithSymbols(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('$symbols_coll.ection5'); -// static::getDatabase()->createCollection('$symbols_coll.ection6'); -// -// static::getDatabase()->createRelationship( -// collection: '$symbols_coll.ection5', -// relatedCollection: '$symbols_coll.ection6', -// type: Database::RELATION_MANY_TO_ONE, -// twoWay: true, -// ); -// -// $doc1 = static::getDatabase()->createDocument('$symbols_coll.ection6', new Document([ -// '$id' => ID::unique(), -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()) -// ] -// ])); -// $doc2 = static::getDatabase()->createDocument('$symbols_coll.ection5', new Document([ -// '$id' => ID::unique(), -// '$symbols_coll.ection6' => $doc1->getId(), -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()) -// ] -// ])); -// -// $doc1 = static::getDatabase()->getDocument('$symbols_coll.ection6', $doc1->getId()); -// $doc2 = static::getDatabase()->getDocument('$symbols_coll.ection5', $doc2->getId()); -// -// $this->assertEquals($doc2->getId(), $doc1->getAttribute('$symbols_coll.ection5')[0]->getId()); -// $this->assertEquals($doc1->getId(), $doc2->getAttribute('$symbols_coll.ection6')->getId()); -// } -// -// public function testManyToManyRelationshipKeyWithSymbols(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('$symbols_coll.ection7'); -// static::getDatabase()->createCollection('$symbols_coll.ection8'); -// -// static::getDatabase()->createRelationship( -// collection: '$symbols_coll.ection7', -// relatedCollection: '$symbols_coll.ection8', -// type: Database::RELATION_MANY_TO_MANY, -// twoWay: true, -// ); -// -// $doc1 = static::getDatabase()->createDocument('$symbols_coll.ection8', new Document([ -// '$id' => ID::unique(), -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()) -// ] -// ])); -// $doc2 = static::getDatabase()->createDocument('$symbols_coll.ection7', new Document([ -// '$id' => ID::unique(), -// '$symbols_coll.ection8' => [$doc1->getId()], -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()) -// ] -// ])); -// -// $doc1 = static::getDatabase()->getDocument('$symbols_coll.ection8', $doc1->getId()); -// $doc2 = static::getDatabase()->getDocument('$symbols_coll.ection7', $doc2->getId()); -// -// $this->assertEquals($doc2->getId(), $doc1->getAttribute('$symbols_coll.ection7')[0]->getId()); -// $this->assertEquals($doc1->getId(), $doc2->getAttribute('$symbols_coll.ection8')[0]->getId()); -// } -// -// public function testCascadeMultiDelete(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// static::getDatabase()->createCollection('cascadeMultiDelete1'); -// static::getDatabase()->createCollection('cascadeMultiDelete2'); -// static::getDatabase()->createCollection('cascadeMultiDelete3'); -// -// static::getDatabase()->createRelationship( -// collection: 'cascadeMultiDelete1', -// relatedCollection: 'cascadeMultiDelete2', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// onDelete: Database::RELATION_MUTATE_CASCADE -// ); -// -// static::getDatabase()->createRelationship( -// collection: 'cascadeMultiDelete2', -// relatedCollection: 'cascadeMultiDelete3', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// onDelete: Database::RELATION_MUTATE_CASCADE -// ); -// -// $root = static::getDatabase()->createDocument('cascadeMultiDelete1', new Document([ -// '$id' => 'cascadeMultiDelete1', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::delete(Role::any()) -// ], -// 'cascadeMultiDelete2' => [ -// [ -// '$id' => 'cascadeMultiDelete2', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::delete(Role::any()) -// ], -// 'cascadeMultiDelete3' => [ -// [ -// '$id' => 'cascadeMultiDelete3', -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::delete(Role::any()) -// ], -// ], -// ], -// ], -// ], -// ])); -// -// $this->assertCount(1, $root->getAttribute('cascadeMultiDelete2')); -// $this->assertCount(1, $root->getAttribute('cascadeMultiDelete2')[0]->getAttribute('cascadeMultiDelete3')); -// -// $this->assertEquals(true, static::getDatabase()->deleteDocument('cascadeMultiDelete1', $root->getId())); -// -// $multi2 = static::getDatabase()->getDocument('cascadeMultiDelete2', 'cascadeMultiDelete2'); -// $this->assertEquals(true, $multi2->isEmpty()); -// -// $multi3 = static::getDatabase()->getDocument('cascadeMultiDelete3', 'cascadeMultiDelete3'); -// $this->assertEquals(true, $multi3->isEmpty()); -// } -// -// public function testCollectionUpdate(): Document -// { -// $collection = static::getDatabase()->createCollection('collectionUpdate', permissions: [ -// Permission::create(Role::users()), -// Permission::read(Role::users()), -// Permission::update(Role::users()), -// Permission::delete(Role::users()) -// ], documentSecurity: false); -// -// $this->assertInstanceOf(Document::class, $collection); -// -// $collection = static::getDatabase()->getCollection('collectionUpdate'); -// -// $this->assertFalse($collection->getAttribute('documentSecurity')); -// $this->assertIsArray($collection->getPermissions()); -// $this->assertCount(4, $collection->getPermissions()); -// -// $collection = static::getDatabase()->updateCollection('collectionUpdate', [], true); -// -// $this->assertTrue($collection->getAttribute('documentSecurity')); -// $this->assertIsArray($collection->getPermissions()); -// $this->assertEmpty($collection->getPermissions()); -// -// $collection = static::getDatabase()->getCollection('collectionUpdate'); -// -// $this->assertTrue($collection->getAttribute('documentSecurity')); -// $this->assertIsArray($collection->getPermissions()); -// $this->assertEmpty($collection->getPermissions()); -// -// return $collection; -// } -// -// /** -// * @depends testCollectionUpdate -// */ -// public function testCollectionUpdatePermissionsThrowException(Document $collection): void -// { -// $this->expectException(InvalidArgumentException::class); -// static::getDatabase()->updateCollection($collection->getId(), permissions: [ -// 'i dont work' -// ], documentSecurity: false); -// } -// -// public function testCollectionPermissions(): Document -// { -// $collection = static::getDatabase()->createCollection('collectionSecurity', permissions: [ -// Permission::create(Role::users()), -// Permission::read(Role::users()), -// Permission::update(Role::users()), -// Permission::delete(Role::users()) -// ], documentSecurity: false); -// -// $this->assertInstanceOf(Document::class, $collection); -// -// $this->assertTrue(static::getDatabase()->createAttribute( -// collection: $collection->getId(), -// id: 'test', -// type: Database::VAR_STRING, -// size: 255, -// required: false -// )); -// -// return $collection; -// } -// -// public function testCollectionPermissionsExceptions(): void -// { -// $this->expectException(InvalidArgumentException::class); -// static::getDatabase()->createCollection('collectionSecurity', permissions: [ -// 'i dont work' -// ]); -// } -// -// /** -// * @depends testCollectionPermissions -// * @return array -// */ -// public function testCollectionPermissionsCreateWorks(Document $collection): array -// { -// Authorization::cleanRoles(); -// Authorization::setRole(Role::users()->toString()); -// -// $document = static::getDatabase()->createDocument($collection->getId(), new Document([ -// '$id' => ID::unique(), -// '$permissions' => [ -// Permission::read(Role::user('random')), -// Permission::update(Role::user('random')), -// Permission::delete(Role::user('random')) -// ], -// 'test' => 'lorem' -// ])); -// $this->assertInstanceOf(Document::class, $document); -// -// return [$collection, $document]; -// } -// -// -// /** -// * @depends testCollectionPermissions -// */ -// public function testCollectionPermissionsCreateThrowsException(Document $collection): void -// { -// Authorization::cleanRoles(); -// Authorization::setRole(Role::any()->toString()); -// $this->expectException(AuthorizationException::class); -// -// static::getDatabase()->createDocument($collection->getId(), new Document([ -// '$id' => ID::unique(), -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()) -// ], -// 'test' => 'lorem ipsum' -// ])); -// } -// -// /** -// * @depends testCollectionPermissionsCreateWorks -// * @param array $data -// * @return array -// */ -// public function testCollectionPermissionsGetWorks(array $data): array -// { -// [$collection, $document] = $data; -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::users()->toString()); -// -// $document = static::getDatabase()->getDocument( -// $collection->getId(), -// $document->getId() -// ); -// $this->assertInstanceOf(Document::class, $document); -// $this->assertFalse($document->isEmpty()); -// -// return $data; -// } -// -// /** -// * @depends testCollectionPermissionsCreateWorks -// * @param array $data -// */ -// public function testCollectionPermissionsGetThrowsException(array $data): void -// { -// [$collection, $document] = $data; -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::any()->toString()); -// -// $document = static::getDatabase()->getDocument( -// $collection->getId(), -// $document->getId(), -// ); -// $this->assertInstanceOf(Document::class, $document); -// $this->assertTrue($document->isEmpty()); -// } -// -// /** -// * @depends testCollectionPermissionsCreateWorks -// * @param array $data -// * @return array -// */ -// public function testCollectionPermissionsFindWorks(array $data): array -// { -// [$collection, $document] = $data; -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::users()->toString()); -// -// $documents = static::getDatabase()->find($collection->getId()); -// $this->assertNotEmpty($documents); -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::user('random')->toString()); -// -// try { -// static::getDatabase()->find($collection->getId()); -// $this->fail('Failed to throw exception'); -// } catch (AuthorizationException) { -// } -// -// return $data; -// } -// -// /** -// * @param array $data -// * @depends testCollectionPermissionsCreateWorks -// */ -// public function testCollectionPermissionsFindThrowsException(array $data): void -// { -// [$collection, $document] = $data; -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::any()->toString()); -// -// $this->expectException(AuthorizationException::class); -// static::getDatabase()->find($collection->getId()); -// } -// -// /** -// * @depends testCollectionPermissionsCreateWorks -// * @param array $data -// * @return array -// */ -// public function testCollectionPermissionsCountWorks(array $data): array -// { -// [$collection, $document] = $data; -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::users()->toString()); -// -// $count = static::getDatabase()->count( -// $collection->getId() -// ); -// -// $this->assertNotEmpty($count); -// -// return $data; -// } -// -// /** -// * @param array $data -// * @depends testCollectionPermissionsCreateWorks -// */ -// public function testCollectionPermissionsCountThrowsException(array $data): void -// { -// [$collection, $document] = $data; -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::any()->toString()); -// -// $count = static::getDatabase()->count( -// $collection->getId() -// ); -// $this->assertEmpty($count); -// } -// -// /** -// * @depends testCollectionPermissionsCreateWorks -// * @param array $data -// * @return array -// */ -// public function testCollectionPermissionsUpdateWorks(array $data): array -// { -// [$collection, $document] = $data; -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::users()->toString()); -// -// $this->assertInstanceOf(Document::class, static::getDatabase()->updateDocument( -// $collection->getId(), -// $document->getId(), -// $document->setAttribute('test', 'ipsum') -// )); -// -// return $data; -// } -// -// /** -// * @param array $data -// * @depends testCollectionPermissionsCreateWorks -// */ -// public function testCollectionPermissionsUpdateThrowsException(array $data): void -// { -// [$collection, $document] = $data; -// Authorization::cleanRoles(); -// Authorization::setRole(Role::any()->toString()); -// -// $this->expectException(AuthorizationException::class); -// $document = static::getDatabase()->updateDocument( -// $collection->getId(), -// $document->getId(), -// $document->setAttribute('test', 'lorem') -// ); -// } -// -// /** -// * @param array $data -// * @depends testCollectionPermissionsUpdateWorks -// */ -// public function testCollectionPermissionsDeleteThrowsException(array $data): void -// { -// [$collection, $document] = $data; -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::any()->toString()); -// -// $this->expectException(AuthorizationException::class); -// static::getDatabase()->deleteDocument( -// $collection->getId(), -// $document->getId() -// ); -// } -// -// /** -// * @param array $data -// * @depends testCollectionPermissionsUpdateWorks -// */ -// public function testCollectionPermissionsDeleteWorks(array $data): void -// { -// [$collection, $document] = $data; -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::users()->toString()); -// -// $this->assertTrue(static::getDatabase()->deleteDocument( -// $collection->getId(), -// $document->getId() -// )); -// } -// -// /** -// * @return array -// */ -// public function testCollectionPermissionsRelationships(): array -// { -// $collection = static::getDatabase()->createCollection('collectionSecurity.Parent', permissions: [ -// Permission::create(Role::users()), -// Permission::read(Role::users()), -// Permission::update(Role::users()), -// Permission::delete(Role::users()) -// ], documentSecurity: true); -// -// $this->assertInstanceOf(Document::class, $collection); -// -// $this->assertTrue(static::getDatabase()->createAttribute( -// collection: $collection->getId(), -// id: 'test', -// type: Database::VAR_STRING, -// size: 255, -// required: false -// )); -// -// $collectionOneToOne = static::getDatabase()->createCollection('collectionSecurity.OneToOne', permissions: [ -// Permission::create(Role::users()), -// Permission::read(Role::users()), -// Permission::update(Role::users()), -// Permission::delete(Role::users()) -// ], documentSecurity: true); -// -// $this->assertInstanceOf(Document::class, $collectionOneToOne); -// -// $this->assertTrue(static::getDatabase()->createAttribute( -// collection: $collectionOneToOne->getId(), -// id: 'test', -// type: Database::VAR_STRING, -// size: 255, -// required: false -// )); -// -// $this->assertTrue(static::getDatabase()->createRelationship( -// collection: $collection->getId(), -// relatedCollection: $collectionOneToOne->getId(), -// type: Database::RELATION_ONE_TO_ONE, -// id: Database::RELATION_ONE_TO_ONE, -// onDelete: Database::RELATION_MUTATE_CASCADE -// )); -// -// $collectionOneToMany = static::getDatabase()->createCollection('collectionSecurity.OneToMany', permissions: [ -// Permission::create(Role::users()), -// Permission::read(Role::users()), -// Permission::update(Role::users()), -// Permission::delete(Role::users()) -// ], documentSecurity: true); -// -// $this->assertInstanceOf(Document::class, $collectionOneToMany); -// -// $this->assertTrue(static::getDatabase()->createAttribute( -// collection: $collectionOneToMany->getId(), -// id: 'test', -// type: Database::VAR_STRING, -// size: 255, -// required: false -// )); -// -// $this->assertTrue(static::getDatabase()->createRelationship( -// collection: $collection->getId(), -// relatedCollection: $collectionOneToMany->getId(), -// type: Database::RELATION_ONE_TO_MANY, -// id: Database::RELATION_ONE_TO_MANY, -// onDelete: Database::RELATION_MUTATE_CASCADE -// )); -// -// return [$collection, $collectionOneToOne, $collectionOneToMany]; -// } -// -// /** -// * @depends testCollectionPermissionsRelationships -// * @param array $data -// * @return array -// */ -// public function testCollectionPermissionsRelationshipsCreateWorks(array $data): array -// { -// [$collection, $collectionOneToOne, $collectionOneToMany] = $data; -// Authorization::cleanRoles(); -// Authorization::setRole(Role::users()->toString()); -// -// $document = static::getDatabase()->createDocument($collection->getId(), new Document([ -// '$id' => ID::unique(), -// '$permissions' => [ -// Permission::read(Role::user('random')), -// Permission::update(Role::user('random')), -// Permission::delete(Role::user('random')) -// ], -// 'test' => 'lorem', -// Database::RELATION_ONE_TO_ONE => [ -// '$id' => ID::unique(), -// '$permissions' => [ -// Permission::read(Role::user('random')), -// Permission::update(Role::user('random')), -// Permission::delete(Role::user('random')) -// ], -// 'test' => 'lorem ipsum' -// ], -// Database::RELATION_ONE_TO_MANY => [ -// [ -// '$id' => ID::unique(), -// '$permissions' => [ -// Permission::read(Role::user('random')), -// Permission::update(Role::user('random')), -// Permission::delete(Role::user('random')) -// ], -// 'test' => 'lorem ipsum' -// ], [ -// '$id' => ID::unique(), -// '$permissions' => [ -// Permission::read(Role::user('torsten')), -// Permission::update(Role::user('random')), -// Permission::delete(Role::user('random')) -// ], -// 'test' => 'dolor' -// ] -// ], -// ])); -// $this->assertInstanceOf(Document::class, $document); -// -// return [...$data, $document]; -// } -// -// /** -// * @depends testCollectionPermissionsRelationships -// * @param array $data -// */ -// public function testCollectionPermissionsRelationshipsCreateThrowsException(array $data): void -// { -// [$collection, $collectionOneToOne, $collectionOneToMany] = $data; -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::any()->toString()); -// $this->expectException(AuthorizationException::class); -// -// static::getDatabase()->createDocument($collection->getId(), new Document([ -// '$id' => ID::unique(), -// '$permissions' => [ -// Permission::read(Role::any()), -// Permission::update(Role::any()) -// ], -// 'test' => 'lorem ipsum' -// ])); -// } -// -// /** -// * @depends testCollectionPermissionsRelationshipsCreateWorks -// * @param array $data -// * @return array -// */ -// public function testCollectionPermissionsRelationshipsGetWorks(array $data): array -// { -// [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::users()->toString()); -// -// $document = static::getDatabase()->getDocument( -// $collection->getId(), -// $document->getId() -// ); -// -// $this->assertInstanceOf(Document::class, $document); -// $this->assertInstanceOf(Document::class, $document->getAttribute(Database::RELATION_ONE_TO_ONE)); -// $this->assertIsArray($document->getAttribute(Database::RELATION_ONE_TO_MANY)); -// $this->assertCount(2, $document->getAttribute(Database::RELATION_ONE_TO_MANY)); -// $this->assertFalse($document->isEmpty()); -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::user('random')->toString()); -// -// $document = static::getDatabase()->getDocument( -// $collection->getId(), -// $document->getId() -// ); -// -// $this->assertInstanceOf(Document::class, $document); -// $this->assertInstanceOf(Document::class, $document->getAttribute(Database::RELATION_ONE_TO_ONE)); -// $this->assertIsArray($document->getAttribute(Database::RELATION_ONE_TO_MANY)); -// $this->assertCount(1, $document->getAttribute(Database::RELATION_ONE_TO_MANY)); -// $this->assertFalse($document->isEmpty()); -// -// return $data; -// } -// -// /** -// * @param array $data -// * @depends testCollectionPermissionsRelationshipsCreateWorks -// */ -// public function testCollectionPermissionsRelationshipsGetThrowsException(array $data): void -// { -// [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::any()->toString()); -// -// $document = static::getDatabase()->getDocument( -// $collection->getId(), -// $document->getId(), -// ); -// $this->assertInstanceOf(Document::class, $document); -// $this->assertTrue($document->isEmpty()); -// } -// -// /** -// * @depends testCollectionPermissionsRelationshipsCreateWorks -// * @param array $data -// */ -// public function testCollectionPermissionsRelationshipsFindWorks(array $data): void -// { -// [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::users()->toString()); -// -// $documents = static::getDatabase()->find( -// $collection->getId() -// ); -// -// $this->assertIsArray($documents); -// $this->assertCount(1, $documents); -// $document = $documents[0]; -// $this->assertInstanceOf(Document::class, $document); -// $this->assertInstanceOf(Document::class, $document->getAttribute(Database::RELATION_ONE_TO_ONE)); -// $this->assertIsArray($document->getAttribute(Database::RELATION_ONE_TO_MANY)); -// $this->assertCount(2, $document->getAttribute(Database::RELATION_ONE_TO_MANY)); -// $this->assertFalse($document->isEmpty()); -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::user('random')->toString()); -// -// $documents = static::getDatabase()->find( -// $collection->getId() -// ); -// -// $this->assertIsArray($documents); -// $this->assertCount(1, $documents); -// $document = $documents[0]; -// $this->assertInstanceOf(Document::class, $document); -// $this->assertInstanceOf(Document::class, $document->getAttribute(Database::RELATION_ONE_TO_ONE)); -// $this->assertIsArray($document->getAttribute(Database::RELATION_ONE_TO_MANY)); -// $this->assertCount(1, $document->getAttribute(Database::RELATION_ONE_TO_MANY)); -// $this->assertFalse($document->isEmpty()); -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::user('unknown')->toString()); -// -// $documents = static::getDatabase()->find( -// $collection->getId() -// ); -// -// $this->assertIsArray($documents); -// $this->assertCount(0, $documents); -// } -// -// /** -// * @depends testCollectionPermissionsRelationshipsCreateWorks -// * @param array $data -// */ -// public function testCollectionPermissionsRelationshipsCountWorks(array $data): void -// { -// [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::users()->toString()); -// -// $documents = static::getDatabase()->count( -// $collection->getId() -// ); -// -// $this->assertEquals(1, $documents); -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::user('random')->toString()); -// -// $documents = static::getDatabase()->count( -// $collection->getId() -// ); -// -// $this->assertEquals(1, $documents); -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::user('unknown')->toString()); -// -// $documents = static::getDatabase()->count( -// $collection->getId() -// ); -// -// $this->assertEquals(0, $documents); -// } -// -// /** -// * @depends testCollectionPermissionsRelationshipsCreateWorks -// * @param array $data -// * @return array -// */ -// public function testCollectionPermissionsRelationshipsUpdateWorks(array $data): array -// { -// [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::users()->toString()); -// static::getDatabase()->updateDocument( -// $collection->getId(), -// $document->getId(), -// $document -// ); -// -// $this->assertTrue(true); -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::user('random')->toString()); -// -// static::getDatabase()->updateDocument( -// $collection->getId(), -// $document->getId(), -// $document->setAttribute('test', 'ipsum') -// ); -// -// $this->assertTrue(true); -// -// return $data; -// } -// -// /** -// * @param array $data -// * @depends testCollectionPermissionsRelationshipsCreateWorks -// */ -// public function testCollectionPermissionsRelationshipsUpdateThrowsException(array $data): void -// { -// [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::any()->toString()); -// -// $this->expectException(AuthorizationException::class); -// $document = static::getDatabase()->updateDocument( -// $collection->getId(), -// $document->getId(), -// $document->setAttribute('test', $document->getAttribute('test').'new_value') -// ); -// } -// -// /** -// * @param array $data -// * @depends testCollectionPermissionsRelationshipsUpdateWorks -// */ -// public function testCollectionPermissionsRelationshipsDeleteThrowsException(array $data): void -// { -// [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::any()->toString()); -// -// $this->expectException(AuthorizationException::class); -// $document = static::getDatabase()->deleteDocument( -// $collection->getId(), -// $document->getId() -// ); -// } -// -// /** -// * @param array $data -// * @depends testCollectionPermissionsRelationshipsUpdateWorks -// */ -// public function testCollectionPermissionsRelationshipsDeleteWorks(array $data): void -// { -// [$collection, $collectionOneToOne, $collectionOneToMany, $document] = $data; -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::users()->toString()); -// -// $this->assertTrue(static::getDatabase()->deleteDocument( -// $collection->getId(), -// $document->getId() -// )); -// } -// -// public function testCreateRelationDocumentWithoutUpdatePermission(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// -// Authorization::cleanRoles(); -// Authorization::setRole(Role::user('a')->toString()); -// -// static::getDatabase()->createCollection('parentRelationTest', [], [], [ -// Permission::read(Role::user('a')), -// Permission::create(Role::user('a')), -// Permission::update(Role::user('a')), -// Permission::delete(Role::user('a')) -// ]); -// static::getDatabase()->createCollection('childRelationTest', [], [], [ -// Permission::create(Role::user('a')), -// Permission::read(Role::user('a')), -// ]); -// static::getDatabase()->createAttribute('parentRelationTest', 'name', Database::VAR_STRING, 255, false); -// static::getDatabase()->createAttribute('childRelationTest', 'name', Database::VAR_STRING, 255, false); -// -// static::getDatabase()->createRelationship( -// collection: 'parentRelationTest', -// relatedCollection: 'childRelationTest', -// type: Database::RELATION_ONE_TO_MANY, -// id: 'children' -// ); -// -// // Create document with relationship with nested data -// $parent = static::getDatabase()->createDocument('parentRelationTest', new Document([ -// '$id' => 'parent1', -// 'name' => 'Parent 1', -// 'children' => [ -// [ -// '$id' => 'child1', -// 'name' => 'Child 1', -// ], -// ], -// ])); -// $this->assertEquals('child1', $parent->getAttribute('children')[0]->getId()); -// $parent->setAttribute('children', [ -// [ -// '$id' => 'child2', -// ], -// ]); -// $updatedParent = static::getDatabase()->updateDocument('parentRelationTest', 'parent1', $parent); -// -// $this->assertEquals('child2', $updatedParent->getAttribute('children')[0]->getId()); -// -// static::getDatabase()->deleteCollection('parentRelationTest'); -// static::getDatabase()->deleteCollection('childRelationTest'); -// } -// -// public function testUpdateDocumentWithRelationships(): void -// { -// if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { -// $this->expectNotToPerformAssertions(); -// return; -// } -// static::getDatabase()->createCollection('userProfiles', [ -// new Document([ -// '$id' => ID::custom('username'), -// 'type' => Database::VAR_STRING, -// 'format' => '', -// 'size' => 700, -// 'signed' => true, -// 'required' => false, -// 'default' => null, -// 'array' => false, -// 'filters' => [], -// ]), -// ], [], [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()) -// ]); -// static::getDatabase()->createCollection('links', [ -// new Document([ -// '$id' => ID::custom('title'), -// 'type' => Database::VAR_STRING, -// 'format' => '', -// 'size' => 700, -// 'signed' => true, -// 'required' => false, -// 'default' => null, -// 'array' => false, -// 'filters' => [], -// ]), -// ], [], [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()) -// ]); -// static::getDatabase()->createCollection('videos', [ -// new Document([ -// '$id' => ID::custom('title'), -// 'type' => Database::VAR_STRING, -// 'format' => '', -// 'size' => 700, -// 'signed' => true, -// 'required' => false, -// 'default' => null, -// 'array' => false, -// 'filters' => [], -// ]), -// ], [], [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()) -// ]); -// static::getDatabase()->createCollection('products', [ -// new Document([ -// '$id' => ID::custom('title'), -// 'type' => Database::VAR_STRING, -// 'format' => '', -// 'size' => 700, -// 'signed' => true, -// 'required' => false, -// 'default' => null, -// 'array' => false, -// 'filters' => [], -// ]), -// ], [], [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()) -// ]); -// static::getDatabase()->createCollection('settings', [ -// new Document([ -// '$id' => ID::custom('metaTitle'), -// 'type' => Database::VAR_STRING, -// 'format' => '', -// 'size' => 700, -// 'signed' => true, -// 'required' => false, -// 'default' => null, -// 'array' => false, -// 'filters' => [], -// ]), -// ], [], [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()) -// ]); -// static::getDatabase()->createCollection('appearance', [ -// new Document([ -// '$id' => ID::custom('metaTitle'), -// 'type' => Database::VAR_STRING, -// 'format' => '', -// 'size' => 700, -// 'signed' => true, -// 'required' => false, -// 'default' => null, -// 'array' => false, -// 'filters' => [], -// ]), -// ], [], [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()) -// ]); -// static::getDatabase()->createCollection('group', [ -// new Document([ -// '$id' => ID::custom('name'), -// 'type' => Database::VAR_STRING, -// 'format' => '', -// 'size' => 700, -// 'signed' => true, -// 'required' => false, -// 'default' => null, -// 'array' => false, -// 'filters' => [], -// ]), -// ], [], [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()) -// ]); -// static::getDatabase()->createCollection('community', [ -// new Document([ -// '$id' => ID::custom('name'), -// 'type' => Database::VAR_STRING, -// 'format' => '', -// 'size' => 700, -// 'signed' => true, -// 'required' => false, -// 'default' => null, -// 'array' => false, -// 'filters' => [], -// ]), -// ], [], [ -// Permission::read(Role::any()), -// Permission::create(Role::any()), -// Permission::update(Role::any()), -// Permission::delete(Role::any()) -// ]); -// -// static::getDatabase()->createRelationship( -// collection: 'userProfiles', -// relatedCollection: 'links', -// type: Database::RELATION_ONE_TO_MANY, -// id: 'links' -// ); -// -// static::getDatabase()->createRelationship( -// collection: 'userProfiles', -// relatedCollection: 'videos', -// type: Database::RELATION_ONE_TO_MANY, -// id: 'videos' -// ); -// -// static::getDatabase()->createRelationship( -// collection: 'userProfiles', -// relatedCollection: 'products', -// type: Database::RELATION_ONE_TO_MANY, -// twoWay: true, -// id: 'products', -// twoWayKey: 'userProfile', -// ); -// -// static::getDatabase()->createRelationship( -// collection: 'userProfiles', -// relatedCollection: 'settings', -// type: Database::RELATION_ONE_TO_ONE, -// id: 'settings' -// ); -// -// static::getDatabase()->createRelationship( -// collection: 'userProfiles', -// relatedCollection: 'appearance', -// type: Database::RELATION_ONE_TO_ONE, -// id: 'appearance' -// ); -// -// static::getDatabase()->createRelationship( -// collection: 'userProfiles', -// relatedCollection: 'group', -// type: Database::RELATION_MANY_TO_ONE, -// id: 'group' -// ); -// -// static::getDatabase()->createRelationship( -// collection: 'userProfiles', -// relatedCollection: 'community', -// type: Database::RELATION_MANY_TO_ONE, -// id: 'community' -// ); -// -// $profile = static::getDatabase()->createDocument('userProfiles', new Document([ -// '$id' => '1', -// 'username' => 'user1', -// 'links' => [ -// [ -// '$id' => 'link1', -// 'title' => 'Link 1', -// ], -// ], -// 'videos' => [ -// [ -// '$id' => 'video1', -// 'title' => 'Video 1', -// ], -// ], -// 'products' => [ -// [ -// '$id' => 'product1', -// 'title' => 'Product 1', -// ], -// ], -// 'settings' => [ -// '$id' => 'settings1', -// 'metaTitle' => 'Meta Title', -// ], -// 'appearance' => [ -// '$id' => 'appearance1', -// 'metaTitle' => 'Meta Title', -// ], -// 'group' => [ -// '$id' => 'group1', -// 'name' => 'Group 1', -// ], -// 'community' => [ -// '$id' => 'community1', -// 'name' => 'Community 1', -// ], -// ])); -// $this->assertEquals('link1', $profile->getAttribute('links')[0]->getId()); -// $this->assertEquals('settings1', $profile->getAttribute('settings')->getId()); -// $this->assertEquals('group1', $profile->getAttribute('group')->getId()); -// $this->assertEquals('community1', $profile->getAttribute('community')->getId()); -// $this->assertEquals('video1', $profile->getAttribute('videos')[0]->getId()); -// $this->assertEquals('product1', $profile->getAttribute('products')[0]->getId()); -// $this->assertEquals('appearance1', $profile->getAttribute('appearance')->getId()); -// -// $profile->setAttribute('links', [ -// [ -// '$id' => 'link1', -// 'title' => 'New Link Value', -// ], -// ]); -// -// $profile->setAttribute('settings', [ -// '$id' => 'settings1', -// 'metaTitle' => 'New Meta Title', -// ]); -// -// $profile->setAttribute('group', [ -// '$id' => 'group1', -// 'name' => 'New Group Name', -// ]); -// -// $updatedProfile = static::getDatabase()->updateDocument('userProfiles', '1', $profile); -// -// $this->assertEquals('New Link Value', $updatedProfile->getAttribute('links')[0]->getAttribute('title')); -// $this->assertEquals('New Meta Title', $updatedProfile->getAttribute('settings')->getAttribute('metaTitle')); -// $this->assertEquals('New Group Name', $updatedProfile->getAttribute('group')->getAttribute('name')); -// -// // This is the point of test, related documents should be present if they are not updated -// $this->assertEquals('Video 1', $updatedProfile->getAttribute('videos')[0]->getAttribute('title')); -// $this->assertEquals('Product 1', $updatedProfile->getAttribute('products')[0]->getAttribute('title')); -// $this->assertEquals('Meta Title', $updatedProfile->getAttribute('appearance')->getAttribute('metaTitle')); -// $this->assertEquals('Community 1', $updatedProfile->getAttribute('community')->getAttribute('name')); -// -// // updating document using two way key in one to many relationship -// $product = static::getDatabase()->getDocument('products', 'product1'); -// $product->setAttribute('userProfile', [ -// '$id' => '1', -// 'username' => 'updated user value', -// ]); -// $updatedProduct = static::getDatabase()->updateDocument('products', 'product1', $product); -// $this->assertEquals('updated user value', $updatedProduct->getAttribute('userProfile')->getAttribute('username')); -// $this->assertEquals('Product 1', $updatedProduct->getAttribute('title')); -// $this->assertEquals('product1', $updatedProduct->getId()); -// $this->assertEquals('1', $updatedProduct->getAttribute('userProfile')->getId()); -// -// static::getDatabase()->deleteCollection('userProfiles'); -// static::getDatabase()->deleteCollection('links'); -// static::getDatabase()->deleteCollection('settings'); -// static::getDatabase()->deleteCollection('group'); -// static::getDatabase()->deleteCollection('community'); -// static::getDatabase()->deleteCollection('videos'); -// static::getDatabase()->deleteCollection('products'); -// static::getDatabase()->deleteCollection('appearance'); -// } -// -// public function testLabels(): void -// { -// $this->assertInstanceOf('Utopia\Database\Document', static::getDatabase()->createCollection( -// 'labels_test', -// )); -// static::getDatabase()->createAttribute('labels_test', 'attr1', Database::VAR_STRING, 10, false); -// -// static::getDatabase()->createDocument('labels_test', new Document([ -// '$id' => 'doc1', -// 'attr1' => 'value1', -// '$permissions' => [ -// Permission::read(Role::label('reader')), -// ], -// ])); -// -// $documents = static::getDatabase()->find('labels_test'); -// -// $this->assertEmpty($documents); -// -// Authorization::setRole(Role::label('reader')->toString()); -// -// $documents = static::getDatabase()->find('labels_test'); -// -// $this->assertCount(1, $documents); -// } -// -// public function testEmptyOperatorValues(): void -// { -// try { -// static::getDatabase()->findOne('documents', [ -// Query::equal('string', []), -// ]); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertInstanceOf(Exception::class, $e); -// $this->assertEquals('Invalid query: Equal queries require at least one value.', $e->getMessage()); -// } -// -// try { -// static::getDatabase()->findOne('documents', [ -// Query::search('string', null), -// ]); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertInstanceOf(Exception::class, $e); -// $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); -// } -// -// try { -// static::getDatabase()->findOne('documents', [ -// Query::notEqual('string', []), -// ]); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertInstanceOf(Exception::class, $e); -// $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); -// } -// -// try { -// static::getDatabase()->findOne('documents', [ -// Query::lessThan('string', []), -// ]); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertInstanceOf(Exception::class, $e); -// $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); -// } -// -// try { -// static::getDatabase()->findOne('documents', [ -// Query::lessThanEqual('string', []), -// ]); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertInstanceOf(Exception::class, $e); -// $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); -// } -// -// try { -// static::getDatabase()->findOne('documents', [ -// Query::greaterThan('string', []), -// ]); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertInstanceOf(Exception::class, $e); -// $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); -// } -// -// try { -// static::getDatabase()->findOne('documents', [ -// Query::greaterThanEqual('string', []), -// ]); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertInstanceOf(Exception::class, $e); -// $this->assertEquals('Invalid query: Query type does not match expected: string', $e->getMessage()); -// } -// -// try { -// static::getDatabase()->findOne('documents', [ -// Query::contains('string', []), -// ]); -// $this->fail('Failed to throw exception'); -// } catch (Exception $e) { -// $this->assertInstanceOf(Exception::class, $e); -// $this->assertEquals('Invalid query: Contains queries require at least one value.', $e->getMessage()); -// } -// } -// -// public function testEvents(): void -// { -// Authorization::skip(function () { -// $database = static::getDatabase(); -// -// $events = [ -// Database::EVENT_DATABASE_CREATE, -// Database::EVENT_DATABASE_LIST, -// Database::EVENT_COLLECTION_CREATE, -// Database::EVENT_COLLECTION_LIST, -// Database::EVENT_COLLECTION_READ, -// Database::EVENT_ATTRIBUTE_CREATE, -// Database::EVENT_ATTRIBUTE_UPDATE, -// Database::EVENT_INDEX_CREATE, -// Database::EVENT_DOCUMENT_CREATE, -// Database::EVENT_DOCUMENT_UPDATE, -// Database::EVENT_DOCUMENT_READ, -// Database::EVENT_DOCUMENT_FIND, -// Database::EVENT_DOCUMENT_FIND, -// Database::EVENT_DOCUMENT_COUNT, -// Database::EVENT_DOCUMENT_SUM, -// Database::EVENT_DOCUMENT_INCREASE, -// Database::EVENT_DOCUMENT_DECREASE, -// Database::EVENT_INDEX_DELETE, -// Database::EVENT_DOCUMENT_DELETE, -// Database::EVENT_ATTRIBUTE_DELETE, -// Database::EVENT_COLLECTION_DELETE, -// Database::EVENT_DATABASE_DELETE, -// ]; -// -// $database->on(Database::EVENT_ALL, 'test', function ($event, $data) use (&$events) { -// $shifted = array_shift($events); -// -// $this->assertEquals($shifted, $event); -// }); -// -// if ($this->getDatabase()->getAdapter()->getSupportForSchemas()) { -// $database->setDefaultDatabase('hellodb'); -// $database->create(); -// } else { -// array_shift($events); -// } -// -// $database->list(); -// -// $database->setDefaultDatabase($this->testDatabase); -// -// $collectionId = ID::unique(); -// $database->createCollection($collectionId); -// $database->listCollections(); -// $database->getCollection($collectionId); -// $database->createAttribute($collectionId, 'attr1', Database::VAR_INTEGER, 2, false); -// $database->updateAttributeRequired($collectionId, 'attr1', true); -// $indexId1 = 'index2_' . uniqid(); -// $database->createIndex($collectionId, $indexId1, Database::INDEX_KEY, ['attr1']); -// -// $document = $database->createDocument($collectionId, new Document([ -// '$id' => 'doc1', -// 'attr1' => 10, -// '$permissions' => [ -// Permission::delete(Role::any()), -// Permission::update(Role::any()), -// Permission::read(Role::any()), -// ], -// ])); -// -// $executed = false; -// $database->on(Database::EVENT_ALL, 'should-not-execute', function ($event, $data) use (&$executed) { -// $executed = true; -// }); -// -// $database->silent(function () use ($database, $collectionId, $document) { -// $database->updateDocument($collectionId, 'doc1', $document->setAttribute('attr1', 15)); -// $database->getDocument($collectionId, 'doc1'); -// $database->find($collectionId); -// $database->findOne($collectionId); -// $database->count($collectionId); -// $database->sum($collectionId, 'attr1'); -// $database->increaseDocumentAttribute($collectionId, $document->getId(), 'attr1'); -// $database->decreaseDocumentAttribute($collectionId, $document->getId(), 'attr1'); -// }, ['should-not-execute']); -// -// $this->assertFalse($executed); -// -// $database->deleteIndex($collectionId, $indexId1); -// $database->deleteDocument($collectionId, 'doc1'); -// $database->deleteAttribute($collectionId, 'attr1'); -// $database->deleteCollection($collectionId); -// $database->delete('hellodb'); -// }); -// } } From 252321dc3a69cb85f9a984dd10f2ce6d3a31add3 Mon Sep 17 00:00:00 2001 From: fogelito Date: Mon, 23 Oct 2023 12:31:52 +0300 Subject: [PATCH 7/9] Some changes --- composer.lock | 10 +- docker-compose.yml | 1 + src/Database/Adapter/MariaDB.php | 23 +-- src/Database/Adapter/Mongo.php | 6 + src/Database/Adapter/Postgres.php | 16 +- src/Database/Adapter/SQL.php | 79 +++---- src/Database/Database.php | 2 + src/Database/Query.php | 32 ++- src/Database/Validator/IndexedQueries.php | 6 - src/Database/Validator/Queries.php | 21 ++ src/Database/Validator/Query/Filter.php | 27 ++- tests/Database/Adapter/MariaDBTest.php | 120 +++++------ tests/Database/Adapter/MongoDBTest.php | 194 +++++++++--------- tests/Database/Adapter/PostgresTest.php | 116 +++++------ tests/Database/Adapter/SQLiteTest.php | 136 ++++++------ tests/Database/Base.php | 42 ++-- tests/Database/QueryTest.php | 2 + tests/Database/Validator/Query/FilterTest.php | 17 ++ tests/Database/Validator/QueryTest.php | 37 ++++ 19 files changed, 480 insertions(+), 407 deletions(-) diff --git a/composer.lock b/composer.lock index d476dd777..3f23d132d 100644 --- a/composer.lock +++ b/composer.lock @@ -839,16 +839,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.38", + "version": "1.10.39", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "5302bb402c57f00fb3c2c015bac86e0827e4b691" + "reference": "d9dedb0413f678b4d03cbc2279a48f91592c97c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/5302bb402c57f00fb3c2c015bac86e0827e4b691", - "reference": "5302bb402c57f00fb3c2c015bac86e0827e4b691", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d9dedb0413f678b4d03cbc2279a48f91592c97c4", + "reference": "d9dedb0413f678b4d03cbc2279a48f91592c97c4", "shasum": "" }, "require": { @@ -897,7 +897,7 @@ "type": "tidelift" } ], - "time": "2023-10-06T14:19:14+00:00" + "time": "2023-10-17T15:46:26+00:00" }, { "name": "phpunit/php-code-coverage", diff --git a/docker-compose.yml b/docker-compose.yml index 296cd6095..d0b4e18f9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,6 +14,7 @@ services: - ./src:/usr/src/code/src - ./tests:/usr/src/code/tests - ./phpunit.xml:/usr/src/code/phpunit.xml + - ./vendor/utopia-php/mongo:/usr/src/code/vendor/utopia-php/mongo ports: - "8708:8708" diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index b2aeb26d0..405baea72 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -1032,17 +1032,10 @@ public function find(string $collection, array $queries = [], ?int $limit = 25, } } -// $filters = $this->getSQLConditions($queries); -// if(!empty($filters)){ -// $where[] = $filters; -// } - -// foreach ($queries as $query) { -// if ($query->getMethod() === Query::TYPE_SELECT) { -// continue; -// } -// $where[] = $this->getSQLCondition($query); -// } + $conditions = $this->getSQLConditions($queries); + if(!empty($conditions)){ + $where[] = $conditions; + } if (Authorization::$status) { $where[] = $this->getSQLPermissionsCondition($name, $roles); @@ -1063,9 +1056,6 @@ public function find(string $collection, array $queries = [], ?int $limit = 25, {$sqlLimit}; "; - var_dump($sql); - - if ($timeout || static::$timeout) { $sql = $this->setTimeoutForQuery($sql, $timeout ? $timeout : static::$timeout); } @@ -1157,8 +1147,9 @@ public function count(string $collection, array $queries = [], ?int $max = null, $where = []; $limit = \is_null($max) ? '' : 'LIMIT :max'; - foreach ($queries as $query) { - $where[] = $this->getSQLCondition($query); + $conditions = $this->getSQLConditions($queries); + if(!empty($conditions)){ + $where[] = $conditions; } if (Authorization::$status) { diff --git a/src/Database/Adapter/Mongo.php b/src/Database/Adapter/Mongo.php index df827a405..5cf07ed01 100644 --- a/src/Database/Adapter/Mongo.php +++ b/src/Database/Adapter/Mongo.php @@ -1252,6 +1252,11 @@ protected function buildFilters(array $queries): array continue; } + if ($query->getMethod() === Query::TYPE_OR) { + $filters['$or'][] = $this->buildFilters($query->getValue()); + continue; + } + if ($query->getAttribute() === '$id') { $query->setAttribute('_uid'); } elseif ($query->getAttribute() === '$internalId') { @@ -1326,6 +1331,7 @@ protected function getQueryOperator(string $operator): string Query::TYPE_BETWEEN => 'between', Query::TYPE_STARTS_WITH, Query::TYPE_ENDS_WITH => '$regex', + Query::TYPE_OR => '$or', default => throw new DatabaseException('Unknown operator:' . $operator . '. Must be one of ' . Query::TYPE_EQUAL . ', ' . Query::TYPE_NOT_EQUAL . ', ' . Query::TYPE_LESSER . ', ' . Query::TYPE_LESSER_EQUAL . ', ' . Query::TYPE_GREATER . ', ' . Query::TYPE_GREATER_EQUAL . ', ' . Query::TYPE_IS_NULL . ', ' . Query::TYPE_IS_NOT_NULL . ', ' . Query::TYPE_BETWEEN . ', ' . Query::TYPE_CONTAINS . ', ' . Query::TYPE_SEARCH . ', ' . Query::TYPE_SELECT), }; } diff --git a/src/Database/Adapter/Postgres.php b/src/Database/Adapter/Postgres.php index 06dfeef9f..442ed074a 100644 --- a/src/Database/Adapter/Postgres.php +++ b/src/Database/Adapter/Postgres.php @@ -1039,14 +1039,11 @@ public function find(string $collection, array $queries = [], ?int $limit = 25, } } - foreach ($queries as $query) { - if ($query->getMethod() === Query::TYPE_SELECT) { - continue; - } - $where[] = $this->getSQLCondition($query); + $conditions = $this->getSQLConditions($queries); + if(!empty($conditions)){ + $where[] = $conditions; } - if (Authorization::$status) { $where[] = $this->getSQLPermissionsCondition($name, $roles); } @@ -1157,10 +1154,11 @@ public function count(string $collection, array $queries = [], ?int $max = null, $where = []; $limit = \is_null($max) ? '' : 'LIMIT :max'; - foreach ($queries as $query) { - $where[] = $this->getSQLCondition($query); + $conditions = $this->getSQLConditions($queries); + if(!empty($conditions)){ + $where[] = $conditions; } - + if (Authorization::$status) { $where[] = $this->getSQLPermissionsCondition($name, $roles); } diff --git a/src/Database/Adapter/SQL.php b/src/Database/Adapter/SQL.php index b60ea61ae..8b3ad993c 100644 --- a/src/Database/Adapter/SQL.php +++ b/src/Database/Adapter/SQL.php @@ -934,31 +934,29 @@ public function getMaxIndexLength(): int return 768; } - -/** - * @param $stmt - * @param Query[] $queries - * @return void - */ -public function bindNestedConditionValue($stmt, array $queries = []){ - /** @var PDOStatement $stmt */ - foreach ($queries as $query) { - if(is_array($query)){ - $this->bindNestedConditionValue($stmt, $query); - } - else { - if ($query->getMethod() === Query::TYPE_SEARCH) continue; - if ($query->getMethod() === Query::TYPE_OR){ - $this->bindNestedConditionValue($stmt, $query->getValues()); // Nested $queries are in values - }else { - foreach ($query->getValues() as $key => $value) { - $placeholder = $this->getSQLPlaceholder($query).'_'.$key; - $stmt->bindValue($placeholder, $value, $this->getPDOType($value)); - } - } - } - }} - +///** +// * @param $stmt +// * @param Query[] $queries +// * @return void +// */ +//public function bindNestedConditionValue($stmt, array $queries = []){ +// /** @var PDOStatement $stmt */ +// foreach ($queries as $query) { +// if(is_array($query)){ +// $this->bindNestedConditionValue($stmt, $query); +// } +// else { +// if ($query->getMethod() === Query::TYPE_SEARCH) continue; +// if ($query->getMethod() === Query::TYPE_OR){ +// $this->bindNestedConditionValue($stmt, $query->getValues()); // Nested $queries are in values +// }else { +// foreach ($query->getValues() as $key => $value) { +// $placeholder = $this->getSQLPlaceholder($query).'_'.$key; +// $stmt->bindValue($placeholder, $value, $this->getPDOType($value)); +// } +// } +// } +// }} public function getSQLConditions(array $queries = []): string { @@ -974,12 +972,6 @@ public function getSQLConditions(array $queries = []): string if($query->getMethod() === Query::TYPE_OR){ $separator = 'or'; $conditions[] = $this->getSQLConditions($query->getValue()); - -// foreach ($query->getValue() as $queriesArray) { -// var_dump($queriesArray); -// -// $conditions[] = $this->getSQLConditions($queriesArray); -// } } else $conditions[] = $this->getSQLCondition($query); } @@ -987,29 +979,4 @@ public function getSQLConditions(array $queries = []): string $tmp = implode(' ' . $separator . ' ', $conditions); return empty($tmp) ? '' : '(' . $tmp . ')'; } - - /** - * @param $stmt - * @param Query[] $queries - * @return void - */ - public function bindNestedConditionValue2($stmt, array $queries = []){ - /** @var PDOStatement $stmt */ - foreach ($queries as $query) { - if(is_array($query)){ - $this->bindNestedConditionValue($stmt, $query); - } - else { - if ($query->getMethod() === Query::TYPE_SEARCH) continue; - if ($query->getMethod() === Query::TYPE_OR){ - $this->bindNestedConditionValue($stmt, $query->getValues()); // Nested $queries are in values - }else { - foreach ($query->getValues() as $key => $value) { - $placeholder = $this->getSQLPlaceholder($query).'_'.$key; - $stmt->bindValue($placeholder, $value, $this->getPDOType($value)); - } - } - } - } - } } \ No newline at end of file diff --git a/src/Database/Database.php b/src/Database/Database.php index c530af9d5..41b8aab57 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -4181,6 +4181,8 @@ public function find(string $collection, array $queries = [], ?int $timeout = nu } } + unset($query); // It is used previously as reference + // Remove internal attributes which are not queried foreach ($queries as $query) { if ($query->getMethod() === Query::TYPE_SELECT) { diff --git a/src/Database/Query.php b/src/Database/Query.php index f4601cd2d..7f87b2f35 100644 --- a/src/Database/Query.php +++ b/src/Database/Query.php @@ -20,8 +20,8 @@ class Query public const TYPE_BETWEEN = 'between'; public const TYPE_STARTS_WITH = 'startsWith'; public const TYPE_ENDS_WITH = 'endsWith'; - public const TYPE_OR = 'or'; public const TYPE_SELECT = 'select'; + public const TYPE_OR = 'or'; // Order methods public const TYPE_ORDERDESC = 'orderDesc'; @@ -178,6 +178,7 @@ public static function isMethod(string $value): bool self::TYPE_BETWEEN, self::TYPE_STARTS_WITH, self::TYPE_ENDS_WITH, + self::TYPE_OR, self::TYPE_SELECT => true, default => false, }; @@ -192,6 +193,12 @@ public static function isMethod(string $value): bool */ public static function parse(string $filter): self { + + if(substr($filter, 0, 3) === 'or('){ + var_dump('Found or operation !!!!!!!!!!!'); + var_dump($filter); + } + // Init empty vars we fill later $method = ''; $params = []; @@ -363,6 +370,13 @@ public static function parse(string $filter): self } return new self($method); + case self::TYPE_OR: + var_dump($method); + var_dump($parsedParams); + var_dump('parsing end ........'); + die; + return new self($method); + default: return new self($method); } @@ -837,4 +851,20 @@ public static function parseQueries(array $queries): array return $parsed; } + + /** + * Is isNested + * + * Function will return true if nested method + * + * @return bool + */ + public function isNested(): bool + { + if($this->getMethod() === self::TYPE_OR){ + return true; + } + + return false; + } } diff --git a/src/Database/Validator/IndexedQueries.php b/src/Database/Validator/IndexedQueries.php index e1a494114..3938ecaab 100644 --- a/src/Database/Validator/IndexedQueries.php +++ b/src/Database/Validator/IndexedQueries.php @@ -73,12 +73,6 @@ public function isValid($value): bool $query = Query::parse($query); } - if($query->getMethod() === Query::TYPE_OR){ - if(!self::isValid($query->getValue())){ - return false; - } - } - $queries[] = $query; } diff --git a/src/Database/Validator/Queries.php b/src/Database/Validator/Queries.php index 808553fd8..18bf50974 100644 --- a/src/Database/Validator/Queries.php +++ b/src/Database/Validator/Queries.php @@ -51,6 +51,8 @@ public function isValid($value): bool return false; } + $queries = []; + foreach ($value as $query) { if (!$query instanceof Query) { try { @@ -61,6 +63,14 @@ public function isValid($value): bool } } + if($query->isNested()){ + if(!self::isValid($query->getValue())){ + return false; + } + } + + $queries[] = $query; + $method = $query->getMethod(); $methodType = match ($method) { Query::TYPE_SELECT => Base::METHOD_TYPE_SELECT, @@ -106,6 +116,17 @@ public function isValid($value): bool } } + // todo: Is there a better way to assure or does not come first? + // todo: what to do about and nested later on when comes first? + $grouped = Query::groupByType($queries); + $filters = $grouped['filters']; + if(isset($filters[0])){ + if ($filters[0]->getMethod() === Query::TYPE_OR) { + $this->message = \ucfirst($filters[0]->getMethod()) . ' query can not come first'; + return false; + } + } + return true; } diff --git a/src/Database/Validator/Query/Filter.php b/src/Database/Validator/Query/Filter.php index 5ba16e715..fe47b692b 100644 --- a/src/Database/Validator/Query/Filter.php +++ b/src/Database/Validator/Query/Filter.php @@ -173,22 +173,19 @@ public function isValid($value): bool return $this->isValidAttributeAndValues($attribute, $value->getValues()); case Query::TYPE_OR: - var_dump($value); - -// if (count($value->getValues()) != 2) { -// $this->message = \ucfirst($method) . ' queries require exactly two values.'; -// return false; -// } -// -// if (!is_array($value->getValues()[0]) || !is_array($value->getValues()[1])) { -// $this->message = \ucfirst($method) . ' queries requires arrays'; -// return false; -// } -// -// $queries = array_merge($value->getValues()[0], $value->getValues()[1]); -// var_dump($queries); + $filters = Query::groupByType($value->getValue())['filters']; + + if(count($value->getValue()) !== count($filters)){ + $this->message = \ucfirst($method) . ' queries requires only filters'; + return false; + } + + if(empty($filters)){ + $this->message = \ucfirst($method) . ' queries require at least one query'; + return false; + } + return true; - break; default: return false; diff --git a/tests/Database/Adapter/MariaDBTest.php b/tests/Database/Adapter/MariaDBTest.php index 8831aef4c..af616f260 100644 --- a/tests/Database/Adapter/MariaDBTest.php +++ b/tests/Database/Adapter/MariaDBTest.php @@ -1,61 +1,61 @@ connect('redis', 6379); -// $redis->flushAll(); -// $cache = new Cache(new RedisAdapter($redis)); -// -// $database = new Database(new MariaDB($pdo), $cache); -// $database->setDefaultDatabase('utopiaTests'); -// $database->setNamespace('myapp_'.uniqid()); -// -// if ($database->exists('utopiaTests')) { -// $database->delete('utopiaTests'); -// } -// -// $database->create(); -// -// return self::$database = $database; -// } -//} + +namespace Utopia\Tests\Adapter; + +use PDO; +use Redis; +use Utopia\Database\Database; +use Utopia\Database\Adapter\MariaDB; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\Redis as RedisAdapter; +use Utopia\Tests\Base; + +class MariaDBTest extends Base +{ + public static ?Database $database = null; + + // TODO@kodumbeats hacky way to identify adapters for tests + // Remove once all methods are implemented + /** + * Return name of adapter + * + * @return string + */ + public static function getAdapterName(): string + { + return "mariadb"; + } + + /** + * @return Database + */ + public static function getDatabase(): Database + { + if (!is_null(self::$database)) { + return self::$database; + } + + $dbHost = 'mariadb'; + $dbPort = '3306'; + $dbUser = 'root'; + $dbPass = 'password'; + + $pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, MariaDB::getPDOAttributes()); + $redis = new Redis(); + $redis->connect('redis', 6379); + $redis->flushAll(); + $cache = new Cache(new RedisAdapter($redis)); + + $database = new Database(new MariaDB($pdo), $cache); + $database->setDefaultDatabase('utopiaTests'); + $database->setNamespace('myapp_'.uniqid()); + + if ($database->exists('utopiaTests')) { + $database->delete('utopiaTests'); + } + + $database->create(); + + return self::$database = $database; + } +} diff --git a/tests/Database/Adapter/MongoDBTest.php b/tests/Database/Adapter/MongoDBTest.php index 3cfcd753a..62c48ae37 100644 --- a/tests/Database/Adapter/MongoDBTest.php +++ b/tests/Database/Adapter/MongoDBTest.php @@ -1,98 +1,98 @@ connect('redis', 6379); -// $redis->flushAll(); -// $cache = new Cache(new RedisAdapter($redis)); -// -// $schema = 'utopiaTests'; // same as $this->testDatabase -// $client = new Client( -// $schema, -// 'mongo', -// 27017, -// 'root', -// 'example', -// false -// ); -// -// $database = new Database(new Mongo($client), $cache); -// $database->setDefaultDatabase($schema); -// $database->setNamespace('myapp_' . uniqid()); -// -// if ($database->exists('utopiaTests')) { -// $database->delete('utopiaTests'); -// } -// -// $database->create(); -// -// return self::$database = $database; -// } -// -// /** -// * @throws Exception -// */ -// public function testCreateExistsDelete(): void -// { -// // Mongo creates databases on the fly, so exists would always pass. So we override this test to remove the exists check. -// $this->assertNotNull(static::getDatabase()->create()); -// $this->assertEquals(true, static::getDatabase()->delete($this->testDatabase)); -// $this->assertEquals(true, static::getDatabase()->create()); -// $this->assertEquals(true, static::getDatabase()->setDefaultDatabase($this->testDatabase)); -// } -// -// public function testRenameAttribute(): void -// { -// $this->assertTrue(true); -// } -// -// public function testRenameAttributeExisting(): void -// { -// $this->assertTrue(true); -// } -// -// public function testUpdateAttributeStructure(): void -// { -// $this->assertTrue(true); -// } -// -// public function testKeywords(): void -// { -// $this->assertTrue(true); -// } -//} + +namespace Utopia\Tests\Adapter; + +use Exception; +use Redis; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\Redis as RedisAdapter; +use Utopia\Database\Adapter\Mongo; +use Utopia\Database\Database; +use Utopia\Mongo\Client; +use Utopia\Tests\Base; + +class MongoDBTest extends Base +{ + public static ?Database $database = null; + + + /** + * Return name of adapter + * + * @return string + */ + public static function getAdapterName(): string + { + return "mongodb"; + } + + /** + * @return Database + * @throws Exception + */ + public static function getDatabase(): Database + { + if (!is_null(self::$database)) { + return self::$database; + } + + $redis = new Redis(); + $redis->connect('redis', 6379); + $redis->flushAll(); + $cache = new Cache(new RedisAdapter($redis)); + + $schema = 'utopiaTests'; // same as $this->testDatabase + $client = new Client( + $schema, + 'mongo', + 27017, + 'root', + 'example', + false + ); + + $database = new Database(new Mongo($client), $cache); + $database->setDefaultDatabase($schema); + $database->setNamespace('myapp_' . uniqid()); + + if ($database->exists('utopiaTests')) { + $database->delete('utopiaTests'); + } + + $database->create(); + + return self::$database = $database; + } + + /** + * @throws Exception + */ + public function testCreateExistsDelete(): void + { + // Mongo creates databases on the fly, so exists would always pass. So we override this test to remove the exists check. + $this->assertNotNull(static::getDatabase()->create()); + $this->assertEquals(true, static::getDatabase()->delete($this->testDatabase)); + $this->assertEquals(true, static::getDatabase()->create()); + $this->assertEquals(true, static::getDatabase()->setDefaultDatabase($this->testDatabase)); + } + + public function testRenameAttribute(): void + { + $this->assertTrue(true); + } + + public function testRenameAttributeExisting(): void + { + $this->assertTrue(true); + } + + public function testUpdateAttributeStructure(): void + { + $this->assertTrue(true); + } + + public function testKeywords(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/Database/Adapter/PostgresTest.php b/tests/Database/Adapter/PostgresTest.php index a4b7b6b9c..9965a8684 100644 --- a/tests/Database/Adapter/PostgresTest.php +++ b/tests/Database/Adapter/PostgresTest.php @@ -1,59 +1,59 @@ connect('redis', 6379); -// $redis->flushAll(); -// $cache = new Cache(new RedisAdapter($redis)); -// -// $database = new Database(new Postgres($pdo), $cache); -// $database->setDefaultDatabase('utopiaTests'); -// $database->setNamespace('myapp_'.uniqid()); -// -// if ($database->exists('utopiaTests')) { -// $database->delete('utopiaTests'); -// } -// -// $database->create(); -// -// return self::$database = $database; -// } -//} + +namespace Utopia\Tests\Adapter; + +use PDO; +use Redis; +use Utopia\Database\Database; +use Utopia\Database\Adapter\Postgres; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\Redis as RedisAdapter; +use Utopia\Tests\Base; + +class PostgresTest extends Base +{ + public static ?Database $database = null; + + /** + * Return name of adapter + * + * @return string + */ + public static function getAdapterName(): string + { + return "postgres"; + } + + /** + * @reture Adapter + */ + public static function getDatabase(): Database + { + if (!is_null(self::$database)) { + return self::$database; + } + + $dbHost = 'postgres'; + $dbPort = '5432'; + $dbUser = 'root'; + $dbPass = 'password'; + + $pdo = new PDO("pgsql:host={$dbHost};port={$dbPort};", $dbUser, $dbPass, Postgres::getPDOAttributes()); + $redis = new Redis(); + $redis->connect('redis', 6379); + $redis->flushAll(); + $cache = new Cache(new RedisAdapter($redis)); + + $database = new Database(new Postgres($pdo), $cache); + $database->setDefaultDatabase('utopiaTests'); + $database->setNamespace('myapp_'.uniqid()); + + if ($database->exists('utopiaTests')) { + $database->delete('utopiaTests'); + } + + $database->create(); + + return self::$database = $database; + } +} diff --git a/tests/Database/Adapter/SQLiteTest.php b/tests/Database/Adapter/SQLiteTest.php index 8f4a176de..8a8d24199 100644 --- a/tests/Database/Adapter/SQLiteTest.php +++ b/tests/Database/Adapter/SQLiteTest.php @@ -1,69 +1,69 @@ connect('redis', 6379); -// $redis->flushAll(); -// -// $cache = new Cache(new RedisAdapter($redis)); -// -// $database = new Database(new SQLite($pdo), $cache); -// $database->setDefaultDatabase('utopiaTests'); -// $database->setNamespace('myapp_'.uniqid()); -// -// return self::$database = $database; -// } -//} + +namespace Utopia\Tests\Adapter; + +use PDO; +use Redis; +use Utopia\Cache\Cache; +use Utopia\Database\Database; +use Utopia\Cache\Adapter\Redis as RedisAdapter; +use Utopia\Database\Adapter\SQLite; +use Utopia\Tests\Base; + +class SQLiteTest extends Base +{ + public static ?Database $database = null; + + // TODO@kodumbeats hacky way to identify adapters for tests + // Remove once all methods are implemented + /** + * Return name of adapter + * + * @return string + */ + public static function getAdapterName(): string + { + return "sqlite"; + } + + /** + * + * @return int + */ + public static function getUsedIndexes(): int + { + return SQLite::getCountOfDefaultIndexes(); + } + + /** + * @return Database + */ + public static function getDatabase(): Database + { + if (!is_null(self::$database)) { + return self::$database; + } + + $sqliteDir = __DIR__."/database.sql"; + + if (file_exists($sqliteDir)) { + unlink($sqliteDir); + } + + $dsn = $sqliteDir; + $dsn = 'memory'; // Overwrite for fast tests + $pdo = new PDO("sqlite:" . $dsn, null, null, SQLite::getPDOAttributes()); + + $redis = new Redis(); + $redis->connect('redis', 6379); + $redis->flushAll(); + + $cache = new Cache(new RedisAdapter($redis)); + + $database = new Database(new SQLite($pdo), $cache); + $database->setDefaultDatabase('utopiaTests'); + $database->setNamespace('myapp_'.uniqid()); + + return self::$database = $database; + } +} diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 9f18c6357..7399127b6 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -2746,17 +2746,6 @@ public function testFindEdgeCases(Document $document): void } } - public function testOr(){ - $documents = static::getDatabase()->find('movies', [ - Query::equal('director', ['TBD']), - Query::or([ - Query::equal('director', ['Joe Johnston']) - ]) - ]); - - $this->assertEquals(3, count($documents)); - } - public function testOrFirstQuery(){ try { static::getDatabase()->find('movies', [ @@ -2766,7 +2755,7 @@ public function testOrFirstQuery(){ ]); $this->fail('Failed to throw exception'); } catch(Exception $e) { - // $this->assertEquals('Please throw exception', $e->getMessage()); + $this->assertEquals('Or query can not come first', $e->getMessage()); } } @@ -2776,10 +2765,20 @@ public function testOrMultipleQueries(){ Query::or([ Query::equal('director', ['TBD']), Query::equal('year', [2026]) - ]) + ]), ]); $this->assertEquals(2, count($documents)); + + $count = static::getDatabase()->count('movies', [ + Query::equal('director', ['Joe Johnston']), + Query::or([ + Query::equal('director', ['TBD']), + Query::equal('year', [2026]) + ]) + ]); + + $this->assertEquals(2, $count); } public function testOrNested(){ @@ -2794,10 +2793,21 @@ public function testOrNested(){ ]) ]); - var_dump($documents); - $this->assertEquals(3, count($documents)); $this->assertArrayNotHasKey('name', $documents[0]); - die; + $this->assertEquals(3, count($documents)); + + $count = static::getDatabase()->count('movies', [ + Query::select(['director']), + Query::equal('director', ['Joe Johnston']), + Query::or([ + Query::equal('name', ['Frozen']), + Query::or([ + Query::equal('name', ['Frozen II']), + ]) + ]) + ]); + + $this->assertEquals(3, $count); } /** diff --git a/tests/Database/QueryTest.php b/tests/Database/QueryTest.php index a7e497959..9ed5cb1eb 100644 --- a/tests/Database/QueryTest.php +++ b/tests/Database/QueryTest.php @@ -453,6 +453,7 @@ public function testisMethod(): void $this->assertTrue(Query::isMethod('isNotNull')); $this->assertTrue(Query::isMethod('between')); $this->assertTrue(Query::isMethod('select')); + $this->assertTrue(Query::isMethod('or')); $this->assertTrue(Query::isMethod(Query::TYPE_EQUAL)); $this->assertTrue(Query::isMethod(Query::TYPE_NOT_EQUAL)); @@ -472,6 +473,7 @@ public function testisMethod(): void $this->assertTrue(Query::isMethod(QUERY::TYPE_IS_NOT_NULL)); $this->assertTrue(Query::isMethod(QUERY::TYPE_BETWEEN)); $this->assertTrue(Query::isMethod(QUERY::TYPE_SELECT)); + $this->assertTrue(Query::isMethod(QUERY::TYPE_OR)); /* Tests for aliases if we enable them: diff --git a/tests/Database/Validator/Query/FilterTest.php b/tests/Database/Validator/Query/FilterTest.php index 752dd97a3..c46c3d1f6 100644 --- a/tests/Database/Validator/Query/FilterTest.php +++ b/tests/Database/Validator/Query/FilterTest.php @@ -93,4 +93,21 @@ public function testEmptyValues(): void $this->assertFalse($this->validator->isValid(Query::parse('greaterThan("attr", [])'))); $this->assertEquals('GreaterThan queries require exactly one value.', $this->validator->getDescription()); } + + public function testOr(): void + { + // todo: How to parse or queries + + $this->assertFalse(false); +// +// $q = Query::parse('or([ +// search("attr", ["American pie"]) +// ])'); +// +// var_dump($q); +// +// $this->assertFalse($this->validator->isValid($q)); +// $this->assertTrue($this->validator->isValid($q)); +// $this->assertEquals('NotEqual queries require exactly one value.', $this->validator->getDescription()); + } } diff --git a/tests/Database/Validator/QueryTest.php b/tests/Database/Validator/QueryTest.php index 76f78c1a5..e95bb96a4 100644 --- a/tests/Database/Validator/QueryTest.php +++ b/tests/Database/Validator/QueryTest.php @@ -311,4 +311,41 @@ public function testQueryEmpty(): void $response = $validator->isValid([Query::isNull('price')]); $this->assertEquals(true, $response); } + + /** + * @throws Exception + */ + public function testOrQuery(): void + { + $validator = new Documents($this->attributes, []); + + $this->assertFalse($validator->isValid( + [Query::or( + [Query::equal('title', [''])] + )])); + + $this->assertEquals('Or query can not come first', $validator->getDescription()); + + $this->assertFalse($validator->isValid( + [ + Query::equal('price', [0]), + Query::or( + [ + Query::equal('not_found', ['']) + ] + )])); + + $this->assertEquals('Invalid query: Attribute not found in schema: not_found', $validator->getDescription()); + + $this->assertFalse($validator->isValid( + [ + Query::equal('price', [10]), + Query::or( + [ + Query::limit(1) + ] + )])); + + $this->assertEquals('Invalid query: Or queries requires only filters', $validator->getDescription()); + } } From 28299750232a6aa8710516417cadd4ca26f3e049 Mon Sep 17 00:00:00 2001 From: fogelito Date: Tue, 24 Oct 2023 13:38:49 +0300 Subject: [PATCH 8/9] cleanFilters tests --- src/Database/Adapter/Mongo.php | 1 - tests/Database/Adapter/MariaDBTest.php | 120 +++++++-------- tests/Database/Adapter/MongoDBTest.php | 194 ++++++++++++------------ tests/Database/Adapter/PostgresTest.php | 116 +++++++------- tests/Database/Adapter/SQLiteTest.php | 136 ++++++++--------- 5 files changed, 283 insertions(+), 284 deletions(-) diff --git a/src/Database/Adapter/Mongo.php b/src/Database/Adapter/Mongo.php index 5cf07ed01..b811e85dd 100644 --- a/src/Database/Adapter/Mongo.php +++ b/src/Database/Adapter/Mongo.php @@ -933,7 +933,6 @@ public function find(string $collection, array $queries = [], ?int $limit = 25, $filters = $this->recursiveReplace($filters, '$', '_', $this->operators); $filters = $this->timeFilter($filters); - /** * @var array */ diff --git a/tests/Database/Adapter/MariaDBTest.php b/tests/Database/Adapter/MariaDBTest.php index af616f260..8831aef4c 100644 --- a/tests/Database/Adapter/MariaDBTest.php +++ b/tests/Database/Adapter/MariaDBTest.php @@ -1,61 +1,61 @@ connect('redis', 6379); - $redis->flushAll(); - $cache = new Cache(new RedisAdapter($redis)); - - $database = new Database(new MariaDB($pdo), $cache); - $database->setDefaultDatabase('utopiaTests'); - $database->setNamespace('myapp_'.uniqid()); - - if ($database->exists('utopiaTests')) { - $database->delete('utopiaTests'); - } - - $database->create(); - - return self::$database = $database; - } -} +// +//namespace Utopia\Tests\Adapter; +// +//use PDO; +//use Redis; +//use Utopia\Database\Database; +//use Utopia\Database\Adapter\MariaDB; +//use Utopia\Cache\Cache; +//use Utopia\Cache\Adapter\Redis as RedisAdapter; +//use Utopia\Tests\Base; +// +//class MariaDBTest extends Base +//{ +// public static ?Database $database = null; +// +// // TODO@kodumbeats hacky way to identify adapters for tests +// // Remove once all methods are implemented +// /** +// * Return name of adapter +// * +// * @return string +// */ +// public static function getAdapterName(): string +// { +// return "mariadb"; +// } +// +// /** +// * @return Database +// */ +// public static function getDatabase(): Database +// { +// if (!is_null(self::$database)) { +// return self::$database; +// } +// +// $dbHost = 'mariadb'; +// $dbPort = '3306'; +// $dbUser = 'root'; +// $dbPass = 'password'; +// +// $pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, MariaDB::getPDOAttributes()); +// $redis = new Redis(); +// $redis->connect('redis', 6379); +// $redis->flushAll(); +// $cache = new Cache(new RedisAdapter($redis)); +// +// $database = new Database(new MariaDB($pdo), $cache); +// $database->setDefaultDatabase('utopiaTests'); +// $database->setNamespace('myapp_'.uniqid()); +// +// if ($database->exists('utopiaTests')) { +// $database->delete('utopiaTests'); +// } +// +// $database->create(); +// +// return self::$database = $database; +// } +//} diff --git a/tests/Database/Adapter/MongoDBTest.php b/tests/Database/Adapter/MongoDBTest.php index 62c48ae37..3cfcd753a 100644 --- a/tests/Database/Adapter/MongoDBTest.php +++ b/tests/Database/Adapter/MongoDBTest.php @@ -1,98 +1,98 @@ connect('redis', 6379); - $redis->flushAll(); - $cache = new Cache(new RedisAdapter($redis)); - - $schema = 'utopiaTests'; // same as $this->testDatabase - $client = new Client( - $schema, - 'mongo', - 27017, - 'root', - 'example', - false - ); - - $database = new Database(new Mongo($client), $cache); - $database->setDefaultDatabase($schema); - $database->setNamespace('myapp_' . uniqid()); - - if ($database->exists('utopiaTests')) { - $database->delete('utopiaTests'); - } - - $database->create(); - - return self::$database = $database; - } - - /** - * @throws Exception - */ - public function testCreateExistsDelete(): void - { - // Mongo creates databases on the fly, so exists would always pass. So we override this test to remove the exists check. - $this->assertNotNull(static::getDatabase()->create()); - $this->assertEquals(true, static::getDatabase()->delete($this->testDatabase)); - $this->assertEquals(true, static::getDatabase()->create()); - $this->assertEquals(true, static::getDatabase()->setDefaultDatabase($this->testDatabase)); - } - - public function testRenameAttribute(): void - { - $this->assertTrue(true); - } - - public function testRenameAttributeExisting(): void - { - $this->assertTrue(true); - } - - public function testUpdateAttributeStructure(): void - { - $this->assertTrue(true); - } - - public function testKeywords(): void - { - $this->assertTrue(true); - } -} +// +//namespace Utopia\Tests\Adapter; +// +//use Exception; +//use Redis; +//use Utopia\Cache\Cache; +//use Utopia\Cache\Adapter\Redis as RedisAdapter; +//use Utopia\Database\Adapter\Mongo; +//use Utopia\Database\Database; +//use Utopia\Mongo\Client; +//use Utopia\Tests\Base; +// +//class MongoDBTest extends Base +//{ +// public static ?Database $database = null; +// +// +// /** +// * Return name of adapter +// * +// * @return string +// */ +// public static function getAdapterName(): string +// { +// return "mongodb"; +// } +// +// /** +// * @return Database +// * @throws Exception +// */ +// public static function getDatabase(): Database +// { +// if (!is_null(self::$database)) { +// return self::$database; +// } +// +// $redis = new Redis(); +// $redis->connect('redis', 6379); +// $redis->flushAll(); +// $cache = new Cache(new RedisAdapter($redis)); +// +// $schema = 'utopiaTests'; // same as $this->testDatabase +// $client = new Client( +// $schema, +// 'mongo', +// 27017, +// 'root', +// 'example', +// false +// ); +// +// $database = new Database(new Mongo($client), $cache); +// $database->setDefaultDatabase($schema); +// $database->setNamespace('myapp_' . uniqid()); +// +// if ($database->exists('utopiaTests')) { +// $database->delete('utopiaTests'); +// } +// +// $database->create(); +// +// return self::$database = $database; +// } +// +// /** +// * @throws Exception +// */ +// public function testCreateExistsDelete(): void +// { +// // Mongo creates databases on the fly, so exists would always pass. So we override this test to remove the exists check. +// $this->assertNotNull(static::getDatabase()->create()); +// $this->assertEquals(true, static::getDatabase()->delete($this->testDatabase)); +// $this->assertEquals(true, static::getDatabase()->create()); +// $this->assertEquals(true, static::getDatabase()->setDefaultDatabase($this->testDatabase)); +// } +// +// public function testRenameAttribute(): void +// { +// $this->assertTrue(true); +// } +// +// public function testRenameAttributeExisting(): void +// { +// $this->assertTrue(true); +// } +// +// public function testUpdateAttributeStructure(): void +// { +// $this->assertTrue(true); +// } +// +// public function testKeywords(): void +// { +// $this->assertTrue(true); +// } +//} diff --git a/tests/Database/Adapter/PostgresTest.php b/tests/Database/Adapter/PostgresTest.php index 9965a8684..a4b7b6b9c 100644 --- a/tests/Database/Adapter/PostgresTest.php +++ b/tests/Database/Adapter/PostgresTest.php @@ -1,59 +1,59 @@ connect('redis', 6379); - $redis->flushAll(); - $cache = new Cache(new RedisAdapter($redis)); - - $database = new Database(new Postgres($pdo), $cache); - $database->setDefaultDatabase('utopiaTests'); - $database->setNamespace('myapp_'.uniqid()); - - if ($database->exists('utopiaTests')) { - $database->delete('utopiaTests'); - } - - $database->create(); - - return self::$database = $database; - } -} +// +//namespace Utopia\Tests\Adapter; +// +//use PDO; +//use Redis; +//use Utopia\Database\Database; +//use Utopia\Database\Adapter\Postgres; +//use Utopia\Cache\Cache; +//use Utopia\Cache\Adapter\Redis as RedisAdapter; +//use Utopia\Tests\Base; +// +//class PostgresTest extends Base +//{ +// public static ?Database $database = null; +// +// /** +// * Return name of adapter +// * +// * @return string +// */ +// public static function getAdapterName(): string +// { +// return "postgres"; +// } +// +// /** +// * @reture Adapter +// */ +// public static function getDatabase(): Database +// { +// if (!is_null(self::$database)) { +// return self::$database; +// } +// +// $dbHost = 'postgres'; +// $dbPort = '5432'; +// $dbUser = 'root'; +// $dbPass = 'password'; +// +// $pdo = new PDO("pgsql:host={$dbHost};port={$dbPort};", $dbUser, $dbPass, Postgres::getPDOAttributes()); +// $redis = new Redis(); +// $redis->connect('redis', 6379); +// $redis->flushAll(); +// $cache = new Cache(new RedisAdapter($redis)); +// +// $database = new Database(new Postgres($pdo), $cache); +// $database->setDefaultDatabase('utopiaTests'); +// $database->setNamespace('myapp_'.uniqid()); +// +// if ($database->exists('utopiaTests')) { +// $database->delete('utopiaTests'); +// } +// +// $database->create(); +// +// return self::$database = $database; +// } +//} diff --git a/tests/Database/Adapter/SQLiteTest.php b/tests/Database/Adapter/SQLiteTest.php index 8a8d24199..8f4a176de 100644 --- a/tests/Database/Adapter/SQLiteTest.php +++ b/tests/Database/Adapter/SQLiteTest.php @@ -1,69 +1,69 @@ connect('redis', 6379); - $redis->flushAll(); - - $cache = new Cache(new RedisAdapter($redis)); - - $database = new Database(new SQLite($pdo), $cache); - $database->setDefaultDatabase('utopiaTests'); - $database->setNamespace('myapp_'.uniqid()); - - return self::$database = $database; - } -} +// +//namespace Utopia\Tests\Adapter; +// +//use PDO; +//use Redis; +//use Utopia\Cache\Cache; +//use Utopia\Database\Database; +//use Utopia\Cache\Adapter\Redis as RedisAdapter; +//use Utopia\Database\Adapter\SQLite; +//use Utopia\Tests\Base; +// +//class SQLiteTest extends Base +//{ +// public static ?Database $database = null; +// +// // TODO@kodumbeats hacky way to identify adapters for tests +// // Remove once all methods are implemented +// /** +// * Return name of adapter +// * +// * @return string +// */ +// public static function getAdapterName(): string +// { +// return "sqlite"; +// } +// +// /** +// * +// * @return int +// */ +// public static function getUsedIndexes(): int +// { +// return SQLite::getCountOfDefaultIndexes(); +// } +// +// /** +// * @return Database +// */ +// public static function getDatabase(): Database +// { +// if (!is_null(self::$database)) { +// return self::$database; +// } +// +// $sqliteDir = __DIR__."/database.sql"; +// +// if (file_exists($sqliteDir)) { +// unlink($sqliteDir); +// } +// +// $dsn = $sqliteDir; +// $dsn = 'memory'; // Overwrite for fast tests +// $pdo = new PDO("sqlite:" . $dsn, null, null, SQLite::getPDOAttributes()); +// +// $redis = new Redis(); +// $redis->connect('redis', 6379); +// $redis->flushAll(); +// +// $cache = new Cache(new RedisAdapter($redis)); +// +// $database = new Database(new SQLite($pdo), $cache); +// $database->setDefaultDatabase('utopiaTests'); +// $database->setNamespace('myapp_'.uniqid()); +// +// return self::$database = $database; +// } +//} From 110da1f5d108737412d8cded7a345ee785551564 Mon Sep 17 00:00:00 2001 From: fogelito Date: Tue, 24 Oct 2023 17:57:20 +0300 Subject: [PATCH 9/9] mongo blocker --- tests/Database/Adapter/MongoDBTest.php | 194 ++++++++++++------------- tests/Database/Adapter/MySQLTest.php | 142 +++++++++--------- tests/Database/Base.php | 2 +- 3 files changed, 169 insertions(+), 169 deletions(-) diff --git a/tests/Database/Adapter/MongoDBTest.php b/tests/Database/Adapter/MongoDBTest.php index 3cfcd753a..62c48ae37 100644 --- a/tests/Database/Adapter/MongoDBTest.php +++ b/tests/Database/Adapter/MongoDBTest.php @@ -1,98 +1,98 @@ connect('redis', 6379); -// $redis->flushAll(); -// $cache = new Cache(new RedisAdapter($redis)); -// -// $schema = 'utopiaTests'; // same as $this->testDatabase -// $client = new Client( -// $schema, -// 'mongo', -// 27017, -// 'root', -// 'example', -// false -// ); -// -// $database = new Database(new Mongo($client), $cache); -// $database->setDefaultDatabase($schema); -// $database->setNamespace('myapp_' . uniqid()); -// -// if ($database->exists('utopiaTests')) { -// $database->delete('utopiaTests'); -// } -// -// $database->create(); -// -// return self::$database = $database; -// } -// -// /** -// * @throws Exception -// */ -// public function testCreateExistsDelete(): void -// { -// // Mongo creates databases on the fly, so exists would always pass. So we override this test to remove the exists check. -// $this->assertNotNull(static::getDatabase()->create()); -// $this->assertEquals(true, static::getDatabase()->delete($this->testDatabase)); -// $this->assertEquals(true, static::getDatabase()->create()); -// $this->assertEquals(true, static::getDatabase()->setDefaultDatabase($this->testDatabase)); -// } -// -// public function testRenameAttribute(): void -// { -// $this->assertTrue(true); -// } -// -// public function testRenameAttributeExisting(): void -// { -// $this->assertTrue(true); -// } -// -// public function testUpdateAttributeStructure(): void -// { -// $this->assertTrue(true); -// } -// -// public function testKeywords(): void -// { -// $this->assertTrue(true); -// } -//} + +namespace Utopia\Tests\Adapter; + +use Exception; +use Redis; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\Redis as RedisAdapter; +use Utopia\Database\Adapter\Mongo; +use Utopia\Database\Database; +use Utopia\Mongo\Client; +use Utopia\Tests\Base; + +class MongoDBTest extends Base +{ + public static ?Database $database = null; + + + /** + * Return name of adapter + * + * @return string + */ + public static function getAdapterName(): string + { + return "mongodb"; + } + + /** + * @return Database + * @throws Exception + */ + public static function getDatabase(): Database + { + if (!is_null(self::$database)) { + return self::$database; + } + + $redis = new Redis(); + $redis->connect('redis', 6379); + $redis->flushAll(); + $cache = new Cache(new RedisAdapter($redis)); + + $schema = 'utopiaTests'; // same as $this->testDatabase + $client = new Client( + $schema, + 'mongo', + 27017, + 'root', + 'example', + false + ); + + $database = new Database(new Mongo($client), $cache); + $database->setDefaultDatabase($schema); + $database->setNamespace('myapp_' . uniqid()); + + if ($database->exists('utopiaTests')) { + $database->delete('utopiaTests'); + } + + $database->create(); + + return self::$database = $database; + } + + /** + * @throws Exception + */ + public function testCreateExistsDelete(): void + { + // Mongo creates databases on the fly, so exists would always pass. So we override this test to remove the exists check. + $this->assertNotNull(static::getDatabase()->create()); + $this->assertEquals(true, static::getDatabase()->delete($this->testDatabase)); + $this->assertEquals(true, static::getDatabase()->create()); + $this->assertEquals(true, static::getDatabase()->setDefaultDatabase($this->testDatabase)); + } + + public function testRenameAttribute(): void + { + $this->assertTrue(true); + } + + public function testRenameAttributeExisting(): void + { + $this->assertTrue(true); + } + + public function testUpdateAttributeStructure(): void + { + $this->assertTrue(true); + } + + public function testKeywords(): void + { + $this->assertTrue(true); + } +} diff --git a/tests/Database/Adapter/MySQLTest.php b/tests/Database/Adapter/MySQLTest.php index 0faefca61..e1a181792 100644 --- a/tests/Database/Adapter/MySQLTest.php +++ b/tests/Database/Adapter/MySQLTest.php @@ -1,72 +1,72 @@ connect('redis', 6379); - $redis->flushAll(); - - $cache = new Cache(new RedisAdapter($redis)); - - $database = new Database(new MySQL($pdo), $cache); - $database->setDefaultDatabase('utopiaTests'); - $database->setNamespace('myapp_'.uniqid()); - - if ($database->exists('utopiaTests')) { - $database->delete('utopiaTests'); - } - - $database->create(); - - return self::$database = $database; - } -} +// +//namespace Utopia\Tests\Adapter; +// +//use PDO; +//use Redis; +//use Utopia\Cache\Cache; +//use Utopia\Database\Database; +//use Utopia\Database\Adapter\MySQL; +//use Utopia\Cache\Adapter\Redis as RedisAdapter; +//use Utopia\Tests\Base; +// +//class MySQLTest extends Base +//{ +// public static ?Database $database = null; +// +// // TODO@kodumbeats hacky way to identify adapters for tests +// // Remove once all methods are implemented +// /** +// * Return name of adapter +// * +// * @return string +// */ +// public static function getAdapterName(): string +// { +// return "mysql"; +// } +// +// /** +// * +// * @return int +// */ +// public static function getUsedIndexes(): int +// { +// return MySQL::getCountOfDefaultIndexes(); +// } +// +// /** +// * @return Database +// */ +// public static function getDatabase(): Database +// { +// if (!is_null(self::$database)) { +// return self::$database; +// } +// +// $dbHost = 'mysql'; +// $dbPort = '3307'; +// $dbUser = 'root'; +// $dbPass = 'password'; +// +// $pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, MySQL::getPDOAttributes()); +// +// $redis = new Redis(); +// $redis->connect('redis', 6379); +// $redis->flushAll(); +// +// $cache = new Cache(new RedisAdapter($redis)); +// +// $database = new Database(new MySQL($pdo), $cache); +// $database->setDefaultDatabase('utopiaTests'); +// $database->setNamespace('myapp_'.uniqid()); +// +// if ($database->exists('utopiaTests')) { +// $database->delete('utopiaTests'); +// } +// +// $database->create(); +// +// return self::$database = $database; +// } +//} diff --git a/tests/Database/Base.php b/tests/Database/Base.php index 7399127b6..d05a94442 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -2765,7 +2765,7 @@ public function testOrMultipleQueries(){ Query::or([ Query::equal('director', ['TBD']), Query::equal('year', [2026]) - ]), + ]) ]); $this->assertEquals(2, count($documents));