From cd806e80d76780d8fcf611cb2fbe3340772f25aa Mon Sep 17 00:00:00 2001 From: arnab Date: Tue, 13 May 2025 15:39:13 +0530 Subject: [PATCH] updated tests and validations --- app/controllers/api/databases.php | 19 +++++- .../e2e/Services/Databases/DatabasesBase.php | 68 +++++++++++++++++-- 2 files changed, 80 insertions(+), 7 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 1b7108329f..3f25f65faf 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -2812,13 +2812,13 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') ->param('key', null, new Key(), 'Index Key.') ->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE]), 'Index type.') ->param('attributes', null, new ArrayList(new Key(true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of attributes to index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' attributes are allowed, each 32 characters long.') - ->param('lengths', [], new ArrayList(new Integer(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Length of index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE, optional:true) ->param('orders', [], new ArrayList(new WhiteList(['ASC', 'DESC'], false, Database::VAR_STRING), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of index orders. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' orders are allowed.', true) + ->param('lengths', [], new ArrayList(new Nullable(new Integer()), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Length of index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE, optional:true) ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $lengths, array $orders, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->action(function (string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, array $lengths, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -2832,6 +2832,10 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') throw new Exception(Exception::COLLECTION_NOT_FOUND); } + if (count($lengths) > count($attributes)) { + throw new Exception(Exception::INDEX_LENGTHS_INVALID); + } + $count = $dbForProject->count('indexes', [ Query::equal('collectionInternalId', [$collection->getInternalId()]), Query::equal('databaseInternalId', [$db->getInternalId()]) @@ -2878,6 +2882,11 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') 'size' => 0 ]; + $totalIndexLength = array_sum($lengths); + if ($totalIndexLength > 768) { + throw new Exception(Exception::INDEX_LIMIT_EXCEEDED, 'Index total length crossing 768'); + } + foreach ($attributes as $i => $attribute) { // find attribute metadata in collection document $attributeIndex = \array_search($attribute, array_column($oldAttributes, 'key')); @@ -2899,7 +2908,11 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') throw new Exception(Exception::ATTRIBUTE_NOT_AVAILABLE, 'Attribute not available: ' . $oldAttributes[$attributeIndex]['key']); } - $lengths[$i] = array_key_exists($i, $lengths) ? $lengths[$i] : null; + if ($lengths[$i] < 0) { + throw new Exception(Exception::INDEX_INVALID, 'Negative index provided for ' . $oldAttributes[$attributeIndex]['key']); + } + + $lengths[$i] ??= null; if ($attributeArray === true) { if ($lengths[$i] === null) { $lengths[$i] = Database::ARRAY_INDEX_LENGTH; diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 3ae88cca57..025394362e 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -1424,13 +1424,14 @@ trait DatabasesBase /** - * @depends testCreateAttributes - */ + * @depends testCreateAttributes + */ public function testGetIndexByKeyWithLengths(array $data): void { $databaseId = $data['databaseId']; $collectionId = $data['moviesId']; + // Test case for valid lengths $create = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/indexes", [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1441,9 +1442,9 @@ trait DatabasesBase 'attributes' => ['title','description'], 'lengths' => [128,200] ]); - $this->assertEquals(202, $create['headers']['status-code']); + // Fetch index and check correct lengths $index = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/indexes/lengthTestIndex", [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1451,7 +1452,66 @@ trait DatabasesBase ]); $this->assertEquals(200, $index['headers']['status-code']); $this->assertEquals('lengthTestIndex', $index['body']['key']); - $this->assertEquals([128,200], $index['body']['lengths']); + $this->assertEquals([128, 200], $index['body']['lengths']); + + // Test case for lengths array overriding + $create = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/indexes", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'lengthOverrideTestIndex', + 'type' => 'key', + 'attributes' => ['title', 'description'], + 'lengths' => [null, 255] + ]); + $this->assertEquals(202, $create['headers']['status-code']); + $index = $this->client->call(Client::METHOD_GET, "/databases/{$databaseId}/collections/{$collectionId}/indexes/lengthOverrideTestIndex", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]); + $this->assertEquals([null, 255], $index['body']['lengths']); + + // Test case for count of lengths greater than attributes (should throw 400) + $create = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/indexes", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'lengthCountExceededIndex', + 'type' => 'key', + 'attributes' => ['title'], + 'lengths' => [128, 128] + ]); + $this->assertEquals(400, $create['headers']['status-code']); + + // Test case for lengths exceeding total of 768 + $create = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/indexes", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'lengthTooLargeIndex', + 'type' => 'key', + 'attributes' => ['title','description','tagline','actors'], + 'lengths' => [256,256,256,20] + ]); + + $this->assertEquals(400, $create['headers']['status-code']); + + // Test case for negative length values + $create = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$collectionId}/indexes", [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'negativeLengthIndex', + 'type' => 'key', + 'attributes' => ['title'], + 'lengths' => [-1] + ]); + $this->assertEquals(400, $create['headers']['status-code']); } /** * @depends testCreateIndexes