createNewProject(); } self::$project = $this->createNewProject(); return self::$project; } /** * Create a new project with team, API key, dev key, webhook, and SMTP config. */ protected function createNewProject(): array { // Small delay to ensure session is fully propagated under parallel load usleep(100000); // 100ms $maxRetries = 5; $team = null; $teamId = ID::unique(); for ($i = 0; $i < $maxRetries; $i++) { $team = $this->client->call(Client::METHOD_POST, '/teams', [ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'cookie' => 'a_session_console=' . $this->getRoot()['session'], 'x-appwrite-project' => 'console', ], [ 'teamId' => $teamId, 'name' => 'Demo Project Team', ]); if ($team['headers']['status-code'] === 201 || $team['headers']['status-code'] === 409) { break; } if ($team['headers']['status-code'] === 401 && $i < $maxRetries - 1) { \usleep(500000); // 500ms delay before retry continue; } } $this->assertContains($team['headers']['status-code'], [201, 409], 'Team creation failed with status: ' . $team['headers']['status-code']); if ($team['headers']['status-code'] === 201) { $this->assertEquals('Demo Project Team', $team['body']['name']); $this->assertNotEmpty($team['body']['$id']); $teamId = $team['body']['$id']; } $project = null; for ($i = 0; $i < $maxRetries; $i++) { $project = $this->client->call(Client::METHOD_POST, '/projects', [ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'cookie' => 'a_session_console=' . $this->getRoot()['session'], 'x-appwrite-project' => 'console', ], [ 'projectId' => ID::unique(), 'region' => System::getEnv('_APP_REGION', 'default'), 'name' => 'Demo Project', 'teamId' => $teamId, 'description' => 'Demo Project Description', 'url' => 'https://appwrite.io', ]); if ($project['headers']['status-code'] === 201) { break; } if ($project['headers']['status-code'] === 401 && $i < $maxRetries - 1) { \usleep(500000); // 500ms delay before retry continue; } } $this->assertEquals(201, $project['headers']['status-code'], 'Project creation failed with status: ' . $project['headers']['status-code']); $this->assertNotEmpty($project['body']); $key = null; for ($i = 0; $i < $maxRetries; $i++) { $key = $this->client->call(Client::METHOD_POST, '/projects/' . $project['body']['$id'] . '/keys', [ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'cookie' => 'a_session_console=' . $this->getRoot()['session'], 'x-appwrite-project' => 'console', ], [ 'keyId' => ID::unique(), 'name' => 'Demo Project Key ' . $project['body']['$id'], 'scopes' => [ 'users.read', 'users.write', 'teams.read', 'teams.write', 'databases.read', 'databases.write', 'collections.read', 'collections.write', 'tables.read', 'tables.write', 'documents.read', 'documents.write', 'rows.read', 'rows.write', 'files.read', 'files.write', 'buckets.read', 'buckets.write', 'sites.read', 'sites.write', 'functions.read', 'functions.write', 'sites.read', 'sites.write', 'executions.read', 'executions.write', 'log.read', 'log.write', 'locale.read', 'avatars.read', 'health.read', 'rules.read', 'rules.write', 'sessions.write', 'targets.read', 'targets.write', 'providers.read', 'providers.write', 'messages.read', 'messages.write', 'topics.write', 'topics.read', 'subscribers.write', 'subscribers.read', 'migrations.write', 'migrations.read', 'tokens.read', 'tokens.write', 'webhooks.read', 'webhooks.write', 'project.read', 'project.write', 'keys.read', 'keys.write', 'platforms.read', 'platforms.write', 'oauth2.read', 'oauth2.write', 'mocks.read', 'mocks.write', 'project.policies.read', 'project.policies.write', 'templates.read', 'templates.write', 'insights.read', 'insights.write', 'reports.read', 'reports.write', ], ]); if ($key['headers']['status-code'] === 201) { break; } if ($key['headers']['status-code'] === 401 && $i < $maxRetries - 1) { \usleep(500000); continue; } } $this->assertEquals(201, $key['headers']['status-code'], 'Key creation failed with status: ' . $key['headers']['status-code']); $this->assertNotEmpty($key['body']); $this->assertNotEmpty($key['body']['secret']); $devKey = $this->client->call(Client::METHOD_POST, '/projects/' . $project['body']['$id'] . '/dev-keys', [ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'cookie' => 'a_session_console=' . $this->getRoot()['session'], 'x-appwrite-project' => 'console', ], [ 'name' => 'Key Test', 'expire' => DateTime::addSeconds(new \DateTime(), 3600), ]); $this->assertEquals(201, $devKey['headers']['status-code']); $this->assertNotEmpty($devKey['body']); $this->assertNotEmpty($devKey['body']['secret']); $webhook = $this->client->call(Client::METHOD_POST, '/webhooks', [ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'cookie' => 'a_session_console=' . $this->getRoot()['session'], 'x-appwrite-project' => $project['body']['$id'], 'x-appwrite-mode' => 'admin' ], [ 'webhookId' => 'unique()', 'name' => 'Webhook Test', 'events' => [ 'databases.*', 'functions.*', 'buckets.*', 'teams.*', 'users.*' ], 'url' => 'http://request-catcher-webhook:5000/', 'tls' => false, ]); $this->assertEquals(201, $webhook['headers']['status-code']); $this->assertNotEmpty($webhook['body']); $this->client->call(Client::METHOD_PATCH, '/projects/' . $project['body']['$id'] . '/smtp', [ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'cookie' => 'a_session_console=' . $this->getRoot()['session'], 'x-appwrite-project' => 'console', ], [ 'enabled' => true, 'senderEmail' => 'mailer@appwrite.io', 'senderName' => 'Mailer', 'host' => 'maildev', 'port' => intval(System::getEnv('_APP_SMTP_PORT', "1025")), 'username' => System::getEnv('_APP_SMTP_USERNAME', 'user'), 'password' => System::getEnv('_APP_SMTP_PASSWORD', 'password'), ]); return [ '$id' => $project['body']['$id'], 'name' => $project['body']['name'], 'apiKey' => $key['body']['secret'], 'devKey' => $devKey['body']['secret'], 'webhookId' => $webhook['body']['$id'], 'signatureKey' => $webhook['body']['secret'], ]; } public function getNewKey(array $scopes) { $projectId = self::$project['$id']; $key = $this->client->call(Client::METHOD_POST, '/projects/' . $projectId . '/keys', [ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'cookie' => 'a_session_console=' . $this->getRoot()['session'], 'x-appwrite-project' => 'console', ], [ 'keyId' => ID::unique(), 'name' => 'Demo Project Key', 'scopes' => $scopes, ]); $this->assertEquals(201, $key['headers']['status-code']); $this->assertNotEmpty($key['body']); $this->assertNotEmpty($key['body']['secret']); return $key['body']['secret']; } public function updateProjectinvalidateSessionsProperty(bool $value) { $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . self::$project['$id'] . '/auth/session-invalidation', array_merge([ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'cookie' => 'a_session_console=' . $this->getRoot()['session'], 'x-appwrite-project' => 'console', ]), [ 'enabled' => $value, ]); return $response['headers']['status-code']; } }