From 20fa8b30175a01382dc6b86cc745566f34314a58 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 18 Sep 2024 14:29:44 +0100 Subject: [PATCH 01/32] fix: flaky functions shot 1 --- .../e2e/Services/Functions/FunctionsBase.php | 25 +++-- .../Functions/FunctionsCustomClientTest.php | 89 ++++++++--------- .../Functions/FunctionsCustomServerTest.php | 98 +++++++++---------- tests/extensions/Async.php | 17 ++++ tests/extensions/Async/Eventually.php | 54 ++++++++++ 5 files changed, 172 insertions(+), 111 deletions(-) create mode 100644 tests/extensions/Async.php create mode 100644 tests/extensions/Async/Eventually.php diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index 2d94b9f0e3..26f2ae7aed 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -2,11 +2,14 @@ namespace Tests\E2E\Services\Functions; +use Appwrite\Tests\Async; use Tests\E2E\Client; use Utopia\CLI\Console; trait FunctionsBase { + use Async; + protected string $stdout = ''; protected string $stderr = ''; @@ -15,29 +18,23 @@ trait FunctionsBase Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr); } - protected function awaitDeploymentIsBuilt($functionId, $deploymentId, $checkForSuccess = true): void + protected function assertDeployment($functionId, $deploymentId, $allowFailure = false) { - while (true) { + $this->assertEventually(function () use ($functionId, $deploymentId, $allowFailure) { $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ]); - if ( - $deployment['headers']['status-code'] >= 400 - || \in_array($deployment['body']['status'], ['ready', 'failed']) - ) { - break; + $status = $deployment['body']['status']; + + if ($allowFailure && $status === 'failed') { + $this->fail('Deployment failed: ' . json_encode($deployment['body']['buildStderr'])); } - \sleep(1); - } - - if ($checkForSuccess) { - $this->assertEquals(200, $deployment['headers']['status-code']); - $this->assertEquals('ready', $deployment['body']['status'], \json_encode($deployment['body'])); - } + $this->assertEquals('ready', $status); + }, 180000, 3000); } // /** diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 92b7c33034..f5f15a0fc6 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -2,7 +2,6 @@ namespace Tests\E2E\Services\Functions; -use Appwrite\Tests\Retry; use CURLFile; use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; @@ -44,7 +43,6 @@ class FunctionsCustomClientTest extends Scope return []; } - #[Retry(count: 2)] public function testCreateExecution(): array { /** @@ -120,7 +118,7 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals(202, $deployment['headers']['status-code']); - $this->awaitDeploymentIsBuilt($function['body']['$id'], $deploymentId); + $this->assertDeployment($function['body']['$id'], $deploymentId); $function = $this->client->call(Client::METHOD_PATCH, '/functions/' . $function['body']['$id'] . '/deployments/' . $deploymentId, [ 'content-type' => 'application/json', @@ -148,25 +146,26 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals(202, $execution['headers']['status-code']); - // Wait for the first scheduled execution to be created - sleep(90); + $this->assertEventually(function () use ($function) { + $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/executions', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); - $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/executions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]); + $this->assertEquals(200, $executions['headers']['status-code']); + $this->assertCount(2, $executions['body']['executions']); - $this->assertEquals(200, $executions['headers']['status-code']); - $this->assertCount(2, $executions['body']['executions']); - $this->assertIsArray($executions['body']['executions']); - $this->assertEquals($executions['body']['executions'][1]['trigger'], 'schedule'); - $this->assertEquals($executions['body']['executions'][1]['status'], 'completed'); - $this->assertEquals($executions['body']['executions'][1]['responseStatusCode'], 200); - $this->assertEquals($executions['body']['executions'][1]['responseBody'], ''); - $this->assertNotEmpty($executions['body']['executions'][1]['logs'], ''); - $this->assertNotEmpty($executions['body']['executions'][1]['errors'], ''); - $this->assertGreaterThan(0, $executions['body']['executions'][1]['duration']); + // Check if the scheduled execution has completed + $scheduledExecution = $executions['body']['executions'][1]; + $this->assertEquals('schedule', $scheduledExecution['trigger']); + $this->assertEquals('completed', $scheduledExecution['status']); + $this->assertEquals(200, $scheduledExecution['responseStatusCode']); + $this->assertEquals('', $scheduledExecution['responseBody']); + $this->assertNotEmpty($scheduledExecution['logs']); + $this->assertNotEmpty($scheduledExecution['errors']); + $this->assertGreaterThan(0, $scheduledExecution['duration']); + }, 120000, 2000); // Cleanup : Delete function $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $function['body']['$id'], [ @@ -216,7 +215,7 @@ class FunctionsCustomClientTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->awaitDeploymentIsBuilt($function['body']['$id'], $deploymentId, true); + $this->assertDeployment($function['body']['$id'], $deploymentId, true); // Schedule execution for the future \date_default_timezone_set('UTC'); @@ -245,25 +244,22 @@ class FunctionsCustomClientTest extends Scope $executionId = $execution['body']['$id']; - $start = \microtime(true); - while (true) { + $this->assertEventually(function () use ($function, $executionId) { $execution = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/executions/' . $executionId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ]); - if ($execution['body']['status'] === 'completed') { - break; - } + $this->assertEquals('completed', $execution['body']['status']); + }, 150000, 2000); // Timeout after 150 seconds, wait 2 seconds between retries - $timeout = 60 + 60 + 15; // up to 1 minute round up, 1 minute schedule postpone, 15s cold start safety - if (\microtime(true) - $start > $timeout) { - $this->fail('Scheduled execution did not complete with status ' . $execution['body']['status'] . ': ' . \json_encode($execution)); - } - - usleep(500000); // 0.5 seconds - } + // After ensuring the execution is completed, fetch it again for assertions + $execution = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/executions/' . $executionId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); $this->assertEquals(200, $execution['headers']['status-code']); $this->assertEquals(200, $execution['body']['responseStatusCode']); @@ -409,7 +405,7 @@ class FunctionsCustomClientTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; - $this->awaitDeploymentIsBuilt($function['body']['$id'], $deploymentId); + $this->assertDeployment($function['body']['$id'], $deploymentId); $function = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, [ 'content-type' => 'application/json', @@ -458,17 +454,16 @@ class FunctionsCustomClientTest extends Scope $executionId = $execution['body']['$id'] ?? ''; - sleep(5); + $this->assertEventually(function () use ($functionId, $executionId, $projectId, $apikey) { + $execution = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $apikey, + ]); - $execution = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ]); - - $this->assertEmpty($execution['body']['responseBody']); - $this->assertEquals(200, $execution['headers']['status-code']); - $this->assertEquals(200, $execution['body']['responseStatusCode']); + $this->assertEquals('completed', $execution['body']['status']); + $this->assertEquals(200, $execution['body']['responseStatusCode']); + }, 30000, 1000); return [ 'functionId' => $functionId @@ -521,7 +516,7 @@ class FunctionsCustomClientTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; - $this->awaitDeploymentIsBuilt($function['body']['$id'], $deploymentId); + $this->assertDeployment($function['body']['$id'], $deploymentId); // Why do we have to do this? $function = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, [ @@ -760,7 +755,7 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals(202, $deployment['headers']['status-code']); - $this->awaitDeploymentIsBuilt($function['body']['$id'], $deploymentId); + $this->assertDeployment($function['body']['$id'], $deploymentId); $function = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, [ 'content-type' => 'application/json', @@ -853,7 +848,7 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals(202, $deployment['headers']['status-code']); - $this->awaitDeploymentIsBuilt($function['body']['$id'], $deploymentId); + $this->assertDeployment($function['body']['$id'], $deploymentId); $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 2958e6cb5f..4005279560 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -3,7 +3,6 @@ namespace Tests\E2E\Services\Functions; use Appwrite\Functions\Specification; -use Appwrite\Tests\Retry; use CURLFile; use PHPUnit\Framework\ExpectationFailedException; use Tests\E2E\Client; @@ -429,7 +428,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(202, $deployment['headers']['status-code']); - $this->awaitDeploymentIsBuilt($function['body']['$id'], $deploymentId); + $this->assertDeployment($function['body']['$id'], $deploymentId); $functionDetails = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, [ 'content-type' => 'application/json', @@ -521,7 +520,7 @@ class FunctionsCustomServerTest extends Scope // Wait for deployment build to finish // Deployment is automatically activated - $this->awaitDeploymentIsBuilt($function['body']['$id'], $deploymentId); + $this->assertDeployment($function['body']['$id'], $deploymentId); $deployments = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, [ 'content-type' => 'application/json', @@ -635,7 +634,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(true, (new DatetimeValidator())->isValid($deployment['body']['$createdAt'])); $this->assertEquals('index.php', $deployment['body']['entrypoint']); - $this->awaitDeploymentIsBuilt($data['functionId'], $deploymentId); + $this->assertDeployment($data['functionId'], $deploymentId); $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/deployments', array_merge([ 'content-type' => 'multipart/form-data', @@ -650,7 +649,7 @@ class FunctionsCustomServerTest extends Scope $deploymentIdInactive = $deployment['body']['$id']; - $this->awaitDeploymentIsBuilt($data['functionId'], $deploymentIdInactive); + $this->assertDeployment($data['functionId'], $deploymentIdInactive); $function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'], array_merge([ 'content-type' => 'application/json', @@ -696,26 +695,16 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(true, (new DatetimeValidator())->isValid($deployment['body']['$createdAt'])); $this->assertEquals('index.php', $deployment['body']['entrypoint']); - // Poll until deployment is in progress - while (true) { + $this->assertEventually(function () use ($data, $deploymentId) { $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments/' . $deploymentId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ]); - if ( - $deployment['headers']['status-code'] >= 400 - || $deployment['body']['status'] === 'building' - ) { - break; - } - - \sleep(1); - } - - $this->assertEquals(200, $deployment['headers']['status-code']); - $this->assertEquals('building', $deployment['body']['status']); + $this->assertEquals(200, $deployment['headers']['status-code']); + $this->assertEquals('building', $deployment['body']['status']); + }, 60000, 1000); // Cancel the deployment build $cancel = $this->client->call(Client::METHOD_PATCH, '/functions/' . $data['functionId'] . '/deployments/' . $deploymentId . '/build', [ @@ -729,10 +718,10 @@ class FunctionsCustomServerTest extends Scope /** * Build worker still runs the build. - * 30s sleep gives worker enough time to finish build. + * 10s sleep gives worker enough time to finish build. * After build finished, it should still be canceled, not ready. */ - \sleep(30); + \sleep(10); $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments/' . $deploymentId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -794,7 +783,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $largeTag['body']['$id']; - $this->awaitDeploymentIsBuilt($data['functionId'], $deploymentId, true); + $this->assertDeployment($data['functionId'], $deploymentId, true); $response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments/' . $deploymentId, array_merge([ 'content-type' => 'application/json', @@ -1294,7 +1283,6 @@ class FunctionsCustomServerTest extends Scope /** * @depends testUpdateDeployment */ - #[Retry(count: 2)] public function testSyncCreateExecution($data): array { /** @@ -1417,14 +1405,23 @@ class FunctionsCustomServerTest extends Scope $executionId = $execution['body']['$id'] ?? ''; $this->assertEquals(202, $execution['headers']['status-code']); - sleep(5); - $execution = $this->client->call(Client::METHOD_DELETE, '/functions/' . $data['functionId'] . '/executions/' . $executionId, array_merge([ + + $this->assertEventually(function () use ($data, $executionId) { + $execution = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions/' . $executionId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]); + + $this->assertEquals(200, $execution['headers']['status-code']); + $this->assertEquals('waiting', $execution['body']['status']); + }, 10000, 500); + + $execution = $this->client->call(Client::METHOD_DELETE, '/functions/' . $data['functionId'] . '/executions/' . $executionId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + ]); $this->assertEquals(204, $execution['headers']['status-code']); - $this->assertEmpty($execution['body']); return $data; } @@ -1432,7 +1429,6 @@ class FunctionsCustomServerTest extends Scope /** * @depends testGetExecution */ - #[Retry(count: 2)] public function testUpdateSpecs($data): array { /** @@ -1622,7 +1618,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(202, $deployment['headers']['status-code']); - $this->awaitDeploymentIsBuilt($functionId, $deploymentId); + $this->assertDeployment($functionId, $deploymentId); $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ 'content-type' => 'application/json', @@ -1773,7 +1769,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->awaitDeploymentIsBuilt($function['body']['$id'], $deploymentId, checkForSuccess: false); + $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ 'content-type' => 'application/json', @@ -1868,7 +1864,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->awaitDeploymentIsBuilt($function['body']['$id'], $deploymentId, checkForSuccess: false); + $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ 'content-type' => 'application/json', @@ -1954,7 +1950,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->awaitDeploymentIsBuilt($function['body']['$id'], $deploymentId, checkForSuccess: false); + $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ 'content-type' => 'application/json', @@ -2054,7 +2050,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->awaitDeploymentIsBuilt($function['body']['$id'], $deploymentId, checkForSuccess: false); + $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ 'content-type' => 'application/json', @@ -2147,7 +2143,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->awaitDeploymentIsBuilt($function['body']['$id'], $deploymentId, checkForSuccess: false); + $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ 'content-type' => 'application/json', @@ -2169,21 +2165,23 @@ class FunctionsCustomServerTest extends Scope ]); $userId = $user['body']['$id']; - $this->assertEquals(201, $user['headers']['status-code']); - // Wait for execution to occur - sleep(15); + $execution = null; + $this->assertEventually(function () use ($functionId, &$execution) { + $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]); - $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + if (empty($executions['body']['executions'])) { + throw new \Exception('No executions found yet'); + } - $execution = $executions['body']['executions'][0]; + $execution = $executions['body']['executions'][0]; + $this->assertEquals('completed', $execution['status']); + }, 30000, 1000); - $this->assertEquals(200, $executions['headers']['status-code']); - $this->assertEquals('completed', $execution['status']); $this->assertEquals(204, $execution['responseStatusCode']); $this->assertStringContainsString($userId, $execution['logs']); $this->assertStringContainsString('Event User', $execution['logs']); @@ -2243,7 +2241,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->awaitDeploymentIsBuilt($function['body']['$id'], $deploymentId, checkForSuccess: false); + $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ 'content-type' => 'application/json', @@ -2346,7 +2344,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->awaitDeploymentIsBuilt($function['body']['$id'], $deploymentId, checkForSuccess: false); + $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ 'content-type' => 'application/json', @@ -2437,7 +2435,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->awaitDeploymentIsBuilt($function['body']['$id'], $deploymentId, checkForSuccess: false); + $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ 'content-type' => 'application/json', @@ -2553,7 +2551,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->awaitDeploymentIsBuilt($function['body']['$id'], $deploymentId, checkForSuccess: false); + $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ 'content-type' => 'application/json', @@ -2639,7 +2637,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->awaitDeploymentIsBuilt($function['body']['$id'], $deploymentId, checkForSuccess: false); + $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ 'content-type' => 'application/json', @@ -2735,7 +2733,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; - $this->awaitDeploymentIsBuilt($functionId, $deploymentId, checkForSuccess: false); + $this->assertDeployment($functionId, $deploymentId, allowFailure: true); // Sync Executions test $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ diff --git a/tests/extensions/Async.php b/tests/extensions/Async.php new file mode 100644 index 0000000000..48af24bc02 --- /dev/null +++ b/tests/extensions/Async.php @@ -0,0 +1,17 @@ +timeoutMs = $timeoutMs; + $this->waitMs = $waitMs; + } + + public function evaluate(mixed $probe, string $description = '', bool $returnResult = false): ?bool + { + if (!is_callable($probe)) { + throw new \Exception('Probe must be a callable'); + } + + $start = microtime(true); + $lastException = null; + + do { + try { + $probe(); + return true; + } catch (\Exception $exception) { + $lastException = $exception; + } + + usleep($this->waitMs * 1000); + } while (microtime(true) - $start < $this->timeoutMs / 1000); + + if ($returnResult) { + return false; + } + + throw $lastException; + } + + protected function failureDescription(mixed $other): string + { + return 'the given probe was satisfied within ' . $this->timeoutMs . 'ms.'; + } + + public function toString(): string + { + return 'Eventually'; + } +} From 121b9f32b816b26d3a4afb49a1e0a57e8f8d6be2 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 18 Sep 2024 14:50:36 +0100 Subject: [PATCH 02/32] fix: allowFailure --- .../e2e/Services/Functions/FunctionsBase.php | 2 +- .../Functions/FunctionsCustomServerTest.php | 177 ++++-------------- 2 files changed, 40 insertions(+), 139 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index 26f2ae7aed..7f33e0b2ee 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -34,7 +34,7 @@ trait FunctionsBase } $this->assertEquals('ready', $status); - }, 180000, 3000); + }, 180000, 1000); } // /** diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 4005279560..193e99d89e 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -1631,33 +1631,32 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(202, $execution['headers']['status-code']); - sleep(20); + $executionData = null; - $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'queries' => [ - Query::equal('trigger', ['http'])->toString(), - ], - ]); + $this->assertEventually(function () use ($functionId, $executionId, &$executionData) { + $executionData = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); - $this->assertEquals($executions['headers']['status-code'], 200); - $this->assertEquals($executions['body']['total'], 1); - $this->assertIsArray($executions['body']['executions']); - $this->assertCount(1, $executions['body']['executions']); - $this->assertEquals($executions['body']['executions'][0]['$id'], $executionId); - $this->assertEquals($executions['body']['executions'][0]['trigger'], 'http'); - $this->assertEquals($executions['body']['executions'][0]['status'], 'failed'); - $this->assertEquals($executions['body']['executions'][0]['responseStatusCode'], 500); - $this->assertGreaterThan(2, $executions['body']['executions'][0]['duration']); - $this->assertLessThan(20, $executions['body']['executions'][0]['duration']); - $this->assertEquals($executions['body']['executions'][0]['responseBody'], ''); - $this->assertEquals($executions['body']['executions'][0]['logs'], ''); - $this->assertStringContainsString('timed out', $executions['body']['executions'][0]['errors']); + $this->assertEquals(200, $executionData['headers']['status-code']); + $this->assertEquals('failed', $executionData['body']['status']); - $start = \microtime(true); - while (true) { + if ($executionData['body']['status'] !== 'failed') { + } + }, 30000, 1000); + + $this->assertEquals('failed', $executionData['body']['status']); + $this->assertEquals(500, $executionData['body']['responseStatusCode']); + $this->assertGreaterThan(2, $executionData['body']['duration']); + $this->assertLessThan(20, $executionData['body']['duration']); + $this->assertEquals('', $executionData['body']['responseBody']); + $this->assertEquals('', $executionData['body']['logs']); + $this->assertStringContainsString('timed out', $executionData['body']['errors']); + + $scheduledExecutionData = null; + + $this->assertEventually(function () use ($functionId, &$scheduledExecutionData) { $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1668,27 +1667,12 @@ class FunctionsCustomServerTest extends Scope ]); $this->assertEquals(200, $executions['headers']['status-code']); + $this->assertEquals(1, $executions['body']['total']); - if (\count($executions['body']['executions']) > 0) { - break; - } + $scheduledExecutionData = $executions['body']['executions'][0]; + }, 140000, 1000); // Wait up to 140 seconds, checking every 1 second - // 0s would mean instant execution - // +60 seconds, maximum possible waiting time before next minute - // +10 seconds, maximum update interval time - // +60 seconds, possible overlap between update and schedule tick - // +10 seconds, maximum execution time including cold-start - // Result: We allow maximum - if (\microtime(true) - $start > 140) { - $this->fail('Execution did not create within 140 seconds of schedule: ' . \json_encode($executions)); - } - - usleep(1000000); // 1 second - } - - $this->assertEquals(200, $executions['headers']['status-code']); - $this->assertGreaterThanOrEqual(1, \count($executions['body']['executions'])); - $this->assertEquals($executions['body']['executions'][0]['trigger'], 'schedule'); + $this->assertEquals('schedule', $scheduledExecutionData['trigger']); // Cleanup : Delete function $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ @@ -1700,6 +1684,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(204, $response['headers']['status-code']); } + /** * * @return array @@ -1769,14 +1754,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); - - $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $deployment['headers']['status-code']); + $this->assertDeployment($function['body']['$id'], $deploymentId); $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ 'content-type' => 'application/json', @@ -1864,17 +1842,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); - - $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $deployment['headers']['status-code']); - - // Wait a little for activation to finish - sleep(5); + $this->assertDeployment($function['body']['$id'], $deploymentId); $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ 'x-appwrite-project' => $this->getProject()['$id'], @@ -1950,17 +1918,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); - - $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $deployment['headers']['status-code']); - - // Wait a little for activation to finish - sleep(5); + $this->assertDeployment($function['body']['$id'], $deploymentId); $bytes = pack('C*', ...[0, 20, 255]); @@ -2050,14 +2008,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); - - $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $deployment['headers']['status-code']); + $this->assertDeployment($function['body']['$id'], $deploymentId); $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ 'content-type' => 'application/json', @@ -2143,17 +2094,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); - - $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $deployment['headers']['status-code']); - - // Wait a little for activation to finish - sleep(5); + $this->assertDeployment($function['body']['$id'], $deploymentId); // Create user to trigger event $user = $this->client->call(Client::METHOD_POST, '/users', array_merge([ @@ -2241,7 +2182,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); + $this->assertDeployment($function['body']['$id'], $deploymentId); $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ 'content-type' => 'application/json', @@ -2344,17 +2285,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); - - $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $deployment['headers']['status-code']); - - // Wait a little for activation to finish - sleep(5); + $this->assertDeployment($function['body']['$id'], $deploymentId); $cookie = 'cookieName=cookieValue; cookie2=value2; cookie3=value=3; cookie4=val:ue4; cookie5=value5'; @@ -2435,17 +2366,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); - - $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $deployment['headers']['status-code']); - - // Wait a little for activation to finish - sleep(5); + $this->assertDeployment($function['body']['$id'], $deploymentId); $cookie = 'cookieName=cookieValue; cookie2=value2; cookie3=value=3; cookie4=val:ue4; cookie5=value5'; @@ -2551,17 +2472,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); - - $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $deployment['headers']['status-code']); - - // Wait a little for activation to finish - sleep(5); + $this->assertDeployment($function['body']['$id'], $deploymentId); $proxyClient = new Client(); $proxyClient->setEndpoint('http://' . $domain); @@ -2637,17 +2548,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->assertDeployment($function['body']['$id'], $deploymentId, allowFailure: true); - - $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $deployment['headers']['status-code']); - - // Wait a little for activation to finish - sleep(5); + $this->assertDeployment($function['body']['$id'], $deploymentId); $proxyClient = new Client(); $proxyClient->setEndpoint('http://' . $domain); @@ -2733,7 +2634,7 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $deployment['body']['$id'] ?? ''; - $this->assertDeployment($functionId, $deploymentId, allowFailure: true); + $this->assertDeployment($functionId, $deploymentId); // Sync Executions test $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ From 979fcacd6cab9bb48aae251f665ad5b576c803ba Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 19 Sep 2024 12:35:52 +0100 Subject: [PATCH 03/32] fix: abstract requests --- .../e2e/Services/Functions/FunctionsBase.php | 391 +++++---- .../Functions/FunctionsConsoleClientTest.php | 173 ++-- .../Functions/FunctionsCustomClientTest.php | 739 +++--------------- .../Functions/FunctionsCustomServerTest.php | 358 +++------ 4 files changed, 469 insertions(+), 1192 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index 7f33e0b2ee..92210a3027 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -3,6 +3,7 @@ namespace Tests\E2E\Services\Functions; use Appwrite\Tests\Async; +use CURLFile; use Tests\E2E\Client; use Utopia\CLI\Console; @@ -13,220 +14,212 @@ trait FunctionsBase protected string $stdout = ''; protected string $stderr = ''; - protected function packageCode($folder) + protected function setupFunction(mixed $params): string { + $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), $params); + $functionId = $function['body']['$id']; + + $this->assertEquals($function['headers']['status-code'], 201); + return $functionId; + } + + protected function setupDeployment(string $functionId, mixed $params): string + { + $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), $params); + $deploymentId = $deployment['body']['$id']; + + $this->assertEquals($deployment['headers']['status-code'], 202); + + $this->assertEventually(function () use ($functionId, $deploymentId) { + $deployment = $this->getDeployment($functionId, $deploymentId); + $this->assertEquals('completed', $deployment['body']['status']); + }, 200000, 500); + + return $deploymentId; + } + + protected function cleanupFunction(string $functionId): void + { + $function = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + + $this->assertEquals($function['headers']['status-code'], 204); + } + + protected function createFunction(mixed $params) + { + $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), $params); + $functionId = $function['body']['$id']; + + if (empty($variables)) { + return $function; + } + + $function = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + return $function; + } + + protected function createVariable($functionId, $key, $value) + { + $variable = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'key' => $key, + 'value' => $value + ]); + + return $variable; + } + + protected function getFunction($functionId) + { + $function = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + return $function; + } + + protected function getDeployment($functionId, $deploymentId) + { + $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + return $deployment; + } + + protected function getExecution($functionId, $executionId) + { + $execution = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + return $execution; + } + + protected function listDeployments($functionId, $params = []) + { + $deployments = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders(), $params)); + + return $deployments; + } + + protected function listExecutions($functionId, $params = []) + { + $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders(), $params)); + + return $executions; + } + + protected function packageFunction(string $folder = 'php') + { + $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; + Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr); + + if (!file_exists($code)) { + throw new \Exception('Failed to create code package. ' . $code . ' does not exist.'); + } + if (filesize($code) > 1024 * 1024 * 5) { + throw new \Exception('Code package is too large. Use the chunked upload method instead.'); + } + + return new CURLFile($code, 'application/x-gzip', \basename($code)); } - protected function assertDeployment($functionId, $deploymentId, $allowFailure = false) - { - $this->assertEventually(function () use ($functionId, $deploymentId, $allowFailure) { - $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], + protected function createDeployment( + string $functionId, + mixed $params, + bool $cli = false, + bool $admin = false + ) { + $authHeaders = $this->getHeaders(); + + if ($admin) { + $authHeaders = [ 'x-appwrite-key' => $this->getProject()['apiKey'], - ]); + ]; + } + if ($cli) { + $authHeaders[] = 'x-sdk-language: cli'; + } - $status = $deployment['body']['status']; + $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', [ + 'content-type' => 'multipart/form-data', + 'x-appwrite-project' => $this->getProject()['$id'], - if ($allowFailure && $status === 'failed') { - $this->fail('Deployment failed: ' . json_encode($deployment['body']['buildStderr'])); - } + ], $params); - $this->assertEquals('ready', $status); - }, 180000, 1000); + return $deployment; } - // /** - // * @depends testCreateTeam - // */ - // public function testGetTeam($data):array - // { - // $id = $data['teamUid'] ?? ''; + protected function getFunctionUsage($functionId, $params) + { + $usage = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders(), $params)); - // /** - // * Test for SUCCESS - // */ - // $response = $this->client->call(Client::METHOD_GET, '/teams/'.$id, array_merge([ - // 'content-type' => 'application/json', - // 'x-appwrite-project' => $this->getProject()['$id'], - // ], $this->getHeaders())); + return $usage; + } - // $this->assertEquals(200, $response['headers']['status-code']); - // $this->assertNotEmpty($response['body']['$id']); - // $this->assertEquals('Arsenal', $response['body']['name']); - // $this->assertGreaterThan(-1, $response['body']['total']); - // $this->assertIsInt($response['body']['total']); - // $this->assertIsInt($response['body']['dateCreated']); + protected function getTemplate(string $templateId) + { + $template = $this->client->call(Client::METHOD_GET, '/functions/templates/' . $templateId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); - // /** - // * Test for FAILURE - // */ + return $template; + } - // return []; - // } + protected function createExecution(string $functionId, mixed $params) + { + $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), $params); - // /** - // * @depends testCreateTeam - // */ - // public function testListTeams($data):array - // { - // /** - // * Test for SUCCESS - // */ - // $response = $this->client->call(Client::METHOD_GET, '/teams', array_merge([ - // 'content-type' => 'application/json', - // 'x-appwrite-project' => $this->getProject()['$id'], - // ], $this->getHeaders())); + return $execution; + } - // $this->assertEquals(200, $response['headers']['status-code']); - // $this->assertGreaterThan(0, $response['body']['total']); - // $this->assertIsInt($response['body']['total']); - // $this->assertCount(3, $response['body']['teams']); + protected function deleteFunction($functionId) + { + $function = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); - // $response = $this->client->call(Client::METHOD_GET, '/teams', array_merge([ - // 'content-type' => 'application/json', - // 'x-appwrite-project' => $this->getProject()['$id'], - // ], $this->getHeaders()), [ - // 'limit' => 2, - // ]); - - // $this->assertEquals(200, $response['headers']['status-code']); - // $this->assertGreaterThan(0, $response['body']['total']); - // $this->assertIsInt($response['body']['total']); - // $this->assertCount(2, $response['body']['teams']); - - // $response = $this->client->call(Client::METHOD_GET, '/teams', array_merge([ - // 'content-type' => 'application/json', - // 'x-appwrite-project' => $this->getProject()['$id'], - // ], $this->getHeaders()), [ - // 'offset' => 1, - // ]); - - // $this->assertEquals(200, $response['headers']['status-code']); - // $this->assertGreaterThan(0, $response['body']['total']); - // $this->assertIsInt($response['body']['total']); - // $this->assertCount(2, $response['body']['teams']); - - // $response = $this->client->call(Client::METHOD_GET, '/teams', array_merge([ - // 'content-type' => 'application/json', - // 'x-appwrite-project' => $this->getProject()['$id'], - // ], $this->getHeaders()), [ - // 'search' => 'Manchester', - // ]); - - // $this->assertEquals(200, $response['headers']['status-code']); - // $this->assertGreaterThan(0, $response['body']['total']); - // $this->assertIsInt($response['body']['total']); - // $this->assertCount(1, $response['body']['teams']); - // $this->assertEquals('Manchester United', $response['body']['teams'][0]['name']); - - // $response = $this->client->call(Client::METHOD_GET, '/teams', array_merge([ - // 'content-type' => 'application/json', - // 'x-appwrite-project' => $this->getProject()['$id'], - // ], $this->getHeaders()), [ - // 'search' => 'United', - // ]); - - // $this->assertEquals(200, $response['headers']['status-code']); - // $this->assertGreaterThan(0, $response['body']['total']); - // $this->assertIsInt($response['body']['total']); - // $this->assertCount(1, $response['body']['teams']); - // $this->assertEquals('Manchester United', $response['body']['teams'][0]['name']); - - // /** - // * Test for FAILURE - // */ - - // return []; - // } - - // public function testUpdateTeam():array - // { - // /** - // * Test for SUCCESS - // */ - // $response = $this->client->call(Client::METHOD_POST, '/teams', array_merge([ - // 'content-type' => 'application/json', - // 'x-appwrite-project' => $this->getProject()['$id'], - // ], $this->getHeaders()), [ - // 'name' => 'Demo' - // ]); - - // $this->assertEquals(201, $response['headers']['status-code']); - // $this->assertNotEmpty($response['body']['$id']); - // $this->assertEquals('Demo', $response['body']['name']); - // $this->assertGreaterThan(-1, $response['body']['total']); - // $this->assertIsInt($response['body']['total']); - // $this->assertIsInt($response['body']['dateCreated']); - - // $response = $this->client->call(Client::METHOD_PUT, '/teams/'.$response['body']['$id'], array_merge([ - // 'content-type' => 'application/json', - // 'x-appwrite-project' => $this->getProject()['$id'], - // ], $this->getHeaders()), [ - // 'name' => 'Demo New' - // ]); - - // $this->assertEquals(200, $response['headers']['status-code']); - // $this->assertNotEmpty($response['body']['$id']); - // $this->assertEquals('Demo New', $response['body']['name']); - // $this->assertGreaterThan(-1, $response['body']['total']); - // $this->assertIsInt($response['body']['total']); - // $this->assertIsInt($response['body']['dateCreated']); - - // /** - // * Test for FAILURE - // */ - // $response = $this->client->call(Client::METHOD_PUT, '/teams/'.$response['body']['$id'], array_merge([ - // 'content-type' => 'application/json', - // 'x-appwrite-project' => $this->getProject()['$id'], - // ], $this->getHeaders()), [ - // ]); - - // $this->assertEquals(400, $response['headers']['status-code']); - - // return []; - // } - - // public function testDeleteTeam():array - // { - // /** - // * Test for SUCCESS - // */ - // $response = $this->client->call(Client::METHOD_POST, '/teams', array_merge([ - // 'content-type' => 'application/json', - // 'x-appwrite-project' => $this->getProject()['$id'], - // ], $this->getHeaders()), [ - // 'name' => 'Demo' - // ]); - - // $teamUid = $response['body']['$id']; - - // $this->assertEquals(201, $response['headers']['status-code']); - // $this->assertNotEmpty($response['body']['$id']); - // $this->assertEquals('Demo', $response['body']['name']); - // $this->assertGreaterThan(-1, $response['body']['total']); - // $this->assertIsInt($response['body']['total']); - // $this->assertIsInt($response['body']['dateCreated']); - - // $response = $this->client->call(Client::METHOD_DELETE, '/teams/'.$teamUid, array_merge([ - // 'content-type' => 'application/json', - // 'x-appwrite-project' => $this->getProject()['$id'], - // ], $this->getHeaders())); - - // $this->assertEquals(204, $response['headers']['status-code']); - // $this->assertEmpty($response['body']); - - // /** - // * Test for FAILURE - // */ - // $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(404, $response['headers']['status-code']); - - // return []; - // } + return $function; + } } diff --git a/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php b/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php index 8cb7f6f869..6ee5a1085d 100644 --- a/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php @@ -13,13 +13,11 @@ class FunctionsConsoleClientTest extends Scope { use ProjectCustom; use SideConsole; + use FunctionsBase; public function testCreateFunction(): array { - $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $function = $this->createFunction([ 'functionId' => ID::unique(), 'name' => 'Test', 'execute' => [Role::user($this->getUser()['$id'])->toString()], @@ -32,87 +30,69 @@ class FunctionsConsoleClientTest extends Scope 'schedule' => '0 0 1 1 *', 'timeout' => 10, ]); - + $functionId = $function['body']['$id']; $this->assertEquals(201, $function['headers']['status-code']); - $response = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $function2 = $this->createFunction([ 'functionId' => ID::unique(), 'name' => 'Test Failure', 'execute' => ['some-random-string'], 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', ]); - - $this->assertEquals(400, $response['headers']['status-code']); + $this->assertEquals(400, $function2['headers']['status-code']); return [ - 'functionId' => $function['body']['$id'] + 'functionId' => $functionId, ]; } /** * @depends testCreateFunction */ - public function testGetCollectionUsage(array $data) + public function testFunctionUsage(array $data) { - /** - * Test for FAILURE - */ - - $response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/usage', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'] - ], $this->getHeaders()), [ - 'range' => '232h' - ]); - - $this->assertEquals(400, $response['headers']['status-code']); - - $response = $this->client->call(Client::METHOD_GET, '/functions/randomFunctionId/usage', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'] - ], $this->getHeaders()), [ - 'range' => '24h' - ]); - - $this->assertEquals(404, $response['headers']['status-code']); - /** * Test for SUCCESS */ - - $response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/usage', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'] - ], $this->getHeaders()), [ + $usage = $this->getFunctionUsage($data['functionId'], [ 'range' => '24h' ]); + $this->assertEquals(200, $usage['headers']['status-code']); + $this->assertEquals(19, count($usage['body'])); + $this->assertEquals('24h', $usage['body']['range']); + $this->assertIsNumeric($usage['body']['deploymentsTotal']); + $this->assertIsNumeric($usage['body']['deploymentsStorageTotal']); + $this->assertIsNumeric($usage['body']['buildsTotal']); + $this->assertIsNumeric($usage['body']['buildsStorageTotal']); + $this->assertIsNumeric($usage['body']['buildsTimeTotal']); + $this->assertIsNumeric($usage['body']['buildsMbSecondsTotal']); + $this->assertIsNumeric($usage['body']['executionsTotal']); + $this->assertIsNumeric($usage['body']['executionsTimeTotal']); + $this->assertIsNumeric($usage['body']['executionsMbSecondsTotal']); + $this->assertIsArray($usage['body']['deployments']); + $this->assertIsArray($usage['body']['deploymentsStorage']); + $this->assertIsArray($usage['body']['builds']); + $this->assertIsArray($usage['body']['buildsTime']); + $this->assertIsArray($usage['body']['buildsStorage']); + $this->assertIsArray($usage['body']['buildsTime']); + $this->assertIsArray($usage['body']['buildsMbSeconds']); + $this->assertIsArray($usage['body']['executions']); + $this->assertIsArray($usage['body']['executionsTime']); + $this->assertIsArray($usage['body']['executionsMbSeconds']); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(19, count($response['body'])); - $this->assertEquals('24h', $response['body']['range']); - $this->assertIsNumeric($response['body']['deploymentsTotal']); - $this->assertIsNumeric($response['body']['deploymentsStorageTotal']); - $this->assertIsNumeric($response['body']['buildsTotal']); - $this->assertIsNumeric($response['body']['buildsStorageTotal']); - $this->assertIsNumeric($response['body']['buildsTimeTotal']); - $this->assertIsNumeric($response['body']['buildsMbSecondsTotal']); - $this->assertIsNumeric($response['body']['executionsTotal']); - $this->assertIsNumeric($response['body']['executionsTimeTotal']); - $this->assertIsNumeric($response['body']['executionsMbSecondsTotal']); - $this->assertIsArray($response['body']['deployments']); - $this->assertIsArray($response['body']['deploymentsStorage']); - $this->assertIsArray($response['body']['builds']); - $this->assertIsArray($response['body']['buildsTime']); - $this->assertIsArray($response['body']['buildsStorage']); - $this->assertIsArray($response['body']['buildsTime']); - $this->assertIsArray($response['body']['buildsMbSeconds']); - $this->assertIsArray($response['body']['executions']); - $this->assertIsArray($response['body']['executionsTime']); - $this->assertIsArray($response['body']['executionsMbSeconds']); + /** + * Test for FAILURE + */ + $usage = $this->getFunctionUsage($data['functionId'], [ + 'range' => '232h' + ]); + $this->assertEquals(400, $usage['headers']['status-code']); + + $usage = $this->getFunctionUsage('randomFunctionId', [ + 'range' => '24h' + ]); + $this->assertEquals(404, $usage['headers']['status-code']); } /** @@ -123,31 +103,40 @@ class FunctionsConsoleClientTest extends Scope /** * Test for SUCCESS */ - - $response = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/variables', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'key' => 'APP_TEST', - 'value' => 'TESTINGVALUE' - ]); - - $this->assertEquals(201, $response['headers']['status-code']); - $variableId = $response['body']['$id']; + $variable = $this->createVariable( + $data['functionId'], + 'APP_TEST', + 'TESTINGVALUE' + ); + $variableId = $variable['body']['$id']; + $this->assertEquals(201, $variable['headers']['status-code']); /** * Test for FAILURE */ + // Test for duplicate key + $variable = $this->createVariable( + $data['functionId'], + 'APP_TEST', + 'ANOTHER_TESTINGVALUE' + ); + $this->assertEquals(409, $variable['headers']['status-code']); - $response = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/variables', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'key' => 'APP_TEST', - 'value' => 'ANOTHER_TESTINGVALUE' - ]); + // Test for invalid key + $variable = $this->createVariable( + $data['functionId'], + str_repeat("A", 256), + 'TESTINGVALUE' + ); + $this->assertEquals(400, $variable['headers']['status-code']); - $this->assertEquals(409, $response['headers']['status-code']); + // Test for invalid value + $variable = $this->createVariable( + $data['functionId'], + 'LONGKEY', + str_repeat("#", 8193) + ); + $this->assertEquals(400, $variable['headers']['status-code']); return array_merge( $data, @@ -155,28 +144,6 @@ class FunctionsConsoleClientTest extends Scope 'variableId' => $variableId ] ); - - $longKey = str_repeat("A", 256); - $response = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/variables', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'key' => $longKey, - 'value' => 'TESTINGVALUE' - ]); - - $this->assertEquals(400, $response['headers']['status-code']); - - $longValue = str_repeat("#", 8193); - $response = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/variables', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'key' => 'LONGKEY', - 'value' => $longValue - ]); - - $this->assertEquals(400, $response['headers']['status-code']); } /** diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index f5f15a0fc6..872249085e 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -2,16 +2,12 @@ namespace Tests\E2E\Services\Functions; -use CURLFile; use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideClient; -use Utopia\Config\Config; -use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; -use Utopia\Database\Query; class FunctionsCustomClientTest extends Scope { @@ -19,15 +15,12 @@ class FunctionsCustomClientTest extends Scope use ProjectCustom; use SideClient; - public function testCreate(): array + public function testCreate() { /** - * Test for SUCCESS + * Test for FAILURE */ - $response1 = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $function = $this->createFunction([ 'functionId' => ID::unique(), 'name' => 'Test', 'events' => [ @@ -37,22 +30,15 @@ class FunctionsCustomClientTest extends Scope 'schedule' => '0 0 1 1 *', 'timeout' => 10, ]); - - $this->assertEquals(401, $response1['headers']['status-code']); - - return []; + $this->assertEquals(401, $function['headers']['status-code']); } - public function testCreateExecution(): array + public function testCreateExecution() { /** * Test for SUCCESS */ - $function = $this->client->call(Client::METHOD_POST, '/functions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], [ + $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test', 'execute' => [Role::user($this->getUser()['$id'])->toString()], @@ -62,121 +48,41 @@ class FunctionsCustomClientTest extends Scope 'users.*.create', 'users.*.delete', ], - 'schedule' => '* * * * *', // execute every minute + 'schedule' => '* * * * *', // Execute every minute 'timeout' => 10, ]); - - $this->assertEquals(201, $function['headers']['status-code']); - - /** Create Variables */ - $variable = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/variables', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], [ - 'key' => 'funcKey1', - 'value' => 'funcValue1', - ]); - - $variable2 = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/variables', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], [ - 'key' => 'funcKey2', - 'value' => 'funcValue2', - ]); - - $variable3 = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/variables', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], [ - 'key' => 'funcKey3', - 'value' => 'funcValue3', - ]); - - $this->assertEquals(201, $variable['headers']['status-code']); - $this->assertEquals(201, $variable2['headers']['status-code']); - $this->assertEquals(201, $variable3['headers']['status-code']); - - $folder = 'php'; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; - $this->packageCode($folder); - - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/deployments', [ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], [ + $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', - 'code' => new CURLFile($code, 'application/x-gzip', \basename($code)), + 'code' => $this->packageFunction('php'), 'activate' => true ]); - $deploymentId = $deployment['body']['$id'] ?? ''; - - $this->assertEquals(202, $deployment['headers']['status-code']); - - $this->assertDeployment($function['body']['$id'], $deploymentId); - - $function = $this->client->call(Client::METHOD_PATCH, '/functions/' . $function['body']['$id'] . '/deployments/' . $deploymentId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(200, $function['headers']['status-code']); - - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/executions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], [ - 'async' => true, + $execution = $this->createExecution($functionId, [ + 'async' => false, ]); - $this->assertEquals(401, $execution['headers']['status-code']); - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $execution = $this->createExecution($functionId, [ 'async' => true, ]); - $this->assertEquals(202, $execution['headers']['status-code']); - $this->assertEventually(function () use ($function) { - $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/executions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]); - + $this->assertEventually(function () use ($functionId) { + $executions = $this->listExecutions($functionId); $this->assertEquals(200, $executions['headers']['status-code']); $this->assertCount(2, $executions['body']['executions']); - // Check if the scheduled execution has completed - $scheduledExecution = $executions['body']['executions'][1]; - $this->assertEquals('schedule', $scheduledExecution['trigger']); - $this->assertEquals('completed', $scheduledExecution['status']); - $this->assertEquals(200, $scheduledExecution['responseStatusCode']); - $this->assertEquals('', $scheduledExecution['responseBody']); - $this->assertNotEmpty($scheduledExecution['logs']); - $this->assertNotEmpty($scheduledExecution['errors']); - $this->assertGreaterThan(0, $scheduledExecution['duration']); - }, 120000, 2000); + $asyncExecution = $executions['body']['executions'][1]; + $this->assertEquals('schedule', $asyncExecution['trigger']); + $this->assertEquals('completed', $asyncExecution['status']); + $this->assertEquals(200, $asyncExecution['responseStatusCode']); + $this->assertEquals('', $asyncExecution['responseBody']); + $this->assertNotEmpty($asyncExecution['logs']); + $this->assertNotEmpty($asyncExecution['errors']); + $this->assertGreaterThan(0, $asyncExecution['duration']); + }, 100000, 250); - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $function['body']['$id'], [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); - - return []; + $this->cleanupFunction($functionId); } public function testCreateScheduledExecution(): void @@ -184,11 +90,7 @@ class FunctionsCustomClientTest extends Scope /** * Test for SUCCESS */ - $function = $this->client->call(Client::METHOD_POST, '/functions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], [ + $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test', 'execute' => [Role::user($this->getUser()['$id'])->toString()], @@ -196,38 +98,20 @@ class FunctionsCustomClientTest extends Scope 'entrypoint' => 'index.php', 'timeout' => 10, ]); - - $this->assertEquals(201, $function['headers']['status-code']); - - $folder = 'php'; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; - $this->packageCode($folder); - - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/deployments', [ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], [ + $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', - 'code' => new CURLFile($code, 'application/x-gzip', \basename($code)), + 'code' => $this->packageFunction('php'), 'activate' => true ]); - $deploymentId = $deployment['body']['$id'] ?? ''; - $this->assertEquals(202, $deployment['headers']['status-code']); - - $this->assertDeployment($function['body']['$id'], $deploymentId, true); // Schedule execution for the future \date_default_timezone_set('UTC'); - $futureTime = (new \DateTime())->add(new \DateInterval('PT2M')); + $futureTime = (new \DateTime())->add(new \DateInterval('PT30S')); // 30 seconds from now $futureTime->setTime($futureTime->format('H'), $futureTime->format('i'), 0, 0); - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $execution = $this->createExecution($functionId, [ 'async' => true, - 'scheduledAt' => $futureTime->format(\DateTime::ATOM), + 'scheduledAt' => $futureTime->format(\DateTime::ATOM), 'path' => '/custom-path', 'method' => 'PATCH', 'body' => 'custom-body', @@ -235,101 +119,60 @@ class FunctionsCustomClientTest extends Scope 'x-custom-header' => 'custom-value' ] ]); - + $executionId = $execution['body']['$id']; $this->assertEquals(202, $execution['headers']['status-code']); $this->assertEquals('scheduled', $execution['body']['status']); $this->assertEquals('PATCH', $execution['body']['requestMethod']); $this->assertEquals('/custom-path', $execution['body']['requestPath']); $this->assertCount(0, $execution['body']['requestHeaders']); - $executionId = $execution['body']['$id']; - - $this->assertEventually(function () use ($function, $executionId) { - $execution = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/executions/' . $executionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]); - + $this->assertEventually(function () use ($functionId, $executionId) { + $execution = $this->getExecution($functionId, $executionId); $this->assertEquals('completed', $execution['body']['status']); - }, 150000, 2000); // Timeout after 150 seconds, wait 2 seconds between retries - - // After ensuring the execution is completed, fetch it again for assertions - $execution = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/executions/' . $executionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]); - - $this->assertEquals(200, $execution['headers']['status-code']); - $this->assertEquals(200, $execution['body']['responseStatusCode']); - $this->assertEquals('completed', $execution['body']['status']); - $this->assertEquals('/custom-path', $execution['body']['requestPath']); - $this->assertEquals('PATCH', $execution['body']['requestMethod']); - $this->assertStringContainsString('body-is-custom-body', $execution['body']['logs']); - $this->assertStringContainsString('custom-header-is-custom-value', $execution['body']['logs']); - $this->assertStringContainsString('method-is-patch', $execution['body']['logs']); - $this->assertStringContainsString('path-is-/custom-path', $execution['body']['logs']); - $this->assertStringContainsString('user-is-' . $this->getUser()['$id'], $execution['body']['logs']); - $this->assertStringContainsString('jwt-is-valid', $execution['body']['logs']); - $this->assertGreaterThan(0, $execution['body']['duration']); + $this->assertEquals(200, $execution['headers']['status-code']); + $this->assertEquals(200, $execution['body']['responseStatusCode']); + $this->assertEquals('completed', $execution['body']['status']); + $this->assertEquals('/custom-path', $execution['body']['requestPath']); + $this->assertEquals('PATCH', $execution['body']['requestMethod']); + $this->assertStringContainsString('body-is-custom-body', $execution['body']['logs']); + $this->assertStringContainsString('custom-header-is-custom-value', $execution['body']['logs']); + $this->assertStringContainsString('method-is-patch', $execution['body']['logs']); + $this->assertStringContainsString('path-is-/custom-path', $execution['body']['logs']); + $this->assertStringContainsString('user-is-' . $this->getUser()['$id'], $execution['body']['logs']); + $this->assertStringContainsString('jwt-is-valid', $execution['body']['logs']); + $this->assertGreaterThan(0, $execution['body']['duration']); + }, 40000, 2500); /* Test for FAILURE */ - // Schedule synchronous execution - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $execution = $this->createExecution($functionId, [ 'async' => false, - 'scheduledAt' => $futureTime->format(\DateTime::ATOM), + 'scheduledAt' => $futureTime->format(\DateTime::ATOM), ]); - $this->assertEquals(400, $execution['headers']['status-code']); // Execution with seconds precision - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $execution = $this->createExecution($functionId, [ 'async' => true, 'scheduledAt' => (new \DateTime("2100-12-08 16:12:02"))->format(\DateTime::ATOM) ]); - $this->assertEquals(400, $execution['headers']['status-code']); // Execution with milliseconds precision - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $execution = $this->createExecution($functionId, [ 'async' => true, 'scheduledAt' => (new \DateTime("2100-12-08 16:12:02.255"))->format(\DateTime::ATOM) ]); - $this->assertEquals(400, $execution['headers']['status-code']); // Execution too soon - $futureTime = (new \DateTime())->add(new \DateInterval('PT1M')); - $futureTime->setTime($futureTime->format('H'), $futureTime->format('i'), 0, 0); - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $execution = $this->createExecution($functionId, [ 'async' => true, - 'scheduledAt' => $futureTime->format(\DateTime::ATOM), + 'scheduledAt' => (new \DateTime())->add(new \DateInterval('PT1S'))->format(\DateTime::ATOM) ]); - $this->assertEquals(400, $execution['headers']['status-code']); - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $function['body']['$id'], [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); + $this->cleanupFunction($functionId, $executionId); } public function testCreateCustomExecution(): array @@ -337,14 +180,7 @@ class FunctionsCustomClientTest extends Scope /** * Test for SUCCESS */ - $projectId = $this->getProject()['$id']; - $apikey = $this->getProject()['apiKey']; - - $function = $this->client->call(Client::METHOD_POST, '/functions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ + $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test', 'execute' => [Role::any()->toString()], @@ -352,77 +188,16 @@ class FunctionsCustomClientTest extends Scope 'entrypoint' => 'index.php', 'timeout' => 10, ]); - - $functionId = $function['body']['$id'] ?? ''; - - $this->assertEquals(201, $function['headers']['status-code']); - - /** Create Variables */ - $variable = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ - 'key' => 'funcKey1', - 'value' => 'funcValue1', - ]); - - $variable2 = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ - 'key' => 'funcKey2', - 'value' => 'funcValue2', - ]); - - $variable3 = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ - 'key' => 'funcKey3', - 'value' => 'funcValue3', - ]); - - $this->assertEquals(201, $variable['headers']['status-code']); - $this->assertEquals(201, $variable2['headers']['status-code']); - $this->assertEquals(201, $variable3['headers']['status-code']); - - $folder = 'php-fn'; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; - $this->packageCode($folder); - - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', [ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ + $deploymentId = $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', - 'code' => new CURLFile($code, 'application/x-gzip', \basename($code)), //different tarball names intentional + 'code' => $this->packageFunction('php-fn'), 'activate' => true ]); - $deploymentId = $deployment['body']['$id'] ?? ''; - - $this->assertDeployment($function['body']['$id'], $deploymentId); - - $function = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], []); - - $this->assertEquals(200, $function['headers']['status-code']); - - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - ], $this->getHeaders()), [ + $execution = $this->createExecution($functionId, [ 'body' => 'foobar', 'async' => false ]); - $output = json_decode($execution['body']['responseBody'], true); $this->assertEquals(201, $execution['headers']['status-code']); $this->assertEquals(200, $execution['body']['responseStatusCode']); @@ -440,30 +215,20 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals('foobar', $output['APPWRITE_FUNCTION_DATA']); $this->assertEquals($this->getUser()['$id'], $output['APPWRITE_FUNCTION_USER_ID']); $this->assertNotEmpty($output['APPWRITE_FUNCTION_JWT']); - $this->assertEquals($projectId, $output['APPWRITE_FUNCTION_PROJECT_ID']); + $this->assertEquals($this->getProject()['$id'], $output['APPWRITE_FUNCTION_PROJECT_ID']); - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - ], $this->getHeaders()), [ + $execution = $this->createExecution($functionId, [ 'body' => 'foobar', 'async' => true ]); - + $executionId = $execution['body']['$id']; $this->assertEquals(202, $execution['headers']['status-code']); - $executionId = $execution['body']['$id'] ?? ''; - - $this->assertEventually(function () use ($functionId, $executionId, $projectId, $apikey) { - $execution = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ]); - + $this->assertEventually(function () use ($functionId, $executionId) { + $execution = $this->getExecution($functionId, $executionId); $this->assertEquals('completed', $execution['body']['status']); $this->assertEquals(200, $execution['body']['responseStatusCode']); - }, 30000, 1000); + }, 10000, 250); return [ 'functionId' => $functionId @@ -475,14 +240,7 @@ class FunctionsCustomClientTest extends Scope /** * Test for SUCCESS */ - $projectId = $this->getProject()['$id']; - $apikey = $this->getProject()['apiKey']; - - $function = $this->client->call(Client::METHOD_POST, '/functions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ + $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test', 'execute' => [Role::any()->toString()], @@ -495,56 +253,25 @@ class FunctionsCustomClientTest extends Scope ], 'timeout' => 10, ]); - - $functionId = $function['body']['$id'] ?? ''; - - $this->assertEquals(201, $function['headers']['status-code']); - - $folder = 'php-fn'; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; - $this->packageCode($folder); - - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', [ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ + $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', - 'code' => new CURLFile($code, 'application/x-gzip', \basename($code)), //different tarball names intentional + 'code' => $this->packageFunction('php-fn'), 'activate' => true ]); - $deploymentId = $deployment['body']['$id'] ?? ''; - - $this->assertDeployment($function['body']['$id'], $deploymentId); - - // Why do we have to do this? - $function = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], []); - - $this->assertEquals(200, $function['headers']['status-code']); - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', [ 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, + 'x-appwrite-project' => $this->getProject()['$id'], ], [ 'data' => 'foobar', 'async' => true, ]); - $this->assertEquals(202, $execution['headers']['status-code']); } - public function testCreateExecutionNoDeployment(): array + public function testCreateExecutionNoDeployment() { - $function = $this->client->call(Client::METHOD_POST, '/functions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], [ + $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test', 'execute' => [], @@ -553,146 +280,18 @@ class FunctionsCustomClientTest extends Scope 'timeout' => 10, ]); - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/executions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], [ - 'async' => true, + $execution = $this->createExecution($functionId, [ + 'async' => true ]); - $this->assertEquals(404, $execution['headers']['status-code']); - - return []; } - /** - * @depends testCreateCustomExecution - */ - public function testListExecutions(array $data) - { - $functionId = $data['functionId']; - $projectId = $this->getProject()['$id']; - $apikey = $this->getProject()['apiKey']; - - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - ], $this->getHeaders()), [ - 'data' => 'foobar' - ]); - - $this->assertEquals(201, $execution['headers']['status-code']); - - $base = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ]); - - $this->assertEquals(200, $base['headers']['status-code']); - $this->assertCount(3, $base['body']['executions']); - $this->assertEquals('completed', $base['body']['executions'][0]['status']); - $this->assertEquals('completed', $base['body']['executions'][1]['status']); - - $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ - 'queries' => [ - Query::limit(1)->toString(), - ], - ]); - - $this->assertEquals(200, $executions['headers']['status-code']); - $this->assertCount(1, $executions['body']['executions']); - - $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ - 'queries' => [ - Query::offset(1)->toString(), - ], - ]); - - $this->assertEquals(200, $executions['headers']['status-code']); - $this->assertCount(2, $executions['body']['executions']); - - $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ - 'queries' => [ - Query::equal('status', ['completed'])->toString(), - ], - ]); - - $this->assertEquals(200, $executions['headers']['status-code']); - $this->assertCount(3, $executions['body']['executions']); - - $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ - 'queries' => [ - Query::equal('status', ['failed'])->toString(), - ], - ]); - - $this->assertEquals(200, $executions['headers']['status-code']); - $this->assertCount(0, $executions['body']['executions']); - - $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ - 'queries' => [ - Query::cursorAfter(new Document(['$id' => $base['body']['executions'][0]['$id']]))->toString(), - ], - ]); - - $this->assertCount(2, $executions['body']['executions']); - $this->assertEquals($base['body']['executions'][1]['$id'], $executions['body']['executions'][0]['$id']); - - $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ - 'queries' => [ - Query::cursorBefore(new Document(['$id' => $base['body']['executions'][1]['$id']]))->toString(), - ], - ]); - - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); - } - - public function testSynchronousExecution(): array + public function testSynchronousExecution() { /** * Test for SUCCESS */ - - $projectId = $this->getProject()['$id']; - $apikey = $this->getProject()['apiKey']; - - $function = $this->client->call(Client::METHOD_POST, '/functions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ + $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test', 'execute' => [Role::any()->toString()], @@ -700,79 +299,16 @@ class FunctionsCustomClientTest extends Scope 'entrypoint' => 'index.php', 'timeout' => 10, ]); - - $functionId = $function['body']['$id'] ?? ''; - - $this->assertEquals(201, $function['headers']['status-code']); - - /** Create Variables */ - $variable = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ - 'key' => 'funcKey1', - 'value' => 'funcValue1', - ]); - - $variable2 = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ - 'key' => 'funcKey2', - 'value' => 'funcValue2', - ]); - - $variable3 = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ - 'key' => 'funcKey3', - 'value' => 'funcValue3', - ]); - - $this->assertEquals(201, $variable['headers']['status-code']); - $this->assertEquals(201, $variable2['headers']['status-code']); - $this->assertEquals(201, $variable3['headers']['status-code']); - - $folder = 'php-fn'; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; - $this->packageCode($folder); - - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', [ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ + $deploymentId = $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', - 'code' => new CURLFile($code, 'application/x-gzip', \basename($code)), //different tarball names intentional + 'code' => $this->packageFunction('php-fn'), 'activate' => true ]); - $deploymentId = $deployment['body']['$id'] ?? ''; - - $this->assertEquals(202, $deployment['headers']['status-code']); - - $this->assertDeployment($function['body']['$id'], $deploymentId); - - $function = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ]); - - $this->assertEquals(200, $function['headers']['status-code']); - - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - ], $this->getHeaders()), [ + $execution = $this->createExecution($functionId, [ 'body' => 'foobar', // Testing default value, should be 'async' => false ]); - $output = json_decode($execution['body']['responseBody'], true); $this->assertEquals(201, $execution['headers']['status-code']); $this->assertEquals('completed', $execution['body']['status']); @@ -789,67 +325,29 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals('foobar', $output['APPWRITE_FUNCTION_DATA']); $this->assertEquals($this->getUser()['$id'], $output['APPWRITE_FUNCTION_USER_ID']); $this->assertNotEmpty($output['APPWRITE_FUNCTION_JWT']); - $this->assertEquals($projectId, $output['APPWRITE_FUNCTION_PROJECT_ID']); + $this->assertEquals($this->getProject()['$id'], $output['APPWRITE_FUNCTION_PROJECT_ID']); // Client should never see logs and errors $this->assertEmpty($execution['body']['logs']); $this->assertEmpty($execution['body']['errors']); - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); - - return []; + $this->cleanupFunction($functionId); } - public function testNonOverrideOfHeaders(): array + public function testNonOverrideOfHeaders() { - /** - * Test for SUCCESS - */ - $projectId = $this->getProject()['$id']; - $apikey = $this->getProject()['apiKey']; - - $function = $this->client->call(Client::METHOD_POST, '/functions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ + $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test', 'execute' => [Role::any()->toString()], 'runtime' => 'node-18.0', 'entrypoint' => 'index.js' ]); - - $functionId = $function['body']['$id'] ?? ''; - - $this->assertEquals(201, $function['headers']['status-code']); - - $folder = 'node'; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; - $this->packageCode($folder); - - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', [ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $projectId, - 'x-appwrite-key' => $apikey, - ], [ + $this->setupDeployment($functionId, [ 'entrypoint' => 'index.js', - 'code' => new CURLFile($code, 'application/x-gzip', \basename($code)), //different tarball names intentional + 'code' => $this->packageFunction('node'), 'activate' => true ]); - $deploymentId = $deployment['body']['$id'] ?? ''; - - $this->assertEquals(202, $deployment['headers']['status-code']); - - $this->assertDeployment($function['body']['$id'], $deploymentId); - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -859,23 +357,13 @@ class FunctionsCustomClientTest extends Scope 'x-appwrite-user-id' => "OVERRIDDEN", 'x-appwrite-user-jwt' => "OVERRIDDEN", ]); - $output = json_decode($execution['body']['responseBody'], true); $this->assertNotEquals('OVERRIDDEN', $output['APPWRITE_FUNCTION_JWT']); $this->assertNotEquals('OVERRIDDEN', $output['APPWRITE_FUNCTION_EVENT']); $this->assertNotEquals('OVERRIDDEN', $output['APPWRITE_FUNCTION_TRIGGER']); $this->assertNotEquals('OVERRIDDEN', $output['APPWRITE_FUNCTION_USER_ID']); - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); - - return []; + $this->cleanupFunction($functionId); } public function testListTemplates() @@ -883,50 +371,43 @@ class FunctionsCustomClientTest extends Scope /** * Test for SUCCESS */ - $expectedTemplates = array_slice(Config::getParam('function-templates', []), 0, 25); + // List all templates $templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([ 'content-type' => 'application/json', ], $this->getHeaders())); - $this->assertEquals(200, $templates['headers']['status-code']); $this->assertGreaterThan(0, $templates['body']['total']); $this->assertIsArray($templates['body']['templates']); - - $this->assertArrayHasKey('runtimes', $templates['body']['templates'][0]); - $this->assertArrayHasKey('useCases', $templates['body']['templates'][0]); - for ($i = 0; $i < 25; $i++) { - $this->assertEquals($expectedTemplates[$i]['name'], $templates['body']['templates'][$i]['name']); - $this->assertEquals($expectedTemplates[$i]['id'], $templates['body']['templates'][$i]['id']); - $this->assertEquals($expectedTemplates[$i]['icon'], $templates['body']['templates'][$i]['icon']); - $this->assertEquals($expectedTemplates[$i]['tagline'], $templates['body']['templates'][$i]['tagline']); - $this->assertEquals($expectedTemplates[$i]['useCases'], $templates['body']['templates'][$i]['useCases']); - $this->assertEquals($expectedTemplates[$i]['vcsProvider'], $templates['body']['templates'][$i]['vcsProvider']); - $this->assertEquals($expectedTemplates[$i]['runtimes'], $templates['body']['templates'][$i]['runtimes']); - $this->assertEquals($expectedTemplates[$i]['variables'], $templates['body']['templates'][$i]['variables']); - if (array_key_exists('scopes', $expectedTemplates[$i])) { - $this->assertEquals($expectedTemplates[$i]['scopes'], $templates['body']['templates'][$i]['scopes']); - } + foreach ($templates['body']['templates'] as $template) { + $this->assertArrayHasKey('name', $template); + $this->assertArrayHasKey('id', $template); + $this->assertArrayHasKey('icon', $template); + $this->assertArrayHasKey('tagline', $template); + $this->assertArrayHasKey('useCases', $template); + $this->assertArrayHasKey('vcsProvider', $template); + $this->assertArrayHasKey('runtimes', $template); + $this->assertArrayHasKey('variables', $template); + $this->assertArrayHasKey('scopes', $template); } - $templates_offset = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([ + // List templates with pagination + $templatesOffset = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([ 'content-type' => 'application/json', ], $this->getHeaders()), [ 'limit' => 1, 'offset' => 2 ]); + $this->assertEquals(200, $templatesOffset['headers']['status-code']); + $this->assertEquals(1, $templatesOffset['body']['total']); + $this->assertEquals($templates['body']['templates'][2]['id'], $templatesOffset['body']['templates'][0]['id']); - $this->assertEquals(200, $templates_offset['headers']['status-code']); - $this->assertEquals(1, $templates_offset['body']['total']); - // assert that offset works as expected - $this->assertEquals($templates['body']['templates'][2]['id'], $templates_offset['body']['templates'][0]['id']); - + // List templates with filters $templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([ 'content-type' => 'application/json', ], $this->getHeaders()), [ 'useCases' => ['starter', 'ai'], 'runtimes' => ['bun-1.0', 'dart-2.16'] ]); - $this->assertEquals(200, $templates['headers']['status-code']); $this->assertGreaterThanOrEqual(3, $templates['body']['total']); $this->assertIsArray($templates['body']['templates']); @@ -936,6 +417,7 @@ class FunctionsCustomClientTest extends Scope $this->assertArrayHasKey('runtimes', $templates['body']['templates'][0]); $this->assertContains('bun-1.0', array_column($templates['body']['templates'][0]['runtimes'], 'name')); + // List templates with pagination and filters $templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -958,16 +440,16 @@ class FunctionsCustomClientTest extends Scope /** * Test for FAILURE */ + // List templates with invalid limit $templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([ 'content-type' => 'application/json', ], $this->getHeaders()), [ 'limit' => 5001, 'offset' => 10, ]); - $this->assertEquals(400, $templates['headers']['status-code']); - $this->assertEquals('Invalid `limit` param: Value must be a valid range between 1 and 5,000', $templates['body']['message']); + // List templates with invalid offset $templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -975,9 +457,7 @@ class FunctionsCustomClientTest extends Scope 'limit' => 5, 'offset' => 5001, ]); - $this->assertEquals(400, $templates['headers']['status-code']); - $this->assertEquals('Invalid `offset` param: Value must be a valid range between 0 and 5,000', $templates['body']['message']); } public function testGetTemplate() @@ -985,10 +465,7 @@ class FunctionsCustomClientTest extends Scope /** * Test for SUCCESS */ - $template = $this->client->call(Client::METHOD_GET, '/functions/templates/query-neo4j-auradb', array_merge([ - 'content-type' => 'application/json', - ], $this->getHeaders()), []); - + $template = $this->getTemplate('query-neo4j-auradb'); $this->assertEquals(200, $template['headers']['status-code']); $this->assertIsArray($template['body']); $this->assertEquals('query-neo4j-auradb', $template['body']['id']); @@ -997,15 +474,13 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals('Graph database with focus on relations between data.', $template['body']['tagline']); $this->assertEquals(['databases'], $template['body']['useCases']); $this->assertEquals('github', $template['body']['vcsProvider']); + $this->assertIsArray($template['body']['runtimes']); + $this->assertIsArray($template['body']['scopes']); /** * Test for FAILURE */ - $template = $this->client->call(Client::METHOD_GET, '/functions/templates/invalid-template-id', array_merge([ - 'content-type' => 'application/json', - ], $this->getHeaders()), []); - + $template = $this->getTemplate('invalid-template-id'); $this->assertEquals(404, $template['headers']['status-code']); - $this->assertEquals('Function Template with the requested ID could not be found.', $template['body']['message']); } } diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 193e99d89e..8fe4aedca7 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -388,12 +388,9 @@ class FunctionsCustomServerTest extends Scope public function testCreateDeploymentFromCLI() { - $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $function = $this->createFunction([ 'functionId' => ID::unique(), - 'name' => 'Test', + 'name' => 'test-cli-deployment', 'execute' => [Role::user($this->getUser()['$id'])->toString()], 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', @@ -401,210 +398,127 @@ class FunctionsCustomServerTest extends Scope 'users.*.create', 'users.*.delete', ], - 'schedule' => '0 0 1 1 *', + 'schedule' => '0 0 1 1 *', // Once a year 'timeout' => 10, ]); + $functionId = $function['body']['$id']; $this->assertEquals(201, $function['headers']['status-code']); - $functionId = $function['body']['$id']; - - $folder = 'php'; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; - $this->packageCode($folder); - - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/deployments', [ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - 'x-sdk-language' => 'cli', - ], [ + $deployment = $this->createDeployment($functionId, [ 'entrypoint' => 'index.php', - 'code' => new CURLFile($code, 'application/x-gzip', \basename($code)), + 'code' => $this->packageFunction('php'), 'activate' => true - ]); - - $deploymentId = $deployment['body']['$id'] ?? ''; + ], cli: true); + $deploymentId = $deployment['body']['$id']; $this->assertEquals(202, $deployment['headers']['status-code']); + $this->assertDeploymentStatus($functionId, $deploymentId, 'ready'); - $this->assertDeployment($function['body']['$id'], $deploymentId); - - $functionDetails = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(200, $functionDetails['headers']['status-code']); - $this->assertEquals('cli', $functionDetails['body']['type']); + $deployment = $this->getDeployment($functionId, $deploymentId); + $this->assertEquals(200, $deployment['headers']['status-code']); + $this->assertEquals('cli', $deployment['body']['type']); } public function testCreateDeploymentFromTemplate() { - $runtimeName = 'php-8.0'; - // Fetch starter template (used to create function later) - $template = $this->client->call(Client::METHOD_GET, '/functions/templates/starter', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + $starterTemplate = $this->getTemplate('starter'); + $this->assertEquals(200, $starterTemplate['headers']['status-code']); - $this->assertEquals(200, $template['headers']['status-code']); + $phpRuntime = array_values(array_filter($starterTemplate['body']['runtimes'], function ($runtime) { + return $runtime['name'] === 'php-8.0'; + }))[0]; - $entrypoint = null; - $rootDirectory = null; - $commands = null; - foreach ($template['body']['runtimes'] as $runtime) { - if ($runtime["name"] !== $runtimeName) { - continue; - } - - $entrypoint = $runtime["entrypoint"]; - $rootDirectory = $runtime["providerRootDirectory"]; - $commands = $runtime["commands"]; - break; - } - - $this->assertNotNull($entrypoint); - - /** - * If below test ever starts failing, it means temaplate used in - * this test now has some variables. This test currently doesnt test variables. - * Remove bellow assertion and update test to crete variable, - * and ensure variable works as expected in execution. - */ - $this->assertEmpty($template['body']['variables']); - - // Create function using settings from template. - // Deployment is automatically created from template inside endpoint - $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], $this->getHeaders()), [ - 'functionId' => ID::unique(), - 'name' => $template['body']['name'], - 'runtime' => $runtimeName, - 'execute' => $template['body']['permissions'], - 'entrypoint' => $entrypoint, - 'events' => $template['body']['events'], - 'schedule' => $template['body']['cron'], - 'timeout' => $template['body']['timeout'], - 'commands' => $commands, - 'scopes' => $template['body']['scopes'], - 'templateRepository' => $template['body']['providerRepositoryId'], - 'templateOwner' => $template['body']['providerOwner'], - 'templateRootDirectory' => $rootDirectory, - 'templateVersion' => $template['body']['providerVersion'], - ]); + // If this fails, the template has variables, and this test needs to be updated + $this->assertEmpty($starterTemplate['body']['variables']); + $function = $this->createFunction( + [ + 'functionId' => ID::unique(), + 'name' => $starterTemplate['body']['name'], + 'runtime' => 'php-8.0', + 'execute' => $starterTemplate['body']['permissions'], + 'entrypoint' => $phpRuntime['entrypoint'], + 'events' => $starterTemplate['body']['events'], + 'schedule' => $starterTemplate['body']['cron'], + 'timeout' => $starterTemplate['body']['timeout'], + 'commands' => $phpRuntime['commands'], + 'scopes' => $starterTemplate['body']['scopes'], + 'templateRepository' => $starterTemplate['body']['providerRepositoryId'], + 'templateOwner' => $starterTemplate['body']['providerOwner'], + 'templateRootDirectory' => $phpRuntime['providerRootDirectory'], + 'templateVersion' => $starterTemplate['body']['providerVersion'], + ] + ); + $functionId = $function['body']['$id']; $this->assertEquals(201, $function['headers']['status-code']); $this->assertNotEmpty($function['body']['$id']); - $functionId = $function['body']['$id']; - - // List deployments so we can await deployment build - $deployments = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - + $deployments = $this->listDeployments($functionId); $this->assertEquals(200, $deployments['headers']['status-code']); $this->assertEquals(1, $deployments['body']['total']); - $this->assertNotEmpty($deployments['body']['deployments'][0]['$id']); - $this->assertEquals(0, $deployments['body']['deployments'][0]['size']); - $deploymentId = $deployments['body']['deployments'][0]['$id']; + $lastDeployment = $deployments['body']['deployments'][0]; + $this->assertNotEmpty($lastDeployment['$id']); + $this->assertEquals(0, $lastDeployment['size']); - // Wait for deployment build to finish - // Deployment is automatically activated - $this->assertDeployment($function['body']['$id'], $deploymentId); + $deploymentId = $lastDeployment['$id']; + $this->assertDeploymentStatus($function['body']['$id'], $deploymentId, 'ready'); - $deployments = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - $this->assertGreaterThan(0, $deployments['body']['size']); - - $function = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], $this->getHeaders()), []); + $deployment = $this->getDeployment($function['body']['$id'], $deploymentId); + $this->assertGreaterThan(0, $deployment['body']['size']); + $function = $this->getFunction($functionId); $this->assertEquals(200, $function['headers']['status-code']); $this->assertEquals($deploymentId, $function['body']['deployment']); - // Execute function to ensure starter code is used - // Also tests if dynamic keys works as expected - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'path' => '/ping' + // Test starter code is used and that dynamic keys work + $execution = $this->createExecution($functionId, [ + 'path' => '/ping', ]); - $this->assertEquals(201, $execution['headers']['status-code']); $this->assertEquals("completed", $execution['body']['status']); $this->assertEquals(200, $execution['body']['responseStatusCode']); $this->assertEquals("Pong", $execution['body']['responseBody']); $this->assertEmpty($execution['body']['errors']); - // Get users to ensure execution logged correct total users + // Test execution logged correct total users $users = $this->client->call(Client::METHOD_GET, '/users', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ], $this->getHeaders()), []); - $this->assertEquals(200, $users['headers']['status-code']); $this->assertIsInt($users['body']['total']); - $totalusers = $users['body']['total']; - - $this->assertStringContainsString("Total users: " . $totalusers, $execution['body']['logs']); + $totalUsers = $users['body']['total']; + $this->assertStringContainsString("Total users: " . $totalUsers, $execution['body']['logs']); // Execute function again but async - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $execution = $this->createExecution($functionId, [ 'path' => '/ping', 'async' => true ]); - + $executionId = $execution['body']['$id']; $this->assertEquals(202, $execution['headers']['status-code']); $this->assertNotEmpty($execution['body']['$id']); $this->assertEquals('waiting', $execution['body']['status']); - $executionId = $execution['body']['$id']; - // Wait for async execuntion to finish - sleep(5); + $this->assertEventually(function () use ($functionId, $executionId, $totalUsers) { + $execution = $this->getExecution($functionId, $executionId); - // Ensure execution was successful - $execution = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); + $this->assertEquals('completed', $execution['body']['status']); + $this->assertEquals(200, $execution['headers']['status-code']); + $this->assertEquals("completed", $execution['body']['status']); + $this->assertEquals(200, $execution['body']['responseStatusCode']); + $this->assertEmpty($execution['body']['responseBody']); + $this->assertEmpty($execution['body']['errors']); + $this->assertStringContainsString("Total users: " . $totalUsers, $execution['body']['logs']); + }, 100000, 250); - $this->assertEquals(200, $execution['headers']['status-code']); - $this->assertEquals("completed", $execution['body']['status']); - $this->assertEquals(200, $execution['body']['responseStatusCode']); - $this->assertEmpty($execution['body']['responseBody']); - $this->assertEmpty($execution['body']['errors']); - $this->assertStringContainsString("Total users: " . $totalusers, $execution['body']['logs']); - - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); + $function = $this->deleteFunction($functionId); + $this->assertEquals(204, $function['headers']['status-code']); } /** @@ -615,15 +529,11 @@ class FunctionsCustomServerTest extends Scope /** * Test for SUCCESS */ - $folder = 'php'; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; - $this->packageCode($folder); - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/deployments', array_merge([ 'content-type' => 'multipart/form-data', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'code' => new CURLFile($code, 'application/x-gzip', \basename($code)), + 'code' => $this->packageFunction('php'), 'activate' => true ]); @@ -640,7 +550,7 @@ class FunctionsCustomServerTest extends Scope 'content-type' => 'multipart/form-data', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'code' => new CURLFile($code, 'application/x-gzip', \basename($code)), + 'code' => $this->packageFunction('php'), 'activate' => 'false' ]); @@ -675,16 +585,11 @@ class FunctionsCustomServerTest extends Scope */ public function testCancelDeploymentBuild($data): void { - // Create a new deployment to cancel - $folder = 'php'; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; - $this->packageCode($folder); - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/deployments', array_merge([ 'content-type' => 'multipart/form-data', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'code' => new CURLFile($code, 'application/x-gzip', \basename($code)), + 'code' => $this->packageFunction('php'), 'activate' => true ]); @@ -1406,16 +1311,6 @@ class FunctionsCustomServerTest extends Scope $executionId = $execution['body']['$id'] ?? ''; $this->assertEquals(202, $execution['headers']['status-code']); - $this->assertEventually(function () use ($data, $executionId) { - $execution = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions/' . $executionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ]); - - $this->assertEquals(200, $execution['headers']['status-code']); - $this->assertEquals('waiting', $execution['body']['status']); - }, 10000, 500); - $execution = $this->client->call(Client::METHOD_DELETE, '/functions/' . $data['functionId'] . '/executions/' . $executionId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1580,87 +1475,41 @@ class FunctionsCustomServerTest extends Scope public function testTimeout() { - $name = 'php-8.0'; - $entrypoint = 'index.php'; - $timeout = 15; - $folder = 'timeout'; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; - $this->packageCode($folder); - - $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'functionId' => ID::unique(), - 'name' => 'Test ' . $name, - 'runtime' => $name, - 'entrypoint' => $entrypoint, + $function = $this->createFunction([ + 'name' => 'timeout-php-8.0', + 'runtime' => 'php-8.0', + 'entrypoint' => 'index.php', 'events' => [], - 'schedule' => '* * * * *', // execute every minute - 'timeout' => $timeout, + 'schedule' => '*/10 * * * *', // Execute every 10 seconds + 'timeout' => 15, ]); - - $functionId = $function['body']['$id'] ?? ''; - + $functionId = $function['body']['id']; $this->assertEquals(201, $function['headers']['status-code']); - $this->assertEquals('* * * * *', $function['body']['schedule']); - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'entrypoint' => $entrypoint, - 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), - 'activate' => true, - ]); - - $deploymentId = $deployment['body']['$id'] ?? ''; - - $this->assertEquals(202, $deployment['headers']['status-code']); - - $this->assertDeployment($functionId, $deploymentId); - - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'async' => true, - ]); + $deployment = $this->createDeployment($functionId, 'php'); + $deploymentId = $deployment['body']['$id']; + $this->assertDeploymentStatus($functionId, $deploymentId, 'ready'); + $execution = $this->createExecution($functionId, ['async' => true]); $executionId = $execution['body']['$id'] ?? ''; - $this->assertEquals(202, $execution['headers']['status-code']); - $executionData = null; + $this->assertEventually(function () use ($functionId, $executionId) { + $execution = $this->getExecution($functionId, $executionId); - $this->assertEventually(function () use ($functionId, $executionId, &$executionData) { - $executionData = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + $this->assertEquals(200, $execution['headers']['status-code']); + $this->assertEquals('failed', $execution['body']['status']); + $this->assertEquals('failed', $execution['body']['status']); + $this->assertEquals(500, $execution['body']['responseStatusCode']); + $this->assertGreaterThan(2, $execution['body']['duration']); + $this->assertLessThan(20, $execution['body']['duration']); + $this->assertEquals('', $execution['body']['responseBody']); + $this->assertEquals('', $execution['body']['logs']); + $this->assertStringContainsString('timed out', $execution['body']['errors']); + }, 20000, 500); - $this->assertEquals(200, $executionData['headers']['status-code']); - $this->assertEquals('failed', $executionData['body']['status']); - - if ($executionData['body']['status'] !== 'failed') { - } - }, 30000, 1000); - - $this->assertEquals('failed', $executionData['body']['status']); - $this->assertEquals(500, $executionData['body']['responseStatusCode']); - $this->assertGreaterThan(2, $executionData['body']['duration']); - $this->assertLessThan(20, $executionData['body']['duration']); - $this->assertEquals('', $executionData['body']['responseBody']); - $this->assertEquals('', $executionData['body']['logs']); - $this->assertStringContainsString('timed out', $executionData['body']['errors']); - - $scheduledExecutionData = null; - - $this->assertEventually(function () use ($functionId, &$scheduledExecutionData) { - $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $this->assertEventually(function () use ($functionId) { + $executions = $this->listExecutions($functionId, [ 'queries' => [ Query::equal('trigger', ['schedule'])->toString(), ], @@ -1669,19 +1518,12 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(200, $executions['headers']['status-code']); $this->assertEquals(1, $executions['body']['total']); - $scheduledExecutionData = $executions['body']['executions'][0]; - }, 140000, 1000); // Wait up to 140 seconds, checking every 1 second + $lastExecution = $executions['body']['executions'][0]; + $this->assertEquals('schedule', $lastExecution['trigger']); + }, 20000, 500); - $this->assertEquals('schedule', $scheduledExecutionData['trigger']); - - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); + $function = $this->deleteFunction($functionId); + $this->assertEquals(204, $function['headers']['status-code']); } From af509f3479a25def65685e090d33bf4ee6cd41f1 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 19 Sep 2024 14:17:38 +0100 Subject: [PATCH 04/32] fix: flakyness --- .../e2e/Services/Functions/FunctionsBase.php | 78 +++++++------------ .../Functions/FunctionsConsoleClientTest.php | 24 ++++-- .../Functions/FunctionsCustomClientTest.php | 10 ++- 3 files changed, 51 insertions(+), 61 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index 92210a3027..cdb4781e5e 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -21,27 +21,26 @@ trait FunctionsBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ]), $params); - $functionId = $function['body']['$id']; + $this->assertEquals($function['headers']['status-code'], 201, 'Setup function failed with status code: ' . $function['headers']['status-code'] . ' and response: ' . json_encode($function['body'])); - $this->assertEquals($function['headers']['status-code'], 201); + $functionId = $function['body']['$id']; return $functionId; } protected function setupDeployment(string $functionId, mixed $params): string { $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ - 'content-type' => 'application/json', + 'content-type' => 'multipart/form-data', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ]), $params); + $this->assertEquals($deployment['headers']['status-code'], 202, 'Setup deployment failed with status code: ' . $deployment['headers']['status-code'] . ' and response: ' . json_encode($deployment['body'])); $deploymentId = $deployment['body']['$id']; - $this->assertEquals($deployment['headers']['status-code'], 202); - $this->assertEventually(function () use ($functionId, $deploymentId) { $deployment = $this->getDeployment($functionId, $deploymentId); $this->assertEquals('completed', $deployment['body']['status']); - }, 200000, 500); + }, 50000, 500); return $deploymentId; } @@ -57,40 +56,27 @@ trait FunctionsBase $this->assertEquals($function['headers']['status-code'], 204); } - protected function createFunction(mixed $params) + protected function createFunction(mixed $params): mixed { $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), $params); - $functionId = $function['body']['$id']; - - if (empty($variables)) { - return $function; - } - - $function = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); return $function; } - protected function createVariable($functionId, $key, $value) + protected function createVariable(string $functionId, mixed $params): mixed { $variable = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'key' => $key, - 'value' => $value - ]); + ], $this->getHeaders()), $params); return $variable; } - protected function getFunction($functionId) + protected function getFunction(string $functionId): mixed { $function = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId, array_merge([ 'content-type' => 'application/json', @@ -100,7 +86,7 @@ trait FunctionsBase return $function; } - protected function getDeployment($functionId, $deploymentId) + protected function getDeployment(string $functionId, string $deploymentId): mixed { $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ 'content-type' => 'application/json', @@ -120,12 +106,22 @@ trait FunctionsBase return $execution; } + protected function listFunctions($params = []) + { + $functions = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), $params); + + return $functions; + } + protected function listDeployments($functionId, $params = []) { $deployments = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders(), $params)); + ], $this->getHeaders()), $params); return $deployments; } @@ -135,19 +131,17 @@ trait FunctionsBase $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders(), $params)); + ], $this->getHeaders()), $params); return $executions; } - protected function packageFunction(string $folder = 'php') + protected function packageFunction(string $folder = 'php'): CURLFile { $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; - Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr); - if (!file_exists($code)) { - throw new \Exception('Failed to create code package. ' . $code . ' does not exist.'); + Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr); } if (filesize($code) > 1024 * 1024 * 5) { throw new \Exception('Code package is too large. Use the chunked upload method instead.'); @@ -158,36 +152,22 @@ trait FunctionsBase protected function createDeployment( string $functionId, - mixed $params, - bool $cli = false, - bool $admin = false + mixed $params ) { - $authHeaders = $this->getHeaders(); - - if ($admin) { - $authHeaders = [ - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]; - } - if ($cli) { - $authHeaders[] = 'x-sdk-language: cli'; - } - - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', [ + $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ 'content-type' => 'multipart/form-data', 'x-appwrite-project' => $this->getProject()['$id'], - - ], $params); + ], $this->getHeaders()), $params); return $deployment; } - protected function getFunctionUsage($functionId, $params) + protected function getFunctionUsage(string $functionId, mixed $params): mixed { $usage = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/usage', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders(), $params)); + ], $this->getHeaders()), $params); return $usage; } diff --git a/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php b/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php index 6ee5a1085d..cbfca09564 100644 --- a/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php @@ -105,8 +105,10 @@ class FunctionsConsoleClientTest extends Scope */ $variable = $this->createVariable( $data['functionId'], - 'APP_TEST', - 'TESTINGVALUE' + [ + 'key' => 'APP_TEST', + 'value' => 'TESTINGVALUE' + ] ); $variableId = $variable['body']['$id']; $this->assertEquals(201, $variable['headers']['status-code']); @@ -117,24 +119,30 @@ class FunctionsConsoleClientTest extends Scope // Test for duplicate key $variable = $this->createVariable( $data['functionId'], - 'APP_TEST', - 'ANOTHER_TESTINGVALUE' + [ + 'key' => 'APP_TEST', + 'value' => 'ANOTHERTESTINGVALUE' + ] ); $this->assertEquals(409, $variable['headers']['status-code']); // Test for invalid key $variable = $this->createVariable( $data['functionId'], - str_repeat("A", 256), - 'TESTINGVALUE' + [ + 'key' => str_repeat("A", 256), + 'value' => 'TESTINGVALUE' + ] ); $this->assertEquals(400, $variable['headers']['status-code']); // Test for invalid value $variable = $this->createVariable( $data['functionId'], - 'LONGKEY', - str_repeat("#", 8193) + [ + 'key' => 'LONGKEY', + 'value' => str_repeat("#", 8193), + ] ); $this->assertEquals(400, $variable['headers']['status-code']); diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 872249085e..1dc81c8171 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -15,7 +15,7 @@ class FunctionsCustomClientTest extends Scope use ProjectCustom; use SideClient; - public function testCreate() + public function testCreateFunction() { /** * Test for FAILURE @@ -80,7 +80,7 @@ class FunctionsCustomClientTest extends Scope $this->assertNotEmpty($asyncExecution['logs']); $this->assertNotEmpty($asyncExecution['errors']); $this->assertGreaterThan(0, $asyncExecution['duration']); - }, 100000, 250); + }, 10000, 250); $this->cleanupFunction($functionId); } @@ -106,7 +106,7 @@ class FunctionsCustomClientTest extends Scope // Schedule execution for the future \date_default_timezone_set('UTC'); - $futureTime = (new \DateTime())->add(new \DateInterval('PT30S')); // 30 seconds from now + $futureTime = (new \DateTime())->add(new \DateInterval('PT15S')); // 15 seconds from now $futureTime->setTime($futureTime->format('H'), $futureTime->format('i'), 0, 0); $execution = $this->createExecution($functionId, [ @@ -126,6 +126,8 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals('/custom-path', $execution['body']['requestPath']); $this->assertCount(0, $execution['body']['requestHeaders']); + \sleep(15); + $this->assertEventually(function () use ($functionId, $executionId) { $execution = $this->getExecution($functionId, $executionId); $this->assertEquals('completed', $execution['body']['status']); @@ -141,7 +143,7 @@ class FunctionsCustomClientTest 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']); - }, 40000, 2500); + }, 10000, 250); /* Test for FAILURE */ // Schedule synchronous execution From 964b616e05b3c6ee2c1ba3b03b276deb95c59ee8 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 19 Sep 2024 20:21:00 +0100 Subject: [PATCH 05/32] fix: refactor custom server test --- .../e2e/Services/Functions/FunctionsBase.php | 16 +- .../Functions/FunctionsCustomClientTest.php | 37 +- .../Functions/FunctionsCustomServerTest.php | 1582 +++++------------ 3 files changed, 481 insertions(+), 1154 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index cdb4781e5e..730c7bff48 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -21,7 +21,7 @@ trait FunctionsBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ]), $params); - $this->assertEquals($function['headers']['status-code'], 201, 'Setup function failed with status code: ' . $function['headers']['status-code'] . ' and response: ' . json_encode($function['body'])); + $this->assertEquals($function['headers']['status-code'], 201, 'Setup function failed with status code: ' . $function['headers']['status-code'] . ' and response: ' . json_encode($function['body'], JSON_PRETTY_PRINT)); $functionId = $function['body']['$id']; return $functionId; @@ -34,12 +34,16 @@ trait FunctionsBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ]), $params); - $this->assertEquals($deployment['headers']['status-code'], 202, 'Setup deployment failed with status code: ' . $deployment['headers']['status-code'] . ' and response: ' . json_encode($deployment['body'])); - $deploymentId = $deployment['body']['$id']; + $this->assertEquals($deployment['headers']['status-code'], 202, 'Setup deployment failed with status code: ' . $deployment['headers']['status-code'] . ' and response: ' . json_encode($deployment['body'], JSON_PRETTY_PRINT)); + $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEventually(function () use ($functionId, $deploymentId) { - $deployment = $this->getDeployment($functionId, $deploymentId); - $this->assertEquals('completed', $deployment['body']['status']); + $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertEquals('ready', $deployment['body']['status']); }, 50000, 500); return $deploymentId; @@ -182,7 +186,7 @@ trait FunctionsBase return $template; } - protected function createExecution(string $functionId, mixed $params) + protected function createExecution(string $functionId, mixed $params = []) { $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 1dc81c8171..dd950ca655 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -48,7 +48,7 @@ class FunctionsCustomClientTest extends Scope 'users.*.create', 'users.*.delete', ], - 'schedule' => '* * * * *', // Execute every minute + 'schedule' => '* * * * *', // Execute every 60 seconds 'timeout' => 10, ]); $this->setupDeployment($functionId, [ @@ -57,18 +57,30 @@ class FunctionsCustomClientTest extends Scope 'activate' => true ]); - $execution = $this->createExecution($functionId, [ - 'async' => false, + // Deny create async execution as guest + $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'async' => true, ]); $this->assertEquals(401, $execution['headers']['status-code']); + // Allow create async execution as user $execution = $this->createExecution($functionId, [ 'async' => true, ]); $this->assertEquals(202, $execution['headers']['status-code']); + // Wait for scheduled execution + \sleep(60); + $this->assertEventually(function () use ($functionId) { - $executions = $this->listExecutions($functionId); + $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['key'], + ]); $this->assertEquals(200, $executions['headers']['status-code']); $this->assertCount(2, $executions['body']['executions']); @@ -80,7 +92,7 @@ class FunctionsCustomClientTest extends Scope $this->assertNotEmpty($asyncExecution['logs']); $this->assertNotEmpty($asyncExecution['errors']); $this->assertGreaterThan(0, $asyncExecution['duration']); - }, 10000, 250); + }, 10000, 500); $this->cleanupFunction($functionId); } @@ -97,6 +109,7 @@ class FunctionsCustomClientTest extends Scope 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', 'timeout' => 10, + 'logging' => true, ]); $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', @@ -106,7 +119,7 @@ class FunctionsCustomClientTest extends Scope // Schedule execution for the future \date_default_timezone_set('UTC'); - $futureTime = (new \DateTime())->add(new \DateInterval('PT15S')); // 15 seconds from now + $futureTime = (new \DateTime())->add(new \DateInterval('PT2M')); // 2 minute in the future $futureTime->setTime($futureTime->format('H'), $futureTime->format('i'), 0, 0); $execution = $this->createExecution($functionId, [ @@ -120,17 +133,18 @@ class FunctionsCustomClientTest extends Scope ] ]); $executionId = $execution['body']['$id']; + $this->assertEquals(202, $execution['headers']['status-code']); $this->assertEquals('scheduled', $execution['body']['status']); $this->assertEquals('PATCH', $execution['body']['requestMethod']); $this->assertEquals('/custom-path', $execution['body']['requestPath']); $this->assertCount(0, $execution['body']['requestHeaders']); - \sleep(15); + \sleep(120); $this->assertEventually(function () use ($functionId, $executionId) { $execution = $this->getExecution($functionId, $executionId); - $this->assertEquals('completed', $execution['body']['status']); + $this->assertEquals(200, $execution['headers']['status-code']); $this->assertEquals(200, $execution['body']['responseStatusCode']); $this->assertEquals('completed', $execution['body']['status']); @@ -143,7 +157,7 @@ class FunctionsCustomClientTest 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, 250); + }, 10000, 500); /* Test for FAILURE */ // Schedule synchronous execution @@ -377,9 +391,11 @@ class FunctionsCustomClientTest extends Scope $templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([ 'content-type' => 'application/json', ], $this->getHeaders())); + $this->assertEquals(200, $templates['headers']['status-code']); $this->assertGreaterThan(0, $templates['body']['total']); $this->assertIsArray($templates['body']['templates']); + foreach ($templates['body']['templates'] as $template) { $this->assertArrayHasKey('name', $template); $this->assertArrayHasKey('id', $template); @@ -389,7 +405,6 @@ class FunctionsCustomClientTest extends Scope $this->assertArrayHasKey('vcsProvider', $template); $this->assertArrayHasKey('runtimes', $template); $this->assertArrayHasKey('variables', $template); - $this->assertArrayHasKey('scopes', $template); } // List templates with pagination @@ -434,9 +449,11 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals(5, $templates['body']['total']); $this->assertIsArray($templates['body']['templates']); $this->assertArrayHasKey('runtimes', $templates['body']['templates'][0]); + foreach ($templates['body']['templates'] as $template) { $this->assertContains($template['useCases'][0], ['databases']); } + $this->assertContains('node-16.0', array_column($templates['body']['templates'][0]['runtimes'], 'name')); /** diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 8fe4aedca7..80f34d6d34 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -10,27 +10,27 @@ use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideServer; use Utopia\App; +use Utopia\CLI\Console; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Datetime as DatetimeValidator; +use function PHPSTORM_META\map; + class FunctionsCustomServerTest extends Scope { use FunctionsBase; use ProjectCustom; use SideServer; - public function testCreate(): array + public function testCreateFunction(): array { /** * Test for SUCCESS */ - $response1 = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $function = $this->createFunction([ 'functionId' => ID::unique(), 'name' => 'Test', 'runtime' => 'php-8.0', @@ -42,162 +42,115 @@ class FunctionsCustomServerTest extends Scope 'schedule' => '0 0 1 1 *', 'timeout' => 10, ]); - - $functionId = $response1['body']['$id'] ?? ''; - - $this->assertEquals(201, $response1['headers']['status-code']); - $this->assertNotEmpty($response1['body']['$id']); - $this->assertEquals('Test', $response1['body']['name']); - $this->assertEquals('php-8.0', $response1['body']['runtime']); + $functionId = $functionId = $function['body']['$id'] ?? ''; $dateValidator = new DatetimeValidator(); - $this->assertEquals(true, $dateValidator->isValid($response1['body']['$createdAt'])); - $this->assertEquals(true, $dateValidator->isValid($response1['body']['$updatedAt'])); - $this->assertEquals('', $response1['body']['deployment']); + $this->assertEquals(201, $function['headers']['status-code']); + $this->assertNotEmpty($function['body']['$id']); + $this->assertEquals('Test', $function['body']['name']); + $this->assertEquals('php-8.0', $function['body']['runtime']); + $this->assertEquals(true, $dateValidator->isValid($function['body']['$createdAt'])); + $this->assertEquals(true, $dateValidator->isValid($function['body']['$updatedAt'])); + $this->assertEquals('', $function['body']['deployment']); $this->assertEquals([ 'buckets.*.create', 'buckets.*.delete', - ], $response1['body']['events']); - $this->assertEquals('0 0 1 1 *', $response1['body']['schedule']); - $this->assertEquals(10, $response1['body']['timeout']); + ], $function['body']['events']); + $this->assertEquals('0 0 1 1 *', $function['body']['schedule']); + $this->assertEquals(10, $function['body']['timeout']); - /** Create Variables */ - $variable = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $variable = $this->createVariable($functionId, [ 'key' => 'funcKey1', 'value' => 'funcValue1', ]); - - $variable2 = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $variable2 = $this->createVariable($functionId, [ 'key' => 'funcKey2', 'value' => 'funcValue2', ]); - - $variable3 = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $variable3 = $this->createVariable($functionId, [ 'key' => 'funcKey3', 'value' => 'funcValue3', ]); - $this->assertEquals(201, $variable['headers']['status-code']); $this->assertEquals(201, $variable2['headers']['status-code']); $this->assertEquals(201, $variable3['headers']['status-code']); - /** - * Test for FAILURE - */ - return [ 'functionId' => $functionId, ]; } /** - * @depends testCreate + * @depends testCreateFunction */ - public function testList(array $data): array + public function testListFunctions(array $data): array { /** * Test for SUCCESS */ - - /** - * Test search queries - */ - $response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + // Test search id + $functions = $this->listFunctions([ 'search' => $data['functionId'] ]); + $this->assertEquals($functions['headers']['status-code'], 200); + $this->assertCount(1, $functions['body']['functions']); + $this->assertEquals($functions['body']['functions'][0]['name'], 'Test'); - $this->assertEquals($response['headers']['status-code'], 200); - $this->assertCount(1, $response['body']['functions']); - $this->assertEquals($response['body']['functions'][0]['name'], 'Test'); - - $response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + // Test pagination limit + $functions = $this->listFunctions([ 'queries' => [ Query::limit(1)->toString(), ], ]); + $this->assertEquals($functions['headers']['status-code'], 200); + $this->assertCount(1, $functions['body']['functions']); - $this->assertEquals($response['headers']['status-code'], 200); - $this->assertCount(1, $response['body']['functions']); - - $response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + // Test pagination offset + $functions = $this->listFunctions([ 'queries' => [ Query::offset(1)->toString(), ], ]); + $this->assertEquals($functions['headers']['status-code'], 200); + $this->assertCount(0, $functions['body']['functions']); - $this->assertEquals($response['headers']['status-code'], 200); - $this->assertCount(0, $response['body']['functions']); - - $response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + // Test filter enabled + $functions = $this->listFunctions([ 'queries' => [ Query::equal('enabled', [true])->toString(), ], ]); + $this->assertEquals($functions['headers']['status-code'], 200); + $this->assertCount(1, $functions['body']['functions']); - $this->assertEquals($response['headers']['status-code'], 200); - $this->assertCount(1, $response['body']['functions']); - - $response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + // Test filter disabled + $functions = $this->listFunctions([ 'queries' => [ Query::equal('enabled', [false])->toString(), ], ]); + $this->assertEquals($functions['headers']['status-code'], 200); + $this->assertCount(0, $functions['body']['functions']); - $this->assertEquals($response['headers']['status-code'], 200); - $this->assertCount(0, $response['body']['functions']); - - $response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + // Test search name + $functions = $this->listFunctions([ 'search' => 'Test' ]); + $this->assertEquals($functions['headers']['status-code'], 200); + $this->assertCount(1, $functions['body']['functions']); + $this->assertEquals($functions['body']['functions'][0]['$id'], $data['functionId']); - $this->assertEquals($response['headers']['status-code'], 200); - $this->assertCount(1, $response['body']['functions']); - $this->assertEquals($response['body']['functions'][0]['$id'], $data['functionId']); - - $response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + // Test search runtime + $functions = $this->listExecutions([ 'search' => 'php-8.0' ]); - - $this->assertEquals($response['headers']['status-code'], 200); - $this->assertCount(1, $response['body']['functions']); - $this->assertEquals($response['body']['functions'][0]['$id'], $data['functionId']); + $this->assertEquals($functions['headers']['status-code'], 200); + $this->assertCount(1, $functions['body']['functions']); + $this->assertEquals($functions['body']['functions'][0]['$id'], $data['functionId']); /** * Test pagination */ - $response = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test 2', 'runtime' => 'php-8.0', @@ -209,42 +162,8 @@ class FunctionsCustomServerTest extends Scope 'schedule' => '0 0 1 1 *', 'timeout' => 10, ]); - $this->assertNotEmpty($response['body']['$id']); - - /** Create Variables */ - $variable = $this->client->call(Client::METHOD_POST, '/functions/' . $response['body']['$id'] . '/variables', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'key' => 'funcKey1', - 'value' => 'funcValue1', - ]); - - $variable2 = $this->client->call(Client::METHOD_POST, '/functions/' . $response['body']['$id'] . '/variables', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'key' => 'funcKey2', - 'value' => 'funcValue2', - ]); - - $variable3 = $this->client->call(Client::METHOD_POST, '/functions/' . $response['body']['$id'] . '/variables', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'key' => 'funcKey3', - 'value' => 'funcValue3', - ]); - - $this->assertEquals(201, $variable['headers']['status-code']); - $this->assertEquals(201, $variable2['headers']['status-code']); - $this->assertEquals(201, $variable3['headers']['status-code']); - - $functions = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + $functions = $this->listFunctions(); $this->assertEquals($functions['headers']['status-code'], 200); $this->assertEquals($functions['body']['total'], 2); $this->assertIsArray($functions['body']['functions']); @@ -252,87 +171,67 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals($functions['body']['functions'][0]['name'], 'Test'); $this->assertEquals($functions['body']['functions'][1]['name'], 'Test 2'); - $response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $functions = $this->listFunctions([ 'queries' => [ Query::cursorAfter(new Document(['$id' => $functions['body']['functions'][0]['$id']]))->toString(), ], ]); + $this->assertEquals($functions['headers']['status-code'], 200); + $this->assertCount(1, $functions['body']['functions']); + $this->assertEquals($functions['body']['functions'][0]['name'], 'Test 2'); - $this->assertEquals($response['headers']['status-code'], 200); - $this->assertCount(1, $response['body']['functions']); - $this->assertEquals($response['body']['functions'][0]['name'], 'Test 2'); - - $response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $functions = $this->listFunctions([ 'queries' => [ Query::cursorBefore(new Document(['$id' => $functions['body']['functions'][1]['$id']]))->toString(), ], ]); - - $this->assertEquals($response['headers']['status-code'], 200); - $this->assertCount(1, $response['body']['functions']); - $this->assertEquals($response['body']['functions'][0]['name'], 'Test'); + $this->assertEquals($functions['headers']['status-code'], 200); + $this->assertCount(1, $functions['body']['functions']); + $this->assertEquals($functions['body']['functions'][0]['name'], 'Test'); /** * Test for FAILURE */ - $response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $functions = $this->listFunctions([ 'queries' => [ Query::cursorAfter(new Document(['$id' => 'unknown']))->toString(), ], ]); - - $this->assertEquals($response['headers']['status-code'], 400); + $this->assertEquals($functions['headers']['status-code'], 400); return $data; } /** - * @depends testList + * @depends testListFunctions */ - public function testGet(array $data): array + public function testGetFunction(array $data): array { /** * Test for SUCCESS */ - $function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'], array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - + $function = $this->getFunction($data['functionId']); $this->assertEquals($function['headers']['status-code'], 200); $this->assertEquals($function['body']['name'], 'Test'); /** * Test for FAILURE */ - $function = $this->client->call(Client::METHOD_GET, '/functions/x', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - + $function = $this->getFunction('x'); $this->assertEquals($function['headers']['status-code'], 404); return $data; } /** - * @depends testGet + * @depends testGetFunction */ - public function testUpdate($data): array + public function testUpdateFunction($data): array { /** * Test for SUCCESS */ - $response1 = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'], array_merge([ + $function = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -347,36 +246,25 @@ class FunctionsCustomServerTest extends Scope 'entrypoint' => 'index.php', ]); - $this->assertEquals(200, $response1['headers']['status-code']); - $this->assertNotEmpty($response1['body']['$id']); - $this->assertEquals('Test1', $response1['body']['name']); + $this->assertEquals(200, $function['headers']['status-code']); + $this->assertNotEmpty($function['body']['$id']); + $this->assertEquals('Test1', $function['body']['name']); $dateValidator = new DatetimeValidator(); - $this->assertEquals(true, $dateValidator->isValid($response1['body']['$createdAt'])); - $this->assertEquals(true, $dateValidator->isValid($response1['body']['$updatedAt'])); - $this->assertEquals('', $response1['body']['deployment']); + $this->assertEquals(true, $dateValidator->isValid($function['body']['$createdAt'])); + $this->assertEquals(true, $dateValidator->isValid($function['body']['$updatedAt'])); + $this->assertEquals('', $function['body']['deployment']); $this->assertEquals([ 'users.*.update.name', 'users.*.update.email', - ], $response1['body']['events']); - $this->assertEquals('0 0 1 1 *', $response1['body']['schedule']); - $this->assertEquals(15, $response1['body']['timeout']); + ], $function['body']['events']); + $this->assertEquals('0 0 1 1 *', $function['body']['schedule']); + $this->assertEquals(15, $function['body']['timeout']); - /** - * Create global variable to test in execution later - */ - $headers = [ - 'content-type' => 'application/json', - 'origin' => 'http://localhost', - 'cookie' => 'a_session_console=' . $this->getRoot()['session'], - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-mode' => 'admin', - ]; - - $variable = $this->client->call(Client::METHOD_POST, '/project/variables', $headers, [ + // Create a variable for later tests + $variable = $this->createVariable($data['functionId'], [ 'key' => 'GLOBAL_VARIABLE', 'value' => 'Global Variable Value', ]); - $this->assertEquals(201, $variable['headers']['status-code']); /** @@ -388,7 +276,7 @@ class FunctionsCustomServerTest extends Scope public function testCreateDeploymentFromCLI() { - $function = $this->createFunction([ + $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'test-cli-deployment', 'execute' => [Role::user($this->getUser()['$id'])->toString()], @@ -401,26 +289,29 @@ class FunctionsCustomServerTest extends Scope 'schedule' => '0 0 1 1 *', // Once a year 'timeout' => 10, ]); - $functionId = $function['body']['$id']; - $this->assertEquals(201, $function['headers']['status-code']); - - $deployment = $this->createDeployment($functionId, [ + $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ + 'content-type' => 'multipart/form-data', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + 'x-sdk-language' => 'cli', + ], $this->getHeaders()), [ 'entrypoint' => 'index.php', 'code' => $this->packageFunction('php'), 'activate' => true - ], cli: true); - $deploymentId = $deployment['body']['$id']; - + ]); + $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); - $this->assertDeploymentStatus($functionId, $deploymentId, 'ready'); - $deployment = $this->getDeployment($functionId, $deploymentId); - $this->assertEquals(200, $deployment['headers']['status-code']); - $this->assertEquals('cli', $deployment['body']['type']); + $this->assertEventually(function () use ($functionId, $deploymentId) { + $deployment = $this->getDeployment($functionId, $deploymentId); + $this->assertEquals(200, $deployment['headers']['status-code']); + $this->assertEquals('completed', $deployment['body']['status']); + $this->assertEquals('cli', $deployment['body']['type']); + }, 500000, 1000); } - public function testCreateDeploymentFromTemplate() + public function testCreateFunctionAndDeploymentFromTemplate() { $starterTemplate = $this->getTemplate('starter'); @@ -451,7 +342,7 @@ class FunctionsCustomServerTest extends Scope 'templateVersion' => $starterTemplate['body']['providerVersion'], ] ); - $functionId = $function['body']['$id']; + $functionId = $functionId = $function['body']['$id'] ?? ''; $this->assertEquals(201, $function['headers']['status-code']); $this->assertNotEmpty($function['body']['$id']); @@ -464,10 +355,12 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(0, $lastDeployment['size']); $deploymentId = $lastDeployment['$id']; - $this->assertDeploymentStatus($function['body']['$id'], $deploymentId, 'ready'); - - $deployment = $this->getDeployment($function['body']['$id'], $deploymentId); - $this->assertGreaterThan(0, $deployment['body']['size']); + $this->assertEventually(function () use ($functionId, $deploymentId) { + $deployment = $this->getDeployment($functionId, $deploymentId); + $this->assertEquals(200, $deployment['headers']['status-code']); + $this->assertEquals('completed', $deployment['body']['status']); + $this->assertEquals('cli', $deployment['body']['type']); + }, 500000, 1000); $function = $this->getFunction($functionId); $this->assertEquals(200, $function['headers']['status-code']); @@ -500,18 +393,16 @@ class FunctionsCustomServerTest extends Scope 'path' => '/ping', 'async' => true ]); - $executionId = $execution['body']['$id']; + $executionId = $execution['body']['$id'] ?? ''; $this->assertEquals(202, $execution['headers']['status-code']); $this->assertNotEmpty($execution['body']['$id']); $this->assertEquals('waiting', $execution['body']['status']); $this->assertEventually(function () use ($functionId, $executionId, $totalUsers) { $execution = $this->getExecution($functionId, $executionId); - - $this->assertEquals('completed', $execution['body']['status']); $this->assertEquals(200, $execution['headers']['status-code']); - $this->assertEquals("completed", $execution['body']['status']); $this->assertEquals(200, $execution['body']['responseStatusCode']); + $this->assertEquals('completed', $execution['body']['status']); $this->assertEmpty($execution['body']['responseBody']); $this->assertEmpty($execution['body']['errors']); $this->assertStringContainsString("Total users: " . $totalUsers, $execution['body']['logs']); @@ -522,21 +413,18 @@ class FunctionsCustomServerTest extends Scope } /** - * @depends testUpdate + * @depends testUpdateFunction */ public function testCreateDeployment($data): array { /** * Test for SUCCESS */ - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/deployments', array_merge([ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $functionId = $data['functionId']; + $deployment = $this->createDeployment($functionId, [ 'code' => $this->packageFunction('php'), 'activate' => true ]); - $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); @@ -544,37 +432,31 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(true, (new DatetimeValidator())->isValid($deployment['body']['$createdAt'])); $this->assertEquals('index.php', $deployment['body']['entrypoint']); - $this->assertDeployment($data['functionId'], $deploymentId); - - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/deployments', array_merge([ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $deployment = $this->createDeployment($functionId, [ 'code' => $this->packageFunction('php'), - 'activate' => 'false' + 'activate' => false ]); - + $deploymentIdInactive = $deployment['body']['$id']; $this->assertEquals(202, $deployment['headers']['status-code']); $this->assertNotEmpty($deployment['body']['$id']); - $deploymentIdInactive = $deployment['body']['$id']; + $this->assertEventually(function () use ($functionId, $deploymentId, $deploymentIdInactive) { + $deployment = $this->getDeployment($functionId, $deploymentId); + $this->assertEquals('completed', $deployment['body']['status']); - $this->assertDeployment($data['functionId'], $deploymentIdInactive); - - $function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'], array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + $deployment = $this->getDeployment($functionId, $deploymentIdInactive); + $this->assertEquals('completed', $deployment['body']['status']); + }, 500000, 1000); + $function = $this->getFunction($functionId); $this->assertEquals(200, $function['headers']['status-code']); $this->assertEquals($deploymentId, $function['body']['deployment']); $this->assertNotEquals($deploymentIdInactive, $function['body']['deployment']); - $deployment = $this->client->call(Client::METHOD_DELETE, '/functions/' . $data['functionId'] . '/deployments/' . $deploymentIdInactive, array_merge([ + $deployment = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId . '/deployments/' . $deploymentIdInactive, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); - $this->assertEquals(204, $deployment['headers']['status-code']); return array_merge($data, ['deploymentId' => $deploymentId]); @@ -585,14 +467,11 @@ class FunctionsCustomServerTest extends Scope */ public function testCancelDeploymentBuild($data): void { - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/deployments', array_merge([ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $functionId = $data['functionId']; + $deployment = $this->createDeployment($functionId, [ 'code' => $this->packageFunction('php'), - 'activate' => true + 'activate' => false ]); - $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); @@ -600,39 +479,28 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(true, (new DatetimeValidator())->isValid($deployment['body']['$createdAt'])); $this->assertEquals('index.php', $deployment['body']['entrypoint']); - $this->assertEventually(function () use ($data, $deploymentId) { - $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments/' . $deploymentId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]); - + $this->assertEventually(function () use ($functionId, $deploymentId) { + $deployment = $this->getDeployment($functionId, $deploymentId); $this->assertEquals(200, $deployment['headers']['status-code']); $this->assertEquals('building', $deployment['body']['status']); - }, 60000, 1000); + }, 100000, 250); - // Cancel the deployment build - $cancel = $this->client->call(Client::METHOD_PATCH, '/functions/' . $data['functionId'] . '/deployments/' . $deploymentId . '/build', [ + // Cancel the deployment + $cancel = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId . '/build', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]); - + ], $this->getHeaders())); $this->assertEquals(200, $cancel['headers']['status-code']); $this->assertEquals('canceled', $cancel['body']['status']); /** * Build worker still runs the build. - * 10s sleep gives worker enough time to finish build. + * 30s sleep gives worker enough time to finish build. * After build finished, it should still be canceled, not ready. */ - \sleep(10); - $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments/' . $deploymentId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]); + \sleep(30); + $deployment = $this->getDeployment($functionId, $deploymentId); $this->assertEquals(200, $deployment['headers']['status-code']); $this->assertEquals('canceled', $deployment['body']['status']); } @@ -645,10 +513,11 @@ class FunctionsCustomServerTest extends Scope /** * Test for Large Code File SUCCESS */ + $functionId = $data['functionId']; $folder = 'php-large'; $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; - $this->packageCode($folder); + Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr); $chunkSize = 5 * 1024 * 1024; $handle = @fopen($code, "rb"); @@ -666,7 +535,7 @@ class FunctionsCustomServerTest extends Scope if (!empty($id)) { $headers['x-appwrite-id'] = $id; } - $largeTag = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/deployments', array_merge($headers, $this->getHeaders()), [ + $largeTag = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge($headers, $this->getHeaders()), [ 'entrypoint' => 'index.php', 'code' => $curlFile, 'activate' => true, @@ -685,19 +554,15 @@ class FunctionsCustomServerTest extends Scope $this->assertLessThan(1024 * 1024 * 10, $largeTag['body']['size']); // ~7MB video file $deploymentSize = $largeTag['body']['size']; - $deploymentId = $largeTag['body']['$id']; - $this->assertDeployment($data['functionId'], $deploymentId, true); - - $response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments/' . $deploymentId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals($deploymentSize, $response['body']['size']); - $this->assertGreaterThan(1024 * 1024 * 10, $response['body']['buildSize']); // ~7MB video file + 10MB sample file + $this->assertEventually(function () use ($functionId, $deploymentId, $deploymentSize) { + $deployment = $this->getDeployment($functionId, $deploymentId); + $this->assertEquals(200, $deployment['headers']['status-code']); + $this->assertEquals('completed', $deployment['body']['status']); + $this->assertEquals($deploymentSize, $deployment['body']['size']); + $this->assertGreaterThan(1024 * 1024 * 10, $deployment['body']['buildSize']); // ~7MB video file + 10MB sample file + }, 500000, 1000); return $data; } @@ -710,6 +575,8 @@ class FunctionsCustomServerTest extends Scope /** * Test for SUCCESS */ + $dateValidator = new DatetimeValidator(); + $response = $this->client->call(Client::METHOD_PATCH, '/functions/' . $data['functionId'] . '/deployments/' . $data['deploymentId'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -717,15 +584,10 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertNotEmpty($response['body']['$id']); - $dateValidator = new DatetimeValidator(); $this->assertEquals(true, $dateValidator->isValid($response['body']['$createdAt'])); $this->assertEquals(true, $dateValidator->isValid($response['body']['$updatedAt'])); $this->assertEquals($data['deploymentId'], $response['body']['deployment']); - /** - * Test for FAILURE - */ - return $data; } @@ -737,127 +599,85 @@ class FunctionsCustomServerTest extends Scope /** * Test for SUCCESS */ - $function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + $functionId = $data['functionId']; + $deployments = $this->listDeployments($functionId); - $this->assertEquals($function['headers']['status-code'], 200); - $this->assertEquals($function['body']['total'], 3); - $this->assertIsArray($function['body']['deployments']); - $this->assertCount(3, $function['body']['deployments']); - $this->assertArrayHasKey('size', $function['body']['deployments'][0]); - $this->assertArrayHasKey('buildSize', $function['body']['deployments'][0]); + $this->assertEquals($deployments['headers']['status-code'], 200); + $this->assertEquals($deployments['body']['total'], 3); + $this->assertIsArray($deployments['body']['deployments']); + $this->assertCount(3, $deployments['body']['deployments']); + $this->assertArrayHasKey('size', $deployments['body']['deployments'][0]); + $this->assertArrayHasKey('buildSize', $deployments['body']['deployments'][0]); - /** - * Test search queries - */ - $function = $this->client->call( - Client::METHOD_GET, - '/functions/' . $data['functionId'] . '/deployments', - array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders(), [ - 'search' => $data['functionId'] - ]) - ); + // Test search id + $deployments = $this->listDeployments($functionId, [ + 'search' => $data['functionId'] + ]); + $this->assertEquals($deployments['headers']['status-code'], 200); + $this->assertEquals(3, $deployments['body']['total']); + $this->assertIsArray($deployments['body']['deployments']); + $this->assertCount(3, $deployments['body']['deployments']); + $this->assertEquals($deployments['body']['deployments'][0]['$id'], $data['deploymentId']); - $this->assertEquals($function['headers']['status-code'], 200); - $this->assertEquals(3, $function['body']['total']); - $this->assertIsArray($function['body']['deployments']); - $this->assertCount(3, $function['body']['deployments']); - $this->assertEquals($function['body']['deployments'][0]['$id'], $data['deploymentId']); - - $function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + // Test pagination limit + $deployments = $this->listDeployments($functionId, [ 'queries' => [ Query::limit(1)->toString(), ], ]); + $this->assertEquals($deployments['headers']['status-code'], 200); + $this->assertCount(1, $deployments['body']['deployments']); - $this->assertEquals($function['headers']['status-code'], 200); - $this->assertCount(1, $function['body']['deployments']); - - $function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $deployments = $this->listDeployments($functionId, [ 'queries' => [ Query::offset(1)->toString(), ], ]); - $this->assertEquals($function['headers']['status-code'], 200); - $this->assertCount(2, $function['body']['deployments']); + $this->assertEquals($deployments['headers']['status-code'], 200); + $this->assertCount(2, $deployments['body']['deployments']); - $function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $deployments = $this->listDeployments($functionId, [ 'queries' => [ Query::equal('entrypoint', ['index.php'])->toString(), ], ]); - $this->assertEquals($function['headers']['status-code'], 200); - $this->assertCount(3, $function['body']['deployments']); + $this->assertEquals($deployments['headers']['status-code'], 200); + $this->assertCount(3, $deployments['body']['deployments']); - $function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $deployments = $this->listDeployments($functionId, [ 'queries' => [ Query::equal('entrypoint', ['index.js'])->toString(), ], ]); - $this->assertEquals($function['headers']['status-code'], 200); - $this->assertCount(0, $function['body']['deployments']); + $this->assertEquals($deployments['headers']['status-code'], 200); + $this->assertCount(0, $deployments['body']['deployments']); - $function = $this->client->call( - Client::METHOD_GET, - '/functions/' . $data['functionId'] . '/deployments', - array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders(), [ - 'search' => 'Test' - ]) - ); + $deployments = $this->listDeployments($functionId, [ + 'search' => 'Test' + ]); - $this->assertEquals($function['headers']['status-code'], 200); - $this->assertEquals(3, $function['body']['total']); - $this->assertIsArray($function['body']['deployments']); - $this->assertCount(3, $function['body']['deployments']); - $this->assertEquals($function['body']['deployments'][0]['$id'], $data['deploymentId']); - $function = $this->client->call( - Client::METHOD_GET, - '/functions/' . $data['functionId'] . '/deployments', - array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders(), [ - 'search' => 'php-8.0' - ]) - ); + $this->assertEquals($deployments['headers']['status-code'], 200); + $this->assertEquals(3, $deployments['body']['total']); + $this->assertIsArray($deployments['body']['deployments']); + $this->assertCount(3, $deployments['body']['deployments']); + $this->assertEquals($deployments['body']['deployments'][0]['$id'], $data['deploymentId']); - $this->assertEquals($function['headers']['status-code'], 200); - $this->assertEquals(3, $function['body']['total']); - $this->assertIsArray($function['body']['deployments']); - $this->assertCount(3, $function['body']['deployments']); - $this->assertEquals($function['body']['deployments'][0]['$id'], $data['deploymentId']); + $deployments = $this->listDeployments($functionId, [ + 'search' => 'php-8.0' + ]); - $function = $this->client->call( - Client::METHOD_GET, - '/functions/' . $data['functionId'] . '/deployments', - array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), + $this->assertEquals($deployments['headers']['status-code'], 200); + $this->assertEquals(3, $deployments['body']['total']); + $this->assertIsArray($deployments['body']['deployments']); + $this->assertCount(3, $deployments['body']['deployments']); + $this->assertEquals($deployments['body']['deployments'][0]['$id'], $data['deploymentId']); + + $deployments = $this->listDeployments( + $functionId, [ 'queries' => [ Query::equal('type', ['manual'])->toString(), @@ -865,16 +685,11 @@ class FunctionsCustomServerTest extends Scope ] ); - $this->assertEquals($function['headers']['status-code'], 200); - $this->assertEquals(3, $function['body']['total']); + $this->assertEquals($deployments['headers']['status-code'], 200); + $this->assertEquals(3, $deployments['body']['total']); - $function = $this->client->call( - Client::METHOD_GET, - '/functions/' . $data['functionId'] . '/deployments', - array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), + $deployments = $this->listDeployments( + $functionId, [ 'queries' => [ Query::equal('type', ['vcs'])->toString(), @@ -882,16 +697,11 @@ class FunctionsCustomServerTest extends Scope ] ); - $this->assertEquals($function['headers']['status-code'], 200); - $this->assertEquals(0, $function['body']['total']); + $this->assertEquals($deployments['headers']['status-code'], 200); + $this->assertEquals(0, $deployments['body']['total']); - $function = $this->client->call( - Client::METHOD_GET, - '/functions/' . $data['functionId'] . '/deployments', - array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), + $deployments = $this->listDeployments( + $functionId, [ 'queries' => [ Query::equal('type', ['invalid-string'])->toString(), @@ -899,16 +709,11 @@ class FunctionsCustomServerTest extends Scope ] ); - $this->assertEquals($function['headers']['status-code'], 200); - $this->assertEquals(0, $function['body']['total']); + $this->assertEquals($deployments['headers']['status-code'], 200); + $this->assertEquals(0, $deployments['body']['total']); - $function = $this->client->call( - Client::METHOD_GET, - '/functions/' . $data['functionId'] . '/deployments', - array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), + $deployments = $this->listDeployments( + $functionId, [ 'queries' => [ Query::greaterThan('size', 10000)->toString(), @@ -916,16 +721,11 @@ class FunctionsCustomServerTest extends Scope ] ); - $this->assertEquals($function['headers']['status-code'], 200); - $this->assertEquals(1, $function['body']['total']); + $this->assertEquals($deployments['headers']['status-code'], 200); + $this->assertEquals(1, $deployments['body']['total']); - $function = $this->client->call( - Client::METHOD_GET, - '/functions/' . $data['functionId'] . '/deployments', - array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), + $deployments = $this->listDeployments( + $functionId, [ 'queries' => [ Query::greaterThan('size', 0)->toString(), @@ -933,57 +733,40 @@ class FunctionsCustomServerTest extends Scope ] ); - $this->assertEquals($function['headers']['status-code'], 200); - $this->assertEquals(3, $function['body']['total']); + $this->assertEquals($deployments['headers']['status-code'], 200); + $this->assertEquals(3, $deployments['body']['total']); - $function = $this->client->call( - Client::METHOD_GET, - '/functions/' . $data['functionId'] . '/deployments', - array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), + $deployments = $this->listDeployments( + $functionId, [ 'queries' => [ Query::greaterThan('size', -100)->toString(), ], ] ); - - $this->assertEquals($function['headers']['status-code'], 200); - $this->assertEquals(3, $function['body']['total']); + $this->assertEquals($deployments['headers']['status-code'], 200); + $this->assertEquals(3, $deployments['body']['total']); /** * Ensure size output and size filters work exactly. * Prevents buildSize being counted towards deployemtn size */ - $response = $this->client->call( - Client::METHOD_GET, - '/functions/' . $data['functionId'] . '/deployments', - array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), + $deployments = $this->listDeployments( + $functionId, [ Query::limit(1)->toString(), ] ); + $this->assertEquals(200, $deployments['headers']['status-code']); + $this->assertGreaterThanOrEqual(1, $deployments['body']['total']); + $this->assertNotEmpty($deployments['body']['deployments'][0]['$id']); + $this->assertNotEmpty($deployments['body']['deployments'][0]['size']); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertGreaterThanOrEqual(1, $response['body']['total']); - $this->assertNotEmpty($response['body']['deployments'][0]['$id']); - $this->assertNotEmpty($response['body']['deployments'][0]['size']); + $deploymentId = $deployments['body']['deployments'][0]['$id']; + $deploymentSize = $deployments['body']['deployments'][0]['size']; - $deploymentId = $function['body']['deployments'][0]['$id']; - $deploymentSize = $function['body']['deployments'][0]['size']; - - $response = $this->client->call( - Client::METHOD_GET, - '/functions/' . $data['functionId'] . '/deployments', - array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), + $deployments = $this->listDeployments( + $functionId, [ 'queries' => [ Query::equal('size', [$deploymentSize])->toString(), @@ -991,20 +774,21 @@ class FunctionsCustomServerTest extends Scope ] ); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertGreaterThan(0, $response['body']['total']); + $this->assertEquals(200, $deployments['headers']['status-code']); + $this->assertGreaterThan(0, $deployments['body']['total']); - $found = false; - foreach ($response['body']['deployments'] as $deployment) { - if ($deployment['$id'] === $deploymentId) { - $found = true; - $this->assertEquals($deploymentSize, $deployment['size']); - break; - } + $matchingDeployment = array_filter( + $deployments['body']['deployments'], + fn($deployment) => $deployment['$id'] === $deploymentId + ); + + $this->assertNotEmpty($matchingDeployment, "Deployment with ID {$deploymentId} not found"); + + if (!empty($matchingDeployment)) { + $deployment = reset($matchingDeployment); + $this->assertEquals($deploymentSize, $deployment['size']); } - $this->assertTrue($found); - return $data; } @@ -1016,27 +800,19 @@ class FunctionsCustomServerTest extends Scope /** * Test for SUCCESS */ - $function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments/' . $data['deploymentId'], array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $this->assertEquals(200, $function['headers']['status-code']); - $this->assertGreaterThan(0, $function['body']['buildTime']); - $this->assertNotEmpty($function['body']['status']); - $this->assertNotEmpty($function['body']['buildLogs']); - $this->assertArrayHasKey('size', $function['body']); - $this->assertArrayHasKey('buildSize', $function['body']); + $deployment = $this->getDeployment($data['functionId'], $data['deploymentId']); + $this->assertEquals(200, $deployment['headers']['status-code']); + $this->assertGreaterThan(0, $deployment['body']['buildTime']); + $this->assertNotEmpty($deployment['body']['status']); + $this->assertNotEmpty($deployment['body']['buildLogs']); + $this->assertArrayHasKey('size', $deployment['body']); + $this->assertArrayHasKey('buildSize', $deployment['body']); /** * Test for FAILURE */ - $function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments/x', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $this->assertEquals($function['headers']['status-code'], 404); + $deployment = $this->getDeployment($data['functionId'], 'x'); + $this->assertEquals($deployment['headers']['status-code'], 404); return $data; } @@ -1049,13 +825,9 @@ class FunctionsCustomServerTest extends Scope /** * Test for SUCCESS */ - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $execution = $this->createExecution($data['functionId'], [ 'async' => false, ]); - $executionId = $execution['body']['$id'] ?? ''; $this->assertEquals(201, $execution['headers']['status-code']); @@ -1077,10 +849,7 @@ class FunctionsCustomServerTest extends Scope $this->assertNotEmpty($execution['body']['logs']); $this->assertLessThan(10, $execution['body']['duration']); - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $execution = $this->createExecution($data['functionId'], [ 'async' => false, 'path' => '/?code=400' ]); @@ -1092,6 +861,8 @@ class FunctionsCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); + + $execution = $this->createExecution($data['functionId']); $this->assertEquals(204, $execution['headers']['status-code']); return array_merge($data, ['executionId' => $executionId]); @@ -1105,82 +876,57 @@ class FunctionsCustomServerTest extends Scope /** * Test for SUCCESS */ - $function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + $executions = $this->listExecutions($data['functionId']); + $this->assertEquals($executions['headers']['status-code'], 200); + $this->assertEquals($executions['body']['total'], 1); + $this->assertIsArray($executions['body']['executions']); + $this->assertCount(1, $executions['body']['executions']); + $this->assertEquals($executions['body']['executions'][0]['$id'], $data['executionId']); - $this->assertEquals($function['headers']['status-code'], 200); - $this->assertEquals($function['body']['total'], 1); - $this->assertIsArray($function['body']['executions']); - $this->assertCount(1, $function['body']['executions']); - $this->assertEquals($function['body']['executions'][0]['$id'], $data['executionId']); - - $response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $executions = $this->listExecutions($data['functionId'], [ 'queries' => [ Query::limit(1)->toString(), ], ]); + $this->assertEquals(200, $executions['headers']['status-code']); + $this->assertCount(1, $executions['body']['executions']); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertCount(1, $response['body']['executions']); - - $response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $executions = $this->listExecutions($data['functionId'], [ 'queries' => [ Query::offset(1)->toString(), ], ]); + $this->assertEquals(200, $executions['headers']['status-code']); + $this->assertCount(0, $executions['body']['executions']); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertCount(0, $response['body']['executions']); - - $response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $executions = $this->listExecutions($data['functionId'], [ 'queries' => [ Query::equal('trigger', ['http'])->toString(), ], ]); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertCount(1, $response['body']['executions']); + $this->assertEquals(200, $executions['headers']['status-code']); + $this->assertCount(1, $executions['body']['executions']); /** * Test search queries */ - - $response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $executions = $this->listExecutions($data['functionId'], [ 'search' => $data['executionId'], ]); + $this->assertEquals(200, $executions['headers']['status-code']); + $this->assertEquals(1, $executions['body']['total']); + $this->assertIsInt($executions['body']['total']); + $this->assertCount(1, $executions['body']['executions']); + $this->assertEquals($data['functionId'], $executions['body']['executions'][0]['functionId']); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(1, $response['body']['total']); - $this->assertIsInt($response['body']['total']); - $this->assertCount(1, $response['body']['executions']); - $this->assertEquals($data['functionId'], $response['body']['executions'][0]['functionId']); - - $response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $executions = $this->listExecutions($data['functionId'], [ 'search' => $data['functionId'], ]); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(1, $response['body']['total']); - $this->assertIsInt($response['body']['total']); - $this->assertCount(1, $response['body']['executions']); - $this->assertEquals($data['executionId'], $response['body']['executions'][0]['$id']); + $this->assertEquals(200, $executions['headers']['status-code']); + $this->assertEquals(1, $executions['body']['total']); + $this->assertIsInt($executions['body']['total']); + $this->assertCount(1, $executions['body']['executions']); + $this->assertEquals($data['executionId'], $executions['body']['executions'][0]['$id']); return $data; } @@ -1193,13 +939,9 @@ class FunctionsCustomServerTest extends Scope /** * Test for SUCCESS */ - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $execution = $this->createExecution($data['functionId'], [ // Testing default value, should be 'async' => false ]); - $this->assertEquals(201, $execution['headers']['status-code']); $this->assertEquals('completed', $execution['body']['status']); $this->assertEquals(200, $execution['body']['responseStatusCode']); @@ -1221,22 +963,14 @@ class FunctionsCustomServerTest extends Scope /** * Test for SUCCESS */ - $function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions/' . $data['executionId'], array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $this->assertEquals($function['headers']['status-code'], 200); - $this->assertEquals($function['body']['$id'], $data['executionId']); + $execution = $this->getExecution($data['functionId'], $data['executionId']); + $this->assertEquals($execution['headers']['status-code'], 200); + $this->assertEquals($execution['body']['$id'], $data['executionId']); /** * Test for FAILURE */ - $function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions/x', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - + $function = $this->getExecution($data['functionId'], 'x'); $this->assertEquals($function['headers']['status-code'], 404); return $data; @@ -1329,7 +1063,8 @@ class FunctionsCustomServerTest extends Scope /** * Test for SUCCESS */ - $response1 = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'], array_merge([ + // Change the function specs + $function = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -1344,22 +1079,19 @@ class FunctionsCustomServerTest extends Scope 'specification' => Specification::S_1VCPU_1GB, ]); - $this->assertEquals(200, $response1['headers']['status-code']); - $this->assertNotEmpty($response1['body']['$id']); - $this->assertEquals(Specification::S_1VCPU_1GB, $response1['body']['specification']); + $this->assertEquals(200, $function['headers']['status-code']); + $this->assertNotEmpty($function['body']['$id']); + $this->assertEquals(Specification::S_1VCPU_1GB, $function['body']['specification']); - // Test Execution - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + // Verify the updated specs + $execution = $this->createExecution($data['functionId']); $output = json_decode($execution['body']['responseBody'], true); - $this->assertEquals(1, $output['APPWRITE_FUNCTION_CPUS']); $this->assertEquals(1024, $output['APPWRITE_FUNCTION_MEMORY']); - $response2 = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'], array_merge([ + // Change the specs to 1vcpu 512mb + $function = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -1374,25 +1106,21 @@ class FunctionsCustomServerTest extends Scope 'specification' => Specification::S_1VCPU_512MB, ]); - $this->assertEquals(200, $response2['headers']['status-code']); - $this->assertNotEmpty($response2['body']['$id']); - $this->assertEquals(Specification::S_1VCPU_512MB, $response2['body']['specification']); + $this->assertEquals(200, $function['headers']['status-code']); + $this->assertNotEmpty($function['body']['$id']); + $this->assertEquals(Specification::S_1VCPU_512MB, $function['body']['specification']); - // Test Execution - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + // Verify the updated specs + $execution = $this->createExecution($data['functionId']); $output = json_decode($execution['body']['responseBody'], true); - $this->assertEquals(1, $output['APPWRITE_FUNCTION_CPUS']); $this->assertEquals(512, $output['APPWRITE_FUNCTION_MEMORY']); /** * Test for FAILURE */ - $response3 = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'], array_merge([ + $function = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -1406,9 +1134,8 @@ class FunctionsCustomServerTest extends Scope 'entrypoint' => 'index.php', 'specification' => 's-2vcpu-512mb', // Invalid specification ]); - - $this->assertEquals(400, $response3['headers']['status-code']); - $this->assertStringStartsWith('Invalid `specification` param: Specification must be one of:', $response3['body']['message']); + $this->assertEquals(400, $function['headers']['status-code']); + $this->assertStringStartsWith('Invalid `specification` param: Specification must be one of:', $function['body']['message']); return $data; } @@ -1421,24 +1148,15 @@ class FunctionsCustomServerTest extends Scope /** * Test for SUCCESS */ - $function = $this->client->call(Client::METHOD_DELETE, '/functions/' . $data['functionId'] . '/deployments/' . $data['deploymentId'], array_merge([ + $deployment = $this->client->call(Client::METHOD_DELETE, '/functions/' . $data['functionId'] . '/deployments/' . $data['deploymentId'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); + $this->assertEquals(204, $deployment['headers']['status-code']); + $this->assertEmpty($deployment['body']); - $this->assertEquals(204, $function['headers']['status-code']); - $this->assertEmpty($function['body']); - - $function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments/' . $data['deploymentId'], array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $this->assertEquals(404, $function['headers']['status-code']); - - /** - * Test for FAILURE - */ + $deployment = $this->getDeployment($data['functionId'], $data['deploymentId']); + $this->assertEquals(404, $deployment['headers']['status-code']); return $data; } @@ -1446,87 +1164,60 @@ class FunctionsCustomServerTest extends Scope /** * @depends testCreateDeployment */ - public function testDelete($data): array + public function testDeleteFunction($data): array { /** * Test for SUCCESS */ - $function = $this->client->call(Client::METHOD_DELETE, '/functions/' . $data['functionId'], array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - + $function = $this->deleteFunction($data['functionId']); $this->assertEquals(204, $function['headers']['status-code']); $this->assertEmpty($function['body']); - $function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'], array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - + $function = $this->getFunction($data['functionId']); $this->assertEquals(404, $function['headers']['status-code']); - /** - * Test for FAILURE - */ - return $data; } - public function testTimeout() + public function testExecutionTimeout() { - $function = $this->createFunction([ + $functionId = $this->setupFunction([ 'name' => 'timeout-php-8.0', 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', 'events' => [], - 'schedule' => '*/10 * * * *', // Execute every 10 seconds - 'timeout' => 15, + 'schedule' => '', + 'timeout' => 5, // Should timeout after 5 seconds + ]); + $this->setupDeployment($functionId, [ + 'code' => $this->packageFunction('php'), + 'activate' => true, ]); - $functionId = $function['body']['id']; - $this->assertEquals(201, $function['headers']['status-code']); - $deployment = $this->createDeployment($functionId, 'php'); - $deploymentId = $deployment['body']['$id']; - $this->assertDeploymentStatus($functionId, $deploymentId, 'ready'); - - $execution = $this->createExecution($functionId, ['async' => true]); + $execution = $this->createExecution($functionId, [ + 'async' => true + ]); $executionId = $execution['body']['$id'] ?? ''; $this->assertEquals(202, $execution['headers']['status-code']); + \sleep(5); // Wait for the function to timeout + $this->assertEventually(function () use ($functionId, $executionId) { $execution = $this->getExecution($functionId, $executionId); $this->assertEquals(200, $execution['headers']['status-code']); $this->assertEquals('failed', $execution['body']['status']); - $this->assertEquals('failed', $execution['body']['status']); $this->assertEquals(500, $execution['body']['responseStatusCode']); $this->assertGreaterThan(2, $execution['body']['duration']); $this->assertLessThan(20, $execution['body']['duration']); $this->assertEquals('', $execution['body']['responseBody']); $this->assertEquals('', $execution['body']['logs']); $this->assertStringContainsString('timed out', $execution['body']['errors']); - }, 20000, 500); + }, 5000, 250); - $this->assertEventually(function () use ($functionId) { - $executions = $this->listExecutions($functionId, [ - 'queries' => [ - Query::equal('trigger', ['schedule'])->toString(), - ], - ]); - - $this->assertEquals(200, $executions['headers']['status-code']); - $this->assertEquals(1, $executions['body']['total']); - - $lastExecution = $executions['body']['executions'][0]; - $this->assertEquals('schedule', $lastExecution['trigger']); - }, 20000, 500); - - $function = $this->deleteFunction($functionId); - $this->assertEquals(204, $function['headers']['status-code']); + $this->cleanupFunction($functionId); } - /** * * @return array @@ -1554,61 +1245,33 @@ class FunctionsCustomServerTest extends Scope */ public function testCreateCustomExecution(string $folder, string $name, string $entrypoint, string $runtimeName, string $runtimeVersion) { - $timeout = 15; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; - $this->packageCode($folder); - - $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test ' . $name, 'runtime' => $name, 'entrypoint' => $entrypoint, 'events' => [], - 'timeout' => $timeout, + 'timeout' => 15, ]); - $functionId = $function['body']['$id'] ?? ''; - - $this->assertEquals(201, $function['headers']['status-code']); - - $variable = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $variable = $this->createVariable($functionId, [ 'key' => 'CUSTOM_VARIABLE', - 'value' => 'variable', + 'value' => 'variable' ]); - $this->assertEquals(201, $variable['headers']['status-code']); - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $deploymentId = $this->setupDeployment($functionId, [ 'entrypoint' => $entrypoint, - 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), + 'code' => $this->packageFunction($folder), 'activate' => true ]); - $deploymentId = $deployment['body']['$id'] ?? ''; - $this->assertEquals(202, $deployment['headers']['status-code']); - - $this->assertDeployment($function['body']['$id'], $deploymentId); - - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $execution = $this->createExecution($functionId, [ 'body' => 'foobar', 'async' => false ]); - $executionId = $execution['body']['$id'] ?? ''; $output = json_decode($execution['body']['responseBody'], true); - $this->assertEquals(201, $execution['headers']['status-code']); $this->assertEquals('completed', $execution['body']['status']); $this->assertEquals(200, $execution['body']['responseStatusCode']); @@ -1627,11 +1290,7 @@ class FunctionsCustomServerTest extends Scope $this->assertStringContainsString('Amazing Function Log', $execution['body']['logs']); $this->assertEmpty($execution['body']['errors']); - $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - + $executions = $this->listExecutions($functionId); $this->assertEquals($executions['headers']['status-code'], 200); $this->assertEquals($executions['body']['total'], 1); $this->assertIsArray($executions['body']['executions']); @@ -1640,55 +1299,28 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals($executions['body']['executions'][0]['trigger'], 'http'); $this->assertStringContainsString('Amazing Function Log', $executions['body']['executions'][0]['logs']); - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); + $this->cleanupFunction($functionId); } public function testCreateCustomExecutionBinaryResponse() { - $timeout = 15; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/php-binary-response/code.tar.gz"; - $this->packageCode('php-binary-response'); - - $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test PHP Binary executions', 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', - 'timeout' => $timeout, + 'timeout' => 15, 'execute' => ['any'] ]); - - $functionId = $function['body']['$id'] ?? ''; - - $this->assertEquals(201, $function['headers']['status-code']); - - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', - 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), + 'code' => $this->packageFunction('php-binary-response'), 'activate' => true ]); - $deploymentId = $deployment['body']['$id'] ?? ''; - $this->assertEquals(202, $deployment['headers']['status-code']); - - $this->assertDeployment($function['body']['$id'], $deploymentId); - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ 'x-appwrite-project' => $this->getProject()['$id'], - 'accept' => 'multipart/form-data', + 'accept' => 'multipart/form-data', // Accept binary response ], $this->getHeaders()), [ 'body' => null, ]); @@ -1708,7 +1340,7 @@ class FunctionsCustomServerTest extends Scope */ $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ 'x-appwrite-project' => $this->getProject()['$id'], - 'accept' => 'application/json', + 'accept' => 'application/json', // Accept JSON response ], $this->getHeaders()), [ 'body' => null, ]); @@ -1716,56 +1348,29 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(400, $execution['headers']['status-code']); $this->assertStringContainsString('Failed to parse response', $execution['body']['message']); - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); + $this->cleanupFunction($functionId); } public function testCreateCustomExecutionBinaryRequest() { - $timeout = 15; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/php-binary-request/code.tar.gz"; - $this->packageCode('php-binary-request'); - - $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test PHP Binary executions', 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', - 'timeout' => $timeout, + 'timeout' => 15, 'execute' => ['any'] ]); - - $functionId = $function['body']['$id'] ?? ''; - - $this->assertEquals(201, $function['headers']['status-code']); - - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', - 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), + 'code' => $this->packageFunction('php-binary-request'), 'activate' => true ]); - $deploymentId = $deployment['body']['$id'] ?? ''; - $this->assertEquals(202, $deployment['headers']['status-code']); - - $this->assertDeployment($function['body']['$id'], $deploymentId); - $bytes = pack('C*', ...[0, 20, 255]); $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'multipart/form-data', + 'content-type' => 'multipart/form-data', // Send binary request 'x-appwrite-project' => $this->getProject()['$id'], 'accept' => 'application/json', ], $this->getHeaders()), [ @@ -1773,7 +1378,6 @@ class FunctionsCustomServerTest extends Scope ], false); $executionBody = json_decode($execution['body'], true); - $this->assertEquals(201, $execution['headers']['status-code']); $this->assertEquals(\md5($bytes), $executionBody['responseBody']); $this->assertStringStartsWith('application/json', $execution['headers']['content-type']); @@ -1782,7 +1386,7 @@ class FunctionsCustomServerTest extends Scope * Test for FAILURE */ $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', + 'content-type' => 'application/json', // Send JSON headers 'x-appwrite-project' => $this->getProject()['$id'], 'accept' => 'application/json', ], $this->getHeaders()), [ @@ -1792,89 +1396,48 @@ class FunctionsCustomServerTest extends Scope $executionBody = json_decode($execution['body'], true); $this->assertNotEquals(\md5($bytes), $executionBody['responseBody']); - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); + $this->cleanupFunction($functionId); } public function testv2Function() { - $timeout = 15; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/php-v2/code.tar.gz"; - $this->packageCode('php-v2'); - - $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test PHP V2', 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', 'events' => [], - 'timeout' => $timeout, + 'timeout' => 15, ]); - $functionId = $function['body']['$id'] ?? ''; - - $this->assertEquals(201, $function['headers']['status-code']); - - $headers = [ + $variable = $this->client->call(Client::METHOD_PATCH, '/mock/functions-v2', [ 'content-type' => 'application/json', 'origin' => 'http://localhost', 'cookie' => 'a_session_console=' . $this->getRoot()['session'], 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-mode' => 'admin', - ]; - - $variable = $this->client->call(Client::METHOD_PATCH, '/mock/functions-v2', $headers, [ + ], [ 'functionId' => $functionId ]); - $this->assertEquals(204, $variable['headers']['status-code']); - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', - 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), + 'code' => $this->packageFunction('php-v2'), 'activate' => true ]); - $deploymentId = $deployment['body']['$id'] ?? ''; - $this->assertEquals(202, $deployment['headers']['status-code']); - - $this->assertDeployment($function['body']['$id'], $deploymentId); - - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $execution = $this->createExecution($functionId, [ 'body' => 'foobar', 'async' => false ]); - $output = json_decode($execution['body']['responseBody'], true); - $this->assertEquals(201, $execution['headers']['status-code']); $this->assertEquals('completed', $execution['body']['status']); $this->assertEquals(200, $execution['body']['responseStatusCode']); $this->assertEquals(true, $output['v2Woks']); - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); + $this->cleanupFunction($functionId); } public function testGetRuntimes() @@ -1902,14 +1465,7 @@ class FunctionsCustomServerTest extends Scope public function testEventTrigger() { - $timeout = 15; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/php-event/code.tar.gz"; - $this->packageCode('php-event'); - - $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test PHP Event executions', 'runtime' => 'php-8.0', @@ -1917,28 +1473,15 @@ class FunctionsCustomServerTest extends Scope 'events' => [ 'users.*.create', ], - 'timeout' => $timeout, + 'timeout' => 15, ]); - - $functionId = $function['body']['$id'] ?? ''; - - $this->assertEquals(201, $function['headers']['status-code']); - - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', - 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), + 'code' => $this->packageFunction('php-event'), 'activate' => true ]); - $deploymentId = $deployment['body']['$id'] ?? ''; - $this->assertEquals(202, $deployment['headers']['status-code']); - - $this->assertDeployment($function['body']['$id'], $deploymentId); - - // Create user to trigger event + // Create user as an event trigger $user = $this->client->call(Client::METHOD_POST, '/users', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1946,111 +1489,57 @@ class FunctionsCustomServerTest extends Scope 'userId' => 'unique()', 'name' => 'Event User' ]); - - $userId = $user['body']['$id']; + $userId = $user['body']['$id'] ?? ''; $this->assertEquals(201, $user['headers']['status-code']); - $execution = null; - $this->assertEventually(function () use ($functionId, &$execution) { - $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ]); + $this->assertEventually(function () use ($functionId, $userId) { + $executions = $this->listExecutions($functionId); + $lastExecution = $executions['body']['executions'][0]; + $this->assertEquals('completed', $lastExecution['status']); + $this->assertEquals(204, $lastExecution['responseStatusCode']); + $this->assertStringContainsString($userId, $lastExecution['logs']); + $this->assertStringContainsString('Event User', $lastExecution['logs']); + }, 10000, 500); - if (empty($executions['body']['executions'])) { - throw new \Exception('No executions found yet'); - } + $this->cleanupFunction($functionId); - $execution = $executions['body']['executions'][0]; - $this->assertEquals('completed', $execution['status']); - }, 30000, 1000); - - $this->assertEquals(204, $execution['responseStatusCode']); - $this->assertStringContainsString($userId, $execution['logs']); - $this->assertStringContainsString('Event User', $execution['logs']); - - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ + // Cleanup user + $user = $this->client->call(Client::METHOD_DELETE, '/users/' . $userId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ], []); - - $this->assertEquals(204, $response['headers']['status-code']); - - // Cleanup : Delete user - $response = $this->client->call(Client::METHOD_DELETE, '/users/' . $userId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); + $this->assertEquals(204, $user['headers']['status-code']); } public function testScopes() { - $timeout = 15; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/php-scopes/code.tar.gz"; - $this->packageCode('php-scopes'); - - $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test PHP Scopes executions', 'commands' => 'composer update --no-interaction --ignore-platform-reqs --optimize-autoloader --prefer-dist --no-dev', 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', 'scopes' => ['users.read'], - 'timeout' => $timeout, + 'timeout' => 15, ]); - $functionId = $function['body']['$id'] ?? ''; - - $this->assertEquals(201, $function['headers']['status-code']); - - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $deploymentId = $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', 'commands' => 'sh setup.sh && composer install', - 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), - 'activate' => true + 'code' => $this->packageFunction('php-scopes'), + 'activate' => true, ]); - $deploymentId = $deployment['body']['$id'] ?? ''; - $this->assertEquals(202, $deployment['headers']['status-code']); - - $this->assertDeployment($function['body']['$id'], $deploymentId); - - $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); + $deployment = $this->getDeployment($functionId, $deploymentId); $this->assertEquals(200, $deployment['headers']['status-code']); $this->assertStringContainsStringIgnoringCase("200 OK", $deployment['body']['buildLogs']); $this->assertStringContainsStringIgnoringCase('"total":', $deployment['body']['buildLogs']); $this->assertStringContainsStringIgnoringCase('"users":', $deployment['body']['buildLogs']); - $deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $deployment['headers']['status-code']); - - // Wait a little for activation to finish - sleep(5); - - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'async' => false + $execution = $this->createExecution($functionId, [ + 'async' => false, ]); $this->assertEquals(201, $execution['headers']['status-code']); @@ -2060,81 +1549,44 @@ class FunctionsCustomServerTest extends Scope $this->assertNotEmpty($execution['body']['responseBody']); $this->assertStringContainsString("total", $execution['body']['responseBody']); - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'async' => true + $execution = $this->createExecution($functionId, [ + 'async' => true, ]); - + $executionId = $execution['body']['$id'] ?? ''; $this->assertEquals(202, $execution['headers']['status-code']); $this->assertNotEmpty($execution['body']['$id']); - \sleep(10); + $this->assertEventually(function () use ($functionId, $executionId) { + $execution = $this->getExecution($functionId, $executionId); - $execution = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $execution['body']['$id'], array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); + $this->assertEquals(200, $execution['headers']['status-code']); + $this->assertEquals('completed', $execution['body']['status']); + $this->assertEquals(200, $execution['body']['responseStatusCode']); + $this->assertGreaterThan(0, $execution['body']['duration']); + $this->assertNotEmpty($execution['body']['responseBody']); + $this->assertStringContainsString("total", $execution['body']['responseBody']); + }, 5000, 250); - $this->assertEquals(200, $execution['headers']['status-code']); - $this->assertEquals('completed', $execution['body']['status']); - $this->assertEquals(200, $execution['body']['responseStatusCode']); - $this->assertGreaterThan(0, $execution['body']['duration']); - $this->assertNotEmpty($execution['body']['logs']); - $this->assertStringContainsString("total", $execution['body']['logs']); - - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); + $this->cleanupFunction($functionId); } public function testCookieExecution() { - $timeout = 15; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/php-cookie/code.tar.gz"; - $this->packageCode('php-cookie'); - - $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test PHP Cookie executions', 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', - 'timeout' => $timeout, + 'timeout' => 15, ]); - - $functionId = $function['body']['$id'] ?? ''; - - $this->assertEquals(201, $function['headers']['status-code']); - - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', - 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), + 'code' => $this->packageFunction('php-cookie'), 'activate' => true ]); - $deploymentId = $deployment['body']['$id'] ?? ''; - $this->assertEquals(202, $deployment['headers']['status-code']); - - $this->assertDeployment($function['body']['$id'], $deploymentId); - $cookie = 'cookieName=cookieValue; cookie2=value2; cookie3=value=3; cookie4=val:ue4; cookie5=value5'; - - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $execution = $this->createExecution($functionId, [ 'async' => false, 'headers' => [ 'cookie' => $cookie @@ -2147,38 +1599,20 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals($cookie, $execution['body']['responseBody']); $this->assertGreaterThan(0, $execution['body']['duration']); - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); + $this->cleanupFunction($functionId); } public function testFunctionsDomain() { - $timeout = 15; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/php-cookie/code.tar.gz"; - $this->packageCode('php-cookie'); - - $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test PHP Cookie executions', 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', - 'timeout' => $timeout, + 'timeout' => 15, 'execute' => ['any'] ]); - $functionId = $function['body']['$id'] ?? ''; - - $this->assertEquals(201, $function['headers']['status-code']); - $rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2196,20 +1630,12 @@ class FunctionsCustomServerTest extends Scope $domain = $rules['body']['rules'][0]['domain']; - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', - 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), + 'code' => $this->packageFunction('php-cookie'), 'activate' => true ]); - $deploymentId = $deployment['body']['$id'] ?? ''; - $this->assertEquals(202, $deployment['headers']['status-code']); - - $this->assertDeployment($function['body']['$id'], $deploymentId); - $cookie = 'cookieName=cookieValue; cookie2=value2; cookie3=value=3; cookie4=val:ue4; cookie5=value5'; $proxyClient = new Client(); @@ -2227,64 +1653,31 @@ class FunctionsCustomServerTest extends Scope // Await Aggregation sleep(App::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', 30)); - $tries = 0; - while (true) { - try { - $response = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/usage', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'] - ], $this->getHeaders()), [ - 'range' => '24h' - ]); + $this->assertEventually(function () use ($functionId) { + $response = $this->getFunctionUsage($functionId, [ + 'range' => '24h' + ]); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(19, count($response['body'])); - $this->assertEquals('24h', $response['body']['range']); - $this->assertEquals(1, $response['body']['executionsTotal']); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(19, count($response['body'])); + $this->assertEquals('24h', $response['body']['range']); + $this->assertEquals(1, $response['body']['executionsTotal']); + }, 25000, 1000); - break; - } catch (ExpectationFailedException $th) { - if ($tries >= 5) { - throw $th; - } else { - $tries++; - sleep(5); - } - } - } - - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); + $this->cleanupFunction($functionId); } public function testFunctionsDomainBinaryResponse() { - $timeout = 15; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/php-binary-response/code.tar.gz"; - $this->packageCode('php-binary-response'); - - $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test PHP Binary executions', 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', - 'timeout' => $timeout, + 'timeout' => 15, 'execute' => ['any'] ]); - $functionId = $function['body']['$id'] ?? ''; - - $this->assertEquals(201, $function['headers']['status-code']); - $rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2302,20 +1695,11 @@ class FunctionsCustomServerTest extends Scope $domain = $rules['body']['rules'][0]['domain']; - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', - 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), - 'activate' => true + 'code' => $this->packageFunction('php-binary-response'), ]); - $deploymentId = $deployment['body']['$id'] ?? ''; - $this->assertEquals(202, $deployment['headers']['status-code']); - - $this->assertDeployment($function['body']['$id'], $deploymentId); - $proxyClient = new Client(); $proxyClient->setEndpoint('http://' . $domain); @@ -2329,38 +1713,20 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(10, $bytes['byte2']); $this->assertEquals(255, $bytes['byte3']); - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); + $this->cleanupFunction($functionId); } public function testFunctionsDomainBinaryRequest() { - $timeout = 15; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/php-binary-request/code.tar.gz"; - $this->packageCode('php-binary-request'); - - $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test PHP Binary executions', 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', - 'timeout' => $timeout, + 'timeout' => 15, 'execute' => ['any'] ]); - $functionId = $function['body']['$id'] ?? ''; - - $this->assertEquals(201, $function['headers']['status-code']); - $rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2378,20 +1744,12 @@ class FunctionsCustomServerTest extends Scope $domain = $rules['body']['rules'][0]['domain']; - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', - 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), + 'code' => $this->packageFunction('php-binary-request'), 'activate' => true ]); - $deploymentId = $deployment['body']['$id'] ?? ''; - $this->assertEquals(202, $deployment['headers']['status-code']); - - $this->assertDeployment($function['body']['$id'], $deploymentId); - $proxyClient = new Client(); $proxyClient->setEndpoint('http://' . $domain); @@ -2402,19 +1760,12 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(\md5($bytes), $response['body']); - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); + $this->cleanupFunction($functionId); } public function testCreateFunctionWithResponseFormatHeader() { - $response = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ + $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-response-format' => '1.5.0', // add response format header @@ -2426,29 +1777,16 @@ class FunctionsCustomServerTest extends Scope 'timeout' => 15, ]); - $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals(201, $function['headers']['status-code']); - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $response['body']['$id'], [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); + $functionId = $function['body']['$id'] ?? ''; - $this->assertEquals(204, $response['headers']['status-code']); + $this->cleanupFunction($functionId); } public function testFunctionLogging() { - // Preparations: Create Function - $folder = 'node'; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; - $this->packageCode($folder); - - $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $function = $this->createFunction([ 'functionId' => ID::unique(), 'runtime' => 'node-18.0', 'name' => 'Logging Test', @@ -2461,38 +1799,22 @@ class FunctionsCustomServerTest extends Scope $this->assertFalse($function['body']['logging']); $this->assertNotEmpty($function['body']['$id']); - $functionId = $function['body']['$id']; + $functionId = $functionId = $function['body']['$id'] ?? ''; - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ - 'content-type' => 'multipart/form-data', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), + $this->setupDeployment($functionId, [ + 'code' => $this->packageFunction('node'), 'activate' => true ]); - $this->assertEquals(202, $deployment['headers']['status-code']); - $this->assertNotEmpty($deployment['body']['$id']); - - $deploymentId = $deployment['body']['$id'] ?? ''; - - $this->assertDeployment($functionId, $deploymentId); - // Sync Executions test - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); + $execution = $this->createExecution($functionId); $this->assertEquals(201, $execution['headers']['status-code']); $this->assertEmpty($execution['body']['logs']); $this->assertEmpty($execution['body']['errors']); // Async Executions test - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $execution = $this->createExecution($functionId, [ 'async' => true ]); @@ -2501,19 +1823,16 @@ class FunctionsCustomServerTest extends Scope $this->assertEmpty($execution['body']['errors']); $this->assertNotEmpty($execution['body']['$id']); - $executionId = $execution['body']['$id']; + $executionId = $execution['body']['$id'] ?? ''; - sleep(5); + $this->assertEventually(function () use ($functionId, $executionId) { + $execution = $this->getExecution($functionId, $executionId); - $execution = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $this->assertEquals(200, $execution['headers']['status-code']); - $this->assertEquals('completed', $execution['body']['status']); - $this->assertEmpty($execution['body']['logs']); - $this->assertEmpty($execution['body']['errors']); + $this->assertEquals(200, $execution['headers']['status-code']); + $this->assertEquals('completed', $execution['body']['status']); + $this->assertEmpty($execution['body']['logs']); + $this->assertEmpty($execution['body']['errors']); + }, 5000, 250); // Domain Executions test $rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([ @@ -2541,10 +1860,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); - $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ + $executions = $this->listExecutions($functionId, [ 'queries' => [ Query::limit(1)->toString(), Query::orderDesc('$id')->toString(), @@ -2557,10 +1873,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEmpty($executions['body']['executions'][0]['errors']); // Ensure executions count - $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + $executions = $this->listExecutions($functionId); $this->assertEquals(200, $executions['headers']['status-code']); $this->assertCount(3, $executions['body']['executions']); @@ -2571,13 +1884,6 @@ class FunctionsCustomServerTest extends Scope $this->assertEmpty($execution['errors']); } - // Cleanup : Delete function - $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ], []); - - $this->assertEquals(204, $response['headers']['status-code']); + $this->cleanupFunction($functionId); } } From 32657e22cc1e8d3ade1dca3fb52962277a4fe54f Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 19 Sep 2024 20:34:38 +0100 Subject: [PATCH 06/32] fix: conflicts --- tests/e2e/Services/Functions/FunctionsBase.php | 2 +- tests/e2e/Services/Functions/FunctionsCustomServerTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index 7cfa04b0ac..04039e3acc 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -146,7 +146,7 @@ trait FunctionsBase $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; if (!file_exists($code)) { - Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr); + Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->output, $this->output); } if (filesize($code) > 1024 * 1024 * 5) { throw new \Exception('Code package is too large. Use the chunked upload method instead.'); diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index e383abe36f..1796871c26 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -513,7 +513,7 @@ class FunctionsCustomServerTest extends Scope $folder = 'php-large'; $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; - Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr); + Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->output, $this->output); $chunkSize = 5 * 1024 * 1024; $handle = @fopen($code, "rb"); From 504711845aae015cbc487e000f5b944a33094fc9 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 19 Sep 2024 20:46:05 +0100 Subject: [PATCH 07/32] fix: stdout --- tests/e2e/Services/Functions/FunctionsBase.php | 2 +- tests/e2e/Services/Functions/FunctionsCustomServerTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index 04039e3acc..d2bb19dea2 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -146,7 +146,7 @@ trait FunctionsBase $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; if (!file_exists($code)) { - Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->output, $this->output); + Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->output); } if (filesize($code) > 1024 * 1024 * 5) { throw new \Exception('Code package is too large. Use the chunked upload method instead.'); diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 1796871c26..7c4cd6d1ee 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -513,7 +513,7 @@ class FunctionsCustomServerTest extends Scope $folder = 'php-large'; $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; - Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->output, $this->output); + Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->output); $chunkSize = 5 * 1024 * 1024; $handle = @fopen($code, "rb"); From 65c0db32852f3eb41a560034b5fb64876d67e859 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 19 Sep 2024 20:58:09 +0100 Subject: [PATCH 08/32] fix: package function --- tests/e2e/Services/Functions/FunctionsBase.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index d2bb19dea2..5ed87b4895 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -141,18 +141,20 @@ trait FunctionsBase return $executions; } - protected function packageFunction(string $folder = 'php'): CURLFile + protected function packageFunction(string $function = 'php'): CURLFile { - $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; + $folderPath = realpath(__DIR__ . '/../../../resources/functions') . "/$function"; + $tarPath = "$folderPath/code.tar.gz"; - if (!file_exists($code)) { - Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->output); + if (!file_exists($tarPath)) { + Console::execute("cd $folderPath && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->output); } - if (filesize($code) > 1024 * 1024 * 5) { + + if (filesize($tarPath) > 1024 * 1024 * 5) { throw new \Exception('Code package is too large. Use the chunked upload method instead.'); } - return new CURLFile($code, 'application/x-gzip', \basename($code)); + return new CURLFile($tarPath, 'application/x-gzip', \basename($tarPath)); } protected function createDeployment( From cb0416be7bd294725c4c6c455c44c05d973f38c9 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 19 Sep 2024 21:08:04 +0100 Subject: [PATCH 09/32] chore: fmt --- tests/e2e/General/UsageTest.php | 7 ++----- .../Functions/FunctionsCustomServerTest.php | 2 +- tests/e2e/Services/GraphQL/Base.php | 19 +++++++++++++++---- .../Services/GraphQL/FunctionsClientTest.php | 7 +------ .../Services/GraphQL/FunctionsServerTest.php | 7 +------ .../Realtime/RealtimeConsoleClientTest.php | 9 +-------- 6 files changed, 21 insertions(+), 30 deletions(-) diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index d3623acffc..69460aa4cd 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -629,9 +629,6 @@ class UsageTest extends Scope $this->assertEquals(201, $response['headers']['status-code']); $this->assertNotEmpty($response['body']['$id']); - $code = realpath(__DIR__ . '/../../resources/functions') . "/php/code.tar.gz"; - $this->packageCode('php'); - $response = $this->client->call( Client::METHOD_POST, '/functions/' . $functionId . '/deployments', @@ -641,8 +638,8 @@ class UsageTest extends Scope ], $this->getHeaders()), [ 'entrypoint' => 'index.php', - 'code' => new CURLFile($code, 'application/x-gzip', \basename($code)), - 'activate' => true + 'code' => $this->packageFunction('php'), + 'activate' => true, ] ); diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 7c4cd6d1ee..4ff6639864 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -775,7 +775,7 @@ class FunctionsCustomServerTest extends Scope $matchingDeployment = array_filter( $deployments['body']['deployments'], - fn($deployment) => $deployment['$id'] === $deploymentId + fn ($deployment) => $deployment['$id'] === $deploymentId ); $this->assertNotEmpty($matchingDeployment, "Deployment with ID {$deploymentId} not found"); diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index b77f006bf8..449275f102 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -2,6 +2,7 @@ namespace Tests\E2E\Services\GraphQL; +use CURLFile; use Utopia\CLI\Console; trait Base @@ -2493,11 +2494,21 @@ trait Base } // Function-related methods - protected string $stdout = ''; - protected string $stderr = ''; + protected string $output = ''; - protected function packageCode($folder): void + protected function packageFunction(string $function = 'php'): CURLFile { - Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout); + $folderPath = realpath(__DIR__ . '/../../../resources/functions') . "/$function"; + $tarPath = "$folderPath/code.tar.gz"; + + if (!file_exists($tarPath)) { + Console::execute("cd $folderPath && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->output); + } + + if (filesize($tarPath) > 1024 * 1024 * 5) { + throw new \Exception('Code package is too large. Use the chunked upload method instead.'); + } + + return new CURLFile($tarPath, 'application/x-gzip', \basename($tarPath)); } } diff --git a/tests/e2e/Services/GraphQL/FunctionsClientTest.php b/tests/e2e/Services/GraphQL/FunctionsClientTest.php index 3f0ee1966a..e7e8421254 100644 --- a/tests/e2e/Services/GraphQL/FunctionsClientTest.php +++ b/tests/e2e/Services/GraphQL/FunctionsClientTest.php @@ -2,7 +2,6 @@ namespace Tests\E2E\Services\GraphQL; -use CURLFile; use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; @@ -83,10 +82,6 @@ class FunctionsClientTest extends Scope $projectId = $this->getProject()['$id']; $query = $this->getQuery(self::$CREATE_DEPLOYMENT); - $folder = 'php'; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; - $this->packageCode($folder); - $gqlPayload = [ 'operations' => \json_encode([ 'query' => $query, @@ -99,7 +94,7 @@ class FunctionsClientTest extends Scope 'map' => \json_encode([ 'code' => ["variables.code"] ]), - 'code' => new CURLFile($code, 'application/gzip', 'code.tar.gz'), + 'code' => $this->packageFunction('php') ]; $deployment = $this->client->call(Client::METHOD_POST, '/graphql', [ diff --git a/tests/e2e/Services/GraphQL/FunctionsServerTest.php b/tests/e2e/Services/GraphQL/FunctionsServerTest.php index 25a671fa1c..c3606244c4 100644 --- a/tests/e2e/Services/GraphQL/FunctionsServerTest.php +++ b/tests/e2e/Services/GraphQL/FunctionsServerTest.php @@ -2,7 +2,6 @@ namespace Tests\E2E\Services\GraphQL; -use CURLFile; use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; @@ -82,10 +81,6 @@ class FunctionsServerTest extends Scope $projectId = $this->getProject()['$id']; $query = $this->getQuery(self::$CREATE_DEPLOYMENT); - $folder = 'php'; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; - $this->packageCode($folder); - $gqlPayload = [ 'operations' => \json_encode([ 'query' => $query, @@ -98,7 +93,7 @@ class FunctionsServerTest extends Scope 'map' => \json_encode([ 'code' => ["variables.code"] ]), - 'code' => new CURLFile($code, 'application/gzip', 'code.tar.gz'), + 'code' => $this->packageFunction('php'), ]; $deployment = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([ diff --git a/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php index 6ab2874f8e..b1777cdba9 100644 --- a/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php @@ -2,7 +2,6 @@ namespace Tests\E2E\Services\Realtime; -use CURLFile; use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; @@ -506,21 +505,15 @@ class RealtimeConsoleClientTest extends Scope * Test Create Deployment */ - $folder = 'php'; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz"; - $this->packageCode($folder); - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ 'content-type' => 'multipart/form-data', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'entrypoint' => 'index.php', - 'code' => new CURLFile($code, 'application/x-gzip', \basename($code)), + 'code' => $this->packageFunction('php'), 'activate' => true ]); - $deploymentId = $deployment['body']['$id'] ?? ''; - $this->assertEquals(202, $deployment['headers']['status-code']); $response = json_decode($client->receive(), true); From 410a35deb510535de565915921afa6c09add1075 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 19 Sep 2024 21:29:18 +0100 Subject: [PATCH 10/32] fix: spacing --- .../Functions/FunctionsCustomServerTest.php | 123 ++++++++++++++---- 1 file changed, 100 insertions(+), 23 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 4ff6639864..47e80efde9 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -38,7 +38,9 @@ class FunctionsCustomServerTest extends Scope 'schedule' => '0 0 1 1 *', 'timeout' => 10, ]); + $functionId = $functionId = $function['body']['$id'] ?? ''; + $dateValidator = new DatetimeValidator(); $this->assertEquals(201, $function['headers']['status-code']); $this->assertNotEmpty($function['body']['$id']); @@ -66,6 +68,7 @@ class FunctionsCustomServerTest extends Scope 'key' => 'funcKey3', 'value' => 'funcValue3', ]); + $this->assertEquals(201, $variable['headers']['status-code']); $this->assertEquals(201, $variable2['headers']['status-code']); $this->assertEquals(201, $variable3['headers']['status-code']); @@ -87,6 +90,7 @@ class FunctionsCustomServerTest extends Scope $functions = $this->listFunctions([ 'search' => $data['functionId'] ]); + $this->assertEquals($functions['headers']['status-code'], 200); $this->assertCount(1, $functions['body']['functions']); $this->assertEquals($functions['body']['functions'][0]['name'], 'Test'); @@ -97,6 +101,7 @@ class FunctionsCustomServerTest extends Scope Query::limit(1)->toString(), ], ]); + $this->assertEquals($functions['headers']['status-code'], 200); $this->assertCount(1, $functions['body']['functions']); @@ -106,6 +111,7 @@ class FunctionsCustomServerTest extends Scope Query::offset(1)->toString(), ], ]); + $this->assertEquals($functions['headers']['status-code'], 200); $this->assertCount(0, $functions['body']['functions']); @@ -115,6 +121,7 @@ class FunctionsCustomServerTest extends Scope Query::equal('enabled', [true])->toString(), ], ]); + $this->assertEquals($functions['headers']['status-code'], 200); $this->assertCount(1, $functions['body']['functions']); @@ -124,6 +131,7 @@ class FunctionsCustomServerTest extends Scope Query::equal('enabled', [false])->toString(), ], ]); + $this->assertEquals($functions['headers']['status-code'], 200); $this->assertCount(0, $functions['body']['functions']); @@ -131,6 +139,7 @@ class FunctionsCustomServerTest extends Scope $functions = $this->listFunctions([ 'search' => 'Test' ]); + $this->assertEquals($functions['headers']['status-code'], 200); $this->assertCount(1, $functions['body']['functions']); $this->assertEquals($functions['body']['functions'][0]['$id'], $data['functionId']); @@ -139,6 +148,7 @@ class FunctionsCustomServerTest extends Scope $functions = $this->listExecutions([ 'search' => 'php-8.0' ]); + $this->assertEquals($functions['headers']['status-code'], 200); $this->assertCount(1, $functions['body']['functions']); $this->assertEquals($functions['body']['functions'][0]['$id'], $data['functionId']); @@ -160,6 +170,7 @@ class FunctionsCustomServerTest extends Scope ]); $functions = $this->listFunctions(); + $this->assertEquals($functions['headers']['status-code'], 200); $this->assertEquals($functions['body']['total'], 2); $this->assertIsArray($functions['body']['functions']); @@ -172,6 +183,7 @@ class FunctionsCustomServerTest extends Scope Query::cursorAfter(new Document(['$id' => $functions['body']['functions'][0]['$id']]))->toString(), ], ]); + $this->assertEquals($functions['headers']['status-code'], 200); $this->assertCount(1, $functions['body']['functions']); $this->assertEquals($functions['body']['functions'][0]['name'], 'Test 2'); @@ -181,6 +193,7 @@ class FunctionsCustomServerTest extends Scope Query::cursorBefore(new Document(['$id' => $functions['body']['functions'][1]['$id']]))->toString(), ], ]); + $this->assertEquals($functions['headers']['status-code'], 200); $this->assertCount(1, $functions['body']['functions']); $this->assertEquals($functions['body']['functions'][0]['name'], 'Test'); @@ -207,6 +220,7 @@ class FunctionsCustomServerTest extends Scope * Test for SUCCESS */ $function = $this->getFunction($data['functionId']); + $this->assertEquals($function['headers']['status-code'], 200); $this->assertEquals($function['body']['name'], 'Test'); @@ -214,6 +228,7 @@ class FunctionsCustomServerTest extends Scope * Test for FAILURE */ $function = $this->getFunction('x'); + $this->assertEquals($function['headers']['status-code'], 404); return $data; @@ -242,10 +257,11 @@ class FunctionsCustomServerTest extends Scope 'entrypoint' => 'index.php', ]); + $dateValidator = new DatetimeValidator(); + $this->assertEquals(200, $function['headers']['status-code']); $this->assertNotEmpty($function['body']['$id']); $this->assertEquals('Test1', $function['body']['name']); - $dateValidator = new DatetimeValidator(); $this->assertEquals(true, $dateValidator->isValid($function['body']['$createdAt'])); $this->assertEquals(true, $dateValidator->isValid($function['body']['$updatedAt'])); $this->assertEquals('', $function['body']['deployment']); @@ -261,11 +277,8 @@ class FunctionsCustomServerTest extends Scope 'key' => 'GLOBAL_VARIABLE', 'value' => 'Global Variable Value', ]); - $this->assertEquals(201, $variable['headers']['status-code']); - /** - * Test for FAILURE - */ + $this->assertEquals(201, $variable['headers']['status-code']); return $data; } @@ -296,13 +309,15 @@ class FunctionsCustomServerTest extends Scope 'code' => $this->packageFunction('php'), 'activate' => true ]); - $deploymentId = $deployment['body']['$id'] ?? ''; + $this->assertEquals(202, $deployment['headers']['status-code']); + $deploymentId = $deployment['body']['$id'] ?? ''; + $this->assertEventually(function () use ($functionId, $deploymentId) { $deployment = $this->getDeployment($functionId, $deploymentId); $this->assertEquals(200, $deployment['headers']['status-code']); - $this->assertEquals('completed', $deployment['body']['status']); + $this->assertEquals('ready', $deployment['body']['status']); $this->assertEquals('cli', $deployment['body']['type']); }, 500000, 1000); } @@ -338,23 +353,28 @@ class FunctionsCustomServerTest extends Scope 'templateVersion' => $starterTemplate['body']['providerVersion'], ] ); - $functionId = $functionId = $function['body']['$id'] ?? ''; + $this->assertEquals(201, $function['headers']['status-code']); $this->assertNotEmpty($function['body']['$id']); + $functionId = $functionId = $function['body']['$id'] ?? ''; + $deployments = $this->listDeployments($functionId); + $this->assertEquals(200, $deployments['headers']['status-code']); $this->assertEquals(1, $deployments['body']['total']); $lastDeployment = $deployments['body']['deployments'][0]; + $this->assertNotEmpty($lastDeployment['$id']); $this->assertEquals(0, $lastDeployment['size']); $deploymentId = $lastDeployment['$id']; + $this->assertEventually(function () use ($functionId, $deploymentId) { $deployment = $this->getDeployment($functionId, $deploymentId); $this->assertEquals(200, $deployment['headers']['status-code']); - $this->assertEquals('completed', $deployment['body']['status']); + $this->assertEquals('ready', $deployment['body']['status']); $this->assertEquals('cli', $deployment['body']['type']); }, 500000, 1000); @@ -366,6 +386,7 @@ class FunctionsCustomServerTest extends Scope $execution = $this->createExecution($functionId, [ 'path' => '/ping', ]); + $this->assertEquals(201, $execution['headers']['status-code']); $this->assertEquals("completed", $execution['body']['status']); $this->assertEquals(200, $execution['body']['responseStatusCode']); @@ -378,10 +399,12 @@ class FunctionsCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ], $this->getHeaders()), []); + $this->assertEquals(200, $users['headers']['status-code']); $this->assertIsInt($users['body']['total']); $totalUsers = $users['body']['total']; + $this->assertStringContainsString("Total users: " . $totalUsers, $execution['body']['logs']); // Execute function again but async @@ -389,13 +412,16 @@ class FunctionsCustomServerTest extends Scope 'path' => '/ping', 'async' => true ]); - $executionId = $execution['body']['$id'] ?? ''; + $this->assertEquals(202, $execution['headers']['status-code']); $this->assertNotEmpty($execution['body']['$id']); $this->assertEquals('waiting', $execution['body']['status']); + $executionId = $execution['body']['$id'] ?? ''; + $this->assertEventually(function () use ($functionId, $executionId, $totalUsers) { $execution = $this->getExecution($functionId, $executionId); + $this->assertEquals(200, $execution['headers']['status-code']); $this->assertEquals(200, $execution['body']['responseStatusCode']); $this->assertEquals('completed', $execution['body']['status']); @@ -405,7 +431,6 @@ class FunctionsCustomServerTest extends Scope }, 100000, 250); $function = $this->deleteFunction($functionId); - $this->assertEquals(204, $function['headers']['status-code']); } /** @@ -417,34 +442,39 @@ class FunctionsCustomServerTest extends Scope * Test for SUCCESS */ $functionId = $data['functionId']; + $deployment = $this->createDeployment($functionId, [ 'code' => $this->packageFunction('php'), 'activate' => true ]); - $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); $this->assertNotEmpty($deployment['body']['$id']); $this->assertEquals(true, (new DatetimeValidator())->isValid($deployment['body']['$createdAt'])); $this->assertEquals('index.php', $deployment['body']['entrypoint']); + $deploymentId = $deployment['body']['$id'] ?? ''; + $deployment = $this->createDeployment($functionId, [ 'code' => $this->packageFunction('php'), 'activate' => false ]); + $deploymentIdInactive = $deployment['body']['$id']; + $this->assertEquals(202, $deployment['headers']['status-code']); $this->assertNotEmpty($deployment['body']['$id']); $this->assertEventually(function () use ($functionId, $deploymentId, $deploymentIdInactive) { $deployment = $this->getDeployment($functionId, $deploymentId); - $this->assertEquals('completed', $deployment['body']['status']); + $this->assertEquals('ready', $deployment['body']['status']); $deployment = $this->getDeployment($functionId, $deploymentIdInactive); - $this->assertEquals('completed', $deployment['body']['status']); + $this->assertEquals('ready', $deployment['body']['status']); }, 500000, 1000); $function = $this->getFunction($functionId); + $this->assertEquals(200, $function['headers']['status-code']); $this->assertEquals($deploymentId, $function['body']['deployment']); $this->assertNotEquals($deploymentIdInactive, $function['body']['deployment']); @@ -453,6 +483,7 @@ class FunctionsCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); + $this->assertEquals(204, $deployment['headers']['status-code']); return array_merge($data, ['deploymentId' => $deploymentId]); @@ -464,10 +495,12 @@ class FunctionsCustomServerTest extends Scope public function testCancelDeploymentBuild($data): void { $functionId = $data['functionId']; + $deployment = $this->createDeployment($functionId, [ 'code' => $this->packageFunction('php'), 'activate' => false ]); + $deploymentId = $deployment['body']['$id'] ?? ''; $this->assertEquals(202, $deployment['headers']['status-code']); @@ -477,6 +510,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEventually(function () use ($functionId, $deploymentId) { $deployment = $this->getDeployment($functionId, $deploymentId); + $this->assertEquals(200, $deployment['headers']['status-code']); $this->assertEquals('building', $deployment['body']['status']); }, 100000, 250); @@ -486,6 +520,7 @@ class FunctionsCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); + $this->assertEquals(200, $cancel['headers']['status-code']); $this->assertEquals('canceled', $cancel['body']['status']); @@ -497,6 +532,7 @@ class FunctionsCustomServerTest extends Scope \sleep(30); $deployment = $this->getDeployment($functionId, $deploymentId); + $this->assertEquals(200, $deployment['headers']['status-code']); $this->assertEquals('canceled', $deployment['body']['status']); } @@ -554,8 +590,9 @@ class FunctionsCustomServerTest extends Scope $this->assertEventually(function () use ($functionId, $deploymentId, $deploymentSize) { $deployment = $this->getDeployment($functionId, $deploymentId); + $this->assertEquals(200, $deployment['headers']['status-code']); - $this->assertEquals('completed', $deployment['body']['status']); + $this->assertEquals('ready', $deployment['body']['status']); $this->assertEquals($deploymentSize, $deployment['body']['size']); $this->assertGreaterThan(1024 * 1024 * 10, $deployment['body']['buildSize']); // ~7MB video file + 10MB sample file }, 500000, 1000); @@ -609,6 +646,7 @@ class FunctionsCustomServerTest extends Scope $deployments = $this->listDeployments($functionId, [ 'search' => $data['functionId'] ]); + $this->assertEquals($deployments['headers']['status-code'], 200); $this->assertEquals(3, $deployments['body']['total']); $this->assertIsArray($deployments['body']['deployments']); @@ -621,6 +659,7 @@ class FunctionsCustomServerTest extends Scope Query::limit(1)->toString(), ], ]); + $this->assertEquals($deployments['headers']['status-code'], 200); $this->assertCount(1, $deployments['body']['deployments']); @@ -655,7 +694,6 @@ class FunctionsCustomServerTest extends Scope 'search' => 'Test' ]); - $this->assertEquals($deployments['headers']['status-code'], 200); $this->assertEquals(3, $deployments['body']['total']); $this->assertIsArray($deployments['body']['deployments']); @@ -753,6 +791,7 @@ class FunctionsCustomServerTest extends Scope Query::limit(1)->toString(), ] ); + $this->assertEquals(200, $deployments['headers']['status-code']); $this->assertGreaterThanOrEqual(1, $deployments['body']['total']); $this->assertNotEmpty($deployments['body']['deployments'][0]['$id']); @@ -775,7 +814,7 @@ class FunctionsCustomServerTest extends Scope $matchingDeployment = array_filter( $deployments['body']['deployments'], - fn ($deployment) => $deployment['$id'] === $deploymentId + fn($deployment) => $deployment['$id'] === $deploymentId ); $this->assertNotEmpty($matchingDeployment, "Deployment with ID {$deploymentId} not found"); @@ -797,6 +836,7 @@ class FunctionsCustomServerTest extends Scope * Test for SUCCESS */ $deployment = $this->getDeployment($data['functionId'], $data['deploymentId']); + $this->assertEquals(200, $deployment['headers']['status-code']); $this->assertGreaterThan(0, $deployment['body']['buildTime']); $this->assertNotEmpty($deployment['body']['status']); @@ -808,6 +848,7 @@ class FunctionsCustomServerTest extends Scope * Test for FAILURE */ $deployment = $this->getDeployment($data['functionId'], 'x'); + $this->assertEquals($deployment['headers']['status-code'], 404); return $data; @@ -824,7 +865,6 @@ class FunctionsCustomServerTest extends Scope $execution = $this->createExecution($data['functionId'], [ 'async' => false, ]); - $executionId = $execution['body']['$id'] ?? ''; $this->assertEquals(201, $execution['headers']['status-code']); $this->assertNotEmpty($execution['body']['$id']); @@ -845,10 +885,13 @@ class FunctionsCustomServerTest extends Scope $this->assertNotEmpty($execution['body']['logs']); $this->assertLessThan(10, $execution['body']['duration']); + $executionId = $execution['body']['$id'] ?? ''; + $execution = $this->createExecution($data['functionId'], [ 'async' => false, 'path' => '/?code=400' ]); + $this->assertEquals(201, $execution['headers']['status-code']); $this->assertEquals('completed', $execution['body']['status']); $this->assertEquals(400, $execution['body']['responseStatusCode']); @@ -859,6 +902,7 @@ class FunctionsCustomServerTest extends Scope ], $this->getHeaders()), []); $execution = $this->createExecution($data['functionId']); + $this->assertEquals(204, $execution['headers']['status-code']); return array_merge($data, ['executionId' => $executionId]); @@ -873,6 +917,7 @@ class FunctionsCustomServerTest extends Scope * Test for SUCCESS */ $executions = $this->listExecutions($data['functionId']); + $this->assertEquals($executions['headers']['status-code'], 200); $this->assertEquals($executions['body']['total'], 1); $this->assertIsArray($executions['body']['executions']); @@ -884,6 +929,7 @@ class FunctionsCustomServerTest extends Scope Query::limit(1)->toString(), ], ]); + $this->assertEquals(200, $executions['headers']['status-code']); $this->assertCount(1, $executions['body']['executions']); @@ -892,6 +938,7 @@ class FunctionsCustomServerTest extends Scope Query::offset(1)->toString(), ], ]); + $this->assertEquals(200, $executions['headers']['status-code']); $this->assertCount(0, $executions['body']['executions']); @@ -900,6 +947,7 @@ class FunctionsCustomServerTest extends Scope Query::equal('trigger', ['http'])->toString(), ], ]); + $this->assertEquals(200, $executions['headers']['status-code']); $this->assertCount(1, $executions['body']['executions']); @@ -909,6 +957,7 @@ class FunctionsCustomServerTest extends Scope $executions = $this->listExecutions($data['functionId'], [ 'search' => $data['executionId'], ]); + $this->assertEquals(200, $executions['headers']['status-code']); $this->assertEquals(1, $executions['body']['total']); $this->assertIsInt($executions['body']['total']); @@ -918,6 +967,7 @@ class FunctionsCustomServerTest extends Scope $executions = $this->listExecutions($data['functionId'], [ 'search' => $data['functionId'], ]); + $this->assertEquals(200, $executions['headers']['status-code']); $this->assertEquals(1, $executions['body']['total']); $this->assertIsInt($executions['body']['total']); @@ -938,6 +988,7 @@ class FunctionsCustomServerTest extends Scope $execution = $this->createExecution($data['functionId'], [ // Testing default value, should be 'async' => false ]); + $this->assertEquals(201, $execution['headers']['status-code']); $this->assertEquals('completed', $execution['body']['status']); $this->assertEquals(200, $execution['body']['responseStatusCode']); @@ -960,6 +1011,7 @@ class FunctionsCustomServerTest extends Scope * Test for SUCCESS */ $execution = $this->getExecution($data['functionId'], $data['executionId']); + $this->assertEquals($execution['headers']['status-code'], 200); $this->assertEquals($execution['body']['$id'], $data['executionId']); @@ -967,6 +1019,7 @@ class FunctionsCustomServerTest extends Scope * Test for FAILURE */ $function = $this->getExecution($data['functionId'], 'x'); + $this->assertEquals($function['headers']['status-code'], 404); return $data; @@ -1008,6 +1061,7 @@ class FunctionsCustomServerTest extends Scope ]); $executionId = $execution['body']['$id'] ?? ''; + $this->assertEquals(202, $execution['headers']['status-code']); $execution = $this->client->call(Client::METHOD_DELETE, '/functions/' . $data['functionId'] . '/executions/' . $executionId, array_merge([ @@ -1039,6 +1093,7 @@ class FunctionsCustomServerTest extends Scope ]); $executionId = $execution['body']['$id'] ?? ''; + $this->assertEquals(202, $execution['headers']['status-code']); $execution = $this->client->call(Client::METHOD_DELETE, '/functions/' . $data['functionId'] . '/executions/' . $executionId, [ @@ -1083,6 +1138,7 @@ class FunctionsCustomServerTest extends Scope $execution = $this->createExecution($data['functionId']); $output = json_decode($execution['body']['responseBody'], true); + $this->assertEquals(1, $output['APPWRITE_FUNCTION_CPUS']); $this->assertEquals(1024, $output['APPWRITE_FUNCTION_MEMORY']); @@ -1110,6 +1166,7 @@ class FunctionsCustomServerTest extends Scope $execution = $this->createExecution($data['functionId']); $output = json_decode($execution['body']['responseBody'], true); + $this->assertEquals(1, $output['APPWRITE_FUNCTION_CPUS']); $this->assertEquals(512, $output['APPWRITE_FUNCTION_MEMORY']); @@ -1130,6 +1187,7 @@ class FunctionsCustomServerTest extends Scope 'entrypoint' => 'index.php', 'specification' => 's-2vcpu-512mb', // Invalid specification ]); + $this->assertEquals(400, $function['headers']['status-code']); $this->assertStringStartsWith('Invalid `specification` param: Specification must be one of:', $function['body']['message']); @@ -1148,10 +1206,12 @@ class FunctionsCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); + $this->assertEquals(204, $deployment['headers']['status-code']); $this->assertEmpty($deployment['body']); $deployment = $this->getDeployment($data['functionId'], $data['deploymentId']); + $this->assertEquals(404, $deployment['headers']['status-code']); return $data; @@ -1166,10 +1226,12 @@ class FunctionsCustomServerTest extends Scope * Test for SUCCESS */ $function = $this->deleteFunction($data['functionId']); + $this->assertEquals(204, $function['headers']['status-code']); $this->assertEmpty($function['body']); $function = $this->getFunction($data['functionId']); + $this->assertEquals(404, $function['headers']['status-code']); return $data; @@ -1193,9 +1255,11 @@ class FunctionsCustomServerTest extends Scope $execution = $this->createExecution($functionId, [ 'async' => true ]); - $executionId = $execution['body']['$id'] ?? ''; + $this->assertEquals(202, $execution['headers']['status-code']); + $executionId = $execution['body']['$id'] ?? ''; + \sleep(5); // Wait for the function to timeout $this->assertEventually(function () use ($functionId, $executionId) { @@ -1254,6 +1318,7 @@ class FunctionsCustomServerTest extends Scope 'key' => 'CUSTOM_VARIABLE', 'value' => 'variable' ]); + $this->assertEquals(201, $variable['headers']['status-code']); $deploymentId = $this->setupDeployment($functionId, [ @@ -1266,7 +1331,7 @@ class FunctionsCustomServerTest extends Scope 'body' => 'foobar', 'async' => false ]); - $executionId = $execution['body']['$id'] ?? ''; + $output = json_decode($execution['body']['responseBody'], true); $this->assertEquals(201, $execution['headers']['status-code']); $this->assertEquals('completed', $execution['body']['status']); @@ -1286,7 +1351,10 @@ class FunctionsCustomServerTest extends Scope $this->assertStringContainsString('Amazing Function Log', $execution['body']['logs']); $this->assertEmpty($execution['body']['errors']); + $executionId = $execution['body']['$id'] ?? ''; + $executions = $this->listExecutions($functionId); + $this->assertEquals($executions['headers']['status-code'], 200); $this->assertEquals($executions['body']['total'], 1); $this->assertIsArray($executions['body']['executions']); @@ -1374,6 +1442,7 @@ class FunctionsCustomServerTest extends Scope ], false); $executionBody = json_decode($execution['body'], true); + $this->assertEquals(201, $execution['headers']['status-code']); $this->assertEquals(\md5($bytes), $executionBody['responseBody']); $this->assertStringStartsWith('application/json', $execution['headers']['content-type']); @@ -1390,6 +1459,7 @@ class FunctionsCustomServerTest extends Scope ], false); $executionBody = json_decode($execution['body'], true); + $this->assertNotEquals(\md5($bytes), $executionBody['responseBody']); $this->cleanupFunction($functionId); @@ -1427,6 +1497,7 @@ class FunctionsCustomServerTest extends Scope 'body' => 'foobar', 'async' => false ]); + $output = json_decode($execution['body']['responseBody'], true); $this->assertEquals(201, $execution['headers']['status-code']); $this->assertEquals('completed', $execution['body']['status']); @@ -1485,12 +1556,16 @@ class FunctionsCustomServerTest extends Scope 'userId' => 'unique()', 'name' => 'Event User' ]); - $userId = $user['body']['$id'] ?? ''; + $this->assertEquals(201, $user['headers']['status-code']); + $userId = $user['body']['$id'] ?? ''; + $this->assertEventually(function () use ($functionId, $userId) { $executions = $this->listExecutions($functionId); + $lastExecution = $executions['body']['executions'][0]; + $this->assertEquals('completed', $lastExecution['status']); $this->assertEquals(204, $lastExecution['responseStatusCode']); $this->assertStringContainsString($userId, $lastExecution['logs']); @@ -1548,10 +1623,12 @@ class FunctionsCustomServerTest extends Scope $execution = $this->createExecution($functionId, [ 'async' => true, ]); - $executionId = $execution['body']['$id'] ?? ''; + $this->assertEquals(202, $execution['headers']['status-code']); $this->assertNotEmpty($execution['body']['$id']); + $executionId = $execution['body']['$id'] ?? ''; + $this->assertEventually(function () use ($functionId, $executionId) { $execution = $this->getExecution($functionId, $executionId); @@ -1646,7 +1723,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($cookie, $response['body']); - // Await Aggregation + // Wait for usage to be aggregated sleep(System::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', 30)); $this->assertEventually(function () use ($functionId) { From 906ef59d95b221af625d508d54551e5c8d88fff7 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 19 Sep 2024 21:36:19 +0100 Subject: [PATCH 11/32] chore: fmt --- tests/e2e/Services/Functions/FunctionsCustomServerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 47e80efde9..82f80a8cd8 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -814,7 +814,7 @@ class FunctionsCustomServerTest extends Scope $matchingDeployment = array_filter( $deployments['body']['deployments'], - fn($deployment) => $deployment['$id'] === $deploymentId + fn ($deployment) => $deployment['$id'] === $deploymentId ); $this->assertNotEmpty($matchingDeployment, "Deployment with ID {$deploymentId} not found"); From 19ea682a52e462418223654dc4e8f1fe3f8b2f1b Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 19 Sep 2024 21:54:25 +0100 Subject: [PATCH 12/32] chore: timeouts --- .../Services/Functions/FunctionsCustomClientTest.php | 2 +- .../Services/Functions/FunctionsCustomServerTest.php | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index dd950ca655..53713cfd40 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -244,7 +244,7 @@ class FunctionsCustomClientTest extends Scope $execution = $this->getExecution($functionId, $executionId); $this->assertEquals('completed', $execution['body']['status']); $this->assertEquals(200, $execution['body']['responseStatusCode']); - }, 10000, 250); + }, 10000, 500); return [ 'functionId' => $functionId diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 82f80a8cd8..b895e4c938 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -373,12 +373,14 @@ class FunctionsCustomServerTest extends Scope $this->assertEventually(function () use ($functionId, $deploymentId) { $deployment = $this->getDeployment($functionId, $deploymentId); + $this->assertEquals(200, $deployment['headers']['status-code']); $this->assertEquals('ready', $deployment['body']['status']); $this->assertEquals('cli', $deployment['body']['type']); }, 500000, 1000); $function = $this->getFunction($functionId); + $this->assertEquals(200, $function['headers']['status-code']); $this->assertEquals($deploymentId, $function['body']['deployment']); @@ -428,7 +430,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEmpty($execution['body']['responseBody']); $this->assertEmpty($execution['body']['errors']); $this->assertStringContainsString("Total users: " . $totalUsers, $execution['body']['logs']); - }, 100000, 250); + }, 10000, 500); $function = $this->deleteFunction($functionId); } @@ -1273,7 +1275,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals('', $execution['body']['responseBody']); $this->assertEquals('', $execution['body']['logs']); $this->assertStringContainsString('timed out', $execution['body']['errors']); - }, 5000, 250); + }, 10000, 500); $this->cleanupFunction($functionId); } @@ -1638,7 +1640,7 @@ class FunctionsCustomServerTest extends Scope $this->assertGreaterThan(0, $execution['body']['duration']); $this->assertNotEmpty($execution['body']['responseBody']); $this->assertStringContainsString("total", $execution['body']['responseBody']); - }, 5000, 250); + }, 10000, 500); $this->cleanupFunction($functionId); } @@ -1905,7 +1907,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals('completed', $execution['body']['status']); $this->assertEmpty($execution['body']['logs']); $this->assertEmpty($execution['body']['errors']); - }, 5000, 250); + }, 10000, 500); // Domain Executions test $rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([ From 92ce8d711d923a1b4398d5d1d6cc7ad847dfc8ff Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 19 Sep 2024 22:10:04 +0100 Subject: [PATCH 13/32] feat: split scheduled tests --- .../Functions/FunctionsConsoleClientTest.php | 12 +- .../Functions/FunctionsCustomClientTest.php | 115 ---------- .../Functions/FunctionsCustomServerTest.php | 36 +--- .../Functions/FunctionsScheduleTest.php | 203 ++++++++++++++++++ 4 files changed, 216 insertions(+), 150 deletions(-) create mode 100644 tests/e2e/Services/Functions/FunctionsScheduleTest.php diff --git a/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php b/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php index cbfca09564..3a02cbcba2 100644 --- a/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php @@ -30,9 +30,11 @@ class FunctionsConsoleClientTest extends Scope 'schedule' => '0 0 1 1 *', 'timeout' => 10, ]); - $functionId = $function['body']['$id']; + $this->assertEquals(201, $function['headers']['status-code']); + $functionId = $function['body']['$id']; + $function2 = $this->createFunction([ 'functionId' => ID::unique(), 'name' => 'Test Failure', @@ -40,6 +42,7 @@ class FunctionsConsoleClientTest extends Scope 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', ]); + $this->assertEquals(400, $function2['headers']['status-code']); return [ @@ -110,9 +113,11 @@ class FunctionsConsoleClientTest extends Scope 'value' => 'TESTINGVALUE' ] ); - $variableId = $variable['body']['$id']; + $this->assertEquals(201, $variable['headers']['status-code']); + $variableId = $variable['body']['$id']; + /** * Test for FAILURE */ @@ -124,6 +129,7 @@ class FunctionsConsoleClientTest extends Scope 'value' => 'ANOTHERTESTINGVALUE' ] ); + $this->assertEquals(409, $variable['headers']['status-code']); // Test for invalid key @@ -134,6 +140,7 @@ class FunctionsConsoleClientTest extends Scope 'value' => 'TESTINGVALUE' ] ); + $this->assertEquals(400, $variable['headers']['status-code']); // Test for invalid value @@ -144,6 +151,7 @@ class FunctionsConsoleClientTest extends Scope 'value' => str_repeat("#", 8193), ] ); + $this->assertEquals(400, $variable['headers']['status-code']); return array_merge( diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 53713cfd40..3447ee0547 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -48,7 +48,6 @@ class FunctionsCustomClientTest extends Scope 'users.*.create', 'users.*.delete', ], - 'schedule' => '* * * * *', // Execute every 60 seconds 'timeout' => 10, ]); $this->setupDeployment($functionId, [ @@ -72,124 +71,10 @@ class FunctionsCustomClientTest extends Scope ]); $this->assertEquals(202, $execution['headers']['status-code']); - // Wait for scheduled execution - \sleep(60); - - $this->assertEventually(function () use ($functionId) { - $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['key'], - ]); - $this->assertEquals(200, $executions['headers']['status-code']); - $this->assertCount(2, $executions['body']['executions']); - - $asyncExecution = $executions['body']['executions'][1]; - $this->assertEquals('schedule', $asyncExecution['trigger']); - $this->assertEquals('completed', $asyncExecution['status']); - $this->assertEquals(200, $asyncExecution['responseStatusCode']); - $this->assertEquals('', $asyncExecution['responseBody']); - $this->assertNotEmpty($asyncExecution['logs']); - $this->assertNotEmpty($asyncExecution['errors']); - $this->assertGreaterThan(0, $asyncExecution['duration']); - }, 10000, 500); - $this->cleanupFunction($functionId); } - public function testCreateScheduledExecution(): void - { - /** - * Test for SUCCESS - */ - $functionId = $this->setupFunction([ - 'functionId' => ID::unique(), - 'name' => 'Test', - 'execute' => [Role::user($this->getUser()['$id'])->toString()], - 'runtime' => 'php-8.0', - 'entrypoint' => 'index.php', - 'timeout' => 10, - 'logging' => true, - ]); - $this->setupDeployment($functionId, [ - 'entrypoint' => 'index.php', - 'code' => $this->packageFunction('php'), - 'activate' => true - ]); - // Schedule execution for the future - \date_default_timezone_set('UTC'); - $futureTime = (new \DateTime())->add(new \DateInterval('PT2M')); // 2 minute in the future - $futureTime->setTime($futureTime->format('H'), $futureTime->format('i'), 0, 0); - - $execution = $this->createExecution($functionId, [ - 'async' => true, - 'scheduledAt' => $futureTime->format(\DateTime::ATOM), - 'path' => '/custom-path', - 'method' => 'PATCH', - 'body' => 'custom-body', - 'headers' => [ - 'x-custom-header' => 'custom-value' - ] - ]); - $executionId = $execution['body']['$id']; - - $this->assertEquals(202, $execution['headers']['status-code']); - $this->assertEquals('scheduled', $execution['body']['status']); - $this->assertEquals('PATCH', $execution['body']['requestMethod']); - $this->assertEquals('/custom-path', $execution['body']['requestPath']); - $this->assertCount(0, $execution['body']['requestHeaders']); - - \sleep(120); - - $this->assertEventually(function () use ($functionId, $executionId) { - $execution = $this->getExecution($functionId, $executionId); - - $this->assertEquals(200, $execution['headers']['status-code']); - $this->assertEquals(200, $execution['body']['responseStatusCode']); - $this->assertEquals('completed', $execution['body']['status']); - $this->assertEquals('/custom-path', $execution['body']['requestPath']); - $this->assertEquals('PATCH', $execution['body']['requestMethod']); - $this->assertStringContainsString('body-is-custom-body', $execution['body']['logs']); - $this->assertStringContainsString('custom-header-is-custom-value', $execution['body']['logs']); - $this->assertStringContainsString('method-is-patch', $execution['body']['logs']); - $this->assertStringContainsString('path-is-/custom-path', $execution['body']['logs']); - $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); - - /* Test for FAILURE */ - // Schedule synchronous execution - $execution = $this->createExecution($functionId, [ - 'async' => false, - 'scheduledAt' => $futureTime->format(\DateTime::ATOM), - ]); - $this->assertEquals(400, $execution['headers']['status-code']); - - // Execution with seconds precision - $execution = $this->createExecution($functionId, [ - 'async' => true, - 'scheduledAt' => (new \DateTime("2100-12-08 16:12:02"))->format(\DateTime::ATOM) - ]); - $this->assertEquals(400, $execution['headers']['status-code']); - - // Execution with milliseconds precision - $execution = $this->createExecution($functionId, [ - 'async' => true, - 'scheduledAt' => (new \DateTime("2100-12-08 16:12:02.255"))->format(\DateTime::ATOM) - ]); - $this->assertEquals(400, $execution['headers']['status-code']); - - // Execution too soon - $execution = $this->createExecution($functionId, [ - 'async' => true, - 'scheduledAt' => (new \DateTime())->add(new \DateInterval('PT1S'))->format(\DateTime::ATOM) - ]); - $this->assertEquals(400, $execution['headers']['status-code']); - - $this->cleanupFunction($functionId, $executionId); - } public function testCreateCustomExecution(): array { diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index b895e4c938..71dce2258c 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -35,7 +35,6 @@ class FunctionsCustomServerTest extends Scope 'buckets.*.create', 'buckets.*.delete', ], - 'schedule' => '0 0 1 1 *', 'timeout' => 10, ]); @@ -53,7 +52,7 @@ class FunctionsCustomServerTest extends Scope 'buckets.*.create', 'buckets.*.delete', ], $function['body']['events']); - $this->assertEquals('0 0 1 1 *', $function['body']['schedule']); + $this->assertEmpty($function['body']['schedule']); $this->assertEquals(10, $function['body']['timeout']); $variable = $this->createVariable($functionId, [ @@ -156,7 +155,7 @@ class FunctionsCustomServerTest extends Scope /** * Test pagination */ - $functionId = $this->setupFunction([ + $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test 2', 'runtime' => 'php-8.0', @@ -165,7 +164,6 @@ class FunctionsCustomServerTest extends Scope 'buckets.*.create', 'buckets.*.delete', ], - 'schedule' => '0 0 1 1 *', 'timeout' => 10, ]); @@ -816,7 +814,7 @@ class FunctionsCustomServerTest extends Scope $matchingDeployment = array_filter( $deployments['body']['deployments'], - fn ($deployment) => $deployment['$id'] === $deploymentId + fn($deployment) => $deployment['$id'] === $deploymentId ); $this->assertNotEmpty($matchingDeployment, "Deployment with ID {$deploymentId} not found"); @@ -1078,35 +1076,7 @@ class FunctionsCustomServerTest extends Scope return $data; } - /** - * @depends testGetExecution - */ - public function testDeleteScheduledExecution($data): array - { - $futureTime = (new \DateTime())->add(new \DateInterval('PT10H')); - $futureTime->setTime($futureTime->format('H'), $futureTime->format('i'), 0, 0); - $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/executions', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'async' => true, - 'scheduledAt' => $futureTime->format('Y-m-d H:i:s'), - ]); - - $executionId = $execution['body']['$id'] ?? ''; - - $this->assertEquals(202, $execution['headers']['status-code']); - - $execution = $this->client->call(Client::METHOD_DELETE, '/functions/' . $data['functionId'] . '/executions/' . $executionId, [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ]); - - $this->assertEquals(204, $execution['headers']['status-code']); - - return $data; - } /** * @depends testGetExecution diff --git a/tests/e2e/Services/Functions/FunctionsScheduleTest.php b/tests/e2e/Services/Functions/FunctionsScheduleTest.php new file mode 100644 index 0000000000..bab9a2a684 --- /dev/null +++ b/tests/e2e/Services/Functions/FunctionsScheduleTest.php @@ -0,0 +1,203 @@ +setupFunction([ + 'functionId' => ID::unique(), + 'name' => 'Test', + 'execute' => [Role::user($this->getUser()['$id'])->toString()], + 'runtime' => 'php-8.0', + 'entrypoint' => 'index.php', + 'events' => [ + 'users.*.create', + 'users.*.delete', + ], + 'schedule' => '* * * * *', // Execute every 60 seconds + 'timeout' => 10, + ]); + + $this->setupDeployment($functionId, [ + 'entrypoint' => 'index.php', + 'code' => $this->packageFunction('php'), + 'activate' => true + ]); + + // Wait for scheduled execution + \sleep(60); + + $this->assertEventually(function () use ($functionId) { + $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['key'], + ]); + + $this->assertEquals(200, $executions['headers']['status-code']); + $this->assertCount(1, $executions['body']['executions']); + + $asyncExecution = $executions['body']['executions'][0]; + + $this->assertEquals('schedule', $asyncExecution['trigger']); + $this->assertEquals('completed', $asyncExecution['status']); + $this->assertEquals(200, $asyncExecution['responseStatusCode']); + $this->assertEquals('', $asyncExecution['responseBody']); + $this->assertNotEmpty($asyncExecution['logs']); + $this->assertNotEmpty($asyncExecution['errors']); + $this->assertGreaterThan(0, $asyncExecution['duration']); + }, 10000, 500); + + $this->cleanupFunction($functionId); + } + + public function testCreateScheduledAtExecution(): void + { + /** + * Test for SUCCESS + */ + $functionId = $this->setupFunction([ + 'functionId' => ID::unique(), + 'name' => 'Test', + 'execute' => [Role::user($this->getUser()['$id'])->toString()], + 'runtime' => 'php-8.0', + 'entrypoint' => 'index.php', + 'timeout' => 10, + 'logging' => true, + ]); + $this->setupDeployment($functionId, [ + 'entrypoint' => 'index.php', + 'code' => $this->packageFunction('php'), + 'activate' => true + ]); + + // Schedule execution for the future + \date_default_timezone_set('UTC'); + $futureTime = (new \DateTime())->add(new \DateInterval('PT2M')); // 2 minute in the future + $futureTime->setTime($futureTime->format('H'), $futureTime->format('i'), 0, 0); + + $execution = $this->createExecution($functionId, [ + 'async' => true, + 'scheduledAt' => $futureTime->format(\DateTime::ATOM), + 'path' => '/custom-path', + 'method' => 'PATCH', + 'body' => 'custom-body', + 'headers' => [ + 'x-custom-header' => 'custom-value' + ] + ]); + $executionId = $execution['body']['$id']; + + $this->assertEquals(202, $execution['headers']['status-code']); + $this->assertEquals('scheduled', $execution['body']['status']); + $this->assertEquals('PATCH', $execution['body']['requestMethod']); + $this->assertEquals('/custom-path', $execution['body']['requestPath']); + $this->assertCount(0, $execution['body']['requestHeaders']); + + \sleep(120); + + $this->assertEventually(function () use ($functionId, $executionId) { + $execution = $this->getExecution($functionId, $executionId); + + $this->assertEquals(200, $execution['headers']['status-code']); + $this->assertEquals(200, $execution['body']['responseStatusCode']); + $this->assertEquals('completed', $execution['body']['status']); + $this->assertEquals('/custom-path', $execution['body']['requestPath']); + $this->assertEquals('PATCH', $execution['body']['requestMethod']); + $this->assertStringContainsString('body-is-custom-body', $execution['body']['logs']); + $this->assertStringContainsString('custom-header-is-custom-value', $execution['body']['logs']); + $this->assertStringContainsString('method-is-patch', $execution['body']['logs']); + $this->assertStringContainsString('path-is-/custom-path', $execution['body']['logs']); + $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); + + /* Test for FAILURE */ + // Schedule synchronous execution + $execution = $this->createExecution($functionId, [ + 'async' => false, + 'scheduledAt' => $futureTime->format(\DateTime::ATOM), + ]); + $this->assertEquals(400, $execution['headers']['status-code']); + + // Execution with seconds precision + $execution = $this->createExecution($functionId, [ + 'async' => true, + 'scheduledAt' => (new \DateTime("2100-12-08 16:12:02"))->format(\DateTime::ATOM) + ]); + $this->assertEquals(400, $execution['headers']['status-code']); + + // Execution with milliseconds precision + $execution = $this->createExecution($functionId, [ + 'async' => true, + 'scheduledAt' => (new \DateTime("2100-12-08 16:12:02.255"))->format(\DateTime::ATOM) + ]); + $this->assertEquals(400, $execution['headers']['status-code']); + + // Execution too soon + $execution = $this->createExecution($functionId, [ + 'async' => true, + 'scheduledAt' => (new \DateTime())->add(new \DateInterval('PT1S'))->format(\DateTime::ATOM) + ]); + $this->assertEquals(400, $execution['headers']['status-code']); + + $this->cleanupFunction($functionId, $executionId); + } + + public function testDeleteScheduledExecution() + { + $functionId = $this->setupFunction([ + 'functionId' => ID::unique(), + 'name' => 'Test', + 'execute' => [Role::user($this->getUser()['$id'])->toString()], + 'runtime' => 'php-8.0', + 'entrypoint' => 'index.php', + 'timeout' => 10, + 'logging' => true, + ]); + + $this->setupDeployment($functionId, [ + 'entrypoint' => 'index.php', + 'code' => $this->packageFunction('php'), + 'activate' => true + ]); + + $futureTime = (new \DateTime())->add(new \DateInterval('PT10H')); + $futureTime->setTime($futureTime->format('H'), $futureTime->format('i'), 0, 0); + + $execution = $this->createExecution($functionId, [ + 'async' => true, + 'scheduledAt' => $futureTime->format('Y-m-d H:i:s'), + ]); + + $this->assertEquals(202, $execution['headers']['status-code']); + + $executionId = $execution['body']['$id'] ?? ''; + + $execution = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(204, $execution['headers']['status-code']); + + $this->cleanupFunction($functionId); + } +} From f04e47b3d455b99ed6d36d04baa6c61143e05313 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 19 Sep 2024 22:11:44 +0100 Subject: [PATCH 14/32] chore: fmt --- tests/e2e/Services/Functions/FunctionsCustomServerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 71dce2258c..5db051da2b 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -814,7 +814,7 @@ class FunctionsCustomServerTest extends Scope $matchingDeployment = array_filter( $deployments['body']['deployments'], - fn($deployment) => $deployment['$id'] === $deploymentId + fn ($deployment) => $deployment['$id'] === $deploymentId ); $this->assertNotEmpty($matchingDeployment, "Deployment with ID {$deploymentId} not found"); From 321227db15a5fc9dbbba5ecc667b6dbcd7cbd05c Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 20 Sep 2024 09:15:05 +0100 Subject: [PATCH 15/32] fix: seperate tests --- .github/workflows/tests.yml | 3 ++- tests/e2e/Services/Functions/FunctionsBase.php | 18 ++++++++---------- .../Functions/FunctionsCustomServerTest.php | 11 ++++++----- .../FunctionsScheduleTest.php | 6 +++--- 4 files changed, 19 insertions(+), 19 deletions(-) rename tests/e2e/Services/{Functions => FunctionsSchedule}/FunctionsScheduleTest.php (98%) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 633bd46ea4..5b82cba594 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -113,6 +113,7 @@ jobs: Console, Databases, Functions, + FunctionsSchedule, GraphQL, Health, Locale, @@ -141,7 +142,7 @@ jobs: run: | docker load --input /tmp/${{ env.IMAGE }}.tar docker compose up -d - sleep 25 + sleep 10 - name: Run ${{matrix.service}} Tests run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index 5ed87b4895..8b19c65705 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -101,7 +101,7 @@ trait FunctionsBase return $deployment; } - protected function getExecution($functionId, $executionId) + protected function getExecution(string $functionId, $executionId): mixed { $execution = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([ 'content-type' => 'application/json', @@ -111,7 +111,7 @@ trait FunctionsBase return $execution; } - protected function listFunctions($params = []) + protected function listFunctions(mixed $params = []): mixed { $functions = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ 'content-type' => 'application/json', @@ -121,7 +121,7 @@ trait FunctionsBase return $functions; } - protected function listDeployments($functionId, $params = []) + protected function listDeployments(string $functionId, $params = []): mixed { $deployments = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments', array_merge([ 'content-type' => 'application/json', @@ -131,7 +131,7 @@ trait FunctionsBase return $deployments; } - protected function listExecutions($functionId, $params = []) + protected function listExecutions(string $functionId, mixed $params = []): mixed { $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([ 'content-type' => 'application/json', @@ -157,10 +157,8 @@ trait FunctionsBase return new CURLFile($tarPath, 'application/x-gzip', \basename($tarPath)); } - protected function createDeployment( - string $functionId, - mixed $params - ) { + protected function createDeployment(string $functionId, mixed $params = []): mixed + { $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ 'content-type' => 'multipart/form-data', 'x-appwrite-project' => $this->getProject()['$id'], @@ -189,7 +187,7 @@ trait FunctionsBase return $template; } - protected function createExecution(string $functionId, mixed $params = []) + protected function createExecution(string $functionId, mixed $params = []): mixed { $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ 'content-type' => 'application/json', @@ -199,7 +197,7 @@ trait FunctionsBase return $execution; } - protected function deleteFunction($functionId) + protected function deleteFunction(string $functionId): mixed { $function = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 5db051da2b..b57cd3d0b8 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -144,7 +144,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals($functions['body']['functions'][0]['$id'], $data['functionId']); // Test search runtime - $functions = $this->listExecutions([ + $functions = $this->listFunctions([ 'search' => 'php-8.0' ]); @@ -490,7 +490,7 @@ class FunctionsCustomServerTest extends Scope } /** - * @depends testUpdate + * @depends testUpdateFunction */ public function testCancelDeploymentBuild($data): void { @@ -538,7 +538,7 @@ class FunctionsCustomServerTest extends Scope } /** - * @depends testUpdate + * @depends testUpdateFunction */ public function testCreateDeploymentLarge($data): array { @@ -814,7 +814,7 @@ class FunctionsCustomServerTest extends Scope $matchingDeployment = array_filter( $deployments['body']['deployments'], - fn ($deployment) => $deployment['$id'] === $deploymentId + fn($deployment) => $deployment['$id'] === $deploymentId ); $this->assertNotEmpty($matchingDeployment, "Deployment with ID {$deploymentId} not found"); @@ -1273,7 +1273,7 @@ class FunctionsCustomServerTest extends Scope * @param string $entrypoint * * @dataProvider provideCustomExecutions - * @depends testTimeout + * @depends testExecutionTimeout */ public function testCreateCustomExecution(string $folder, string $name, string $entrypoint, string $runtimeName, string $runtimeVersion) { @@ -1743,6 +1743,7 @@ class FunctionsCustomServerTest extends Scope $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', 'code' => $this->packageFunction('php-binary-response'), + 'activate' => true ]); $proxyClient = new Client(); diff --git a/tests/e2e/Services/Functions/FunctionsScheduleTest.php b/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php similarity index 98% rename from tests/e2e/Services/Functions/FunctionsScheduleTest.php rename to tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php index bab9a2a684..f39efb42b5 100644 --- a/tests/e2e/Services/Functions/FunctionsScheduleTest.php +++ b/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php @@ -6,14 +6,14 @@ use Appwrite\ID; use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; -use Tests\E2E\Scopes\SideServer; +use Tests\E2E\Scopes\SideClient; use Utopia\Database\Helpers\Role; class FunctionsScheduleTest extends Scope { use FunctionsBase; use ProjectCustom; - use SideServer; + use SideClient; public function testCreateScheduledExecution() { @@ -47,7 +47,7 @@ class FunctionsScheduleTest extends Scope $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['key'], + 'x-appwrite-key' => $this->getProject()['apiKey'], ]); $this->assertEquals(200, $executions['headers']['status-code']); From 72ddef855ed6452c7ffd9dd5d82ec43908ef01f0 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 20 Sep 2024 09:16:18 +0100 Subject: [PATCH 16/32] chore: fmt --- tests/e2e/Services/Functions/FunctionsCustomServerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index b57cd3d0b8..e9297a109f 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -814,7 +814,7 @@ class FunctionsCustomServerTest extends Scope $matchingDeployment = array_filter( $deployments['body']['deployments'], - fn($deployment) => $deployment['$id'] === $deploymentId + fn ($deployment) => $deployment['$id'] === $deploymentId ); $this->assertNotEmpty($matchingDeployment, "Deployment with ID {$deploymentId} not found"); From e4ad2ed566e349c1bfa849074bcbebeafaf8446f Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 20 Sep 2024 09:33:26 +0100 Subject: [PATCH 17/32] fix: realtime tests --- .../FunctionsScheduleTest.php | 33 ++++++++++++------- tests/e2e/Services/Realtime/RealtimeBase.php | 21 ++++++++++++ .../Realtime/RealtimeCustomClientTest.php | 9 +---- 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php b/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php index f39efb42b5..7e36662e9e 100644 --- a/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php +++ b/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php @@ -6,14 +6,14 @@ use Appwrite\ID; use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; -use Tests\E2E\Scopes\SideClient; +use Tests\E2E\Scopes\SideServer; use Utopia\Database\Helpers\Role; class FunctionsScheduleTest extends Scope { use FunctionsBase; use ProjectCustom; - use SideClient; + use SideServer; public function testCreateScheduledExecution() { @@ -92,16 +92,27 @@ class FunctionsScheduleTest extends Scope $futureTime = (new \DateTime())->add(new \DateInterval('PT2M')); // 2 minute in the future $futureTime->setTime($futureTime->format('H'), $futureTime->format('i'), 0, 0); - $execution = $this->createExecution($functionId, [ - 'async' => true, - 'scheduledAt' => $futureTime->format(\DateTime::ATOM), - 'path' => '/custom-path', - 'method' => 'PATCH', - 'body' => 'custom-body', - 'headers' => [ - 'x-custom-header' => 'custom-value' + + $execution = $this->client->call( + Client::METHOD_POST, + '/functions/' . $functionId . '/executions', + [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'origin' => 'http://localhost', + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $this->getUser()['session'], + ], + [ + 'async' => true, + 'scheduledAt' => $futureTime->format(\DateTime::ATOM), + 'path' => '/custom-path', + 'method' => 'PATCH', + 'body' => 'custom-body', + 'headers' => [ + 'x-custom-header' => 'custom-value' + ] ] - ]); + ); $executionId = $execution['body']['$id']; $this->assertEquals(202, $execution['headers']['status-code']); diff --git a/tests/e2e/Services/Realtime/RealtimeBase.php b/tests/e2e/Services/Realtime/RealtimeBase.php index 30c411ba93..f0cd0e7c3b 100644 --- a/tests/e2e/Services/Realtime/RealtimeBase.php +++ b/tests/e2e/Services/Realtime/RealtimeBase.php @@ -2,6 +2,8 @@ namespace Tests\E2E\Services\Realtime; +use CURLFile; +use Utopia\CLI\Console; use WebSocket\Client as WebSocketClient; use WebSocket\ConnectionException; @@ -71,4 +73,23 @@ trait RealtimeBase $this->expectException(ConnectionException::class); // Check if server disconnnected client $client->close(); } + + // Function-related methods + protected string $output = ''; + + protected function packageFunction(string $function = 'php'): CURLFile + { + $folderPath = realpath(__DIR__ . '/../../../resources/functions') . "/$function"; + $tarPath = "$folderPath/code.tar.gz"; + + if (!file_exists($tarPath)) { + Console::execute("cd $folderPath && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->output); + } + + if (filesize($tarPath) > 1024 * 1024 * 5) { + throw new \Exception('Code package is too large. Use the chunked upload method instead.'); + } + + return new CURLFile($tarPath, 'application/x-gzip', \basename($tarPath)); + } } diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php index 86aaee2ab0..4c23bb2eb9 100644 --- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php @@ -7,7 +7,6 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideClient; -use Utopia\CLI\Console; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -1271,19 +1270,13 @@ class RealtimeCustomClientTest extends Scope $this->assertEquals($function['headers']['status-code'], 201); $this->assertNotEmpty($function['body']['$id']); - $folder = 'timeout'; - $output = ''; - $code = realpath(__DIR__ . '/../../../resources/functions') . "/{$folder}/code.tar.gz"; - - Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/{$folder} && tar --exclude code.tar.gz -czf code.tar.gz .", '', $output); - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ 'content-type' => 'multipart/form-data', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'entrypoint' => 'index.php', - 'code' => new CURLFile($code, 'application/x-gzip', basename($code)), + 'code' => $this->packageFunction('timeout'), 'activate' => true ]); From f24863349e133f51712aae84ba686ae86eaf3437 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 20 Sep 2024 09:41:34 +0100 Subject: [PATCH 18/32] fixes --- .../Functions/FunctionsCustomServerTest.php | 24 ++++++++++--------- tests/e2e/Services/Realtime/RealtimeBase.php | 21 ---------------- .../Realtime/RealtimeCustomClientTest.php | 2 ++ 3 files changed, 15 insertions(+), 32 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index e9297a109f..d95d611f13 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -176,25 +176,25 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals($functions['body']['functions'][0]['name'], 'Test'); $this->assertEquals($functions['body']['functions'][1]['name'], 'Test 2'); - $functions = $this->listFunctions([ + $functions1 = $this->listFunctions([ 'queries' => [ Query::cursorAfter(new Document(['$id' => $functions['body']['functions'][0]['$id']]))->toString(), ], ]); - $this->assertEquals($functions['headers']['status-code'], 200); - $this->assertCount(1, $functions['body']['functions']); - $this->assertEquals($functions['body']['functions'][0]['name'], 'Test 2'); + $this->assertEquals($functions1['headers']['status-code'], 200); + $this->assertCount(1, $functions1['body']['functions']); + $this->assertEquals($functions1['body']['functions'][0]['name'], 'Test 2'); - $functions = $this->listFunctions([ + $functions2 = $this->listFunctions([ 'queries' => [ Query::cursorBefore(new Document(['$id' => $functions['body']['functions'][1]['$id']]))->toString(), ], ]); - $this->assertEquals($functions['headers']['status-code'], 200); - $this->assertCount(1, $functions['body']['functions']); - $this->assertEquals($functions['body']['functions'][0]['name'], 'Test'); + $this->assertEquals($functions2['headers']['status-code'], 200); + $this->assertCount(1, $functions2['body']['functions']); + $this->assertEquals($functions2['body']['functions'][0]['name'], 'Test'); /** * Test for FAILURE @@ -285,7 +285,7 @@ class FunctionsCustomServerTest extends Scope { $functionId = $this->setupFunction([ 'functionId' => ID::unique(), - 'name' => 'test-cli-deployment', + 'name' => 'Test', 'execute' => [Role::user($this->getUser()['$id'])->toString()], 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', @@ -297,12 +297,12 @@ class FunctionsCustomServerTest extends Scope 'timeout' => 10, ]); - $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ + $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', [ 'content-type' => 'multipart/form-data', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], 'x-sdk-language' => 'cli', - ], $this->getHeaders()), [ + ], [ 'entrypoint' => 'index.php', 'code' => $this->packageFunction('php'), 'activate' => true @@ -314,6 +314,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEventually(function () use ($functionId, $deploymentId) { $deployment = $this->getDeployment($functionId, $deploymentId); + $this->assertEquals(200, $deployment['headers']['status-code']); $this->assertEquals('ready', $deployment['body']['status']); $this->assertEquals('cli', $deployment['body']['type']); @@ -1212,6 +1213,7 @@ class FunctionsCustomServerTest extends Scope public function testExecutionTimeout() { $functionId = $this->setupFunction([ + 'functionId' => ID::unique(), 'name' => 'timeout-php-8.0', 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', diff --git a/tests/e2e/Services/Realtime/RealtimeBase.php b/tests/e2e/Services/Realtime/RealtimeBase.php index f0cd0e7c3b..30c411ba93 100644 --- a/tests/e2e/Services/Realtime/RealtimeBase.php +++ b/tests/e2e/Services/Realtime/RealtimeBase.php @@ -2,8 +2,6 @@ namespace Tests\E2E\Services\Realtime; -use CURLFile; -use Utopia\CLI\Console; use WebSocket\Client as WebSocketClient; use WebSocket\ConnectionException; @@ -73,23 +71,4 @@ trait RealtimeBase $this->expectException(ConnectionException::class); // Check if server disconnnected client $client->close(); } - - // Function-related methods - protected string $output = ''; - - protected function packageFunction(string $function = 'php'): CURLFile - { - $folderPath = realpath(__DIR__ . '/../../../resources/functions') . "/$function"; - $tarPath = "$folderPath/code.tar.gz"; - - if (!file_exists($tarPath)) { - Console::execute("cd $folderPath && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->output); - } - - if (filesize($tarPath) > 1024 * 1024 * 5) { - throw new \Exception('Code package is too large. Use the chunked upload method instead.'); - } - - return new CURLFile($tarPath, 'application/x-gzip', \basename($tarPath)); - } } diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php index 4c23bb2eb9..260d5da3bf 100644 --- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php @@ -7,6 +7,7 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideClient; +use Tests\E2E\Services\Functions\FunctionsBase; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -14,6 +15,7 @@ use WebSocket\ConnectionException; class RealtimeCustomClientTest extends Scope { + use FunctionsBase; use RealtimeBase; use ProjectCustom; use SideClient; From 1509aa544de051c61856fe4cc42d64fcc3ab2d3c Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 20 Sep 2024 09:43:41 +0100 Subject: [PATCH 19/32] debug: output fail deployment --- tests/e2e/Services/Functions/FunctionsBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index 8b19c65705..529846c623 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -44,7 +44,7 @@ trait FunctionsBase 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ])); - $this->assertEquals('ready', $deployment['body']['status']); + $this->assertEquals('ready', $deployment['body']['status'], 'Deployment status is not ready, deployment: ' . json_encode($deployment['body'], JSON_PRETTY_PRINT)); }, 50000, 500); return $deploymentId; From 39490e981988ce24ead726a47e571ad9a6053c7f Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 20 Sep 2024 10:06:58 +0100 Subject: [PATCH 20/32] chore: bump tests --- .github/workflows/tests.yml | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5b82cba594..4bc7b18d23 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,22 +16,22 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Build Appwrite - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v6 with: context: . push: false tags: ${{ env.IMAGE }} load: true - cache-from: type=gha - cache-to: type=gha,mode=max + cache-from: type=gha,scope=appwrite + cache-to: type=gha,mode=max,scope=appwrite outputs: type=docker,dest=/tmp/${{ env.IMAGE }}.tar build-args: | DEBUG=false @@ -39,9 +39,11 @@ jobs: VERSION=dev - name: Cache Docker Image - uses: actions/cache@v3 + uses: actions/cache@v4 with: key: ${{ env.CACHE_KEY }} + restore-keys: | + appwrite-dev- path: /tmp/${{ env.IMAGE }}.tar unit_test: @@ -51,10 +53,10 @@ jobs: steps: - name: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Load Cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: key: ${{ env.CACHE_KEY }} path: /tmp/${{ env.IMAGE }}.tar @@ -81,10 +83,10 @@ jobs: needs: setup steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Load Cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: key: ${{ env.CACHE_KEY }} path: /tmp/${{ env.IMAGE }}.tar @@ -129,10 +131,10 @@ jobs: steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Load Cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: key: ${{ env.CACHE_KEY }} path: /tmp/${{ env.IMAGE }}.tar @@ -142,7 +144,7 @@ jobs: run: | docker load --input /tmp/${{ env.IMAGE }}.tar docker compose up -d - sleep 10 + sleep 25 - name: Run ${{matrix.service}} Tests run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug @@ -150,15 +152,15 @@ jobs: - name: Run ${{matrix.service}} Shared Tables Tests run: _APP_DATABASE_SHARED_TABLES=database_db_main docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug - benchamrking: + benchmarking: name: Benchmark runs-on: ubuntu-latest needs: setup steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Load Cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: key: ${{ env.CACHE_KEY }} path: /tmp/${{ env.IMAGE }}.tar From 8e13aafbc854ff8cba1b1b62c2683af97ca42bc6 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 20 Sep 2024 10:29:49 +0100 Subject: [PATCH 21/32] fixes --- .../Functions/FunctionsCustomServerTest.php | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index d95d611f13..66f77a1df8 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -454,30 +454,34 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(true, (new DatetimeValidator())->isValid($deployment['body']['$createdAt'])); $this->assertEquals('index.php', $deployment['body']['entrypoint']); - $deploymentId = $deployment['body']['$id'] ?? ''; + $deploymentIdActive = $deployment['body']['$id'] ?? ''; + + $this->assertEventually(function () use ($functionId, $deploymentIdActive) { + $deployment = $this->getDeployment($functionId, $deploymentIdActive); + + $this->assertEquals('ready', $deployment['body']['status']); + }, 50000, 500); $deployment = $this->createDeployment($functionId, [ 'code' => $this->packageFunction('php'), 'activate' => false ]); - $deploymentIdInactive = $deployment['body']['$id']; - $this->assertEquals(202, $deployment['headers']['status-code']); $this->assertNotEmpty($deployment['body']['$id']); - $this->assertEventually(function () use ($functionId, $deploymentId, $deploymentIdInactive) { - $deployment = $this->getDeployment($functionId, $deploymentId); - $this->assertEquals('ready', $deployment['body']['status']); + $deploymentIdInactive = $deployment['body']['$id'] ?? ''; + $this->assertEventually(function () use ($functionId, $deploymentIdInactive) { $deployment = $this->getDeployment($functionId, $deploymentIdInactive); + $this->assertEquals('ready', $deployment['body']['status']); - }, 500000, 1000); + }, 50000, 500); $function = $this->getFunction($functionId); $this->assertEquals(200, $function['headers']['status-code']); - $this->assertEquals($deploymentId, $function['body']['deployment']); + $this->assertEquals($deploymentIdActive, $function['body']['deployment']); $this->assertNotEquals($deploymentIdInactive, $function['body']['deployment']); $deployment = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId . '/deployments/' . $deploymentIdInactive, array_merge([ @@ -487,7 +491,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(204, $deployment['headers']['status-code']); - return array_merge($data, ['deploymentId' => $deploymentId]); + return array_merge($data, ['deploymentId' => $deploymentIdActive]); } /** @@ -1222,7 +1226,7 @@ class FunctionsCustomServerTest extends Scope 'timeout' => 5, // Should timeout after 5 seconds ]); $this->setupDeployment($functionId, [ - 'code' => $this->packageFunction('php'), + 'code' => $this->packageFunction('timeout'), 'activate' => true, ]); @@ -1562,7 +1566,7 @@ class FunctionsCustomServerTest extends Scope $functionId = $this->setupFunction([ 'functionId' => ID::unique(), 'name' => 'Test PHP Scopes executions', - 'commands' => 'composer update --no-interaction --ignore-platform-reqs --optimize-autoloader --prefer-dist --no-dev', + 'commands' => 'sh setup.sh && composer install', 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', 'scopes' => ['users.read'], @@ -1571,7 +1575,6 @@ class FunctionsCustomServerTest extends Scope $deploymentId = $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', - 'commands' => 'sh setup.sh && composer install', 'code' => $this->packageFunction('php-scopes'), 'activate' => true, ]); From 41490602f77b59677d64c9e6b67af1ee1b52d774 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 20 Sep 2024 10:38:43 +0100 Subject: [PATCH 22/32] fix: template --- tests/e2e/Services/Functions/FunctionsCustomServerTest.php | 1 - tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 66f77a1df8..7021030331 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -375,7 +375,6 @@ 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); $function = $this->getFunction($functionId); diff --git a/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php b/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php index 7e36662e9e..bee15693f1 100644 --- a/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php +++ b/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php @@ -62,7 +62,7 @@ class FunctionsScheduleTest extends Scope $this->assertNotEmpty($asyncExecution['logs']); $this->assertNotEmpty($asyncExecution['errors']); $this->assertGreaterThan(0, $asyncExecution['duration']); - }, 10000, 500); + }, 15000, 500); $this->cleanupFunction($functionId); } From 2d102a122c4c1f8c60110770a6285bbe445e33ab Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 20 Sep 2024 13:42:08 +0100 Subject: [PATCH 23/32] fix: tests --- .../Functions/FunctionsCustomServerTest.php | 47 +++++-------------- .../FunctionsScheduleTest.php | 2 +- 2 files changed, 13 insertions(+), 36 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 7021030331..fec815c2ef 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -463,7 +463,7 @@ class FunctionsCustomServerTest extends Scope $deployment = $this->createDeployment($functionId, [ 'code' => $this->packageFunction('php'), - 'activate' => false + 'activate' => 'false' ]); $this->assertEquals(202, $deployment['headers']['status-code']); @@ -502,7 +502,7 @@ class FunctionsCustomServerTest extends Scope $deployment = $this->createDeployment($functionId, [ 'code' => $this->packageFunction('php'), - 'activate' => false + 'activate' => 'false' ]); $deploymentId = $deployment['body']['$id'] ?? ''; @@ -646,18 +646,6 @@ class FunctionsCustomServerTest extends Scope $this->assertArrayHasKey('size', $deployments['body']['deployments'][0]); $this->assertArrayHasKey('buildSize', $deployments['body']['deployments'][0]); - // Test search id - $deployments = $this->listDeployments($functionId, [ - 'search' => $data['functionId'] - ]); - - $this->assertEquals($deployments['headers']['status-code'], 200); - $this->assertEquals(3, $deployments['body']['total']); - $this->assertIsArray($deployments['body']['deployments']); - $this->assertCount(3, $deployments['body']['deployments']); - $this->assertEquals($deployments['body']['deployments'][0]['$id'], $data['deploymentId']); - - // Test pagination limit $deployments = $this->listDeployments($functionId, [ 'queries' => [ Query::limit(1)->toString(), @@ -694,16 +682,6 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals($deployments['headers']['status-code'], 200); $this->assertCount(0, $deployments['body']['deployments']); - $deployments = $this->listDeployments($functionId, [ - 'search' => 'Test' - ]); - - $this->assertEquals($deployments['headers']['status-code'], 200); - $this->assertEquals(3, $deployments['body']['total']); - $this->assertIsArray($deployments['body']['deployments']); - $this->assertCount(3, $deployments['body']['deployments']); - $this->assertEquals($deployments['body']['deployments'][0]['$id'], $data['deploymentId']); - $deployments = $this->listDeployments($functionId, [ 'search' => 'php-8.0' ]); @@ -818,7 +796,7 @@ class FunctionsCustomServerTest extends Scope $matchingDeployment = array_filter( $deployments['body']['deployments'], - fn ($deployment) => $deployment['$id'] === $deploymentId + fn($deployment) => $deployment['$id'] === $deploymentId ); $this->assertNotEmpty($matchingDeployment, "Deployment with ID {$deploymentId} not found"); @@ -905,8 +883,6 @@ class FunctionsCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); - $execution = $this->createExecution($data['functionId']); - $this->assertEquals(204, $execution['headers']['status-code']); return array_merge($data, ['executionId' => $executionId]); @@ -922,11 +898,12 @@ class FunctionsCustomServerTest extends Scope */ $executions = $this->listExecutions($data['functionId']); - $this->assertEquals($executions['headers']['status-code'], 200); - $this->assertEquals($executions['body']['total'], 1); + fwrite(STDERR, print_r(json_encode($executions, JSON_PRETTY_PRINT), true)); + + $this->assertEquals(200, $executions['headers']['status-code']); + $this->assertEquals(1, $executions['body']['total']); $this->assertIsArray($executions['body']['executions']); $this->assertCount(1, $executions['body']['executions']); - $this->assertEquals($executions['body']['executions'][0]['$id'], $data['executionId']); $executions = $this->listExecutions($data['functionId'], [ 'queries' => [ @@ -939,12 +916,12 @@ class FunctionsCustomServerTest extends Scope $executions = $this->listExecutions($data['functionId'], [ 'queries' => [ - Query::offset(1)->toString(), + Query::offset(0)->toString(), ], ]); $this->assertEquals(200, $executions['headers']['status-code']); - $this->assertCount(0, $executions['body']['executions']); + $this->assertCount(1, $executions['body']['executions']); $executions = $this->listExecutions($data['functionId'], [ 'queries' => [ @@ -1217,7 +1194,7 @@ class FunctionsCustomServerTest extends Scope { $functionId = $this->setupFunction([ 'functionId' => ID::unique(), - 'name' => 'timeout-php-8.0', + 'name' => 'Test php-8.0', 'runtime' => 'php-8.0', 'entrypoint' => 'index.php', 'events' => [], @@ -1612,8 +1589,8 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals('completed', $execution['body']['status']); $this->assertEquals(200, $execution['body']['responseStatusCode']); $this->assertGreaterThan(0, $execution['body']['duration']); - $this->assertNotEmpty($execution['body']['responseBody']); - $this->assertStringContainsString("total", $execution['body']['responseBody']); + $this->assertNotEmpty($execution['body']['logs']); + $this->assertStringContainsString("total", $execution['body']['logs']); }, 10000, 500); $this->cleanupFunction($functionId); diff --git a/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php b/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php index bee15693f1..cbe54b6445 100644 --- a/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php +++ b/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php @@ -62,7 +62,7 @@ class FunctionsScheduleTest extends Scope $this->assertNotEmpty($asyncExecution['logs']); $this->assertNotEmpty($asyncExecution['errors']); $this->assertGreaterThan(0, $asyncExecution['duration']); - }, 15000, 500); + }, 60000, 500); $this->cleanupFunction($functionId); } From cefc7a1d4baf4ac670125d8ff491617092b6687d Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 20 Sep 2024 13:44:34 +0100 Subject: [PATCH 24/32] chore: fmt --- tests/e2e/Services/Functions/FunctionsCustomServerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index fec815c2ef..050c0681c5 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -796,7 +796,7 @@ class FunctionsCustomServerTest extends Scope $matchingDeployment = array_filter( $deployments['body']['deployments'], - fn($deployment) => $deployment['$id'] === $deploymentId + fn ($deployment) => $deployment['$id'] === $deploymentId ); $this->assertNotEmpty($matchingDeployment, "Deployment with ID {$deploymentId} not found"); From 42ae429153156a6b20d7fd92cfbef699e02631ef Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 20 Sep 2024 13:49:29 +0100 Subject: [PATCH 25/32] chore: remove test --- tests/e2e/Services/Functions/FunctionsCustomServerTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 050c0681c5..a0395a08b0 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -898,8 +898,6 @@ class FunctionsCustomServerTest extends Scope */ $executions = $this->listExecutions($data['functionId']); - fwrite(STDERR, print_r(json_encode($executions, JSON_PRETTY_PRINT), true)); - $this->assertEquals(200, $executions['headers']['status-code']); $this->assertEquals(1, $executions['body']['total']); $this->assertIsArray($executions['body']['executions']); From df9e80a9629c638a276f96717096e406b71f98c2 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 20 Sep 2024 17:02:23 +0100 Subject: [PATCH 26/32] feat: handle bools --- tests/e2e/Client.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/e2e/Client.php b/tests/e2e/Client.php index 0774f1c6fd..0595611570 100644 --- a/tests/e2e/Client.php +++ b/tests/e2e/Client.php @@ -303,6 +303,8 @@ class Client if (is_array($value)) { $output += $this->flatten($value, $finalKey); // @todo: handle name collision here if needed + } elseif (is_bool($value)) { + $output[$finalKey] = $value ? 'true' : 'false'; } else { $output[$finalKey] = $value; } From f0485ca87010df935785a3c4a8c29c47ca3d034e Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 20 Sep 2024 18:30:26 +0100 Subject: [PATCH 27/32] fix: use boolean false --- tests/e2e/Services/Functions/FunctionsCustomServerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index a0395a08b0..9166a7c4e8 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -463,7 +463,7 @@ class FunctionsCustomServerTest extends Scope $deployment = $this->createDeployment($functionId, [ 'code' => $this->packageFunction('php'), - 'activate' => 'false' + 'activate' => false ]); $this->assertEquals(202, $deployment['headers']['status-code']); @@ -502,7 +502,7 @@ class FunctionsCustomServerTest extends Scope $deployment = $this->createDeployment($functionId, [ 'code' => $this->packageFunction('php'), - 'activate' => 'false' + 'activate' => false ]); $deploymentId = $deployment['body']['$id'] ?? ''; From f6e3759f6b05cb87a446c982e94ce91518ed663a Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Sun, 22 Sep 2024 20:14:58 +0100 Subject: [PATCH 28/32] chore: add async --- tests/e2e/Services/Functions/FunctionsBase.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index badab22147..15acce8ba2 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -2,12 +2,15 @@ namespace Tests\E2E\Services\Functions; +use Appwrite\Tests\Async; use CURLFile; use Tests\E2E\Client; use Utopia\CLI\Console; trait FunctionsBase { + use Async; + protected string $stdout = ''; protected string $stderr = ''; From 0753163066d24591688bee6f095acf12703c4d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 25 Sep 2024 13:32:09 +0200 Subject: [PATCH 29/32] PR review changes --- tests/e2e/Client.php | 2 -- tests/e2e/Services/Functions/FunctionsBase.php | 11 ++++------- tests/e2e/Services/GraphQL/Base.php | 6 ++---- tests/extensions/Async/Eventually.php | 7 +------ 4 files changed, 7 insertions(+), 19 deletions(-) diff --git a/tests/e2e/Client.php b/tests/e2e/Client.php index 0595611570..0774f1c6fd 100644 --- a/tests/e2e/Client.php +++ b/tests/e2e/Client.php @@ -303,8 +303,6 @@ class Client if (is_array($value)) { $output += $this->flatten($value, $finalKey); // @todo: handle name collision here if needed - } elseif (is_bool($value)) { - $output[$finalKey] = $value ? 'true' : 'false'; } else { $output[$finalKey] = $value; } diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index 15acce8ba2..a1bb8f2b21 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -142,14 +142,12 @@ trait FunctionsBase return $executions; } - protected function packageFunction(string $function = 'php'): CURLFile + protected function packageFunction(string $function): CURLFile { $folderPath = realpath(__DIR__ . '/../../../resources/functions') . "/$function"; $tarPath = "$folderPath/code.tar.gz"; - if (!file_exists($tarPath)) { - Console::execute("cd $folderPath && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr); - } + Console::execute("cd $folderPath && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr); if (filesize($tarPath) > 1024 * 1024 * 5) { throw new \Exception('Code package is too large. Use the chunked upload method instead.'); @@ -200,11 +198,10 @@ trait FunctionsBase protected function deleteFunction(string $functionId): mixed { - $function = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ + $function = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]); + ], $this->getHeaders())); return $function; } diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index 01f9508a81..0b3250cecf 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -2497,14 +2497,12 @@ trait Base protected string $stdout = ''; protected string $stderr = ''; - protected function packageFunction(string $function = 'php'): CURLFile + protected function packageFunction(string $function): CURLFile { $folderPath = realpath(__DIR__ . '/../../../resources/functions') . "/$function"; $tarPath = "$folderPath/code.tar.gz"; - if (!file_exists($tarPath)) { - Console::execute("cd $folderPath && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr); - } + Console::execute("cd $folderPath && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr); if (filesize($tarPath) > 1024 * 1024 * 5) { throw new \Exception('Code package is too large. Use the chunked upload method instead.'); diff --git a/tests/extensions/Async/Eventually.php b/tests/extensions/Async/Eventually.php index e4987de410..9840b1a114 100644 --- a/tests/extensions/Async/Eventually.php +++ b/tests/extensions/Async/Eventually.php @@ -6,13 +6,8 @@ use PHPUnit\Framework\Constraint\Constraint; final class Eventually extends Constraint { - private int $timeoutMs; - private int $waitMs; - - public function __construct(int $timeoutMs, int $waitMs) + public function __construct(private int $timeoutMs, private int $waitMs) { - $this->timeoutMs = $timeoutMs; - $this->waitMs = $waitMs; } public function evaluate(mixed $probe, string $description = '', bool $returnResult = false): ?bool From 05bcb92f73a34d2c175aba50151ec52502ee37c1 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:45:06 +0100 Subject: [PATCH 30/32] test: extra sleep --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4bc7b18d23..6e7012527d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -144,7 +144,7 @@ jobs: run: | docker load --input /tmp/${{ env.IMAGE }}.tar docker compose up -d - sleep 25 + sleep 30 - name: Run ${{matrix.service}} Tests run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug From a45db60313b52115eadbbfb0ddc3b4d473c71942 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 2 Oct 2024 14:16:25 +0100 Subject: [PATCH 31/32] fix: falsy values --- tests/e2e/General/UsageTest.php | 4 ++-- .../Functions/FunctionsCustomClientTest.php | 4 ++-- .../Functions/FunctionsCustomServerTest.php | 18 +++++++++--------- .../FunctionsScheduleTest.php | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index db75a5db45..c50a15fd3f 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -931,7 +931,7 @@ class UsageTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'] ], $this->getHeaders()), [ - 'async' => false, + 'async' => 'false', ] ); @@ -955,7 +955,7 @@ class UsageTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'] ], $this->getHeaders()), [ - 'async' => false, + 'async' => 'false', ] ); diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 3447ee0547..31cc05f423 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -97,7 +97,7 @@ class FunctionsCustomClientTest extends Scope $execution = $this->createExecution($functionId, [ 'body' => 'foobar', - 'async' => false + 'async' => 'false' ]); $output = json_decode($execution['body']['responseBody'], true); $this->assertEquals(201, $execution['headers']['status-code']); @@ -208,7 +208,7 @@ class FunctionsCustomClientTest extends Scope $execution = $this->createExecution($functionId, [ 'body' => 'foobar', - // Testing default value, should be 'async' => false + // Testing default value, should be 'async' => 'false' ]); $output = json_decode($execution['body']['responseBody'], true); $this->assertEquals(201, $execution['headers']['status-code']); diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 14ec660c4b..2c3ddbcf58 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -463,7 +463,7 @@ class FunctionsCustomServerTest extends Scope $deployment = $this->createDeployment($functionId, [ 'code' => $this->packageFunction('php'), - 'activate' => false + 'activate' => 'false' ]); $this->assertEquals(202, $deployment['headers']['status-code']); @@ -502,7 +502,7 @@ class FunctionsCustomServerTest extends Scope $deployment = $this->createDeployment($functionId, [ 'code' => $this->packageFunction('php'), - 'activate' => false + 'activate' => 'false' ]); $deploymentId = $deployment['body']['$id'] ?? ''; @@ -845,7 +845,7 @@ class FunctionsCustomServerTest extends Scope * Test for SUCCESS */ $execution = $this->createExecution($data['functionId'], [ - 'async' => false, + 'async' => 'false', ]); $this->assertEquals(201, $execution['headers']['status-code']); @@ -870,7 +870,7 @@ class FunctionsCustomServerTest extends Scope $executionId = $execution['body']['$id'] ?? ''; $execution = $this->createExecution($data['functionId'], [ - 'async' => false, + 'async' => 'false', 'path' => '/?code=400' ]); @@ -965,7 +965,7 @@ class FunctionsCustomServerTest extends Scope * Test for SUCCESS */ $execution = $this->createExecution($data['functionId'], [ - // Testing default value, should be 'async' => false + // Testing default value, should be 'async' => 'false' ]); $this->assertEquals(201, $execution['headers']['status-code']); @@ -1281,7 +1281,7 @@ class FunctionsCustomServerTest extends Scope $execution = $this->createExecution($functionId, [ 'body' => 'foobar', - 'async' => false + 'async' => 'false' ]); $output = json_decode($execution['body']['responseBody'], true); @@ -1447,7 +1447,7 @@ class FunctionsCustomServerTest extends Scope $execution = $this->createExecution($functionId, [ 'body' => 'foobar', - 'async' => false + 'async' => 'false' ]); $output = json_decode($execution['body']['responseBody'], true); @@ -1561,7 +1561,7 @@ class FunctionsCustomServerTest extends Scope $this->assertStringContainsStringIgnoringCase('"users":', $deployment['body']['buildLogs']); $execution = $this->createExecution($functionId, [ - 'async' => false, + 'async' => 'false', ]); $this->assertEquals(201, $execution['headers']['status-code']); @@ -1611,7 +1611,7 @@ class FunctionsCustomServerTest extends Scope $cookie = 'cookieName=cookieValue; cookie2=value2; cookie3=value=3; cookie4=val:ue4; cookie5=value5'; $execution = $this->createExecution($functionId, [ - 'async' => false, + 'async' => 'false', 'headers' => [ 'cookie' => $cookie ] diff --git a/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php b/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php index cbe54b6445..b1315103b1 100644 --- a/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php +++ b/tests/e2e/Services/FunctionsSchedule/FunctionsScheduleTest.php @@ -143,7 +143,7 @@ class FunctionsScheduleTest extends Scope /* Test for FAILURE */ // Schedule synchronous execution $execution = $this->createExecution($functionId, [ - 'async' => false, + 'async' => 'false', 'scheduledAt' => $futureTime->format(\DateTime::ATOM), ]); $this->assertEquals(400, $execution['headers']['status-code']); From 886687ed34fc275b29bfa6c542419555c85d8320 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 2 Oct 2024 15:19:15 +0100 Subject: [PATCH 32/32] fix: reorder assertions --- tests/e2e/Services/Functions/FunctionsCustomServerTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 2c3ddbcf58..3af050438f 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -1450,10 +1450,11 @@ class FunctionsCustomServerTest extends Scope 'async' => 'false' ]); - $output = json_decode($execution['body']['responseBody'], true); $this->assertEquals(201, $execution['headers']['status-code']); $this->assertEquals('completed', $execution['body']['status']); $this->assertEquals(200, $execution['body']['responseStatusCode']); + + $output = json_decode($execution['body']['responseBody'], true); $this->assertEquals(true, $output['v2Woks']); $this->cleanupFunction($functionId);