mirror of
https://github.com/appwrite/appwrite
synced 2026-04-26 16:07:32 +00:00
1547 lines
60 KiB
PHP
1547 lines
60 KiB
PHP
<?php
|
|
|
|
namespace Tests\E2E\Services\Realtime;
|
|
|
|
use CURLFile;
|
|
use Tests\E2E\Client;
|
|
use Tests\E2E\Scopes\ProjectCustom;
|
|
use Tests\E2E\Scopes\Scope;
|
|
use Tests\E2E\Scopes\SideClient;
|
|
use Tests\E2E\Services\Functions\FunctionsBase;
|
|
use Utopia\Database\Helpers\ID;
|
|
use Utopia\Database\Helpers\Permission;
|
|
use Utopia\Database\Helpers\Role;
|
|
use Utopia\Database\Query;
|
|
use WebSocket\TimeoutException;
|
|
|
|
class RealtimeCustomClientQueryTest extends Scope
|
|
{
|
|
use FunctionsBase;
|
|
use RealtimeBase;
|
|
use ProjectCustom;
|
|
use SideClient;
|
|
|
|
public function testAccountChannelWithQuery()
|
|
{
|
|
$user = $this->getUser();
|
|
$userId = $user['$id'] ?? '';
|
|
$session = $user['session'] ?? '';
|
|
$projectId = $this->getProject()['$id'];
|
|
|
|
// Subscribe with query that matches current user
|
|
$client = $this->getWebsocket(['account'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::equal('$id', [$userId])->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('connected', $response['type']);
|
|
|
|
// Update account name - should receive event (matches query)
|
|
$name = "Test User " . uniqid();
|
|
$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
|
|
]);
|
|
|
|
$event = json_decode($client->receive(), true);
|
|
$this->assertEquals('event', $event['type']);
|
|
$this->assertEquals($name, $event['data']['payload']['name']);
|
|
|
|
$client->close();
|
|
|
|
|
|
$user = $this->getUser();
|
|
$userId = $user['$id'] ?? '';
|
|
$session = $user['session'] ?? '';
|
|
$projectId = $this->getProject()['$id'];
|
|
|
|
// Subscribe with query that does NOT match current user
|
|
$client = $this->getWebsocket(['account'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::notEqual('$id', [$userId])->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('connected', $response['type']);
|
|
|
|
// Update account name - should NOT receive event (doesn't match query)
|
|
$name = "Test User " . uniqid();
|
|
$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
|
|
]);
|
|
|
|
// Should timeout - no event should be received
|
|
try {
|
|
$client->receive();
|
|
$this->fail('Expected TimeoutException - event should be filtered');
|
|
} catch (TimeoutException $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
$client->close();
|
|
}
|
|
|
|
public function testDatabaseChannelWithQuery()
|
|
{
|
|
$user = $this->getUser();
|
|
$session = $user['session'] ?? '';
|
|
$projectId = $this->getProject()['$id'];
|
|
|
|
// Setup database and collection
|
|
$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' => 'Query Test 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' => 'Test Collection',
|
|
'permissions' => [
|
|
Permission::create(Role::user($user['$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' => 'status',
|
|
'size' => 256,
|
|
'required' => false,
|
|
]);
|
|
|
|
sleep(2);
|
|
|
|
$targetDocumentId = ID::unique();
|
|
|
|
// Subscribe with query for specific document ID
|
|
$client = $this->getWebsocket(['documents'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::equal('$id', [$targetDocumentId])->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('connected', $response['type']);
|
|
|
|
// Create document with matching ID - should receive event
|
|
$document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => $targetDocumentId,
|
|
'data' => [
|
|
'status' => 'active'
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
$event = json_decode($client->receive(), true);
|
|
$this->assertEquals('event', $event['type']);
|
|
$this->assertEquals($targetDocumentId, $event['data']['payload']['$id']);
|
|
|
|
// Create document with different ID - should NOT receive event
|
|
$otherDocumentId = ID::unique();
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => $otherDocumentId,
|
|
'data' => [
|
|
'status' => 'inactive'
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
try {
|
|
$client->receive();
|
|
$this->fail('Expected TimeoutException - event should be filtered');
|
|
} catch (TimeoutException $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
$client->close();
|
|
|
|
// Setup database and collection
|
|
$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' => 'NotEqual Test 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' => 'Test Collection',
|
|
'permissions' => [
|
|
Permission::create(Role::user($user['$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' => 'status',
|
|
'size' => 256,
|
|
'required' => false,
|
|
]);
|
|
|
|
sleep(2);
|
|
|
|
$excludedDocumentId = ID::unique();
|
|
|
|
// Subscribe with query that excludes specific document ID
|
|
$client = $this->getWebsocket(['documents'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::notEqual('$id', [$excludedDocumentId])->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('connected', $response['type']);
|
|
|
|
// Create document with different ID - should receive event
|
|
$allowedDocumentId = ID::unique();
|
|
$document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => $allowedDocumentId,
|
|
'data' => [
|
|
'status' => 'active'
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
$event = json_decode($client->receive(), true);
|
|
$this->assertEquals('event', $event['type']);
|
|
$this->assertEquals($allowedDocumentId, $event['data']['payload']['$id']);
|
|
|
|
// Create document with excluded ID - should NOT receive event
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => $excludedDocumentId,
|
|
'data' => [
|
|
'status' => 'inactive'
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
try {
|
|
$client->receive();
|
|
$this->fail('Expected TimeoutException - event should be filtered');
|
|
} catch (TimeoutException $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
$client->close();
|
|
|
|
// Setup database and collection
|
|
$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' => 'GreaterThan Test 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' => 'Test Collection',
|
|
'permissions' => [
|
|
Permission::create(Role::user($user['$id'])),
|
|
],
|
|
'documentSecurity' => true,
|
|
]);
|
|
$collectionId = $collection['body']['$id'];
|
|
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
]), [
|
|
'key' => 'score',
|
|
'required' => false,
|
|
]);
|
|
|
|
sleep(2);
|
|
|
|
// Subscribe with query for score > 50
|
|
$client = $this->getWebsocket(['documents'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::greaterThan('score', 50)->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('connected', $response['type']);
|
|
|
|
// Create document with score > 50 - should receive event
|
|
$document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'score' => 75
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
$event = json_decode($client->receive(), true);
|
|
$this->assertEquals('event', $event['type']);
|
|
$this->assertEquals(75, $event['data']['payload']['score']);
|
|
|
|
// Create document with score <= 50 - should NOT receive event
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'score' => 30
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
try {
|
|
$client->receive();
|
|
$this->fail('Expected TimeoutException - event should be filtered');
|
|
} catch (TimeoutException $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
$client->close();
|
|
|
|
// Setup database and collection
|
|
$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' => 'LesserThan Test 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' => 'Test Collection',
|
|
'permissions' => [
|
|
Permission::create(Role::user($user['$id'])),
|
|
],
|
|
'documentSecurity' => true,
|
|
]);
|
|
$collectionId = $collection['body']['$id'];
|
|
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
]), [
|
|
'key' => 'age',
|
|
'required' => false,
|
|
]);
|
|
|
|
sleep(2);
|
|
|
|
// Subscribe with query for age < 18
|
|
$client = $this->getWebsocket(['documents'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::lessThan('age', 18)->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('connected', $response['type']);
|
|
|
|
// Create document with age < 18 - should receive event
|
|
$document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'age' => 15
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
$event = json_decode($client->receive(), true);
|
|
$this->assertEquals('event', $event['type']);
|
|
$this->assertEquals(15, $event['data']['payload']['age']);
|
|
|
|
// Create document with age >= 18 - should NOT receive event
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'age' => 25
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
try {
|
|
$client->receive();
|
|
$this->fail('Expected TimeoutException - event should be filtered');
|
|
} catch (TimeoutException $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
$client->close();
|
|
|
|
// Setup database and collection
|
|
$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' => 'GreaterEqual Test 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' => 'Test Collection',
|
|
'permissions' => [
|
|
Permission::create(Role::user($user['$id'])),
|
|
],
|
|
'documentSecurity' => true,
|
|
]);
|
|
$collectionId = $collection['body']['$id'];
|
|
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
]), [
|
|
'key' => 'priority',
|
|
'required' => false,
|
|
]);
|
|
|
|
sleep(2);
|
|
|
|
// Subscribe with query for priority >= 5
|
|
$client = $this->getWebsocket(['documents'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::greaterThanEqual('priority', 5)->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('connected', $response['type']);
|
|
|
|
// Create document with priority = 5 - should receive event
|
|
$document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'priority' => 5
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
$event = json_decode($client->receive(), true);
|
|
$this->assertEquals('event', $event['type']);
|
|
$this->assertEquals(5, $event['data']['payload']['priority']);
|
|
|
|
// Create document with priority > 5 - should receive event
|
|
$document2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'priority' => 8
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
$event = json_decode($client->receive(), true);
|
|
$this->assertEquals('event', $event['type']);
|
|
$this->assertEquals(8, $event['data']['payload']['priority']);
|
|
|
|
// Create document with priority < 5 - should NOT receive event
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'priority' => 3
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
try {
|
|
$client->receive();
|
|
$this->fail('Expected TimeoutException - event should be filtered');
|
|
} catch (TimeoutException $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
$client->close();
|
|
|
|
// Setup database and collection
|
|
$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' => 'LesserEqual Test 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' => 'Test Collection',
|
|
'permissions' => [
|
|
Permission::create(Role::user($user['$id'])),
|
|
],
|
|
'documentSecurity' => true,
|
|
]);
|
|
$collectionId = $collection['body']['$id'];
|
|
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
]), [
|
|
'key' => 'level',
|
|
'required' => false,
|
|
]);
|
|
|
|
sleep(2);
|
|
|
|
// Subscribe with query for level <= 10
|
|
$client = $this->getWebsocket(['documents'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::lessThanEqual('level', 10)->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('connected', $response['type']);
|
|
|
|
// Create document with level = 10 - should receive event
|
|
$document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'level' => 10
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
$event = json_decode($client->receive(), true);
|
|
$this->assertEquals('event', $event['type']);
|
|
$this->assertEquals(10, $event['data']['payload']['level']);
|
|
|
|
// Create document with level < 10 - should receive event
|
|
$document2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'level' => 7
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
$event = json_decode($client->receive(), true);
|
|
$this->assertEquals('event', $event['type']);
|
|
$this->assertEquals(7, $event['data']['payload']['level']);
|
|
|
|
// Create document with level > 10 - should NOT receive event
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'level' => 15
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
try {
|
|
$client->receive();
|
|
$this->fail('Expected TimeoutException - event should be filtered');
|
|
} catch (TimeoutException $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
$client->close();
|
|
|
|
// Setup database and collection
|
|
$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' => 'IsNull Test 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' => 'Test Collection',
|
|
'permissions' => [
|
|
Permission::create(Role::user($user['$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' => 'description',
|
|
'size' => 256,
|
|
'required' => false,
|
|
]);
|
|
|
|
sleep(2);
|
|
|
|
// Subscribe with query for description IS NULL
|
|
$client = $this->getWebsocket(['documents'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::isNull('description')->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('connected', $response['type']);
|
|
|
|
// Create document without description - should receive event
|
|
$document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'description' => null
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
$event = json_decode($client->receive(), true);
|
|
$this->assertEquals('event', $event['type']);
|
|
|
|
// Create document with description - should NOT receive event
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'description' => 'Has description'
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
try {
|
|
$client->receive();
|
|
$this->fail('Expected TimeoutException - event should be filtered');
|
|
} catch (TimeoutException $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
$client->close();
|
|
|
|
// Setup database and collection
|
|
$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' => 'IsNotNull Test 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' => 'Test Collection',
|
|
'permissions' => [
|
|
Permission::create(Role::user($user['$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' => 'email',
|
|
'size' => 256,
|
|
'required' => false,
|
|
]);
|
|
|
|
sleep(2);
|
|
|
|
// Subscribe with query for email IS NOT NULL
|
|
$client = $this->getWebsocket(['documents'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::isNotNull('email')->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('connected', $response['type']);
|
|
|
|
// Create document with email - should receive event
|
|
$document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'email' => 'test@example.com'
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
$event = json_decode($client->receive(), true);
|
|
$this->assertEquals('event', $event['type']);
|
|
$this->assertEquals('test@example.com', $event['data']['payload']['email']);
|
|
|
|
// Create document without email - should NOT receive event
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
try {
|
|
$client->receive();
|
|
$this->fail('Expected TimeoutException - event should be filtered');
|
|
} catch (TimeoutException $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
$client->close();
|
|
|
|
// Setup database and collection
|
|
$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' => 'And Test 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' => 'Test Collection',
|
|
'permissions' => [
|
|
Permission::create(Role::user($user['$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' => 'status',
|
|
'size' => 256,
|
|
'required' => false,
|
|
]);
|
|
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
]), [
|
|
'key' => 'priority',
|
|
'required' => false,
|
|
]);
|
|
|
|
sleep(2);
|
|
|
|
// Subscribe with AND query: status = 'active' AND priority > 5
|
|
$client = $this->getWebsocket(['documents'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::and([
|
|
Query::equal('status', ['active']),
|
|
Query::greaterThan('priority', 5)
|
|
])->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('connected', $response['type']);
|
|
|
|
// Create document matching both conditions - should receive event
|
|
$document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'status' => 'active',
|
|
'priority' => 8
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
$event = json_decode($client->receive(), true);
|
|
$this->assertEquals('event', $event['type']);
|
|
$this->assertEquals('active', $event['data']['payload']['status']);
|
|
$this->assertEquals(8, $event['data']['payload']['priority']);
|
|
|
|
// Create document with status = 'active' but priority <= 5 - should NOT receive event
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'status' => 'active',
|
|
'priority' => 3
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
try {
|
|
$client->receive();
|
|
$this->fail('Expected TimeoutException - event should be filtered');
|
|
} catch (TimeoutException $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
// Create document with priority > 5 but status != 'active' - should NOT receive event
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'status' => 'inactive',
|
|
'priority' => 9
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
try {
|
|
$client->receive();
|
|
$this->fail('Expected TimeoutException - event should be filtered');
|
|
} catch (TimeoutException $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
$client->close();
|
|
|
|
// Setup database and collection
|
|
$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' => 'Or Test 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' => 'Test Collection',
|
|
'permissions' => [
|
|
Permission::create(Role::user($user['$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' => 'type',
|
|
'size' => 256,
|
|
'required' => false,
|
|
]);
|
|
|
|
sleep(2);
|
|
|
|
// Subscribe with OR query: type = 'urgent' OR type = 'critical'
|
|
$client = $this->getWebsocket(['documents'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::or([
|
|
Query::equal('type', ['urgent']),
|
|
Query::equal('type', ['critical'])
|
|
])->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('connected', $response['type']);
|
|
|
|
// Create document with type = 'urgent' - should receive event
|
|
$document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'type' => 'urgent'
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
$event = json_decode($client->receive(), true);
|
|
$this->assertEquals('event', $event['type']);
|
|
$this->assertEquals('urgent', $event['data']['payload']['type']);
|
|
|
|
// Create document with type = 'critical' - should receive event
|
|
$document2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'type' => 'critical'
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
$event = json_decode($client->receive(), true);
|
|
$this->assertEquals('event', $event['type']);
|
|
$this->assertEquals('critical', $event['data']['payload']['type']);
|
|
|
|
// Create document with type = 'normal' - should NOT receive event
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'type' => 'normal'
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
try {
|
|
$client->receive();
|
|
$this->fail('Expected TimeoutException - event should be filtered');
|
|
} catch (TimeoutException $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
$client->close();
|
|
|
|
// Setup database and collection
|
|
$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' => 'Complex Query Test 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' => 'Test Collection',
|
|
'permissions' => [
|
|
Permission::create(Role::user($user['$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' => 'category',
|
|
'size' => 256,
|
|
'required' => false,
|
|
]);
|
|
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
]), [
|
|
'key' => 'score',
|
|
'required' => false,
|
|
]);
|
|
|
|
sleep(2);
|
|
|
|
// Subscribe with complex query: (category = 'premium' OR category = 'vip') AND score >= 80
|
|
$client = $this->getWebsocket(['documents'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::and([
|
|
Query::or([
|
|
Query::equal('category', ['premium']),
|
|
Query::equal('category', ['vip'])
|
|
]),
|
|
Query::greaterThanEqual('score', 80)
|
|
])->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('connected', $response['type']);
|
|
|
|
// Create document with category = 'premium' and score >= 80 - should receive event
|
|
$document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'category' => 'premium',
|
|
'score' => 85
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
$event = json_decode($client->receive(), true);
|
|
$this->assertEquals('event', $event['type']);
|
|
$this->assertEquals('premium', $event['data']['payload']['category']);
|
|
$this->assertEquals(85, $event['data']['payload']['score']);
|
|
|
|
// Create document with category = 'vip' and score >= 80 - should receive event
|
|
$document2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'category' => 'vip',
|
|
'score' => 90
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
$event = json_decode($client->receive(), true);
|
|
$this->assertEquals('event', $event['type']);
|
|
$this->assertEquals('vip', $event['data']['payload']['category']);
|
|
$this->assertEquals(90, $event['data']['payload']['score']);
|
|
|
|
// Create document with category = 'premium' but score < 80 - should NOT receive event
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'category' => 'premium',
|
|
'score' => 70
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
try {
|
|
$client->receive();
|
|
$this->fail('Expected TimeoutException - event should be filtered');
|
|
} catch (TimeoutException $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
// Create document with score >= 80 but category != 'premium' or 'vip' - should NOT receive event
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => ID::unique(),
|
|
'data' => [
|
|
'category' => 'standard',
|
|
'score' => 85
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
try {
|
|
$client->receive();
|
|
$this->fail('Expected TimeoutException - event should be filtered');
|
|
} catch (TimeoutException $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
$client->close();
|
|
}
|
|
|
|
public function testFilesChannelWithQuery()
|
|
{
|
|
$user = $this->getUser();
|
|
$session = $user['session'] ?? '';
|
|
$projectId = $this->getProject()['$id'];
|
|
|
|
// Create bucket
|
|
$bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
|
]), [
|
|
'bucketId' => ID::unique(),
|
|
'name' => 'Query Test Bucket',
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
Permission::create(Role::any()),
|
|
]
|
|
]);
|
|
$bucketId = $bucket['body']['$id'];
|
|
|
|
$targetFileId = ID::unique();
|
|
|
|
// Subscribe with query for specific file ID
|
|
$client = $this->getWebsocket(['files'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::equal('$id', [$targetFileId])->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('connected', $response['type']);
|
|
|
|
// Create file with matching ID - should receive event
|
|
$file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', array_merge([
|
|
'content-type' => 'multipart/form-data',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'fileId' => $targetFileId,
|
|
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'),
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
$event = json_decode($client->receive(), true);
|
|
$this->assertEquals('event', $event['type']);
|
|
$this->assertEquals($targetFileId, $event['data']['payload']['$id']);
|
|
|
|
// Create file with different ID - should NOT receive event
|
|
$otherFileId = ID::unique();
|
|
$this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', array_merge([
|
|
'content-type' => 'multipart/form-data',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'fileId' => $otherFileId,
|
|
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo2.png'),
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
try {
|
|
$client->receive();
|
|
$this->fail('Expected TimeoutException - event should be filtered');
|
|
} catch (TimeoutException $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
$client->close();
|
|
}
|
|
|
|
public function testMultipleQueriesWithAndLogic()
|
|
{
|
|
$user = $this->getUser();
|
|
$session = $user['session'] ?? '';
|
|
$projectId = $this->getProject()['$id'];
|
|
|
|
// Setup database and collection
|
|
$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' => 'Multiple Queries Test 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' => 'Test Collection',
|
|
'permissions' => [
|
|
Permission::create(Role::user($user['$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' => 'status',
|
|
'size' => 256,
|
|
'required' => false,
|
|
]);
|
|
|
|
sleep(2);
|
|
|
|
$targetDocId = ID::unique();
|
|
|
|
// Subscribe with multiple queries (AND logic - ALL queries must match for event to be received)
|
|
$client = $this->getWebsocket(['documents'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::equal('$id', [$targetDocId])->toString(),
|
|
Query::equal('status', ['active'])->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('connected', $response['type']);
|
|
|
|
// Create document matching BOTH queries - should receive event
|
|
$document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => $targetDocId,
|
|
'data' => [
|
|
'status' => 'active'
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
$event = json_decode($client->receive(), true);
|
|
$this->assertEquals('event', $event['type']);
|
|
$this->assertEquals($targetDocId, $event['data']['payload']['$id']);
|
|
$this->assertEquals('active', $event['data']['payload']['status']);
|
|
|
|
// Create document with matching ID but wrong status - should NOT receive event (only one query matches)
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => $targetDocId,
|
|
'data' => [
|
|
'status' => 'inactive'
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
try {
|
|
$client->receive();
|
|
$this->fail('Expected TimeoutException - event should be filtered (ID matches but status does not)');
|
|
} catch (TimeoutException $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
// Create document with matching status but wrong ID - should NOT receive event (only one query matches)
|
|
$otherDocId = ID::unique();
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => $otherDocId,
|
|
'data' => [
|
|
'status' => 'active'
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
try {
|
|
$client->receive();
|
|
$this->fail('Expected TimeoutException - event should be filtered (status matches but ID does not)');
|
|
} catch (TimeoutException $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
// Create document matching NEITHER query - should NOT receive event
|
|
$anotherDocId = ID::unique();
|
|
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $projectId,
|
|
], $this->getHeaders()), [
|
|
'documentId' => $anotherDocId,
|
|
'data' => [
|
|
'status' => 'inactive'
|
|
],
|
|
'permissions' => [
|
|
Permission::read(Role::any()),
|
|
],
|
|
]);
|
|
|
|
try {
|
|
$client->receive();
|
|
$this->fail('Expected TimeoutException - event should be filtered (neither query matches)');
|
|
} catch (TimeoutException $e) {
|
|
$this->assertTrue(true);
|
|
}
|
|
|
|
$client->close();
|
|
}
|
|
|
|
public function testInvalidQueryShouldNotSubscribe()
|
|
{
|
|
$user = $this->getUser();
|
|
$session = $user['session'] ?? '';
|
|
$projectId = $this->getProject()['$id'];
|
|
|
|
// Test 1: Simple invalid query method (contains is not allowed)
|
|
$client = $this->getWebsocket(['documents'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::contains('status', ['active'])->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('error', $response['type']);
|
|
$this->assertStringContainsString('not supported in Realtime queries', $response['data']['message']);
|
|
$this->assertStringContainsString('contains', $response['data']['message']);
|
|
|
|
// Test 2: Invalid query method in nested AND query
|
|
$client = $this->getWebsocket(['documents'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::and([
|
|
Query::equal('status', ['active']),
|
|
Query::search('name', 'test') // search is not allowed
|
|
])->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('error', $response['type']);
|
|
$this->assertStringContainsString('not supported in Realtime queries', $response['data']['message']);
|
|
$this->assertStringContainsString('search', $response['data']['message']);
|
|
|
|
// Test 3: Invalid query method in nested OR query
|
|
$client = $this->getWebsocket(['documents'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::or([
|
|
Query::equal('status', ['active']),
|
|
Query::between('score', 0, 100) // between is not allowed
|
|
])->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('error', $response['type']);
|
|
$this->assertStringContainsString('not supported in Realtime queries', $response['data']['message']);
|
|
$this->assertStringContainsString('between', $response['data']['message']);
|
|
|
|
// Test 4: Deeply nested invalid query (AND -> OR -> invalid)
|
|
$client = $this->getWebsocket(['documents'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::and([
|
|
Query::equal('status', ['active']),
|
|
Query::or([
|
|
Query::greaterThan('score', 50),
|
|
Query::startsWith('name', 'test') // startsWith is not allowed
|
|
])
|
|
])->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('error', $response['type']);
|
|
$this->assertStringContainsString('not supported in Realtime queries', $response['data']['message']);
|
|
$this->assertStringContainsString('startsWith', $response['data']['message']);
|
|
|
|
// Test 5: Multiple invalid queries in nested structure
|
|
$client = $this->getWebsocket(['documents'], [
|
|
'origin' => 'http://localhost',
|
|
'cookie' => 'a_session_' . $projectId . '=' . $session,
|
|
], null, [
|
|
Query::and([
|
|
Query::contains('tags', ['important']), // contains is not allowed
|
|
Query::or([
|
|
Query::endsWith('email', '@example.com'), // endsWith is not allowed
|
|
Query::equal('status', ['active'])
|
|
])
|
|
])->toString(),
|
|
]);
|
|
|
|
$response = json_decode($client->receive(), true);
|
|
$this->assertEquals('error', $response['type']);
|
|
$this->assertStringContainsString('not supported in Realtime queries', $response['data']['message']);
|
|
// Should catch the first invalid method encountered
|
|
$this->assertTrue(
|
|
str_contains($response['data']['message'], 'contains') ||
|
|
str_contains($response['data']['message'], 'endsWith')
|
|
);
|
|
}
|
|
}
|