mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
perf: Remove @depends from Migrations, Projects, and Tokens tests
Added helper methods with static caching for independent test execution. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
2a3e2d8be5
commit
2d160747ca
@@ -3,7 +3,6 @@
|
||||
namespace Tests\E2E\Services\Migrations;
|
||||
|
||||
use CURLFile;
|
||||
use PHPUnit\Framework\Attributes\Depends;
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\General\UsageTest;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
@@ -25,6 +24,18 @@ trait MigrationsBase
|
||||
*/
|
||||
protected static array $destinationProject = [];
|
||||
|
||||
/**
|
||||
* Cached database data for independent test execution
|
||||
* @var array
|
||||
*/
|
||||
protected static array $cachedDatabaseData = [];
|
||||
|
||||
/**
|
||||
* Cached table data for independent test execution
|
||||
* @var array
|
||||
*/
|
||||
protected static array $cachedTableData = [];
|
||||
|
||||
/**
|
||||
* @param bool $fresh
|
||||
* @return array
|
||||
@@ -43,6 +54,97 @@ trait MigrationsBase
|
||||
return self::$destinationProject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a database for migration tests with static caching
|
||||
* @return array
|
||||
*/
|
||||
protected function setupMigrationDatabase(): array
|
||||
{
|
||||
if (!empty(static::$cachedDatabaseData)) {
|
||||
return static::$cachedDatabaseData;
|
||||
}
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/databases', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Test Database'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertNotEmpty($response['body']['$id']);
|
||||
|
||||
static::$cachedDatabaseData = [
|
||||
'databaseId' => $response['body']['$id'],
|
||||
];
|
||||
|
||||
return static::$cachedDatabaseData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a table with column for migration tests with static caching
|
||||
* @return array
|
||||
*/
|
||||
protected function setupMigrationTable(): array
|
||||
{
|
||||
if (!empty(static::$cachedTableData)) {
|
||||
return static::$cachedTableData;
|
||||
}
|
||||
|
||||
// Ensure database exists first
|
||||
$dbData = $this->setupMigrationDatabase();
|
||||
$databaseId = $dbData['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'Test Table',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
|
||||
$tableId = $table['body']['$id'];
|
||||
|
||||
// Create Column
|
||||
$response = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'key' => 'name',
|
||||
'size' => 100,
|
||||
'encrypt' => false,
|
||||
'required' => true
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $response['headers']['status-code']);
|
||||
|
||||
// Wait for column to be ready
|
||||
$this->assertEventually(function () use ($databaseId, $tableId) {
|
||||
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/name', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('available', $response['body']['status']);
|
||||
}, 5000, 500);
|
||||
|
||||
static::$cachedTableData = [
|
||||
'databaseId' => $databaseId,
|
||||
'tableId' => $tableId,
|
||||
];
|
||||
|
||||
return static::$cachedTableData;
|
||||
}
|
||||
|
||||
public function performMigrationSync(array $body): array
|
||||
{
|
||||
$migration = $this->client->call(Client::METHOD_POST, '/migrations/appwrite', [
|
||||
@@ -373,7 +475,7 @@ trait MigrationsBase
|
||||
/**
|
||||
* Databases
|
||||
*/
|
||||
public function testAppwriteMigrationDatabase(): array
|
||||
public function testAppwriteMigrationDatabase(): void
|
||||
{
|
||||
$response = $this->client->call(Client::METHOD_POST, '/databases', [
|
||||
'content-type' => 'application/json',
|
||||
@@ -428,14 +530,18 @@ trait MigrationsBase
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
]);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
];
|
||||
// Cleanup on source
|
||||
$this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
}
|
||||
|
||||
#[Depends('testAppwriteMigrationDatabase')]
|
||||
public function testAppwriteMigrationDatabasesTable(array $data): array
|
||||
public function testAppwriteMigrationDatabasesTable(): void
|
||||
{
|
||||
// Set up database using helper method (with static caching)
|
||||
$data = $this->setupMigrationDatabase();
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', [
|
||||
@@ -525,26 +631,32 @@ trait MigrationsBase
|
||||
$this->assertEquals(100, $response['body']['size']);
|
||||
$this->assertEquals(true, $response['body']['required']);
|
||||
|
||||
// Cleanup
|
||||
// Cleanup on destination
|
||||
$this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getDestinationProject()['$id'],
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
]);
|
||||
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'tableId' => $tableId,
|
||||
];
|
||||
// Cleanup on source
|
||||
$this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
// Clear the cache since we cleaned up
|
||||
static::$cachedDatabaseData = [];
|
||||
}
|
||||
|
||||
#[Depends('testAppwriteMigrationDatabasesTable')]
|
||||
public function testAppwriteMigrationDatabasesRow(array $data): void
|
||||
public function testAppwriteMigrationDatabasesRow(): void
|
||||
{
|
||||
$table = $data['tableId'];
|
||||
// Set up table using helper method (with static caching)
|
||||
$data = $this->setupMigrationTable();
|
||||
$tableId = $data['tableId'];
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$row = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $table . '/rows', [
|
||||
$row = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
@@ -594,7 +706,7 @@ trait MigrationsBase
|
||||
$this->assertEquals(0, $result['statusCounters'][$resource]['warning']);
|
||||
}
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $table . '/rows/' . $rowId, [
|
||||
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getDestinationProject()['$id'],
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
@@ -606,12 +718,23 @@ trait MigrationsBase
|
||||
$this->assertEquals($rowId, $response['body']['$id']);
|
||||
$this->assertEquals('Test Row', $response['body']['name']);
|
||||
|
||||
// Cleanup
|
||||
// Cleanup on destination
|
||||
$this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getDestinationProject()['$id'],
|
||||
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
|
||||
]);
|
||||
|
||||
// Cleanup on source
|
||||
$this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
// Clear the caches since we cleaned up
|
||||
static::$cachedDatabaseData = [];
|
||||
static::$cachedTableData = [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,9 +4,394 @@ namespace Tests\E2E\Services\Projects;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\System\System;
|
||||
|
||||
trait ProjectsBase
|
||||
{
|
||||
private static array $cachedProjectData = [];
|
||||
private static array $cachedProjectWithWebhook = [];
|
||||
private static array $cachedProjectWithKey = [];
|
||||
private static array $cachedProjectWithPlatform = [];
|
||||
private static array $cachedProjectWithVariable = [];
|
||||
private static array $cachedProjectWithAuthLimit = [];
|
||||
private static array $cachedProjectWithServicesDisabled = [];
|
||||
|
||||
/**
|
||||
* Setup and cache a basic project with team
|
||||
*/
|
||||
protected function setupProjectData(): array
|
||||
{
|
||||
if (!empty(self::$cachedProjectData)) {
|
||||
return self::$cachedProjectData;
|
||||
}
|
||||
|
||||
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Project Test',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $team['headers']['status-code']);
|
||||
|
||||
$project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'projectId' => ID::unique(),
|
||||
'name' => 'Project Test',
|
||||
'teamId' => $team['body']['$id'],
|
||||
'region' => System::getEnv('_APP_REGION', 'default')
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $project['headers']['status-code']);
|
||||
|
||||
self::$cachedProjectData = [
|
||||
'projectId' => $project['body']['$id'],
|
||||
'teamId' => $team['body']['$id']
|
||||
];
|
||||
|
||||
return self::$cachedProjectData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup and cache a project with a webhook
|
||||
*/
|
||||
protected function setupProjectWithWebhook(): array
|
||||
{
|
||||
if (!empty(self::$cachedProjectWithWebhook)) {
|
||||
return self::$cachedProjectWithWebhook;
|
||||
}
|
||||
|
||||
$projectData = $this->setupProjectData();
|
||||
$id = $projectData['projectId'];
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/webhooks', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'Webhook Test',
|
||||
'events' => ['users.*.create', 'users.*.update.email'],
|
||||
'url' => 'https://appwrite.io',
|
||||
'security' => true,
|
||||
'httpUser' => 'username',
|
||||
'httpPass' => 'password',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
self::$cachedProjectWithWebhook = array_merge($projectData, [
|
||||
'webhookId' => $response['body']['$id'],
|
||||
'signatureKey' => $response['body']['signatureKey']
|
||||
]);
|
||||
|
||||
return self::$cachedProjectWithWebhook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup and cache a project with an API key
|
||||
*/
|
||||
protected function setupProjectWithKey(): array
|
||||
{
|
||||
if (!empty(self::$cachedProjectWithKey)) {
|
||||
return self::$cachedProjectWithKey;
|
||||
}
|
||||
|
||||
$projectData = $this->setupProjectData();
|
||||
$id = $projectData['projectId'];
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/keys', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'Key Test',
|
||||
'scopes' => ['teams.read', 'teams.write'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
self::$cachedProjectWithKey = array_merge($projectData, [
|
||||
'keyId' => $response['body']['$id'],
|
||||
'secret' => $response['body']['secret']
|
||||
]);
|
||||
|
||||
return self::$cachedProjectWithKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup and cache a project with platforms
|
||||
*/
|
||||
protected function setupProjectWithPlatform(): array
|
||||
{
|
||||
if (!empty(self::$cachedProjectWithPlatform)) {
|
||||
return self::$cachedProjectWithPlatform;
|
||||
}
|
||||
|
||||
$projectData = $this->setupProjectData();
|
||||
$id = $projectData['projectId'];
|
||||
|
||||
// Create web platform
|
||||
$response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/platforms', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'type' => 'web',
|
||||
'name' => 'Web App',
|
||||
'hostname' => 'localhost',
|
||||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$platformWebId = $response['body']['$id'];
|
||||
|
||||
// Create flutter-ios platform
|
||||
$response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/platforms', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'type' => 'flutter-ios',
|
||||
'name' => 'Flutter App (iOS)',
|
||||
'key' => 'com.example.ios',
|
||||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$platformFultteriOSId = $response['body']['$id'];
|
||||
|
||||
// Create flutter-android platform
|
||||
$response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/platforms', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'type' => 'flutter-android',
|
||||
'name' => 'Flutter App (Android)',
|
||||
'key' => 'com.example.android',
|
||||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$platformFultterAndroidId = $response['body']['$id'];
|
||||
|
||||
// Create flutter-web platform
|
||||
$response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/platforms', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'type' => 'flutter-web',
|
||||
'name' => 'Flutter App (Web)',
|
||||
'hostname' => 'flutter.appwrite.io',
|
||||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$platformFultterWebId = $response['body']['$id'];
|
||||
|
||||
// Create apple-ios platform
|
||||
$response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/platforms', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'type' => 'apple-ios',
|
||||
'name' => 'iOS App',
|
||||
'key' => 'com.example.ios',
|
||||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$platformAppleIosId = $response['body']['$id'];
|
||||
|
||||
// Create apple-macos platform
|
||||
$response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/platforms', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'type' => 'apple-macos',
|
||||
'name' => 'macOS App',
|
||||
'key' => 'com.example.macos',
|
||||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$platformAppleMacOsId = $response['body']['$id'];
|
||||
|
||||
// Create apple-watchos platform
|
||||
$response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/platforms', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'type' => 'apple-watchos',
|
||||
'name' => 'watchOS App',
|
||||
'key' => 'com.example.watchos',
|
||||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$platformAppleWatchOsId = $response['body']['$id'];
|
||||
|
||||
// Create apple-tvos platform
|
||||
$response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/platforms', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'type' => 'apple-tvos',
|
||||
'name' => 'tvOS App',
|
||||
'key' => 'com.example.tvos',
|
||||
]);
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$platformAppleTvOsId = $response['body']['$id'];
|
||||
|
||||
self::$cachedProjectWithPlatform = array_merge($projectData, [
|
||||
'platformWebId' => $platformWebId,
|
||||
'platformFultteriOSId' => $platformFultteriOSId,
|
||||
'platformFultterAndroidId' => $platformFultterAndroidId,
|
||||
'platformFultterWebId' => $platformFultterWebId,
|
||||
'platformAppleIosId' => $platformAppleIosId,
|
||||
'platformAppleMacOsId' => $platformAppleMacOsId,
|
||||
'platformAppleWatchOsId' => $platformAppleWatchOsId,
|
||||
'platformAppleTvOsId' => $platformAppleTvOsId,
|
||||
]);
|
||||
|
||||
return self::$cachedProjectWithPlatform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup and cache a project with variables
|
||||
*/
|
||||
protected function setupProjectWithVariable(): array
|
||||
{
|
||||
if (!empty(self::$cachedProjectWithVariable)) {
|
||||
return self::$cachedProjectWithVariable;
|
||||
}
|
||||
|
||||
$projectData = $this->setupProjectData();
|
||||
|
||||
// Create a non-secret variable
|
||||
$variable = $this->client->call(Client::METHOD_POST, '/project/variables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectData['projectId'],
|
||||
'x-appwrite-mode' => 'admin',
|
||||
], $this->getHeaders()), [
|
||||
'key' => 'APP_TEST',
|
||||
'value' => 'TESTINGVALUE',
|
||||
'secret' => false
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $variable['headers']['status-code']);
|
||||
$variableId = $variable['body']['$id'];
|
||||
|
||||
// Create a secret variable
|
||||
$variable = $this->client->call(Client::METHOD_POST, '/project/variables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectData['projectId'],
|
||||
'x-appwrite-mode' => 'admin',
|
||||
], $this->getHeaders()), [
|
||||
'key' => 'APP_TEST_1',
|
||||
'value' => 'TESTINGVALUE_1',
|
||||
'secret' => true
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $variable['headers']['status-code']);
|
||||
$secretVariableId = $variable['body']['$id'];
|
||||
|
||||
self::$cachedProjectWithVariable = array_merge($projectData, [
|
||||
'variableId' => $variableId,
|
||||
'secretVariableId' => $secretVariableId
|
||||
]);
|
||||
|
||||
return self::$cachedProjectWithVariable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup and cache a project with auth limit configured
|
||||
*/
|
||||
protected function setupProjectWithAuthLimit(): array
|
||||
{
|
||||
if (!empty(self::$cachedProjectWithAuthLimit)) {
|
||||
return self::$cachedProjectWithAuthLimit;
|
||||
}
|
||||
|
||||
$projectData = $this->setupProjectData();
|
||||
$id = $projectData['projectId'];
|
||||
|
||||
// Set auth limit to 0 (unlimited) for the base setup
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/auth/limit', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'limit' => 0,
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
||||
self::$cachedProjectWithAuthLimit = $projectData;
|
||||
|
||||
return self::$cachedProjectWithAuthLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup and cache a project with services disabled
|
||||
*/
|
||||
protected function setupProjectWithServicesDisabled(): array
|
||||
{
|
||||
if (!empty(self::$cachedProjectWithServicesDisabled)) {
|
||||
return self::$cachedProjectWithServicesDisabled;
|
||||
}
|
||||
|
||||
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
|
||||
]), [
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Project Test',
|
||||
]);
|
||||
$this->assertEquals(201, $team['headers']['status-code']);
|
||||
|
||||
$project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
|
||||
]), [
|
||||
'projectId' => ID::unique(),
|
||||
'name' => 'Project Test',
|
||||
'teamId' => $team['body']['$id'],
|
||||
'region' => System::getEnv('_APP_REGION', 'default')
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $project['headers']['status-code']);
|
||||
|
||||
$id = $project['body']['$id'];
|
||||
$services = require(__DIR__ . '/../../../../app/config/services.php');
|
||||
|
||||
// Disable all optional services
|
||||
foreach ($services as $service) {
|
||||
if (!$service['optional']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$key = $service['key'] ?? '';
|
||||
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/service', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
|
||||
]), [
|
||||
'service' => $key,
|
||||
'status' => false,
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
}
|
||||
|
||||
// Re-enable all services for the cached project
|
||||
foreach ($services as $service) {
|
||||
if (!$service['optional']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$key = $service['key'] ?? '';
|
||||
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/service/', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'service' => $key,
|
||||
'status' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
self::$cachedProjectWithServicesDisabled = ['projectId' => $id];
|
||||
|
||||
return self::$cachedProjectWithServicesDisabled;
|
||||
}
|
||||
|
||||
protected function setupProject(mixed $params): string
|
||||
{
|
||||
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,6 @@
|
||||
namespace Tests\E2E\Services\Tokens;
|
||||
|
||||
use CURLFile;
|
||||
use PHPUnit\Framework\Attributes\Depends;
|
||||
use Tests\E2E\Client;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
@@ -11,7 +10,72 @@ use Utopia\Database\Helpers\Role;
|
||||
|
||||
trait TokensBase
|
||||
{
|
||||
public function testCreateBucketAndFile(): array
|
||||
private static array $bucketAndFileData = [];
|
||||
|
||||
protected function setupBucketAndFile(): array
|
||||
{
|
||||
if (!empty(static::$bucketAndFileData)) {
|
||||
return static::$bucketAndFileData;
|
||||
}
|
||||
|
||||
$bucket = $this->client->call(
|
||||
Client::METHOD_POST,
|
||||
'/storage/buckets',
|
||||
[
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
],
|
||||
[
|
||||
'name' => 'Test Bucket',
|
||||
'bucketId' => ID::unique(),
|
||||
'allowedFileExtensions' => ['jpg', 'png', 'jfif'],
|
||||
]
|
||||
);
|
||||
|
||||
$bucketId = $bucket['body']['$id'];
|
||||
|
||||
$file = $this->client->call(
|
||||
Client::METHOD_POST,
|
||||
'/storage/buckets/' . $bucketId . '/files',
|
||||
[
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
],
|
||||
[
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'),
|
||||
]
|
||||
);
|
||||
|
||||
$fileId = $file['body']['$id'];
|
||||
|
||||
$token = $this->client->call(
|
||||
Client::METHOD_POST,
|
||||
'/tokens/buckets/' . $bucketId . '/files/' . $fileId,
|
||||
[
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]
|
||||
);
|
||||
|
||||
static::$bucketAndFileData = [
|
||||
'fileId' => $fileId,
|
||||
'bucketId' => $bucketId,
|
||||
'token' => $token['body'],
|
||||
'jwtToken' => $token['body']['secret'],
|
||||
'guestHeaders' => [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
],
|
||||
];
|
||||
|
||||
return static::$bucketAndFileData;
|
||||
}
|
||||
|
||||
public function testCreateBucketAndFile(): void
|
||||
{
|
||||
$bucket = $this->client->call(
|
||||
Client::METHOD_POST,
|
||||
@@ -65,21 +129,11 @@ trait TokensBase
|
||||
$this->assertEquals(201, $token['headers']['status-code']);
|
||||
$this->assertEquals($bucketId . ':' . $fileId, $token['body']['resourceId']);
|
||||
$this->assertEquals(TOKENS_RESOURCE_TYPE_FILES, $token['body']['resourceType']);
|
||||
|
||||
return [
|
||||
'fileId' => $fileId,
|
||||
'bucketId' => $bucketId,
|
||||
'token' => $token['body'],
|
||||
'guestHeaders' => [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[Depends('testCreateBucketAndFile')]
|
||||
public function testFailuresWithoutToken(array $data): array
|
||||
public function testFailuresWithoutToken(): void
|
||||
{
|
||||
$data = $this->setupBucketAndFile();
|
||||
$fileId = $data['fileId'];
|
||||
$bucketId = $data['bucketId'];
|
||||
$guestHeaders = $data['guestHeaders'];
|
||||
@@ -135,13 +189,11 @@ trait TokensBase
|
||||
$this->assertEquals(401, $failedDownload['headers']['status-code']);
|
||||
$this->assertEquals('user_unauthorized', $failedDownload['body']['type']);
|
||||
$this->assertEquals('No permissions provided for action \'read\'', $failedDownload['body']['message']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
#[Depends('testCreateBucketAndFile')]
|
||||
public function testPreviewFileWithToken(array $data): array
|
||||
public function testPreviewFileWithToken(): void
|
||||
{
|
||||
$data = $this->setupBucketAndFile();
|
||||
$token = $data['token'];
|
||||
$fileId = $data['fileId'];
|
||||
$bucketId = $data['bucketId'];
|
||||
@@ -169,14 +221,11 @@ trait TokensBase
|
||||
$this->assertEquals($image->getImageWidth(), $original->getImageWidth());
|
||||
$this->assertEquals($image->getImageHeight(), $original->getImageHeight());
|
||||
$this->assertEquals('PNG', $image->getImageFormat());
|
||||
|
||||
$data['jwtToken'] = $tokenJWT;
|
||||
return $data;
|
||||
}
|
||||
|
||||
#[Depends('testPreviewFileWithToken')]
|
||||
public function testCustomPreviewFileWithToken(array $data): array
|
||||
public function testCustomPreviewFileWithToken(): void
|
||||
{
|
||||
$data = $this->setupBucketAndFile();
|
||||
$fileId = $data['fileId'];
|
||||
$bucketId = $data['bucketId'];
|
||||
$jwtToken = $data['jwtToken'];
|
||||
@@ -209,13 +258,11 @@ trait TokensBase
|
||||
$this->assertEquals($image->getImageWidth(), $original->getImageWidth());
|
||||
$this->assertEquals($image->getImageHeight(), $original->getImageHeight());
|
||||
$this->assertEquals('PNG', $image->getImageFormat());
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
#[Depends('testPreviewFileWithToken')]
|
||||
public function testViewFileWithToken(array $data): void
|
||||
public function testViewFileWithToken(): void
|
||||
{
|
||||
$data = $this->setupBucketAndFile();
|
||||
$fileId = $data['fileId'];
|
||||
$bucketId = $data['bucketId'];
|
||||
$jwtToken = $data['jwtToken'];
|
||||
@@ -241,27 +288,27 @@ trait TokensBase
|
||||
$this->assertEquals('PNG', $image->getImageFormat());
|
||||
}
|
||||
|
||||
#[Depends('testPreviewFileWithToken')]
|
||||
public function testDownloadFileWithToken(array $data): void
|
||||
public function testDownloadFileWithToken(): void
|
||||
{
|
||||
$data = $this->setupBucketAndFile();
|
||||
$fileId = $data['fileId'];
|
||||
$bucketId = $data['bucketId'];
|
||||
$jwtToken = $data['jwtToken'];
|
||||
$guestHeaders = $data['guestHeaders'];
|
||||
|
||||
$fileFailedDownload = $this->client->call(
|
||||
$fileDownload = $this->client->call(
|
||||
Client::METHOD_GET,
|
||||
'/storage/buckets/' . $bucketId . '/files/' . $fileId . '/download',
|
||||
$guestHeaders,
|
||||
[
|
||||
'token' => $jwtToken
|
||||
'token' => $jwtToken
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(200, $fileFailedDownload['headers']['status-code']);
|
||||
$this->assertEquals(200, $fileDownload['headers']['status-code']);
|
||||
|
||||
$image = new \Imagick();
|
||||
$image->readImageBlob($fileFailedDownload['body']);
|
||||
$image->readImageBlob($fileDownload['body']);
|
||||
$original = new \Imagick(__DIR__ . '/../../../resources/logo.png');
|
||||
|
||||
$this->assertEquals($image->getImageWidth(), $original->getImageWidth());
|
||||
|
||||
@@ -5,7 +5,6 @@ namespace Tests\E2E\Services\Tokens;
|
||||
use Ahc\Jwt\JWT;
|
||||
use Ahc\Jwt\JWTException;
|
||||
use CURLFile;
|
||||
use PHPUnit\Framework\Attributes\Depends;
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
@@ -22,7 +21,63 @@ class TokensConsoleClientTest extends Scope
|
||||
use ProjectCustom;
|
||||
use SideServer;
|
||||
|
||||
public function testCreateToken(): array
|
||||
private static array $tokenData = [];
|
||||
|
||||
protected function setupToken(): array
|
||||
{
|
||||
if (!empty(static::$tokenData)) {
|
||||
return static::$tokenData;
|
||||
}
|
||||
|
||||
$bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()), [
|
||||
'bucketId' => ID::unique(),
|
||||
'name' => 'Test Bucket',
|
||||
'fileSecurity' => true,
|
||||
'maximumFileSize' => 2000000, //2MB
|
||||
'allowedFileExtensions' => ['jpg', 'png', 'jfif'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$bucketId = $bucket['body']['$id'];
|
||||
|
||||
$file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', array_merge([
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'),
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$fileId = $file['body']['$id'];
|
||||
|
||||
$token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()));
|
||||
|
||||
static::$tokenData = [
|
||||
'fileId' => $fileId,
|
||||
'bucketId' => $bucketId,
|
||||
'tokenId' => $token['body']['$id'],
|
||||
];
|
||||
|
||||
return static::$tokenData;
|
||||
}
|
||||
|
||||
public function testCreateToken(): void
|
||||
{
|
||||
|
||||
$bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', array_merge([
|
||||
@@ -113,17 +168,11 @@ class TokensConsoleClientTest extends Scope
|
||||
$this->fail('Failed to decode JWT: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'fileId' => $fileId,
|
||||
'bucketId' => $bucketId,
|
||||
'tokenId' => $token['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
#[Depends('testCreateToken')]
|
||||
public function testUpdateToken(array $data): array
|
||||
public function testUpdateToken(): void
|
||||
{
|
||||
$data = $this->setupToken();
|
||||
$tokenId = $data['tokenId'];
|
||||
|
||||
// Failure case: Expire date is in the past
|
||||
@@ -182,13 +231,11 @@ class TokensConsoleClientTest extends Scope
|
||||
} catch (JWTException $e) {
|
||||
$this->fail('Failed to decode JWT: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
#[Depends('testCreateToken')]
|
||||
public function testListTokens(array $data): array
|
||||
public function testListTokens(): void
|
||||
{
|
||||
$data = $this->setupToken();
|
||||
$res = $this->client->call(
|
||||
Client::METHOD_GET,
|
||||
'/tokens/buckets/' . $data['bucketId'] . '/files/' . $data['fileId'],
|
||||
@@ -236,14 +283,23 @@ class TokensConsoleClientTest extends Scope
|
||||
$this->fail('Failed to decode JWT for token ' . $token['$id'] . ': ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
#[Depends('testUpdateToken')]
|
||||
public function testDeleteToken(array $data): array
|
||||
public function testDeleteToken(): void
|
||||
{
|
||||
$tokenId = $data['tokenId'];
|
||||
// Create a fresh token specifically for deletion test
|
||||
$data = $this->setupToken();
|
||||
$bucketId = $data['bucketId'];
|
||||
$fileId = $data['fileId'];
|
||||
|
||||
// Create a new token to delete
|
||||
$token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(201, $token['headers']['status-code']);
|
||||
$tokenId = $token['body']['$id'];
|
||||
|
||||
$res = $this->client->call(Client::METHOD_DELETE, '/tokens/' . $tokenId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
@@ -251,6 +307,5 @@ class TokensConsoleClientTest extends Scope
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(204, $res['headers']['status-code']);
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace Tests\E2E\Services\Tokens;
|
||||
|
||||
use CURLFile;
|
||||
use PHPUnit\Framework\Attributes\Depends;
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
@@ -19,7 +18,64 @@ class TokensCustomServerTest extends Scope
|
||||
use ProjectCustom;
|
||||
use SideServer;
|
||||
|
||||
public function testCreateToken(): array
|
||||
private static array $tokenData = [];
|
||||
|
||||
protected function setupToken(): array
|
||||
{
|
||||
if (!empty(static::$tokenData)) {
|
||||
return static::$tokenData;
|
||||
}
|
||||
|
||||
$bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'bucketId' => ID::unique(),
|
||||
'name' => 'Test Bucket',
|
||||
'fileSecurity' => true,
|
||||
'maximumFileSize' => 2000000, //2MB
|
||||
'allowedFileExtensions' => ['jpg', 'png', 'jfif'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$bucketId = $bucket['body']['$id'];
|
||||
|
||||
$file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', array_merge([
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'),
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$fileId = $file['body']['$id'];
|
||||
|
||||
$token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()));
|
||||
|
||||
static::$tokenData = [
|
||||
'fileId' => $fileId,
|
||||
'bucketId' => $bucketId,
|
||||
'tokenId' => $token['body']['$id'],
|
||||
];
|
||||
|
||||
return static::$tokenData;
|
||||
}
|
||||
|
||||
public function testCreateToken(): void
|
||||
{
|
||||
|
||||
$bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', [
|
||||
@@ -79,17 +135,11 @@ class TokensCustomServerTest extends Scope
|
||||
|
||||
$this->assertEquals(201, $token['headers']['status-code']);
|
||||
$this->assertEquals('files', $token['body']['resourceType']);
|
||||
|
||||
return [
|
||||
'fileId' => $fileId,
|
||||
'bucketId' => $bucketId,
|
||||
'tokenId' => $token['body']['$id'],
|
||||
];
|
||||
}
|
||||
|
||||
#[Depends('testCreateToken')]
|
||||
public function testUpdateToken(array $data): array
|
||||
public function testUpdateToken(): void
|
||||
{
|
||||
$data = $this->setupToken();
|
||||
$tokenId = $data['tokenId'];
|
||||
|
||||
// Failure case: Expire date is in the past
|
||||
@@ -126,13 +176,11 @@ class TokensCustomServerTest extends Scope
|
||||
]);
|
||||
|
||||
$this->assertEmpty($token['body']['expire']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
#[Depends('testCreateToken')]
|
||||
public function testListTokens(array $data): array
|
||||
public function testListTokens(): void
|
||||
{
|
||||
$data = $this->setupToken();
|
||||
$res = $this->client->call(
|
||||
Client::METHOD_GET,
|
||||
'/tokens/buckets/' . $data['bucketId'] . '/files/' . $data['fileId'],
|
||||
@@ -145,13 +193,23 @@ class TokensCustomServerTest extends Scope
|
||||
|
||||
$this->assertIsArray($res['body']);
|
||||
$this->assertEquals(200, $res['headers']['status-code']);
|
||||
return $data;
|
||||
}
|
||||
|
||||
#[Depends('testUpdateToken')]
|
||||
public function testDeleteToken(array $data): array
|
||||
public function testDeleteToken(): void
|
||||
{
|
||||
$tokenId = $data['tokenId'];
|
||||
// Create a fresh token specifically for deletion test
|
||||
$data = $this->setupToken();
|
||||
$bucketId = $data['bucketId'];
|
||||
$fileId = $data['fileId'];
|
||||
|
||||
// Create a new token to delete
|
||||
$token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(201, $token['headers']['status-code']);
|
||||
$tokenId = $token['body']['$id'];
|
||||
|
||||
$res = $this->client->call(Client::METHOD_DELETE, '/tokens/' . $tokenId, [
|
||||
'content-type' => 'application/json',
|
||||
@@ -160,6 +218,5 @@ class TokensCustomServerTest extends Scope
|
||||
]);
|
||||
|
||||
$this->assertEquals(204, $res['headers']['status-code']);
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user