diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9f7143b98b..a681575f0a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -98,16 +98,14 @@ jobs: fail-on-cache-miss: true - name: Load and Start Appwrite + timeout-minutes: 3 run: | docker load --input /tmp/${{ env.IMAGE }}.tar docker compose up -d - sleep 10 - - - name: Logs - run: docker compose logs appwrite - - - name: Doctor - run: docker compose exec -T appwrite doctor + until docker compose exec -T appwrite doctor > /dev/null 2>&1; do + echo "Waiting for Appwrite to be ready..." + sleep 2 + done - name: Environment Variables run: docker compose exec -T appwrite vars @@ -115,8 +113,8 @@ jobs: - name: Run Unit Tests uses: itznotabug/php-retry@v3 with: - max_attempts: 3 - timeout_minutes: 30 + max_attempts: 2 + timeout_minutes: 15 job_id: ${{ job.check_run_id }} github_token: ${{ secrets.GITHUB_TOKEN }} test_dir: tests/unit @@ -144,11 +142,15 @@ jobs: fail-on-cache-miss: true - name: Load and Start Appwrite + timeout-minutes: 3 run: | docker load --input /tmp/${{ env.IMAGE }}.tar docker compose up -d - sleep 10 - + until docker compose exec -T appwrite doctor > /dev/null 2>&1; do + echo "Waiting for Appwrite to be ready..." + sleep 2 + done + - name: Wait for Open Runtimes timeout-minutes: 3 run: | @@ -160,8 +162,8 @@ jobs: - name: Run General Tests uses: itznotabug/php-retry@v3 with: - max_attempts: 3 - timeout_minutes: 30 + max_attempts: 2 + timeout_minutes: 15 job_id: ${{ job.check_run_id }} github_token: ${{ secrets.GITHUB_TOKEN }} test_dir: tests/e2e/General @@ -246,12 +248,16 @@ jobs: fi - name: Load and Start Appwrite + timeout-minutes: 3 env: _APP_BROWSER_HOST: http://invalid-browser/v1 run: | docker load --input /tmp/${{ env.IMAGE }}.tar docker compose up -d - sleep 30 + until docker compose exec -T appwrite doctor > /dev/null 2>&1; do + echo "Waiting for Appwrite to be ready..." + sleep 2 + done - name: Wait for Open Runtimes timeout-minutes: 3 @@ -264,8 +270,8 @@ jobs: - name: Run ${{ matrix.service }} tests with Project table mode uses: itznotabug/php-retry@v3 with: - max_attempts: 3 - timeout_minutes: 40 + max_attempts: 2 + timeout_minutes: 20 job_id: ${{ job.check_run_id }} github_token: ${{ secrets.GITHUB_TOKEN }} test_dir: tests/e2e/Services/${{ matrix.service }} @@ -348,10 +354,14 @@ jobs: fail-on-cache-miss: true - name: Load and Start Appwrite + timeout-minutes: 3 run: | docker load --input /tmp/${{ env.IMAGE }}.tar docker compose up -d - sleep 30 + until docker compose exec -T appwrite doctor > /dev/null 2>&1; do + echo "Waiting for Appwrite to be ready..." + sleep 2 + done - name: Wait for Open Runtimes timeout-minutes: 3 @@ -364,8 +374,8 @@ jobs: - name: Run ${{ matrix.service }} tests with ${{ matrix.tables-mode }} table mode uses: itznotabug/php-retry@v3 with: - max_attempts: 3 - timeout_minutes: 40 + max_attempts: 2 + timeout_minutes: 20 job_id: ${{ job.check_run_id }} github_token: ${{ secrets.GITHUB_TOKEN }} test_dir: tests/e2e/Services/${{ matrix.service }} @@ -421,17 +431,21 @@ jobs: fail-on-cache-miss: true - name: Load and Start Appwrite + timeout-minutes: 3 run: | docker load --input /tmp/${{ env.IMAGE }}.tar sed -i 's/_APP_OPTIONS_ABUSE=disabled/_APP_OPTIONS_ABUSE=enabled/' .env docker compose up -d - sleep 30 + until docker compose exec -T appwrite doctor > /dev/null 2>&1; do + echo "Waiting for Appwrite to be ready..." + sleep 2 + done - name: Run Projects tests in dedicated table mode uses: itznotabug/php-retry@v3 with: - max_attempts: 3 - timeout_minutes: 30 + max_attempts: 2 + timeout_minutes: 15 job_id: ${{ job.check_run_id }} github_token: ${{ secrets.GITHUB_TOKEN }} test_dir: tests/e2e/Services/Projects @@ -481,17 +495,21 @@ jobs: fail-on-cache-miss: true - name: Load and Start Appwrite + timeout-minutes: 3 run: | docker load --input /tmp/${{ env.IMAGE }}.tar sed -i 's/_APP_OPTIONS_ABUSE=disabled/_APP_OPTIONS_ABUSE=enabled/' .env docker compose up -d - sleep 30 + until docker compose exec -T appwrite doctor > /dev/null 2>&1; do + echo "Waiting for Appwrite to be ready..." + sleep 2 + done - name: Run Projects tests in ${{ matrix.tables-mode }} table mode uses: itznotabug/php-retry@v3 with: - max_attempts: 3 - timeout_minutes: 30 + max_attempts: 2 + timeout_minutes: 15 job_id: ${{ job.check_run_id }} github_token: ${{ secrets.GITHUB_TOKEN }} test_dir: tests/e2e/Services/Projects @@ -539,17 +557,21 @@ jobs: fail-on-cache-miss: true - name: Load and Start Appwrite + timeout-minutes: 3 run: | docker load --input /tmp/${{ env.IMAGE }}.tar sed -i 's/_APP_OPTIONS_ABUSE=disabled/_APP_OPTIONS_ABUSE=enabled/' .env docker compose up -d - sleep 30 + until docker compose exec -T appwrite doctor > /dev/null 2>&1; do + echo "Waiting for Appwrite to be ready..." + sleep 2 + done - name: Run Site tests with browser connected in dedicated table mode uses: itznotabug/php-retry@v3 with: - max_attempts: 3 - timeout_minutes: 30 + max_attempts: 2 + timeout_minutes: 15 job_id: ${{ job.check_run_id }} github_token: ${{ secrets.GITHUB_TOKEN }} test_dir: tests/e2e/Services/Sites @@ -600,17 +622,21 @@ jobs: fail-on-cache-miss: true - name: Load and Start Appwrite + timeout-minutes: 3 run: | docker load --input /tmp/${{ env.IMAGE }}.tar sed -i 's/_APP_OPTIONS_ABUSE=disabled/_APP_OPTIONS_ABUSE=enabled/' .env docker compose up -d - sleep 30 + until docker compose exec -T appwrite doctor > /dev/null 2>&1; do + echo "Waiting for Appwrite to be ready..." + sleep 2 + done - name: Run Site tests with browser connected in ${{ matrix.tables-mode }} table mode uses: itznotabug/php-retry@v3 with: - max_attempts: 3 - timeout_minutes: 30 + max_attempts: 2 + timeout_minutes: 15 job_id: ${{ job.check_run_id }} github_token: ${{ secrets.GITHUB_TOKEN }} test_dir: tests/e2e/Services/Sites diff --git a/.gitignore b/.gitignore index fae08aa116..3d73586326 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ dev/yasd_init.php Makefile appwrite.config.json /.zed/ -/app/config/specs/ \ No newline at end of file +/app/config/specs/ +.phpunit.cache diff --git a/tests/e2e/General/AbuseTest.php b/tests/e2e/General/AbuseTest.php index cfd12b0e9f..e30186bcc0 100644 --- a/tests/e2e/General/AbuseTest.php +++ b/tests/e2e/General/AbuseTest.php @@ -356,7 +356,19 @@ class AbuseTest extends Scope 'required' => true, ]); - sleep(1); + $attrEndpoint = $isCollection + ? '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/title' + : '/tablesdb/' . $databaseId . '/tables/' . $collectionId . '/columns/title'; + + $this->assertEventually(function () use ($attrEndpoint) { + $attr = $this->client->call(Client::METHOD_GET, $attrEndpoint, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals(200, $attr['headers']['status-code']); + $this->assertEquals('available', $attr['body']['status']); + }, 30_000, 500); return [ 'databaseId' => $databaseId, diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index 18e4d8af79..e7b5b1d844 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -527,7 +527,18 @@ class UsageTest extends Scope $this->assertEquals('name', $response['body']['key']); - sleep(self::WAIT); + $this->assertEventually(function () use ($databaseId, $collectionId) { + $attr = $this->client->call( + Client::METHOD_GET, + '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/name', + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()) + ); + $this->assertEquals(200, $attr['headers']['status-code']); + $this->assertEquals('available', $attr['body']['status']); + }, 30_000, 500); $requestsTotal += 1; @@ -769,7 +780,18 @@ class UsageTest extends Scope $this->assertEquals('name', $response['body']['key']); - sleep(self::WAIT); + $this->assertEventually(function () use ($databaseId, $tableId) { + $attr = $this->client->call( + Client::METHOD_GET, + '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/name', + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()) + ); + $this->assertEquals(200, $attr['headers']['status-code']); + $this->assertEquals('available', $attr['body']['status']); + }, 30_000, 500); $requestsTotal += 1; @@ -842,66 +864,66 @@ class UsageTest extends Scope $tablesTotal = $data['tablesTotal']; $databasesTotal = $data['databasesTotal']; - sleep(self::WAIT); + $this->assertEventually(function () use ($requestsTotal, $databasesTotal, $absoluteRowsTotal, $absoluteTablesTotal, $tablesTotal, $rowsTotal, $databaseId, $tableId) { + $response = $this->client->call( + Client::METHOD_GET, + '/project/usage', + $this->getConsoleHeaders(), + [ + 'period' => '1d', + 'startDate' => self::getToday(), + 'endDate' => self::getTomorrow(), + ] + ); - $response = $this->client->call( - Client::METHOD_GET, - '/project/usage', - $this->getConsoleHeaders(), - [ - 'period' => '1d', - 'startDate' => self::getToday(), - 'endDate' => self::getTomorrow(), - ] - ); + $this->assertGreaterThanOrEqual(31, count($response['body'])); + $this->assertCount(1, $response['body']['requests']); + $this->assertCount(1, $response['body']['network']); + $this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']); + $this->validateDates($response['body']['requests']); + $this->assertEquals($databasesTotal, $response['body']['databasesTotal']); - $this->assertGreaterThanOrEqual(31, count($response['body'])); - $this->assertCount(1, $response['body']['requests']); - $this->assertCount(1, $response['body']['network']); - $this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']); - $this->validateDates($response['body']['requests']); - $this->assertEquals($databasesTotal, $response['body']['databasesTotal']); + // project level includes all i.e. documents + rows total. + $this->assertEquals($absoluteRowsTotal, $response['body']['rowsTotal']); - // project level includes all i.e. documents + rows total. - $this->assertEquals($absoluteRowsTotal, $response['body']['rowsTotal']); + $response = $this->client->call( + Client::METHOD_GET, + '/databases/usage?range=30d', + $this->getConsoleHeaders() + ); - $response = $this->client->call( - Client::METHOD_GET, - '/databases/usage?range=30d', - $this->getConsoleHeaders() - ); + $this->assertEquals($databasesTotal, $response['body']['databases'][array_key_last($response['body']['databases'])]['value']); + $this->validateDates($response['body']['databases']); - $this->assertEquals($databasesTotal, $response['body']['databases'][array_key_last($response['body']['databases'])]['value']); - $this->validateDates($response['body']['databases']); + // database level includes all i.e. collections + tables total. + $this->assertEquals($absoluteTablesTotal, $response['body']['tables'][array_key_last($response['body']['tables'])]['value']); // database level + $this->validateDates($response['body']['tables']); - // database level includes all i.e. collections + tables total. - $this->assertEquals($absoluteTablesTotal, $response['body']['tables'][array_key_last($response['body']['tables'])]['value']); // database level - $this->validateDates($response['body']['tables']); + // database level includes all i.e. documents + rows total. + $this->assertEquals($absoluteRowsTotal, $response['body']['rows'][array_key_last($response['body']['rows'])]['value']); + $this->validateDates($response['body']['rows']); - // database level includes all i.e. documents + rows total. - $this->assertEquals($absoluteRowsTotal, $response['body']['rows'][array_key_last($response['body']['rows'])]['value']); - $this->validateDates($response['body']['rows']); + $response = $this->client->call( + Client::METHOD_GET, + '/databases/' . $databaseId . '/usage?range=30d', + $this->getConsoleHeaders() + ); - $response = $this->client->call( - Client::METHOD_GET, - '/databases/' . $databaseId . '/usage?range=30d', - $this->getConsoleHeaders() - ); + $this->assertEquals($tablesTotal, $response['body']['tables'][array_key_last($response['body']['tables'])]['value']); + $this->validateDates($response['body']['tables']); - $this->assertEquals($tablesTotal, $response['body']['tables'][array_key_last($response['body']['tables'])]['value']); - $this->validateDates($response['body']['tables']); + $this->assertEquals($rowsTotal, $response['body']['rows'][array_key_last($response['body']['rows'])]['value']); + $this->validateDates($response['body']['rows']); - $this->assertEquals($rowsTotal, $response['body']['rows'][array_key_last($response['body']['rows'])]['value']); - $this->validateDates($response['body']['rows']); + $response = $this->client->call( + Client::METHOD_GET, + '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/usage?range=30d', + $this->getConsoleHeaders() + ); - $response = $this->client->call( - Client::METHOD_GET, - '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/usage?range=30d', - $this->getConsoleHeaders() - ); - - $this->assertEquals($rowsTotal, $response['body']['rows'][array_key_last($response['body']['rows'])]['value']); - $this->validateDates($response['body']['rows']); + $this->assertEquals($rowsTotal, $response['body']['rows'][array_key_last($response['body']['rows'])]['value']); + $this->validateDates($response['body']['rows']); + }, 30_000, 1000); return $data; } @@ -1033,11 +1055,22 @@ class UsageTest extends Scope $this->assertNotEmpty($response['body']['$id']); $this->assertEquals($functionId, $response['body']['functionId']); - sleep(self::WAIT); + $executionId = $response['body']['$id']; + + $this->assertEventually(function () use ($functionId, $executionId) { + $response = $this->client->call( + Client::METHOD_GET, + '/functions/' . $functionId . '/executions/' . $executionId, + array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), + ); + $this->assertContains($response['body']['status'], ['completed', 'failed']); + }, 30_000, 500); $response = $this->client->call( Client::METHOD_GET, - '/functions/' . $functionId . '/executions/' . $response['body']['$id'], + '/functions/' . $functionId . '/executions/' . $executionId, array_merge([ 'x-appwrite-project' => $this->getProject()['$id'] ], $this->getHeaders()), diff --git a/tests/e2e/Scopes/ProjectCustom.php b/tests/e2e/Scopes/ProjectCustom.php index 23b2eaeb1a..b75f46cfb8 100644 --- a/tests/e2e/Scopes/ProjectCustom.php +++ b/tests/e2e/Scopes/ProjectCustom.php @@ -61,7 +61,7 @@ trait ProjectCustom } if ($team['headers']['status-code'] === 401 && $i < $maxRetries - 1) { - \sleep(1); // 1s delay before retry + \usleep(500000); // 500ms delay before retry continue; } } @@ -94,7 +94,7 @@ trait ProjectCustom } if ($project['headers']['status-code'] === 401 && $i < $maxRetries - 1) { - \sleep(1); // 1s delay before retry + \usleep(500000); // 500ms delay before retry continue; } } diff --git a/tests/e2e/Scopes/SchemaPolling.php b/tests/e2e/Scopes/SchemaPolling.php index ddbc4c3d31..27e414fd24 100644 --- a/tests/e2e/Scopes/SchemaPolling.php +++ b/tests/e2e/Scopes/SchemaPolling.php @@ -20,7 +20,7 @@ trait SchemaPolling * @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 = 1200000, int $waitMs = 500): void + protected function waitForAttribute(string $databaseId, string $containerId, string $attributeKey, int $timeoutMs = 120000, int $waitMs = 500): void { $this->assertEventually(function () use ($databaseId, $containerId, $attributeKey) { $attribute = $this->client->call( @@ -53,7 +53,7 @@ trait SchemaPolling * @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 = 1200000, int $waitMs = 500): void + protected function waitForAttributes(string $databaseId, string $containerId, array $attributeKeys, int $timeoutMs = 120000, int $waitMs = 500): void { $this->assertEventually(function () use ($databaseId, $containerId, $attributeKeys) { $container = $this->client->call( @@ -97,7 +97,7 @@ trait SchemaPolling * @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 = 1200000, int $waitMs = 500): void + protected function waitForAttributeCount(string $databaseId, string $containerId, int $count, int $timeoutMs = 120000, int $waitMs = 500): void { $this->assertEventually(function () use ($databaseId, $containerId, $count) { $container = $this->client->call( @@ -139,7 +139,7 @@ trait SchemaPolling * @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 = 1200000, int $waitMs = 500): void + protected function waitForIndex(string $databaseId, string $containerId, string $indexKey, int $timeoutMs = 120000, int $waitMs = 500): void { $this->assertEventually(function () use ($databaseId, $containerId, $indexKey) { $index = $this->client->call( @@ -173,7 +173,7 @@ trait SchemaPolling * @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 = 1200000, int $waitMs = 500): void + protected function waitForAllIndexes(string $databaseId, string $containerId, int $timeoutMs = 120000, int $waitMs = 500): void { $this->assertEventually(function () use ($databaseId, $containerId) { $container = $this->client->call( @@ -204,10 +204,10 @@ trait SchemaPolling * * @param string $databaseId The database ID * @param string $containerId The collection/table ID - * @param int $timeoutMs Maximum time to wait in milliseconds (default 20 minutes for CI stability under parallel load) + * @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 = 1200000, int $waitMs = 500): void + protected function waitForAllAttributes(string $databaseId, string $containerId, int $timeoutMs = 120000, int $waitMs = 500): void { $this->assertEventually(function () use ($databaseId, $containerId) { $container = $this->client->call( diff --git a/tests/e2e/Scopes/Scope.php b/tests/e2e/Scopes/Scope.php index 4f9efa129d..0cfaacc3f0 100644 --- a/tests/e2e/Scopes/Scope.php +++ b/tests/e2e/Scopes/Scope.php @@ -145,19 +145,23 @@ abstract class Scope extends TestCase protected function getLastEmail(int $limit = 1): array { - sleep(3); + $result = []; + $this->assertEventually(function () use (&$result, $limit) { + $emails = json_decode(file_get_contents('http://maildev:1080/email'), true); - $emails = json_decode(file_get_contents('http://maildev:1080/email'), true); + $this->assertNotEmpty($emails, 'Maildev should have at least one email'); + $this->assertIsArray($emails); - if ($emails && is_array($emails)) { if ($limit === 1) { - return end($emails); + $result = end($emails); } else { - return array_slice($emails, -1 * $limit); + $result = array_slice($emails, -1 * $limit); } - } - return []; + $this->assertNotEmpty($result, 'Expected email result to be non-empty'); + }, 15_000, 500); + + return $result; } /** @@ -166,25 +170,30 @@ abstract class Scope extends TestCase */ protected function getLastEmailByAddress(string $address): array { - sleep(3); + $result = []; + $this->assertEventually(function () use (&$result, $address) { + $emails = json_decode(file_get_contents('http://maildev:1080/email'), true); - $emails = json_decode(file_get_contents('http://maildev:1080/email'), true); + $this->assertNotEmpty($emails, 'Maildev should have at least one email'); + $this->assertIsArray($emails); - if ($emails && is_array($emails)) { // Search from the end (most recent) to the beginning for ($i = count($emails) - 1; $i >= 0; $i--) { $email = $emails[$i]; if (isset($email['to']) && is_array($email['to'])) { foreach ($email['to'] as $recipient) { if (isset($recipient['address']) && $recipient['address'] === $address) { - return $email; + $result = $email; + return; } } } } - } - return []; + $this->fail("No email found for address: {$address}"); + }, 15_000, 500); + + return $result; } protected function extractQueryParamsFromEmailLink(string $html): array @@ -289,8 +298,6 @@ abstract class Scope extends TestCase $query = http_build_query($queryParams); - sleep(2); - for ($attempt = 0; $attempt < $maxAttempts; $attempt++) { $requests = json_decode(file_get_contents('http://' . $hostname . ':5000/__find_request__?' . $query), true); if (is_array($requests)) { diff --git a/tests/e2e/Services/Account/AccountConsoleClientTest.php b/tests/e2e/Services/Account/AccountConsoleClientTest.php index 8d3d8be896..78f7798193 100644 --- a/tests/e2e/Services/Account/AccountConsoleClientTest.php +++ b/tests/e2e/Services/Account/AccountConsoleClientTest.php @@ -77,16 +77,17 @@ class AccountConsoleClientTest extends Scope 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session, ])); $this->assertEquals($response['headers']['status-code'], 204); - sleep(2); - $response = $this->client->call(Client::METHOD_DELETE, '/account', array_merge([ - 'origin' => 'http://localhost', - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session, - ])); + $this->assertEventually(function () use ($session) { + $response = $this->client->call(Client::METHOD_DELETE, '/account', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session, + ])); - $this->assertEquals($response['headers']['status-code'], 204); + $this->assertEquals(204, $response['headers']['status-code']); + }, 10_000, 500); } public function testSessionAlert(): void diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index 9010db77ec..5480a3a7a0 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -1010,7 +1010,6 @@ class AccountCustomClientTest extends Scope public function testGetAccountLogs(): void { - sleep(5); // Use fresh account for predictable log count $data = $this->createFreshAccountWithSession(); $session = $data['session']; @@ -1233,7 +1232,7 @@ class AccountCustomClientTest extends Scope ]); $this->assertEquals(201, $response['headers']['status-code']); - sleep(1); + usleep(500000); } $response = $this->client->call(Client::METHOD_GET, '/account/sessions', array_merge([ @@ -1316,7 +1315,7 @@ class AccountCustomClientTest extends Scope ]); $this->assertEquals(201, $response['headers']['status-code']); - sleep(1); + usleep(500000); } $response = $this->client->call(Client::METHOD_GET, '/account/sessions', array_merge([ diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 1adb1c1e5f..ffbbd61a68 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -2791,8 +2791,8 @@ trait DatabasesBase 'required' => false, ]); + $this->waitForAttribute($databaseId, $person['body']['$id'], 'fullName'); - sleep(1); // Wait for worker $relation = $this->client->call(Client::METHOD_POST, $this->getSchemaUrl($databaseId, $person['body']['$id']) . '/relationship', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2805,7 +2805,7 @@ trait DatabasesBase 'onDelete' => Database::RELATION_MUTATE_CASCADE, ]); - sleep(1); // Wait for worker + $this->waitForAttribute($databaseId, $person['body']['$id'], 'library'); $libraryName = $this->client->call(Client::METHOD_POST, $this->getSchemaUrl($databaseId, $library['body']['$id']) . '/string', array_merge([ 'content-type' => 'application/json', @@ -2817,7 +2817,7 @@ trait DatabasesBase 'required' => true, ]); - sleep(1); // Wait for worker + $this->waitForAttribute($databaseId, $library['body']['$id'], 'libraryName'); $this->assertEquals(202, $libraryName['headers']['status-code']); @@ -5645,7 +5645,7 @@ trait DatabasesBase $createdAt = $document['body']['$createdAt']; $updatedAt = $document['body']['$updatedAt']; - \sleep(1); + \usleep(500000); $document = $this->client->call(Client::METHOD_PATCH, $this->getRecordUrl($data['databaseId'], $data['moviesId'], $documentId), $headers, [ 'data' => [ @@ -5659,7 +5659,7 @@ trait DatabasesBase $this->assertEquals($document['body']['$createdAt'], $createdAt); $this->assertNotEquals($document['body']['$updatedAt'], $updatedAt); - \sleep(1); + \usleep(500000); $document = $this->client->call(Client::METHOD_PATCH, $this->getRecordUrl($data['databaseId'], $data['moviesId'], $documentId), $headers, [ 'data' => [ @@ -9036,7 +9036,8 @@ trait DatabasesBase ]); $this->assertEquals(200, $updated['headers']['status-code']); - sleep(2); + $this->waitForAttribute($databaseId, $collectionId, 'pOptional'); + $retriedIndex = $this->client->call(Client::METHOD_POST, $this->getIndexUrl($databaseId, $collectionId), array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -10149,8 +10150,8 @@ trait DatabasesBase ]); $this->assertEquals(201, $row1['headers']['status-code']); - // Sleep to ensure different creation times - sleep(1); + // Ensure different creation times + usleep(500000); $row2 = $this->client->call(Client::METHOD_POST, $this->getRecordUrl($databaseId, $posts['body']['$id']), array_merge([ 'content-type' => 'application/json', @@ -10170,8 +10171,7 @@ trait DatabasesBase // Get the creation time of the second post to use as boundary $secondPostCreatedAt = $row2['body']['$createdAt']; - // Sleep again - sleep(1); + usleep(500000); $row3 = $this->client->call(Client::METHOD_POST, $this->getRecordUrl($databaseId, $posts['body']['$id']), array_merge([ 'content-type' => 'application/json', @@ -10290,8 +10290,8 @@ trait DatabasesBase ]); $this->assertEquals(201, $row1['headers']['status-code']); - // Sleep to ensure different creation times - sleep(1); + // Ensure different creation times + usleep(500000); $row2 = $this->client->call(Client::METHOD_POST, $this->getRecordUrl($databaseId, $events['body']['$id']), array_merge([ 'content-type' => 'application/json', @@ -10311,8 +10311,7 @@ trait DatabasesBase // Get the creation time of the second event to use as boundary $secondEventCreatedAt = $row2['body']['$createdAt']; - // Sleep again - sleep(1); + usleep(500000); $row3 = $this->client->call(Client::METHOD_POST, $this->getRecordUrl($databaseId, $events['body']['$id']), array_merge([ 'content-type' => 'application/json', @@ -10432,8 +10431,8 @@ trait DatabasesBase $this->assertEquals(201, $row1['headers']['status-code']); $firstArticleCreatedAt = $row1['body']['$createdAt']; - // Sleep to ensure different timestamps - sleep(1); + // Ensure different timestamps + usleep(500000); // Create second article $row2 = $this->client->call(Client::METHOD_POST, $this->getRecordUrl($databaseId, $articles['body']['$id']), array_merge([ @@ -10452,8 +10451,7 @@ trait DatabasesBase $this->assertEquals(201, $row2['headers']['status-code']); $secondArticleCreatedAt = $row2['body']['$createdAt']; - // Sleep again - sleep(1); + usleep(500000); // Create third article $row3 = $this->client->call(Client::METHOD_POST, $this->getRecordUrl($databaseId, $articles['body']['$id']), array_merge([ @@ -10472,8 +10470,7 @@ trait DatabasesBase $this->assertEquals(201, $row3['headers']['status-code']); $thirdArticleCreatedAt = $row3['body']['$createdAt']; - // Sleep again - sleep(1); + usleep(500000); // Create fourth article $row4 = $this->client->call(Client::METHOD_POST, $this->getRecordUrl($databaseId, $articles['body']['$id']), array_merge([ @@ -10655,7 +10652,7 @@ trait DatabasesBase $taskThreeId = $row3['body']['$id']; // Update first task - sleep(1); + usleep(500000); $this->client->call(Client::METHOD_PATCH, $this->getContainerUrl($databaseId, $tasks['body']['$id']) . '/' . $this->getRecordResource() . '/' . $taskOneId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -10666,7 +10663,7 @@ trait DatabasesBase ]); // Update second task and get its updated time - sleep(1); + usleep(500000); $updatedTaskTwo = $this->client->call(Client::METHOD_PATCH, $this->getContainerUrl($databaseId, $tasks['body']['$id']) . '/' . $this->getRecordResource() . '/' . $taskTwoId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -10678,7 +10675,7 @@ trait DatabasesBase $secondTaskUpdatedAt = $updatedTaskTwo['body']['$updatedAt']; // Update third task - sleep(1); + usleep(500000); $this->client->call(Client::METHOD_PATCH, $this->getContainerUrl($databaseId, $tasks['body']['$id']) . '/' . $this->getRecordResource() . '/' . $taskThreeId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -10828,7 +10825,7 @@ trait DatabasesBase $orderThreeId = $row3['body']['$id']; // Update first order - sleep(1); + usleep(500000); $this->client->call(Client::METHOD_PATCH, $this->getContainerUrl($databaseId, $orders['body']['$id']) . '/' . $this->getRecordResource() . '/' . $orderOneId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -10839,7 +10836,7 @@ trait DatabasesBase ]); // Update second order and get its updated time - sleep(1); + usleep(500000); $updatedOrderTwo = $this->client->call(Client::METHOD_PATCH, $this->getContainerUrl($databaseId, $orders['body']['$id']) . '/' . $this->getRecordResource() . '/' . $orderTwoId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -10851,7 +10848,7 @@ trait DatabasesBase $secondOrderUpdatedAt = $updatedOrderTwo['body']['$updatedAt']; // Update third order - sleep(1); + usleep(500000); $this->client->call(Client::METHOD_PATCH, $this->getContainerUrl($databaseId, $orders['body']['$id']) . '/' . $this->getRecordResource() . '/' . $orderThreeId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -10963,8 +10960,8 @@ trait DatabasesBase ]); $this->assertEquals(201, $row1['headers']['status-code']); - // Sleep to ensure different timestamps - sleep(1); + // Ensure different timestamps + usleep(500000); // Create second product $row2 = $this->client->call(Client::METHOD_POST, $this->getRecordUrl($databaseId, $products['body']['$id']), array_merge([ @@ -10983,8 +10980,7 @@ trait DatabasesBase ]); $this->assertEquals(201, $row2['headers']['status-code']); - // Sleep again - sleep(1); + usleep(500000); // Create third product $row3 = $this->client->call(Client::METHOD_POST, $this->getRecordUrl($databaseId, $products['body']['$id']), array_merge([ @@ -11003,8 +10999,7 @@ trait DatabasesBase ]); $this->assertEquals(201, $row3['headers']['status-code']); - // Sleep again - sleep(1); + usleep(500000); // Create fourth product $row4 = $this->client->call(Client::METHOD_POST, $this->getRecordUrl($databaseId, $products['body']['$id']), array_merge([ @@ -11024,7 +11019,7 @@ trait DatabasesBase $this->assertEquals(201, $row4['headers']['status-code']); // Now update products in sequence to get different updatedAt timestamps - sleep(1); + usleep(500000); // Update first product $update1 = $this->client->call(Client::METHOD_PATCH, $this->getContainerUrl($databaseId, $products['body']['$id']) . '/' . $this->getRecordResource() . '/' . $row1['body']['$id'], array_merge([ @@ -11038,7 +11033,7 @@ trait DatabasesBase $this->assertEquals(200, $update1['headers']['status-code']); $firstProductUpdatedAt = $update1['body']['$updatedAt']; - sleep(1); + usleep(500000); // Update second product $update2 = $this->client->call(Client::METHOD_PATCH, $this->getContainerUrl($databaseId, $products['body']['$id']) . '/' . $this->getRecordResource() . '/' . $row2['body']['$id'], array_merge([ @@ -11052,7 +11047,7 @@ trait DatabasesBase $this->assertEquals(200, $update2['headers']['status-code']); $secondProductUpdatedAt = $update2['body']['$updatedAt']; - sleep(1); + usleep(500000); // Update third product $update3 = $this->client->call(Client::METHOD_PATCH, $this->getContainerUrl($databaseId, $products['body']['$id']) . '/' . $this->getRecordResource() . '/' . $row3['body']['$id'], array_merge([ @@ -11066,7 +11061,7 @@ trait DatabasesBase $this->assertEquals(200, $update3['headers']['status-code']); $thirdProductUpdatedAt = $update3['body']['$updatedAt']; - sleep(1); + usleep(500000); // Update fourth product $update4 = $this->client->call(Client::METHOD_PATCH, $this->getContainerUrl($databaseId, $products['body']['$id']) . '/' . $this->getRecordResource() . '/' . $row4['body']['$id'], array_merge([ diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesStringTypesTest.php b/tests/e2e/Services/Databases/Legacy/DatabasesStringTypesTest.php index c8c8971626..0ee1f2d691 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesStringTypesTest.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesStringTypesTest.php @@ -83,8 +83,8 @@ class DatabasesStringTypesTest extends Scope 'key' => 'varchar_min', 'size' => 1, 'required' => false, ]); - // Small delay between batches to avoid overwhelming the worker - sleep(1); + // Wait for varchar attributes to be available before creating more + $this->waitForAllAttributes($databaseId, $collectionId); // Create text attributes $this->client->call(Client::METHOD_POST, $base . '/text', $headers, [ @@ -100,7 +100,7 @@ class DatabasesStringTypesTest extends Scope 'key' => 'text_array', 'required' => false, 'array' => true, ]); - sleep(1); + $this->waitForAllAttributes($databaseId, $collectionId); // Create mediumtext attributes $this->client->call(Client::METHOD_POST, $base . '/mediumtext', $headers, [ @@ -116,7 +116,7 @@ class DatabasesStringTypesTest extends Scope 'key' => 'mediumtext_array', 'required' => false, 'array' => true, ]); - sleep(1); + $this->waitForAllAttributes($databaseId, $collectionId); // Create longtext attributes $this->client->call(Client::METHOD_POST, $base . '/longtext', $headers, [ @@ -541,7 +541,7 @@ class DatabasesStringTypesTest extends Scope $collectionId = $data['collectionId']; // Wait for attributes to be created - sleep(2); + $this->waitForAllAttributes($databaseId, $collectionId); $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes', [ 'content-type' => 'application/json', @@ -787,15 +787,14 @@ class DatabasesStringTypesTest extends Scope $this->assertEquals(204, $deleteVarchar['headers']['status-code']); // Wait for async deletion to complete - sleep(2); - - // Verify deletion - $getDeleted = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/varchar_min', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]); - - $this->assertEquals(404, $getDeleted['headers']['status-code']); + // Poll until async deletion completes + $this->assertEventually(function () use ($databaseId, $collectionId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/varchar_min', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals(404, $response['headers']['status-code']); + }, 30000, 250); } } diff --git a/tests/e2e/Services/Databases/Permissions/LegacyPermissionsGuestTest.php b/tests/e2e/Services/Databases/Permissions/LegacyPermissionsGuestTest.php index 5543461dbd..21ca04ce42 100644 --- a/tests/e2e/Services/Databases/Permissions/LegacyPermissionsGuestTest.php +++ b/tests/e2e/Services/Databases/Permissions/LegacyPermissionsGuestTest.php @@ -6,6 +6,7 @@ use PHPUnit\Framework\Attributes\DataProvider; use Tests\E2E\Client; use Tests\E2E\Scopes\ApiLegacy; use Tests\E2E\Scopes\ProjectCustom; +use Tests\E2E\Scopes\SchemaPolling; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideClient; use Utopia\Database\Helpers\ID; @@ -18,6 +19,7 @@ class LegacyPermissionsGuestTest extends Scope use ProjectCustom; use SideClient; use ApiLegacy; + use SchemaPolling; public function createCollection(): array { @@ -84,7 +86,8 @@ class LegacyPermissionsGuestTest extends Scope ] ); - sleep(2); + $this->waitForAttribute($databaseId, $publicCollection['id'], 'title'); + $this->waitForAttribute($databaseId, $privateCollection['id'], 'title'); return [ 'databaseId' => $databaseId, @@ -330,7 +333,7 @@ class LegacyPermissionsGuestTest extends Scope ] ); - sleep(1); + $this->waitForAttribute($databaseId, $moviesId, 'title'); $document = $this->client->call( Client::METHOD_POST, diff --git a/tests/e2e/Services/Databases/Permissions/LegacyPermissionsMemberTest.php b/tests/e2e/Services/Databases/Permissions/LegacyPermissionsMemberTest.php index 7910c79470..6ef3e450eb 100644 --- a/tests/e2e/Services/Databases/Permissions/LegacyPermissionsMemberTest.php +++ b/tests/e2e/Services/Databases/Permissions/LegacyPermissionsMemberTest.php @@ -6,6 +6,7 @@ use PHPUnit\Framework\Attributes\DataProvider; use Tests\E2E\Client; use Tests\E2E\Scopes\ApiLegacy; use Tests\E2E\Scopes\ProjectCustom; +use Tests\E2E\Scopes\SchemaPolling; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideClient; use Utopia\Database\Helpers\ID; @@ -18,6 +19,7 @@ class LegacyPermissionsMemberTest extends Scope use ProjectCustom; use SideClient; use ApiLegacy; + use SchemaPolling; public array $collections = []; @@ -161,7 +163,9 @@ class LegacyPermissionsMemberTest extends Scope ); $this->assertEquals(202, $response['headers']['status-code']); - sleep(2); + $this->waitForAttribute($databaseId, $this->collections['public'], 'title'); + $this->waitForAttribute($databaseId, $this->collections['private'], 'title'); + $this->waitForAttribute($databaseId, $this->collections['doconly'], 'title'); self::$setupDatabaseCache[$cacheKey] = [ 'users' => $this->users, diff --git a/tests/e2e/Services/Databases/Permissions/LegacyPermissionsTeamTest.php b/tests/e2e/Services/Databases/Permissions/LegacyPermissionsTeamTest.php index c51bc37c76..c0d880e739 100644 --- a/tests/e2e/Services/Databases/Permissions/LegacyPermissionsTeamTest.php +++ b/tests/e2e/Services/Databases/Permissions/LegacyPermissionsTeamTest.php @@ -6,6 +6,7 @@ use PHPUnit\Framework\Attributes\DataProvider; use Tests\E2E\Client; use Tests\E2E\Scopes\ApiLegacy; use Tests\E2E\Scopes\ProjectCustom; +use Tests\E2E\Scopes\SchemaPolling; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideClient; use Utopia\Database\Helpers\ID; @@ -18,6 +19,7 @@ class LegacyPermissionsTeamTest extends Scope use ProjectCustom; use SideClient; use ApiLegacy; + use SchemaPolling; public array $collections = []; public string $databaseId = 'testpermissiondb'; @@ -110,7 +112,8 @@ class LegacyPermissionsTeamTest extends Scope ] ); - sleep(2); + $this->waitForAttribute($this->databaseId, $this->collections['collection1'], 'title'); + $this->waitForAttribute($this->databaseId, $this->collections['collection2'], 'title'); return $this->collections; } diff --git a/tests/e2e/Services/Databases/Permissions/TablesDBPermissionsGuestTest.php b/tests/e2e/Services/Databases/Permissions/TablesDBPermissionsGuestTest.php index 042087870e..f19c7fd06b 100644 --- a/tests/e2e/Services/Databases/Permissions/TablesDBPermissionsGuestTest.php +++ b/tests/e2e/Services/Databases/Permissions/TablesDBPermissionsGuestTest.php @@ -6,6 +6,7 @@ use PHPUnit\Framework\Attributes\DataProvider; use Tests\E2E\Client; use Tests\E2E\Scopes\ApiTablesDB; use Tests\E2E\Scopes\ProjectCustom; +use Tests\E2E\Scopes\SchemaPolling; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideClient; use Utopia\Database\Helpers\ID; @@ -18,6 +19,7 @@ class TablesDBPermissionsGuestTest extends Scope use ProjectCustom; use SideClient; use ApiTablesDB; + use SchemaPolling; public function createCollection(): array { @@ -90,7 +92,8 @@ class TablesDBPermissionsGuestTest extends Scope ); $this->assertEquals(202, $privateSchema['headers']['status-code']); - sleep(2); + $this->waitForAttribute($databaseId, $publicCollection['id'], 'title'); + $this->waitForAttribute($databaseId, $privateCollection['id'], 'title'); return [ 'databaseId' => $databaseId, @@ -338,7 +341,7 @@ class TablesDBPermissionsGuestTest extends Scope ); $this->assertEquals(202, $schema['headers']['status-code']); - sleep(1); + $this->waitForAttribute($databaseId, $moviesId, 'title'); $document = $this->client->call( Client::METHOD_POST, diff --git a/tests/e2e/Services/Databases/Permissions/TablesDBPermissionsMemberTest.php b/tests/e2e/Services/Databases/Permissions/TablesDBPermissionsMemberTest.php index 6f5c7e3249..3b3d9dd6ff 100644 --- a/tests/e2e/Services/Databases/Permissions/TablesDBPermissionsMemberTest.php +++ b/tests/e2e/Services/Databases/Permissions/TablesDBPermissionsMemberTest.php @@ -6,6 +6,7 @@ use PHPUnit\Framework\Attributes\DataProvider; use Tests\E2E\Client; use Tests\E2E\Scopes\ApiTablesDB; use Tests\E2E\Scopes\ProjectCustom; +use Tests\E2E\Scopes\SchemaPolling; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideClient; use Utopia\Database\Helpers\ID; @@ -18,6 +19,7 @@ class TablesDBPermissionsMemberTest extends Scope use ProjectCustom; use SideClient; use ApiTablesDB; + use SchemaPolling; public array $collections = []; @@ -161,7 +163,9 @@ class TablesDBPermissionsMemberTest extends Scope ); $this->assertEquals(202, $response['headers']['status-code']); - sleep(2); + $this->waitForAttribute($databaseId, $this->collections['public'], 'title'); + $this->waitForAttribute($databaseId, $this->collections['private'], 'title'); + $this->waitForAttribute($databaseId, $this->collections['doconly'], 'title'); self::$setupDatabaseCache[$cacheKey] = [ 'users' => $this->users, diff --git a/tests/e2e/Services/Databases/Permissions/TablesDBPermissionsTeamTest.php b/tests/e2e/Services/Databases/Permissions/TablesDBPermissionsTeamTest.php index 7b8dfd4f4f..e1632c197b 100644 --- a/tests/e2e/Services/Databases/Permissions/TablesDBPermissionsTeamTest.php +++ b/tests/e2e/Services/Databases/Permissions/TablesDBPermissionsTeamTest.php @@ -6,6 +6,7 @@ use PHPUnit\Framework\Attributes\DataProvider; use Tests\E2E\Client; use Tests\E2E\Scopes\ApiTablesDB; use Tests\E2E\Scopes\ProjectCustom; +use Tests\E2E\Scopes\SchemaPolling; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideClient; use Utopia\Database\Helpers\ID; @@ -18,6 +19,7 @@ class TablesDBPermissionsTeamTest extends Scope use ProjectCustom; use SideClient; use ApiTablesDB; + use SchemaPolling; public array $collections = []; public string $databaseId = 'testpermissiondb'; @@ -114,7 +116,8 @@ class TablesDBPermissionsTeamTest extends Scope ); $this->assertEquals(202, $schema2['headers']['status-code']); - sleep(2); + $this->waitForAttribute($this->databaseId, $this->collections['collection1'], 'title'); + $this->waitForAttribute($this->databaseId, $this->collections['collection2'], 'title'); return $this->collections; } diff --git a/tests/e2e/Services/Databases/TablesDB/DatabasesStringTypesTest.php b/tests/e2e/Services/Databases/TablesDB/DatabasesStringTypesTest.php index fd538eb4d5..8635df0c07 100644 --- a/tests/e2e/Services/Databases/TablesDB/DatabasesStringTypesTest.php +++ b/tests/e2e/Services/Databases/TablesDB/DatabasesStringTypesTest.php @@ -81,7 +81,7 @@ class DatabasesStringTypesTest extends Scope 'key' => 'varchar_min', 'size' => 1, 'required' => false, ]); - sleep(1); + $this->waitForAllAttributes($databaseId, $tableId); // Create text columns $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/text', $headers, [ @@ -97,7 +97,7 @@ class DatabasesStringTypesTest extends Scope 'key' => 'text_array', 'required' => false, 'array' => true, ]); - sleep(1); + $this->waitForAllAttributes($databaseId, $tableId); // Create mediumtext columns $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/mediumtext', $headers, [ @@ -113,7 +113,7 @@ class DatabasesStringTypesTest extends Scope 'key' => 'mediumtext_array', 'required' => false, 'array' => true, ]); - sleep(1); + $this->waitForAllAttributes($databaseId, $tableId); // Create longtext columns $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/longtext', $headers, [ @@ -793,16 +793,14 @@ class DatabasesStringTypesTest extends Scope $this->assertEquals(204, $deleteVarchar['headers']['status-code']); - // Wait for async deletion to complete - sleep(2); - - // Verify deletion - $getDeleted = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/varchar_min', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]); - - $this->assertEquals(404, $getDeleted['headers']['status-code']); + // Poll until async deletion completes + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/varchar_min', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals(404, $response['headers']['status-code']); + }, 30000, 250); } } diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index 7cf9894509..77c9367c44 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -43,7 +43,7 @@ trait FunctionsBase } if ($attempt < $maxRetries) { - \sleep(\min($attempt * 2, 10)); + \sleep(\min($attempt, 3)); } } @@ -89,7 +89,7 @@ trait FunctionsBase } $this->assertEquals('ready', $status, 'Deployment status is not ready, deployment: ' . json_encode($deployment['body'], JSON_PRETTY_PRINT)); - }, 360000, 500); + }, 120000, 500); // Not === so multipart/form-data works fine too if (($params['activate'] ?? false) == true) { @@ -101,7 +101,7 @@ trait FunctionsBase ])); $this->assertNotEquals(401, $function['headers']['status-code'], 'Auth failed while polling function activation'); $this->assertEquals($deploymentId, $function['body']['deploymentId'] ?? '', 'Deployment is not activated, deployment: ' . json_encode($function['body'], JSON_PRETTY_PRINT)); - }, 360000, 500); + }, 120000, 500); } return $deploymentId; @@ -122,7 +122,7 @@ trait FunctionsBase } if ($i < $maxRetries - 1) { - \sleep(1); + \usleep(500000); } } diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 98013e6879..1d61cb0ebb 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -572,7 +572,15 @@ class FunctionsCustomClientTest extends Scope ]); $this->assertEquals(202, $attribute['headers']['status-code']); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId) { + $attr = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/name', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals(200, $attr['headers']['status-code']); + $this->assertEquals('available', $attr['body']['status']); + }, 30_000, 500); $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 4f63bed1e9..53844fe2c8 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -548,7 +548,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(200, $deployment['headers']['status-code']); $this->assertEquals('ready', $deployment['body']['status']); $this->assertEquals('cli', $deployment['body']['type']); - }, 500000, 1000); + }, 120000, 500); } public function testCreateFunctionAndDeploymentFromTemplate() @@ -1021,7 +1021,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals('ready', $deployment['body']['status']); $this->assertEquals($deploymentSize, $deployment['body']['sourceSize']); $this->assertGreaterThan(1024 * 1024 * 10, $deployment['body']['buildSize']); // ~7MB video file + 10MB sample file - }, 500000, 1000); + }, 120000, 500); } public function testUpdateDeployment(): void @@ -2133,7 +2133,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(24, count($response['body'])); $this->assertEquals('24h', $response['body']['range']); $this->assertEquals(1, $response['body']['executionsTotal']); - }, 25000, 1000); + }, 25000, 500); $this->cleanupFunction($functionId); } diff --git a/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php b/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php index 38ef30c971..700af00775 100644 --- a/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php +++ b/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php @@ -64,7 +64,7 @@ class FunctionsScheduleTest extends Scope $this->assertNotEmpty($asyncExecution['$id']); $headers = array_column($asyncExecution['requestHeaders'] ?? [], 'value', 'name'); $this->assertEmpty($headers['x-appwrite-client-ip'] ?? ''); - }, 180000, 1000); // 3 minute timeout with 1s polling for CI stability + }, 180000, 500); // 3 minute timeout with 500ms polling for CI stability $this->cleanupFunction($functionId); } @@ -90,7 +90,7 @@ class FunctionsScheduleTest extends Scope // Schedule execution for the future \date_default_timezone_set('UTC'); - $futureTime = (new \DateTime())->add(new \DateInterval('PT2M')); // 2 minute in the future + $futureTime = (new \DateTime())->add(new \DateInterval('PT1M')); // 1 minute in the future $futureTime->setTime($futureTime->format('H'), $futureTime->format('i'), 0, 0); @@ -124,8 +124,6 @@ class FunctionsScheduleTest extends Scope $this->assertEquals('x-appwrite-client-ip', $execution['body']['requestHeaders'][0]['name']); $this->assertNotEmpty($execution['body']['requestHeaders'][0]['value']); - \sleep(120); - $this->assertEventually(function () use ($functionId, $executionId) { $execution = $this->getExecution($functionId, $executionId); @@ -141,7 +139,7 @@ class FunctionsScheduleTest extends Scope $this->assertStringContainsString('user-is-' . $this->getUser()['$id'], $execution['body']['logs']); $this->assertStringContainsString('jwt-is-valid', $execution['body']['logs']); $this->assertGreaterThan(0, $execution['body']['duration']); - }, 10000, 500); + }, 120000, 500); /* Test for FAILURE */ // Schedule synchronous execution diff --git a/tests/e2e/Services/GraphQL/FunctionsServerTest.php b/tests/e2e/Services/GraphQL/FunctionsServerTest.php index ef4eef1c36..a66789d646 100644 --- a/tests/e2e/Services/GraphQL/FunctionsServerTest.php +++ b/tests/e2e/Services/GraphQL/FunctionsServerTest.php @@ -147,7 +147,7 @@ class FunctionsServerTest extends Scope } $this->assertEquals('ready', $deployment['status']); - }, 240000); + }, 120000); static::$cachedDeployment[$key] = $deployment; return $deployment; diff --git a/tests/e2e/Services/GraphQL/Legacy/AbuseTest.php b/tests/e2e/Services/GraphQL/Legacy/AbuseTest.php index a5dc2ec685..dfec046f55 100644 --- a/tests/e2e/Services/GraphQL/Legacy/AbuseTest.php +++ b/tests/e2e/Services/GraphQL/Legacy/AbuseTest.php @@ -175,7 +175,14 @@ class AbuseTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'], ], $gqlPayload); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/name', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); return [ 'databaseId' => $databaseId, diff --git a/tests/e2e/Services/GraphQL/Legacy/AuthTest.php b/tests/e2e/Services/GraphQL/Legacy/AuthTest.php index 7c1086b075..4a3e49cc60 100644 --- a/tests/e2e/Services/GraphQL/Legacy/AuthTest.php +++ b/tests/e2e/Services/GraphQL/Legacy/AuthTest.php @@ -140,7 +140,16 @@ class AuthTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'], ], $gqlPayload); - sleep(1); + $databaseId = $this->database['body']['data']['databasesCreate']['_id']; + $collectionId = $this->collection['body']['data']['databasesCreateCollection']['_id']; + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/name', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); } public function testInvalidAuth() diff --git a/tests/e2e/Services/GraphQL/Legacy/DatabaseClientTest.php b/tests/e2e/Services/GraphQL/Legacy/DatabaseClientTest.php index 2e79c5b5c0..4d34dc6b23 100644 --- a/tests/e2e/Services/GraphQL/Legacy/DatabaseClientTest.php +++ b/tests/e2e/Services/GraphQL/Legacy/DatabaseClientTest.php @@ -211,7 +211,22 @@ class DatabaseClientTest extends Scope } $data = $this->setupAttributes(); - sleep(3); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/name', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/age', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $projectId = $this->getProject()['$id']; $query = $this->getQuery(self::CREATE_DOCUMENT); @@ -311,7 +326,14 @@ class DatabaseClientTest extends Scope ]; $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); $this->assertArrayNotHasKey('errors', $res['body']); - sleep(1); + $this->assertEventually(function () use ($databaseId, $collectionId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/name', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Step 4: Create documents $query = $this->getQuery(self::CREATE_DOCUMENTS); @@ -606,7 +628,22 @@ class DatabaseClientTest extends Scope { // Create a fresh document for deletion to avoid conflicts with other tests $data = $this->setupAttributes(); - sleep(1); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/name', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/age', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $projectId = $this->getProject()['$id']; diff --git a/tests/e2e/Services/GraphQL/Legacy/DatabaseServerTest.php b/tests/e2e/Services/GraphQL/Legacy/DatabaseServerTest.php index bdf600839f..ebaed96de8 100644 --- a/tests/e2e/Services/GraphQL/Legacy/DatabaseServerTest.php +++ b/tests/e2e/Services/GraphQL/Legacy/DatabaseServerTest.php @@ -192,7 +192,14 @@ class DatabaseServerTest extends Scope ] ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $gqlPayload); - sleep(1); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/name', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Update string attribute $query = $this->getQuery(self::UPDATE_STRING_ATTRIBUTE); @@ -222,7 +229,14 @@ class DatabaseServerTest extends Scope ] ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $gqlPayload); - sleep(1); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/age', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Update integer attribute $query = $this->getQuery(self::UPDATE_INTEGER_ATTRIBUTE); @@ -252,7 +266,14 @@ class DatabaseServerTest extends Scope ] ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $gqlPayload); - sleep(1); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/alive', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Update boolean attribute $query = $this->getQuery(self::UPDATE_BOOLEAN_ATTRIBUTE); @@ -283,7 +304,14 @@ class DatabaseServerTest extends Scope ] ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $gqlPayload); - sleep(1); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/salary', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Update float attribute $query = $this->getQuery(self::UPDATE_FLOAT_ATTRIBUTE); @@ -313,7 +341,14 @@ class DatabaseServerTest extends Scope ] ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $gqlPayload); - sleep(1); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Update email attribute $query = $this->getQuery(self::UPDATE_EMAIL_ATTRIBUTE); @@ -346,7 +381,14 @@ class DatabaseServerTest extends Scope ] ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $gqlPayload); - sleep(1); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/role', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Update enum attribute $query = $this->getQuery(self::UPDATE_ENUM_ATTRIBUTE); @@ -379,7 +421,14 @@ class DatabaseServerTest extends Scope ] ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $gqlPayload); - sleep(1); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/dob', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Update datetime attribute $query = $this->getQuery(self::UPDATE_DATETIME_ATTRIBUTE); @@ -408,7 +457,14 @@ class DatabaseServerTest extends Scope ] ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $gqlPayload); - sleep(3); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/ip', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Update IP attribute $query = $this->getQuery(self::UPDATE_IP_ATTRIBUTE); @@ -437,7 +493,14 @@ class DatabaseServerTest extends Scope ] ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $gqlPayload); - sleep(3); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/url', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Update URL attribute $query = $this->getQuery(self::UPDATE_URL_ATTRIBUTE); @@ -453,8 +516,15 @@ class DatabaseServerTest extends Scope ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $gqlPayload); - // Wait for all attributes to become available before returning - sleep(5); + // Poll for the last attribute to confirm all are available + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/url', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); self::$allAttributesCache[$cacheKey] = $data; return self::$allAttributesCache[$cacheKey]; @@ -684,7 +754,14 @@ class DatabaseServerTest extends Scope ]; $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); $this->assertArrayNotHasKey('errors', $res['body']); - sleep(1); + $this->assertEventually(function () use ($databaseId, $collectionId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/name', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Step 4: Create documents $query = $this->getQuery(self::CREATE_DOCUMENTS); @@ -783,8 +860,14 @@ class DatabaseServerTest extends Scope ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $gqlPayload); - // Wait for attributes to be available - sleep(1); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/name', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $query = $this->getQuery(self::UPDATE_STRING_ATTRIBUTE); $gqlPayload = [ @@ -866,8 +949,14 @@ class DatabaseServerTest extends Scope ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $gqlPayload); - // Wait for attributes to be available - sleep(1); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/age', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $query = $this->getQuery(self::UPDATE_INTEGER_ATTRIBUTE); $gqlPayload = [ @@ -949,8 +1038,14 @@ class DatabaseServerTest extends Scope ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $gqlPayload); - // Wait for attributes to be available - sleep(1); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/alive', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $query = $this->getQuery(self::UPDATE_BOOLEAN_ATTRIBUTE); $gqlPayload = [ @@ -1034,8 +1129,14 @@ class DatabaseServerTest extends Scope ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $gqlPayload); - // Wait for attributes to be available - sleep(1); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/salary', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $query = $this->getQuery(self::UPDATE_FLOAT_ATTRIBUTE); $gqlPayload = [ @@ -1117,8 +1218,14 @@ class DatabaseServerTest extends Scope ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $gqlPayload); - // Wait for attributes to be available - sleep(1); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $query = $this->getQuery(self::UPDATE_EMAIL_ATTRIBUTE); $gqlPayload = [ @@ -1206,8 +1313,14 @@ class DatabaseServerTest extends Scope ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $gqlPayload); - // Wait for attributes to be available - sleep(1); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/role', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $query = $this->getQuery(self::UPDATE_ENUM_ATTRIBUTE); $gqlPayload = [ @@ -1292,8 +1405,14 @@ class DatabaseServerTest extends Scope ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $gqlPayload); - // Wait for attributes to be available - sleep(1); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/dob', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $query = $this->getQuery(self::UPDATE_DATETIME_ATTRIBUTE); $gqlPayload = [ @@ -1355,7 +1474,14 @@ class DatabaseServerTest extends Scope { $data = $this->setupRelationship(); - sleep(1); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection2']['_id'] . '/attributes/actors', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $projectId = $this->getProject()['$id']; $query = $this->getQuery(self::UPDATE_RELATIONSHIP_ATTRIBUTE); @@ -1436,8 +1562,14 @@ class DatabaseServerTest extends Scope ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $gqlPayload); - // Wait for attributes to be available - sleep(3); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/ip', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $query = $this->getQuery(self::UPDATE_IP_ATTRIBUTE); $gqlPayload = [ @@ -1517,8 +1649,14 @@ class DatabaseServerTest extends Scope ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $gqlPayload); - // Wait for attributes to be available - sleep(3); + $this->assertEventually(function () use ($data) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $data['database']['_id'] . '/collections/' . $data['collection']['_id'] . '/attributes/url', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $query = $this->getQuery(self::UPDATE_URL_ATTRIBUTE); $gqlPayload = [ diff --git a/tests/e2e/Services/GraphQL/MessagingTest.php b/tests/e2e/Services/GraphQL/MessagingTest.php index b415fa7513..322c51c1f7 100644 --- a/tests/e2e/Services/GraphQL/MessagingTest.php +++ b/tests/e2e/Services/GraphQL/MessagingTest.php @@ -523,13 +523,21 @@ class MessagingTest extends Scope $this->assertEquals(200, $email['headers']['status-code']); - \sleep(5); + $emailMessageId = $email['body']['data']['messagingCreateEmail']['_id']; + $this->assertEventually(function () use ($emailMessageId) { + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $emailMessageId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertContains($response['body']['status'], ['sent', 'failed']); + }, 30000, 500); $query = $this->getQuery(self::GET_MESSAGE); $graphQLPayload = [ 'query' => $query, 'variables' => [ - 'messageId' => $email['body']['data']['messagingCreateEmail']['_id'], + 'messageId' => $emailMessageId, ], ]; $message = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ @@ -674,13 +682,21 @@ class MessagingTest extends Scope $this->assertEquals(200, $sms['headers']['status-code']); - \sleep(5); + $smsMessageId = $sms['body']['data']['messagingCreateSMS']['_id']; + $this->assertEventually(function () use ($smsMessageId) { + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $smsMessageId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertContains($response['body']['status'], ['sent', 'failed']); + }, 30000, 500); $query = $this->getQuery(self::GET_MESSAGE); $graphQLPayload = [ 'query' => $query, 'variables' => [ - 'messageId' => $sms['body']['data']['messagingCreateSMS']['_id'], + 'messageId' => $smsMessageId, ], ]; $message = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ @@ -827,13 +843,21 @@ class MessagingTest extends Scope $this->assertEquals(200, $push['headers']['status-code']); - \sleep(5); + $pushMessageId = $push['body']['data']['messagingCreatePushNotification']['_id']; + $this->assertEventually(function () use ($pushMessageId) { + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $pushMessageId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertContains($response['body']['status'], ['sent', 'failed']); + }, 30000, 500); $query = $this->getQuery(self::GET_MESSAGE); $graphQLPayload = [ 'query' => $query, 'variables' => [ - 'messageId' => $push['body']['data']['messagingCreatePushNotification']['_id'], + 'messageId' => $pushMessageId, ], ]; $message = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ @@ -1130,13 +1154,21 @@ class MessagingTest extends Scope $this->assertEquals(200, $email['headers']['status-code']); - \sleep(5); + $updatedEmailId = $email['body']['data']['messagingUpdateEmail']['_id']; + $this->assertEventually(function () use ($updatedEmailId) { + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $updatedEmailId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertContains($response['body']['status'], ['sent', 'failed']); + }, 30000, 500); $query = $this->getQuery(self::GET_MESSAGE); $graphQLPayload = [ 'query' => $query, 'variables' => [ - 'messageId' => $email['body']['data']['messagingUpdateEmail']['_id'], + 'messageId' => $updatedEmailId, ], ]; $message = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ @@ -1194,13 +1226,21 @@ class MessagingTest extends Scope $this->assertEquals(200, $sms['headers']['status-code']); - \sleep(5); + $updatedSmsId = $sms['body']['data']['messagingUpdateSMS']['_id']; + $this->assertEventually(function () use ($updatedSmsId) { + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $updatedSmsId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertContains($response['body']['status'], ['sent', 'failed']); + }, 30000, 500); $query = $this->getQuery(self::GET_MESSAGE); $graphQLPayload = [ 'query' => $query, 'variables' => [ - 'messageId' => $sms['body']['data']['messagingUpdateSMS']['_id'], + 'messageId' => $updatedSmsId, ], ]; $message = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ @@ -1259,13 +1299,21 @@ class MessagingTest extends Scope $this->assertEquals(200, $push['headers']['status-code']); - \sleep(5); + $updatedPushId = $push['body']['data']['messagingUpdatePushNotification']['_id']; + $this->assertEventually(function () use ($updatedPushId) { + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $updatedPushId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertContains($response['body']['status'], ['sent', 'failed']); + }, 30000, 500); $query = $this->getQuery(self::GET_MESSAGE); $graphQLPayload = [ 'query' => $query, 'variables' => [ - 'messageId' => $push['body']['data']['messagingUpdatePushNotification']['_id'], + 'messageId' => $updatedPushId, ], ]; $message = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ diff --git a/tests/e2e/Services/GraphQL/TablesDB/AbuseTest.php b/tests/e2e/Services/GraphQL/TablesDB/AbuseTest.php index 9fab95a9ea..05185149fd 100644 --- a/tests/e2e/Services/GraphQL/TablesDB/AbuseTest.php +++ b/tests/e2e/Services/GraphQL/TablesDB/AbuseTest.php @@ -175,7 +175,14 @@ class AbuseTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'], ], $gqlPayload); - sleep(2); + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/name', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); return [ 'databaseId' => $databaseId, diff --git a/tests/e2e/Services/GraphQL/TablesDB/AuthTest.php b/tests/e2e/Services/GraphQL/TablesDB/AuthTest.php index ded79a4a71..9c6910fb30 100644 --- a/tests/e2e/Services/GraphQL/TablesDB/AuthTest.php +++ b/tests/e2e/Services/GraphQL/TablesDB/AuthTest.php @@ -140,7 +140,17 @@ class AuthTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'], ], $gqlPayload); - sleep(1); + $databaseId = $this->database['body']['data']['databasesCreate']['_id']; + $tableId = $this->table['body']['data']['tablesDBCreateTable']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/name', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); } public function testInvalidAuth() diff --git a/tests/e2e/Services/GraphQL/TablesDB/DatabaseClientTest.php b/tests/e2e/Services/GraphQL/TablesDB/DatabaseClientTest.php index 921c44cb71..9a39db88f3 100644 --- a/tests/e2e/Services/GraphQL/TablesDB/DatabaseClientTest.php +++ b/tests/e2e/Services/GraphQL/TablesDB/DatabaseClientTest.php @@ -205,7 +205,20 @@ class DatabaseClientTest extends Scope } $data = $this->setupColumns(); - sleep(3); + + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + foreach (['name', 'age'] as $columnKey) { + $this->assertEventually(function () use ($databaseId, $tableId, $columnKey) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/' . $columnKey, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); + } $projectId = $this->getProject()['$id']; $query = $this->getQuery(self::CREATE_ROW); @@ -309,7 +322,15 @@ class DatabaseClientTest extends Scope $res = $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); $this->assertArrayNotHasKey('errors', $res['body']); - sleep(1); + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/name', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Step 4: Create rows $query = $this->getQuery(self::CREATE_ROWS); @@ -590,7 +611,20 @@ class DatabaseClientTest extends Scope { // Need to create a fresh row for deletion since we can't delete the cached row $data = $this->setupColumns(); - sleep(1); + + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + foreach (['name', 'age'] as $columnKey) { + $this->assertEventually(function () use ($databaseId, $tableId, $columnKey) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/' . $columnKey, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); + } $projectId = $this->getProject()['$id']; diff --git a/tests/e2e/Services/GraphQL/TablesDB/DatabaseServerTest.php b/tests/e2e/Services/GraphQL/TablesDB/DatabaseServerTest.php index 235307e5e2..634c9d8c03 100644 --- a/tests/e2e/Services/GraphQL/TablesDB/DatabaseServerTest.php +++ b/tests/e2e/Services/GraphQL/TablesDB/DatabaseServerTest.php @@ -170,15 +170,24 @@ class DatabaseServerTest extends Scope // Check if already updated by looking for default value $projectId = $this->getProject()['$id']; - // Wait for columns to be available - sleep(1); + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/name', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $query = $this->getQuery(self::UPDATE_STRING_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'name', 'required' => false, 'default' => 'Default Value', @@ -229,15 +238,24 @@ class DatabaseServerTest extends Scope $data = $this->setupIntegerColumn(); $projectId = $this->getProject()['$id']; - // Wait for columns to be available - sleep(1); + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/age', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $query = $this->getQuery(self::UPDATE_INTEGER_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'age', 'required' => false, 'min' => 12, @@ -288,15 +306,24 @@ class DatabaseServerTest extends Scope $data = $this->setupBooleanColumn(); $projectId = $this->getProject()['$id']; - // Wait for columns to be available - sleep(1); + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/alive', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $query = $this->getQuery(self::UPDATE_BOOLEAN_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'alive', 'required' => false, 'default' => true @@ -348,15 +375,24 @@ class DatabaseServerTest extends Scope $data = $this->setupFloatColumn(); $projectId = $this->getProject()['$id']; - // Wait for columns to be available - sleep(1); + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/salary', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $query = $this->getQuery(self::UPDATE_FLOAT_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'salary', 'required' => false, 'min' => 100.0, @@ -407,15 +443,24 @@ class DatabaseServerTest extends Scope $data = $this->setupEmailColumn(); $projectId = $this->getProject()['$id']; - // Wait for columns to be available - sleep(1); + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/email', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $query = $this->getQuery(self::UPDATE_EMAIL_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'email', 'required' => false, 'default' => 'torsten@appwrite.io', @@ -469,15 +514,24 @@ class DatabaseServerTest extends Scope $data = $this->setupEnumColumn(); $projectId = $this->getProject()['$id']; - // Wait for columns to be available - sleep(1); + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/role', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $query = $this->getQuery(self::UPDATE_ENUM_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'role', 'required' => false, 'elements' => [ @@ -531,15 +585,24 @@ class DatabaseServerTest extends Scope $data = $this->setupDatetimeColumn(); $projectId = $this->getProject()['$id']; - // Wait for columns to be available - sleep(1); + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/dob', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $query = $this->getQuery(self::UPDATE_DATETIME_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'dob', 'required' => false, 'default' => '2000-01-01T00:00:00Z' @@ -591,14 +654,24 @@ class DatabaseServerTest extends Scope $data = $this->setupRelationshipColumn(); $projectId = $this->getProject()['$id']; - sleep(1); + $databaseId = $data['database']['_id']; + $tableId = $data['table2']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/actors', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $query = $this->getQuery(self::UPDATE_RELATIONSHIP_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table2']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'actors', 'onDelete' => Database::RELATION_MUTATE_CASCADE, ] @@ -647,15 +720,24 @@ class DatabaseServerTest extends Scope $data = $this->setupIPColumn(); $projectId = $this->getProject()['$id']; - // Wait for columns to be available - sleep(3); + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/ip', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $query = $this->getQuery(self::UPDATE_IP_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'ip', 'required' => false, 'default' => '127.0.0.1' @@ -705,15 +787,24 @@ class DatabaseServerTest extends Scope $data = $this->setupURLColumn(); $projectId = $this->getProject()['$id']; - // Wait for columns to be available - sleep(3); + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/url', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $query = $this->getQuery(self::UPDATE_URL_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'url', 'required' => false, 'default' => 'https://cloud.appwrite.io' @@ -800,16 +891,29 @@ class DatabaseServerTest extends Scope $this->setupUpdatedDatetimeColumn(); $data = $this->setupUpdatedEnumColumn(); - // Wait for all columns to be available - sleep(3); - $projectId = $this->getProject()['$id']; + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + // Wait for all columns to be available + $columns = ['name', 'age', 'alive', 'salary', 'email', 'dob', 'role']; + foreach ($columns as $columnKey) { + $this->assertEventually(function () use ($databaseId, $tableId, $columnKey) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/' . $columnKey, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); + } + $query = $this->getQuery(self::CREATE_ROW); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'rowId' => ID::unique(), 'data' => [ 'name' => 'John Doe', @@ -904,7 +1008,15 @@ class DatabaseServerTest extends Scope ]; $this->client->call(Client::METHOD_POST, '/graphql', $headers, $payload); - sleep(1); + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/name', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Step 4: Create rows $query = $this->getQuery(self::CREATE_ROWS); @@ -986,16 +1098,25 @@ class DatabaseServerTest extends Scope { $data = $this->setupStringColumn(); - // Wait for columns to be available - sleep(1); - $projectId = $this->getProject()['$id']; + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/name', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); + $query = $this->getQuery(self::UPDATE_STRING_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'name', 'required' => false, 'default' => 'Default Value', @@ -1055,16 +1176,25 @@ class DatabaseServerTest extends Scope { $data = $this->setupIntegerColumn(); - // Wait for columns to be available - sleep(1); - $projectId = $this->getProject()['$id']; + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/age', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); + $query = $this->getQuery(self::UPDATE_INTEGER_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'age', 'required' => false, 'min' => 12, @@ -1126,16 +1256,25 @@ class DatabaseServerTest extends Scope { $data = $this->setupBooleanColumn(); - // Wait for columns to be available - sleep(1); - $projectId = $this->getProject()['$id']; + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/alive', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); + $query = $this->getQuery(self::UPDATE_BOOLEAN_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'alive', 'required' => false, 'default' => true @@ -1196,16 +1335,25 @@ class DatabaseServerTest extends Scope { $data = $this->setupFloatColumn(); - // Wait for columns to be available - sleep(1); - $projectId = $this->getProject()['$id']; + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/salary', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); + $query = $this->getQuery(self::UPDATE_FLOAT_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'salary', 'required' => false, 'min' => 100.0, @@ -1267,16 +1415,25 @@ class DatabaseServerTest extends Scope { $data = $this->setupEmailColumn(); - // Wait for columns to be available - sleep(1); - $projectId = $this->getProject()['$id']; + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/email', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); + $query = $this->getQuery(self::UPDATE_EMAIL_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'email', 'required' => false, 'default' => 'torsten@appwrite.io', @@ -1340,16 +1497,25 @@ class DatabaseServerTest extends Scope { $data = $this->setupEnumColumn(); - // Wait for columns to be available - sleep(1); - $projectId = $this->getProject()['$id']; + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/role', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); + $query = $this->getQuery(self::UPDATE_ENUM_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'role', 'required' => false, 'elements' => [ @@ -1414,16 +1580,25 @@ class DatabaseServerTest extends Scope { $data = $this->setupDatetimeColumn(); - // Wait for columns to be available - sleep(1); - $projectId = $this->getProject()['$id']; + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/dob', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); + $query = $this->getQuery(self::UPDATE_DATETIME_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'dob', 'required' => false, 'default' => '2000-01-01T00:00:00Z' @@ -1478,15 +1653,25 @@ class DatabaseServerTest extends Scope { $data = $this->setupRelationshipColumn(); - sleep(1); + $databaseId = $data['database']['_id']; + $tableId = $data['table2']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/actors', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $projectId = $this->getProject()['$id']; $query = $this->getQuery(self::UPDATE_RELATIONSHIP_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table2']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'actors', 'onDelete' => Database::RELATION_MUTATE_CASCADE, ] @@ -1542,16 +1727,25 @@ class DatabaseServerTest extends Scope { $data = $this->setupIPColumn(); - // Wait for columns to be available - sleep(3); + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/ip', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $projectId = $this->getProject()['$id']; $query = $this->getQuery(self::UPDATE_IP_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'ip', 'required' => false, 'default' => '127.0.0.1' @@ -1610,16 +1804,25 @@ class DatabaseServerTest extends Scope { $data = $this->setupURLColumn(); - // Wait for columns to be available - sleep(3); + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + $this->assertEventually(function () use ($databaseId, $tableId) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/url', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $projectId = $this->getProject()['$id']; $query = $this->getQuery(self::UPDATE_URL_COLUMN); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'key' => 'url', 'required' => false, 'default' => 'https://cloud.appwrite.io' @@ -1695,16 +1898,29 @@ class DatabaseServerTest extends Scope $this->setupUpdatedDatetimeColumn(); $data = $this->setupUpdatedEnumColumn(); - // Wait for all columns to be available - sleep(3); - $projectId = $this->getProject()['$id']; + $databaseId = $data['database']['_id']; + $tableId = $data['table']['_id']; + + // Wait for all columns to be available + $columns = ['name', 'age', 'alive', 'salary', 'email', 'dob', 'role']; + foreach ($columns as $columnKey) { + $this->assertEventually(function () use ($databaseId, $tableId, $columnKey) { + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/' . $columnKey, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); + } + $query = $this->getQuery(self::CREATE_ROW); $gqlPayload = [ 'query' => $query, 'variables' => [ - 'databaseId' => $data['database']['_id'], - 'tableId' => $data['table']['_id'], + 'databaseId' => $databaseId, + 'tableId' => $tableId, 'rowId' => ID::unique(), 'data' => [ 'name' => 'John Doe', diff --git a/tests/e2e/Services/Messaging/MessagingBase.php b/tests/e2e/Services/Messaging/MessagingBase.php index deed30ba6f..160ea61568 100644 --- a/tests/e2e/Services/Messaging/MessagingBase.php +++ b/tests/e2e/Services/Messaging/MessagingBase.php @@ -517,7 +517,15 @@ trait MessagingBase 'content' => 'Check out the new blog post at http://localhost', ]); - \sleep(2); + $messageId = $email['body']['$id']; + $this->assertEventually(function () use ($messageId) { + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $messageId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertContains($response['body']['status'], ['sent', 'failed']); + }, 30000, 500); self::$sentEmailData[$cacheKey] = [ 'message' => $email['body'], @@ -619,7 +627,15 @@ trait MessagingBase 'content' => '064763', ]); - \sleep(5); + $smsMessageId = $sms['body']['$id']; + $this->assertEventually(function () use ($smsMessageId) { + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $smsMessageId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertContains($response['body']['status'], ['sent', 'failed']); + }, 30000, 500); self::$sentSmsData[$cacheKey] = $sms['body']; @@ -715,7 +731,15 @@ trait MessagingBase 'body' => 'Test-Notification-Body-Sent', ]); - \sleep(5); + $pushMessageId = $push['body']['$id']; + $this->assertEventually(function () use ($pushMessageId) { + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $pushMessageId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertContains($response['body']['status'], ['sent', 'failed']); + }, 30000, 500); self::$sentPushData[$cacheKey] = $push['body']; @@ -1743,7 +1767,14 @@ trait MessagingBase $bucketId = $bucket['body']['$id']; - \sleep(1); + $this->assertEventually(function () use ($bucketId) { + $response = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals(200, $response['headers']['status-code']); + }, 10000, 500); // Create file $file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', [ @@ -1837,7 +1868,7 @@ trait MessagingBase $this->assertEquals(200, $message['headers']['status-code']); $this->assertContains($message['body']['status'], [MessageStatus::FAILED, MessageStatus::PROCESSING]); - }, 60000, 1000); + }, 60000, 500); } public function testScheduledToDraftMessage(): void @@ -1884,16 +1915,17 @@ trait MessagingBase $this->assertEquals(200, $message['headers']['status-code']); $this->assertEquals(MessageStatus::DRAFT, $message['body']['status']); - \sleep(8); - - $message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $message['body']['$id'], [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]); - - $this->assertEquals(200, $message['headers']['status-code']); - $this->assertEquals(MessageStatus::DRAFT, $message['body']['status']); + // Verify the message remains in DRAFT status and is not processed by the scheduler + $draftMessageId = $message['body']['$id']; + $this->assertEventually(function () use ($draftMessageId) { + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $draftMessageId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(MessageStatus::DRAFT, $response['body']['status']); + }, 10000, 500); } public function testDraftToScheduledMessage(): void @@ -2001,22 +2033,21 @@ trait MessagingBase $messageId = $message['body']['$id']; - // Verify message is still scheduled after a short wait - // (scheduled far enough in the future that the scheduler won't process it) - \sleep(5); - - $message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $messageId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]); - - $this->assertEquals(200, $message['headers']['status-code']); - $this->assertEquals(MessageStatus::SCHEDULED, $message['body']['status']); - $this->assertEquals( - (new \DateTime($scheduledAt))->getTimestamp(), - (new \DateTime($message['body']['scheduledAt']))->getTimestamp() - ); + // Verify the message remains scheduled (scheduled far enough in the future + // that the scheduler won't process it) + $this->assertEventually(function () use ($messageId, $scheduledAt) { + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $messageId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(MessageStatus::SCHEDULED, $response['body']['status']); + $this->assertEquals( + (new \DateTime($scheduledAt))->getTimestamp(), + (new \DateTime($response['body']['scheduledAt']))->getTimestamp() + ); + }, 10000, 500); } public function testSendEmail(): array @@ -2105,9 +2136,17 @@ trait MessagingBase $this->assertEquals(201, $email['headers']['status-code']); - \sleep(2); + $emailMessageId = $email['body']['$id']; + $this->assertEventually(function () use ($emailMessageId) { + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $emailMessageId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertContains($response['body']['status'], ['sent', 'failed']); + }, 30000, 500); - $message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $email['body']['$id'], [ + $message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $emailMessageId, [ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2168,9 +2207,17 @@ trait MessagingBase $this->assertEquals(200, $updatedEmail['headers']['status-code']); - \sleep(5); + $updatedEmailId = $updatedEmail['body']['$id']; + $this->assertEventually(function () use ($updatedEmailId) { + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $updatedEmailId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertContains($response['body']['status'], ['sent', 'failed']); + }, 30000, 500); - $message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $updatedEmail['body']['$id'], [ + $message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $updatedEmailId, [ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2278,9 +2325,17 @@ trait MessagingBase $this->assertEquals(201, $sms['headers']['status-code']); - \sleep(5); + $smsMessageId = $sms['body']['$id']; + $this->assertEventually(function () use ($smsMessageId) { + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $smsMessageId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertContains($response['body']['status'], ['sent', 'failed']); + }, 30000, 500); - $message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $sms['body']['$id'], [ + $message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $smsMessageId, [ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2333,9 +2388,17 @@ trait MessagingBase $this->assertEquals(200, $updatedSms['headers']['status-code']); - \sleep(2); + $updatedSmsId = $updatedSms['body']['$id']; + $this->assertEventually(function () use ($updatedSmsId) { + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $updatedSmsId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertContains($response['body']['status'], ['sent', 'failed']); + }, 30000, 500); - $message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $updatedSms['body']['$id'], [ + $message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $updatedSmsId, [ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2440,9 +2503,17 @@ trait MessagingBase $this->assertEquals(201, $push['headers']['status-code']); - \sleep(5); + $pushMessageId = $push['body']['$id']; + $this->assertEventually(function () use ($pushMessageId) { + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $pushMessageId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertContains($response['body']['status'], ['sent', 'failed']); + }, 30000, 500); - $message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $push['body']['$id'], [ + $message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $pushMessageId, [ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2496,9 +2567,17 @@ trait MessagingBase $this->assertEquals(200, $updatedPush['headers']['status-code']); - \sleep(5); + $updatedPushId = $updatedPush['body']['$id']; + $this->assertEventually(function () use ($updatedPushId) { + $response = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $updatedPushId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertContains($response['body']['status'], ['sent', 'failed']); + }, 30000, 500); - $message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $updatedPush['body']['$id'], [ + $message = $this->client->call(Client::METHOD_GET, '/messaging/messages/' . $updatedPushId, [ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], diff --git a/tests/e2e/Services/Migrations/MigrationsBase.php b/tests/e2e/Services/Migrations/MigrationsBase.php index 97a31502a2..eb13f0ca27 100644 --- a/tests/e2e/Services/Migrations/MigrationsBase.php +++ b/tests/e2e/Services/Migrations/MigrationsBase.php @@ -1418,7 +1418,18 @@ trait MigrationsBase $this->assertEquals(202, $longtext['headers']['status-code']); - \sleep(3); + $this->assertEventually(function () use ($databaseId, $collectionId) { + $collection = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + $this->assertEquals(200, $collection['headers']['status-code']); + $this->assertNotEmpty($collection['body']['attributes']); + foreach ($collection['body']['attributes'] as $attr) { + $this->assertEquals('available', $attr['status'], "Attribute '{$attr['key']}' is not available yet"); + } + }, 30_000, 500); // Create sample documents for ($i = 1; $i <= 10; $i++) { diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 0572d1b119..ee4f5ba051 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -1778,8 +1778,6 @@ class ProjectsConsoleClientTest extends Scope $sessionCookie = $response['headers']['set-cookie']; $sessionId2 = $response['body']['$id']; - sleep(5); // fixes flaky tests. - /** * List sessions */ @@ -5683,17 +5681,17 @@ class ProjectsConsoleClientTest extends Scope ]); $this->assertEquals(401, $response['headers']['status-code']); - sleep(5); - - $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-dev-key' => $devKey['secret'] - ], [ - 'email' => 'user@appwrite.io', - 'password' => 'password' - ]); - $this->assertEquals(429, $response['headers']['status-code']); + $this->assertEventually(function () use ($projectId, $devKey) { + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-dev-key' => $devKey['secret'] + ], [ + 'email' => 'user@appwrite.io', + 'password' => 'password' + ]); + $this->assertEquals(429, $response['headers']['status-code']); + }, 15_000, 500); } #[Group('abuseEnabled')] diff --git a/tests/e2e/Services/Proxy/ProxyBase.php b/tests/e2e/Services/Proxy/ProxyBase.php index 5c9acb43ab..81b11d1041 100644 --- a/tests/e2e/Services/Proxy/ProxyBase.php +++ b/tests/e2e/Services/Proxy/ProxyBase.php @@ -213,7 +213,7 @@ trait ProxyBase 'x-appwrite-key' => $this->getProject()['apiKey'], ])); $this->assertEquals($deploymentId, $site['body']['deploymentId'], 'Deployment is not activated, deployment: ' . json_encode($site['body'], JSON_PRETTY_PRINT)); - }, 300000, 500); + }, 120000, 500); return ['siteId' => $siteId, 'deploymentId' => $deploymentId]; } diff --git a/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php index ffb8530b3b..3da00898c9 100644 --- a/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php @@ -68,7 +68,7 @@ class RealtimeConsoleClientTest extends Scope ], $this->getHeaders())); $this->assertEquals(200, $attribute['headers']['status-code']); $this->assertEquals('available', $attribute['body']['status']); - }, 900000, 500); + }, 120000, 500); return ['actorsId' => $actorsId, 'databaseId' => $databaseId]; } @@ -120,7 +120,7 @@ class RealtimeConsoleClientTest extends Scope ], $this->getHeaders())); $this->assertEquals(200, $column['headers']['status-code']); $this->assertEquals('available', $column['body']['status']); - }, 900000, 500); + }, 120000, 500); return ['actorsId' => $actorsId, 'databaseId' => $databaseId]; } @@ -151,7 +151,7 @@ class RealtimeConsoleClientTest extends Scope ], $this->getHeaders())); $this->assertEquals(200, $index['headers']['status-code'], 'Index polling returned ' . $index['headers']['status-code'] . ': ' . json_encode($index['body'] ?? '')); $this->assertEquals('available', $index['body']['status']); - }, 900000, 500); + }, 120000, 500); return $data; } @@ -182,7 +182,7 @@ class RealtimeConsoleClientTest extends Scope ], $this->getHeaders())); $this->assertEquals(200, $index['headers']['status-code'], 'Index polling returned ' . $index['headers']['status-code'] . ': ' . json_encode($index['body'] ?? '')); $this->assertEquals('available', $index['body']['status']); - }, 900000, 500); + }, 120000, 500); return $data; } diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientQueryTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientQueryTest.php index b1fbb14bbb..8104fa7bd0 100644 --- a/tests/e2e/Services/Realtime/RealtimeCustomClientQueryTest.php +++ b/tests/e2e/Services/Realtime/RealtimeCustomClientQueryTest.php @@ -136,7 +136,14 @@ class RealtimeCustomClientQueryTest extends Scope 'required' => false, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/status', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $targetDocumentId = ID::unique(); @@ -228,7 +235,14 @@ class RealtimeCustomClientQueryTest extends Scope 'required' => false, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/status', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $excludedDocumentId = ID::unique(); @@ -319,7 +333,14 @@ class RealtimeCustomClientQueryTest extends Scope 'required' => false, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/score', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Subscribe with query for score > 50 $client = $this->getWebsocket(['documents'], [ @@ -407,7 +428,14 @@ class RealtimeCustomClientQueryTest extends Scope 'required' => false, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/age', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Subscribe with query for age < 18 $client = $this->getWebsocket(['documents'], [ @@ -495,7 +523,14 @@ class RealtimeCustomClientQueryTest extends Scope 'required' => false, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/priority', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Subscribe with query for priority >= 5 $client = $this->getWebsocket(['documents'], [ @@ -601,7 +636,14 @@ class RealtimeCustomClientQueryTest extends Scope 'required' => false, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/level', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Subscribe with query for level <= 10 $client = $this->getWebsocket(['documents'], [ @@ -708,7 +750,14 @@ class RealtimeCustomClientQueryTest extends Scope 'required' => false, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/description', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Subscribe with query for description IS NULL $client = $this->getWebsocket(['documents'], [ @@ -796,7 +845,14 @@ class RealtimeCustomClientQueryTest extends Scope 'required' => false, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/email', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Subscribe with query for email IS NOT NULL $client = $this->getWebsocket(['documents'], [ @@ -892,7 +948,20 @@ class RealtimeCustomClientQueryTest extends Scope 'required' => false, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/status', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/priority', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Subscribe with AND query: status = 'active' AND priority > 5 $client = $this->getWebsocket(['documents'], [ @@ -1009,7 +1078,14 @@ class RealtimeCustomClientQueryTest extends Scope 'required' => false, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/type', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Subscribe with OR query: type = 'urgent' OR type = 'critical' $client = $this->getWebsocket(['documents'], [ @@ -1128,7 +1204,20 @@ class RealtimeCustomClientQueryTest extends Scope 'required' => false, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/category', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/score', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Subscribe with complex query: (category = 'premium' OR category = 'vip') AND score >= 80 $client = $this->getWebsocket(['documents'], [ @@ -1275,7 +1364,14 @@ class RealtimeCustomClientQueryTest extends Scope 'required' => false, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/status', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // Subscribe only to the fully-qualified documents channel for this collection $scopedChannel = 'databases.' . $databaseId . '.collections.' . $collectionId . '.documents'; @@ -1351,7 +1447,14 @@ class RealtimeCustomClientQueryTest extends Scope 'required' => false, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/status', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $targetDocumentId = ID::unique(); @@ -1525,7 +1628,14 @@ class RealtimeCustomClientQueryTest extends Scope 'required' => false, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/status', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $targetDocId = ID::unique(); @@ -1744,7 +1854,14 @@ class RealtimeCustomClientQueryTest extends Scope 'required' => false, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/status', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', array_merge([ 'content-type' => 'application/json', @@ -1756,7 +1873,14 @@ class RealtimeCustomClientQueryTest extends Scope 'required' => false, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/category', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $queryStatusActive = Query::equal('status', ['active'])->toString(); $queryStatusPending = Query::equal('status', ['pending'])->toString(); @@ -1955,7 +2079,14 @@ class RealtimeCustomClientQueryTest extends Scope 'required' => false, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/status', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $queryStatusActive = Query::equal('status', ['active'])->toString(); $queryStatusPending = Query::equal('status', ['pending'])->toString(); @@ -2088,7 +2219,14 @@ class RealtimeCustomClientQueryTest extends Scope 'required' => false, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId, $projectId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/status', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $targetDocumentId = ID::unique(); @@ -2183,7 +2321,7 @@ class RealtimeCustomClientQueryTest extends Scope 'url' => 'http://localhost', ]); - sleep(3); + sleep(1); // Verify subscription is still working after permission change $nonMatchingDocumentId = ID::unique(); @@ -2225,7 +2363,7 @@ class RealtimeCustomClientQueryTest extends Scope ], ]); - sleep(2); + sleep(1); // This should NOT receive event because the query is for $targetDocumentId, not $targetDocumentId2 // This verifies the query is preserved after permission change @@ -2247,7 +2385,7 @@ class RealtimeCustomClientQueryTest extends Scope ]); // Wait a bit for the event to be processed - sleep(3); + sleep(1); // Verify the event is received with the preserved subscription $event2 = json_decode($client->receive(), true); @@ -2424,7 +2562,7 @@ class RealtimeCustomClientQueryTest extends Scope $response = json_decode($clientWithNonMatchingQuery->receive(), true); $this->assertEquals('connected', $response['type']); - sleep(6); + sleep(2); // Client without query should receive event $eventNoQuery = json_decode($clientNoQuery->receive(), true); diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php index cde21acdd8..93dda3f29a 100644 --- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php @@ -774,7 +774,14 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals(256, $name['body']['size']); $this->assertTrue($name['body']['required']); - sleep(2); + $this->assertEventually(function () use ($databaseId, $actorsId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $actorsId . '/attributes/name', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); /** * Test Document Create @@ -1351,7 +1358,14 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals(256, $name['body']['size']); $this->assertTrue($name['body']['required']); - sleep(2); + $this->assertEventually(function () use ($databaseId, $actorsId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $actorsId . '/attributes/name', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // create $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$actorsId}/documents", array_merge([ @@ -1896,7 +1910,14 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals(256, $name['body']['size']); $this->assertTrue($name['body']['required']); - sleep(2); + $this->assertEventually(function () use ($databaseId, $actorsId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $actorsId . '/attributes/name', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); /** * Test Document Create @@ -2584,7 +2605,14 @@ class RealtimeCustomClientTest extends Scope 'required' => true, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/name', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); /** * Test Transaction Create with Single Document @@ -2786,7 +2814,14 @@ class RealtimeCustomClientTest extends Scope 'required' => true, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/name', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); /** * Test Multiple Operations in Single Transaction @@ -2935,7 +2970,14 @@ class RealtimeCustomClientTest extends Scope 'required' => true, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $collectionId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/name', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); /** * Test Transaction Rollback - Should NOT trigger realtime events @@ -3072,7 +3114,23 @@ class RealtimeCustomClientTest extends Scope 'required' => false, ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $level1Id) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $level1Id . '/attributes/name', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); + + $this->assertEventually(function () use ($databaseId, $level2Id) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $level2Id . '/attributes/name', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); // two-way one-to-one relationship from level1 to level2 $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$level1Id}/attributes/relationship", array_merge([ @@ -3087,7 +3145,14 @@ class RealtimeCustomClientTest extends Scope 'onDelete' => 'cascade', ]); - sleep(2); + $this->assertEventually(function () use ($databaseId, $level1Id) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $level1Id . '/attributes/level2Ref', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $doc2 = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$level2Id}/documents", array_merge([ 'content-type' => 'application/json', @@ -3209,7 +3274,14 @@ class RealtimeCustomClientTest extends Scope 'required' => true, ]); - Coroutine::sleep(1); + $this->assertEventually(function () use ($databaseId, $collectionId) { + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/name', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('available', $response['body']['status']); + }, 30000, 250); $creates = [ ['name' => 'Doc A'], diff --git a/tests/e2e/Services/Sites/SitesBase.php b/tests/e2e/Services/Sites/SitesBase.php index ce2bbd220a..b940dda742 100644 --- a/tests/e2e/Services/Sites/SitesBase.php +++ b/tests/e2e/Services/Sites/SitesBase.php @@ -65,7 +65,7 @@ trait SitesBase } $this->assertEquals('ready', $deployment['body']['status'], 'Deployment status is not ready, deployment: ' . json_encode($deployment['body'], JSON_PRETTY_PRINT)); - }, 240000, 500); + }, 120000, 500); // Not === so multipart/form-data works fine too if (($params['activate'] ?? false) == true) { @@ -338,7 +338,7 @@ trait SitesBase $maxRetries = 3; for ($attempt = 0; $attempt < $maxRetries; $attempt++) { if ($attempt > 0) { - sleep(5); + sleep(2); } $ch = curl_init("https://api.github.com/repos/{$owner}/{$repository}/commits/main"); diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index 161e37382e..dbed1e884b 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -883,7 +883,7 @@ class SitesCustomServerTest extends Scope $deployment = $this->getDeployment($siteId, $deploymentIdActive); $this->assertEquals('ready', $deployment['body']['status']); - }, 120000, 1000); + }, 120000, 500); $deployment = $this->createDeployment($siteId, [ 'code' => $this->packageSite('static-single-file'), @@ -899,7 +899,7 @@ class SitesCustomServerTest extends Scope $deployment = $this->getDeployment($siteId, $deploymentIdInactive); $this->assertEquals('ready', $deployment['body']['status']); - }, 300000, 500); + }, 120000, 500); $site = $this->getSite($siteId); @@ -992,7 +992,7 @@ class SitesCustomServerTest extends Scope $deployment = $this->getDeployment($siteId, $deploymentId); $this->assertEquals('ready', $deployment['body']['status']); - }, 120000, 1000); + }, 120000, 500); /** * Test for SUCCESS @@ -1239,7 +1239,7 @@ class SitesCustomServerTest extends Scope $deployment = $this->getDeployment($siteId, $deploymentId); $this->assertEquals('ready', $deployment['body']['status']); - }, 120000, 1000); + }, 120000, 500); /** * Test for SUCCESS @@ -1364,7 +1364,7 @@ class SitesCustomServerTest extends Scope $deployment = $this->getDeployment($siteId, $deploymentId); $this->assertEquals('ready', $deployment['body']['status']); - }, 120000, 1000); + }, 120000, 500); /** * Test for SUCCESS @@ -1589,7 +1589,7 @@ class SitesCustomServerTest extends Scope $this->assertEventually(function () use ($siteId) { $site = $this->getSite($siteId); $this->assertNotEmpty($site['body']['deploymentId']); - }, 200000, 500); + }, 120000, 500); $domain = $this->setupSiteDomain($siteId); $proxyClient = new Client(); @@ -1660,7 +1660,7 @@ class SitesCustomServerTest extends Scope $this->assertEventually(function () use ($siteId) { $site = $this->getSite($siteId); $this->assertNotEmpty($site['body']['deploymentId']); - }, 200000, 500); + }, 120000, 500); $domain = $this->setupSiteDomain($siteId); $proxyClient = new Client(); @@ -1738,7 +1738,7 @@ class SitesCustomServerTest extends Scope $this->assertEventually(function () use ($siteId) { $site = $this->getSite($siteId); $this->assertNotEmpty($site['body']['deploymentId']); - }, 200000, 500); + }, 120000, 500); $domain = $this->setupSiteDomain($siteId); $proxyClient = new Client(); @@ -1840,7 +1840,7 @@ class SitesCustomServerTest extends Scope $this->assertEquals(200, $rules['headers']['status-code']); $this->assertEquals(0, $rules['body']['total']); - }, 120000, 1000); + }, 120000, 500); $response = $proxyClient->call(Client::METHOD_GET, '/'); @@ -2081,7 +2081,7 @@ class SitesCustomServerTest extends Scope if (!empty($logs['body']['executions'])) { break; } - \sleep(1); + \usleep(500000); } $this->assertNotEmpty($logs['body']['executions'], 'Execution logs were not available within timeout'); $this->assertEquals(200, $logs['headers']['status-code']); @@ -2152,11 +2152,11 @@ class SitesCustomServerTest extends Scope $this->assertEquals(200, $site['headers']['status-code']); // Wait for the logging config change to propagate to the site runtime - \sleep(5); - - $response = $proxyClient->call(Client::METHOD_GET, '/logs-inline'); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertStringContainsString("Inline logs printed.", $response['body']); + $this->assertEventually(function () use ($proxyClient) { + $response = $proxyClient->call(Client::METHOD_GET, '/logs-inline'); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertStringContainsString("Inline logs printed.", $response['body']); + }, 15_000, 500); // Poll for the NEW log entry (after logging was disabled) to appear $timeout = 30; @@ -2175,7 +2175,7 @@ class SitesCustomServerTest extends Scope $newLog = $logs['body']['executions'][0]; break; } - \sleep(1); + \usleep(500000); } $this->assertNotNull($newLog, 'New log entry should appear after logging-disabled request'); $this->assertEquals("GET", $newLog['requestMethod']); @@ -2205,7 +2205,7 @@ class SitesCustomServerTest extends Scope $newLog = $logs['body']['executions'][0]; break; } - \sleep(1); + \usleep(500000); } $this->assertNotNull($newLog, 'New log entry should appear after logging-disabled /logs-action request'); $this->assertEquals("GET", $newLog['requestMethod']); @@ -2298,7 +2298,7 @@ class SitesCustomServerTest extends Scope $this->assertEventually(function () use ($siteId, $deploymentId2) { $site = $this->getSite($siteId); $this->assertEquals($deploymentId2, $site['body']['deploymentId']); - }, 120000, 1000); + }, 120000, 500); $response = $proxyClient->call(Client::METHOD_GET, '/not-found'); $this->assertStringContainsString("Index page", $response['body']); @@ -2664,13 +2664,13 @@ class SitesCustomServerTest extends Scope $response = $proxyClient->call(Client::METHOD_GET, '/'); $this->assertEquals(200, $response['headers']['status-code']); $this->assertStringContainsString('Sub-directory index', $response['body']); - }, 30000, 2000); + }, 30000, 500); $this->assertEventually(function () use ($proxyClient) { $response1 = $proxyClient->call(Client::METHOD_GET, '/project1'); $this->assertEquals(200, $response1['headers']['status-code']); $this->assertStringContainsString('Sub-directory project1', $response1['body']); - }, 30000, 2000); + }, 30000, 500); $response2 = $proxyClient->call(Client::METHOD_GET, '/project1/'); $this->assertEquals(200, $response2['headers']['status-code']); @@ -2816,7 +2816,7 @@ class SitesCustomServerTest extends Scope $deployment = $this->getDeployment($siteId, $deploymentId); $this->assertEquals('failed', $deployment['body']['status']); - }, 120000, 1000); + }, 120000, 500); // deployment failed error page $response = $proxyClient->call(Client::METHOD_GET, '/', followRedirects: false, headers: [ diff --git a/tests/e2e/Services/Storage/StorageBase.php b/tests/e2e/Services/Storage/StorageBase.php index 178f3deff8..9f4105e1cb 100644 --- a/tests/e2e/Services/Storage/StorageBase.php +++ b/tests/e2e/Services/Storage/StorageBase.php @@ -907,7 +907,15 @@ trait StorageBase $this->assertEquals(204, $file['headers']['status-code']); $this->assertEmpty($file['body']); - sleep(1); + + $this->assertEventually(function () use ($data, $fileId) { + $file = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $data['bucketId'] . '/files/' . $fileId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + $this->assertEquals(404, $file['headers']['status-code']); + }, 10_000, 500); + //upload again using the same ID $file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', array_merge([ 'content-type' => 'multipart/form-data', diff --git a/tests/e2e/Services/Teams/TeamsBaseServer.php b/tests/e2e/Services/Teams/TeamsBaseServer.php index b8a614b8ef..9c585d3224 100644 --- a/tests/e2e/Services/Teams/TeamsBaseServer.php +++ b/tests/e2e/Services/Teams/TeamsBaseServer.php @@ -323,19 +323,18 @@ trait TeamsBaseServer $this->assertEquals($user['headers']['status-code'], 204); /** Wait for deletes worker to delete membership and update team membership count */ - sleep(5); + $this->assertEventually(function () use ($teamUid) { + $response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); - /** Get Team Count */ - $response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertNotEmpty($response['body']['$id']); - $this->assertEquals('Arsenal', $response['body']['name']); - $this->assertEquals(0, $response['body']['total']); - $this->assertIsInt($response['body']['total']); - $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['$createdAt'])); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals('Arsenal', $response['body']['name']); + $this->assertEquals(0, $response['body']['total']); + $this->assertIsInt($response['body']['total']); + $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['$createdAt'])); + }, 30_000, 500); } } diff --git a/tests/e2e/Services/Users/UsersBase.php b/tests/e2e/Services/Users/UsersBase.php index 073ecf8f19..5c7b289722 100644 --- a/tests/e2e/Services/Users/UsersBase.php +++ b/tests/e2e/Services/Users/UsersBase.php @@ -1423,17 +1423,18 @@ trait UsersBase $this->assertEquals($user['headers']['status-code'], 200); $this->assertNotEmpty($user['body']['$id']); $this->assertEmpty($user['body']['password']); - sleep(5); - $session = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], [ - 'email' => 'users.service@updated.com', - 'password' => 'password' - ]); + $this->assertEventually(function () { + $session = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'email' => 'users.service@updated.com', + 'password' => 'password' + ]); - $this->assertEquals(401, $session['headers']['status-code']); + $this->assertEquals(401, $session['headers']['status-code']); + }, 15_000, 500); $this->updateProjectinvalidateSessionsProperty(true); $user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/password', array_merge([ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php index 6db31b7ee6..a980e9cde9 100644 --- a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php +++ b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php @@ -904,7 +904,7 @@ class WebhooksCustomServerTest extends Scope $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); - }, 30000, 1000); + }, 30000, 500); } public function testDeleteDeployment(): void