From 907be8dbd1c1d540f71f81cdbebb657260e29454 Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Tue, 23 Sep 2025 12:39:28 +0530 Subject: [PATCH 01/12] Add support for schemaless attributes in Database and Structure classes --- src/Database/Database.php | 5 + src/Database/Validator/Structure.php | 20 +++- tests/e2e/Adapter/Scopes/DocumentTests.php | 127 +++++++++++++++++++++ 3 files changed, 149 insertions(+), 3 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index 0bee028ae..1a280a0e1 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -3906,6 +3906,7 @@ public function createDocument(string $collection, Document $document): Document $this->adapter->getIdAttributeType(), $this->adapter->getMinDateTime(), $this->adapter->getMaxDateTime(), + $this->adapter->getSupportForAttributes() ); if (!$structure->isValid($document)) { throw new StructureException($structure->getDescription()); @@ -4006,6 +4007,7 @@ public function createDocuments( $this->adapter->getIdAttributeType(), $this->adapter->getMinDateTime(), $this->adapter->getMaxDateTime(), + $this->adapter->getSupportForAttributes() ); if (!$validator->isValid($document)) { throw new StructureException($validator->getDescription()); @@ -4559,6 +4561,7 @@ public function updateDocument(string $collection, string $id, Document $documen $this->adapter->getIdAttributeType(), $this->adapter->getMinDateTime(), $this->adapter->getMaxDateTime(), + $this->adapter->getSupportForAttributes() ); if (!$structureValidator->isValid($document)) { // Make sure updated structure still apply collection rules (if any) throw new StructureException($structureValidator->getDescription()); @@ -4693,6 +4696,7 @@ public function updateDocuments( $this->adapter->getIdAttributeType(), $this->adapter->getMinDateTime(), $this->adapter->getMaxDateTime(), + $this->adapter->getSupportForAttributes() ); if (!$validator->isValid($updates)) { @@ -5404,6 +5408,7 @@ public function upsertDocumentsWithIncrease( $this->adapter->getIdAttributeType(), $this->adapter->getMinDateTime(), $this->adapter->getMaxDateTime(), + $this->adapter->getSupportForAttributes() ); if (!$validator->isValid($document)) { diff --git a/src/Database/Validator/Structure.php b/src/Database/Validator/Structure.php index cfb12fa3a..72ef2eaf5 100644 --- a/src/Database/Validator/Structure.php +++ b/src/Database/Validator/Structure.php @@ -86,6 +86,7 @@ class Structure extends Validator 'filters' => [], ] ]; + private array $internalAttributes = []; /** * @var array @@ -106,7 +107,12 @@ public function __construct( private readonly string $idAttributeType, private readonly \DateTime $minAllowedDate = new \DateTime('0000-01-01'), private readonly \DateTime $maxAllowedDate = new \DateTime('9999-12-31'), + private bool $supportForAttributes = true ) { + $this->internalAttributes = array_reduce($this->attributes, function ($carry, $attribute) { + $carry[$attribute['$id']] = 1; + return $carry; + }, []); } /** @@ -252,11 +258,14 @@ public function isValid($document): bool protected function checkForAllRequiredValues(array $structure, array $attributes, array &$keys): bool { foreach ($attributes as $key => $attribute) { // Check all required attributes are set + // schemaless adapter and not an internal attribute + if (!$this->supportForAttributes && !isset($this->internalAttributes[$key])) { + return true; + } $name = $attribute['$id'] ?? ''; $required = $attribute['required'] ?? false; $keys[$name] = $attribute; // List of allowed attributes to help find unknown ones - if ($required && !isset($structure[$name])) { $this->message = 'Missing required attribute "'.$name.'"'; return false; @@ -276,6 +285,9 @@ protected function checkForAllRequiredValues(array $structure, array $attributes */ protected function checkForUnknownAttributes(array $structure, array $keys): bool { + if (!$this->supportForAttributes) { + return true; + } foreach ($structure as $key => $value) { if (!array_key_exists($key, $keys)) { // Check no unknown attributes are set $this->message = 'Unknown attribute: "'.$key.'"'; @@ -357,8 +369,10 @@ protected function checkForInvalidAttributeValues(array $structure, array $keys) break; default: - $this->message = 'Unknown attribute type "'.$type.'"'; - return false; + if ($this->supportForAttributes) { + $this->message = 'Unknown attribute type "'.$type.'"'; + return false; + } } /** Error message label, either 'format' or 'type' */ diff --git a/tests/e2e/Adapter/Scopes/DocumentTests.php b/tests/e2e/Adapter/Scopes/DocumentTests.php index 03461b12f..e8782134f 100644 --- a/tests/e2e/Adapter/Scopes/DocumentTests.php +++ b/tests/e2e/Adapter/Scopes/DocumentTests.php @@ -6053,4 +6053,131 @@ public function testCreateUpdateDocumentsMismatch(): void } $database->deleteCollection($colName); } + + public function testSchemalessCreateDocumentWithExtraAttribute() + { + /** @var Database $database */ + $database = static::getDatabase(); + + if ($database->getAdapter()->getSupportForAttributes()) { + $this->markTestSkipped('This test is only for schemaless adapters'); + } + + $colName = uniqid("schemaless"); + $database->createCollection($colName); + $database->createAttribute($colName, 'key', Database::VAR_STRING, 50, true); + $database->createAttribute($colName, 'value', Database::VAR_STRING, 50, false, 'value'); + + $permissions = [Permission::read(Role::any()), Permission::write(Role::any()), Permission::update(Role::any()), Permission::delete(Role::any())]; + + // Valid documents without any predefined attributes + $docs = [ + new Document(['$id' => 'doc1', '$permissions' => $permissions, 'freeA' => 'doc1']), + new Document(['$id' => 'doc2', '$permissions' => $permissions, 'freeB' => 'test']), + new Document(['$id' => 'doc3', '$permissions' => $permissions]), + ]; + $this->assertEquals(3, $database->createDocuments($colName, $docs)); + + // Any extra attributes should be allowed (fully schemaless) + $docs = [ + new Document(['$id' => 'doc11', 'title' => 'doc1', '$permissions' => $permissions]), + new Document(['$id' => 'doc21', 'moviename' => 'doc2', 'moviedescription' => 'test', '$permissions' => $permissions]), + new Document(['$id' => 'doc31', '$permissions' => $permissions]), + ]; + + $createdDocs = $database->createDocuments($colName, $docs); + $this->assertEquals(3, $createdDocs); + + // Create a single document with extra attribute as well + $single = $database->createDocument($colName, new Document(['$id' => 'docS', 'extra' => 'yes', '$permissions' => $permissions])); + $this->assertEquals('docS', $single->getId()); + $this->assertEquals('yes', $single->getAttribute('extra')); + + $found = $database->find($colName); + $this->assertCount(7, $found); + $doc11 = $database->getDocument($colName, 'doc11'); + $this->assertEquals('doc1', $doc11->getAttribute('title')); + + $doc21 = $database->getDocument($colName, 'doc21'); + $this->assertEquals('doc2', $doc21->getAttribute('moviename')); + $this->assertEquals('test', $doc21->getAttribute('moviedescription')); + + $updated = $database->updateDocument($colName, 'doc31', new Document(['moviename' => 'updated'])) + ; + $this->assertEquals('updated', $updated->getAttribute('moviename')); + + $this->assertTrue($database->deleteDocument($colName, 'doc21')); + $deleted = $database->getDocument($colName, 'doc21'); + $this->assertTrue($deleted->isEmpty()); + $remaining = $database->find($colName); + $this->assertCount(6, $remaining); + + // Bulk update: set a new extra attribute on all remaining docs + $modified = $database->updateDocuments($colName, new Document(['bulkExtra' => 'yes'])); + $this->assertEquals(6, $modified); + $all = $database->find($colName); + foreach ($all as $doc) { + $this->assertEquals('yes', $doc->getAttribute('bulkExtra')); + } + + // Upsert: create new and update existing with extra attributes preserved + $upserts = [ + new Document(['$id' => 'docU1', 'extraU' => 1, '$permissions' => $permissions]), + new Document(['$id' => 'doc1', 'extraU' => 2, '$permissions' => $permissions]), + ]; + $countUpserts = $database->upsertDocuments($colName, $upserts); + $this->assertEquals(2, $countUpserts); + $docU1 = $database->getDocument($colName, 'docU1'); + $this->assertEquals(1, $docU1->getAttribute('extraU')); + $doc1AfterUpsert = $database->getDocument($colName, 'doc1'); + $this->assertEquals(2, $doc1AfterUpsert->getAttribute('extraU')); + + // Increase/Decrease numeric attribute: add numeric attribute and mutate it + $database->createAttribute($colName, 'counter', Database::VAR_INTEGER, 0, false, 0); + $docS = $database->getDocument($colName, 'docS'); + $this->assertEquals(0, $docS->getAttribute('counter')); + $docS = $database->increaseDocumentAttribute($colName, 'docS', 'counter', 5); + $this->assertEquals(5, $docS->getAttribute('counter')); + $docS = $database->decreaseDocumentAttribute($colName, 'docS', 'counter', 3); + $this->assertEquals(2, $docS->getAttribute('counter')); + + $deletedByCounter = $database->deleteDocuments($colName, [Query::equal('counter', [2])]); + $this->assertEquals(1, $deletedByCounter); + + $deletedCount = $database->deleteDocuments($colName, [Query::startsWith('$id', 'doc')]); + $this->assertEquals(6, $deletedCount); + $postDelete = $database->find($colName); + $this->assertCount(0, $postDelete); + + $database->deleteCollection($colName); + } + + public function testSchemaEnforcedDocumentCreation() + { + /** @var Database $database */ + $database = static::getDatabase(); + + if (!$database->getAdapter()->getSupportForAttributes()) { + $this->markTestSkipped('This test is only for schema-enforced adapters'); + } + + $colName = uniqid("schema"); + $database->createCollection($colName); + $database->createAttribute($colName, 'key', Database::VAR_STRING, 50, true); + $database->createAttribute($colName, 'value', Database::VAR_STRING, 50, false, 'value'); + + $permissions = [Permission::read(Role::any()), Permission::write(Role::any()), Permission::update(Role::any())]; + + // Extra attributes should fail + $docs = [ + new Document(['$id' => 'doc11', 'key' => 'doc1', 'title' => 'doc1', '$permissions' => $permissions]), + new Document(['$id' => 'doc21', 'key' => 'doc2', 'moviename' => 'doc2', 'moviedescription' => 'test', '$permissions' => $permissions]), + new Document(['$id' => 'doc31', 'key' => 'doc3', '$permissions' => $permissions]), + ]; + + $this->expectException(StructureException::class); + $database->createDocuments($colName, $docs); + + $database->deleteCollection($colName); + } } From 2f5843311959347ba7dbeb04aad63226ef173349 Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Tue, 23 Sep 2025 12:49:07 +0530 Subject: [PATCH 02/12] added internal attributes validation tests --- tests/e2e/Adapter/Scopes/DocumentTests.php | 42 ++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Adapter/Scopes/DocumentTests.php b/tests/e2e/Adapter/Scopes/DocumentTests.php index e8782134f..141dcb500 100644 --- a/tests/e2e/Adapter/Scopes/DocumentTests.php +++ b/tests/e2e/Adapter/Scopes/DocumentTests.php @@ -6054,7 +6054,7 @@ public function testCreateUpdateDocumentsMismatch(): void $database->deleteCollection($colName); } - public function testSchemalessCreateDocumentWithExtraAttribute() + public function testSchemalessDocumentOperation(): void { /** @var Database $database */ $database = static::getDatabase(); @@ -6152,7 +6152,45 @@ public function testSchemalessCreateDocumentWithExtraAttribute() $database->deleteCollection($colName); } - public function testSchemaEnforcedDocumentCreation() + public function testSchemalessDocumentInvalidInteralAttributeValidation(): void + { + /** @var Database $database */ + $database = static::getDatabase(); + + // test to ensure internal attributes are checked during creating schemaless document + if ($database->getAdapter()->getSupportForAttributes()) { + $this->markTestSkipped('This test is only for schemaless adapters'); + } + + $colName = uniqid("schemaless"); + $database->createCollection($colName); + try { + $docs = [ + new Document(['$id' => true, 'freeA' => 'doc1']), + new Document(['$id' => true, 'freeB' => 'test']), + new Document(['$id' => true]), + ]; + $database->createDocuments($colName, $docs); + } catch (\Throwable $e) { + $this->assertInstanceOf(StructureException::class, $e); + } + + try { + $docs = [ + new Document(['$createdAt' => true, 'freeA' => 'doc1']), + new Document(['$updatedAt' => true, 'freeB' => 'test']), + new Document(['$permissions' => 12]), + ]; + $database->createDocuments($colName, $docs); + } catch (\Throwable $e) { + $this->assertInstanceOf(StructureException::class, $e); + } + + $database->deleteCollection($colName); + + } + + public function testSchemaEnforcedDocumentCreation(): void { /** @var Database $database */ $database = static::getDatabase(); From bd0d63e9f85850a88cb6e9bb8a206612e9f314bf Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Tue, 23 Sep 2025 12:52:35 +0530 Subject: [PATCH 03/12] updated doc string for internal attributes map --- src/Database/Validator/Structure.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Database/Validator/Structure.php b/src/Database/Validator/Structure.php index 72ef2eaf5..c9b779a93 100644 --- a/src/Database/Validator/Structure.php +++ b/src/Database/Validator/Structure.php @@ -86,6 +86,9 @@ class Structure extends Validator 'filters' => [], ] ]; + /** + * @var array + */ private array $internalAttributes = []; /** From da706a9d6c7fcba2f0927ddedb1eea85a3902d7d Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Tue, 23 Sep 2025 15:00:12 +0530 Subject: [PATCH 04/12] Add support for attributes in Database and related validators --- src/Database/Database.php | 4 ++ src/Database/Validator/Index.php | 13 +++- src/Database/Validator/Queries/Documents.php | 6 +- src/Database/Validator/Query/Filter.php | 7 +- src/Database/Validator/Query/Order.php | 7 +- src/Database/Validator/Query/Select.php | 7 +- tests/e2e/Adapter/Scopes/AttributeTests.php | 75 +++++++++++++++----- tests/e2e/Adapter/Scopes/DocumentTests.php | 38 ++++++---- tests/e2e/Adapter/Scopes/GeneralTests.php | 6 ++ tests/e2e/Adapter/Scopes/IndexTests.php | 4 +- 10 files changed, 124 insertions(+), 43 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index 1a280a0e1..4a7154715 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -1411,6 +1411,7 @@ public function createCollection(string $id, array $attributes = [], array $inde $this->adapter->getSupportForSpatialAttributes(), $this->adapter->getSupportForSpatialIndexNull(), $this->adapter->getSupportForSpatialIndexOrder(), + $this->adapter->getSupportForAttributes() ); foreach ($indexes as $index) { if (!$validator->isValid($index)) { @@ -2420,6 +2421,7 @@ public function updateAttribute(string $collection, string $id, ?string $type = $this->adapter->getSupportForSpatialAttributes(), $this->adapter->getSupportForSpatialIndexNull(), $this->adapter->getSupportForSpatialIndexOrder(), + $this->adapter->getSupportForAttributes() ); foreach ($indexes as $index) { @@ -3363,6 +3365,7 @@ public function createIndex(string $collection, string $id, string $type, array $this->adapter->getSupportForSpatialAttributes(), $this->adapter->getSupportForSpatialIndexNull(), $this->adapter->getSupportForSpatialIndexOrder(), + $this->adapter->getSupportForAttributes() ); if (!$validator->isValid($index)) { throw new IndexException($validator->getDescription()); @@ -6402,6 +6405,7 @@ public function find(string $collection, array $queries = [], string $forPermiss $this->maxQueryValues, $this->adapter->getMinDateTime(), $this->adapter->getMaxDateTime(), + $this->adapter->getSupportForAttributes() ); if (!$validator->isValid($queries)) { throw new QueryException($validator->getDescription()); diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index bab80c173..ec2c437e3 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -30,6 +30,8 @@ class Index extends Validator protected bool $spatialIndexOrderSupport; + protected bool $supportForAttributes; + /** * @param array $attributes * @param int $maxLength @@ -40,7 +42,7 @@ class Index extends Validator * @param bool $spatialIndexOrderSupport * @throws DatabaseException */ - public function __construct(array $attributes, int $maxLength, array $reservedKeys = [], bool $arrayIndexSupport = false, bool $spatialIndexSupport = false, bool $spatialIndexNullSupport = false, bool $spatialIndexOrderSupport = false) + public function __construct(array $attributes, int $maxLength, array $reservedKeys = [], bool $arrayIndexSupport = false, bool $spatialIndexSupport = false, bool $spatialIndexNullSupport = false, bool $spatialIndexOrderSupport = false, bool $supportForAttributes = true) { $this->maxLength = $maxLength; $this->reservedKeys = $reservedKeys; @@ -48,6 +50,7 @@ public function __construct(array $attributes, int $maxLength, array $reservedKe $this->spatialIndexSupport = $spatialIndexSupport; $this->spatialIndexNullSupport = $spatialIndexNullSupport; $this->spatialIndexOrderSupport = $spatialIndexOrderSupport; + $this->supportForAttributes = $supportForAttributes; foreach ($attributes as $attribute) { $key = \strtolower($attribute->getAttribute('key', $attribute->getAttribute('$id'))); @@ -75,7 +78,7 @@ public function getDescription(): string public function checkAttributesNotFound(Document $index): bool { foreach ($index->getAttribute('attributes', []) as $attribute) { - if (!isset($this->attributes[\strtolower($attribute)])) { + if ($this->supportForAttributes && !isset($this->attributes[\strtolower($attribute)])) { $this->message = 'Invalid index attribute "' . $attribute . '" not found'; return false; } @@ -123,6 +126,9 @@ public function checkDuplicatedAttributes(Document $index): bool */ public function checkFulltextIndexNonString(Document $index): bool { + if (!$this->supportForAttributes) { + return true; + } if ($index->getAttribute('type') === Database::INDEX_FULLTEXT) { foreach ($index->getAttribute('attributes', []) as $attribute) { $attribute = $this->attributes[\strtolower($attribute)] ?? new Document(); @@ -141,6 +147,9 @@ public function checkFulltextIndexNonString(Document $index): bool */ public function checkArrayIndex(Document $index): bool { + if (!$this->supportForAttributes) { + return true; + } $attributes = $index->getAttribute('attributes', []); $orders = $index->getAttribute('orders', []); $lengths = $index->getAttribute('lengths', []); diff --git a/src/Database/Validator/Queries/Documents.php b/src/Database/Validator/Queries/Documents.php index 289ccbe5b..e7bb9101a 100644 --- a/src/Database/Validator/Queries/Documents.php +++ b/src/Database/Validator/Queries/Documents.php @@ -30,6 +30,7 @@ public function __construct( int $maxValuesCount = 100, \DateTime $minAllowedDate = new \DateTime('0000-01-01'), \DateTime $maxAllowedDate = new \DateTime('9999-12-31'), + private bool $supportForAttributes = true ) { $attributes[] = new Document([ '$id' => '$id', @@ -66,9 +67,10 @@ public function __construct( $maxValuesCount, $minAllowedDate, $maxAllowedDate, + $this->supportForAttributes ), - new Order($attributes), - new Select($attributes), + new Order($attributes, $this->supportForAttributes), + new Select($attributes, $this->supportForAttributes), ]; parent::__construct($attributes, $indexes, $validators); diff --git a/src/Database/Validator/Query/Filter.php b/src/Database/Validator/Query/Filter.php index 9c60f551c..e92f96293 100644 --- a/src/Database/Validator/Query/Filter.php +++ b/src/Database/Validator/Query/Filter.php @@ -31,6 +31,7 @@ public function __construct( private readonly int $maxValuesCount = 100, private readonly \DateTime $minAllowedDate = new \DateTime('0000-01-01'), private readonly \DateTime $maxAllowedDate = new \DateTime('9999-12-31'), + private bool $supportForAttributes = true ) { foreach ($attributes as $attribute) { $this->schema[$attribute->getAttribute('key', $attribute->getAttribute('$id'))] = $attribute->getArrayCopy(); @@ -67,7 +68,7 @@ protected function isValidAttribute(string $attribute): bool } // Search for attribute in schema - if (!isset($this->schema[$attribute])) { + if ($this->supportForAttributes && !isset($this->schema[$attribute])) { $this->message = 'Attribute not found in schema: ' . $attribute; return false; } @@ -94,6 +95,10 @@ protected function isValidAttributeAndValues(string $attribute, array $values, s $attribute = \explode('.', $attribute)[0]; } + if (!$this->supportForAttributes && !isset($this->schema[$attribute])) { + return true; + } + $attributeSchema = $this->schema[$attribute]; if (count($values) > $this->maxValuesCount) { diff --git a/src/Database/Validator/Query/Order.php b/src/Database/Validator/Query/Order.php index f0e7f2d56..1d051afba 100644 --- a/src/Database/Validator/Query/Order.php +++ b/src/Database/Validator/Query/Order.php @@ -11,12 +11,15 @@ class Order extends Base * @var array */ protected array $schema = []; + protected bool $supportForAttributes; /** * @param array $attributes + * @param bool $supportForAttributes */ - public function __construct(array $attributes = []) + public function __construct(array $attributes = [], bool $supportForAttributes = true) { + $this->supportForAttributes = $supportForAttributes; foreach ($attributes as $attribute) { $this->schema[$attribute->getAttribute('key', $attribute->getAttribute('$id'))] = $attribute->getArrayCopy(); } @@ -29,7 +32,7 @@ public function __construct(array $attributes = []) protected function isValidAttribute(string $attribute): bool { // Search for attribute in schema - if (!isset($this->schema[$attribute])) { + if ($this->supportForAttributes && !isset($this->schema[$attribute])) { $this->message = 'Attribute not found in schema: ' . $attribute; return false; } diff --git a/src/Database/Validator/Query/Select.php b/src/Database/Validator/Query/Select.php index 40572b828..690903a4a 100644 --- a/src/Database/Validator/Query/Select.php +++ b/src/Database/Validator/Query/Select.php @@ -12,6 +12,7 @@ class Select extends Base * @var array */ protected array $schema = []; + protected bool $supportForAttributes; /** * List of internal attributes @@ -29,9 +30,11 @@ class Select extends Base /** * @param array $attributes + * @param bool $supportForAttributes */ - public function __construct(array $attributes = []) + public function __construct(array $attributes = [], bool $supportForAttributes = true) { + $this->supportForAttributes = $supportForAttributes; foreach ($attributes as $attribute) { $this->schema[$attribute->getAttribute('key', $attribute->getAttribute('$id'))] = $attribute->getArrayCopy(); } @@ -89,7 +92,7 @@ public function isValid($value): bool continue; } - if (!isset($this->schema[$attribute]) && $attribute !== '*') { + if ($this->supportForAttributes && !isset($this->schema[$attribute]) && $attribute !== '*') { $this->message = 'Attribute not found in schema: ' . $attribute; return false; } diff --git a/tests/e2e/Adapter/Scopes/AttributeTests.php b/tests/e2e/Adapter/Scopes/AttributeTests.php index 25ee025d8..0342e7b74 100644 --- a/tests/e2e/Adapter/Scopes/AttributeTests.php +++ b/tests/e2e/Adapter/Scopes/AttributeTests.php @@ -392,6 +392,10 @@ public function testUpdateAttributeRequired(): void /** @var Database $database */ $database = static::getDatabase(); + if (!$database->getAdapter()->getSupportForAttributes()) { + $this->markTestSkipped('This test is only for schema-enforced adapters'); + } + $database->updateAttributeRequired('flowers', 'inStock', true); $this->expectExceptionMessage('Invalid document structure: Missing required attribute "inStock"'); @@ -448,7 +452,9 @@ public function testUpdateAttributeFormat(): void { /** @var Database $database */ $database = static::getDatabase(); - + if (!$database->getAdapter()->getSupportForAttributes()) { + $this->markTestSkipped('This test is only for schema-enforced adapters'); + } $database->createAttribute('flowers', 'price', Database::VAR_INTEGER, 0, false); $doc = $database->createDocument('flowers', new Document([ @@ -648,6 +654,9 @@ public function testUpdateAttributeRename(): void { /** @var Database $database */ $database = static::getDatabase(); + if (!$database->getAdapter()->getSupportForAttributes()) { + $this->markTestSkipped('This test is only for schema-enforced adapters'); + } $database->createCollection('rename_test'); @@ -1330,7 +1339,9 @@ public function testArrayAttribute(): void $database->createDocument($collection, new Document([])); $this->fail('Failed to throw exception'); } catch (Throwable $e) { - $this->assertEquals('Invalid document structure: Missing required attribute "booleans"', $e->getMessage()); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->assertEquals('Invalid document structure: Missing required attribute "booleans"', $e->getMessage()); + } } $database->updateAttribute($collection, 'booleans', required: false); @@ -1350,7 +1361,9 @@ public function testArrayAttribute(): void ])); $this->fail('Failed to throw exception'); } catch (Throwable $e) { - $this->assertEquals('Invalid document structure: Attribute "short[\'0\']" has invalid type. Value must be a valid string and no longer than 5 chars', $e->getMessage()); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->assertEquals('Invalid document structure: Attribute "short[\'0\']" has invalid type. Value must be a valid string and no longer than 5 chars', $e->getMessage()); + } } try { @@ -1359,7 +1372,9 @@ public function testArrayAttribute(): void ])); $this->fail('Failed to throw exception'); } catch (Throwable $e) { - $this->assertEquals('Invalid document structure: Attribute "names[\'1\']" has invalid type. Value must be a valid string and no longer than 255 chars', $e->getMessage()); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->assertEquals('Invalid document structure: Attribute "names[\'1\']" has invalid type. Value must be a valid string and no longer than 255 chars', $e->getMessage()); + } } try { @@ -1368,7 +1383,9 @@ public function testArrayAttribute(): void ])); $this->fail('Failed to throw exception'); } catch (Throwable $e) { - $this->assertEquals('Invalid document structure: Attribute "age" has invalid type. Value must be a valid integer', $e->getMessage()); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->assertEquals('Invalid document structure: Attribute "age" has invalid type. Value must be a valid integer', $e->getMessage()); + } } try { @@ -1377,7 +1394,9 @@ public function testArrayAttribute(): void ])); $this->fail('Failed to throw exception'); } catch (Throwable $e) { - $this->assertEquals('Invalid document structure: Attribute "age" has invalid type. Value must be a valid range between 0 and 2,147,483,647', $e->getMessage()); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->assertEquals('Invalid document structure: Attribute "age" has invalid type. Value must be a valid range between 0 and 2,147,483,647', $e->getMessage()); + } } $database->createDocument($collection, new Document([ @@ -1566,17 +1585,22 @@ public function testCreateDatetime(): void $database = static::getDatabase(); $database->createCollection('datetime'); - - $this->assertEquals(true, $database->createAttribute('datetime', 'date', Database::VAR_DATETIME, 0, true, null, true, false, null, [], ['datetime'])); - $this->assertEquals(true, $database->createAttribute('datetime', 'date2', Database::VAR_DATETIME, 0, false, null, true, false, null, [], ['datetime'])); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->assertEquals(true, $database->createAttribute('datetime', 'date', Database::VAR_DATETIME, 0, true, null, true, false, null, [], ['datetime'])); + $this->assertEquals(true, $database->createAttribute('datetime', 'date2', Database::VAR_DATETIME, 0, false, null, true, false, null, [], ['datetime'])); + } try { $database->createDocument('datetime', new Document([ 'date' => ['2020-01-01'], // array ])); - $this->fail('Failed to throw exception'); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->fail('Failed to throw exception'); + } } catch (Exception $e) { - $this->assertInstanceOf(StructureException::class, $e); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->assertInstanceOf(StructureException::class, $e); + } } $doc = $database->createDocument('datetime', new Document([ @@ -1619,20 +1643,29 @@ public function testCreateDatetime(): void try { $database->createDocument('datetime', new Document([ - 'date' => "1975-12-06 00:00:61" // 61 seconds is invalid + '$id' => 'datenew1', + 'date' => "1975-12-06 00:00:61", // 61 seconds is invalid, ])); - $this->fail('Failed to throw exception'); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->fail('Failed to throw exception'); + } } catch (Exception $e) { - $this->assertInstanceOf(StructureException::class, $e); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->assertInstanceOf(StructureException::class, $e); + } } try { $database->createDocument('datetime', new Document([ 'date' => '+055769-02-14T17:56:18.000Z' ])); - $this->fail('Failed to throw exception'); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->fail('Failed to throw exception'); + } } catch (Exception $e) { - $this->assertInstanceOf(StructureException::class, $e); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->assertInstanceOf(StructureException::class, $e); + } } $invalidDates = [ @@ -1656,10 +1689,14 @@ public function testCreateDatetime(): void $database->find('datetime', [ Query::equal('date', [$date]) ]); - $this->fail('Failed to throw exception'); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->fail('Failed to throw exception'); + } } catch (Throwable $e) { - $this->assertTrue($e instanceof QueryException); - $this->assertEquals('Invalid query: Query value is invalid for attribute "date"', $e->getMessage()); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->assertTrue($e instanceof QueryException); + $this->assertEquals('Invalid query: Query value is invalid for attribute "date"', $e->getMessage()); + } } } diff --git a/tests/e2e/Adapter/Scopes/DocumentTests.php b/tests/e2e/Adapter/Scopes/DocumentTests.php index 141dcb500..0ee2582bd 100644 --- a/tests/e2e/Adapter/Scopes/DocumentTests.php +++ b/tests/e2e/Adapter/Scopes/DocumentTests.php @@ -229,8 +229,10 @@ public function testCreateDocument(): Document ])); $this->fail('Failed to throw exception'); } catch (Throwable $e) { - $this->assertTrue($e instanceof StructureException); - $this->assertStringContainsString('Invalid document structure: Attribute "float_unsigned" has invalid type. Value must be a valid range between 0 and', $e->getMessage()); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->assertTrue($e instanceof StructureException); + $this->assertStringContainsString('Invalid document structure: Attribute "float_unsigned" has invalid type. Value must be a valid range between 0 and', $e->getMessage()); + } } try { @@ -248,8 +250,10 @@ public function testCreateDocument(): Document ])); $this->fail('Failed to throw exception'); } catch (Throwable $e) { - $this->assertTrue($e instanceof StructureException); - $this->assertEquals('Invalid document structure: Attribute "bigint_unsigned" has invalid type. Value must be a valid range between 0 and 9,223,372,036,854,775,807', $e->getMessage()); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->assertTrue($e instanceof StructureException); + $this->assertEquals('Invalid document structure: Attribute "bigint_unsigned" has invalid type. Value must be a valid range between 0 and 9,223,372,036,854,775,807', $e->getMessage()); + } } try { @@ -270,8 +274,10 @@ public function testCreateDocument(): Document ])); $this->fail('Failed to throw exception'); } catch (Throwable $e) { - $this->assertTrue($e instanceof StructureException); - $this->assertEquals('Invalid document structure: Attribute "$sequence" has invalid type. Invalid sequence value', $e->getMessage()); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->assertTrue($e instanceof StructureException); + $this->assertEquals('Invalid document structure: Attribute "$sequence" has invalid type. Invalid sequence value', $e->getMessage()); + } } /** @@ -933,7 +939,9 @@ public function testUpsertDocumentsAttributeMismatch(): void ]); $this->fail('Failed to throw exception'); } catch (Throwable $e) { - $this->assertTrue($e instanceof StructureException, $e->getMessage()); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->assertTrue($e instanceof StructureException, $e->getMessage()); + } } // Ensure missing optionals on existing document is allowed @@ -5151,16 +5159,18 @@ public function testFulltextIndexWithInteger(): void { /** @var Database $database */ $database = static::getDatabase(); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->expectException(Exception::class); + if (!$this->getDatabase()->getAdapter()->getSupportForFulltextIndex()) { + $this->expectExceptionMessage('Fulltext index is not supported'); + } else { + $this->expectExceptionMessage('Attribute "integer_signed" cannot be part of a FULLTEXT index, must be of type string'); + } - $this->expectException(Exception::class); - - if (!$this->getDatabase()->getAdapter()->getSupportForFulltextIndex()) { - $this->expectExceptionMessage('Fulltext index is not supported'); + $database->createIndex('documents', 'fulltext_integer', Database::INDEX_FULLTEXT, ['string','integer_signed']); } else { - $this->expectExceptionMessage('Attribute "integer_signed" cannot be part of a FULLTEXT index, must be of type string'); + $this->markTestSkipped('This test is only for schema based adapters'); } - - $database->createIndex('documents', 'fulltext_integer', Database::INDEX_FULLTEXT, ['string','integer_signed']); } public function testEnableDisableValidation(): void diff --git a/tests/e2e/Adapter/Scopes/GeneralTests.php b/tests/e2e/Adapter/Scopes/GeneralTests.php index 1664273c1..d618682c1 100644 --- a/tests/e2e/Adapter/Scopes/GeneralTests.php +++ b/tests/e2e/Adapter/Scopes/GeneralTests.php @@ -95,6 +95,9 @@ public function testPreserveDatesUpdate(): void /** @var Database $database */ $database = static::getDatabase(); + if (!$database->getAdapter()->getSupportForAttributes()) { + $this->markTestSkipped('This test is only for schema based adapters'); + } $database->setPreserveDates(true); @@ -190,6 +193,9 @@ public function testPreserveDatesCreate(): void /** @var Database $database */ $database = static::getDatabase(); + if (!$database->getAdapter()->getSupportForAttributes()) { + $this->markTestSkipped('This test is only for schema based adapters'); + } $database->setPreserveDates(true); diff --git a/tests/e2e/Adapter/Scopes/IndexTests.php b/tests/e2e/Adapter/Scopes/IndexTests.php index df3207f35..3d2aa5917 100644 --- a/tests/e2e/Adapter/Scopes/IndexTests.php +++ b/tests/e2e/Adapter/Scopes/IndexTests.php @@ -249,7 +249,9 @@ public function testIndexValidation(): void try { $database->createCollection($collection->getId(), $attributes, $indexes); - $this->fail('Failed to throw exception'); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->fail('Failed to throw exception'); + } } catch (Exception $e) { $this->assertEquals($errorMessage, $e->getMessage()); } From e917ec22127b41f138ecabf91a518ad32bbb65d4 Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Tue, 23 Sep 2025 15:39:15 +0530 Subject: [PATCH 05/12] test fix and linting --- src/Database/Validator/Index.php | 1 - src/Database/Validator/Query/Filter.php | 1 - tests/e2e/Adapter/Scopes/AttributeTests.php | 28 ++++++++++++++------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/Database/Validator/Index.php b/src/Database/Validator/Index.php index ec2c437e3..e22bfaaff 100644 --- a/src/Database/Validator/Index.php +++ b/src/Database/Validator/Index.php @@ -31,7 +31,6 @@ class Index extends Validator protected bool $spatialIndexOrderSupport; protected bool $supportForAttributes; - /** * @param array $attributes * @param int $maxLength diff --git a/src/Database/Validator/Query/Filter.php b/src/Database/Validator/Query/Filter.php index e92f96293..b040f70c6 100644 --- a/src/Database/Validator/Query/Filter.php +++ b/src/Database/Validator/Query/Filter.php @@ -98,7 +98,6 @@ protected function isValidAttributeAndValues(string $attribute, array $values, s if (!$this->supportForAttributes && !isset($this->schema[$attribute])) { return true; } - $attributeSchema = $this->schema[$attribute]; if (count($values) > $this->maxValuesCount) { diff --git a/tests/e2e/Adapter/Scopes/AttributeTests.php b/tests/e2e/Adapter/Scopes/AttributeTests.php index 0342e7b74..b7e9c4eb7 100644 --- a/tests/e2e/Adapter/Scopes/AttributeTests.php +++ b/tests/e2e/Adapter/Scopes/AttributeTests.php @@ -1470,20 +1470,28 @@ public function testArrayAttribute(): void if ($database->getAdapter()->getSupportForIndexArray()) { try { $database->createIndex($collection, 'indx', Database::INDEX_FULLTEXT, ['names']); - $this->fail('Failed to throw exception'); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->fail('Failed to throw exception'); + } } catch (Throwable $e) { - if ($database->getAdapter()->getSupportForFulltextIndex()) { - $this->assertEquals('"Fulltext" index is forbidden on array attributes', $e->getMessage()); - } else { - $this->assertEquals('Fulltext index is not supported', $e->getMessage()); + if ($database->getAdapter()->getSupportForAttributes()) { + if ($database->getAdapter()->getSupportForFulltextIndex()) { + $this->assertEquals('"Fulltext" index is forbidden on array attributes', $e->getMessage()); + } else { + $this->assertEquals('Fulltext index is not supported', $e->getMessage()); + } } } try { $database->createIndex($collection, 'indx', Database::INDEX_KEY, ['numbers', 'names'], [100,100]); - $this->fail('Failed to throw exception'); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->fail('Failed to throw exception'); + } } catch (Throwable $e) { - $this->assertEquals('An index may only contain one array attribute', $e->getMessage()); + if ($database->getAdapter()->getSupportForAttributes()) { + $this->assertEquals('An index may only contain one array attribute', $e->getMessage()); + } } } @@ -1517,8 +1525,10 @@ public function testArrayAttribute(): void $database->createIndex($collection, 'indx3', Database::INDEX_KEY, ['names'], [255], ['desc']); try { - $database->createIndex($collection, 'indx4', Database::INDEX_KEY, ['age', 'names'], [10, 255], []); - $this->fail('Failed to throw exception'); + if ($database->getAdapter()->getSupportForAttributes()) { + $database->createIndex($collection, 'indx4', Database::INDEX_KEY, ['age', 'names'], [10, 255], []); + $this->fail('Failed to throw exception'); + } } catch (Throwable $e) { $this->assertEquals('Cannot set a length on "integer" attributes', $e->getMessage()); } From d1d5c574dfcf176df74bbf42de49a4a9a51c8d56 Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Tue, 23 Sep 2025 15:43:26 +0530 Subject: [PATCH 06/12] updated tests failing case for the supportForAttributes --- tests/e2e/Adapter/Scopes/AttributeTests.php | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/e2e/Adapter/Scopes/AttributeTests.php b/tests/e2e/Adapter/Scopes/AttributeTests.php index b7e9c4eb7..c25041549 100644 --- a/tests/e2e/Adapter/Scopes/AttributeTests.php +++ b/tests/e2e/Adapter/Scopes/AttributeTests.php @@ -1474,12 +1474,10 @@ public function testArrayAttribute(): void $this->fail('Failed to throw exception'); } } catch (Throwable $e) { - if ($database->getAdapter()->getSupportForAttributes()) { - if ($database->getAdapter()->getSupportForFulltextIndex()) { - $this->assertEquals('"Fulltext" index is forbidden on array attributes', $e->getMessage()); - } else { - $this->assertEquals('Fulltext index is not supported', $e->getMessage()); - } + if ($database->getAdapter()->getSupportForFulltextIndex()) { + $this->assertEquals('"Fulltext" index is forbidden on array attributes', $e->getMessage()); + } else { + $this->assertEquals('Fulltext index is not supported', $e->getMessage()); } } @@ -1491,6 +1489,8 @@ public function testArrayAttribute(): void } catch (Throwable $e) { if ($database->getAdapter()->getSupportForAttributes()) { $this->assertEquals('An index may only contain one array attribute', $e->getMessage()); + } else { + $this->assertEquals('Index already exists', $e->getMessage()); } } } @@ -1703,10 +1703,8 @@ public function testCreateDatetime(): void $this->fail('Failed to throw exception'); } } catch (Throwable $e) { - if ($database->getAdapter()->getSupportForAttributes()) { - $this->assertTrue($e instanceof QueryException); - $this->assertEquals('Invalid query: Query value is invalid for attribute "date"', $e->getMessage()); - } + $this->assertTrue($e instanceof QueryException); + $this->assertEquals('Invalid query: Query value is invalid for attribute "date"', $e->getMessage()); } } From ea50e437ba8a7477e39fea72bd925be9b1d7133a Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Wed, 24 Sep 2025 09:50:59 +0530 Subject: [PATCH 07/12] pr followups * removed redundant internal attribute property * removed marked skipped * used construcuted promoted property --- src/Database/Validator/Queries/Documents.php | 8 ++++---- src/Database/Validator/Query/Order.php | 4 +--- src/Database/Validator/Query/Select.php | 4 +--- src/Database/Validator/Structure.php | 12 ++---------- tests/e2e/Adapter/Scopes/AttributeTests.php | 6 +++--- tests/e2e/Adapter/Scopes/DocumentTests.php | 2 +- 6 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/Database/Validator/Queries/Documents.php b/src/Database/Validator/Queries/Documents.php index e7bb9101a..ca3127312 100644 --- a/src/Database/Validator/Queries/Documents.php +++ b/src/Database/Validator/Queries/Documents.php @@ -30,7 +30,7 @@ public function __construct( int $maxValuesCount = 100, \DateTime $minAllowedDate = new \DateTime('0000-01-01'), \DateTime $maxAllowedDate = new \DateTime('9999-12-31'), - private bool $supportForAttributes = true + bool $supportForAttributes = true ) { $attributes[] = new Document([ '$id' => '$id', @@ -67,10 +67,10 @@ public function __construct( $maxValuesCount, $minAllowedDate, $maxAllowedDate, - $this->supportForAttributes + $supportForAttributes ), - new Order($attributes, $this->supportForAttributes), - new Select($attributes, $this->supportForAttributes), + new Order($attributes, $supportForAttributes), + new Select($attributes, $supportForAttributes), ]; parent::__construct($attributes, $indexes, $validators); diff --git a/src/Database/Validator/Query/Order.php b/src/Database/Validator/Query/Order.php index 1d051afba..6a4830cf5 100644 --- a/src/Database/Validator/Query/Order.php +++ b/src/Database/Validator/Query/Order.php @@ -11,15 +11,13 @@ class Order extends Base * @var array */ protected array $schema = []; - protected bool $supportForAttributes; /** * @param array $attributes * @param bool $supportForAttributes */ - public function __construct(array $attributes = [], bool $supportForAttributes = true) + public function __construct(array $attributes = [], protected bool $supportForAttributes = true) { - $this->supportForAttributes = $supportForAttributes; foreach ($attributes as $attribute) { $this->schema[$attribute->getAttribute('key', $attribute->getAttribute('$id'))] = $attribute->getArrayCopy(); } diff --git a/src/Database/Validator/Query/Select.php b/src/Database/Validator/Query/Select.php index 690903a4a..b0ed9e564 100644 --- a/src/Database/Validator/Query/Select.php +++ b/src/Database/Validator/Query/Select.php @@ -12,7 +12,6 @@ class Select extends Base * @var array */ protected array $schema = []; - protected bool $supportForAttributes; /** * List of internal attributes @@ -32,9 +31,8 @@ class Select extends Base * @param array $attributes * @param bool $supportForAttributes */ - public function __construct(array $attributes = [], bool $supportForAttributes = true) + public function __construct(array $attributes = [], protected bool $supportForAttributes = true) { - $this->supportForAttributes = $supportForAttributes; foreach ($attributes as $attribute) { $this->schema[$attribute->getAttribute('key', $attribute->getAttribute('$id'))] = $attribute->getArrayCopy(); } diff --git a/src/Database/Validator/Structure.php b/src/Database/Validator/Structure.php index c9b779a93..585eca022 100644 --- a/src/Database/Validator/Structure.php +++ b/src/Database/Validator/Structure.php @@ -86,10 +86,6 @@ class Structure extends Validator 'filters' => [], ] ]; - /** - * @var array - */ - private array $internalAttributes = []; /** * @var array @@ -112,10 +108,6 @@ public function __construct( private readonly \DateTime $maxAllowedDate = new \DateTime('9999-12-31'), private bool $supportForAttributes = true ) { - $this->internalAttributes = array_reduce($this->attributes, function ($carry, $attribute) { - $carry[$attribute['$id']] = 1; - return $carry; - }, []); } /** @@ -261,8 +253,8 @@ public function isValid($document): bool protected function checkForAllRequiredValues(array $structure, array $attributes, array &$keys): bool { foreach ($attributes as $key => $attribute) { // Check all required attributes are set - // schemaless adapter and not an internal attribute - if (!$this->supportForAttributes && !isset($this->internalAttributes[$key])) { + $isInternalAttribute = in_array($key, array_column($this->attributes, '$id')); + if (!$this->supportForAttributes && $isInternalAttribute) { return true; } $name = $attribute['$id'] ?? ''; diff --git a/tests/e2e/Adapter/Scopes/AttributeTests.php b/tests/e2e/Adapter/Scopes/AttributeTests.php index c25041549..567793c1f 100644 --- a/tests/e2e/Adapter/Scopes/AttributeTests.php +++ b/tests/e2e/Adapter/Scopes/AttributeTests.php @@ -393,7 +393,7 @@ public function testUpdateAttributeRequired(): void $database = static::getDatabase(); if (!$database->getAdapter()->getSupportForAttributes()) { - $this->markTestSkipped('This test is only for schema-enforced adapters'); + $this->expectNotToPerformAssertions(); } $database->updateAttributeRequired('flowers', 'inStock', true); @@ -453,7 +453,7 @@ public function testUpdateAttributeFormat(): void /** @var Database $database */ $database = static::getDatabase(); if (!$database->getAdapter()->getSupportForAttributes()) { - $this->markTestSkipped('This test is only for schema-enforced adapters'); + $this->expectNotToPerformAssertions(); } $database->createAttribute('flowers', 'price', Database::VAR_INTEGER, 0, false); @@ -655,7 +655,7 @@ public function testUpdateAttributeRename(): void /** @var Database $database */ $database = static::getDatabase(); if (!$database->getAdapter()->getSupportForAttributes()) { - $this->markTestSkipped('This test is only for schema-enforced adapters'); + $this->expectNotToPerformAssertions(); } $database->createCollection('rename_test'); diff --git a/tests/e2e/Adapter/Scopes/DocumentTests.php b/tests/e2e/Adapter/Scopes/DocumentTests.php index 0ee2582bd..ae2b091df 100644 --- a/tests/e2e/Adapter/Scopes/DocumentTests.php +++ b/tests/e2e/Adapter/Scopes/DocumentTests.php @@ -6206,7 +6206,7 @@ public function testSchemaEnforcedDocumentCreation(): void $database = static::getDatabase(); if (!$database->getAdapter()->getSupportForAttributes()) { - $this->markTestSkipped('This test is only for schema-enforced adapters'); + $this->expectNotToPerformAssertions(); } $colName = uniqid("schema"); From 1db3f61d681287111b90d9981da3c745d1a7f7f8 Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Wed, 24 Sep 2025 09:58:05 +0530 Subject: [PATCH 08/12] fix -- removed using key and using attribute keys for internal attribute checking --- src/Database/Validator/Structure.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Database/Validator/Structure.php b/src/Database/Validator/Structure.php index 585eca022..b10793bb6 100644 --- a/src/Database/Validator/Structure.php +++ b/src/Database/Validator/Structure.php @@ -252,8 +252,8 @@ public function isValid($document): bool */ protected function checkForAllRequiredValues(array $structure, array $attributes, array &$keys): bool { - foreach ($attributes as $key => $attribute) { // Check all required attributes are set - $isInternalAttribute = in_array($key, array_column($this->attributes, '$id')); + foreach ($attributes as $attribute) { // Check all required attributes are set + $isInternalAttribute = in_array($attribute['$id'], array_column($this->attributes, '$id')); if (!$this->supportForAttributes && $isInternalAttribute) { return true; } From a48bfacbe63dd939c6a09be512c123d02718b7c5 Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Wed, 24 Sep 2025 10:22:13 +0530 Subject: [PATCH 09/12] updated the usage of expectNotToPerformAssertions for schmaless testing --- tests/e2e/Adapter/Scopes/AttributeTests.php | 3 +++ tests/e2e/Adapter/Scopes/DocumentTests.php | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Adapter/Scopes/AttributeTests.php b/tests/e2e/Adapter/Scopes/AttributeTests.php index 567793c1f..60eb25f77 100644 --- a/tests/e2e/Adapter/Scopes/AttributeTests.php +++ b/tests/e2e/Adapter/Scopes/AttributeTests.php @@ -394,6 +394,7 @@ public function testUpdateAttributeRequired(): void if (!$database->getAdapter()->getSupportForAttributes()) { $this->expectNotToPerformAssertions(); + return; } $database->updateAttributeRequired('flowers', 'inStock', true); @@ -454,6 +455,7 @@ public function testUpdateAttributeFormat(): void $database = static::getDatabase(); if (!$database->getAdapter()->getSupportForAttributes()) { $this->expectNotToPerformAssertions(); + return; } $database->createAttribute('flowers', 'price', Database::VAR_INTEGER, 0, false); @@ -656,6 +658,7 @@ public function testUpdateAttributeRename(): void $database = static::getDatabase(); if (!$database->getAdapter()->getSupportForAttributes()) { $this->expectNotToPerformAssertions(); + return; } $database->createCollection('rename_test'); diff --git a/tests/e2e/Adapter/Scopes/DocumentTests.php b/tests/e2e/Adapter/Scopes/DocumentTests.php index ae2b091df..8e2eea282 100644 --- a/tests/e2e/Adapter/Scopes/DocumentTests.php +++ b/tests/e2e/Adapter/Scopes/DocumentTests.php @@ -6070,7 +6070,8 @@ public function testSchemalessDocumentOperation(): void $database = static::getDatabase(); if ($database->getAdapter()->getSupportForAttributes()) { - $this->markTestSkipped('This test is only for schemaless adapters'); + $this->expectNotToPerformAssertions(); + return; } $colName = uniqid("schemaless"); @@ -6169,7 +6170,8 @@ public function testSchemalessDocumentInvalidInteralAttributeValidation(): void // test to ensure internal attributes are checked during creating schemaless document if ($database->getAdapter()->getSupportForAttributes()) { - $this->markTestSkipped('This test is only for schemaless adapters'); + $this->expectNotToPerformAssertions(); + return; } $colName = uniqid("schemaless"); @@ -6207,6 +6209,7 @@ public function testSchemaEnforcedDocumentCreation(): void if (!$database->getAdapter()->getSupportForAttributes()) { $this->expectNotToPerformAssertions(); + return; } $colName = uniqid("schema"); From 385ba81ed6c67726c0e2c4bb2d3f9d6d6b2f6353 Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Wed, 24 Sep 2025 10:23:58 +0530 Subject: [PATCH 10/12] updated leftover markskipped to expectNotToPerformAssertions --- tests/e2e/Adapter/Scopes/DocumentTests.php | 3 ++- tests/e2e/Adapter/Scopes/GeneralTests.php | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/e2e/Adapter/Scopes/DocumentTests.php b/tests/e2e/Adapter/Scopes/DocumentTests.php index 8e2eea282..db308431f 100644 --- a/tests/e2e/Adapter/Scopes/DocumentTests.php +++ b/tests/e2e/Adapter/Scopes/DocumentTests.php @@ -5169,7 +5169,8 @@ public function testFulltextIndexWithInteger(): void $database->createIndex('documents', 'fulltext_integer', Database::INDEX_FULLTEXT, ['string','integer_signed']); } else { - $this->markTestSkipped('This test is only for schema based adapters'); + $this->expectNotToPerformAssertions(); + return; } } diff --git a/tests/e2e/Adapter/Scopes/GeneralTests.php b/tests/e2e/Adapter/Scopes/GeneralTests.php index d618682c1..97087f3d6 100644 --- a/tests/e2e/Adapter/Scopes/GeneralTests.php +++ b/tests/e2e/Adapter/Scopes/GeneralTests.php @@ -96,7 +96,8 @@ public function testPreserveDatesUpdate(): void /** @var Database $database */ $database = static::getDatabase(); if (!$database->getAdapter()->getSupportForAttributes()) { - $this->markTestSkipped('This test is only for schema based adapters'); + $this->expectNotToPerformAssertions(); + return; } $database->setPreserveDates(true); @@ -194,7 +195,8 @@ public function testPreserveDatesCreate(): void /** @var Database $database */ $database = static::getDatabase(); if (!$database->getAdapter()->getSupportForAttributes()) { - $this->markTestSkipped('This test is only for schema based adapters'); + $this->expectNotToPerformAssertions(); + return; } $database->setPreserveDates(true); From 82004997c539d62b97a0102180a6af297b1e283a Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Wed, 24 Sep 2025 10:52:09 +0530 Subject: [PATCH 11/12] removed unnecessary check for the internal attribute in the checkForAllRequiredValues --- src/Database/Validator/Structure.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Database/Validator/Structure.php b/src/Database/Validator/Structure.php index b10793bb6..5232cfb83 100644 --- a/src/Database/Validator/Structure.php +++ b/src/Database/Validator/Structure.php @@ -253,10 +253,6 @@ public function isValid($document): bool protected function checkForAllRequiredValues(array $structure, array $attributes, array &$keys): bool { foreach ($attributes as $attribute) { // Check all required attributes are set - $isInternalAttribute = in_array($attribute['$id'], array_column($this->attributes, '$id')); - if (!$this->supportForAttributes && $isInternalAttribute) { - return true; - } $name = $attribute['$id'] ?? ''; $required = $attribute['required'] ?? false; From 533e5300ebcb80864525772173abddcff73512b2 Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Wed, 24 Sep 2025 10:55:04 +0530 Subject: [PATCH 12/12] linting --- src/Database/Validator/Structure.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Database/Validator/Structure.php b/src/Database/Validator/Structure.php index 5232cfb83..421eafd83 100644 --- a/src/Database/Validator/Structure.php +++ b/src/Database/Validator/Structure.php @@ -252,11 +252,16 @@ public function isValid($document): bool */ protected function checkForAllRequiredValues(array $structure, array $attributes, array &$keys): bool { + if (!$this->supportForAttributes) { + return true; + } + foreach ($attributes as $attribute) { // Check all required attributes are set $name = $attribute['$id'] ?? ''; $required = $attribute['required'] ?? false; $keys[$name] = $attribute; // List of allowed attributes to help find unknown ones + if ($required && !isset($structure[$name])) { $this->message = 'Missing required attribute "'.$name.'"'; return false;