From 6c62d5b792a1b3eff24200d9e2e252ba70e8ba58 Mon Sep 17 00:00:00 2001 From: harsh mahajan Date: Tue, 15 Sep 2026 13:02:16 +0530 Subject: [PATCH 1/3] Cover consistent errors for invalid one-way reverse relationship values --- .../Scopes/Relationships/OneToOneTests.php | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/e2e/Adapter/Scopes/Relationships/OneToOneTests.php b/tests/e2e/Adapter/Scopes/Relationships/OneToOneTests.php index e67c411389..5ed956c0e4 100644 --- a/tests/e2e/Adapter/Scopes/Relationships/OneToOneTests.php +++ b/tests/e2e/Adapter/Scopes/Relationships/OneToOneTests.php @@ -8,6 +8,7 @@ use Utopia\Database\Exception\Authorization as AuthorizationException; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Limit as LimitException; +use Utopia\Database\Exception\Relationship as RelationshipException; use Utopia\Database\Exception\Restricted as RestrictedException; use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Helpers\ID; @@ -17,6 +18,47 @@ trait OneToOneTests { + public function testCreateOneWayReverseRelationshipError(): void + { + $database = $this->getDatabase(); + if (!$database->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + $parents = ID::unique(); + $children = ID::unique(); + $database->createCollection($parents); + $database->createCollection($children); + $database->createAttribute($parents, 'name', Database::VAR_STRING, 255, false); + $database->createRelationship($parents, $children, Database::RELATION_ONE_TO_ONE, false, 'child', 'parent', Database::RELATION_MUTATE_SET_NULL); + $database->createDocument($parents, new Document(['$id' => 'parent'])); + + try { + foreach ([['name' => 'parent'], ['parent'], [], 'parent', new Document(['$id' => 'parent'])] as $value) { + $id = ID::unique(); + try { + $database->createDocument($children, new Document(['$id' => $id, 'parent' => $value])); + $this->fail('Expected the one-way reverse relationship to be rejected.'); + } catch (RelationshipException $error) { + $this->assertSame('Invalid relationship value. Cannot set a value from the child side of a oneToOne relationship when twoWay is false.', $error->getMessage()); + } + $this->assertTrue($database->getDocument($children, $id)->isEmpty()); + $this->assertNull($database->getDocument($parents, 'parent')->getAttribute('child')); + } + + $forward = $database->createDocument($parents, new Document([ + '$id' => 'forward', + 'child' => new Document(['$id' => 'nested']), + ])); + $this->assertSame('nested', $forward->getAttribute('child')->getId()); + $this->assertSame('nested', $database->getDocument($parents, 'forward')->getAttribute('child')->getId()); + } finally { + $database->deleteCollection($parents); + $database->deleteCollection($children); + } + } + public function testOneToOneOneWayRelationship(): void { /** @var Database $database */ From 1393df55f9980d234cf52cdc3e543e28f42b7ebf Mon Sep 17 00:00:00 2001 From: harsh mahajan Date: Tue, 15 Sep 2026 13:02:43 +0530 Subject: [PATCH 2/3] Report reverse one-way relationship errors before array cardinality errors --- src/Database/Database.php | 4 ++++ tests/e2e/Adapter/Scopes/Relationships/OneToOneTests.php | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index d7f1a01b2a..d581529fa5 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -5998,6 +5998,10 @@ private function createDocumentRelationships(Document $collection, Document $doc try { switch (\gettype($value)) { case 'array': + if ($relationType === Database::RELATION_ONE_TO_ONE && !$twoWay && $side === Database::RELATION_SIDE_CHILD) { + throw new RelationshipException('Invalid relationship value. Cannot set a value from the child side of a oneToOne relationship when twoWay is false.'); + } + if ( ($relationType === Database::RELATION_MANY_TO_ONE && $side === Database::RELATION_SIDE_PARENT) || ($relationType === Database::RELATION_ONE_TO_MANY && $side === Database::RELATION_SIDE_CHILD) || diff --git a/tests/e2e/Adapter/Scopes/Relationships/OneToOneTests.php b/tests/e2e/Adapter/Scopes/Relationships/OneToOneTests.php index 5ed956c0e4..ceb135645a 100644 --- a/tests/e2e/Adapter/Scopes/Relationships/OneToOneTests.php +++ b/tests/e2e/Adapter/Scopes/Relationships/OneToOneTests.php @@ -28,8 +28,9 @@ public function testCreateOneWayReverseRelationshipError(): void $parents = ID::unique(); $children = ID::unique(); - $database->createCollection($parents); - $database->createCollection($children); + $permissions = [Permission::read(Role::any()), Permission::create(Role::any()), Permission::update(Role::any())]; + $database->createCollection($parents, [], [], $permissions); + $database->createCollection($children, [], [], $permissions); $database->createAttribute($parents, 'name', Database::VAR_STRING, 255, false); $database->createRelationship($parents, $children, Database::RELATION_ONE_TO_ONE, false, 'child', 'parent', Database::RELATION_MUTATE_SET_NULL); $database->createDocument($parents, new Document(['$id' => 'parent'])); From 4fdb01cd1c2b101715921e38d176efba0a266d33 Mon Sep 17 00:00:00 2001 From: harsh mahajan Date: Thu, 17 Sep 2026 11:57:44 +0530 Subject: [PATCH 3/3] test: cover the one-way child array value with the other invalid values --- .../e2e/Adapter/Scopes/RelationshipTests.php | 28 ++++++++++++ .../Scopes/Relationships/OneToOneTests.php | 43 ------------------- 2 files changed, 28 insertions(+), 43 deletions(-) diff --git a/tests/e2e/Adapter/Scopes/RelationshipTests.php b/tests/e2e/Adapter/Scopes/RelationshipTests.php index 2b162b2384..dbfba7bfc2 100644 --- a/tests/e2e/Adapter/Scopes/RelationshipTests.php +++ b/tests/e2e/Adapter/Scopes/RelationshipTests.php @@ -2185,6 +2185,34 @@ public function testCreateInvalidArrayIntValueRelationship(): void ])); } + public function testCreateInvalidOneWayChildArrayValueRelationship(): void + { + /** @var Database $database */ + $database = $this->getDatabase(); + + if (!$database->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + $database->createCollection('reverse1'); + $database->createCollection('reverse2'); + + $database->createRelationship( + collection: 'reverse1', + relatedCollection: 'reverse2', + type: Database::RELATION_ONE_TO_ONE, + ); + + $this->expectException(RelationshipException::class); + $this->expectExceptionMessage('Invalid relationship value. Cannot set a value from the child side of a oneToOne relationship when twoWay is false.'); + + $database->createDocument('reverse2', new Document([ + '$id' => ID::unique(), + 'reverse1' => ['name' => 'reverse'], + ])); + } + public function testCreateEmptyValueRelationship(): void { /** @var Database $database */ diff --git a/tests/e2e/Adapter/Scopes/Relationships/OneToOneTests.php b/tests/e2e/Adapter/Scopes/Relationships/OneToOneTests.php index ceb135645a..e67c411389 100644 --- a/tests/e2e/Adapter/Scopes/Relationships/OneToOneTests.php +++ b/tests/e2e/Adapter/Scopes/Relationships/OneToOneTests.php @@ -8,7 +8,6 @@ use Utopia\Database\Exception\Authorization as AuthorizationException; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Limit as LimitException; -use Utopia\Database\Exception\Relationship as RelationshipException; use Utopia\Database\Exception\Restricted as RestrictedException; use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Helpers\ID; @@ -18,48 +17,6 @@ trait OneToOneTests { - public function testCreateOneWayReverseRelationshipError(): void - { - $database = $this->getDatabase(); - if (!$database->getAdapter()->getSupportForRelationships()) { - $this->expectNotToPerformAssertions(); - return; - } - - $parents = ID::unique(); - $children = ID::unique(); - $permissions = [Permission::read(Role::any()), Permission::create(Role::any()), Permission::update(Role::any())]; - $database->createCollection($parents, [], [], $permissions); - $database->createCollection($children, [], [], $permissions); - $database->createAttribute($parents, 'name', Database::VAR_STRING, 255, false); - $database->createRelationship($parents, $children, Database::RELATION_ONE_TO_ONE, false, 'child', 'parent', Database::RELATION_MUTATE_SET_NULL); - $database->createDocument($parents, new Document(['$id' => 'parent'])); - - try { - foreach ([['name' => 'parent'], ['parent'], [], 'parent', new Document(['$id' => 'parent'])] as $value) { - $id = ID::unique(); - try { - $database->createDocument($children, new Document(['$id' => $id, 'parent' => $value])); - $this->fail('Expected the one-way reverse relationship to be rejected.'); - } catch (RelationshipException $error) { - $this->assertSame('Invalid relationship value. Cannot set a value from the child side of a oneToOne relationship when twoWay is false.', $error->getMessage()); - } - $this->assertTrue($database->getDocument($children, $id)->isEmpty()); - $this->assertNull($database->getDocument($parents, 'parent')->getAttribute('child')); - } - - $forward = $database->createDocument($parents, new Document([ - '$id' => 'forward', - 'child' => new Document(['$id' => 'nested']), - ])); - $this->assertSame('nested', $forward->getAttribute('child')->getId()); - $this->assertSame('nested', $database->getDocument($parents, 'forward')->getAttribute('child')->getId()); - } finally { - $database->deleteCollection($parents); - $database->deleteCollection($children); - } - } - public function testOneToOneOneWayRelationship(): void { /** @var Database $database */