2021-03-02 09:56:18 +00:00
< ? php
namespace Tests\E2E\Services\Realtime ;
2021-12-06 12:03:12 +00:00
use CURLFile ;
2025-08-05 06:23:17 +00:00
use Exception ;
2025-12-24 08:11:31 +00:00
use Swoole\Coroutine ;
2021-12-06 12:03:12 +00:00
use Tests\E2E\Client ;
2021-03-02 09:56:18 +00:00
use Tests\E2E\Scopes\ProjectCustom ;
2024-03-06 17:34:21 +00:00
use Tests\E2E\Scopes\Scope ;
2021-03-02 09:56:18 +00:00
use Tests\E2E\Scopes\SideClient ;
2024-11-07 11:05:37 +00:00
use Tests\E2E\Services\Functions\FunctionsBase ;
2022-12-14 15:42:25 +00:00
use Utopia\Database\Helpers\ID ;
2022-12-14 16:04:06 +00:00
use Utopia\Database\Helpers\Permission ;
use Utopia\Database\Helpers\Role ;
2021-12-06 12:03:12 +00:00
use WebSocket\ConnectionException ;
2025-08-05 06:23:17 +00:00
use WebSocket\TimeoutException ;
2021-03-02 09:56:18 +00:00
class RealtimeCustomClientTest extends Scope
{
2024-11-07 11:05:37 +00:00
use FunctionsBase ;
2021-03-02 09:56:18 +00:00
use RealtimeBase ;
use ProjectCustom ;
use SideClient ;
2021-12-06 12:03:12 +00:00
public function testChannelParsing ()
{
$user = $this -> getUser ();
$userId = $user [ '$id' ] ? ? '' ;
$session = $user [ 'session' ] ? ? '' ;
2025-10-04 06:26:22 +00:00
$headers = [
2021-12-06 12:03:12 +00:00
'origin' => 'http://localhost' ,
2022-04-18 16:21:45 +00:00
'cookie' => 'a_session_' . $this -> getProject ()[ '$id' ] . '=' . $session
2021-12-06 12:03:12 +00:00
];
2025-05-09 09:41:14 +00:00
$client = $this -> getWebsocket ([ 'documents' ], $headers );
2021-12-06 12:03:12 +00:00
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'user' ]);
$this -> assertCount ( 1 , $response [ 'data' ][ 'channels' ]);
2025-05-09 09:41:14 +00:00
$this -> assertContains ( 'documents' , $response [ 'data' ][ 'channels' ]);
2021-12-06 12:03:12 +00:00
$this -> assertEquals ( $userId , $response [ 'data' ][ 'user' ][ '$id' ]);
$client -> close ();
$client = $this -> getWebsocket ([ 'account' ], $headers );
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'user' ]);
$this -> assertCount ( 2 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'account' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'account.' . $userId , $response [ 'data' ][ 'channels' ]);
$this -> assertEquals ( $userId , $response [ 'data' ][ 'user' ][ '$id' ]);
$client -> close ();
2025-05-09 09:41:14 +00:00
$client = $this -> getWebsocket ([ 'account' , 'documents' , 'account.123' ], $headers );
2021-12-06 12:03:12 +00:00
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'user' ]);
$this -> assertCount ( 3 , $response [ 'data' ][ 'channels' ]);
2025-05-09 09:41:14 +00:00
$this -> assertContains ( 'documents' , $response [ 'data' ][ 'channels' ]);
2021-12-06 12:03:12 +00:00
$this -> assertContains ( 'account' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'account.' . $userId , $response [ 'data' ][ 'channels' ]);
$this -> assertEquals ( $userId , $response [ 'data' ][ 'user' ][ '$id' ]);
$client -> close ();
$client = $this -> getWebsocket ([
'account' ,
'files' ,
'files.1' ,
2025-05-09 09:41:14 +00:00
'collections' ,
2025-06-13 09:41:18 +00:00
'tables' ,
2025-05-09 09:41:14 +00:00
'collections.1.documents' ,
'collections.2.documents' ,
2025-06-13 09:41:18 +00:00
'tables.1.rows' ,
'tables.2.rows' ,
2025-05-09 09:41:14 +00:00
'documents' ,
2025-06-13 09:41:18 +00:00
'rows' ,
2025-05-09 09:41:14 +00:00
'collections.1.documents.1' ,
'collections.2.documents.2' ,
2025-06-13 09:41:18 +00:00
'tables.1.rows.1' ,
'tables.2.rows.2' ,
2021-12-06 12:03:12 +00:00
], $headers );
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'user' ]);
2025-06-13 09:41:18 +00:00
$this -> assertCount ( 16 , $response [ 'data' ][ 'channels' ]);
2021-12-06 12:03:12 +00:00
$this -> assertContains ( 'account' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'account.' . $userId , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'files' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'files.1' , $response [ 'data' ][ 'channels' ]);
2025-05-09 09:41:14 +00:00
$this -> assertContains ( 'collections' , $response [ 'data' ][ 'channels' ]);
2025-06-13 09:41:18 +00:00
$this -> assertContains ( 'tables' , $response [ 'data' ][ 'channels' ]);
2025-05-09 09:41:14 +00:00
$this -> assertContains ( 'collections.1.documents' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'collections.2.documents' , $response [ 'data' ][ 'channels' ]);
2025-06-13 09:41:18 +00:00
$this -> assertContains ( 'tables.1.rows' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'tables.2.rows' , $response [ 'data' ][ 'channels' ]);
2025-05-09 09:41:14 +00:00
$this -> assertContains ( 'documents' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'collections.1.documents.1' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'collections.2.documents.2' , $response [ 'data' ][ 'channels' ]);
2025-06-13 09:41:18 +00:00
$this -> assertContains ( 'tables.1.rows.1' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'tables.2.rows.2' , $response [ 'data' ][ 'channels' ]);
2021-12-06 12:03:12 +00:00
$this -> assertEquals ( $userId , $response [ 'data' ][ 'user' ][ '$id' ]);
$client -> close ();
}
2024-11-07 11:05:37 +00:00
public function testPingPong ()
{
$client = $this -> getWebsocket ([ 'files' ], [
'origin' => 'http://localhost'
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertCount ( 1 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'files' , $response [ 'data' ][ 'channels' ]);
$client -> send ( \json_encode ([
'type' => 'ping'
]));
$response = json_decode ( $client -> receive (), true );
$this -> assertEquals ( 'pong' , $response [ 'type' ]);
$client -> close ();
}
2021-12-06 12:03:12 +00:00
public function testManualAuthentication ()
{
$user = $this -> getUser ();
$userId = $user [ '$id' ] ? ? '' ;
$session = $user [ 'session' ] ? ? '' ;
/**
* Test for SUCCESS
*/
$client = $this -> getWebsocket ([ 'account' ], [
'origin' => 'http://localhost'
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertCount ( 1 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'account' , $response [ 'data' ][ 'channels' ]);
$client -> send ( \json_encode ([
'type' => 'authentication' ,
'data' => [
'session' => $session
]
]));
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'response' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertEquals ( 'authentication' , $response [ 'data' ][ 'to' ]);
$this -> assertTrue ( $response [ 'data' ][ 'success' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'user' ]);
$this -> assertEquals ( $userId , $response [ 'data' ][ 'user' ][ '$id' ]);
/**
* Test for FAILURE
*/
$client -> send ( \json_encode ([
'type' => 'authentication' ,
'data' => [
'session' => 'invalid_session'
]
]));
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'error' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertEquals ( 1003 , $response [ 'data' ][ 'code' ]);
$this -> assertEquals ( 'Session is not valid.' , $response [ 'data' ][ 'message' ]);
$client -> send ( \json_encode ([
'type' => 'authentication' ,
'data' => []
]));
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'error' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertEquals ( 1003 , $response [ 'data' ][ 'code' ]);
$this -> assertEquals ( 'Payload is not valid.' , $response [ 'data' ][ 'message' ]);
$client -> send ( \json_encode ([
'type' => 'unknown' ,
'data' => [
'session' => 'invalid_session'
]
]));
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'error' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertEquals ( 1003 , $response [ 'data' ][ 'code' ]);
$this -> assertEquals ( 'Message type is not valid.' , $response [ 'data' ][ 'message' ]);
$client -> send ( \json_encode ([
'test' => '123' ,
]));
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'error' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertEquals ( 1003 , $response [ 'data' ][ 'code' ]);
$this -> assertEquals ( 'Message format is not valid.' , $response [ 'data' ][ 'message' ]);
$client -> close ();
}
public function testConnectionPlatform ()
{
/**
* Test for FAILURE
*/
2025-05-09 09:41:14 +00:00
$client = $this -> getWebsocket ([ 'documents' ], [ 'origin' => 'http://appwrite.unknown' ]);
2021-12-06 12:03:12 +00:00
$payload = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $payload );
$this -> assertArrayHasKey ( 'data' , $payload );
$this -> assertEquals ( 'error' , $payload [ 'type' ]);
$this -> assertEquals ( 1008 , $payload [ 'data' ][ 'code' ]);
$this -> assertEquals ( 'Invalid Origin. Register your new client (appwrite.unknown) as a new Web platform on your project console dashboard' , $payload [ 'data' ][ 'message' ]);
2022-03-01 19:52:29 +00:00
\usleep ( 250000 ); // 250ms
2021-12-06 12:03:12 +00:00
$this -> expectException ( ConnectionException :: class ); // Check if server disconnnected client
$client -> close ();
}
public function testChannelAccount ()
{
$user = $this -> getUser ();
$userId = $user [ '$id' ] ? ? '' ;
$session = $user [ 'session' ] ? ? '' ;
$projectId = $this -> getProject ()[ '$id' ];
$client = $this -> getWebsocket ([ 'account' ], [
'origin' => 'http://localhost' ,
2022-04-18 16:21:45 +00:00
'cookie' => 'a_session_' . $projectId . '=' . $session
2021-12-06 12:03:12 +00:00
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertCount ( 2 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'account' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'account.' . $userId , $response [ 'data' ][ 'channels' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'user' ]);
$this -> assertEquals ( $userId , $response [ 'data' ][ 'user' ][ '$id' ]);
/**
* Test Account Name Event
*/
$name = " Torsten Dittmann " ;
$this -> client -> call ( Client :: METHOD_PATCH , '/account/name' , array_merge ([
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
'cookie' => 'a_session_' . $projectId . '=' . $session ,
]), [
'name' => $name
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertCount ( 2 , $response [ 'data' ][ 'channels' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertContains ( 'account' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'account.' . $userId , $response [ 'data' ][ 'channels' ]);
2022-04-18 16:21:45 +00:00
$this -> assertContains ( " users. { $userId } .update.name " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.update.name " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$this -> assertEquals ( $name , $response [ 'data' ][ 'payload' ][ 'name' ]);
/**
* Test Account Password Event
*/
$this -> client -> call ( Client :: METHOD_PATCH , '/account/password' , array_merge ([
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
2022-04-18 16:21:45 +00:00
'cookie' => 'a_session_' . $projectId . '=' . $session ,
2021-12-06 12:03:12 +00:00
]), [
'password' => 'new-password' ,
'oldPassword' => 'password' ,
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertCount ( 2 , $response [ 'data' ][ 'channels' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertContains ( 'account' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'account.' . $userId , $response [ 'data' ][ 'channels' ]);
2022-04-18 16:21:45 +00:00
$this -> assertContains ( " users. { $userId } .update.password " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.update.password " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$this -> assertEquals ( $name , $response [ 'data' ][ 'payload' ][ 'name' ]);
/**
* Test Account Email Update
*/
$this -> client -> call ( Client :: METHOD_PATCH , '/account/email' , array_merge ([
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
2022-04-18 16:21:45 +00:00
'cookie' => 'a_session_' . $projectId . '=' . $session ,
2021-12-06 12:03:12 +00:00
]), [
'email' => 'torsten@appwrite.io' ,
'password' => 'new-password' ,
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertCount ( 2 , $response [ 'data' ][ 'channels' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertContains ( 'account' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'account.' . $userId , $response [ 'data' ][ 'channels' ]);
2022-04-18 16:21:45 +00:00
$this -> assertContains ( " users. { $userId } .update.email " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.update.email " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$this -> assertEquals ( 'torsten@appwrite.io' , $response [ 'data' ][ 'payload' ][ 'email' ]);
/**
* Test Account Verification Create
*/
2022-04-13 12:39:31 +00:00
$verification = $this -> client -> call ( Client :: METHOD_POST , '/account/verification' , array_merge ([
2021-12-06 12:03:12 +00:00
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
2022-04-18 16:21:45 +00:00
'cookie' => 'a_session_' . $projectId . '=' . $session ,
2021-12-06 12:03:12 +00:00
]), [
'url' => 'http://localhost/verification' ,
]);
2022-04-18 16:21:45 +00:00
$verificationId = $verification [ 'body' ][ '$id' ];
2021-12-06 12:03:12 +00:00
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertCount ( 2 , $response [ 'data' ][ 'channels' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
2024-04-03 03:37:58 +00:00
$this -> assertArrayNotHasKey ( 'secret' , $response [ 'data' ]);
2021-12-06 12:03:12 +00:00
$this -> assertContains ( 'account' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'account.' . $userId , $response [ 'data' ][ 'channels' ]);
2022-04-18 16:21:45 +00:00
$this -> assertContains ( " users. { $userId } .verification. { $verificationId } .create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .verification. { $verificationId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .verification.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .verification.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.verification. { $verificationId } .create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.verification. { $verificationId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.verification.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.verification.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$lastEmail = $this -> getLastEmail ();
2025-03-29 13:29:36 +00:00
$tokens = $this -> extractQueryParamsFromEmailLink ( $lastEmail [ 'html' ]);
$verification = $tokens [ 'secret' ];
2021-12-06 12:03:12 +00:00
/**
* Test Account Verification Complete
*/
2022-04-13 12:39:31 +00:00
$verification = $this -> client -> call ( Client :: METHOD_PUT , '/account/verification' , array_merge ([
2021-12-06 12:03:12 +00:00
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
2022-04-18 16:21:45 +00:00
'cookie' => 'a_session_' . $projectId . '=' . $session ,
2021-12-06 12:03:12 +00:00
]), [
'userId' => $userId ,
'secret' => $verification ,
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertCount ( 2 , $response [ 'data' ][ 'channels' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertContains ( 'account' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'account.' . $userId , $response [ 'data' ][ 'channels' ]);
2022-04-18 16:21:45 +00:00
$this -> assertContains ( " users. { $userId } .verification. { $verificationId } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .verification. { $verificationId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .verification.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .verification.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.verification. { $verificationId } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.verification. { $verificationId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.verification.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.verification.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
/**
* Test Acoount Prefs Update
*/
$this -> client -> call ( Client :: METHOD_PATCH , '/account/prefs' , array_merge ([
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
2022-04-18 16:21:45 +00:00
'cookie' => 'a_session_' . $projectId . '=' . $session ,
2021-12-06 12:03:12 +00:00
]), [
'prefs' => [
'prefKey1' => 'prefValue1' ,
'prefKey2' => 'prefValue2' ,
]
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertCount ( 2 , $response [ 'data' ][ 'channels' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertContains ( 'account' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'account.' . $userId , $response [ 'data' ][ 'channels' ]);
2022-04-18 16:21:45 +00:00
$this -> assertContains ( " users. { $userId } .update.prefs " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.update.prefs " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
2022-10-27 17:32:49 +00:00
$createSession = function () use ( $projectId ) : array {
$response = $this -> client -> call ( Client :: METHOD_POST , '/account/sessions/email' , array_merge ([
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
]), [
'email' => 'torsten@appwrite.io' ,
'password' => 'new-password' ,
]);
2023-12-08 23:17:13 +00:00
$sessionNew = $response [ 'cookies' ][ 'a_session_' . $projectId ];
2022-10-27 17:32:49 +00:00
$sessionNewId = $response [ 'body' ][ '$id' ];
return array ( " session " => $sessionNew , " sessionId " => $sessionNewId );
};
2021-12-06 12:03:12 +00:00
/**
* Test Account Session Create
*/
2022-10-27 17:32:49 +00:00
$sessionData = $createSession ();
2021-12-06 12:03:12 +00:00
2022-10-27 17:32:49 +00:00
$sessionNew = $sessionData [ 'session' ];
$sessionNewId = $sessionData [ 'sessionId' ];
2021-12-06 12:03:12 +00:00
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertCount ( 2 , $response [ 'data' ][ 'channels' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertContains ( 'account' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'account.' . $userId , $response [ 'data' ][ 'channels' ]);
2022-04-18 16:21:45 +00:00
$this -> assertContains ( " users. { $userId } .sessions. { $sessionNewId } .create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .sessions. { $sessionNewId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .sessions.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .sessions.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.sessions. { $sessionNewId } .create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.sessions. { $sessionNewId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.sessions.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.sessions.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
/**
* Test Account Session Delete
*/
2022-04-18 16:21:45 +00:00
$this -> client -> call ( Client :: METHOD_DELETE , '/account/sessions/' . $sessionNewId , array_merge ([
2021-12-06 12:03:12 +00:00
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
2022-04-18 16:21:45 +00:00
'cookie' => 'a_session_' . $projectId . '=' . $sessionNew ,
2021-12-06 12:03:12 +00:00
]));
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertCount ( 2 , $response [ 'data' ][ 'channels' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertContains ( 'account' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'account.' . $userId , $response [ 'data' ][ 'channels' ]);
2022-04-18 16:21:45 +00:00
$this -> assertContains ( " users. { $userId } .sessions. { $sessionNewId } .delete " , $response [ 'data' ][ 'events' ]);
2022-10-27 17:32:49 +00:00
$this -> assertContains ( " users. { $userId } .sessions. { $sessionNewId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .sessions.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .sessions.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.sessions. { $sessionNewId } .delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.sessions. { $sessionNewId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.sessions.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.sessions.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.* " , $response [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
/**
* Test User Account Session Delete
*/
$sessionData = $createSession ();
$sessionNew = $sessionData [ 'session' ];
$sessionNewId = $sessionData [ 'sessionId' ];
$client -> receive (); // Receive the creation message and drop; this was tested earlier already
$this -> client -> call ( Client :: METHOD_DELETE , '/users/' . $userId . '/sessions/' . $sessionNewId , array_merge ([
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
]));
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertCount ( 2 , $response [ 'data' ][ 'channels' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertContains ( 'account' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'account.' . $userId , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " users. { $userId } .sessions. { $sessionNewId } .delete " , $response [ 'data' ][ 'events' ]);
2022-04-18 16:21:45 +00:00
$this -> assertContains ( " users. { $userId } .sessions. { $sessionNewId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .sessions.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .sessions.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.sessions. { $sessionNewId } .delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.sessions. { $sessionNewId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.sessions.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.sessions.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
/**
* Test Account Create Recovery
*/
2022-04-13 12:39:31 +00:00
$recovery = $this -> client -> call ( Client :: METHOD_POST , '/account/recovery' , array_merge ([
2021-12-06 12:03:12 +00:00
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
]), [
'email' => 'torsten@appwrite.io' ,
'url' => 'http://localhost/recovery' ,
]);
2022-04-13 12:39:31 +00:00
$recoveryId = $recovery [ 'body' ][ '$id' ];
2021-12-06 12:03:12 +00:00
$response = json_decode ( $client -> receive (), true );
$lastEmail = $this -> getLastEmail ();
2025-03-29 13:50:27 +00:00
$tokens = $this -> extractQueryParamsFromEmailLink ( $lastEmail [ 'html' ]);
$recovery = $tokens [ 'secret' ];
2021-12-06 12:03:12 +00:00
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertCount ( 2 , $response [ 'data' ][ 'channels' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertContains ( 'account' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'account.' . $userId , $response [ 'data' ][ 'channels' ]);
2022-04-18 16:21:45 +00:00
$this -> assertContains ( " users. { $userId } .recovery. { $recoveryId } .create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .recovery. { $recoveryId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .recovery.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .recovery.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.recovery. { $recoveryId } .create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.recovery. { $recoveryId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.recovery.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.recovery.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$response = $this -> client -> call ( Client :: METHOD_PUT , '/account/recovery' , array_merge ([
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
]), [
'userId' => $userId ,
'secret' => $recovery ,
'password' => 'test-recovery' ,
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertCount ( 2 , $response [ 'data' ][ 'channels' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertContains ( 'account' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'account.' . $userId , $response [ 'data' ][ 'channels' ]);
2022-04-18 16:21:45 +00:00
$this -> assertContains ( " users. { $userId } .recovery. { $recoveryId } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .recovery. { $recoveryId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .recovery.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } .recovery.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users. { $userId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.recovery. { $recoveryId } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.recovery. { $recoveryId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.recovery.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.*.recovery.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " users.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$client -> close ();
}
public function testChannelDatabase ()
{
$user = $this -> getUser ();
$session = $user [ 'session' ] ? ? '' ;
$projectId = $this -> getProject ()[ '$id' ];
2025-05-09 09:41:14 +00:00
$client = $this -> getWebsocket ([ 'documents' , 'collections' ], [
2021-12-06 12:03:12 +00:00
'origin' => 'http://localhost' ,
2025-12-22 12:31:00 +00:00
'cookie' => 'a_session_' . $projectId . '=' . $session ,
], null );
2021-12-06 12:03:12 +00:00
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertCount ( 2 , $response [ 'data' ][ 'channels' ]);
2025-05-09 09:41:14 +00:00
$this -> assertContains ( 'documents' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'collections' , $response [ 'data' ][ 'channels' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'user' ]);
$this -> assertEquals ( $user [ '$id' ], $response [ 'data' ][ 'user' ][ '$id' ]);
2022-06-22 10:51:49 +00:00
/**
* Test Database Create
*/
$database = $this -> client -> call ( Client :: METHOD_POST , '/databases' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
2022-08-14 10:33:36 +00:00
'databaseId' => ID :: unique (),
2022-06-22 10:51:49 +00:00
'name' => 'Actors DB' ,
]);
$databaseId = $database [ 'body' ][ '$id' ];
2021-12-06 12:03:12 +00:00
/**
* Test Collection Create
*/
2022-06-22 10:51:49 +00:00
$actors = $this -> client -> call ( Client :: METHOD_POST , '/databases/' . $databaseId . '/collections' , array_merge ([
2021-12-06 12:03:12 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
2025-05-09 09:41:14 +00:00
'collectionId' => ID :: unique (),
2021-12-06 12:03:12 +00:00
'name' => 'Actors' ,
2022-08-13 14:10:28 +00:00
'permissions' => [
2022-08-27 03:16:37 +00:00
Permission :: create ( Role :: user ( $this -> getUser ()[ '$id' ])),
2022-08-13 14:10:28 +00:00
],
2022-08-03 04:17:49 +00:00
'documentSecurity' => true ,
2021-12-16 18:12:06 +00:00
]);
2022-04-18 16:21:45 +00:00
$actorsId = $actors [ 'body' ][ '$id' ];
2021-12-16 18:12:06 +00:00
2022-06-22 10:51:49 +00:00
$name = $this -> client -> call ( Client :: METHOD_POST , '/databases/' . $databaseId . '/collections/' . $actorsId . '/attributes/string' , array_merge ([
2021-12-16 18:12:06 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
2021-12-17 10:41:26 +00:00
'key' => 'name' ,
2021-12-16 18:12:06 +00:00
'size' => 256 ,
'required' => true ,
]);
2025-05-09 09:41:14 +00:00
$this -> assertEquals ( 202 , $name [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'name' , $name [ 'body' ][ 'key' ]);
$this -> assertEquals ( 'string' , $name [ 'body' ][ 'type' ]);
$this -> assertEquals ( 256 , $name [ 'body' ][ 'size' ]);
$this -> assertTrue ( $name [ 'body' ][ 'required' ]);
2021-12-16 18:12:06 +00:00
sleep ( 2 );
/**
* Test Document Create
*/
2022-06-22 10:51:49 +00:00
$document = $this -> client -> call ( Client :: METHOD_POST , '/databases/' . $databaseId . '/collections/' . $actorsId . '/documents' , array_merge ([
2021-12-16 18:12:06 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
2025-05-09 09:41:14 +00:00
'documentId' => ID :: unique (),
2021-12-16 18:12:06 +00:00
'data' => [
'name' => 'Chris Evans'
],
2022-08-03 04:17:49 +00:00
'permissions' => [
2022-08-14 05:21:11 +00:00
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
2022-08-03 04:17:49 +00:00
],
2021-12-06 12:03:12 +00:00
]);
$response = json_decode ( $client -> receive (), true );
2022-04-18 16:21:45 +00:00
$documentId = $document [ 'body' ][ '$id' ];
2021-12-06 12:03:12 +00:00
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
2025-05-09 09:46:10 +00:00
$this -> assertCount ( 6 , $response [ 'data' ][ 'channels' ]);
2025-05-09 09:41:14 +00:00
$this -> assertContains ( 'documents' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'databases.' . $databaseId . '.collections.' . $actorsId . '.documents.' . $documentId , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'databases.' . $databaseId . '.collections.' . $actorsId . '.documents' , $response [ 'data' ][ 'channels' ]);
2025-10-06 14:43:36 +00:00
$this -> assertContains ( 'databases.' . $databaseId . '.tables.' . $actorsId . '.rows.' . $documentId , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'databases.' . $databaseId . '.tables.' . $actorsId . '.rows' , $response [ 'data' ][ 'channels' ]);
2025-05-09 09:41:14 +00:00
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $documentId } .create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $documentId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents. { $documentId } .create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents. { $documentId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response [ 'data' ][ 'events' ]);
2022-06-22 10:51:49 +00:00
$this -> assertContains ( " databases. { $databaseId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response [ 'data' ][ 'events' ]);
2021-12-16 18:12:06 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
2025-05-09 09:41:14 +00:00
$this -> assertEquals ( 'Chris Evans' , $response [ 'data' ][ 'payload' ][ 'name' ]);
2021-12-16 18:12:06 +00:00
/**
* Test Document Update
*/
2022-06-22 10:51:49 +00:00
$document = $this -> client -> call ( Client :: METHOD_PATCH , '/databases/' . $databaseId . '/collections/' . $actorsId . '/documents/' . $documentId , array_merge ([
2021-12-16 18:12:06 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
2025-05-09 09:41:14 +00:00
'documentId' => ID :: unique (),
2021-12-16 18:12:06 +00:00
'data' => [
'name' => 'Chris Evans 2'
],
2022-08-03 04:17:49 +00:00
'permissions' => [
2022-08-14 05:21:11 +00:00
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
2022-08-03 04:17:49 +00:00
],
2021-12-16 18:12:06 +00:00
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
2025-05-09 09:46:10 +00:00
$this -> assertCount ( 6 , $response [ 'data' ][ 'channels' ]);
2025-05-09 09:41:14 +00:00
$this -> assertContains ( 'documents' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $documentId } " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $documentId } .update " , $response [ 'data' ][ 'events' ]);
2025-10-06 14:43:36 +00:00
$this -> assertContains ( " databases. { $databaseId } .tables. { $actorsId } .rows " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .tables. { $actorsId } .rows. { $documentId } .update " , $response [ 'data' ][ 'events' ]);
2025-05-09 09:41:14 +00:00
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $documentId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents. { $documentId } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents. { $documentId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response [ 'data' ][ 'events' ]);
2022-06-22 10:51:49 +00:00
$this -> assertContains ( " databases. { $databaseId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response [ 'data' ][ 'events' ]);
2021-12-16 18:12:06 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
2025-05-09 09:41:14 +00:00
$this -> assertEquals ( 'Chris Evans 2' , $response [ 'data' ][ 'payload' ][ 'name' ]);
2021-12-16 18:12:06 +00:00
/**
* Test Document Delete
*/
2022-06-22 10:51:49 +00:00
$document = $this -> client -> call ( Client :: METHOD_POST , '/databases/' . $databaseId . '/collections/' . $actorsId . '/documents' , array_merge ([
2021-12-16 18:12:06 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
2025-05-09 09:41:14 +00:00
'documentId' => ID :: unique (),
2021-12-16 18:12:06 +00:00
'data' => [
'name' => 'Bradley Cooper'
],
2022-08-03 04:17:49 +00:00
'permissions' => [
2022-08-14 05:21:11 +00:00
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
2022-08-03 04:17:49 +00:00
],
2021-12-06 12:03:12 +00:00
]);
2021-12-16 18:12:06 +00:00
$client -> receive ();
2022-04-18 16:21:45 +00:00
$documentId = $document [ 'body' ][ '$id' ];
2022-06-22 10:51:49 +00:00
$this -> client -> call ( Client :: METHOD_DELETE , '/databases/' . $databaseId . '/collections/' . $actorsId . '/documents/' . $documentId , array_merge ([
2021-12-16 18:12:06 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()));
2021-12-06 12:03:12 +00:00
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
2025-05-09 09:46:10 +00:00
$this -> assertCount ( 6 , $response [ 'data' ][ 'channels' ]);
2025-05-09 09:41:14 +00:00
$this -> assertContains ( 'documents' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $documentId } " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents " , $response [ 'data' ][ 'channels' ]);
2025-10-06 14:43:36 +00:00
$this -> assertContains ( " databases. { $databaseId } .tables. { $actorsId } .rows. { $documentId } " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .tables. { $actorsId } .rows " , $response [ 'data' ][ 'channels' ]);
2025-05-09 09:41:14 +00:00
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $documentId } .delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $documentId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents. { $documentId } .delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents. { $documentId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response [ 'data' ][ 'events' ]);
2022-06-22 10:51:49 +00:00
$this -> assertContains ( " databases. { $databaseId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response [ 'data' ][ 'events' ]);
2021-12-16 18:12:06 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
2025-05-09 09:41:14 +00:00
$this -> assertEquals ( 'Bradley Cooper' , $response [ 'data' ][ 'payload' ][ 'name' ]);
2021-12-16 18:12:06 +00:00
2025-08-04 13:31:48 +00:00
// test bulk create
$documents = $this -> client -> call ( Client :: METHOD_POST , " /databases/ { $databaseId } /collections/ { $actorsId } /documents " , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'documents' => [
[
'$id' => ID :: unique (),
'name' => 'Robert Downey Jr.' ,
'$permissions' => [
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
],
[
'$id' => ID :: unique (),
'name' => 'Scarlett Johansson' ,
'$permissions' => [
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
]
],
]);
// Receive first document event
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertCount ( 6 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $response [ 'data' ][ 'payload' ][ '$id' ] } .create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$id' , $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( 'name' , $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$permissions' , $response [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response [ 'data' ][ 'payload' ][ '$permissions' ]);
$this -> assertContains ( Permission :: read ( Role :: any ()), $response [ 'data' ][ 'payload' ][ '$permissions' ]);
$this -> assertContains ( Permission :: update ( Role :: any ()), $response [ 'data' ][ 'payload' ][ '$permissions' ]);
$this -> assertContains ( Permission :: delete ( Role :: any ()), $response [ 'data' ][ 'payload' ][ '$permissions' ]);
// Receive second document event
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertCount ( 6 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $response [ 'data' ][ 'payload' ][ '$id' ] } .create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$id' , $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( 'name' , $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$permissions' , $response [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response [ 'data' ][ 'payload' ][ '$permissions' ]);
$this -> assertContains ( Permission :: read ( Role :: any ()), $response [ 'data' ][ 'payload' ][ '$permissions' ]);
$this -> assertContains ( Permission :: update ( Role :: any ()), $response [ 'data' ][ 'payload' ][ '$permissions' ]);
$this -> assertContains ( Permission :: delete ( Role :: any ()), $response [ 'data' ][ 'payload' ][ '$permissions' ]);
// test bulk update
$response = $this -> client -> call ( Client :: METHOD_PATCH , '/databases/' . $databaseId . '/collections/' . $actorsId . '/documents/' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'data' => [
2025-08-05 06:23:17 +00:00
'name' => 'Marvel Hero' ,
'$permissions' => [
Permission :: read ( Role :: user ( $this -> getUser ()[ '$id' ])),
Permission :: update ( Role :: user ( $this -> getUser ()[ '$id' ])),
Permission :: delete ( Role :: user ( $this -> getUser ()[ '$id' ])),
]
2025-08-04 13:31:48 +00:00
],
]);
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
// Receive first document update event
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertCount ( 6 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $response [ 'data' ][ 'payload' ][ '$id' ] } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$id' , $response [ 'data' ][ 'payload' ]);
$this -> assertEquals ( 'Marvel Hero' , $response [ 'data' ][ 'payload' ][ 'name' ]);
$this -> assertArrayHasKey ( '$permissions' , $response [ 'data' ][ 'payload' ]);
// Receive second document update event
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertCount ( 6 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $response [ 'data' ][ 'payload' ][ '$id' ] } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$id' , $response [ 'data' ][ 'payload' ]);
$this -> assertEquals ( 'Marvel Hero' , $response [ 'data' ][ 'payload' ][ 'name' ]);
$this -> assertArrayHasKey ( '$permissions' , $response [ 'data' ][ 'payload' ]);
// Receive third document update event
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertCount ( 6 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $response [ 'data' ][ 'payload' ][ '$id' ] } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$id' , $response [ 'data' ][ 'payload' ]);
$this -> assertEquals ( 'Marvel Hero' , $response [ 'data' ][ 'payload' ][ 'name' ]);
$this -> assertArrayHasKey ( '$permissions' , $response [ 'data' ][ 'payload' ]);
// Test bulk delete
$response = $this -> client -> call ( Client :: METHOD_DELETE , " /databases/ { $databaseId } /collections/ { $actorsId } /documents " , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]));
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
// Receive first document delete event
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertCount ( 6 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $response [ 'data' ][ 'payload' ][ '$id' ] } .delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$id' , $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( 'name' , $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$permissions' , $response [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response [ 'data' ][ 'payload' ][ '$permissions' ]);
// Receive second document delete event
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertCount ( 6 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $response [ 'data' ][ 'payload' ][ '$id' ] } .delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$id' , $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( 'name' , $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$permissions' , $response [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response [ 'data' ][ 'payload' ][ '$permissions' ]);
// Receive third document delete event
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertCount ( 6 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $response [ 'data' ][ 'payload' ][ '$id' ] } .delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$id' , $response [ 'data' ][ 'payload' ]);
2025-09-03 14:06:24 +00:00
$this -> assertArrayHasKey ( 'name' , $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$permissions' , $response [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response [ 'data' ][ 'payload' ][ '$permissions' ]);
// bulk upsert
$this -> client -> call ( Client :: METHOD_PUT , " /databases/ { $databaseId } /collections/ { $actorsId } /documents " , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'documents' => [
[
'$id' => ID :: unique (),
'name' => 'Robert Downey Jr.' ,
'$permissions' => [
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
]
],
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertCount ( 6 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $response [ 'data' ][ 'payload' ][ '$id' ] } .upsert " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.*.upsert " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.upsert " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.*.upsert " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.upsert " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$id' , $response [ 'data' ][ 'payload' ]);
2025-08-04 13:31:48 +00:00
$this -> assertArrayHasKey ( 'name' , $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$permissions' , $response [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response [ 'data' ][ 'payload' ][ '$permissions' ]);
2021-12-16 18:12:06 +00:00
$client -> close ();
}
2025-08-04 13:31:48 +00:00
public function testChannelDatabaseBulkOperationMultipleClient ()
{
// user with api key will do operations and other valid users
$user1 = $this -> getUser ( true );
$user1Id = $user1 [ '$id' ];
$session = $user1 [ 'session' ] ? ? '' ;
$projectId = $this -> getProject ()[ '$id' ];
$client1 = $this -> getWebsocket ([ 'documents' , 'collections' ], [
'origin' => 'http://localhost' ,
'cookie' => 'a_session_' . $projectId . '=' . $session
]);
$response = json_decode ( $client1 -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
2025-08-05 08:07:24 +00:00
$user2 = $this -> getUser ( true );
2025-08-04 13:31:48 +00:00
$user2Id = $user2 [ '$id' ];
$session = $user2 [ 'session' ] ? ? '' ;
$projectId = $this -> getProject ()[ '$id' ];
$client2 = $this -> getWebsocket ([ 'documents' , 'collections' ], [
'origin' => 'http://localhost' ,
'cookie' => 'a_session_' . $projectId . '=' . $session
]);
$response = json_decode ( $client2 -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
/**
* Test Database Create
*/
$database = $this -> client -> call ( Client :: METHOD_POST , '/databases' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'databaseId' => ID :: unique (),
'name' => 'Actors DB' ,
]);
$databaseId = $database [ 'body' ][ '$id' ];
/**
* Test Collection Create
*/
$actors = $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' ]
]), [
'collectionId' => ID :: unique (),
'name' => 'Actors' ,
'permissions' => [
Permission :: create ( Role :: user ( $this -> getUser ()[ '$id' ])),
],
'documentSecurity' => true ,
]);
$actorsId = $actors [ 'body' ][ '$id' ];
$name = $this -> client -> call ( Client :: METHOD_POST , '/databases/' . $databaseId . '/collections/' . $actorsId . '/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 , $name [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'name' , $name [ 'body' ][ 'key' ]);
$this -> assertEquals ( 'string' , $name [ 'body' ][ 'type' ]);
$this -> assertEquals ( 256 , $name [ 'body' ][ 'size' ]);
$this -> assertTrue ( $name [ 'body' ][ 'required' ]);
sleep ( 2 );
// create
$this -> client -> call ( Client :: METHOD_POST , " /databases/ { $databaseId } /collections/ { $actorsId } /documents " , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'documents' => [
[
'$id' => ID :: unique (),
'name' => 'Any' ,
'$permissions' => [
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
],
[
'$id' => ID :: unique (),
'name' => 'Users' ,
'$permissions' => [
Permission :: read ( Role :: users ()),
Permission :: update ( Role :: users ()),
Permission :: delete ( Role :: users ()),
],
],
[
'$id' => ID :: unique (),
'name' => 'User1' ,
'$permissions' => [
Permission :: read ( Role :: user ( $user1Id )),
],
],
[
'$id' => ID :: unique (),
'name' => 'User2' ,
'$permissions' => [
Permission :: read ( Role :: user ( $user2Id )),
],
],
[
'$id' => ID :: unique (),
2025-08-05 06:23:17 +00:00
'name' => 'User2' ,
2025-08-04 13:31:48 +00:00
'$permissions' => [
Permission :: read ( Role :: user ( $user2Id )),
],
]
],
]);
// Receive and assert for client1 - should receive 3 individual document events
for ( $i = 0 ; $i < 3 ; $i ++ ) {
$response1 = json_decode ( $client1 -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response1 );
$this -> assertArrayHasKey ( 'data' , $response1 );
$this -> assertEquals ( 'event' , $response1 [ 'type' ]);
$this -> assertNotEmpty ( $response1 [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response1 [ 'data' ]);
$this -> assertCount ( 6 , $response1 [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $response1 [ 'data' ][ 'payload' ][ '$id' ] } .create " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.*.create " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.create " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.*.create " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.create " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response1 [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response1 [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$id' , $response1 [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( 'name' , $response1 [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$permissions' , $response1 [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response1 [ 'data' ][ 'payload' ][ '$permissions' ]);
}
// Receive and assert for client2 - should receive 4 individual document events
for ( $i = 0 ; $i < 4 ; $i ++ ) {
$response2 = json_decode ( $client2 -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response2 );
$this -> assertArrayHasKey ( 'data' , $response2 );
$this -> assertEquals ( 'event' , $response2 [ 'type' ]);
$this -> assertNotEmpty ( $response2 [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response2 [ 'data' ]);
$this -> assertCount ( 6 , $response2 [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $response2 [ 'data' ][ 'payload' ][ '$id' ] } .create " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.*.create " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.create " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.*.create " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.create " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response2 [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response2 [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$id' , $response2 [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( 'name' , $response2 [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$permissions' , $response2 [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response2 [ 'data' ][ 'payload' ][ '$permissions' ]);
}
2025-08-05 06:23:17 +00:00
// Perform bulk update(making it only accessible by user1)
2025-08-04 13:31:48 +00:00
$response = $this -> client -> call ( Client :: METHOD_PATCH , " /databases/ { $databaseId } /collections/ { $actorsId } /documents/ " , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'data' => [
2025-08-05 06:23:17 +00:00
'name' => 'Marvel Hero' ,
'$permissions' => [
Permission :: read ( Role :: user ( $user1Id )),
Permission :: update ( Role :: user ( $user1Id )),
Permission :: delete ( Role :: user ( $user1Id )),
]
2025-08-04 13:31:48 +00:00
],
]);
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
2025-08-05 06:23:17 +00:00
// Receive and assert for client1
for ( $i = 0 ; $i < 5 ; $i ++ ) {
2025-08-04 13:31:48 +00:00
$response1 = json_decode ( $client1 -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response1 );
$this -> assertArrayHasKey ( 'data' , $response1 );
$this -> assertEquals ( 'event' , $response1 [ 'type' ]);
$this -> assertNotEmpty ( $response1 [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response1 [ 'data' ]);
$this -> assertCount ( 6 , $response1 [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $response1 [ 'data' ][ 'payload' ][ '$id' ] } .update " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.*.update " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.update " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.*.update " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.update " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response1 [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response1 [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$id' , $response1 [ 'data' ][ 'payload' ]);
$this -> assertEquals ( 'Marvel Hero' , $response1 [ 'data' ][ 'payload' ][ 'name' ]);
$this -> assertArrayHasKey ( '$permissions' , $response1 [ 'data' ][ 'payload' ]);
}
2025-08-05 06:23:17 +00:00
// client2 shouldn't receive any event and lead to timeout
try {
json_decode ( $client2 -> receive (), true );
$this -> fail ( 'Expected TimeoutException was not thrown.' );
} catch ( Exception $e ) {
$this -> assertInstanceOf ( TimeoutException :: class , $e );
}
// Perform bulk update(making it only accessible by user2)
$response = $this -> client -> call ( Client :: METHOD_PATCH , " /databases/ { $databaseId } /collections/ { $actorsId } /documents/ " , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'data' => [
'name' => 'Marvel Hero' ,
'$permissions' => [
Permission :: read ( Role :: user ( $user2Id )),
Permission :: update ( Role :: user ( $user2Id )),
Permission :: delete ( Role :: user ( $user2Id )),
]
],
]);
// Receive and assert for client2
for ( $i = 0 ; $i < 5 ; $i ++ ) {
$response2 = json_decode ( $client2 -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response2 );
$this -> assertArrayHasKey ( 'data' , $response2 );
$this -> assertEquals ( 'event' , $response2 [ 'type' ]);
$this -> assertNotEmpty ( $response2 [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response2 [ 'data' ]);
$this -> assertCount ( 6 , $response2 [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $response2 [ 'data' ][ 'payload' ][ '$id' ] } .update " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.*.update " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.update " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.*.update " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.update " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response2 [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response2 [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$id' , $response2 [ 'data' ][ 'payload' ]);
$this -> assertEquals ( 'Marvel Hero' , $response2 [ 'data' ][ 'payload' ][ 'name' ]);
$this -> assertArrayHasKey ( '$permissions' , $response2 [ 'data' ][ 'payload' ]);
}
// client1 shouldn't receive any event and lead to timeout
try {
json_decode ( $client1 -> receive (), true );
$this -> fail ( 'Expected TimeoutException was not thrown.' );
} catch ( Exception $e ) {
$this -> assertInstanceOf ( TimeoutException :: class , $e );
}
// Updating the permission for both the users
$response = $this -> client -> call ( Client :: METHOD_PATCH , " /databases/ { $databaseId } /collections/ { $actorsId } /documents/ " , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'data' => [
'name' => 'Marvel Hero' ,
'$permissions' => [
Permission :: read ( Role :: users ()),
Permission :: update ( Role :: users ()),
Permission :: delete ( Role :: users ()),
]
],
]);
// both user1 and user2 should receive the event
for ( $i = 0 ; $i < 5 ; $i ++ ) {
$response1 = json_decode ( $client1 -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response1 );
$this -> assertArrayHasKey ( 'data' , $response1 );
$this -> assertEquals ( 'event' , $response1 [ 'type' ]);
$this -> assertNotEmpty ( $response1 [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response1 [ 'data' ]);
$this -> assertCount ( 6 , $response1 [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $response1 [ 'data' ][ 'payload' ][ '$id' ] } .update " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.*.update " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.update " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.*.update " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.update " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response1 [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response1 [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$id' , $response1 [ 'data' ][ 'payload' ]);
$this -> assertEquals ( 'Marvel Hero' , $response1 [ 'data' ][ 'payload' ][ 'name' ]);
$this -> assertArrayHasKey ( '$permissions' , $response1 [ 'data' ][ 'payload' ]);
2025-08-04 13:31:48 +00:00
$response2 = json_decode ( $client2 -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response2 );
$this -> assertArrayHasKey ( 'data' , $response2 );
$this -> assertEquals ( 'event' , $response2 [ 'type' ]);
$this -> assertNotEmpty ( $response2 [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response2 [ 'data' ]);
$this -> assertCount ( 6 , $response2 [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $response2 [ 'data' ][ 'payload' ][ '$id' ] } .update " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.*.update " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.update " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.*.update " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.update " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response2 [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response2 [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$id' , $response2 [ 'data' ][ 'payload' ]);
$this -> assertEquals ( 'Marvel Hero' , $response2 [ 'data' ][ 'payload' ][ 'name' ]);
$this -> assertArrayHasKey ( '$permissions' , $response2 [ 'data' ][ 'payload' ]);
}
// Perform bulk delete
$response = $this -> client -> call ( Client :: METHOD_DELETE , " /databases/ { $databaseId } /collections/ { $actorsId } /documents " , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]));
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
2025-08-05 06:23:17 +00:00
// Receive and assert for client1
for ( $i = 0 ; $i < 5 ; $i ++ ) {
2025-08-04 13:31:48 +00:00
$response1 = json_decode ( $client1 -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response1 );
$this -> assertArrayHasKey ( 'data' , $response1 );
$this -> assertEquals ( 'event' , $response1 [ 'type' ]);
$this -> assertNotEmpty ( $response1 [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response1 [ 'data' ]);
$this -> assertCount ( 6 , $response1 [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $response1 [ 'data' ][ 'payload' ][ '$id' ] } .delete " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.*.delete " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.delete " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.*.delete " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.delete " , $response1 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response1 [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response1 [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response1 [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$id' , $response1 [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( 'name' , $response1 [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$permissions' , $response1 [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response1 [ 'data' ][ 'payload' ][ '$permissions' ]);
}
2025-08-05 06:23:17 +00:00
// Receive and assert for client2
for ( $i = 0 ; $i < 5 ; $i ++ ) {
2025-08-04 13:31:48 +00:00
$response2 = json_decode ( $client2 -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response2 );
$this -> assertArrayHasKey ( 'data' , $response2 );
$this -> assertEquals ( 'event' , $response2 [ 'type' ]);
$this -> assertNotEmpty ( $response2 [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response2 [ 'data' ]);
$this -> assertCount ( 6 , $response2 [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $response2 [ 'data' ][ 'payload' ][ '$id' ] } .delete " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.*.delete " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.delete " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.*.delete " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.delete " , $response2 [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response2 [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response2 [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response2 [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$id' , $response2 [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( 'name' , $response2 [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$permissions' , $response2 [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response2 [ 'data' ][ 'payload' ][ '$permissions' ]);
}
2025-09-03 14:23:20 +00:00
// bulk upsert
$this -> client -> call ( Client :: METHOD_PUT , " /databases/ { $databaseId } /collections/ { $actorsId } /documents " , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'documents' => [
[
'$id' => ID :: unique (),
'name' => 'Robert Downey Jr.' ,
'$permissions' => [
Permission :: read ( Role :: user ( $user1Id )),
],
],
[
'$id' => ID :: unique (),
'name' => 'Thor' ,
'$permissions' => [
Permission :: read ( Role :: user ( $user2Id )),
],
]
],
]);
$response = json_decode ( $client1 -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertCount ( 6 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $response [ 'data' ][ 'payload' ][ '$id' ] } .upsert " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.*.upsert " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.upsert " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.*.upsert " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.upsert " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$id' , $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( 'name' , $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$permissions' , $response [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response [ 'data' ][ 'payload' ][ '$permissions' ]);
// client1 shouldnot receive more than 1 event
try {
json_decode ( json_decode ( $client1 -> receive (), true ));
$this -> fail ( 'Expected TimeoutException was not thrown.' );
} catch ( Exception $e ) {
$this -> assertInstanceOf ( TimeoutException :: class , $e );
}
$response = json_decode ( $client2 -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertCount ( 6 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $response [ 'data' ][ 'payload' ][ '$id' ] } .upsert " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.*.upsert " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.upsert " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.*.upsert " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.*.collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.upsert " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$id' , $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( 'name' , $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( '$permissions' , $response [ 'data' ][ 'payload' ]);
$this -> assertIsArray ( $response [ 'data' ][ 'payload' ][ '$permissions' ]);
// client2 shouldnot receive more than 1 event
try {
json_decode ( json_decode ( $client2 -> receive (), true ));
$this -> fail ( 'Expected TimeoutException was not thrown.' );
} catch ( Exception $e ) {
$this -> assertInstanceOf ( TimeoutException :: class , $e );
}
2025-08-04 13:31:48 +00:00
$client1 -> close ();
$client2 -> close ();
}
2021-12-16 18:12:06 +00:00
public function testChannelDatabaseCollectionPermissions ()
{
$user = $this -> getUser ();
$session = $user [ 'session' ] ? ? '' ;
$projectId = $this -> getProject ()[ '$id' ];
2025-05-09 09:41:14 +00:00
$client = $this -> getWebsocket ([ 'documents' , 'collections' ], [
2021-12-16 18:12:06 +00:00
'origin' => 'http://localhost' ,
2022-04-18 16:21:45 +00:00
'cookie' => 'a_session_' . $projectId . '=' . $session
2021-12-16 18:12:06 +00:00
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
2021-12-06 12:03:12 +00:00
$this -> assertCount ( 2 , $response [ 'data' ][ 'channels' ]);
2025-05-09 09:41:14 +00:00
$this -> assertContains ( 'documents' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'collections' , $response [ 'data' ][ 'channels' ]);
2021-12-16 18:12:06 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'user' ]);
$this -> assertEquals ( $user [ '$id' ], $response [ 'data' ][ 'user' ][ '$id' ]);
2022-06-22 10:51:49 +00:00
/**
* Test Database Create
*/
$database = $this -> client -> call ( Client :: METHOD_POST , '/databases' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
2022-08-14 10:33:36 +00:00
'databaseId' => ID :: unique (),
2022-06-22 10:51:49 +00:00
'name' => 'Actors DB' ,
]);
$databaseId = $database [ 'body' ][ '$id' ];
2021-12-16 18:12:06 +00:00
/**
* Test Collection Create
*/
2022-06-22 10:51:49 +00:00
$actors = $this -> client -> call ( Client :: METHOD_POST , '/databases/' . $databaseId . '/collections' , array_merge ([
2021-12-16 18:12:06 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
2025-05-09 09:41:14 +00:00
'collectionId' => ID :: unique (),
2021-12-16 18:12:06 +00:00
'name' => 'Actors' ,
2022-08-03 04:17:49 +00:00
'permissions' => [
2022-08-14 05:21:11 +00:00
Permission :: read ( Role :: any ()),
Permission :: create ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
2022-08-03 04:17:49 +00:00
]
2021-12-16 18:12:06 +00:00
]);
2021-12-06 12:03:12 +00:00
2022-04-18 16:21:45 +00:00
$actorsId = $actors [ 'body' ][ '$id' ];
2021-12-06 12:03:12 +00:00
2022-06-22 10:51:49 +00:00
$name = $this -> client -> call ( Client :: METHOD_POST , '/databases/' . $databaseId . '/collections/' . $actorsId . '/attributes/string' , array_merge ([
2021-12-06 12:03:12 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
2021-12-16 16:10:01 +00:00
'key' => 'name' ,
2021-12-06 12:03:12 +00:00
'size' => 256 ,
'required' => true ,
]);
2025-05-09 09:41:14 +00:00
$this -> assertEquals ( 202 , $name [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'name' , $name [ 'body' ][ 'key' ]);
$this -> assertEquals ( 'string' , $name [ 'body' ][ 'type' ]);
$this -> assertEquals ( 256 , $name [ 'body' ][ 'size' ]);
$this -> assertTrue ( $name [ 'body' ][ 'required' ]);
2021-12-06 12:03:12 +00:00
sleep ( 2 );
/**
* Test Document Create
*/
2022-06-22 10:51:49 +00:00
$document = $this -> client -> call ( Client :: METHOD_POST , '/databases/' . $databaseId . '/collections/' . $actorsId . '/documents' , array_merge ([
2021-12-06 12:03:12 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
2025-05-09 09:41:14 +00:00
'documentId' => ID :: unique (),
2021-12-06 12:03:12 +00:00
'data' => [
'name' => 'Chris Evans'
],
2022-08-03 04:17:49 +00:00
'permissions' => [],
2021-12-06 12:03:12 +00:00
]);
2022-04-18 16:21:45 +00:00
$documentId = $document [ 'body' ][ '$id' ];
2021-12-06 12:03:12 +00:00
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
2025-05-09 09:46:10 +00:00
$this -> assertCount ( 6 , $response [ 'data' ][ 'channels' ]);
2025-05-09 09:41:14 +00:00
$this -> assertContains ( 'documents' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $documentId } " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $documentId } .create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $documentId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents. { $documentId } .create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents. { $documentId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response [ 'data' ][ 'events' ]);
2022-06-22 10:51:49 +00:00
$this -> assertContains ( " databases. { $databaseId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
2025-05-09 09:41:14 +00:00
$this -> assertEquals ( 'Chris Evans' , $response [ 'data' ][ 'payload' ][ 'name' ]);
2021-12-06 12:03:12 +00:00
/**
* Test Document Update
*/
2022-06-22 10:51:49 +00:00
$document = $this -> client -> call ( Client :: METHOD_PATCH , '/databases/' . $databaseId . '/collections/' . $actorsId . '/documents/' . $documentId , array_merge ([
2021-12-06 12:03:12 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'data' => [
'name' => 'Chris Evans 2'
],
2022-08-03 04:17:49 +00:00
'permissions' => [],
2021-12-06 12:03:12 +00:00
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
2025-05-09 09:46:10 +00:00
$this -> assertCount ( 6 , $response [ 'data' ][ 'channels' ]);
2025-05-09 09:41:14 +00:00
$this -> assertContains ( 'documents' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $documentId } " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $documentId } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $documentId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents. { $documentId } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents. { $documentId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response [ 'data' ][ 'events' ]);
2022-06-22 10:51:49 +00:00
$this -> assertContains ( " databases. { $databaseId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
2025-05-09 09:41:14 +00:00
$this -> assertEquals ( 'Chris Evans 2' , $response [ 'data' ][ 'payload' ][ 'name' ]);
2021-12-06 12:03:12 +00:00
/**
* Test Document Delete
*/
2022-06-22 10:51:49 +00:00
$document = $this -> client -> call ( Client :: METHOD_POST , '/databases/' . $databaseId . '/collections/' . $actorsId . '/documents' , array_merge ([
2021-12-06 12:03:12 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
2025-05-09 09:41:14 +00:00
'documentId' => ID :: unique (),
2021-12-06 12:03:12 +00:00
'data' => [
'name' => 'Bradley Cooper'
],
2022-08-03 04:17:49 +00:00
'permissions' => [],
2021-12-06 12:03:12 +00:00
]);
2022-04-18 16:21:45 +00:00
$documentId = $document [ 'body' ][ '$id' ];
2021-12-06 12:03:12 +00:00
$client -> receive ();
2022-06-22 10:51:49 +00:00
$this -> client -> call ( Client :: METHOD_DELETE , '/databases/' . $databaseId . '/collections/' . $actorsId . '/documents/' . $documentId , array_merge ([
2021-12-06 12:03:12 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()));
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
2025-05-09 09:46:10 +00:00
$this -> assertCount ( 6 , $response [ 'data' ][ 'channels' ]);
2025-05-09 09:41:14 +00:00
$this -> assertContains ( 'documents' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $documentId } " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $documentId } .delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents. { $documentId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } .documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections. { $actorsId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents. { $documentId } .delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents. { $documentId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.*.documents.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases. { $databaseId } .collections.* " , $response [ 'data' ][ 'events' ]);
2022-06-22 10:51:49 +00:00
$this -> assertContains ( " databases. { $databaseId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " databases.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
2025-05-09 09:41:14 +00:00
$this -> assertEquals ( 'Bradley Cooper' , $response [ 'data' ][ 'payload' ][ 'name' ]);
2021-12-06 12:03:12 +00:00
$client -> close ();
}
public function testChannelFiles ()
{
$user = $this -> getUser ();
$session = $user [ 'session' ] ? ? '' ;
$projectId = $this -> getProject ()[ '$id' ];
$client = $this -> getWebsocket ([ 'files' ], [
'origin' => 'http://localhost' ,
2022-04-18 16:21:45 +00:00
'cookie' => 'a_session_' . $projectId . '=' . $session
2021-12-06 12:03:12 +00:00
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertCount ( 1 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'files' , $response [ 'data' ][ 'channels' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'user' ]);
$this -> assertEquals ( $user [ '$id' ], $response [ 'data' ][ 'user' ][ '$id' ]);
2021-12-14 08:01:17 +00:00
/**
* Test Bucket Create
*/
$bucket1 = $this -> client -> call ( Client :: METHOD_POST , '/storage/buckets' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
2022-08-14 10:33:36 +00:00
'bucketId' => ID :: unique (),
2021-12-14 08:01:17 +00:00
'name' => 'Bucket 1' ,
2022-08-03 04:17:49 +00:00
'permissions' => [
2022-08-14 05:21:11 +00:00
Permission :: read ( Role :: any ()),
Permission :: create ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
2022-08-03 04:17:49 +00:00
]
2021-12-14 08:01:17 +00:00
]);
2022-04-18 16:21:45 +00:00
$bucketId = $bucket1 [ 'body' ][ '$id' ];
2021-12-06 12:03:12 +00:00
/**
* Test File Create
*/
2022-04-18 16:21:45 +00:00
$file = $this -> client -> call ( Client :: METHOD_POST , '/storage/buckets/' . $bucketId . '/files' , array_merge ([
2021-12-06 12:03:12 +00:00
'content-type' => 'multipart/form-data' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
2022-08-14 10:33:36 +00:00
'fileId' => ID :: unique (),
2021-12-06 12:03:12 +00:00
'file' => new CURLFile ( realpath ( __DIR__ . '/../../../resources/logo.png' ), 'image/png' , 'logo.png' ),
2022-08-03 04:17:49 +00:00
'permissions' => [
2022-08-14 05:21:11 +00:00
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
2022-08-03 04:17:49 +00:00
],
2021-12-06 12:03:12 +00:00
]);
2022-04-18 16:21:45 +00:00
$fileId = $file [ 'body' ][ '$id' ];
2021-12-06 12:03:12 +00:00
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
2021-12-14 08:01:17 +00:00
$this -> assertCount ( 3 , $response [ 'data' ][ 'channels' ]);
2021-12-06 12:03:12 +00:00
$this -> assertContains ( 'files' , $response [ 'data' ][ 'channels' ]);
2022-04-18 16:21:45 +00:00
$this -> assertContains ( " buckets. { $bucketId } .files. { $fileId } " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " buckets. { $bucketId } .files " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " buckets. { $bucketId } .files. { $fileId } .create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets. { $bucketId } .files. { $fileId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets. { $bucketId } .files.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets. { $bucketId } .files.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets. { $bucketId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets.*.files. { $fileId } .create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets.*.files. { $fileId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets.*.files.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets.*.files.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
2022-04-18 16:21:45 +00:00
$fileId = $file [ 'body' ][ '$id' ];
2021-12-06 12:03:12 +00:00
/**
* Test File Update
*/
2022-04-18 16:21:45 +00:00
$this -> client -> call ( Client :: METHOD_PUT , '/storage/buckets/' . $bucketId . '/files/' . $fileId , array_merge ([
2021-12-06 12:03:12 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
2022-08-03 04:17:49 +00:00
'permissions' => [
2022-08-14 05:21:11 +00:00
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
2022-08-03 04:17:49 +00:00
],
2021-12-06 12:03:12 +00:00
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
2021-12-14 08:01:17 +00:00
$this -> assertCount ( 3 , $response [ 'data' ][ 'channels' ]);
2021-12-06 12:03:12 +00:00
$this -> assertContains ( 'files' , $response [ 'data' ][ 'channels' ]);
2022-04-18 16:21:45 +00:00
$this -> assertContains ( " buckets. { $bucketId } .files. { $fileId } " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " buckets. { $bucketId } .files " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " buckets. { $bucketId } .files. { $fileId } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets. { $bucketId } .files. { $fileId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets. { $bucketId } .files.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets. { $bucketId } .files.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets. { $bucketId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets.*.files. { $fileId } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets.*.files. { $fileId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets.*.files.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets.*.files.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
/**
* Test File Delete
*/
2022-04-18 16:21:45 +00:00
$this -> client -> call ( Client :: METHOD_DELETE , '/storage/buckets/' . $bucketId . '/files/' . $fileId , array_merge ([
2021-12-06 12:03:12 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()));
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
2021-12-14 08:01:17 +00:00
$this -> assertCount ( 3 , $response [ 'data' ][ 'channels' ]);
2021-12-06 12:03:12 +00:00
$this -> assertContains ( 'files' , $response [ 'data' ][ 'channels' ]);
2022-04-18 16:21:45 +00:00
$this -> assertContains ( " buckets. { $bucketId } .files. { $fileId } " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " buckets. { $bucketId } .files " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " buckets. { $bucketId } .files. { $fileId } .delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets. { $bucketId } .files. { $fileId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets. { $bucketId } .files.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets. { $bucketId } .files.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets. { $bucketId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets.*.files. { $fileId } .delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets.*.files. { $fileId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets.*.files.*.delete " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets.*.files.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " buckets.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$client -> close ();
}
public function testChannelExecutions ()
{
$user = $this -> getUser ();
$session = $user [ 'session' ] ? ? '' ;
$projectId = $this -> getProject ()[ '$id' ];
$client = $this -> getWebsocket ([ 'executions' ], [
'origin' => 'http://localhost' ,
2022-04-18 16:21:45 +00:00
'cookie' => 'a_session_' . $projectId . '=' . $session
2021-12-06 12:03:12 +00:00
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertCount ( 1 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'executions' , $response [ 'data' ][ 'channels' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'user' ]);
$this -> assertEquals ( $user [ '$id' ], $response [ 'data' ][ 'user' ][ '$id' ]);
/**
* Test Functions Create
*/
$function = $this -> client -> call ( Client :: METHOD_POST , '/functions' , [
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
], [
2022-08-14 10:33:36 +00:00
'functionId' => ID :: unique (),
2025-07-09 14:58:55 +00:00
'name' => 'Test timeout execution' ,
2022-08-03 04:17:49 +00:00
'execute' => [ 'users' ],
2025-07-09 14:58:55 +00:00
'runtime' => 'node-22' ,
'entrypoint' => 'index.js' ,
2021-12-06 12:03:12 +00:00
'timeout' => 10 ,
]);
$functionId = $function [ 'body' ][ '$id' ] ? ? '' ;
2025-05-09 09:41:14 +00:00
$this -> assertEquals ( 201 , $function [ 'headers' ][ 'status-code' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $function [ 'body' ][ '$id' ]);
2022-04-18 16:21:45 +00:00
$deployment = $this -> client -> call ( Client :: METHOD_POST , '/functions/' . $functionId . '/deployments' , array_merge ([
2021-12-06 12:03:12 +00:00
'content-type' => 'multipart/form-data' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
2024-09-20 08:33:26 +00:00
'code' => $this -> packageFunction ( 'timeout' ),
2023-03-01 12:00:36 +00:00
'activate' => true
2021-12-06 12:03:12 +00:00
]);
2022-01-24 23:46:43 +00:00
$deploymentId = $deployment [ 'body' ][ '$id' ] ? ? '' ;
2021-12-06 12:03:12 +00:00
2025-05-09 09:41:14 +00:00
$this -> assertEquals ( 202 , $deployment [ 'headers' ][ 'status-code' ]);
2022-01-24 23:46:43 +00:00
$this -> assertNotEmpty ( $deployment [ 'body' ][ '$id' ]);
2021-12-06 12:03:12 +00:00
2023-08-29 03:15:02 +00:00
// Poll until deployment is built
2025-03-03 16:07:33 +00:00
$this -> assertEventually ( function () use ( $function , $deploymentId ) {
2023-08-29 03:15:02 +00:00
$deployment = $this -> client -> call ( Client :: METHOD_GET , '/functions/' . $function [ 'body' ][ '$id' ] . '/deployments/' . $deploymentId , [
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
]);
2025-03-03 16:07:33 +00:00
$this -> assertEquals ( 'ready' , $deployment [ 'body' ][ 'status' ], \json_encode ( $deployment [ 'body' ]));
});
2022-01-06 09:45:56 +00:00
2022-04-18 16:21:45 +00:00
$response = $this -> client -> call ( Client :: METHOD_PATCH , '/functions/' . $functionId . '/deployments/' . $deploymentId , array_merge ([
2021-12-06 12:03:12 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
2022-02-15 09:16:32 +00:00
]), []);
2021-12-06 12:03:12 +00:00
2024-05-06 07:55:11 +00:00
$this -> assertEquals ( 200 , $response [ 'headers' ][ 'status-code' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'body' ][ '$id' ]);
2022-04-18 16:21:45 +00:00
$execution = $this -> client -> call ( Client :: METHOD_POST , '/functions/' . $functionId . '/executions' , array_merge ([
2021-12-06 12:03:12 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ]
2022-09-07 08:24:49 +00:00
], $this -> getHeaders ()), [
'async' => true
]);
2021-12-06 12:03:12 +00:00
2024-05-06 07:55:11 +00:00
$this -> assertEquals ( 202 , $execution [ 'headers' ][ 'status-code' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $execution [ 'body' ][ '$id' ]);
$response = json_decode ( $client -> receive (), true );
$responseUpdate = json_decode ( $client -> receive (), true );
2022-04-18 16:21:45 +00:00
$executionId = $execution [ 'body' ][ '$id' ];
2021-12-06 12:03:12 +00:00
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
2024-09-30 14:53:25 +00:00
$this -> assertCount ( 5 , $response [ 'data' ][ 'channels' ]);
2022-02-28 15:17:37 +00:00
$this -> assertContains ( 'console' , $response [ 'data' ][ 'channels' ]);
2024-09-30 14:53:25 +00:00
$this -> assertContains ( " projects. { $this -> getProject ()[ '$id' ] } " , $response [ 'data' ][ 'channels' ]);
2021-12-06 12:03:12 +00:00
$this -> assertContains ( 'executions' , $response [ 'data' ][ 'channels' ]);
2022-04-18 16:21:45 +00:00
$this -> assertContains ( " executions. { $executionId } " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " functions. { $functionId } " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " functions. { $functionId } .executions. { $executionId } .create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " functions. { $functionId } .executions. { $executionId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " functions. { $functionId } .executions.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " functions. { $functionId } .executions.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " functions. { $functionId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " functions.*.executions. { $executionId } .create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " functions.*.executions. { $executionId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " functions.*.executions.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " functions.*.executions.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " functions.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$this -> assertArrayHasKey ( 'type' , $responseUpdate );
$this -> assertArrayHasKey ( 'data' , $responseUpdate );
$this -> assertEquals ( 'event' , $responseUpdate [ 'type' ]);
$this -> assertNotEmpty ( $responseUpdate [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $responseUpdate [ 'data' ]);
2024-09-30 14:53:25 +00:00
$this -> assertCount ( 5 , $responseUpdate [ 'data' ][ 'channels' ]);
2022-02-28 15:42:52 +00:00
$this -> assertContains ( 'console' , $responseUpdate [ 'data' ][ 'channels' ]);
2024-09-30 14:53:25 +00:00
$this -> assertContains ( " projects. { $this -> getProject ()[ '$id' ] } " , $response [ 'data' ][ 'channels' ]);
2021-12-06 12:03:12 +00:00
$this -> assertContains ( 'executions' , $responseUpdate [ 'data' ][ 'channels' ]);
2022-04-18 16:21:45 +00:00
$this -> assertContains ( " executions. { $executionId } " , $responseUpdate [ 'data' ][ 'channels' ]);
$this -> assertContains ( " functions. { $functionId } " , $responseUpdate [ 'data' ][ 'channels' ]);
$this -> assertContains ( " functions. { $functionId } .executions. { $executionId } .update " , $responseUpdate [ 'data' ][ 'events' ]);
$this -> assertContains ( " functions. { $functionId } .executions. { $executionId } " , $responseUpdate [ 'data' ][ 'events' ]);
$this -> assertContains ( " functions. { $functionId } .executions.*.update " , $responseUpdate [ 'data' ][ 'events' ]);
$this -> assertContains ( " functions. { $functionId } .executions.* " , $responseUpdate [ 'data' ][ 'events' ]);
$this -> assertContains ( " functions. { $functionId } " , $responseUpdate [ 'data' ][ 'events' ]);
$this -> assertContains ( " functions.*.executions. { $executionId } .update " , $responseUpdate [ 'data' ][ 'events' ]);
$this -> assertContains ( " functions.*.executions. { $executionId } " , $responseUpdate [ 'data' ][ 'events' ]);
$this -> assertContains ( " functions.*.executions.*.update " , $responseUpdate [ 'data' ][ 'events' ]);
$this -> assertContains ( " functions.*.executions.* " , $responseUpdate [ 'data' ][ 'events' ]);
$this -> assertContains ( " functions.* " , $responseUpdate [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $responseUpdate [ 'data' ][ 'payload' ]);
$client -> close ();
2022-02-09 20:20:28 +00:00
2022-05-23 14:54:50 +00:00
// Cleanup : Delete function
2022-04-18 16:21:45 +00:00
$response = $this -> client -> call ( Client :: METHOD_DELETE , '/functions/' . $functionId , [
2022-02-09 20:20:28 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ],
], []);
$this -> assertEquals ( 204 , $response [ 'headers' ][ 'status-code' ]);
2021-12-06 12:03:12 +00:00
}
public function testChannelTeams () : array
{
$user = $this -> getUser ();
$session = $user [ 'session' ] ? ? '' ;
$projectId = $this -> getProject ()[ '$id' ];
$client = $this -> getWebsocket ([ 'teams' ], [
'origin' => 'http://localhost' ,
2022-04-18 16:21:45 +00:00
'cookie' => 'a_session_' . $projectId . '=' . $session
2021-12-06 12:03:12 +00:00
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertCount ( 1 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'teams' , $response [ 'data' ][ 'channels' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'user' ]);
$this -> assertEquals ( $user [ '$id' ], $response [ 'data' ][ 'user' ][ '$id' ]);
/**
* Test Team Create
*/
$team = $this -> client -> call ( Client :: METHOD_POST , '/teams' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
], $this -> getHeaders ()), [
2022-08-14 10:33:36 +00:00
'teamId' => ID :: unique (),
2021-12-06 12:03:12 +00:00
'name' => 'Arsenal'
]);
$teamId = $team [ 'body' ][ '$id' ] ? ? '' ;
$this -> assertEquals ( 201 , $team [ 'headers' ][ 'status-code' ]);
$this -> assertNotEmpty ( $team [ 'body' ][ '$id' ]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertCount ( 2 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'teams' , $response [ 'data' ][ 'channels' ]);
2022-04-18 16:21:45 +00:00
$this -> assertContains ( " teams. { $teamId } " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " teams. { $teamId } .create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams. { $teamId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams.*.create " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
/**
* Test Team Update
*/
2022-04-18 16:21:45 +00:00
$team = $this -> client -> call ( Client :: METHOD_PUT , '/teams/' . $teamId , array_merge ([
2021-12-06 12:03:12 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
], $this -> getHeaders ()), [
'name' => 'Manchester'
]);
2025-05-09 09:41:14 +00:00
$this -> assertEquals ( 200 , $team [ 'headers' ][ 'status-code' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $team [ 'body' ][ '$id' ]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertCount ( 2 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'teams' , $response [ 'data' ][ 'channels' ]);
2022-04-18 16:21:45 +00:00
$this -> assertContains ( " teams. { $teamId } " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " teams. { $teamId } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams. { $teamId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
2023-03-06 14:24:02 +00:00
/**
* Test Team Update Prefs
*/
2023-03-23 12:06:48 +00:00
$team = $this -> client -> call ( Client :: METHOD_PUT , '/teams/' . $teamId . '/prefs' , array_merge ([
2023-03-06 14:24:02 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
], $this -> getHeaders ()), [
'prefs' => [
'funcKey1' => 'funcValue1' ,
'funcKey2' => 'funcValue2' ,
]
]);
2025-05-09 09:41:14 +00:00
$this -> assertEquals ( 200 , $team [ 'headers' ][ 'status-code' ]);
$this -> assertEquals ( 'funcValue1' , $team [ 'body' ][ 'funcKey1' ]);
$this -> assertEquals ( 'funcValue2' , $team [ 'body' ][ 'funcKey2' ]);
2023-03-06 14:24:02 +00:00
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertCount ( 2 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'teams' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " teams. { $teamId } " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " teams. { $teamId } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams. { $teamId } .update.prefs " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams. { $teamId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams.*.update.prefs " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams.* " , $response [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
2025-05-09 09:41:14 +00:00
$this -> assertEquals ( 'funcValue1' , $response [ 'data' ][ 'payload' ][ 'funcKey1' ]);
$this -> assertEquals ( 'funcValue2' , $response [ 'data' ][ 'payload' ][ 'funcKey2' ]);
2023-03-06 14:24:02 +00:00
2021-12-06 12:03:12 +00:00
$client -> close ();
return [ 'teamId' => $teamId ];
}
/**
* @ depends testChannelTeams
*/
public function testChannelMemberships ( array $data )
{
$teamId = $data [ 'teamId' ] ? ? '' ;
$user = $this -> getUser ();
$session = $user [ 'session' ] ? ? '' ;
$projectId = $this -> getProject ()[ '$id' ];
$client = $this -> getWebsocket ([ 'memberships' ], [
'origin' => 'http://localhost' ,
2022-04-18 16:21:45 +00:00
'cookie' => 'a_session_' . $projectId . '=' . $session
2021-12-06 12:03:12 +00:00
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertCount ( 1 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'memberships' , $response [ 'data' ][ 'channels' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'user' ]);
$this -> assertEquals ( $user [ '$id' ], $response [ 'data' ][ 'user' ][ '$id' ]);
2022-04-18 16:21:45 +00:00
$response = $this -> client -> call ( Client :: METHOD_GET , '/teams/' . $teamId . '/memberships' , array_merge ([
2021-12-06 12:03:12 +00:00
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()));
$membershipId = $response [ 'body' ][ 'memberships' ][ 0 ][ '$id' ];
/**
* Test Update Membership
*/
$roles = [ 'admin' , 'editor' , 'uncle' ];
2022-04-18 16:21:45 +00:00
$this -> client -> call ( Client :: METHOD_PATCH , '/teams/' . $teamId . '/memberships/' . $membershipId , array_merge ([
2021-12-06 12:03:12 +00:00
'origin' => 'http://localhost' ,
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'roles' => $roles
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertCount ( 2 , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( 'memberships' , $response [ 'data' ][ 'channels' ]);
2022-04-18 16:21:45 +00:00
$this -> assertContains ( " memberships. { $membershipId } " , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " teams. { $teamId } .memberships. { $membershipId } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams. { $teamId } .memberships. { $membershipId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams. { $teamId } .memberships.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams. { $teamId } .memberships.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams. { $teamId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams.*.memberships. { $membershipId } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams.*.memberships. { $membershipId } " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams.*.memberships.*.update " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams.*.memberships.* " , $response [ 'data' ][ 'events' ]);
$this -> assertContains ( " teams.* " , $response [ 'data' ][ 'events' ]);
2021-12-06 12:03:12 +00:00
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$client -> close ();
}
2025-10-04 03:48:44 +00:00
public function testChannelDatabaseTransaction ()
{
$user = $this -> getUser ();
$session = $user [ 'session' ] ? ? '' ;
$projectId = $this -> getProject ()[ '$id' ];
$client = $this -> getWebsocket ([ 'documents' ], [
'origin' => 'http://localhost' ,
'cookie' => 'a_session_' . $projectId . '=' . $session
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
/**
* Setup Database and Collection
*/
$database = $this -> client -> call ( Client :: METHOD_POST , '/databases' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'databaseId' => ID :: unique (),
'name' => 'Transactions DB' ,
]);
$databaseId = $database [ 'body' ][ '$id' ];
$collection = $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' ]
]), [
'collectionId' => ID :: unique (),
'name' => 'Test Collection' ,
'permissions' => [
Permission :: create ( Role :: user ( $this -> getUser ()[ '$id' ])),
],
'documentSecurity' => true ,
]);
$collectionId = $collection [ 'body' ][ '$id' ];
$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 ,
]);
sleep ( 2 );
/**
* Test Transaction Create with Single Document
*/
$transaction = $this -> client -> call ( Client :: METHOD_POST , '/tablesdb/transactions' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'ttl' => 3600 // 1 hour
]);
$this -> assertEquals ( 201 , $transaction [ 'headers' ][ 'status-code' ], 'Failed to create transaction: ' . json_encode ( $transaction [ 'body' ]));
$this -> assertNotEmpty ( $transaction [ 'body' ][ '$id' ]);
$transactionId = $transaction [ 'body' ][ '$id' ];
$documentId = ID :: unique ();
$operationsResponse = $this -> client -> call ( Client :: METHOD_POST , '/tablesdb/transactions/' . $transactionId . '/operations' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'operations' => [
[
'databaseId' => $databaseId ,
'tableId' => $collectionId ,
'rowId' => $documentId ,
'action' => 'create' ,
'data' => [
'name' => 'Transaction Document' ,
'$permissions' => [
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
],
]
]
]);
$this -> assertEquals ( 201 , $operationsResponse [ 'headers' ][ 'status-code' ], 'Failed to add operations: ' . json_encode ( $operationsResponse [ 'body' ]));
// Commit transaction
$commitResponse = $this -> client -> call ( Client :: METHOD_PATCH , '/tablesdb/transactions/' . $transactionId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'commit' => true
]);
$this -> assertEquals ( 200 , $commitResponse [ 'headers' ][ 'status-code' ], 'Failed to commit transaction: ' . json_encode ( $commitResponse [ 'body' ]));
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertArrayHasKey ( 'data' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertNotEmpty ( $response [ 'data' ]);
$this -> assertArrayHasKey ( 'timestamp' , $response [ 'data' ]);
$this -> assertContains ( 'documents' , $response [ 'data' ][ 'channels' ]);
$this -> assertContains ( " databases. { $databaseId } .tables. { $collectionId } .rows. { $documentId } .create " , $response [ 'data' ][ 'events' ]);
$this -> assertNotEmpty ( $response [ 'data' ][ 'payload' ]);
$this -> assertEquals ( 'Transaction Document' , $response [ 'data' ][ 'payload' ][ 'name' ]);
/**
* Test Transaction Update
*/
$transaction = $this -> client -> call ( Client :: METHOD_POST , '/tablesdb/transactions' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'ttl' => 3600
]);
$transactionId = $transaction [ 'body' ][ '$id' ];
$this -> client -> call ( Client :: METHOD_POST , '/tablesdb/transactions/' . $transactionId . '/operations' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'operations' => [
[
'databaseId' => $databaseId ,
'tableId' => $collectionId ,
'rowId' => $documentId ,
'action' => 'update' ,
'data' => [
'name' => 'Updated Transaction Document' ,
],
]
]
]);
$this -> client -> call ( Client :: METHOD_PATCH , '/tablesdb/transactions/' . $transactionId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'commit' => true
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertContains ( " databases. { $databaseId } .tables. { $collectionId } .rows. { $documentId } .update " , $response [ 'data' ][ 'events' ]);
$this -> assertEquals ( 'Updated Transaction Document' , $response [ 'data' ][ 'payload' ][ 'name' ]);
/**
* Test Transaction Delete
*/
$transaction = $this -> client -> call ( Client :: METHOD_POST , '/tablesdb/transactions' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'ttl' => 3600
]);
$transactionId = $transaction [ 'body' ][ '$id' ];
$this -> client -> call ( Client :: METHOD_POST , '/tablesdb/transactions/' . $transactionId . '/operations' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'operations' => [
[
'databaseId' => $databaseId ,
'tableId' => $collectionId ,
'rowId' => $documentId ,
'action' => 'delete' ,
]
]
]);
$this -> client -> call ( Client :: METHOD_PATCH , '/tablesdb/transactions/' . $transactionId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'commit' => true
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertEquals ( 'event' , $response [ 'type' ]);
$this -> assertContains ( " databases. { $databaseId } .tables. { $collectionId } .rows. { $documentId } .delete " , $response [ 'data' ][ 'events' ]);
$client -> close ();
}
public function testChannelDatabaseTransactionMultipleOperations ()
{
$user = $this -> getUser ();
$session = $user [ 'session' ] ? ? '' ;
$projectId = $this -> getProject ()[ '$id' ];
$client = $this -> getWebsocket ([ 'documents' ], [
'origin' => 'http://localhost' ,
'cookie' => 'a_session_' . $projectId . '=' . $session
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
/**
* Setup Database and Collection
*/
$database = $this -> client -> call ( Client :: METHOD_POST , '/databases' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'databaseId' => ID :: unique (),
'name' => 'Multi-Op DB' ,
]);
$databaseId = $database [ 'body' ][ '$id' ];
$collection = $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' ]
]), [
'collectionId' => ID :: unique (),
'name' => 'Test Collection' ,
'permissions' => [
Permission :: create ( Role :: user ( $this -> getUser ()[ '$id' ])),
],
'documentSecurity' => true ,
]);
$collectionId = $collection [ 'body' ][ '$id' ];
$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 ,
]);
sleep ( 2 );
/**
* Test Multiple Operations in Single Transaction
*/
$transaction = $this -> client -> call ( Client :: METHOD_POST , '/tablesdb/transactions' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'ttl' => 3600
]);
$transactionId = $transaction [ 'body' ][ '$id' ];
$documentId1 = ID :: unique ();
$documentId2 = ID :: unique ();
$documentId3 = ID :: unique ();
$this -> client -> call ( Client :: METHOD_POST , '/tablesdb/transactions/' . $transactionId . '/operations' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'operations' => [
[
'databaseId' => $databaseId ,
'tableId' => $collectionId ,
'rowId' => $documentId1 ,
'action' => 'create' ,
'data' => [
'name' => 'Doc 1' ,
'$permissions' => [
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
],
],
[
'databaseId' => $databaseId ,
'tableId' => $collectionId ,
'rowId' => $documentId2 ,
'action' => 'create' ,
'data' => [
'name' => 'Doc 2' ,
'$permissions' => [
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
],
],
[
'databaseId' => $databaseId ,
'tableId' => $collectionId ,
'rowId' => $documentId3 ,
'action' => 'create' ,
'data' => [
'name' => 'Doc 3' ,
'$permissions' => [
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
],
]
]
]);
$this -> client -> call ( Client :: METHOD_PATCH , '/tablesdb/transactions/' . $transactionId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'commit' => true
]);
// Should receive 3 events, one for each document
$response1 = json_decode ( $client -> receive (), true );
$response2 = json_decode ( $client -> receive (), true );
$response3 = json_decode ( $client -> receive (), true );
$this -> assertEquals ( 'event' , $response1 [ 'type' ]);
$this -> assertEquals ( 'event' , $response2 [ 'type' ]);
$this -> assertEquals ( 'event' , $response3 [ 'type' ]);
$receivedDocIds = [
$response1 [ 'data' ][ 'payload' ][ '$id' ],
$response2 [ 'data' ][ 'payload' ][ '$id' ],
$response3 [ 'data' ][ 'payload' ][ '$id' ],
];
$this -> assertContains ( $documentId1 , $receivedDocIds );
$this -> assertContains ( $documentId2 , $receivedDocIds );
$this -> assertContains ( $documentId3 , $receivedDocIds );
$client -> close ();
}
public function testChannelDatabaseTransactionRollback ()
{
$user = $this -> getUser ();
$session = $user [ 'session' ] ? ? '' ;
$projectId = $this -> getProject ()[ '$id' ];
$client = $this -> getWebsocket ([ 'documents' ], [
'origin' => 'http://localhost' ,
'cookie' => 'a_session_' . $projectId . '=' . $session
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
/**
* Setup Database and Collection
*/
$database = $this -> client -> call ( Client :: METHOD_POST , '/databases' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'databaseId' => ID :: unique (),
'name' => 'Rollback DB' ,
]);
$databaseId = $database [ 'body' ][ '$id' ];
$collection = $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' ]
]), [
'collectionId' => ID :: unique (),
'name' => 'Test Collection' ,
'permissions' => [
Permission :: create ( Role :: user ( $this -> getUser ()[ '$id' ])),
],
'documentSecurity' => true ,
]);
$collectionId = $collection [ 'body' ][ '$id' ];
$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 ,
]);
sleep ( 2 );
/**
* Test Transaction Rollback - Should NOT trigger realtime events
*/
$transaction = $this -> client -> call ( Client :: METHOD_POST , '/tablesdb/transactions' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'ttl' => 3600
]);
$transactionId = $transaction [ 'body' ][ '$id' ];
$documentId = ID :: unique ();
$this -> client -> call ( Client :: METHOD_POST , '/tablesdb/transactions/' . $transactionId . '/operations' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'operations' => [
[
'databaseId' => $databaseId ,
'tableId' => $collectionId ,
'rowId' => $documentId ,
'action' => 'create' ,
'data' => [ 'name' => 'Rollback Document' ],
]
]
]);
// Rollback transaction
$this -> client -> call ( Client :: METHOD_PATCH , '/tablesdb/transactions/' . $transactionId , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $this -> getProject ()[ '$id' ],
], $this -> getHeaders ()), [
'rollback' => true
]);
// Wait a bit to ensure no event is received
sleep ( 1 );
try {
2025-12-24 13:26:55 +00:00
$client -> receive ();
2025-10-04 03:48:44 +00:00
$this -> fail ( 'Should not receive any event after rollback' );
} catch ( TimeoutException $e ) {
// Expected - no event should be triggered
$this -> assertTrue ( true );
}
$client -> close ();
}
2025-10-04 04:03:52 +00:00
2025-10-02 12:57:20 +00:00
public function testRelationshipPayloadHidesRelatedDoc ()
{
$user = $this -> getUser ();
$session = $user [ 'session' ] ? ? '' ;
$projectId = $this -> getProject ()[ '$id' ];
$client = $this -> getWebsocket ([ 'documents' ], [
'origin' => 'http://localhost' ,
'cookie' => 'a_session_' . $projectId . '=' . $session
]);
$response = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $response );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
// Create database
$database = $this -> client -> call ( Client :: METHOD_POST , '/databases' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'databaseId' => ID :: unique (),
'name' => 'db-rel'
]);
$databaseId = $database [ 'body' ][ '$id' ];
$level1 = $this -> client -> call ( Client :: METHOD_POST , " /databases/ { $databaseId } /collections " , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'collectionId' => ID :: unique (),
'name' => 'level1' ,
'permissions' => [
Permission :: read ( Role :: any ()),
Permission :: create ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
'documentSecurity' => true ,
]);
$level1Id = $level1 [ 'body' ][ '$id' ];
$level2 = $this -> client -> call ( Client :: METHOD_POST , " /databases/ { $databaseId } /collections " , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'collectionId' => ID :: unique (),
'name' => 'level2' ,
'permissions' => [
Permission :: read ( Role :: any ()),
Permission :: create ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
'documentSecurity' => true ,
]);
$level2Id = $level2 [ 'body' ][ '$id' ];
$this -> client -> call ( Client :: METHOD_POST , " /databases/ { $databaseId } /collections/ { $level1Id } /attributes/string " , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'key' => 'name' ,
'size' => 256 ,
'required' => false ,
]);
$this -> client -> call ( Client :: METHOD_POST , " /databases/ { $databaseId } /collections/ { $level2Id } /attributes/string " , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'key' => 'name' ,
'size' => 256 ,
'required' => false ,
]);
sleep ( 2 );
// two-way one-to-one relationship from level1 to level2
$this -> client -> call ( Client :: METHOD_POST , " /databases/ { $databaseId } /collections/ { $level1Id } /attributes/relationship " , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'relatedCollectionId' => $level2Id ,
'type' => 'oneToOne' ,
'twoWay' => true ,
'key' => 'level2Ref' ,
'onDelete' => 'cascade' ,
]);
sleep ( 2 );
$doc2 = $this -> client -> call ( Client :: METHOD_POST , " /databases/ { $databaseId } /collections/ { $level2Id } /documents " , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
], $this -> getHeaders ()), [
'documentId' => ID :: unique (),
2025-10-04 06:26:22 +00:00
'data' => [ 'name' => 'L2' ],
2025-10-02 12:57:20 +00:00
'permissions' => [
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
]);
$doc2Id = $doc2 [ 'body' ][ '$id' ];
$doc1 = $this -> client -> call ( Client :: METHOD_POST , " /databases/ { $databaseId } /collections/ { $level1Id } /documents " , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
], $this -> getHeaders ()), [
'documentId' => ID :: unique (),
2025-10-04 06:26:22 +00:00
'data' => [ 'name' => 'L1' ],
2025-10-02 12:57:20 +00:00
'permissions' => [
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
]);
$doc1Id = $doc1 [ 'body' ][ '$id' ];
json_decode ( $client -> receive (), true );
$this -> client -> call ( Client :: METHOD_PATCH , " /databases/ { $databaseId } /collections/ { $level1Id } /documents/ { $doc1Id } " , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'data' => [
'level2Ref' => $doc2Id ,
],
]);
// payload should not contain the relationship attribute 'level2Ref'
$event = json_decode ( $client -> receive (), true );
$this -> assertArrayHasKey ( 'type' , $event );
$this -> assertEquals ( 'event' , $event [ 'type' ]);
$this -> assertArrayHasKey ( 'data' , $event );
$this -> assertNotEmpty ( $event [ 'data' ]);
$this -> assertArrayHasKey ( 'payload' , $event [ 'data' ]);
$this -> assertArrayHasKey ( '$id' , $event [ 'data' ][ 'payload' ]);
$this -> assertEquals ( $doc1Id , $event [ 'data' ][ 'payload' ][ '$id' ]);
$this -> assertArrayNotHasKey ( 'level2Ref' , $event [ 'data' ][ 'payload' ]);
$client -> close ();
}
2025-12-24 08:11:31 +00:00
/**
* Simulate concurrent realtime traffic using Swoole coroutines .
* Opens multiple websocket clients concurrently , then performs create / update / delete ops .
*/
public function testConcurrentRealtimeTrafficCoroutines ()
{
if ( ! class_exists ( \Swoole\Coroutine :: class )) {
$this -> markTestSkipped ( 'Swoole Coroutine not available in this environment.' );
}
$user = $this -> getUser ();
$session = $user [ 'session' ] ? ? '' ;
$projectId = $this -> getProject ()[ '$id' ];
Coroutine\run ( function () use ( $session , $projectId ) {
$headers = [
'origin' => 'http://localhost' ,
'cookie' => 'a_session_' . $projectId . '=' . $session
];
$clientCount = 5 ;
$clients = [];
for ( $i = 0 ; $i < $clientCount ; $i ++ ) {
$clients [] = $this -> getWebsocket ([ 'documents' , 'collections' ], $headers );
}
foreach ( $clients as $client ) {
$response = json_decode ( $client -> receive (), true );
$this -> assertEquals ( 'connected' , $response [ 'type' ]);
}
// Setup DB/collection/attribute
$database = $this -> client -> call ( Client :: METHOD_POST , '/databases' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'databaseId' => ID :: unique (),
'name' => 'Concurrent DB' ,
]);
$databaseId = $database [ 'body' ][ '$id' ];
$collection = $this -> client -> call ( Client :: METHOD_POST , '/databases/' . $databaseId . '/collections' , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'collectionId' => ID :: unique (),
'name' => 'Concurrent Collection' ,
'permissions' => [
Permission :: create ( Role :: user ( $this -> getUser ()[ '$id' ])),
],
'documentSecurity' => true ,
]);
$collectionId = $collection [ 'body' ][ '$id' ];
$this -> client -> call ( Client :: METHOD_POST , " /databases/ { $databaseId } /collections/ { $collectionId } /attributes/string " , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'key' => 'name' ,
'size' => 64 ,
'required' => true ,
]);
Coroutine :: sleep ( 1 );
$creates = [
[ 'name' => 'Doc A' ],
[ 'name' => 'Doc B' ],
[ 'name' => 'Doc C' ],
[ 'name' => 'Doc D' ],
[ 'name' => 'Doc E' ],
[ 'name' => 'Doc F' ],
];
$expectedEvents = count ( $creates );
// Per-client receipts
$receivedEvents = array_fill ( 0 , $clientCount , []);
// Launch receiver coroutines (one per client)
foreach ( $clients as $idx => $client ) {
Coroutine :: create ( function () use ( $client , & $receivedEvents , $expectedEvents , $idx ) {
$local = [];
for ( $i = 0 ; $i < $expectedEvents ; $i ++ ) {
$event = json_decode ( $client -> receive (), true );
$local [] = $event ;
}
$receivedEvents [ $idx ] = $local ;
});
}
// Create docs
foreach ( $creates as $payload ) {
$this -> client -> call ( Client :: METHOD_POST , " /databases/ { $databaseId } /collections/ { $collectionId } /documents " , array_merge ([
'content-type' => 'application/json' ,
'x-appwrite-project' => $projectId ,
'x-appwrite-key' => $this -> getProject ()[ 'apiKey' ]
]), [
'documentId' => ID :: unique (),
'data' => $payload ,
'permissions' => [
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
]);
}
// Wait for receivers to collect; timeout ~10s
$deadline = microtime ( true ) + 10 ;
while ( microtime ( true ) < $deadline ) {
$done = true ;
foreach ( $receivedEvents as $events ) {
if ( count ( $events ) < $expectedEvents ) {
$done = false ;
break ;
}
}
if ( $done ) {
break ;
}
Coroutine :: sleep ( 0.1 );
}
$expectedNames = array_column ( $creates , 'name' );
for ( $c = 0 ; $c < $clientCount ; $c ++ ) {
$events = $receivedEvents [ $c ];
$this -> assertCount ( $expectedEvents , $events , 'Unexpected event count on client ' . $c );
$seen = [];
foreach ( $events as $event ) {
$this -> assertEquals ( 'event' , $event [ 'type' ]);
$this -> assertArrayHasKey ( 'payload' , $event [ 'data' ]);
$seen [] = $event [ 'data' ][ 'payload' ][ 'name' ] ? ? '' ;
}
foreach ( $expectedNames as $name ) {
$this -> assertContains ( $name , $seen );
}
}
foreach ( $clients as $client ) {
$client -> close ();
}
});
}
2022-04-18 16:21:45 +00:00
}