Files
appwrite/tests/e2e/Scopes/SchemaPolling.php
2026-03-19 20:30:42 +05:30

241 lines
10 KiB
PHP

<?php
namespace Tests\E2E\Scopes;
use Appwrite\Tests\Async\Exceptions\Critical;
use Tests\E2E\Client;
/**
* Trait for polling schema changes (attributes, indexes) instead of using sleep().
* Uses assertEventually to wait for async operations to complete.
*/
trait SchemaPolling
{
/**
* Wait for an attribute to become available.
*
* @param string $databaseId The database ID
* @param string $containerId The collection/table ID
* @param string $attributeKey The attribute key to wait for
* @param int $timeoutMs Maximum time to wait in milliseconds
* @param int $waitMs Time between polling attempts in milliseconds
*/
protected function waitForAttribute(string $databaseId, string $containerId, string $attributeKey, int $timeoutMs = 240000, int $waitMs = 500): void
{
if (!$this->getSupportForAttributes()) {
return;
}
$this->assertEventually(function () use ($databaseId, $containerId, $attributeKey) {
$attribute = $this->client->call(
Client::METHOD_GET,
$this->getSchemaUrl($databaseId, $containerId) . '/' . $attributeKey,
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
])
);
$this->assertEquals(200, $attribute['headers']['status-code']);
$status = $attribute['body']['status'] ?? '';
if ($status === 'failed') {
throw new Critical("Attribute '{$attributeKey}' failed: " . ($attribute['body']['error'] ?? 'unknown error'));
}
$this->assertEquals('available', $status);
}, $timeoutMs, $waitMs);
}
/**
* Wait for multiple attributes to become available.
*
* @param string $databaseId The database ID
* @param string $containerId The collection/table ID
* @param array $attributeKeys Array of attribute keys to wait for
* @param int $timeoutMs Maximum time to wait in milliseconds
* @param int $waitMs Time between polling attempts in milliseconds
*/
protected function waitForAttributes(string $databaseId, string $containerId, array $attributeKeys, int $timeoutMs = 240000, int $waitMs = 500): void
{
$this->assertEventually(function () use ($databaseId, $containerId, $attributeKeys) {
$container = $this->client->call(
Client::METHOD_GET,
$this->getContainerUrl($databaseId, $containerId),
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
])
);
$this->assertEquals(200, $container['headers']['status-code']);
$this->assertArrayHasKey('body', $container);
$this->assertArrayHasKey($this->getSchemaResource(), $container['body']);
$attributes = $container['body'][$this->getSchemaResource()];
$availableKeys = [];
foreach ($attributes as $attr) {
if ($attr['status'] === 'failed') {
throw new Critical("Attribute '{$attr['key']}' failed: " . ($attr['error'] ?? 'unknown error'));
}
if ($attr['status'] === 'available') {
$availableKeys[] = $attr['key'];
}
}
foreach ($attributeKeys as $key) {
$this->assertContains($key, $availableKeys, "Attribute '$key' is not available yet");
}
}, $timeoutMs, $waitMs);
}
/**
* Wait for the collection/table to have at least a certain number of available attributes.
*
* @param string $databaseId The database ID
* @param string $containerId The collection/table ID
* @param int $count Minimum number of available attributes required
* @param int $timeoutMs Maximum time to wait in milliseconds
* @param int $waitMs Time between polling attempts in milliseconds
*/
protected function waitForAttributeCount(string $databaseId, string $containerId, int $count, int $timeoutMs = 240000, int $waitMs = 500): void
{
$this->assertEventually(function () use ($databaseId, $containerId, $count) {
$container = $this->client->call(
Client::METHOD_GET,
$this->getContainerUrl($databaseId, $containerId),
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
])
);
$this->assertEquals(200, $container['headers']['status-code']);
$this->assertArrayHasKey('body', $container);
$this->assertArrayHasKey($this->getSchemaResource(), $container['body']);
$attributes = $container['body'][$this->getSchemaResource()];
$availableCount = 0;
foreach ($attributes as $attr) {
if ($attr['status'] === 'failed') {
throw new Critical("Attribute '{$attr['key']}' failed: " . ($attr['error'] ?? 'unknown error'));
}
if ($attr['status'] === 'available') {
$availableCount++;
}
}
$this->assertGreaterThanOrEqual($count, $availableCount, "Expected at least $count available attributes, got $availableCount");
}, $timeoutMs, $waitMs);
}
/**
* Wait for an index to become available.
*
* @param string $databaseId The database ID
* @param string $containerId The collection/table ID
* @param string $indexKey The index key to wait for
* @param int $timeoutMs Maximum time to wait in milliseconds
* @param int $waitMs Time between polling attempts in milliseconds
*/
protected function waitForIndex(string $databaseId, string $containerId, string $indexKey, int $timeoutMs = 240000, int $waitMs = 500): void
{
$this->assertEventually(function () use ($databaseId, $containerId, $indexKey) {
$index = $this->client->call(
Client::METHOD_GET,
$this->getIndexUrl($databaseId, $containerId, $indexKey),
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
])
);
$this->assertEquals(200, $index['headers']['status-code']);
$this->assertArrayHasKey('body', $index);
$this->assertArrayHasKey('status', $index['body']);
$status = $index['body']['status'] ?? '';
if ($status === 'failed') {
throw new Critical("Index '{$indexKey}' failed: " . ($index['body']['error'] ?? 'unknown error'));
}
$this->assertEquals('available', $status);
}, $timeoutMs, $waitMs);
}
/**
* Wait for all indexes in a collection/table to become available.
*
* @param string $databaseId The database ID
* @param string $containerId The collection/table ID
* @param int $timeoutMs Maximum time to wait in milliseconds
* @param int $waitMs Time between polling attempts in milliseconds
*/
protected function waitForAllIndexes(string $databaseId, string $containerId, int $timeoutMs = 240000, int $waitMs = 500): void
{
$this->assertEventually(function () use ($databaseId, $containerId) {
$container = $this->client->call(
Client::METHOD_GET,
$this->getContainerUrl($databaseId, $containerId),
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
])
);
$this->assertEquals(200, $container['headers']['status-code']);
$this->assertArrayHasKey('body', $container);
$this->assertArrayHasKey('indexes', $container['body']);
foreach ($container['body']['indexes'] as $index) {
if ($index['status'] === 'failed') {
throw new Critical("Index '{$index['key']}' failed: " . ($index['error'] ?? 'unknown error'));
}
$this->assertEquals('available', $index['status'], "Index '{$index['key']}' is not available yet");
}
}, $timeoutMs, $waitMs);
}
/**
* Wait for all attributes in a collection/table to become available.
*
* @param string $databaseId The database ID
* @param string $containerId The collection/table ID
* @param int $timeoutMs Maximum time to wait in milliseconds
* @param int $waitMs Time between polling attempts in milliseconds
*/
protected function waitForAllAttributes(string $databaseId, string $containerId, int $timeoutMs = 240000, int $waitMs = 500): void
{
$this->assertEventually(function () use ($databaseId, $containerId) {
$container = $this->client->call(
Client::METHOD_GET,
$this->getContainerUrl($databaseId, $containerId),
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
])
);
// Tolerate transient 500s during heavy attribute processing
$this->assertContains($container['headers']['status-code'], [200], "Expected 200 but got {$container['headers']['status-code']} polling container {$containerId}");
$schemaResource = $this->getSchemaResource();
$this->assertNotEmpty($container['body'][$schemaResource], "No attributes found in container {$containerId}");
foreach ($container['body'][$schemaResource] as $attribute) {
if ($attribute['status'] === 'failed') {
throw new Critical("Attribute '{$attribute['key']}' failed: " . ($attribute['error'] ?? 'unknown error'));
}
$this->assertEquals('available', $attribute['status'], "Attribute '{$attribute['key']}' is not available yet");
}
}, $timeoutMs, $waitMs);
}
}