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 ;
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 (),
2026-02-04 07:53:19 +00:00
'endpoint' => $this -> webEndpoint ,
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 ,
],
2026-02-04 07:53:19 +00:00
'endpoint' => $this -> webEndpoint ,
2024-10-31 06:04:44 +00:00
'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 ,
],
2026-02-04 07:53:19 +00:00
'endpoint' => $this -> webEndpoint ,
2024-10-31 05:35:54 +00:00
'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 ,
],
2026-02-04 07:53:19 +00:00
'endpoint' => $this -> webEndpoint ,
2024-10-31 06:04:44 +00:00
'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 ,
],
2026-02-04 07:53:19 +00:00
'endpoint' => $this -> webEndpoint ,
2024-10-31 05:35:54 +00:00
'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-07-18 05:25:48 +00:00
public function testAppwriteMigrationDatabasesTable ( array $data ) : array
2024-10-31 05:35:54 +00:00
{
$databaseId = $data [ 'databaseId' ];
2025-08-18 09:16:01 +00:00
$table = $this -> client -> call ( Client :: METHOD_POST , '/tablesdb/' . $databaseId . '/tables' , [
2024-10-31 05:35:54 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
], [
2025-07-18 05:25:48 +00:00
'tableId' => ID :: unique (),
'name' => 'Test Table' ,
2024-10-31 05:35:54 +00:00
]);
2025-07-18 05:25:48 +00:00
$this -> assertEquals ( 201 , $table [ 'headers' ][ 'status-code' ]);
2024-10-31 05:35:54 +00:00
2025-07-18 05:25:48 +00:00
$tableId = $table [ 'body' ][ '$id' ];
2024-10-31 05:35:54 +00:00
2025-07-18 05:25:48 +00:00
// Create Column
2025-08-18 09:16:01 +00:00
$response = $this -> client -> call ( Client :: METHOD_POST , '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string' , [
2024-10-31 05:35:54 +00:00
'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' ]);
2025-07-18 05:25:48 +00:00
// Wait for column to be ready
$this -> assertEventually ( function () use ( $databaseId , $tableId ) {
2025-08-18 09:16:01 +00:00
$response = $this -> client -> call ( Client :: METHOD_GET , '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/name' , [
2024-10-31 05:35:54 +00:00
'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 ,
2025-07-18 05:25:48 +00:00
Resource :: TYPE_TABLE ,
Resource :: TYPE_COLUMN ,
2024-10-31 05:35:54 +00:00
],
2026-02-04 07:53:19 +00:00
'endpoint' => $this -> webEndpoint ,
2024-10-31 05:35:54 +00:00
'projectId' => $this -> getProject ()[ '$id' ],
'apiKey' => $this -> getProject ()[ 'apiKey' ],
]);
$this -> assertEquals ( 'completed' , $result [ 'status' ]);
2025-07-18 05:25:48 +00:00
$this -> assertEquals ([ Resource :: TYPE_DATABASE , Resource :: TYPE_TABLE , Resource :: TYPE_COLUMN ], $result [ 'resources' ]);
2024-10-31 05:35:54 +00:00
2025-07-18 05:25:48 +00:00
foreach ([ Resource :: TYPE_DATABASE , Resource :: TYPE_TABLE , Resource :: TYPE_COLUMN ] 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' ]);
}
2025-08-18 09:16:01 +00:00
$response = $this -> client -> call ( Client :: METHOD_GET , '/tablesdb/' . $databaseId . '/tables/' . $tableId , [
2024-10-31 05:35:54 +00:00
'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' ]);
2025-07-18 05:25:48 +00:00
$this -> assertEquals ( $tableId , $response [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'Test Table' , $response [ 'body' ][ 'name' ]);
2024-10-31 05:35:54 +00:00
2025-08-18 09:16:01 +00:00
$response = $this -> client -> call ( Client :: METHOD_GET , '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/name' , [
2024-10-31 05:35:54 +00:00
'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-07-18 05:25:48 +00:00
'tableId' => $tableId ,
2024-10-31 05:35:54 +00:00
];
}
/**
2025-07-18 05:25:48 +00:00
* @ depends testAppwriteMigrationDatabasesTable
2024-10-31 05:35:54 +00:00
*/
2025-07-18 05:25:48 +00:00
public function testAppwriteMigrationDatabasesRow ( array $data ) : void
2024-10-31 05:35:54 +00:00
{
2025-07-18 05:25:48 +00:00
$table = $data [ 'tableId' ];
2024-10-31 05:35:54 +00:00
$databaseId = $data [ 'databaseId' ];
2025-08-18 09:16:01 +00:00
$row = $this -> client -> call ( Client :: METHOD_POST , '/tablesdb/' . $databaseId . '/tables/' . $table . '/rows' , [
2024-10-31 05:35:54 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
], [
2025-07-18 05:25:48 +00:00
'rowId' => ID :: unique (),
2024-10-31 05:35:54 +00:00
'data' => [
2025-07-18 05:25:48 +00:00
'name' => 'Test Row' ,
2024-10-31 05:35:54 +00:00
]
]);
2025-07-18 05:25:48 +00:00
$this -> assertEquals ( 201 , $row [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $row [ 'body' ]);
$this -> assertNotEmpty ( $row [ 'body' ][ '$id' ]);
2024-10-31 05:35:54 +00:00
2025-07-18 05:25:48 +00:00
$rowId = $row [ 'body' ][ '$id' ];
2024-10-31 05:35:54 +00:00
$result = $this -> performMigrationSync ([
'resources' => [
Resource :: TYPE_DATABASE ,
2025-07-18 05:25:48 +00:00
Resource :: TYPE_TABLE ,
Resource :: TYPE_COLUMN ,
Resource :: TYPE_ROW ,
2024-10-31 05:35:54 +00:00
],
2026-02-04 07:53:19 +00:00
'endpoint' => $this -> webEndpoint ,
2024-10-31 05:35:54 +00:00
'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' ]);
2025-07-18 05:25:48 +00:00
$this -> assertEquals ([ Resource :: TYPE_DATABASE , Resource :: TYPE_TABLE , Resource :: TYPE_COLUMN , Resource :: TYPE_ROW ], $result [ 'resources' ]);
2024-10-31 06:04:44 +00:00
2025-07-18 05:25:48 +00:00
// TODO: Add TYPE_ROW to the migration status counters once pending issue is resolved
foreach ([ Resource :: TYPE_DATABASE , Resource :: TYPE_TABLE , Resource :: TYPE_COLUMN ] 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' ]);
}
2025-08-18 09:16:01 +00:00
$response = $this -> client -> call ( Client :: METHOD_GET , '/tablesdb/' . $databaseId . '/tables/' . $table . '/rows/' . $rowId , [
2024-10-31 05:35:54 +00:00
'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' ]);
2025-07-18 05:25:48 +00:00
$this -> assertEquals ( $rowId , $response [ 'body' ][ '$id' ]);
$this -> assertEquals ( 'Test Row' , $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
],
2026-02-04 07:53:19 +00:00
'endpoint' => $this -> webEndpoint ,
2024-10-31 05:35:54 +00:00
'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
],
2026-02-04 07:53:19 +00:00
'endpoint' => $this -> webEndpoint ,
2024-10-31 06:04:44 +00:00
'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' ,
2025-07-09 14:58:55 +00:00
'runtime' => 'node-22' ,
'entrypoint' => 'index.js'
2024-10-31 06:04:44 +00:00
]);
$deploymentId = $this -> setupDeployment ( $functionId , [
2025-07-09 14:58:55 +00:00
'code' => $this -> packageFunction ( 'basic' ),
2024-10-31 06:04:44 +00:00
'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
],
2026-02-04 07:53:19 +00:00
'endpoint' => $this -> webEndpoint ,
2024-10-31 06:04:44 +00:00
'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' ]);
2025-07-10 12:43:56 +00:00
$this -> assertEquals ( 'node-22' , $response [ 'body' ][ 'runtime' ]);
$this -> assertEquals ( 'index.js' , $response [ 'body' ][ 'entrypoint' ]);
2024-10-31 06:04:44 +00:00
$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 ));
2025-06-02 16:15:01 +00:00
}, 100000 , 500 );
2024-10-31 06:04:44 +00:00
// 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 .
*/
2025-09-24 10:38:35 +00:00
public function testCreateCSVImport () : void
2025-04-16 11:22:23 +00:00
{
2025-07-30 04:46:14 +00:00
// Make a database
2025-04-16 11:22:23 +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 -> assertNotEmpty ( $response [ 'body' ][ '$id' ]);
$this -> assertEquals ( 201 , $response [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'Test Database' , $response [ 'body' ][ 'name' ]);
$databaseId = $response [ 'body' ][ '$id' ];
2025-07-18 05:25:48 +00:00
// make a table
2025-08-18 09:16:01 +00:00
$response = $this -> client -> call ( Client :: METHOD_POST , '/tablesdb/' . $databaseId . '/tables' , array_merge ([
2025-04-16 11:22:23 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
2025-07-18 05:25:48 +00:00
'name' => 'Test table' ,
'tableId' => ID :: unique (),
2025-04-16 11:22:23 +00:00
]);
$this -> assertEquals ( 201 , $response [ 'headers' ][ 'status-code' ]);
2025-07-18 05:25:48 +00:00
$this -> assertEquals ( $response [ 'body' ][ 'name' ], 'Test table' );
2025-04-16 11:22:23 +00:00
2025-07-18 05:25:48 +00:00
$tableId = $response [ 'body' ][ '$id' ];
2025-04-16 11:22:23 +00:00
2025-07-18 05:25:48 +00:00
// make columns
2025-08-18 09:16:01 +00:00
$response = $this -> client -> call ( Client :: METHOD_POST , '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string' , array_merge ([
2025-04-16 11:22:23 +00:00
'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 );
2025-08-18 09:16:01 +00:00
$response = $this -> client -> call ( Client :: METHOD_POST , '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer' , array_merge ([
2025-04-16 11:22:23 +00:00
'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-07-30 04:50:09 +00:00
'documents-internals' => $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' ,
2025-07-30 04:50:09 +00:00
'irrelevant-column' ,
'documents-internals' => " { $label } .csv " ,
2025-04-17 05:06:05 +00:00
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-07-18 05:25:48 +00:00
// missing column, fail in worker.
2025-04-17 05:06:05 +00:00
$missingColumn = $this -> performCsvMigration (
[
'fileId' => $fileIds [ 'missing-column' ],
'bucketId' => $bucketIds [ 'missing-column' ],
2025-07-18 05:25:48 +00:00
'resourceId' => $databaseId . ':' . $tableId ,
2025-04-17 05:06:05 +00:00
]
);
2025-07-18 05:25:48 +00:00
$this -> assertEventually ( function () use ( $missingColumn , $databaseId , $tableId ) {
2025-04-17 05:06:05 +00:00
$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' ]);
2025-07-18 05:25:48 +00:00
$this -> assertContains ( Resource :: TYPE_ROW , $migration [ 'body' ][ 'resources' ]);
2025-04-17 05:06:05 +00:00
$this -> assertEmpty ( $migration [ 'body' ][ 'statusCounters' ]);
2025-07-18 05:25:48 +00:00
$errorJson = $migration [ 'body' ][ 'errors' ][ 0 ];
$errorData = json_decode ( $errorJson , true );
2025-04-17 05:06:05 +00:00
$this -> assertThat (
2025-08-09 06:23:25 +00:00
implode ( " \n " , $migration [ 'body' ][ 'errors' ]),
$this -> stringContains ( " CSV header validation failed: Missing required column: 'age' " )
2025-04-17 05:06:05 +00:00
);
2025-08-09 06:23:25 +00:00
}, 60_000 , 500 );
2025-04-17 05:06:05 +00:00
// missing row data, fail in worker.
$missingColumn = $this -> performCsvMigration (
[
'fileId' => $fileIds [ 'missing-row' ],
'bucketId' => $bucketIds [ 'missing-row' ],
2025-07-18 05:25:48 +00:00
'resourceId' => $databaseId . ':' . $tableId ,
2025-04-17 05:06:05 +00:00
]
);
2025-07-18 05:25:48 +00:00
$this -> assertEventually ( function () use ( $missingColumn , $databaseId , $tableId ) {
2025-04-17 05:06:05 +00:00
$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' ]);
2025-07-18 05:25:48 +00:00
$this -> assertContains ( Resource :: TYPE_ROW , $migration [ 'body' ][ 'resources' ]);
2025-04-17 05:06:05 +00:00
$this -> assertEmpty ( $migration [ 'body' ][ 'statusCounters' ]);
2025-07-18 05:25:48 +00:00
$errorJson = $migration [ 'body' ][ 'errors' ][ 0 ];
$errorData = json_decode ( $errorJson , true );
2025-04-17 05:06:05 +00:00
$this -> assertThat (
2025-08-09 06:23:25 +00:00
implode ( " \n " , $migration [ 'body' ][ 'errors' ]),
2025-04-17 05:06:05 +00:00
$this -> stringContains ( 'CSV row does not match the number of header columns' )
);
2025-08-09 06:23:25 +00:00
}, 60_000 , 500 );
2025-04-17 05:06:05 +00:00
2025-08-09 06:23:25 +00:00
// irrelevant column - email, success.
2025-04-17 05:06:05 +00:00
$irrelevantColumn = $this -> performCsvMigration (
[
'fileId' => $fileIds [ 'irrelevant-column' ],
'bucketId' => $bucketIds [ 'irrelevant-column' ],
2025-07-18 05:25:48 +00:00
'resourceId' => $databaseId . ':' . $tableId ,
2025-04-17 05:06:05 +00:00
]
);
2025-07-18 05:25:48 +00:00
$this -> assertEventually ( function () use ( $irrelevantColumn , $databaseId , $tableId ) {
2025-04-17 05:06:05 +00:00
$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' ]);
2025-08-09 06:23:25 +00:00
$this -> assertEquals ( 'completed' , $migration [ 'body' ][ 'status' ]);
2025-04-17 05:06:05 +00:00
$this -> assertEquals ( 'CSV' , $migration [ 'body' ][ 'source' ]);
$this -> assertEquals ( 'Appwrite' , $migration [ 'body' ][ 'destination' ]);
2025-07-18 05:25:48 +00:00
$this -> assertContains ( Resource :: TYPE_ROW , $migration [ 'body' ][ 'resources' ]);
2025-08-09 06:23:25 +00:00
$this -> assertArrayHasKey ( Resource :: TYPE_ROW , $migration [ 'body' ][ 'statusCounters' ]);
$this -> assertEquals ( 100 , $migration [ 'body' ][ 'statusCounters' ][ Resource :: TYPE_ROW ][ 'success' ]);
}, 10_000 , 500 );
2025-04-17 05:06:05 +00:00
2025-07-30 04:46:14 +00:00
// all data exists, pass.
2025-04-16 11:22:23 +00:00
$migration = $this -> performCsvMigration (
[
2026-02-04 07:53:19 +00:00
'endpoint' => $this -> webEndpoint ,
2025-04-21 06:42:01 +00:00
'fileId' => $fileIds [ 'default' ],
'bucketId' => $bucketIds [ 'default' ],
2025-07-18 05:25:48 +00:00
'resourceId' => $databaseId . ':' . $tableId ,
2025-04-16 11:22:23 +00:00
]
);
2025-08-06 05:36:28 +00:00
$this -> assertEventually ( function () use ( $migration , $databaseId , $tableId ) {
2025-07-30 04:46:14 +00:00
$migrationId = $migration [ 'body' ][ '$id' ];
2025-04-16 11:22:23 +00:00
$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' ]);
2025-07-18 05:25:48 +00:00
$this -> assertContains ( Resource :: TYPE_ROW , $migration [ 'body' ][ 'resources' ]);
$this -> assertArrayHasKey ( Resource :: TYPE_ROW , $migration [ 'body' ][ 'statusCounters' ]);
2025-08-06 05:24:00 +00:00
$this -> assertEquals ( 100 , $migration [ 'body' ][ 'statusCounters' ][ Resource :: TYPE_ROW ][ 'success' ]);
2025-07-30 11:42:01 +00:00
}, 10_000 , 500 );
2025-04-16 11:22:23 +00:00
2025-07-18 05:25:48 +00:00
// get rows count
2025-08-18 13:24:51 +00:00
$rows = $this -> client -> call ( Client :: METHOD_GET , '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows' , array_merge ([
2025-04-16 11:22:23 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'queries' => [
Query :: limit ( 150 ) -> toString ()
]
]);
2025-08-06 05:36:28 +00:00
$this -> assertEquals ( 200 , $rows [ 'headers' ][ 'status-code' ]);
2025-08-06 06:05:00 +00:00
$this -> assertIsArray ( $rows [ 'body' ][ 'rows' ]);
2025-08-06 05:36:28 +00:00
$this -> assertIsNumeric ( $rows [ 'body' ][ 'total' ]);
2025-08-09 06:23:25 +00:00
$this -> assertEquals ( 200 , $rows [ 'body' ][ 'total' ]);
2025-07-30 12:12:59 +00:00
2025-07-30 04:50:09 +00:00
// all data exists and includes internals, pass.
$migration = $this -> performCsvMigration (
[
2026-02-04 07:53:19 +00:00
'endpoint' => $this -> webEndpoint ,
2025-07-30 04:50:09 +00:00
'fileId' => $fileIds [ 'documents-internals' ],
'bucketId' => $bucketIds [ 'documents-internals' ],
2025-08-06 05:36:28 +00:00
'resourceId' => $databaseId . ':' . $tableId ,
2025-07-30 04:50:09 +00:00
]
);
2025-08-06 05:36:28 +00:00
$this -> assertEventually ( function () use ( $migration , $databaseId , $tableId ) {
2025-07-30 04:50:09 +00:00
$migrationId = $migration [ '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 ( 'completed' , $migration [ 'body' ][ 'status' ]);
$this -> assertEquals ( 'CSV' , $migration [ 'body' ][ 'source' ]);
$this -> assertEquals ( 'Appwrite' , $migration [ 'body' ][ 'destination' ]);
2025-08-06 05:24:00 +00:00
$this -> assertContains ( Resource :: TYPE_ROW , $migration [ 'body' ][ 'resources' ]);
$this -> assertArrayHasKey ( Resource :: TYPE_ROW , $migration [ 'body' ][ 'statusCounters' ]);
$this -> assertEquals ( 25 , $migration [ 'body' ][ 'statusCounters' ][ Resource :: TYPE_ROW ][ 'success' ]);
2025-07-30 11:42:01 +00:00
}, 10_000 , 500 );
2025-04-16 11:22:23 +00:00
}
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 );
}
2025-09-24 10:38:35 +00:00
/**
* Test CSV export with email notification
*/
public function testCreateCSVExport () : void
{
// Create a database
$database = $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 Export Database'
]);
$this -> assertEquals ( 201 , $database [ 'headers' ][ 'status-code' ]);
$databaseId = $database [ 'body' ][ '$id' ];
// Create a collection
$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' ]
], [
'collectionId' => ID :: unique (),
'name' => 'Test Export Collection' ,
'permissions' => []
]);
$this -> assertEquals ( 201 , $collection [ 'headers' ][ 'status-code' ]);
$collectionId = $collection [ 'body' ][ '$id' ];
// Create a simple attribute like the basic test
$name = $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' => 255 ,
'required' => true ,
]);
$this -> assertEquals ( 202 , $name [ 'headers' ][ 'status-code' ]);
// Create a simple attribute like the basic test
$email = $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' => 'email' ,
'size' => 255 ,
'required' => false ,
]);
$this -> assertEquals ( 202 , $email [ 'headers' ][ 'status-code' ]);
\sleep ( 3 );
// Create sample documents
for ( $i = 1 ; $i <= 10 ; $i ++ ) {
$doc = $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' ]
], [
'documentId' => ID :: unique (),
'data' => [
'name' => 'Test User ' . $i ,
'email' => 'user' . $i . '@appwrite.io'
]
]);
$this -> assertEquals ( 201 , $doc [ 'headers' ][ 'status-code' ], 'Failed to create document ' . $i );
}
// Verify documents were created
$docs = $this -> client -> call ( Client :: METHOD_GET , '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents' , [
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]);
$this -> assertEquals ( 200 , $docs [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 10 , $docs [ 'body' ][ 'total' ], 'Expected 10 documents but got ' . $docs [ 'body' ][ 'total' ]);
2025-11-12 09:18:25 +00:00
// Perform CSV export with notification enabled (uses internal bucket)
2025-09-24 10:38:35 +00:00
$migration = $this -> client -> call ( Client :: METHOD_POST , '/migrations/csv/exports' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ]
], $this -> getHeaders ()), [
'resourceId' => $databaseId . ':' . $collectionId ,
'filename' => 'test-export' ,
'columns' => [],
'delimiter' => ',' ,
'enclosure' => '"' ,
'escape' => '\\' ,
'header' => true ,
'notify' => true
]);
$this -> assertEquals ( 202 , $migration [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $migration [ 'body' ][ '$id' ]);
$migrationId = $migration [ 'body' ][ '$id' ];
2025-11-12 09:18:25 +00:00
$this -> assertEventually ( function () use ( $migrationId ) {
2025-09-24 10:38:35 +00:00
$response = $this -> client -> call ( Client :: METHOD_GET , '/migrations/' . $migrationId , [
'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 ( 'finished' , $response [ 'body' ][ 'stage' ]);
$this -> assertEquals ( 'completed' , $response [ 'body' ][ 'status' ]);
$this -> assertEquals ( 'Appwrite' , $response [ 'body' ][ 'source' ]);
$this -> assertEquals ( 'CSV' , $response [ 'body' ][ 'destination' ]);
return true ;
2025-11-25 03:04:46 +00:00
}, 30_000 , 500 );
2025-09-24 10:38:35 +00:00
// Check that email was sent with download link
$lastEmail = $this -> getLastEmail ();
$this -> assertNotEmpty ( $lastEmail );
$this -> assertEquals ( 'Your CSV export is ready' , $lastEmail [ 'subject' ]);
$this -> assertStringContainsStringIgnoringCase ( 'Your data export has been completed successfully' , $lastEmail [ 'text' ]);
// Extract download URL from email HTML
\preg_match ( '/href="([^"]*\/storage\/buckets\/[^"]*\/push[^"]*)"/' , $lastEmail [ 'html' ], $matches );
$this -> assertNotEmpty ( $matches [ 1 ], 'Download URL not found in email' );
$downloadUrl = html_entity_decode ( $matches [ 1 ]);
// Parse the URL to extract components
$components = \parse_url ( $downloadUrl );
$this -> assertNotEmpty ( $components );
\parse_str ( $components [ 'query' ] ? ? '' , $queryParams );
$this -> assertArrayHasKey ( 'jwt' , $queryParams , 'JWT not found in download URL' );
$this -> assertNotEmpty ( $queryParams [ 'jwt' ]);
2025-11-12 09:18:25 +00:00
$this -> assertArrayHasKey ( 'project' , $queryParams , 'Project not found in download URL' );
$this -> assertStringContainsString ( '/storage/buckets/default/files/' , $downloadUrl );
2025-09-24 10:38:35 +00:00
// Test download with JWT
$path = \str_replace ( '/v1' , '' , $components [ 'path' ]);
$downloadWithJwt = $this -> client -> call ( Client :: METHOD_GET , $path . '?project=' . $queryParams [ 'project' ] . '&jwt=' . $queryParams [ 'jwt' ]);
$this -> assertEquals ( 200 , $downloadWithJwt [ 'headers' ][ 'status-code' ], 'Failed to download file with JWT' );
2025-11-13 11:15:18 +00:00
// Verify the downloaded content is valid CSV
$csvData = $downloadWithJwt [ 'body' ];
$this -> assertNotEmpty ( $csvData , 'CSV export should not be empty' );
$this -> assertStringContainsString ( 'name' , $csvData , 'CSV should contain the name column header' );
$this -> assertStringContainsString ( 'email' , $csvData , 'CSV should contain the email column header' );
$this -> assertStringContainsString ( 'Test User 1' , $csvData , 'CSV should contain test data' );
2025-09-24 10:38:35 +00:00
2025-11-13 11:15:18 +00:00
// Cleanup
2025-09-24 10:38:35 +00:00
$this -> client -> call ( Client :: METHOD_DELETE , '/databases/' . $databaseId , [
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]);
}
2024-10-30 05:27:25 +00:00
}