2024-10-30 05:23:24 +00:00
< ? php
namespace Tests\E2E\Services\Migrations ;
2024-10-31 05:35:54 +00:00
use CURLFile ;
2024-10-30 05:23:24 +00:00
use Tests\E2E\Client ;
2025-02-11 07:56:39 +00:00
use Tests\E2E\General\UsageTest ;
2024-10-30 05:23:24 +00:00
use Tests\E2E\Scopes\ProjectCustom ;
2024-10-31 05:35:54 +00:00
use Tests\E2E\Services\Functions\FunctionsBase ;
2024-10-30 05:23:24 +00:00
use Utopia\Database\Helpers\ID ;
2024-10-31 05:35:54 +00:00
use Utopia\Database\Helpers\Permission ;
use Utopia\Database\Helpers\Role ;
2025-04-16 11:22:23 +00:00
use Utopia\Database\Query ;
2024-10-30 05:23:24 +00:00
use Utopia\Migration\Resource ;
use Utopia\Migration\Sources\Appwrite ;
2025-04-16 11:22:23 +00:00
use Utopia\Migration\Sources\CSV ;
2024-10-30 05:23:24 +00:00
trait MigrationsBase
{
use ProjectCustom ;
2024-10-31 05:35:54 +00:00
use FunctionsBase ;
2024-10-30 05:23:24 +00:00
/**
* @ var array
*/
2025-02-11 07:56:39 +00:00
protected static array $destinationProject = [];
2024-10-30 05:23:24 +00:00
/**
* @ param bool $fresh
* @ return array
*/
2025-02-11 07:56:39 +00:00
public function getDestinationProject ( bool $fresh = false ) : array
2024-10-30 05:23:24 +00:00
{
2024-10-31 05:35:54 +00:00
if ( ! empty ( self :: $destinationProject ) && ! $fresh ) {
return self :: $destinationProject ;
2024-10-30 05:23:24 +00:00
}
$projectBackup = self :: $project ;
2024-10-31 05:35:54 +00:00
self :: $destinationProject = $this -> getProject ( true );
2024-10-30 05:23:24 +00:00
self :: $project = $projectBackup ;
2024-10-31 05:35:54 +00:00
return self :: $destinationProject ;
2024-10-30 05:23:24 +00:00
}
2025-02-11 09:04:52 +00:00
public function performMigrationSync ( array $body ) : array
{
2024-10-31 05:35:54 +00:00
$migration = $this -> client -> call ( Client :: METHOD_POST , '/migrations/appwrite' , [
'content-type' => 'application/json' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-31 05:35:54 +00:00
], $body );
2024-10-30 05:23:24 +00:00
$this -> assertEquals ( 202 , $migration [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $migration [ 'body' ]);
$this -> assertNotEmpty ( $migration [ 'body' ][ '$id' ]);
2024-10-30 05:27:25 +00:00
2025-03-03 16:31:41 +00:00
$migrationResult = [];
$this -> assertEventually ( function () use ( $migration , & $migrationResult ) {
2024-10-30 05:23:24 +00:00
$response = $this -> client -> call ( Client :: METHOD_GET , '/migrations/' . $migration [ 'body' ][ '$id' ], [
'content-type' => 'application/json' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-30 05:23:24 +00:00
]);
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $response [ 'body' ]);
$this -> assertNotEmpty ( $response [ 'body' ][ '$id' ]);
if ( $response [ 'body' ][ 'status' ] === 'failed' ) {
2025-03-03 16:31:41 +00:00
$this -> fail ( 'Migration failed' . json_encode ( $response [ 'body' ], JSON_PRETTY_PRINT ));
2024-10-30 05:23:24 +00:00
}
$this -> assertNotEquals ( 'failed' , $response [ 'body' ][ 'status' ]);
2025-03-03 16:31:41 +00:00
$this -> assertEquals ( 'completed' , $response [ 'body' ][ 'status' ]);
2024-10-30 05:23:24 +00:00
2025-03-03 16:31:41 +00:00
$migrationResult = $response [ 'body' ];
2024-10-30 05:23:24 +00:00
2025-03-03 16:31:41 +00:00
return true ;
});
2025-02-11 07:56:39 +00:00
2025-03-03 16:31:41 +00:00
return $migrationResult ;
2024-10-30 05:23:24 +00:00
}
/**
* Appwrite E2E Migration Tests
*/
2025-02-11 07:56:39 +00:00
public function testCreateAppwriteMigration () : void
2024-10-30 05:23:24 +00:00
{
2024-10-31 05:35:54 +00:00
$response = $this -> performMigrationSync ([
2024-10-30 05:23:24 +00:00
'resources' => Appwrite :: getSupportedResources (),
'endpoint' => 'http://localhost/v1' ,
2024-10-31 05:35:54 +00:00
'projectId' => $this -> getProject ()[ '$id' ],
'apiKey' => $this -> getProject ()[ 'apiKey' ],
2024-10-30 05:23:24 +00:00
]);
2024-10-31 05:35:54 +00:00
$this -> assertEquals ( Appwrite :: getSupportedResources (), $response [ 'resources' ]);
$this -> assertEquals ( 'Appwrite' , $response [ 'source' ]);
$this -> assertEquals ( 'Appwrite' , $response [ 'destination' ]);
$this -> assertEmpty ( $response [ 'statusCounters' ]);
2024-10-30 05:23:24 +00:00
}
/**
* Auth
*/
2025-02-11 07:56:39 +00:00
public function testAppwriteMigrationAuthUserPassword () : void
2024-10-30 05:23:24 +00:00
{
$response = $this -> client -> call ( Client :: METHOD_POST , '/users' , [
'content-type' => 'application/json' ,
2024-10-31 05:35:54 +00:00
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
2024-10-30 05:23:24 +00:00
], [
'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' ];
2024-10-31 05:35:54 +00:00
$result = $this -> performMigrationSync ([
2024-10-31 06:04:44 +00:00
'resources' => [
Resource :: TYPE_USER ,
],
'endpoint' => 'http://localhost/v1' ,
'projectId' => $this -> getProject ()[ '$id' ],
'apiKey' => $this -> getProject ()[ 'apiKey' ],
]);
2024-10-30 05:23:24 +00:00
$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' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-30 05:23:24 +00:00
]);
$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' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-30 05:23:24 +00:00
]);
$this -> client -> call ( Client :: METHOD_DELETE , '/users/' . $user [ '$id' ], [
'content-type' => 'application/json' ,
2024-10-31 05:35:54 +00:00
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
2024-10-30 05:23:24 +00:00
]);
}
2025-02-11 07:56:39 +00:00
public function testAppwriteMigrationAuthUserPhone () : void
2024-10-30 05:23:24 +00:00
{
$response = $this -> client -> call ( Client :: METHOD_POST , '/users' , [
'content-type' => 'application/json' ,
2024-10-31 05:35:54 +00:00
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
2024-10-30 05:23:24 +00:00
], [
'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' ];
2024-10-31 05:35:54 +00:00
$result = $this -> performMigrationSync ([
'resources' => [
Resource :: TYPE_USER ,
],
'endpoint' => 'http://localhost/v1' ,
'projectId' => $this -> getProject ()[ '$id' ],
'apiKey' => $this -> getProject ()[ 'apiKey' ],
]);
2024-10-30 05:23:24 +00:00
$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' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-30 05:23:24 +00:00
]);
$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' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-30 05:23:24 +00:00
]);
}
2025-02-11 07:56:39 +00:00
public function testAppwriteMigrationAuthTeam () : void
2024-10-30 05:23:24 +00:00
{
$user = $this -> client -> call ( Client :: METHOD_POST , '/users' , [
'content-type' => 'application/json' ,
2024-10-31 05:35:54 +00:00
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
2024-10-30 05:23:24 +00:00
], [
'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' ,
2024-10-31 05:35:54 +00:00
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
2024-10-30 05:23:24 +00:00
], [
'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' ,
2024-10-31 05:35:54 +00:00
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
2024-10-30 05:23:24 +00:00
], [
'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' ]);
2024-10-31 05:35:54 +00:00
$result = $this -> performMigrationSync ([
2024-10-31 06:04:44 +00:00
'resources' => [
Resource :: TYPE_USER ,
Resource :: TYPE_TEAM ,
Resource :: TYPE_MEMBERSHIP ,
],
'endpoint' => 'http://localhost/v1' ,
'projectId' => $this -> getProject ()[ '$id' ],
'apiKey' => $this -> getProject ()[ 'apiKey' ],
]);
2024-10-30 05:23:24 +00:00
$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' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-30 05:23:24 +00:00
]);
$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' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-30 05:23:24 +00:00
]);
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $response [ 'body' ]);
$membership = $response [ 'body' ][ 'memberships' ][ 0 ];
2024-10-30 05:27:25 +00:00
2024-10-30 05:23:24 +00:00
$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' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-30 05:23:24 +00:00
]);
$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' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-30 05:23:24 +00:00
]);
$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' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-30 05:23:24 +00:00
]);
}
/**
* Databases
*/
2025-02-11 07:56:39 +00:00
public function testAppwriteMigrationDatabase () : array
2024-10-31 05:35:54 +00:00
{
$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' ]);
$databaseId = $response [ 'body' ][ '$id' ];
$result = $this -> performMigrationSync ([
'resources' => [
Resource :: TYPE_DATABASE ,
],
'endpoint' => 'http://localhost/v1' ,
'projectId' => $this -> getProject ()[ '$id' ],
'apiKey' => $this -> getProject ()[ 'apiKey' ],
]);
$this -> assertEquals ( 'completed' , $result [ 'status' ]);
$this -> assertEquals ([ Resource :: TYPE_DATABASE ], $result [ 'resources' ]);
$this -> assertArrayHasKey ( Resource :: TYPE_DATABASE , $result [ 'statusCounters' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_DATABASE ][ 'error' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_DATABASE ][ 'pending' ]);
$this -> assertEquals ( 1 , $result [ 'statusCounters' ][ Resource :: TYPE_DATABASE ][ 'success' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_DATABASE ][ 'processing' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_DATABASE ][ 'warning' ]);
$response = $this -> client -> call ( Client :: METHOD_GET , '/databases/' . $databaseId , [
'content-type' => 'application/json' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-31 05:35:54 +00:00
]);
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $response [ 'body' ]);
$this -> assertNotEmpty ( $response [ 'body' ][ '$id' ]);
$this -> assertEquals ( $databaseId , $response [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'Test Database' , $response [ 'body' ][ 'name' ]);
// Cleanup on destination
$this -> client -> call ( Client :: METHOD_DELETE , '/databases/' . $databaseId , [
'content-type' => 'application/json' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-31 05:35:54 +00:00
]);
return [
'databaseId' => $databaseId ,
];
}
/**
* @ depends testAppwriteMigrationDatabase
*/
2025-02-11 07:56:39 +00:00
public function testAppwriteMigrationDatabasesCollection ( array $data ) : array
2024-10-31 05:35:54 +00:00
{
$databaseId = $data [ 'databaseId' ];
$collection = $this -> client -> call ( Client :: METHOD_POST , '/databases/' . $databaseId . '/collections' , [
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
], [
2025-05-06 05:59:56 +00:00
'tableId' => ID :: unique (),
2024-10-31 05:35:54 +00:00
'name' => 'Test Collection' ,
]);
$this -> assertEquals ( 201 , $collection [ 'headers' ][ 'status-code' ]);
$collectionId = $collection [ 'body' ][ '$id' ];
// Create Attribute
$response = $this -> client -> call ( Client :: METHOD_POST , '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/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 attribute to be ready
$this -> assertEventually ( function () use ( $databaseId , $collectionId ) {
$response = $this -> client -> call ( Client :: METHOD_GET , '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/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 );
$result = $this -> performMigrationSync ([
'resources' => [
Resource :: TYPE_DATABASE ,
Resource :: TYPE_COLLECTION ,
Resource :: TYPE_ATTRIBUTE ,
],
'endpoint' => 'http://localhost/v1' ,
'projectId' => $this -> getProject ()[ '$id' ],
'apiKey' => $this -> getProject ()[ 'apiKey' ],
]);
$this -> assertEquals ( 'completed' , $result [ 'status' ]);
$this -> assertEquals ([ Resource :: TYPE_DATABASE , Resource :: TYPE_COLLECTION , Resource :: TYPE_ATTRIBUTE ], $result [ 'resources' ]);
foreach ([ Resource :: TYPE_DATABASE , Resource :: TYPE_COLLECTION , Resource :: TYPE_ATTRIBUTE ] as $resource ) {
$this -> assertArrayHasKey ( $resource , $result [ 'statusCounters' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ $resource ][ 'error' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ $resource ][ 'pending' ]);
$this -> assertEquals ( 1 , $result [ 'statusCounters' ][ $resource ][ 'success' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ $resource ][ 'processing' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ $resource ][ 'warning' ]);
}
$response = $this -> client -> call ( Client :: METHOD_GET , '/databases/' . $databaseId . '/collections/' . $collectionId , [
'content-type' => 'application/json' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-31 05:35:54 +00:00
]);
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $response [ 'body' ]);
$this -> assertEquals ( $collectionId , $response [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'Test Collection' , $response [ 'body' ][ 'name' ]);
$response = $this -> client -> call ( Client :: METHOD_GET , '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/name' , [
'content-type' => 'application/json' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-31 05:35:54 +00:00
]);
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $response [ 'body' ]);
$this -> assertEquals ( 'name' , $response [ 'body' ][ 'key' ]);
$this -> assertEquals ( 100 , $response [ 'body' ][ 'size' ]);
$this -> assertEquals ( true , $response [ 'body' ][ 'required' ]);
// Cleanup
$this -> client -> call ( Client :: METHOD_DELETE , '/databases/' . $databaseId , [
'content-type' => 'application/json' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-31 05:35:54 +00:00
]);
return [
'databaseId' => $databaseId ,
2025-05-06 05:59:56 +00:00
'tableId' => $collectionId ,
2024-10-31 05:35:54 +00:00
];
}
/**
* @ depends testAppwriteMigrationDatabasesCollection
*/
2025-02-11 07:56:39 +00:00
public function testAppwriteMigrationDatabasesDocument ( array $data ) : void
2024-10-31 05:35:54 +00:00
{
$databaseId = $data [ 'databaseId' ];
2025-05-06 05:59:56 +00:00
$collectionId = $data [ 'tableId' ];
2024-10-31 05:35:54 +00:00
$document = $this -> client -> call ( Client :: METHOD_POST , '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents' , [
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
], [
2025-05-06 05:59:56 +00:00
'rowId' => ID :: unique (),
2024-10-31 05:35:54 +00:00
'data' => [
'name' => 'Test Document' ,
]
]);
$this -> assertEquals ( 201 , $document [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $document [ 'body' ]);
$this -> assertNotEmpty ( $document [ 'body' ][ '$id' ]);
$documentId = $document [ 'body' ][ '$id' ];
$result = $this -> performMigrationSync ([
'resources' => [
Resource :: TYPE_DATABASE ,
Resource :: TYPE_COLLECTION ,
2024-10-31 05:48:03 +00:00
Resource :: TYPE_ATTRIBUTE ,
2024-10-31 05:35:54 +00:00
Resource :: TYPE_DOCUMENT ,
],
'endpoint' => 'http://localhost/v1' ,
'projectId' => $this -> getProject ()[ '$id' ],
'apiKey' => $this -> getProject ()[ 'apiKey' ],
]);
2025-02-11 07:56:39 +00:00
$finalStats = $this -> client -> call ( Client :: METHOD_GET , '/project/usage' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'startDate' => UsageTest :: getYesterday (),
'endDate' => UsageTest :: getTomorrow (),
]);
2024-10-31 05:35:54 +00:00
$this -> assertEquals ( 'completed' , $result [ 'status' ]);
2024-10-31 05:48:03 +00:00
$this -> assertEquals ([ Resource :: TYPE_DATABASE , Resource :: TYPE_COLLECTION , Resource :: TYPE_ATTRIBUTE , Resource :: TYPE_DOCUMENT ], $result [ 'resources' ]);
2024-10-31 06:04:44 +00:00
//TODO: Add TYPE_DOCUMENT to the migration status counters once pending issue is resolved
foreach ([ Resource :: TYPE_DATABASE , Resource :: TYPE_COLLECTION , Resource :: TYPE_ATTRIBUTE ] as $resource ) {
2024-10-31 05:35:54 +00:00
$this -> assertArrayHasKey ( $resource , $result [ 'statusCounters' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ $resource ][ 'error' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ $resource ][ 'pending' ]);
$this -> assertEquals ( 1 , $result [ 'statusCounters' ][ $resource ][ 'success' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ $resource ][ 'processing' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ $resource ][ 'warning' ]);
}
$response = $this -> client -> call ( Client :: METHOD_GET , '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId , [
'content-type' => 'application/json' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-31 05:35:54 +00:00
]);
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $response [ 'body' ]);
$this -> assertEquals ( $documentId , $response [ 'body' ][ '$id' ]);
2024-10-31 06:04:44 +00:00
$this -> assertEquals ( 'Test Document' , $response [ 'body' ][ 'name' ]);
2024-10-31 05:35:54 +00:00
// Cleanup
$this -> client -> call ( Client :: METHOD_DELETE , '/databases/' . $databaseId , [
'content-type' => 'application/json' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-31 05:35:54 +00:00
]);
}
2024-10-30 05:23:24 +00:00
2024-10-30 05:27:25 +00:00
/**
* Storage
*/
2025-02-11 07:56:39 +00:00
public function testAppwriteMigrationStorageBucket () : void
2024-10-31 05:35:54 +00:00
{
$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' ,
'permissions' => [
Permission :: read ( Role :: any ()),
Permission :: create ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
'maximumFileSize' => 1000000 ,
'allowedFileExtensions' => [ 'pdf' ],
'compression' => 'gzip' ,
'encryption' => false ,
'antivirus' => false
]);
$this -> assertEquals ( 201 , $bucket [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $bucket [ 'body' ]);
$this -> assertNotEmpty ( $bucket [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'Test Bucket' , $bucket [ 'body' ][ 'name' ]);
$result = $this -> performMigrationSync ([
'resources' => [
Resource :: TYPE_BUCKET
],
'endpoint' => 'http://localhost/v1' ,
'projectId' => $this -> getProject ()[ '$id' ],
'apiKey' => $this -> getProject ()[ 'apiKey' ],
]);
$this -> assertEquals ( 'completed' , $result [ 'status' ]);
$this -> assertEquals ([ Resource :: TYPE_BUCKET ], $result [ 'resources' ]);
$this -> assertArrayHasKey ( Resource :: TYPE_BUCKET , $result [ 'statusCounters' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_BUCKET ][ 'error' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_BUCKET ][ 'pending' ]);
$this -> assertEquals ( 1 , $result [ 'statusCounters' ][ Resource :: TYPE_BUCKET ][ 'success' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_BUCKET ][ 'processing' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_BUCKET ][ 'warning' ]);
$response = $this -> client -> call ( Client :: METHOD_GET , '/storage/buckets/' . $bucket [ 'body' ][ '$id' ], [
'content-type' => 'application/json' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-31 05:35:54 +00:00
]);
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $response [ 'body' ]);
$this -> assertNotEmpty ( $response [ 'body' ][ '$id' ]);
$this -> assertEquals ( $bucket [ 'body' ][ '$id' ], $response [ 'body' ][ '$id' ]);
$this -> assertEquals ( $bucket [ 'body' ][ 'name' ], $response [ 'body' ][ 'name' ]);
$this -> assertEquals ( $bucket [ 'body' ][ '$permissions' ], $response [ 'body' ][ '$permissions' ]);
$this -> assertEquals ( $bucket [ 'body' ][ 'maximumFileSize' ], $response [ 'body' ][ 'maximumFileSize' ]);
$this -> assertEquals ( $bucket [ 'body' ][ 'allowedFileExtensions' ], $response [ 'body' ][ 'allowedFileExtensions' ]);
$this -> assertEquals ( $bucket [ 'body' ][ 'compression' ], $response [ 'body' ][ 'compression' ]);
$this -> assertEquals ( $bucket [ 'body' ][ 'encryption' ], $response [ 'body' ][ 'encryption' ]);
$this -> assertEquals ( $bucket [ 'body' ][ 'antivirus' ], $response [ 'body' ][ 'antivirus' ]);
// Cleanup
$this -> client -> call ( Client :: METHOD_DELETE , '/storage/buckets/' . $bucket [ 'body' ][ '$id' ], [
'content-type' => 'application/json' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-31 05:35:54 +00:00
]);
$this -> client -> call ( Client :: METHOD_DELETE , '/storage/buckets/' . $bucket [ 'body' ][ '$id' ], [
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
]);
}
2025-02-11 07:56:39 +00:00
public function testAppwriteMigrationStorageFiles () : void
2024-10-31 05:35:54 +00:00
{
$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 ()),
],
]);
$this -> assertEquals ( 201 , $bucket [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $bucket [ 'body' ][ '$id' ]);
$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' ),
'permissions' => [
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
]);
$this -> assertEquals ( 201 , $file [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $file [ 'body' ][ '$id' ]);
$fileId = $file [ 'body' ][ '$id' ];
$result = $this -> performMigrationSync ([
2024-10-31 06:04:44 +00:00
'resources' => [
Resource :: TYPE_BUCKET ,
Resource :: TYPE_FILE
],
'endpoint' => 'http://localhost/v1' ,
'projectId' => $this -> getProject ()[ '$id' ],
'apiKey' => $this -> getProject ()[ 'apiKey' ],
]);
2024-10-31 05:35:54 +00:00
$this -> assertEquals ( 'completed' , $result [ 'status' ]);
$this -> assertEquals ([ Resource :: TYPE_BUCKET , Resource :: TYPE_FILE ], $result [ 'resources' ]);
$this -> assertArrayHasKey ( Resource :: TYPE_BUCKET , $result [ 'statusCounters' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_BUCKET ][ 'error' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_BUCKET ][ 'pending' ]);
$this -> assertEquals ( 1 , $result [ 'statusCounters' ][ Resource :: TYPE_BUCKET ][ 'success' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_BUCKET ][ 'processing' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_BUCKET ][ 'warning' ]);
$this -> assertArrayHasKey ( Resource :: TYPE_FILE , $result [ 'statusCounters' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_FILE ][ 'error' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_FILE ][ 'pending' ]);
$this -> assertEquals ( 1 , $result [ 'statusCounters' ][ Resource :: TYPE_FILE ][ 'success' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_FILE ][ 'processing' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_FILE ][ 'warning' ]);
$response = $this -> client -> call ( Client :: METHOD_GET , '/storage/buckets/' . $bucketId . '/files/' . $fileId , [
'content-type' => 'application/json' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-31 05:35:54 +00:00
]);
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $response [ 'body' ]);
$this -> assertNotEmpty ( $response [ 'body' ][ '$id' ]);
$this -> assertEquals ( $fileId , $response [ 'body' ][ '$id' ]);
// Cleanup
$this -> client -> call ( Client :: METHOD_DELETE , '/storage/buckets/' . $bucketId . '/files/' . $fileId , [
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
]);
$this -> client -> call ( Client :: METHOD_DELETE , '/storage/buckets/' . $bucketId . '/files/' . $fileId , [
'content-type' => 'application/json' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-31 05:35:54 +00:00
]);
}
2024-10-30 05:23:24 +00:00
/**
* Functions
*/
2025-02-11 07:56:39 +00:00
public function testAppwriteMigrationFunction () : void
2024-10-31 06:04:44 +00:00
{
$functionId = $this -> setupFunction ([
'functionId' => ID :: unique (),
'name' => 'Test' ,
'runtime' => 'php-8.0' ,
'entrypoint' => 'index.php'
]);
$deploymentId = $this -> setupDeployment ( $functionId , [
'entrypoint' => 'index.php' ,
'code' => $this -> packageFunction ( 'php' ),
'activate' => true
]);
2024-10-31 06:13:50 +00:00
$result = $this -> performMigrationSync ([
2024-10-31 06:04:44 +00:00
'resources' => [
Resource :: TYPE_FUNCTION ,
Resource :: TYPE_DEPLOYMENT
],
'endpoint' => 'http://localhost/v1' ,
'projectId' => $this -> getProject ()[ '$id' ],
'apiKey' => $this -> getProject ()[ 'apiKey' ],
]);
$this -> assertEquals ( 'completed' , $result [ 'status' ]);
$this -> assertEquals ([ Resource :: TYPE_FUNCTION , Resource :: TYPE_DEPLOYMENT ], $result [ 'resources' ]);
$this -> assertArrayHasKey ( Resource :: TYPE_FUNCTION , $result [ 'statusCounters' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_FUNCTION ][ 'error' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_FUNCTION ][ 'pending' ]);
$this -> assertEquals ( 1 , $result [ 'statusCounters' ][ Resource :: TYPE_FUNCTION ][ 'success' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_FUNCTION ][ 'processing' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_FUNCTION ][ 'warning' ]);
$this -> assertArrayHasKey ( Resource :: TYPE_DEPLOYMENT , $result [ 'statusCounters' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_DEPLOYMENT ][ 'error' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_DEPLOYMENT ][ 'pending' ]);
$this -> assertEquals ( 1 , $result [ 'statusCounters' ][ Resource :: TYPE_DEPLOYMENT ][ 'success' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_DEPLOYMENT ][ 'processing' ]);
$this -> assertEquals ( 0 , $result [ 'statusCounters' ][ Resource :: TYPE_DEPLOYMENT ][ 'warning' ]);
$response = $this -> client -> call ( Client :: METHOD_GET , '/functions/' . $functionId , [
'content-type' => 'application/json' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-31 06:04:44 +00:00
]);
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $response [ 'body' ]);
$this -> assertNotEmpty ( $response [ 'body' ][ '$id' ]);
$this -> assertEquals ( $functionId , $response [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'Test' , $response [ 'body' ][ 'name' ]);
$this -> assertEquals ( 'php-8.0' , $response [ 'body' ][ 'runtime' ]);
$this -> assertEquals ( 'index.php' , $response [ 'body' ][ 'entrypoint' ]);
$this -> assertEventually ( function () use ( $functionId ) {
$deployments = $this -> client -> call ( Client :: METHOD_GET , '/functions/' . $functionId . '/deployments/' , array_merge ([
'content-type' => 'application/json' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-31 06:04:44 +00:00
]));
$this -> assertEquals ( 200 , $deployments [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $deployments [ 'body' ]);
$this -> assertEquals ( 1 , $deployments [ 'body' ][ 'total' ]);
$this -> assertEquals ( 'ready' , $deployments [ 'body' ][ 'deployments' ][ 0 ][ 'status' ], 'Deployment status is not ready, deployment: ' . json_encode ( $deployments [ 'body' ][ 'deployments' ][ 0 ], JSON_PRETTY_PRINT ));
}, 50000 , 500 );
// Attempt execution
$execution = $this -> client -> call ( Client :: METHOD_POST , '/functions/' . $functionId . '/executions' , [
'content-type' => 'application/json' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-31 06:04:44 +00:00
], [
'body' => 'test'
]);
$this -> assertEquals ( 201 , $execution [ 'headers' ][ 'status-code' ]);
$this -> assertStringContainsString ( 'body-is-test' , $execution [ 'body' ][ 'logs' ]);
// Cleanup
$this -> client -> call ( Client :: METHOD_DELETE , '/functions/' . $functionId , [
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
]);
$this -> client -> call ( Client :: METHOD_DELETE , '/functions/' . $functionId , [
'content-type' => 'application/json' ,
2025-02-11 07:56:39 +00:00
'x-appwrite-project' => $this -> getDestinationProject ()[ '$id' ],
'x-appwrite-key' => $this -> getDestinationProject ()[ 'apiKey' ],
2024-10-31 06:04:44 +00:00
]);
}
2025-04-16 11:22:23 +00:00
/**
* Import documents from a CSV file .
*/
public function testCreateCsvMigration () : array
{
// make a database
$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 -> assertNotEmpty ( $response [ 'body' ][ '$id' ]);
$this -> assertEquals ( 201 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'Test Database' , $response [ 'body' ][ 'name' ]);
$databaseId = $response [ 'body' ][ '$id' ];
// make a collection
$response = $this -> client -> call ( Client :: METHOD_POST , '/databases/' . $databaseId . '/collections' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'name' => 'Test collection' ,
2025-05-06 05:59:56 +00:00
'tableId' => ID :: unique (),
2025-04-16 11:22:23 +00:00
]);
$this -> assertEquals ( 201 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( $response [ 'body' ][ 'name' ], 'Test collection' );
$collectionId = $response [ 'body' ][ '$id' ];
// make attributes
$response = $this -> client -> call ( Client :: METHOD_POST , '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'key' => 'name' ,
'size' => 256 ,
'required' => true ,
]);
$this -> assertEquals ( 202 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( $response [ 'body' ][ 'key' ], 'name' );
$this -> assertEquals ( $response [ 'body' ][ 'type' ], 'string' );
$this -> assertEquals ( $response [ 'body' ][ 'size' ], 256 );
$this -> assertEquals ( $response [ 'body' ][ 'required' ], true );
$response = $this -> client -> call ( Client :: METHOD_POST , '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'key' => 'age' ,
'min' => 18 ,
'max' => 65 ,
'required' => true ,
]);
$this -> assertEquals ( 202 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( $response [ 'body' ][ 'key' ], 'age' );
$this -> assertEquals ( $response [ 'body' ][ 'type' ], 'integer' );
$this -> assertEquals ( $response [ 'body' ][ 'min' ], 18 );
$this -> assertEquals ( $response [ 'body' ][ 'max' ], 65 );
$this -> assertEquals ( $response [ 'body' ][ 'required' ], true );
// make a bucket, upload a file to it!
$bucketOne = $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' ,
'maximumFileSize' => 2000000 , //2MB
'allowedFileExtensions' => [ 'csv' ],
'compression' => 'gzip' ,
'encryption' => true
]);
$this -> assertEquals ( 201 , $bucketOne [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $bucketOne [ 'body' ][ '$id' ]);
$bucketOneId = $bucketOne [ 'body' ][ '$id' ];
$bucketIds = [
2025-04-21 06:42:01 +00:00
'default' => $bucketOneId ,
'missing-row' => $bucketOneId ,
'missing-column' => $bucketOneId ,
'irrelevant-column' => $bucketOneId ,
2025-04-16 11:22:23 +00:00
];
$fileIds = [];
foreach ( $bucketIds as $label => $bucketId ) {
2025-04-17 05:06:05 +00:00
$csvFileName = match ( $label ) {
'missing-row' ,
'missing-column' ,
'irrelevant-column' => " { $label } .csv " ,
default => 'documents.csv' ,
};
$mimeType = match ( $csvFileName ) {
default => 'text/csv' ,
'missing-row.csv' => 'text/plain' , // invalid csv structure, falls back to plain text!
};
2025-04-16 11:22:23 +00:00
$response = $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 (),
2025-04-17 05:06:05 +00:00
'file' => new CURLFile ( realpath ( __DIR__ . '/../../../resources/csv/' . $csvFileName ), $mimeType , $csvFileName ),
2025-04-16 11:22:23 +00:00
]);
$this -> assertEquals ( 201 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $response [ 'body' ][ '$id' ]);
2025-04-17 05:06:05 +00:00
$this -> assertEquals ( $csvFileName , $response [ 'body' ][ 'name' ]);
$this -> assertEquals ( $mimeType , $response [ 'body' ][ 'mimeType' ]);
2025-04-16 11:22:23 +00:00
$fileIds [ $label ] = $response [ 'body' ][ '$id' ];
}
2025-04-17 05:06:05 +00:00
// missing attribute, fail in worker.
$missingColumn = $this -> performCsvMigration (
[
'fileId' => $fileIds [ 'missing-column' ],
'bucketId' => $bucketIds [ 'missing-column' ],
'resourceId' => $databaseId . ':' . $collectionId ,
]
);
$this -> assertEventually ( function () use ( $missingColumn , $databaseId , $collectionId ) {
$migrationId = $missingColumn [ 'body' ][ '$id' ];
$migration = $this -> client -> call ( Client :: METHOD_GET , '/migrations/' . $migrationId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()));
$this -> assertEquals ( 200 , $migration [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'finished' , $migration [ 'body' ][ 'stage' ]);
$this -> assertEquals ( 'failed' , $migration [ 'body' ][ 'status' ]);
$this -> assertEquals ( 'CSV' , $migration [ 'body' ][ 'source' ]);
$this -> assertEquals ( 'Appwrite' , $migration [ 'body' ][ 'destination' ]);
$this -> assertContains ( Resource :: TYPE_DOCUMENT , $migration [ 'body' ][ 'resources' ]);
$this -> assertEmpty ( $migration [ 'body' ][ 'statusCounters' ]);
$this -> assertThat (
implode ( " \n " , $migration [ 'body' ][ 'errors' ]),
$this -> stringContains ( " CSV header mismatch. Missing attribute: 'age' " )
);
}, 60000 , 500 );
// missing row data, fail in worker.
$missingColumn = $this -> performCsvMigration (
[
'fileId' => $fileIds [ 'missing-row' ],
'bucketId' => $bucketIds [ 'missing-row' ],
'resourceId' => $databaseId . ':' . $collectionId ,
]
);
$this -> assertEventually ( function () use ( $missingColumn , $databaseId , $collectionId ) {
$migrationId = $missingColumn [ 'body' ][ '$id' ];
$migration = $this -> client -> call ( Client :: METHOD_GET , '/migrations/' . $migrationId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()));
$this -> assertEquals ( 200 , $migration [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'finished' , $migration [ 'body' ][ 'stage' ]);
$this -> assertEquals ( 'failed' , $migration [ 'body' ][ 'status' ]);
$this -> assertEquals ( 'CSV' , $migration [ 'body' ][ 'source' ]);
$this -> assertEquals ( 'Appwrite' , $migration [ 'body' ][ 'destination' ]);
$this -> assertContains ( Resource :: TYPE_DOCUMENT , $migration [ 'body' ][ 'resources' ]);
$this -> assertEmpty ( $migration [ 'body' ][ 'statusCounters' ]);
$this -> assertThat (
implode ( " \n " , $migration [ 'body' ][ 'errors' ]),
$this -> stringContains ( 'CSV row does not match the number of header columns' )
);
}, 60000 , 500 );
// irrelevant column - email, fail in worker.
$irrelevantColumn = $this -> performCsvMigration (
[
'fileId' => $fileIds [ 'irrelevant-column' ],
'bucketId' => $bucketIds [ 'irrelevant-column' ],
'resourceId' => $databaseId . ':' . $collectionId ,
]
);
$this -> assertEventually ( function () use ( $irrelevantColumn , $databaseId , $collectionId ) {
$migrationId = $irrelevantColumn [ 'body' ][ '$id' ];
$migration = $this -> client -> call ( Client :: METHOD_GET , '/migrations/' . $migrationId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()));
$this -> assertEquals ( 200 , $migration [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'finished' , $migration [ 'body' ][ 'stage' ]);
$this -> assertEquals ( 'failed' , $migration [ 'body' ][ 'status' ]);
$this -> assertEquals ( 'CSV' , $migration [ 'body' ][ 'source' ]);
$this -> assertEquals ( 'Appwrite' , $migration [ 'body' ][ 'destination' ]);
$this -> assertContains ( Resource :: TYPE_DOCUMENT , $migration [ 'body' ][ 'resources' ]);
$this -> assertEmpty ( $migration [ 'body' ][ 'statusCounters' ]);
$this -> assertThat (
implode ( " \n " , $migration [ 'body' ][ 'errors' ]),
$this -> stringContains ( " CSV header mismatch. Unexpected attribute: 'email' " )
);
}, 60000 , 500 );
2025-04-21 06:42:01 +00:00
// all data exists, pass/
2025-04-16 11:22:23 +00:00
$migration = $this -> performCsvMigration (
[
'endpoint' => 'http://localhost/v1' ,
2025-04-21 06:42:01 +00:00
'fileId' => $fileIds [ 'default' ],
'bucketId' => $bucketIds [ 'default' ],
2025-04-16 11:22:23 +00:00
'resourceId' => $databaseId . ':' . $collectionId ,
]
);
$this -> assertEmpty ( $migration [ 'body' ][ 'statusCounters' ]);
$this -> assertEquals ( 'CSV' , $migration [ 'body' ][ 'source' ]);
$this -> assertEquals ( 'pending' , $migration [ 'body' ][ 'status' ]);
$this -> assertEquals ( 'Appwrite' , $migration [ 'body' ][ 'destination' ]);
$this -> assertContains ( Resource :: TYPE_DOCUMENT , $migration [ 'body' ][ 'resources' ]);
return [
'databaseId' => $databaseId ,
2025-05-06 05:59:56 +00:00
'tableId' => $collectionId ,
2025-04-16 11:22:23 +00:00
'migrationId' => $migration [ 'body' ][ '$id' ],
];
}
/**
* @ depends testCreateCsvMigration
*/
public function testImportSuccessful ( array $response ) : void
{
$databaseId = $response [ 'databaseId' ];
2025-05-06 05:59:56 +00:00
$collectionId = $response [ 'tableId' ];
2025-04-16 11:22:23 +00:00
$migrationId = $response [ 'migrationId' ];
$documentsCountInCSV = 100 ;
// get migration stats
$this -> assertEventually ( function () use ( $migrationId , $databaseId , $collectionId , $documentsCountInCSV ) {
$migration = $this -> client -> call ( Client :: METHOD_GET , '/migrations/' . $migrationId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()));
$this -> assertEquals ( 200 , $migration [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'finished' , $migration [ 'body' ][ 'stage' ]);
$this -> assertEquals ( 'completed' , $migration [ 'body' ][ 'status' ]);
$this -> assertEquals ( 'CSV' , $migration [ 'body' ][ 'source' ]);
$this -> assertEquals ( 'Appwrite' , $migration [ 'body' ][ 'destination' ]);
$this -> assertContains ( Resource :: TYPE_DOCUMENT , $migration [ 'body' ][ 'resources' ]);
$this -> assertArrayHasKey ( Resource :: TYPE_DOCUMENT , $migration [ 'body' ][ 'statusCounters' ]);
$this -> assertEquals ( $documentsCountInCSV , $migration [ 'body' ][ 'statusCounters' ][ Resource :: TYPE_DOCUMENT ][ 'success' ]);
}, 60000 , 500 );
// get documents count
$documents = $this -> client -> call ( Client :: METHOD_GET , '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'queries' => [
// there should be only 100!
Query :: limit ( 150 ) -> toString ()
]
]);
$this -> assertEquals ( 200 , $documents [ 'headers' ][ 'status-code' ]);
2025-05-05 11:33:17 +00:00
$this -> assertIsArray ( $documents [ 'body' ][ 'rows' ]);
2025-04-16 11:22:23 +00:00
$this -> assertIsNumeric ( $documents [ 'body' ][ 'total' ]);
$this -> assertEquals ( $documentsCountInCSV , $documents [ 'body' ][ 'total' ]);
}
private function performCsvMigration ( array $body ) : array
{
return $this -> client -> call ( Client :: METHOD_POST , '/migrations/csv' , [
'content-type' => 'application/json' ,
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $body );
}
2024-10-30 05:27:25 +00:00
}