Conversation
📝 WalkthroughWalkthroughThis pull request introduces a new Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes The changes span seven files with significant structural shifts in test synchronization logic. The new 🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Security Scan Results for PRDocker Image Scan Results
Source Code Scan Results🎉 No vulnerabilities found! |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
tests/e2e/Services/Databases/TablesDB/DatabasesBase.php (1)
4523-4523: TypoCLient::METHOD_PUTwill cause a fatal error
CLient(capital “L”) is not the imported class; this line will fail with an undefined class error instead of executing the request. It should useClient::METHOD_PUTlike the rest of the file.Proposed fix
- $this->client->call(CLient::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $tableId, [ + $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $tableId, [tests/e2e/Services/Databases/Legacy/Transactions/ACIDTest.php (1)
213-235: Missing wait for therequired_fieldattribute.The test creates both
required_field(lines 213-221) andage(lines 224-233) attributes, but only waits forageto be ready (line 235). Since the transaction operations at lines 258-260 use both attributes,required_fieldmust also be available before proceeding. The previous fixed sleep likely gave both attributes time to become ready, but the explicit polling for onlyagemay not.⏱️ Proposed fix to wait for both attributes
]); + $this->waitForAttribute($databaseId, $collectionId, 'required_field'); $this->waitForAttribute($databaseId, $collectionId, 'age');
🤖 Fix all issues with AI agents
In @tests/e2e/Traits/SchemaPoll.php:
- Around line 149-181: In waitForAttributeDeletion, add explicit handling for a
'failed' status: after reading $status from the response in the closure inside
assertEventually, if $status === 'failed' throw an Exception (or fail the
assertion) indicating attribute deletion failed with status 'failed' for the
given $attributeKey; keep the existing checks for 'available' and 'deleting'
unchanged so the poll only succeeds when the attribute is 404 or otherwise
confirmed deleted.
- Around line 183-215: In waitForIndexDeletion, explicitly handle the 'failed'
index status like waitForAttributeDeletion does: when reading $status from the
response body in waitForIndexDeletion, add a check for $status === 'failed' and
throw a clear Exception (including the indexKey and status) so the poll does not
treat a failed deletion as success; keep the existing check for 'available' and
the assertNotEquals('deleting', $status) but ensure 'failed' causes an immediate
failure during polling.
🧹 Nitpick comments (8)
tests/e2e/Services/Databases/Legacy/DatabasesBase.php (1)
357-359: Consider replacing fixed sleeps with SchemaPoll polling helpersNow that
SchemaPollis mixed into this trait, these hard-codedsleep()calls (and similar ones elsewhere in this file) could be gradually replaced with the trait’swaitFor*helpers so tests wait on actual attribute/index readiness instead of time-based guesses. This would reduce flakiness and speed up runs when the worker is faster than the fixed delay.tests/e2e/Services/Databases/TablesDB/DatabasesBase.php (1)
370-372: Consider replacing remainingsleep()calls with SchemaPoll wait helpersNow that
SchemaPollis wired in, the fixedsleep()after column/index creation (and similar sleeps elsewhere in this trait) can be swapped forwaitForAttribute/waitForAllAttributes/waitForIndex-style helpers to reduce flakiness and keep this suite consistent with the rest of the PR.tests/e2e/Services/Databases/TablesDB/DatabasesCustomClientTest.php (1)
214-214: Consider replacing fixed delay with polling.This fixed
usleep(500 * 1000)is inconsistent with the PR's objective to remove database sleeps. Since table deletion is asynchronous, consider using a polling approach similar to the attribute waits, or verify that the subsequent 404 check (lines 217-222) adequately handles the async nature of deletion without needing this delay.🔄 Suggested polling approach
If table deletion timing is critical, consider adding a helper method similar to
waitForAttributethat polls for table deletion:// Poll until table returns 404 $this->assertEventually(function () use ($databaseId) { $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/permissionCheck', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ])); $this->assertEquals(404, $response['headers']['status-code']); return true; }, 5000, 100);Alternatively, if the immediate 404 check works reliably without the sleep, the
usleepcould potentially be removed entirely.tests/e2e/Services/Databases/TablesDB/DatabasesCustomServerTest.php (2)
1306-1317: Replace hand-rolled polling loops withassertEventuallyfor consistencyBoth deletion checks use manual
microtime/usleeploops to wait for a 404, duplicating whatassertEventuallyalready does inSchemaPoll. Refactoring toassertEventuallywould reduce boilerplate and give clearer failure output on timeouts.Example refactor using
assertEventually- $start = microtime(true) * 1000; - while ((microtime(true) * 1000) - $start < 30000) { - $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $table2, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ])); - if ($response['headers']['status-code'] === 404) { - break; - } - usleep(100 * 1000); - } + $this->assertEventually(function () use ($databaseId, $table2) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $table2, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + + $this->assertEquals(404, $response['headers']['status-code']); + return true; + }, 30000, 100);A similar pattern can be applied in
cleanupRelationshipTable()for/tablesdb/database1.Also applies to: 3697-3710
4497-4497: Consider replacing remaining fixedusleepcalls with pollingThe two
usleep(500 * 1000);waits around the bulk-update cache invalidation are small but still time-based heuristics. If these tests are still flaky on slow environments, you might want to replace them with anassertEventuallycheck on whatever invariant you expect after the cache purge (e.g., row count or field values) instead of sleeping for a fixed duration.Also applies to: 4523-4523
tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (2)
3745-3766: Make database deletion polling fail explicitly on timeout
cleanupRelationshipCollection()’s manual polling loop exits quietly after 30s even if/databases/database1never returns 404, which can leave residual state and make later tests fail in harder-to-diagnose ways. Adding an explicit failure after the loop would make such issues surface clearly.💡 Minimal change to fail on timeout
$this->client->call(Client::METHOD_DELETE, '/databases/database1', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - // Poll until database is deleted (returns 404) + // Poll until database is deleted (returns 404) $start = microtime(true) * 1000; $timeoutMs = 30000; while ((microtime(true) * 1000) - $start < $timeoutMs) { $response = $this->client->call(Client::METHOD_GET, '/databases/database1', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]); if ($response['headers']['status-code'] === 404) { return; } usleep(100 * 1000); } + + $this->fail('Timed out waiting for database1 deletion');
4553-4553: Consider replacing fixed 500 ms cache sleeps with assertion-based pollingThe two
usleep(500 * 1000)calls still rely on hard-coded timing for cache purge, which may be tight on slower CI or busy DB nodes and can reintroduce flakiness. Since you already haveassertEventuallyavailable, you could instead poll for the expected document state (e.g., updatednumbervalues) and drop these fixed sleeps.Also applies to: 4579-4579
tests/e2e/Services/Databases/Legacy/DatabasesCustomClientTest.php (1)
214-225: Consider extracting collection deletion polling to SchemaPoll trait.This manual polling loop is functional but inconsistent with the trait-based approach used for attributes and indexes. Extracting a
waitForCollectionDeletionmethod to the SchemaPoll trait would improve code reusability and consistency across the test suite.♻️ Example trait method to add to SchemaPoll.php
protected function waitForCollectionDeletion( string $databaseId, string $collectionId, int $timeoutMs = 30000, int $intervalMs = 100 ): void { $config = $this->getSchemaApiConfig(); $this->assertEventually(function () use ($config, $databaseId, $collectionId) { $response = $this->client->call( Client::METHOD_GET, $config['basePath'] . '/' . $databaseId . '/' . $config['collectionPath'] . '/' . $collectionId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]) ); $this->assertEquals(404, $response['headers']['status-code']); return true; }, $timeoutMs, $intervalMs); }Then replace the manual loop with:
-$start = microtime(true) * 1000; -while ((microtime(true) * 1000) - $start < 30000) { - $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/permissionCheck', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); - if ($response['headers']['status-code'] === 404) { - break; - } - usleep(100 * 1000); -} +$this->waitForCollectionDeletion($databaseId, 'permissionCheck');
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
tests/e2e/Services/Databases/Legacy/DatabasesBase.phptests/e2e/Services/Databases/Legacy/DatabasesCustomClientTest.phptests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.phptests/e2e/Services/Databases/Legacy/Transactions/ACIDTest.phptests/e2e/Services/Databases/TablesDB/DatabasesBase.phptests/e2e/Services/Databases/TablesDB/DatabasesCustomClientTest.phptests/e2e/Services/Databases/TablesDB/DatabasesCustomServerTest.phptests/e2e/Traits/SchemaPoll.php
🧰 Additional context used
🧬 Code graph analysis (6)
tests/e2e/Services/Databases/TablesDB/DatabasesCustomClientTest.php (1)
tests/e2e/Traits/SchemaPoll.php (2)
waitForAttribute(19-47)waitForAllAttributes(79-112)
tests/e2e/Services/Databases/TablesDB/DatabasesBase.php (1)
tests/e2e/Traits/SchemaPoll.php (1)
getSchemaApiConfig(9-17)
tests/e2e/Services/Databases/TablesDB/DatabasesCustomServerTest.php (1)
tests/e2e/Traits/SchemaPoll.php (6)
waitForAllAttributes(79-112)waitForAttribute(19-47)waitForIndex(49-77)waitForAttributeDeletion(149-181)waitForIndexDeletion(183-215)waitForAllIndexes(114-147)
tests/e2e/Traits/SchemaPoll.php (3)
tests/e2e/Client.php (1)
Client(8-342)tests/e2e/Services/Databases/TablesDB/DatabasesBase.php (1)
getSchemaApiConfig(25-33)src/Appwrite/Extend/Exception.php (1)
Exception(7-472)
tests/e2e/Services/Databases/Legacy/Transactions/ACIDTest.php (1)
tests/e2e/Traits/SchemaPoll.php (2)
waitForAttribute(19-47)waitForIndex(49-77)
tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (1)
tests/e2e/Traits/SchemaPoll.php (6)
waitForAttribute(19-47)waitForAllAttributes(79-112)waitForIndex(49-77)waitForAttributeDeletion(149-181)waitForIndexDeletion(183-215)waitForAllIndexes(114-147)
🔇 Additional comments (19)
tests/e2e/Services/Databases/Legacy/DatabasesBase.php (1)
5-21: SchemaPoll trait integration looks correctImporting
Tests\E2E\Traits\SchemaPolland addinguse SchemaPoll;inDatabasesBasecleanly exposes the new polling helpers to all legacy database tests that use this trait, without altering existing behavior. No issues from this wiring alone.tests/e2e/Services/Databases/TablesDB/DatabasesBase.php (1)
7-7: SchemaPoll integration and TablesDB API config override look correctThe new
SchemaPolltrait usage plusgetSchemaApiConfig()override cleanly point the polling helpers at/tablesdb/{databaseId}/tables/{tableId}/columns|indexes, matching the existing endpoints in this trait. No functional issues spotted here.Also applies to: 20-33
tests/e2e/Services/Databases/TablesDB/DatabasesCustomClientTest.php (5)
67-69: LGTM! Proper synchronization after attribute creation.The change from sleep to
waitForAttributeensures the test waits dynamically for attribute readiness rather than relying on fixed delays. This improves test reliability and reduces flakiness.
158-160: LGTM! Consistent polling pattern.Correctly waits for the 'name' attribute to be available before proceeding with document operations.
290-290: LGTM! Proper relationship attribute synchronization.Correctly waits for the relationship attribute to be available before proceeding with relationship operations.
374-374: LGTM! Appropriate conditional synchronization.The implementation correctly waits for relationship attributes after successful creation (lines 374, 409, 445) while skipping waits for failed requests that return 409 conflicts (lines 392, 427, 464). The added comments clearly document why no synchronization is needed for failed requests.
Also applies to: 392-394, 409-409, 427-429, 445-445, 464-466
668-672: LGTM! Efficient bulk synchronization.Using
waitForAllAttributesfor each table is the correct approach after creating multiple attributes and relationships. This ensures all schema changes are complete before proceeding with document operations, improving test reliability while being more efficient than waiting for each attribute individually.tests/e2e/Services/Databases/TablesDB/DatabasesCustomServerTest.php (2)
730-730: SchemaPoll helper usage looks correct and improves test determinismAll the new
waitForAttribute*/waitForAll*/waitForIndex*calls are wired with the expected(databaseId, tableId, key)ordering and placed before dependent row/assert logic, which should remove the old timing races around async schema changes. No issues from a correctness perspective.Also applies to: 830-830, 863-863, 891-891, 935-935, 979-980, 1008-1009, 1020-1020, 1084-1085, 1108-1109, 1137-1138, 1149-1149, 1298-1298, 1378-1378, 1445-1445, 1667-1667, 3734-3734, 3848-3848, 3962-3962, 4080-4080, 4228-4228, 4466-4466, 4687-4687, 4755-4755, 4946-4946, 5219-5219, 5279-5279, 5401-5401, 5687-5687, 5932-5932, 6244-6244, 6641-6641
3686-3686: Comment on synchronous table creation aligns with new waiting strategyThe note that tables are created synchronously explains why no
waitFor*helper is used here, while schema elements and relationships now rely on polling. This is consistent as long as table creation semantics stay synchronous.tests/e2e/Services/Databases/Legacy/Transactions/ACIDTest.php (4)
9-9: LGTM!The import and application of the
SchemaPolltrait enables polling utilities for schema readiness checks, replacing fixed sleeps with dynamic resource polling.Also applies to: 19-19
78-79: LGTM!Properly waits for both the email attribute and unique_email index to be available before proceeding with document operations. This ensures the schema is fully ready and eliminates race conditions.
356-356: LGTM!Correctly waits for the
counterattribute to be available before creating the initial document and proceeding with transaction operations.
513-513: LGTM!Correctly waits for the
dataattribute to be available before proceeding with transaction operations.tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php (1)
747-1724: SchemaPoll-based waits correctly gate async schema and relationship operationsThe new
waitForAttribute*/waitForIndex*/waitForAll*usages line up with the attributes and indexes you create/delete, and they consistently wrap the subsequent assertions that depend on those resources. This should substantially reduce timing-related flakiness compared to the previous fixed sleeps while preserving the existing test intent.Also applies to: 3790-4522, 4760-5352, 5473-5991, 6303-6693
tests/e2e/Services/Databases/Legacy/DatabasesCustomClientTest.php (3)
69-70: LGTM - Clean replacement of sleep with explicit polling.The attribute polling ensures the 'title' attribute is available before proceeding, improving test reliability.
683-688: LGTM - Proper synchronization for complex relationship setup.The sequential polling ensures all attributes (including relationships) across five collections are available before proceeding with nested document creation. This is a good replacement for fixed sleeps and properly handles the asynchronous nature of schema changes.
287-287: LGTM - Clear documentation of synchronous operations.The comments explaining when polling is not needed (synchronous collection creation and immediate validation failures) improve test readability and help future maintainers understand the control flow.
Also applies to: 374-374, 407-409, 442-443, 479-481
tests/e2e/Traits/SchemaPoll.php (2)
9-17: LGTM - Clean abstraction for API path configuration.The overridable
getSchemaApiConfig()method allows different test suites (Legacy vs TablesDB) to use the same polling utilities with different API endpoints, promoting code reuse.
19-147: LGTM - Robust polling implementation for resource creation and availability.The polling methods for attributes and indexes properly handle:
- Explicit 'failed' status checks with descriptive error messages
- Configurable timeouts and intervals with sensible defaults (30s for attributes, 60s for indexes)
- Support for both Legacy and TablesDB APIs through the configurable path mechanism
- Batch operations (
waitForAllAttributes,waitForAllIndexes) with proper iteration and error reportingThis is a solid foundation for eliminating flaky test timing issues.
| protected function waitForAttributeDeletion( | ||
| string $databaseId, | ||
| string $collectionId, | ||
| string $attributeKey, | ||
| int $timeoutMs = 30000, | ||
| int $intervalMs = 100 | ||
| ): void { | ||
| $config = $this->getSchemaApiConfig(); | ||
|
|
||
| $this->assertEventually(function () use ($config, $databaseId, $collectionId, $attributeKey) { | ||
| $response = $this->client->call( | ||
| Client::METHOD_GET, | ||
| $config['basePath'] . '/' . $databaseId . '/' . $config['collectionPath'] . '/' . $collectionId . '/' . $config['attributePath'] . '/' . $attributeKey, | ||
| array_merge([ | ||
| 'content-type' => 'application/json', | ||
| 'x-appwrite-project' => $this->getProject()['$id'], | ||
| ], $this->getHeaders()) | ||
| ); | ||
|
|
||
| if ($response['headers']['status-code'] === 404) { | ||
| return true; | ||
| } | ||
|
|
||
| $status = $response['body']['status'] ?? 'unknown'; | ||
|
|
||
| if ($status === 'available') { | ||
| throw new \Exception("Attribute '{$attributeKey}' deletion failed - still available"); | ||
| } | ||
|
|
||
| $this->assertNotEquals('deleting', $status); | ||
| return true; | ||
| }, $timeoutMs, $intervalMs); | ||
| } |
There was a problem hiding this comment.
Add explicit handling for 'failed' status in deletion polling.
The current logic doesn't explicitly check for a 'failed' status during deletion. If a deletion operation fails and the status becomes 'failed' (rather than remaining 'deleting' or returning to 'available'), the method will return true and consider it successful.
🐛 Proposed fix to handle 'failed' status
protected function waitForAttributeDeletion(
string $databaseId,
string $collectionId,
string $attributeKey,
int $timeoutMs = 30000,
int $intervalMs = 100
): void {
$config = $this->getSchemaApiConfig();
$this->assertEventually(function () use ($config, $databaseId, $collectionId, $attributeKey) {
$response = $this->client->call(
Client::METHOD_GET,
$config['basePath'] . '/' . $databaseId . '/' . $config['collectionPath'] . '/' . $collectionId . '/' . $config['attributePath'] . '/' . $attributeKey,
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders())
);
if ($response['headers']['status-code'] === 404) {
return true;
}
$status = $response['body']['status'] ?? 'unknown';
if ($status === 'available') {
throw new \Exception("Attribute '{$attributeKey}' deletion failed - still available");
}
+ if ($status === 'failed') {
+ throw new \Exception("Attribute '{$attributeKey}' deletion failed: " . ($response['body']['error'] ?? 'unknown error'));
+ }
+
$this->assertNotEquals('deleting', $status);
return true;
}, $timeoutMs, $intervalMs);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| protected function waitForAttributeDeletion( | |
| string $databaseId, | |
| string $collectionId, | |
| string $attributeKey, | |
| int $timeoutMs = 30000, | |
| int $intervalMs = 100 | |
| ): void { | |
| $config = $this->getSchemaApiConfig(); | |
| $this->assertEventually(function () use ($config, $databaseId, $collectionId, $attributeKey) { | |
| $response = $this->client->call( | |
| Client::METHOD_GET, | |
| $config['basePath'] . '/' . $databaseId . '/' . $config['collectionPath'] . '/' . $collectionId . '/' . $config['attributePath'] . '/' . $attributeKey, | |
| array_merge([ | |
| 'content-type' => 'application/json', | |
| 'x-appwrite-project' => $this->getProject()['$id'], | |
| ], $this->getHeaders()) | |
| ); | |
| if ($response['headers']['status-code'] === 404) { | |
| return true; | |
| } | |
| $status = $response['body']['status'] ?? 'unknown'; | |
| if ($status === 'available') { | |
| throw new \Exception("Attribute '{$attributeKey}' deletion failed - still available"); | |
| } | |
| $this->assertNotEquals('deleting', $status); | |
| return true; | |
| }, $timeoutMs, $intervalMs); | |
| } | |
| protected function waitForAttributeDeletion( | |
| string $databaseId, | |
| string $collectionId, | |
| string $attributeKey, | |
| int $timeoutMs = 30000, | |
| int $intervalMs = 100 | |
| ): void { | |
| $config = $this->getSchemaApiConfig(); | |
| $this->assertEventually(function () use ($config, $databaseId, $collectionId, $attributeKey) { | |
| $response = $this->client->call( | |
| Client::METHOD_GET, | |
| $config['basePath'] . '/' . $databaseId . '/' . $config['collectionPath'] . '/' . $collectionId . '/' . $config['attributePath'] . '/' . $attributeKey, | |
| array_merge([ | |
| 'content-type' => 'application/json', | |
| 'x-appwrite-project' => $this->getProject()['$id'], | |
| ], $this->getHeaders()) | |
| ); | |
| if ($response['headers']['status-code'] === 404) { | |
| return true; | |
| } | |
| $status = $response['body']['status'] ?? 'unknown'; | |
| if ($status === 'available') { | |
| throw new \Exception("Attribute '{$attributeKey}' deletion failed - still available"); | |
| } | |
| if ($status === 'failed') { | |
| throw new \Exception("Attribute '{$attributeKey}' deletion failed: " . ($response['body']['error'] ?? 'unknown error')); | |
| } | |
| $this->assertNotEquals('deleting', $status); | |
| return true; | |
| }, $timeoutMs, $intervalMs); | |
| } |
🤖 Prompt for AI Agents
In @tests/e2e/Traits/SchemaPoll.php around lines 149 - 181, In
waitForAttributeDeletion, add explicit handling for a 'failed' status: after
reading $status from the response in the closure inside assertEventually, if
$status === 'failed' throw an Exception (or fail the assertion) indicating
attribute deletion failed with status 'failed' for the given $attributeKey; keep
the existing checks for 'available' and 'deleting' unchanged so the poll only
succeeds when the attribute is 404 or otherwise confirmed deleted.
| protected function waitForIndexDeletion( | ||
| string $databaseId, | ||
| string $collectionId, | ||
| string $indexKey, | ||
| int $timeoutMs = 60000, | ||
| int $intervalMs = 100 | ||
| ): void { | ||
| $config = $this->getSchemaApiConfig(); | ||
|
|
||
| $this->assertEventually(function () use ($config, $databaseId, $collectionId, $indexKey) { | ||
| $response = $this->client->call( | ||
| Client::METHOD_GET, | ||
| $config['basePath'] . '/' . $databaseId . '/' . $config['collectionPath'] . '/' . $collectionId . '/' . $config['indexPath'] . '/' . $indexKey, | ||
| array_merge([ | ||
| 'content-type' => 'application/json', | ||
| 'x-appwrite-project' => $this->getProject()['$id'], | ||
| ], $this->getHeaders()) | ||
| ); | ||
|
|
||
| if ($response['headers']['status-code'] === 404) { | ||
| return true; | ||
| } | ||
|
|
||
| $status = $response['body']['status'] ?? 'unknown'; | ||
|
|
||
| if ($status === 'available') { | ||
| throw new \Exception("Index '{$indexKey}' deletion failed - still available"); | ||
| } | ||
|
|
||
| $this->assertNotEquals('deleting', $status); | ||
| return true; | ||
| }, $timeoutMs, $intervalMs); | ||
| } |
There was a problem hiding this comment.
Add explicit handling for 'failed' status in index deletion polling.
Similar to waitForAttributeDeletion, this method doesn't explicitly check for a 'failed' status. If an index deletion fails with a 'failed' status, the method will incorrectly consider it successful.
🐛 Proposed fix to handle 'failed' status
protected function waitForIndexDeletion(
string $databaseId,
string $collectionId,
string $indexKey,
int $timeoutMs = 60000,
int $intervalMs = 100
): void {
$config = $this->getSchemaApiConfig();
$this->assertEventually(function () use ($config, $databaseId, $collectionId, $indexKey) {
$response = $this->client->call(
Client::METHOD_GET,
$config['basePath'] . '/' . $databaseId . '/' . $config['collectionPath'] . '/' . $collectionId . '/' . $config['indexPath'] . '/' . $indexKey,
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders())
);
if ($response['headers']['status-code'] === 404) {
return true;
}
$status = $response['body']['status'] ?? 'unknown';
if ($status === 'available') {
throw new \Exception("Index '{$indexKey}' deletion failed - still available");
}
+ if ($status === 'failed') {
+ throw new \Exception("Index '{$indexKey}' deletion failed: " . ($response['body']['error'] ?? 'unknown error'));
+ }
+
$this->assertNotEquals('deleting', $status);
return true;
}, $timeoutMs, $intervalMs);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| protected function waitForIndexDeletion( | |
| string $databaseId, | |
| string $collectionId, | |
| string $indexKey, | |
| int $timeoutMs = 60000, | |
| int $intervalMs = 100 | |
| ): void { | |
| $config = $this->getSchemaApiConfig(); | |
| $this->assertEventually(function () use ($config, $databaseId, $collectionId, $indexKey) { | |
| $response = $this->client->call( | |
| Client::METHOD_GET, | |
| $config['basePath'] . '/' . $databaseId . '/' . $config['collectionPath'] . '/' . $collectionId . '/' . $config['indexPath'] . '/' . $indexKey, | |
| array_merge([ | |
| 'content-type' => 'application/json', | |
| 'x-appwrite-project' => $this->getProject()['$id'], | |
| ], $this->getHeaders()) | |
| ); | |
| if ($response['headers']['status-code'] === 404) { | |
| return true; | |
| } | |
| $status = $response['body']['status'] ?? 'unknown'; | |
| if ($status === 'available') { | |
| throw new \Exception("Index '{$indexKey}' deletion failed - still available"); | |
| } | |
| $this->assertNotEquals('deleting', $status); | |
| return true; | |
| }, $timeoutMs, $intervalMs); | |
| } | |
| protected function waitForIndexDeletion( | |
| string $databaseId, | |
| string $collectionId, | |
| string $indexKey, | |
| int $timeoutMs = 60000, | |
| int $intervalMs = 100 | |
| ): void { | |
| $config = $this->getSchemaApiConfig(); | |
| $this->assertEventually(function () use ($config, $databaseId, $collectionId, $indexKey) { | |
| $response = $this->client->call( | |
| Client::METHOD_GET, | |
| $config['basePath'] . '/' . $databaseId . '/' . $config['collectionPath'] . '/' . $collectionId . '/' . $config['indexPath'] . '/' . $indexKey, | |
| array_merge([ | |
| 'content-type' => 'application/json', | |
| 'x-appwrite-project' => $this->getProject()['$id'], | |
| ], $this->getHeaders()) | |
| ); | |
| if ($response['headers']['status-code'] === 404) { | |
| return true; | |
| } | |
| $status = $response['body']['status'] ?? 'unknown'; | |
| if ($status === 'available') { | |
| throw new \Exception("Index '{$indexKey}' deletion failed - still available"); | |
| } | |
| if ($status === 'failed') { | |
| throw new \Exception("Index '{$indexKey}' deletion failed: " . ($response['body']['error'] ?? 'unknown error')); | |
| } | |
| $this->assertNotEquals('deleting', $status); | |
| return true; | |
| }, $timeoutMs, $intervalMs); | |
| } |
🤖 Prompt for AI Agents
In @tests/e2e/Traits/SchemaPoll.php around lines 183 - 215, In
waitForIndexDeletion, explicitly handle the 'failed' index status like
waitForAttributeDeletion does: when reading $status from the response body in
waitForIndexDeletion, add a check for $status === 'failed' and throw a clear
Exception (including the indexKey and status) so the poll does not treat a
failed deletion as success; keep the existing check for 'available' and the
assertNotEquals('deleting', $status) but ensure 'failed' causes an immediate
failure during polling.
✨ Benchmark results
⚡ Benchmark Comparison
|
What does this PR do?
(Provide a description of what this PR does and why it's needed.)
Test Plan
(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work. Screenshots may also be helpful.)
Related PRs and Issues
Checklist