createVariable( ID::unique(), 'APP_KEY', 'my-secret-value', ); $this->assertSame(201, $variable['headers']['status-code']); $this->assertNotEmpty($variable['body']['$id']); $this->assertSame('APP_KEY', $variable['body']['key']); $this->assertSame(true, $variable['body']['secret']); $this->assertSame('', $variable['body']['value']); $this->assertSame('project', $variable['body']['resourceType']); $this->assertSame('', $variable['body']['resourceId']); $dateValidator = new DatetimeValidator(); $this->assertSame(true, $dateValidator->isValid($variable['body']['$createdAt'])); $this->assertSame(true, $dateValidator->isValid($variable['body']['$updatedAt'])); // Verify via GET $get = $this->getVariable($variable['body']['$id']); $this->assertSame(200, $get['headers']['status-code']); $this->assertSame($variable['body']['$id'], $get['body']['$id']); $this->assertSame('APP_KEY', $get['body']['key']); // Verify via LIST $list = $this->listVariables(null, true); $this->assertSame(200, $list['headers']['status-code']); $this->assertGreaterThanOrEqual(1, $list['body']['total']); $this->assertGreaterThanOrEqual(1, \count($list['body']['variables'])); // Cleanup $this->deleteVariable($variable['body']['$id']); } public function testCreateVariableNonSecret(): void { $variable = $this->createVariable( ID::unique(), 'PUBLIC_KEY', 'public-value', false ); $this->assertSame(201, $variable['headers']['status-code']); $this->assertNotEmpty($variable['body']['$id']); $this->assertSame('PUBLIC_KEY', $variable['body']['key']); $this->assertSame(false, $variable['body']['secret']); $this->assertIsBool($variable['body']['secret']); $this->assertSame('public-value', $variable['body']['value']); // Cleanup $this->deleteVariable($variable['body']['$id']); } public function testCreateVariableSecretValueHidden(): void { $variable = $this->createVariable( ID::unique(), 'SECRET_KEY', 'hidden-value', true ); $this->assertSame(201, $variable['headers']['status-code']); $this->assertSame(true, $variable['body']['secret']); $this->assertSame('', $variable['body']['value']); // Verify value is also hidden on GET $get = $this->getVariable($variable['body']['$id']); $this->assertSame(200, $get['headers']['status-code']); $this->assertSame('', $get['body']['value']); // Cleanup $this->deleteVariable($variable['body']['$id']); } public function testCreateVariableWithoutAuthentication(): void { $response = $this->createVariable( ID::unique(), 'NO_AUTH_KEY', 'no-auth-value', null, false ); $this->assertSame(401, $response['headers']['status-code']); } public function testCreateVariableInvalidId(): void { $variable = $this->createVariable( '!invalid-id!', 'INVALID_ID_KEY', 'value', ); $this->assertSame(400, $variable['headers']['status-code']); } public function testCreateVariableMissingKey(): void { $response = $this->createVariable( ID::unique(), null, 'some-value', ); $this->assertSame(400, $response['headers']['status-code']); } public function testCreateVariableMissingValue(): void { $response = $this->createVariable( ID::unique(), 'MISSING_VALUE_KEY', null, ); $this->assertSame(400, $response['headers']['status-code']); } public function testCreateVariableDuplicateId(): void { $variableId = ID::unique(); $variable = $this->createVariable( $variableId, 'DUP_KEY_1', 'value1', ); $this->assertSame(201, $variable['headers']['status-code']); // Attempt to create with same ID $duplicate = $this->createVariable( $variableId, 'DUP_KEY_2', 'value2', ); $this->assertSame(409, $duplicate['headers']['status-code']); $this->assertSame('variable_already_exists', $duplicate['body']['type']); // Cleanup $this->deleteVariable($variableId); } public function testCreateVariableCustomId(): void { $customId = 'my-custom-variable-id'; $variable = $this->createVariable( $customId, 'CUSTOM_ID_KEY', 'custom-value', ); $this->assertSame(201, $variable['headers']['status-code']); $this->assertSame($customId, $variable['body']['$id']); // Verify via GET $get = $this->getVariable($customId); $this->assertSame(200, $get['headers']['status-code']); $this->assertSame($customId, $get['body']['$id']); // Cleanup $this->deleteVariable($customId); } // Update variable tests public function testUpdateVariable(): void { $variable = $this->createVariable( ID::unique(), 'ORIGINAL_KEY', 'original-value', false ); $this->assertSame(201, $variable['headers']['status-code']); $variableId = $variable['body']['$id']; // Update key and value $updated = $this->updateVariable($variableId, 'UPDATED_KEY', 'updated-value'); $this->assertSame(200, $updated['headers']['status-code']); $this->assertSame($variableId, $updated['body']['$id']); $this->assertSame('UPDATED_KEY', $updated['body']['key']); $this->assertSame('updated-value', $updated['body']['value']); // Verify update persisted via GET $get = $this->getVariable($variableId); $this->assertSame(200, $get['headers']['status-code']); $this->assertSame('UPDATED_KEY', $get['body']['key']); $this->assertSame('updated-value', $get['body']['value']); // Cleanup $this->deleteVariable($variableId); } public function testUpdateVariableKey(): void { $variable = $this->createVariable( ID::unique(), 'KEY_BEFORE', 'unchanged-value', false ); $this->assertSame(201, $variable['headers']['status-code']); $variableId = $variable['body']['$id']; // Update only key $updated = $this->updateVariable($variableId, 'KEY_AFTER'); $this->assertSame(200, $updated['headers']['status-code']); $this->assertSame('KEY_AFTER', $updated['body']['key']); $this->assertSame('unchanged-value', $updated['body']['value']); // Cleanup $this->deleteVariable($variableId); } public function testUpdateVariableValue(): void { $variable = $this->createVariable( ID::unique(), 'UNCHANGED_KEY', 'value-before', false ); $this->assertSame(201, $variable['headers']['status-code']); $variableId = $variable['body']['$id']; // Update only value $updated = $this->updateVariable($variableId, null, 'value-after'); $this->assertSame(200, $updated['headers']['status-code']); $this->assertSame('UNCHANGED_KEY', $updated['body']['key']); $this->assertSame('value-after', $updated['body']['value']); // Cleanup $this->deleteVariable($variableId); } public function testUpdateVariableSetSecret(): void { $variable = $this->createVariable( ID::unique(), 'MAKE_SECRET_KEY', 'some-value', false ); $this->assertSame(201, $variable['headers']['status-code']); $this->assertSame(false, $variable['body']['secret']); $variableId = $variable['body']['$id']; // Update to secret $updated = $this->updateVariable($variableId, null, null, true); $this->assertSame(200, $updated['headers']['status-code']); $this->assertSame(true, $updated['body']['secret']); $this->assertSame('', $updated['body']['value']); // Cleanup $this->deleteVariable($variableId); } public function testUpdateVariableCannotUnsetSecret(): void { $variable = $this->createVariable( ID::unique(), 'UNSET_SECRET_KEY', 'secret-value', true ); $this->assertSame(201, $variable['headers']['status-code']); $variableId = $variable['body']['$id']; // Attempt to unset secret $updated = $this->updateVariable($variableId, null, null, false); $this->assertSame(400, $updated['headers']['status-code']); $this->assertSame('variable_cannot_unset_secret', $updated['body']['type']); // Verify variable is unchanged $get = $this->getVariable($variableId); $this->assertSame(200, $get['headers']['status-code']); $this->assertSame(true, $get['body']['secret']); // Cleanup $this->deleteVariable($variableId); } public function testUpdateVariableNoOp(): void { $variable = $this->createVariable( ID::unique(), 'NOOP_KEY', 'noop-value', false ); $this->assertSame(201, $variable['headers']['status-code']); $variableId = $variable['body']['$id']; // Update with no parameters should fail with 400 $updated = $this->updateVariable($variableId); $this->assertSame(400, $updated['headers']['status-code']); // Cleanup $this->deleteVariable($variableId); } public function testUpdateVariableWithoutAuthentication(): void { $variable = $this->createVariable( ID::unique(), 'AUTH_UPDATE_KEY', 'auth-value', ); $this->assertSame(201, $variable['headers']['status-code']); $variableId = $variable['body']['$id']; // Attempt update without authentication $response = $this->updateVariable($variableId, 'UPDATED_KEY', null, null, false); $this->assertSame(401, $response['headers']['status-code']); // Cleanup $this->deleteVariable($variableId); } public function testUpdateVariableNotFound(): void { $updated = $this->updateVariable('non-existent-id', 'NEW_KEY', 'new-value'); $this->assertSame(404, $updated['headers']['status-code']); $this->assertSame('variable_not_found', $updated['body']['type']); } // Get variable tests public function testGetVariable(): void { $variable = $this->createVariable( ID::unique(), 'GET_TEST_KEY', 'get-test-value', false ); $this->assertSame(201, $variable['headers']['status-code']); $variableId = $variable['body']['$id']; $get = $this->getVariable($variableId); $this->assertSame(200, $get['headers']['status-code']); $this->assertSame($variableId, $get['body']['$id']); $this->assertSame('GET_TEST_KEY', $get['body']['key']); $this->assertSame('get-test-value', $get['body']['value']); $this->assertSame(false, $get['body']['secret']); $this->assertSame('project', $get['body']['resourceType']); $this->assertSame('', $get['body']['resourceId']); $dateValidator = new DatetimeValidator(); $this->assertSame(true, $dateValidator->isValid($get['body']['$createdAt'])); $this->assertSame(true, $dateValidator->isValid($get['body']['$updatedAt'])); // Cleanup $this->deleteVariable($variableId); } public function testGetVariableNotFound(): void { $get = $this->getVariable('non-existent-id'); $this->assertSame(404, $get['headers']['status-code']); $this->assertSame('variable_not_found', $get['body']['type']); } public function testGetVariableWithoutAuthentication(): void { $variable = $this->createVariable( ID::unique(), 'AUTH_GET_KEY', 'auth-get-value', ); $this->assertSame(201, $variable['headers']['status-code']); $variableId = $variable['body']['$id']; // Attempt GET without authentication $response = $this->getVariable($variableId, false); $this->assertSame(401, $response['headers']['status-code']); // Cleanup $this->deleteVariable($variableId); } // List variables tests public function testListVariables(): void { // Create multiple variables $variable1 = $this->createVariable( ID::unique(), 'LIST_KEY_ALPHA', 'alpha-value', false ); $this->assertSame(201, $variable1['headers']['status-code']); $variable2 = $this->createVariable( ID::unique(), 'LIST_KEY_BETA', 'beta-value', true ); $this->assertSame(201, $variable2['headers']['status-code']); $variable3 = $this->createVariable( ID::unique(), 'LIST_KEY_GAMMA', 'gamma-value', false ); $this->assertSame(201, $variable3['headers']['status-code']); // List all $list = $this->listVariables(null, true); $this->assertSame(200, $list['headers']['status-code']); $this->assertGreaterThanOrEqual(3, $list['body']['total']); $this->assertGreaterThanOrEqual(3, \count($list['body']['variables'])); $this->assertIsArray($list['body']['variables']); // Verify structure of returned variables foreach ($list['body']['variables'] as $variable) { $this->assertArrayHasKey('$id', $variable); $this->assertArrayHasKey('$createdAt', $variable); $this->assertArrayHasKey('$updatedAt', $variable); $this->assertArrayHasKey('key', $variable); $this->assertArrayHasKey('value', $variable); $this->assertArrayHasKey('secret', $variable); $this->assertArrayHasKey('resourceType', $variable); $this->assertArrayHasKey('resourceId', $variable); } // Cleanup $this->deleteVariable($variable1['body']['$id']); $this->deleteVariable($variable2['body']['$id']); $this->deleteVariable($variable3['body']['$id']); } public function testListVariablesWithLimit(): void { $variable1 = $this->createVariable( ID::unique(), 'LIMIT_KEY_1', 'limit-value-1', ); $this->assertSame(201, $variable1['headers']['status-code']); $variable2 = $this->createVariable( ID::unique(), 'LIMIT_KEY_2', 'limit-value-2', ); $this->assertSame(201, $variable2['headers']['status-code']); // List with limit of 1 $list = $this->listVariables([ Query::limit(1)->toString(), ], true); $this->assertSame(200, $list['headers']['status-code']); $this->assertCount(1, $list['body']['variables']); $this->assertGreaterThanOrEqual(2, $list['body']['total']); // Cleanup $this->deleteVariable($variable1['body']['$id']); $this->deleteVariable($variable2['body']['$id']); } public function testListVariablesWithOffset(): void { $variable1 = $this->createVariable( ID::unique(), 'OFFSET_KEY_1', 'offset-value-1', ); $this->assertSame(201, $variable1['headers']['status-code']); $variable2 = $this->createVariable( ID::unique(), 'OFFSET_KEY_2', 'offset-value-2', ); $this->assertSame(201, $variable2['headers']['status-code']); // List all to get total $listAll = $this->listVariables(null, true); $this->assertSame(200, $listAll['headers']['status-code']); $totalAll = \count($listAll['body']['variables']); // List with offset $listOffset = $this->listVariables([ Query::offset(1)->toString(), ], true); $this->assertSame(200, $listOffset['headers']['status-code']); $this->assertCount($totalAll - 1, $listOffset['body']['variables']); // Cleanup $this->deleteVariable($variable1['body']['$id']); $this->deleteVariable($variable2['body']['$id']); } public function testListVariablesWithoutTotal(): void { $variable = $this->createVariable( ID::unique(), 'NO_TOTAL_KEY', 'no-total-value', ); $this->assertSame(201, $variable['headers']['status-code']); // List with total=false $list = $this->listVariables(null, false); $this->assertSame(200, $list['headers']['status-code']); $this->assertSame(0, $list['body']['total']); $this->assertGreaterThanOrEqual(1, \count($list['body']['variables'])); // Cleanup $this->deleteVariable($variable['body']['$id']); } public function testListVariablesCursorPagination(): void { $variable1 = $this->createVariable( ID::unique(), 'CURSOR_KEY_1', 'cursor-value-1', ); $this->assertSame(201, $variable1['headers']['status-code']); $variable2 = $this->createVariable( ID::unique(), 'CURSOR_KEY_2', 'cursor-value-2', ); $this->assertSame(201, $variable2['headers']['status-code']); // Get first page with limit 1 $page1 = $this->listVariables([ Query::limit(1)->toString(), ], true); $this->assertSame(200, $page1['headers']['status-code']); $this->assertCount(1, $page1['body']['variables']); $cursorId = $page1['body']['variables'][0]['$id']; // Get next page using cursor $page2 = $this->listVariables([ Query::limit(1)->toString(), Query::cursorAfter(new Document(['$id' => $cursorId]))->toString(), ], true); $this->assertSame(200, $page2['headers']['status-code']); $this->assertCount(1, $page2['body']['variables']); $this->assertNotEquals($cursorId, $page2['body']['variables'][0]['$id']); // Cleanup $this->deleteVariable($variable1['body']['$id']); $this->deleteVariable($variable2['body']['$id']); } public function testListVariablesWithoutAuthentication(): void { $response = $this->listVariables(null, null, false); $this->assertSame(401, $response['headers']['status-code']); } public function testListVariablesInvalidCursor(): void { $list = $this->listVariables([ Query::cursorAfter(new Document(['$id' => 'non-existent-id']))->toString(), ], true); $this->assertSame(400, $list['headers']['status-code']); } // Delete variable tests public function testDeleteVariable(): void { $variable = $this->createVariable( ID::unique(), 'DELETE_KEY', 'delete-value', ); $this->assertSame(201, $variable['headers']['status-code']); $variableId = $variable['body']['$id']; // Verify it exists $get = $this->getVariable($variableId); $this->assertSame(200, $get['headers']['status-code']); // Delete $delete = $this->deleteVariable($variableId); $this->assertSame(204, $delete['headers']['status-code']); $this->assertEmpty($delete['body']); // Verify it no longer exists $get = $this->getVariable($variableId); $this->assertSame(404, $get['headers']['status-code']); $this->assertSame('variable_not_found', $get['body']['type']); } public function testDeleteVariableNotFound(): void { $delete = $this->deleteVariable('non-existent-id'); $this->assertSame(404, $delete['headers']['status-code']); $this->assertSame('variable_not_found', $delete['body']['type']); } public function testDeleteVariableWithoutAuthentication(): void { $variable = $this->createVariable( ID::unique(), 'DELETE_AUTH_KEY', 'delete-auth-value', ); $this->assertSame(201, $variable['headers']['status-code']); $variableId = $variable['body']['$id']; // Attempt DELETE without authentication $response = $this->deleteVariable($variableId, false); $this->assertSame(401, $response['headers']['status-code']); // Verify it still exists $get = $this->getVariable($variableId); $this->assertSame(200, $get['headers']['status-code']); // Cleanup $this->deleteVariable($variableId); } public function testDeleteVariableRemovedFromList(): void { $variable = $this->createVariable( ID::unique(), 'DELETE_LIST_KEY', 'delete-list-value', ); $this->assertSame(201, $variable['headers']['status-code']); $variableId = $variable['body']['$id']; // Get list count before delete $listBefore = $this->listVariables(null, true); $this->assertSame(200, $listBefore['headers']['status-code']); $countBefore = $listBefore['body']['total']; // Delete $delete = $this->deleteVariable($variableId); $this->assertSame(204, $delete['headers']['status-code']); // Get list count after delete $listAfter = $this->listVariables(null, true); $this->assertSame(200, $listAfter['headers']['status-code']); $this->assertSame($countBefore - 1, $listAfter['body']['total']); // Verify the deleted variable is not in the list $ids = \array_column($listAfter['body']['variables'], '$id'); $this->assertNotContains($variableId, $ids); } public function testDeleteVariableDoubleDelete(): void { $variable = $this->createVariable( ID::unique(), 'DOUBLE_DELETE_KEY', 'double-delete-value', ); $this->assertSame(201, $variable['headers']['status-code']); $variableId = $variable['body']['$id']; // First delete succeeds $delete = $this->deleteVariable($variableId); $this->assertSame(204, $delete['headers']['status-code']); // Second delete returns 404 $delete = $this->deleteVariable($variableId); $this->assertSame(404, $delete['headers']['status-code']); $this->assertSame('variable_not_found', $delete['body']['type']); } // Integration tests /** * Test that project variables are available in function build and runtime. */ public function testProjectVariableInFunction(): void { $projectId = $this->getProject()['$id']; $apiKey = $this->getProject()['apiKey']; // 1. Create a project variable $variable = $this->createVariable( ID::unique(), 'GLOBAL_VARIABLE', 'Project Variable Value', false ); $this->assertSame(201, $variable['headers']['status-code']); $variableId = $variable['body']['$id']; // 2. Create a function with build commands that echo the variable $function = $this->client->call(Client::METHOD_POST, '/functions', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'x-appwrite-key' => $apiKey, ], [ 'functionId' => ID::unique(), 'name' => 'Project Variable Test', 'runtime' => 'node-22', 'entrypoint' => 'index.js', 'execute' => ['any'], 'timeout' => 15, 'commands' => 'echo $GLOBAL_VARIABLE', ]); $this->assertSame(201, $function['headers']['status-code']); $functionId = $function['body']['$id']; // 3. Deploy the function (basic function reads GLOBAL_VARIABLE from env) $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', [ 'content-type' => 'multipart/form-data', 'x-appwrite-project' => $projectId, 'x-appwrite-key' => $apiKey, ], [ 'code' => $this->packageCode('functions', 'basic'), 'activate' => true, ]); $this->assertSame(202, $deployment['headers']['status-code']); $deploymentId = $deployment['body']['$id'] ?? ''; // 4. Wait for deployment to be ready and activated $this->assertEventually(function () use ($projectId, $apiKey, $functionId, $deploymentId) { $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'x-appwrite-key' => $apiKey, ]); $status = $deployment['body']['status'] ?? ''; if ($status === 'failed') { throw new Critical('Deployment build failed: ' . ($deployment['body']['buildLogs'] ?? 'no logs')); } $this->assertSame('ready', $status, 'Deployment status is not ready'); }, 120000, 500); $this->assertEventually(function () use ($projectId, $apiKey, $functionId, $deploymentId) { $function = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'x-appwrite-key' => $apiKey, ]); $this->assertSame($deploymentId, $function['body']['deploymentId'] ?? ''); }, 120000, 500); // 5. Verify the project variable was available during build $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'x-appwrite-key' => $apiKey, ]); $this->assertSame(200, $deployment['headers']['status-code']); $this->assertStringContainsString('Project Variable Value', $deployment['body']['buildLogs']); // 6. Execute the function and verify the project variable is in runtime output $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, ], $this->getHeaders()), [ 'async' => false, ]); $this->assertSame(201, $execution['headers']['status-code']); $this->assertSame('completed', $execution['body']['status']); $this->assertSame(200, $execution['body']['responseStatusCode']); $output = json_decode($execution['body']['responseBody'], true); $this->assertSame('Project Variable Value', $output['GLOBAL_VARIABLE']); // Cleanup $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'x-appwrite-key' => $apiKey, ]); $this->deleteVariable($variableId); } /** * Test that project variables are available in site build and SSR runtime. */ public function testProjectVariableInSite(): void { $projectId = $this->getProject()['$id']; $apiKey = $this->getProject()['apiKey']; // 1. Create a project variable $variable = $this->createVariable( ID::unique(), 'name', 'ProjectVarTest', ); $this->assertSame(201, $variable['headers']['status-code']); $variableId = $variable['body']['$id']; // 2. Create a site $site = $this->client->call(Client::METHOD_POST, '/sites', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'x-appwrite-key' => $apiKey, ], [ 'siteId' => ID::unique(), 'name' => 'Project Variable Astro Site', 'framework' => 'astro', 'adapter' => 'ssr', 'buildRuntime' => 'node-22', 'outputDirectory' => './dist', 'buildCommand' => 'echo $name && npm run build', 'installCommand' => 'npm ci', 'fallbackFile' => '', ]); $this->assertSame(201, $site['headers']['status-code']); $siteId = $site['body']['$id']; // 3. Setup domain for proxy access $sitesDomain = \explode(',', System::getEnv('_APP_DOMAIN_SITES', ''))[0]; $rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/site', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, ], $this->getHeaders()), [ 'domain' => ID::unique() . '.' . $sitesDomain, 'siteId' => $siteId, ]); $this->assertSame(201, $rule['headers']['status-code']); // 4. Deploy the site (astro site reads import.meta.env.name) $deployment = $this->client->call(Client::METHOD_POST, '/sites/' . $siteId . '/deployments', [ 'content-type' => 'multipart/form-data', 'x-appwrite-project' => $projectId, 'x-appwrite-key' => $apiKey, ], [ 'code' => $this->packageCode('sites', 'astro'), 'activate' => 'true', ]); $this->assertSame(202, $deployment['headers']['status-code']); $deploymentId = $deployment['body']['$id'] ?? ''; // 5. Wait for deployment to be ready and activated $this->assertEventually(function () use ($projectId, $apiKey, $siteId, $deploymentId) { $deployment = $this->client->call(Client::METHOD_GET, '/sites/' . $siteId . '/deployments/' . $deploymentId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'x-appwrite-key' => $apiKey, ]); $status = $deployment['body']['status'] ?? ''; if ($status === 'failed') { throw new Critical('Site deployment failed: ' . json_encode($deployment['body'], JSON_PRETTY_PRINT)); } $this->assertSame('ready', $status, 'Deployment status is not ready'); }, 120000, 500); $this->assertEventually(function () use ($projectId, $apiKey, $siteId, $deploymentId) { $site = $this->client->call(Client::METHOD_GET, '/sites/' . $siteId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'x-appwrite-key' => $apiKey, ]); $this->assertSame($deploymentId, $site['body']['deploymentId'] ?? ''); }, 120000, 500); // 6. Verify the project variable was available during build $deployment = $this->client->call(Client::METHOD_GET, '/sites/' . $siteId . '/deployments/' . $deploymentId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'x-appwrite-key' => $apiKey, ]); $this->assertSame(200, $deployment['headers']['status-code']); $this->assertStringContainsString('ProjectVarTest', $deployment['body']['buildLogs']); // 7. Get the domain and access the site $rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, ], $this->getHeaders()), [ 'queries' => [ Query::equal('deploymentResourceId', [$siteId])->toString(), Query::equal('trigger', ['manual'])->toString(), Query::equal('type', ['deployment'])->toString(), ], ]); $this->assertSame(200, $rules['headers']['status-code']); $this->assertGreaterThanOrEqual(1, \count($rules['body']['rules'])); $domain = $rules['body']['rules'][0]['domain']; $proxyClient = new Client(); $proxyClient->setEndpoint('http://' . $domain); $response = $proxyClient->call(Client::METHOD_GET, '/'); $this->assertSame(200, $response['headers']['status-code']); $this->assertStringContainsString('Env variable is ProjectVarTest', $response['body']); $this->assertStringNotContainsString('Variable not found', $response['body']); // Cleanup $this->client->call(Client::METHOD_DELETE, '/sites/' . $siteId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, 'x-appwrite-key' => $apiKey, ]); $this->deleteVariable($variableId); } // Helpers protected function createVariable(string $variableId, ?string $key, ?string $value, ?bool $secret = null, bool $authenticated = true): mixed { $params = [ 'variableId' => $variableId, ]; if ($key !== null) { $params['key'] = $key; } if ($value !== null) { $params['value'] = $value; } if ($secret !== null) { $params['secret'] = $secret; } $headers = [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ]; if ($authenticated) { $headers = array_merge($headers, $this->getHeaders()); } return $this->client->call(Client::METHOD_POST, '/project/variables', $headers, $params); } protected function updateVariable(string $variableId, ?string $key = null, ?string $value = null, ?bool $secret = null, bool $authenticated = true): mixed { $params = []; if ($key !== null) { $params['key'] = $key; } if ($value !== null) { $params['value'] = $value; } if ($secret !== null) { $params['secret'] = $secret; } $headers = [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ]; if ($authenticated) { $headers = array_merge($headers, $this->getHeaders()); } return $this->client->call(Client::METHOD_PUT, '/project/variables/' . $variableId, $headers, $params); } protected function getVariable(string $variableId, bool $authenticated = true): mixed { $headers = [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ]; if ($authenticated) { $headers = array_merge($headers, $this->getHeaders()); } return $this->client->call(Client::METHOD_GET, '/project/variables/' . $variableId, $headers); } /** * @param array|null $queries */ protected function listVariables(?array $queries, ?bool $total, bool $authenticated = true): mixed { $headers = [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ]; if ($authenticated) { $headers = array_merge($headers, $this->getHeaders()); } return $this->client->call(Client::METHOD_GET, '/project/variables', $headers, [ 'queries' => $queries, 'total' => $total, ]); } protected function deleteVariable(string $variableId, bool $authenticated = true): mixed { $headers = [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ]; if ($authenticated) { $headers = array_merge($headers, $this->getHeaders()); } return $this->client->call(Client::METHOD_DELETE, '/project/variables/' . $variableId, $headers); } protected function packageCode(string $type, string $name): CURLFile { $folderPath = realpath(__DIR__ . '/../../../resources/' . $type) . "/$name"; $tarPath = "$folderPath/code.tar.gz"; Console::execute("cd $folderPath && tar --exclude code.tar.gz --exclude node_modules -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.'); } return new CURLFile($tarPath, 'application/x-gzip', \basename($tarPath)); } }