From bb72fd8834b0be69be3dc3318db8ae2db53d7c72 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Wed, 30 Oct 2024 14:23:24 +0900 Subject: [PATCH] Fix source creds being over written and begin implementing migrations tests --- .github/workflows/tests.yml | 1 + composer.lock | 110 ++--- phpunit.xml | 1 + src/Appwrite/Platform/Workers/Migrations.php | 16 +- tests/e2e/Scopes/ProjectCustom.php | 2 + .../Services/Migrations/MigrationsBase.php | 441 ++++++++++++++++++ .../MigrationsConsoleClientTest.php | 13 + 7 files changed, 521 insertions(+), 63 deletions(-) create mode 100644 tests/e2e/Services/Migrations/MigrationsBase.php create mode 100644 tests/e2e/Services/Migrations/MigrationsConsoleClientTest.php diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7c53b03b52..29aa9b0581 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -125,6 +125,7 @@ jobs: Webhooks, VCS, Messaging, + Migrations ] steps: diff --git a/composer.lock b/composer.lock index 5c7d48373e..82504334a1 100644 --- a/composer.lock +++ b/composer.lock @@ -157,16 +157,16 @@ }, { "name": "appwrite/php-runtimes", - "version": "0.16.2", + "version": "0.16.4", "source": { "type": "git", "url": "https://github.com/appwrite/runtimes.git", - "reference": "c33005e3eaaf2d427e9fd1077d5335e31f4d36f9" + "reference": "7e4741337b9373f77210396e68eca539018cabd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/runtimes/zipball/c33005e3eaaf2d427e9fd1077d5335e31f4d36f9", - "reference": "c33005e3eaaf2d427e9fd1077d5335e31f4d36f9", + "url": "https://api.github.com/repos/appwrite/runtimes/zipball/7e4741337b9373f77210396e68eca539018cabd1", + "reference": "7e4741337b9373f77210396e68eca539018cabd1", "shasum": "" }, "require": { @@ -206,9 +206,9 @@ ], "support": { "issues": "https://github.com/appwrite/runtimes/issues", - "source": "https://github.com/appwrite/runtimes/tree/0.16.2" + "source": "https://github.com/appwrite/runtimes/tree/0.16.4" }, - "time": "2024-10-09T15:02:52+00:00" + "time": "2024-10-26T10:39:59+00:00" }, { "name": "beberlei/assert", @@ -2175,16 +2175,16 @@ }, { "name": "utopia-php/migration", - "version": "0.6.9", + "version": "0.6.10", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "ce97cdf2ca82e7cec78e2ed484ef2c71ebe8744b" + "reference": "6379fd1cd54cb9cb2d5b5999abcfab49801da510" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/ce97cdf2ca82e7cec78e2ed484ef2c71ebe8744b", - "reference": "ce97cdf2ca82e7cec78e2ed484ef2c71ebe8744b", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/6379fd1cd54cb9cb2d5b5999abcfab49801da510", + "reference": "6379fd1cd54cb9cb2d5b5999abcfab49801da510", "shasum": "" }, "require": { @@ -2225,9 +2225,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/0.6.9" + "source": "https://github.com/utopia-php/migration/tree/0.6.10" }, - "time": "2024-10-16T08:33:21+00:00" + "time": "2024-10-29T02:19:43+00:00" }, { "name": "utopia-php/mongo", @@ -2341,16 +2341,16 @@ }, { "name": "utopia-php/platform", - "version": "0.7.0", + "version": "0.7.1", "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52" + "reference": "3433a0f1a54988f2a59c735f507745cb2c24638a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/beeea0f2c9bce14a6869fc5c87a1047cdecb5c52", - "reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/3433a0f1a54988f2a59c735f507745cb2c24638a", + "reference": "3433a0f1a54988f2a59c735f507745cb2c24638a", "shasum": "" }, "require": { @@ -2385,9 +2385,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.7.0" + "source": "https://github.com/utopia-php/platform/tree/0.7.1" }, - "time": "2024-05-08T17:00:55+00:00" + "time": "2024-10-22T10:27:49+00:00" }, { "name": "utopia-php/pools", @@ -5875,16 +5875,16 @@ }, { "name": "symfony/console", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee" + "reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0fa539d12b3ccf068a722bbbffa07ca7079af9ee", - "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee", + "url": "https://api.github.com/repos/symfony/console/zipball/bb5192af6edc797cbab5c8e8ecfea2fe5f421e57", + "reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57", "shasum": "" }, "require": { @@ -5948,7 +5948,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.5" + "source": "https://github.com/symfony/console/tree/v7.1.6" }, "funding": [ { @@ -5964,7 +5964,7 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:28:38+00:00" + "time": "2024-10-09T08:46:59+00:00" }, { "name": "symfony/deprecation-contracts", @@ -6035,16 +6035,16 @@ }, { "name": "symfony/filesystem", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a" + "reference": "c835867b3c62bb05c7fe3d637c871c7ae52024d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/61fe0566189bf32e8cfee78335d8776f64a66f5a", - "reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/c835867b3c62bb05c7fe3d637c871c7ae52024d4", + "reference": "c835867b3c62bb05c7fe3d637c871c7ae52024d4", "shasum": "" }, "require": { @@ -6081,7 +6081,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.1.5" + "source": "https://github.com/symfony/filesystem/tree/v7.1.6" }, "funding": [ { @@ -6097,20 +6097,20 @@ "type": "tidelift" } ], - "time": "2024-09-17T09:16:35+00:00" + "time": "2024-10-25T15:11:02+00:00" }, { "name": "symfony/finder", - "version": "v7.1.4", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "d95bbf319f7d052082fb7af147e0f835a695e823" + "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/d95bbf319f7d052082fb7af147e0f835a695e823", - "reference": "d95bbf319f7d052082fb7af147e0f835a695e823", + "url": "https://api.github.com/repos/symfony/finder/zipball/2cb89664897be33f78c65d3d2845954c8d7a43b8", + "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8", "shasum": "" }, "require": { @@ -6145,7 +6145,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.1.4" + "source": "https://github.com/symfony/finder/tree/v7.1.6" }, "funding": [ { @@ -6161,20 +6161,20 @@ "type": "tidelift" } ], - "time": "2024-08-13T14:28:19+00:00" + "time": "2024-10-01T08:31:23+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.1.1", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55" + "reference": "85e95eeede2d41cd146146e98c9c81d9214cae85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/47aa818121ed3950acd2b58d1d37d08a94f9bf55", - "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/85e95eeede2d41cd146146e98c9c81d9214cae85", + "reference": "85e95eeede2d41cd146146e98c9c81d9214cae85", "shasum": "" }, "require": { @@ -6212,7 +6212,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.1.1" + "source": "https://github.com/symfony/options-resolver/tree/v7.1.6" }, "funding": [ { @@ -6228,7 +6228,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/polyfill-ctype", @@ -6546,16 +6546,16 @@ }, { "name": "symfony/process", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "5c03ee6369281177f07f7c68252a280beccba847" + "reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/5c03ee6369281177f07f7c68252a280beccba847", - "reference": "5c03ee6369281177f07f7c68252a280beccba847", + "url": "https://api.github.com/repos/symfony/process/zipball/6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e", + "reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e", "shasum": "" }, "require": { @@ -6587,7 +6587,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.1.5" + "source": "https://github.com/symfony/process/tree/v7.1.6" }, "funding": [ { @@ -6603,7 +6603,7 @@ "type": "tidelift" } ], - "time": "2024-09-19T21:48:23+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/service-contracts", @@ -6690,16 +6690,16 @@ }, { "name": "symfony/string", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306" + "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/d66f9c343fa894ec2037cc928381df90a7ad4306", - "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306", + "url": "https://api.github.com/repos/symfony/string/zipball/61b72d66bf96c360a727ae6232df5ac83c71f626", + "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626", "shasum": "" }, "require": { @@ -6757,7 +6757,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.5" + "source": "https://github.com/symfony/string/tree/v7.1.6" }, "funding": [ { @@ -6773,7 +6773,7 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:28:38+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "textalk/websocket", @@ -7005,7 +7005,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/phpunit.xml b/phpunit.xml index 90ebd4225f..4c4e55ea4e 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -33,6 +33,7 @@ ./tests/e2e/Services/Storage ./tests/e2e/Services/Webhooks ./tests/e2e/Services/Messaging + ./tests/e2e/Services/Migrations ./tests/e2e/Services/Functions/FunctionsBase.php ./tests/e2e/Services/Functions/FunctionsCustomServerTest.php ./tests/e2e/Services/Functions/FunctionsCustomClientTest.php diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index beff0b064b..eccf3a50d9 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -124,7 +124,7 @@ class Migrations extends Action ), SourceAppwrite::getName() => new SourceAppwrite( $credentials['projectId'], - $credentials['endpoint'], + $credentials['endpoint'] === 'http://localhost/v1' ? 'http://appwrite/v1' : $credentials['endpoint'], $credentials['apiKey'], ), default => throw new \Exception('Invalid source type'), @@ -134,16 +134,16 @@ class Migrations extends Action /** * @throws Exception */ - protected function processDestination(Document $migration): Destination + protected function processDestination(Document $migration, string $apiKey): Destination { $destination = $migration->getAttribute('destination'); $credentials = $migration->getAttribute('credentials'); return match ($destination) { DestinationAppwrite::getName() => new DestinationAppwrite( - $credentials['projectId'], - $credentials['endpoint'], - $credentials['apiKey'], + $this->project->getId(), + 'http://appwrite/v1', + $apiKey, $this->dbForProject, Config::getParam('collections', [])['databases']['collections'], ), @@ -272,8 +272,8 @@ class Migrations extends Action $migration = $this->dbForProject->getDocument('migrations', $migration->getId()); if ( - $migration->getAttribute('source') === SourceAppwrite::getName() || - $migration->getAttribute('destination') === DestinationAppwrite::getName() + $migration->getAttribute('source') === SourceAppwrite::getName() && + empty($migration->getAttribute('credentials', [])) ) { $credentials = $migration->getAttribute('credentials', []); @@ -291,7 +291,7 @@ class Migrations extends Action $log->addTag('type', $migration->getAttribute('source')); $source = $this->processSource($migration); - $destination = $this->processDestination($migration); + $destination = $this->processDestination($migration, $tempAPIKey->getAttribute('secret')); $source->report(); diff --git a/tests/e2e/Scopes/ProjectCustom.php b/tests/e2e/Scopes/ProjectCustom.php index ad2c93790c..7f84ace6f2 100644 --- a/tests/e2e/Scopes/ProjectCustom.php +++ b/tests/e2e/Scopes/ProjectCustom.php @@ -94,6 +94,8 @@ trait ProjectCustom 'topics.read', 'subscribers.write', 'subscribers.read', + 'migrations.write', + 'migrations.read' ], ]); diff --git a/tests/e2e/Services/Migrations/MigrationsBase.php b/tests/e2e/Services/Migrations/MigrationsBase.php new file mode 100644 index 0000000000..ff9687c3cf --- /dev/null +++ b/tests/e2e/Services/Migrations/MigrationsBase.php @@ -0,0 +1,441 @@ +getProject(true); + self::$project = $projectBackup; + + return self::$sideProject; + } + + public function performMigrationSync( + callable $migrationRequest, + ): array { + $migration = $migrationRequest(); + + $this->assertEquals(202, $migration['headers']['status-code']); + $this->assertNotEmpty($migration['body']); + $this->assertNotEmpty($migration['body']['$id']); + + $attempts = 0; + while ($attempts < 5) { + $response = $this->client->call(Client::METHOD_GET, '/migrations/' . $migration['body']['$id'], [ + + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$id']); + + if ($response['body']['status'] === 'failed') { + var_dump($response['body']); + } + + $this->assertNotEquals('failed', $response['body']['status']); + + if ($response['body']['status'] === 'completed') { + return $response['body']; + } + + if ($attempts === 4) { + $this->assertEquals('completed', $response['body']['status']); + } + + $attempts++; + sleep(5); + } + } + + /** + * Appwrite E2E Migration Tests + */ + public function testCreateAppwriteMigration() + { + $response = $this->client->call(Client::METHOD_POST, '/migrations/appwrite', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'resources' => Appwrite::getSupportedResources(), + 'endpoint' => 'http://localhost/v1', + 'projectId' => $this->getSideProject()['$id'], + 'apiKey' => $this->getSideProject()['apiKey'], + ]); + + $this->assertEquals(202, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals('Appwrite', $response['body']['source']); + + // Check empty migration completes without issues + sleep(5); + + $attempts = 0; + $maxAttempts = 10; + while ($attempts < $maxAttempts) { + $response = $this->client->call(Client::METHOD_GET, '/migrations/' . $response['body']['$id'], [ + + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals('Appwrite', $response['body']['source']); + + $this->assertNotEquals('failed', $response['body']['status']); + + if ($response['body']['status'] === 'completed') { + break; + } + + if ($attempts === $maxAttempts - 1) { + $this->assertEquals('completed', $response['body']['status']); + } + + $attempts++; + sleep(2); + } + } + + /** + * Auth + */ + public function testAppwriteMigrationAuthUserPassword() + { + $sideProject = $this->getSideProject(); + + $response = $this->client->call(Client::METHOD_POST, '/users', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $sideProject['$id'], + 'x-appwrite-key' => $sideProject['apiKey'], + ], [ + 'userId' => ID::unique(), + 'email' => 'test@test.com', + 'password' => 'password', + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals('test@test.com', $response['body']['email']); + + $user = $response['body']; + + $result = $this->performMigrationSync(function () { + return $this->client->call(Client::METHOD_POST, '/migrations/appwrite', [ + + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'resources' => [ + Resource::TYPE_USER, + ], + 'endpoint' => 'http://localhost/v1', + 'projectId' => $this->getSideProject()['$id'], + 'apiKey' => $this->getSideProject()['apiKey'], + ]); + }); + + $this->assertEquals('completed', $result['status']); + $this->assertEquals([Resource::TYPE_USER], $result['resources']); + $this->assertArrayHasKey(Resource::TYPE_USER, $result['statusCounters']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_USER]['error']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_USER]['pending']); + $this->assertEquals(1, $result['statusCounters'][Resource::TYPE_USER]['success']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_USER]['processing']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_USER]['warning']); + + $response = $this->client->call(Client::METHOD_GET, '/users/' . $user['$id'], [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals($user['email'], $response['body']['email']); + $this->assertEquals($user['password'], $response['body']['password']); + + // Cleanup + $this->client->call(Client::METHOD_DELETE, '/users/' . $user['$id'], [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->client->call(Client::METHOD_DELETE, '/users/' . $user['$id'], [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $sideProject['$id'], + 'x-appwrite-key' => $sideProject['apiKey'], + ]); + } + + public function testAppwriteMigrationAuthUserPhone() + { + $sideProject = $this->getSideProject(); + + $response = $this->client->call(Client::METHOD_POST, '/users', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $sideProject['$id'], + 'x-appwrite-key' => $sideProject['apiKey'], + ], [ + 'userId' => ID::unique(), + 'phone' => '+12065550100', + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals('+12065550100', $response['body']['phone']); + + $user = $response['body']; + + $result = $this->performMigrationSync(function () { + return $this->client->call(Client::METHOD_POST, '/migrations/appwrite', [ + + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'resources' => [ + Resource::TYPE_USER, + ], + 'endpoint' => 'http://localhost/v1', + 'projectId' => $this->getSideProject()['$id'], + 'apiKey' => $this->getSideProject()['apiKey'], + ]); + }); + + $this->assertEquals('completed', $result['status']); + $this->assertEquals([Resource::TYPE_USER], $result['resources']); + $this->assertArrayHasKey(Resource::TYPE_USER, $result['statusCounters']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_USER]['error']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_USER]['pending']); + $this->assertEquals(1, $result['statusCounters'][Resource::TYPE_USER]['success']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_USER]['processing']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_USER]['warning']); + + $response = $this->client->call(Client::METHOD_GET, '/users/' . $user['$id'], [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals($user['phone'], $response['body']['phone']); + + // Cleanup + $this->client->call(Client::METHOD_DELETE, '/users/' . $user['$id'], [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->client->call(Client::METHOD_DELETE, '/users/' . $user['$id'], [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $sideProject['$id'], + 'x-appwrite-key' => $sideProject['apiKey'], + ]); + } + + public function testAppwriteMigrationAuthTeam() + { + $sideProject = $this->getSideProject(); + + $user = $this->client->call(Client::METHOD_POST, '/users', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $sideProject['$id'], + 'x-appwrite-key' => $sideProject['apiKey'], + ], [ + 'userId' => ID::unique(), + 'email' => 'test@test.com', + 'password' => 'password', + ]); + + $this->assertEquals(201, $user['headers']['status-code']); + $this->assertNotEmpty($user['body']); + $this->assertNotEmpty($user['body']['$id']); + $this->assertEquals('test@test.com', $user['body']['email']); + + $team = $this->client->call(Client::METHOD_POST, '/teams', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $sideProject['$id'], + 'x-appwrite-key' => $sideProject['apiKey'], + ], [ + 'teamId' => ID::unique(), + 'name' => 'Test Team', + ]); + + $this->assertEquals(201, $team['headers']['status-code']); + $this->assertNotEmpty($team['body']); + $this->assertNotEmpty($team['body']['$id']); + + $membership = $this->client->call(Client::METHOD_POST, '/teams/' . $team['body']['$id'] . '/memberships', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $sideProject['$id'], + 'x-appwrite-key' => $sideProject['apiKey'], + ], [ + 'teamId' => $team['body']['$id'], + 'userId' => $user['body']['$id'], + 'roles' => ['owner'], + ]); + + $this->assertEquals(201, $membership['headers']['status-code']); + $this->assertNotEmpty($membership['body']); + $this->assertNotEmpty($membership['body']['$id']); + + $result = $this->performMigrationSync(function () { + return $this->client->call(Client::METHOD_POST, '/migrations/appwrite', [ + + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'resources' => [ + Resource::TYPE_USER, + Resource::TYPE_TEAM, + Resource::TYPE_MEMBERSHIP, + ], + 'endpoint' => 'http://localhost/v1', + 'projectId' => $this->getSideProject()['$id'], + 'apiKey' => $this->getSideProject()['apiKey'], + ]); + }); + + $this->assertEquals('completed', $result['status']); + $this->assertEquals([Resource::TYPE_USER, Resource::TYPE_TEAM, Resource::TYPE_MEMBERSHIP], $result['resources']); + $this->assertArrayHasKey(Resource::TYPE_USER, $result['statusCounters']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_USER]['error']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_USER]['pending']); + $this->assertEquals(1, $result['statusCounters'][Resource::TYPE_USER]['success']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_USER]['processing']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_USER]['warning']); + + $this->assertArrayHasKey(Resource::TYPE_TEAM, $result['statusCounters']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_TEAM]['error']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_TEAM]['pending']); + $this->assertEquals(1, $result['statusCounters'][Resource::TYPE_TEAM]['success']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_TEAM]['processing']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_TEAM]['warning']); + + $this->assertArrayHasKey(Resource::TYPE_MEMBERSHIP, $result['statusCounters']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_MEMBERSHIP]['error']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_MEMBERSHIP]['pending']); + $this->assertEquals(1, $result['statusCounters'][Resource::TYPE_MEMBERSHIP]['success']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_MEMBERSHIP]['processing']); + $this->assertEquals(0, $result['statusCounters'][Resource::TYPE_MEMBERSHIP]['warning']); + + $response = $this->client->call(Client::METHOD_GET, '/teams/' . $team['body']['$id'], [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertEquals($team['body']['name'], $response['body']['name']); + + $response = $this->client->call(Client::METHOD_GET, '/teams/' . $team['body']['$id'] . '/memberships', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']); + + $membership = $response['body']['memberships'][0]; + + $this->assertEquals($user['body']['$id'], $membership['userId']); + $this->assertEquals($team['body']['$id'], $membership['teamId']); + $this->assertEquals(['owner'], $membership['roles']); + + // Cleanup + $this->client->call(Client::METHOD_DELETE, '/teams/' . $team['body']['$id'], [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->client->call(Client::METHOD_DELETE, '/teams/' . $team['body']['$id'], [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $sideProject['$id'], + 'x-appwrite-key' => $sideProject['apiKey'], + ]); + + $this->client->call(Client::METHOD_DELETE, '/users/' . $user['body']['$id'], [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->client->call(Client::METHOD_DELETE, '/users/' . $user['body']['$id'], [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $sideProject['$id'], + 'x-appwrite-key' => $sideProject['apiKey'], + ]); + + $this->client->call(Client::METHOD_DELETE, '/teams/' . $team['body']['$id'], [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->client->call(Client::METHOD_DELETE, '/teams/' . $team['body']['$id'], [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $sideProject['$id'], + 'x-appwrite-key' => $sideProject['apiKey'], + ]); + } + + /** + * Databases + */ + + /** + * Storage + */ + + /** + * Functions + */ +} \ No newline at end of file diff --git a/tests/e2e/Services/Migrations/MigrationsConsoleClientTest.php b/tests/e2e/Services/Migrations/MigrationsConsoleClientTest.php new file mode 100644 index 0000000000..a6711d5b75 --- /dev/null +++ b/tests/e2e/Services/Migrations/MigrationsConsoleClientTest.php @@ -0,0 +1,13 @@ +