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); } }