mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb
# Conflicts: # app/controllers/api/account.php # app/controllers/api/messaging.php # app/controllers/api/projects.php # app/controllers/api/storage.php # app/controllers/api/teams.php # app/controllers/api/users.php # composer.lock # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Line/Update.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Point/Update.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Polygon/Update.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php # src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Update.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Datetime/Update.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Email/Update.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Enum/Update.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Float/Update.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/IP/Update.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Integer/Update.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Update.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Update.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Update.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Create.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Update.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Update.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/URL/Update.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Delete.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Update.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Upsert.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Decrement.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Create.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Upsert.php # src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php # src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php # src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php # src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php # src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Update.php
This commit is contained in:
@@ -28,6 +28,7 @@ class UsageTest extends Scope
|
||||
FunctionsBase::createVariable insteadof SitesBase;
|
||||
FunctionsBase::getVariable insteadof SitesBase;
|
||||
FunctionsBase::listVariables insteadof SitesBase;
|
||||
FunctionsBase::helperGetLatestCommit insteadof SitesBase;
|
||||
FunctionsBase::updateVariable insteadof SitesBase;
|
||||
FunctionsBase::deleteVariable insteadof SitesBase;
|
||||
FunctionsBase::getDeployment insteadof SitesBase;
|
||||
|
||||
@@ -41,6 +41,11 @@ trait AccountBase
|
||||
$this->assertNotEmpty($response['body']['accessedAt']);
|
||||
$this->assertArrayHasKey('targets', $response['body']);
|
||||
$this->assertEquals($email, $response['body']['targets'][0]['identifier']);
|
||||
$this->assertArrayNotHasKey('emailCanonical', $response['body']);
|
||||
$this->assertArrayNotHasKey('emailIsFree', $response['body']);
|
||||
$this->assertArrayNotHasKey('emailIsDisposable', $response['body']);
|
||||
$this->assertArrayNotHasKey('emailIsCorporate', $response['body']);
|
||||
$this->assertArrayNotHasKey('emailIsCanonical', $response['body']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
|
||||
@@ -88,4 +88,112 @@ class AccountConsoleClientTest extends Scope
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 204);
|
||||
}
|
||||
|
||||
public function testSessionAlert(): void
|
||||
{
|
||||
$email = uniqid() . 'session-alert@appwrite.io';
|
||||
$password = 'password123';
|
||||
$name = 'Session Alert Tester';
|
||||
|
||||
// Create a new account
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-dev-key' => $this->getProject()['devKey'] ?? ''
|
||||
]), [
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
// Create first session for the new account
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'user-agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',
|
||||
]), [
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
// Create second session for the new account
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'user-agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',
|
||||
]), [
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
]);
|
||||
|
||||
|
||||
// Check the alert email
|
||||
$lastEmail = $this->getLastEmail();
|
||||
|
||||
$this->assertEquals($email, $lastEmail['to'][0]['address']);
|
||||
$this->assertStringContainsString('Security alert: new session', $lastEmail['subject']);
|
||||
$this->assertStringContainsString($response['body']['ip'], $lastEmail['text']); // IP Address
|
||||
$this->assertStringContainsString('Unknown', $lastEmail['text']); // Country
|
||||
$this->assertStringContainsString($response['body']['clientName'], $lastEmail['text']); // Client name
|
||||
$this->assertStringContainsStringIgnoringCase('Appwrite logo', $lastEmail['html']);
|
||||
|
||||
// Verify no alert sent in OTP login
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account/tokens/email', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => ID::unique(),
|
||||
'email' => 'otpuser2@appwrite.io'
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 201);
|
||||
$this->assertNotEmpty($response['body']['$id']);
|
||||
$this->assertNotEmpty($response['body']['$createdAt']);
|
||||
$this->assertNotEmpty($response['body']['userId']);
|
||||
$this->assertNotEmpty($response['body']['expire']);
|
||||
$this->assertEmpty($response['body']['secret']);
|
||||
$this->assertEmpty($response['body']['phrase']);
|
||||
$this->assertStringContainsStringIgnoringCase('New login detected on '. $this->getProject()['name'], $lastEmail['text']);
|
||||
|
||||
$userId = $response['body']['userId'];
|
||||
|
||||
$lastEmail = $this->getLastEmail();
|
||||
|
||||
$this->assertEquals('otpuser2@appwrite.io', $lastEmail['to'][0]['address']);
|
||||
$this->assertEquals('OTP for ' . $this->getProject()['name'] . ' Login', $lastEmail['subject']);
|
||||
|
||||
// Find 6 concurrent digits in email text - OTP
|
||||
preg_match_all("/\b\d{6}\b/", $lastEmail['text'], $matches);
|
||||
$code = ($matches[0] ?? [])[0] ?? '';
|
||||
|
||||
$this->assertNotEmpty($code);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/token', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]), [
|
||||
'userId' => $userId,
|
||||
'secret' => $code
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$this->assertEquals($userId, $response['body']['userId']);
|
||||
$this->assertNotEmpty($response['body']['$id']);
|
||||
$this->assertNotEmpty($response['body']['expire']);
|
||||
$this->assertEmpty($response['body']['secret']);
|
||||
|
||||
$lastEmailId = $lastEmail['id'];
|
||||
$lastEmail = $this->getLastEmail();
|
||||
$this->assertEquals($lastEmailId, $lastEmail['id']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,6 +389,26 @@ class AccountCustomClientTest extends Scope
|
||||
$this->assertIsNumeric($responseLimitOffset['body']['total']);
|
||||
|
||||
$this->assertEquals($response['body']['logs'][1], $responseLimitOffset['body']['logs'][0]);
|
||||
|
||||
/**
|
||||
* Test for total=false
|
||||
*/
|
||||
$logsWithIncludeTotalFalse = $this->client->call(Client::METHOD_GET, '/account/logs', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]), [
|
||||
'total' => false
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $logsWithIncludeTotalFalse['headers']['status-code']);
|
||||
$this->assertIsArray($logsWithIncludeTotalFalse['body']);
|
||||
$this->assertIsArray($logsWithIncludeTotalFalse['body']['logs']);
|
||||
$this->assertIsInt($logsWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertEquals(0, $logsWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertGreaterThan(0, count($logsWithIncludeTotalFalse['body']['logs']));
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
@@ -1348,10 +1368,7 @@ class AccountCustomClientTest extends Scope
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateAccountSession
|
||||
*/
|
||||
public function testSessionAlert($data): void
|
||||
public function testSessionAlert(): void
|
||||
{
|
||||
$email = uniqid() . 'session-alert@appwrite.io';
|
||||
$password = 'password123';
|
||||
@@ -1417,6 +1434,7 @@ class AccountCustomClientTest extends Scope
|
||||
$this->assertStringContainsString($response['body']['ip'], $lastEmail['text']); // IP Address
|
||||
$this->assertStringContainsString('Unknown', $lastEmail['text']); // Country
|
||||
$this->assertStringContainsString($response['body']['clientName'], $lastEmail['text']); // Client name
|
||||
$this->assertStringNotContainsStringIgnoringCase('Appwrite logo', $lastEmail['html']);
|
||||
|
||||
// Verify no alert sent in OTP login
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account/tokens/email', array_merge([
|
||||
|
||||
@@ -558,4 +558,739 @@ trait AvatarsBase
|
||||
$this->assertEquals('PNG', $image->getImageFormat());
|
||||
$this->assertEquals(strlen(\file_get_contents(__DIR__ . '/../../../resources/initials.png')), strlen($response['body']));
|
||||
}
|
||||
|
||||
public function testGetScreenshot(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('image/png', $response['headers']['content-type']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertGreaterThan(100000, strlen($response['body']));
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'headers' => [
|
||||
'User-Agent' => 'Mozilla/5.0 (compatible; AppwriteBot/1.0)',
|
||||
'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('image/png', $response['headers']['content-type']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE - Invalid headers parameter types
|
||||
*/
|
||||
|
||||
// Test with string headers (should fail)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'headers' => 'invalid-headers-string',
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test with numeric headers (should fail)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'headers' => 123,
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test with boolean headers (should fail)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'headers' => true,
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test with null headers - framework converts null to empty array, so this passes
|
||||
// Skipping this test as null is converted to [] by the framework before validation
|
||||
|
||||
// Test with regular array (indexed array) - should fail
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'headers' => ['value1', 'value2', 'value3'], // Indexed array
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test with mixed array (some numeric keys) - Assoc validator allows this
|
||||
// Mixed arrays are considered associative by the Assoc validator
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'headers' => ['User-Agent' => 'MyApp', 'value2', 'Accept' => 'text/html'], // Mixed array
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
||||
// Test with empty array (should pass - empty associative array)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'headers' => [], // Empty associative array should pass
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
||||
// Test with valid headers object (should pass)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'headers' => [
|
||||
'User-Agent' => 'MyApp/1.0',
|
||||
'Accept' => 'text/html,application/xhtml+xml',
|
||||
'Accept-Language' => 'en-US,en;q=0.9'
|
||||
],
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
||||
// Test with headers containing special characters (should pass)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'headers' => [
|
||||
'X-Custom-Header' => 'custom-value',
|
||||
'Authorization' => 'Bearer token123',
|
||||
'Content-Type' => 'application/json'
|
||||
],
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
||||
// Test with custom viewport width and height
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'viewportWidth' => 1920,
|
||||
'viewportHeight' => 1080,
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('image/png', $response['headers']['content-type']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
||||
// Test with minimum valid viewport dimensions
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'viewportWidth' => 1,
|
||||
'viewportHeight' => 1,
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('image/png', $response['headers']['content-type']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
||||
// Test with maximum valid viewport dimensions
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'viewportWidth' => 1920,
|
||||
'viewportHeight' => 1080,
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('image/png', $response['headers']['content-type']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE - Invalid URL parameter
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'invalid-url',
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'ftp://example.com', // Non-HTTP/HTTPS URL
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE - Invalid viewport parameters
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'viewportWidth' => 0, // Too small
|
||||
'viewportHeight' => 720,
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'viewportWidth' => 2000, // Too large
|
||||
'viewportHeight' => 720,
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'viewportWidth' => 1280,
|
||||
'viewportHeight' => 0, // Too small
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'viewportWidth' => 1280,
|
||||
'viewportHeight' => 2000, // Too large
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE - Invalid width/height parameters
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => -1, // Invalid width (negative)
|
||||
'height' => 600,
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 3000, // Invalid height
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE - Invalid sleep parameter
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'sleep' => -1, // Negative sleep
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'sleep' => 15, // Too large
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE - Invalid quality parameter
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'quality' => -2, // Too small
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'quality' => 150, // Too large
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE - Invalid output parameter
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'output' => 'invalid-format',
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Test for SUCCESS - New screenshot parameters
|
||||
*/
|
||||
// Test with theme parameter
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'theme' => 'dark',
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('image/png', $response['headers']['content-type']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
||||
// Test with scale parameter
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'scale' => 2.0,
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('image/png', $response['headers']['content-type']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
||||
// Test with userAgent parameter
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'userAgent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('image/png', $response['headers']['content-type']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
||||
// Test with fullpage parameter
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'fullpage' => true,
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('image/png', $response['headers']['content-type']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
||||
// Test with locale parameter
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'locale' => 'en-US',
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('image/png', $response['headers']['content-type']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
||||
// Test with timezone parameter
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'timezone' => 'America/New_York',
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('image/png', $response['headers']['content-type']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
||||
// Test with geolocation parameters
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'latitude' => 40.7128,
|
||||
'longitude' => -74.0060,
|
||||
'accuracy' => 100,
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('image/png', $response['headers']['content-type']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
||||
// Test with touch parameter
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'touch' => true,
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('image/png', $response['headers']['content-type']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
||||
// Test with permissions parameter
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'permissions' => [
|
||||
'geolocation',
|
||||
'camera',
|
||||
'microphone',
|
||||
'notifications'
|
||||
],
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('image/png', $response['headers']['content-type']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
||||
// Test with original dimensions (width=0, height=0)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 0,
|
||||
'height' => 0,
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('image/png', $response['headers']['content-type']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
||||
// Test with all new parameters combined
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'scale' => 1.5,
|
||||
'theme' => 'dark',
|
||||
'userAgent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
|
||||
'fullpage' => true,
|
||||
'locale' => 'en-GB',
|
||||
'timezone' => 'Europe/London',
|
||||
'latitude' => 51.5074,
|
||||
'longitude' => -0.1278,
|
||||
'accuracy' => 50,
|
||||
'touch' => true,
|
||||
'permissions' => [
|
||||
'geolocation',
|
||||
'camera',
|
||||
'microphone',
|
||||
'notifications',
|
||||
'clipboard-read',
|
||||
'clipboard-write'
|
||||
],
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('image/png', $response['headers']['content-type']);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE - Invalid new parameters
|
||||
*/
|
||||
|
||||
// Test invalid theme parameter
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://test' . time() . '.com',
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'theme' => 'invalid-theme',
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test invalid scale parameter (too small)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://test' . time() . '.com',
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'scale' => 0.05, // Too small (min 0.1)
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test invalid scale parameter (too large)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://test' . time() . '.com',
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'scale' => 5.0, // Too large (max 3.0)
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test invalid userAgent parameter (too long)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'userAgent' => str_repeat('A', 513), // Too long (max 512)
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test invalid fullpage parameter (non-boolean)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'fullpage' => 'invalid-boolean',
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test invalid locale parameter (too long)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'locale' => 'en-US-very-long-locale-string',
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test invalid timezone parameter
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'timezone' => 'Invalid/Timezone',
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test invalid latitude parameter (too high)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'latitude' => 91, // Too high (max 90)
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test invalid latitude parameter (too low)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'latitude' => -91, // Too low (min -90)
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test invalid longitude parameter (too high)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'longitude' => 181, // Too high (max 180)
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test invalid longitude parameter (too low)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'longitude' => -181, // Too low (min -180)
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test invalid accuracy parameter (too high)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'accuracy' => 100001, // Too high (max 100000)
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test invalid accuracy parameter (negative)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'accuracy' => -1, // Negative (min 0)
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test invalid touch parameter (non-boolean)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'touch' => 'invalid-boolean',
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test invalid permissions parameter (non-array)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'permissions' => 'invalid-permissions-string',
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test invalid permissions parameter (numeric array)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'permissions' => ['geolocation', 'camera', 'microphone'], // This should pass as it's a valid array
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
||||
// Test empty permissions array (should pass)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'permissions' => [], // Empty array should pass
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
||||
// Test invalid permission names (should fail)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'permissions' => ['invalid-permission', 'another-invalid'],
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test mixed valid and invalid permissions (should fail)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'permissions' => ['geolocation', 'invalid-permission'],
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
// Test valid permission names (should pass)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'permissions' => ['geolocation', 'camera', 'microphone', 'notifications'],
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
||||
// Test advanced permission names (should pass)
|
||||
$response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'url' => 'https://appwrite.io?x=' . time() . rand(1000, 9999),
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'permissions' => ['geolocation', 'camera', 'microphone'],
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Operator;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
|
||||
@@ -986,6 +987,24 @@ trait DatabasesBase
|
||||
$this->assertEquals(200, $attributes['headers']['status-code']);
|
||||
$this->assertEquals(12, $attributes['body']['total']);
|
||||
|
||||
/**
|
||||
* Test for SUCCESS with total=false
|
||||
*/
|
||||
$attributesWithIncludeTotalFalse = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'total' => false
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $attributesWithIncludeTotalFalse['headers']['status-code']);
|
||||
$this->assertIsArray($attributesWithIncludeTotalFalse['body']);
|
||||
$this->assertIsArray($attributesWithIncludeTotalFalse['body']['attributes']);
|
||||
$this->assertIsInt($attributesWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertEquals(0, $attributesWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertGreaterThan(0, count($attributesWithIncludeTotalFalse['body']['attributes']));
|
||||
|
||||
$attributes = $attributes['body']['attributes'];
|
||||
$this->assertIsArray($attributes);
|
||||
$this->assertCount(12, $attributes);
|
||||
@@ -3136,6 +3155,404 @@ trait DatabasesBase
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testOperators(): void
|
||||
{
|
||||
// Create database
|
||||
$database = $this->client->call(Client::METHOD_POST, '/databases', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Test Database for Operators'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
// Create collection
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'Operator Tests',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$collectionId = $collection['body']['$id'];
|
||||
|
||||
// Create attributes
|
||||
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'title',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'releaseYear',
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'duration',
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'actors',
|
||||
'size' => 256,
|
||||
'required' => false,
|
||||
'array' => true,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'integers',
|
||||
'required' => false,
|
||||
'array' => true,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'tagline',
|
||||
'size' => 512,
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/datetime', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'birthDay',
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
// Wait for attributes to be created
|
||||
sleep(2);
|
||||
|
||||
// Create a document to test operators
|
||||
$document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'Operator Test',
|
||||
'releaseYear' => 2020,
|
||||
'duration' => 120,
|
||||
'actors' => ['Actor1', 'Actor2'],
|
||||
'integers' => [10, 20],
|
||||
'tagline' => 'Original',
|
||||
'birthDay' => '2020-01-01 12:00:00',
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::update(Role::user($this->getUser()['$id'])),
|
||||
Permission::delete(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $document['headers']['status-code']);
|
||||
$documentId = $document['body']['$id'];
|
||||
|
||||
// Test increment operator on integer
|
||||
$updated = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'releaseYear' => Operator::increment(5)->toString(),
|
||||
'duration' => Operator::increment(10)->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $updated['headers']['status-code']);
|
||||
$this->assertEquals(2025, $updated['body']['releaseYear']);
|
||||
$this->assertEquals(130, $updated['body']['duration']);
|
||||
|
||||
// Test decrement operator
|
||||
$updated = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'releaseYear' => Operator::decrement(3)->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $updated['headers']['status-code']);
|
||||
$this->assertEquals(2022, $updated['body']['releaseYear']);
|
||||
|
||||
// Test array append operator
|
||||
$updated = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'actors' => Operator::arrayAppend(['Actor3'])->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $updated['headers']['status-code']);
|
||||
$this->assertEquals(['Actor1', 'Actor2', 'Actor3'], $updated['body']['actors']);
|
||||
|
||||
// Test array prepend operator
|
||||
$updated = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'actors' => Operator::arrayPrepend(['Actor0'])->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $updated['headers']['status-code']);
|
||||
$this->assertEquals(['Actor0', 'Actor1', 'Actor2', 'Actor3'], $updated['body']['actors']);
|
||||
|
||||
// Test string concat operator
|
||||
$updated = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'tagline' => Operator::stringConcat(' Appended')->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $updated['headers']['status-code']);
|
||||
$this->assertEquals('Original Appended', $updated['body']['tagline']);
|
||||
|
||||
// Test multiple operators in a single update
|
||||
$updated = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'releaseYear' => Operator::increment(1)->toString(),
|
||||
'integers' => Operator::arrayAppend([30])->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $updated['headers']['status-code']);
|
||||
$this->assertEquals(2023, $updated['body']['releaseYear']);
|
||||
$this->assertEquals([10, 20, 30], $updated['body']['integers']);
|
||||
|
||||
// Test upsert with operators
|
||||
$upsertId = ID::unique();
|
||||
$upserted = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $upsertId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'title' => 'Upsert Test',
|
||||
'releaseYear' => 2020,
|
||||
'actors' => [],
|
||||
'birthDay' => '2020-01-01 12:00:00',
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::update(Role::user($this->getUser()['$id'])),
|
||||
Permission::delete(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $upserted['headers']['status-code']);
|
||||
|
||||
$upserted = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $upsertId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'title' => 'Upsert Test Updated',
|
||||
'releaseYear' => Operator::increment(5)->toString(),
|
||||
'actors' => [],
|
||||
'birthDay' => '2020-01-01 12:00:00',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $upserted['headers']['status-code']);
|
||||
$this->assertEquals(2025, $upserted['body']['releaseYear']);
|
||||
}
|
||||
|
||||
public function testBulkOperators(): void
|
||||
{
|
||||
// Create database
|
||||
$database = $this->client->call(Client::METHOD_POST, '/databases', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Test Database for Bulk Operators'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
// Create collection
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'Bulk Operator Tests',
|
||||
'documentSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::users()),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$collectionId = $collection['body']['$id'];
|
||||
|
||||
// Create attributes
|
||||
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'title',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'releaseYear',
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'actors',
|
||||
'size' => 256,
|
||||
'required' => false,
|
||||
'array' => true,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/datetime', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'birthDay',
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
// Wait for attributes to be created
|
||||
sleep(2);
|
||||
|
||||
// Create multiple documents
|
||||
$document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'Bulk Test 1',
|
||||
'releaseYear' => 2020,
|
||||
'actors' => ['Actor1'],
|
||||
'birthDay' => '2020-01-01 12:00:00',
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::read(Role::users()),
|
||||
Permission::update(Role::users()),
|
||||
Permission::delete(Role::users()),
|
||||
],
|
||||
]);
|
||||
|
||||
$document2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'Bulk Test 2',
|
||||
'releaseYear' => 2021,
|
||||
'actors' => ['Actor2'],
|
||||
'birthDay' => '2020-01-01 12:00:00',
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::read(Role::users()),
|
||||
Permission::update(Role::users()),
|
||||
Permission::delete(Role::users()),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $document1['headers']['status-code']);
|
||||
$this->assertEquals(201, $document2['headers']['status-code']);
|
||||
|
||||
// Test bulk update with operators
|
||||
$bulkUpdate = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'data' => [
|
||||
'releaseYear' => Operator::increment(10)->toString(),
|
||||
],
|
||||
'queries' => [
|
||||
Query::startsWith('title', 'Bulk Test')->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $bulkUpdate['headers']['status-code']);
|
||||
$this->assertGreaterThanOrEqual(2, $bulkUpdate['body']['total']);
|
||||
|
||||
// Verify the updates
|
||||
$verify1 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $document1['body']['$id'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $verify1['headers']['status-code']);
|
||||
$this->assertEquals(2030, $verify1['body']['releaseYear']);
|
||||
|
||||
$verify2 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $document2['body']['$id'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $verify2['headers']['status-code']);
|
||||
$this->assertEquals(2031, $verify2['body']['releaseYear']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDocument
|
||||
*/
|
||||
|
||||
@@ -55,6 +55,23 @@ class DatabasesCustomServerTest extends Scope
|
||||
$this->assertEquals($test1['body']['$id'], $databases['body']['databases'][0]['$id']);
|
||||
$this->assertEquals($test2['body']['$id'], $databases['body']['databases'][1]['$id']);
|
||||
|
||||
/**
|
||||
* Test for SUCCESS with total=false
|
||||
*/
|
||||
$databasesWithIncludeTotalFalse = $this->client->call(Client::METHOD_GET, '/databases', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'total' => false
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $databasesWithIncludeTotalFalse['headers']['status-code']);
|
||||
$this->assertIsArray($databasesWithIncludeTotalFalse['body']);
|
||||
$this->assertIsArray($databasesWithIncludeTotalFalse['body']['databases']);
|
||||
$this->assertIsInt($databasesWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertEquals(0, $databasesWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertGreaterThan(0, count($databasesWithIncludeTotalFalse['body']['databases']));
|
||||
|
||||
$base = array_reverse($databases['body']['databases']);
|
||||
|
||||
$databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([
|
||||
@@ -395,6 +412,23 @@ class DatabasesCustomServerTest extends Scope
|
||||
$this->assertEquals($test2['body']['$id'], $collections['body']['collections'][1]['$id']);
|
||||
$this->assertEquals($test1['body']['enabled'], $collections['body']['collections'][0]['enabled']);
|
||||
|
||||
/**
|
||||
* Test for SUCCESS with total=false
|
||||
*/
|
||||
$collectionsWithIncludeTotalFalse = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'total' => false
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $collectionsWithIncludeTotalFalse['headers']['status-code']);
|
||||
$this->assertIsArray($collectionsWithIncludeTotalFalse['body']);
|
||||
$this->assertIsArray($collectionsWithIncludeTotalFalse['body']['collections']);
|
||||
$this->assertIsInt($collectionsWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertEquals(0, $collectionsWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertGreaterThan(0, count($collectionsWithIncludeTotalFalse['body']['collections']));
|
||||
|
||||
$base = array_reverse($collections['body']['collections']);
|
||||
|
||||
$collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
@@ -4580,6 +4614,23 @@ class DatabasesCustomServerTest extends Scope
|
||||
$this->assertEquals(200, $documents['headers']['status-code']);
|
||||
$this->assertEquals(10, $documents['body']['total']);
|
||||
|
||||
/**
|
||||
* Test for SUCCESS with total=false
|
||||
*/
|
||||
$documentsWithIncludeTotalFalse = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['$id'] . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'total' => false
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $documentsWithIncludeTotalFalse['headers']['status-code']);
|
||||
$this->assertIsArray($documentsWithIncludeTotalFalse['body']);
|
||||
$this->assertIsArray($documentsWithIncludeTotalFalse['body']['documents']);
|
||||
$this->assertIsInt($documentsWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertEquals(0, $documentsWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertGreaterThan(0, count($documentsWithIncludeTotalFalse['body']['documents']));
|
||||
|
||||
$returnedDocuments = $response['body']['documents'];
|
||||
$refetchedDocuments = $documents['body']['documents'];
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Operator;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
|
||||
@@ -3042,6 +3043,404 @@ trait DatabasesBase
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testOperators(): void
|
||||
{
|
||||
// Create database
|
||||
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Test Database for Operators'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
// Create table
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'Operator Tests',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$tableId = $table['body']['$id'];
|
||||
|
||||
// Create columns
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'title',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'releaseYear',
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'duration',
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'actors',
|
||||
'size' => 256,
|
||||
'required' => false,
|
||||
'array' => true,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'integers',
|
||||
'required' => false,
|
||||
'array' => true,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'tagline',
|
||||
'size' => 512,
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/datetime', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'birthDay',
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
// Wait for columns to be created
|
||||
sleep(2);
|
||||
|
||||
// Create a row to test operators
|
||||
$row = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'rowId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'Operator Test',
|
||||
'releaseYear' => 2020,
|
||||
'duration' => 120,
|
||||
'actors' => ['Actor1', 'Actor2'],
|
||||
'integers' => [10, 20],
|
||||
'tagline' => 'Original',
|
||||
'birthDay' => '2020-01-01 12:00:00',
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::update(Role::user($this->getUser()['$id'])),
|
||||
Permission::delete(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $row['headers']['status-code']);
|
||||
$rowId = $row['body']['$id'];
|
||||
|
||||
// Test increment operator on integer
|
||||
$updated = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'releaseYear' => Operator::increment(5)->toString(),
|
||||
'duration' => Operator::increment(10)->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $updated['headers']['status-code']);
|
||||
$this->assertEquals(2025, $updated['body']['releaseYear']);
|
||||
$this->assertEquals(130, $updated['body']['duration']);
|
||||
|
||||
// Test decrement operator
|
||||
$updated = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'releaseYear' => Operator::decrement(3)->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $updated['headers']['status-code']);
|
||||
$this->assertEquals(2022, $updated['body']['releaseYear']);
|
||||
|
||||
// Test array append operator
|
||||
$updated = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'actors' => Operator::arrayAppend(['Actor3'])->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $updated['headers']['status-code']);
|
||||
$this->assertEquals(['Actor1', 'Actor2', 'Actor3'], $updated['body']['actors']);
|
||||
|
||||
// Test array prepend operator
|
||||
$updated = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'actors' => Operator::arrayPrepend(['Actor0'])->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $updated['headers']['status-code']);
|
||||
$this->assertEquals(['Actor0', 'Actor1', 'Actor2', 'Actor3'], $updated['body']['actors']);
|
||||
|
||||
// Test string concat operator
|
||||
$updated = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'tagline' => Operator::stringConcat(' Appended')->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $updated['headers']['status-code']);
|
||||
$this->assertEquals('Original Appended', $updated['body']['tagline']);
|
||||
|
||||
// Test multiple operators in a single update
|
||||
$updated = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $rowId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'releaseYear' => Operator::increment(1)->toString(),
|
||||
'integers' => Operator::arrayAppend([30])->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $updated['headers']['status-code']);
|
||||
$this->assertEquals(2023, $updated['body']['releaseYear']);
|
||||
$this->assertEquals([10, 20, 30], $updated['body']['integers']);
|
||||
|
||||
// Test upsert with operators
|
||||
$upsertId = ID::unique();
|
||||
$upserted = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $upsertId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'title' => 'Upsert Test',
|
||||
'releaseYear' => 2020,
|
||||
'actors' => [],
|
||||
'birthDay' => '2020-01-01 12:00:00',
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::update(Role::user($this->getUser()['$id'])),
|
||||
Permission::delete(Role::user($this->getUser()['$id'])),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $upserted['headers']['status-code']);
|
||||
|
||||
$upserted = $this->client->call(Client::METHOD_PUT, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $upsertId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'data' => [
|
||||
'title' => 'Upsert Test Updated',
|
||||
'releaseYear' => Operator::increment(5)->toString(),
|
||||
'actors' => [],
|
||||
'birthDay' => '2020-01-01 12:00:00',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $upserted['headers']['status-code']);
|
||||
$this->assertEquals(2025, $upserted['body']['releaseYear']);
|
||||
}
|
||||
|
||||
public function testBulkOperators(): void
|
||||
{
|
||||
// Create database
|
||||
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Test Database for Bulk Operators'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
// Create table
|
||||
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'Bulk Operator Tests',
|
||||
'rowSecurity' => true,
|
||||
'permissions' => [
|
||||
Permission::create(Role::users()),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$tableId = $table['body']['$id'];
|
||||
|
||||
// Create columns
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'title',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'releaseYear',
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/string', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'actors',
|
||||
'size' => 256,
|
||||
'required' => false,
|
||||
'array' => true,
|
||||
]);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/datetime', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'birthDay',
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
// Wait for columns to be created
|
||||
sleep(2);
|
||||
|
||||
// Create multiple rows
|
||||
$row1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'rowId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'Bulk Test 1',
|
||||
'releaseYear' => 2020,
|
||||
'actors' => ['Actor1'],
|
||||
'birthDay' => '2020-01-01 12:00:00',
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::read(Role::users()),
|
||||
Permission::update(Role::users()),
|
||||
Permission::delete(Role::users()),
|
||||
],
|
||||
]);
|
||||
|
||||
$row2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'rowId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'Bulk Test 2',
|
||||
'releaseYear' => 2021,
|
||||
'actors' => ['Actor2'],
|
||||
'birthDay' => '2020-01-01 12:00:00',
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::read(Role::users()),
|
||||
Permission::update(Role::users()),
|
||||
Permission::delete(Role::users()),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $row1['headers']['status-code']);
|
||||
$this->assertEquals(201, $row2['headers']['status-code']);
|
||||
|
||||
// Test bulk update with operators
|
||||
$bulkUpdate = $this->client->call(Client::METHOD_PATCH, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'data' => [
|
||||
'releaseYear' => Operator::increment(10)->toString(),
|
||||
],
|
||||
'queries' => [
|
||||
Query::startsWith('title', 'Bulk Test')->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $bulkUpdate['headers']['status-code']);
|
||||
$this->assertGreaterThanOrEqual(2, $bulkUpdate['body']['total']);
|
||||
|
||||
// Verify the updates
|
||||
$verify1 = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row1['body']['$id'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $verify1['headers']['status-code']);
|
||||
$this->assertEquals(2030, $verify1['body']['releaseYear']);
|
||||
|
||||
$verify2 = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows/' . $row2['body']['$id'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $verify2['headers']['status-code']);
|
||||
$this->assertEquals(2031, $verify2['body']['releaseYear']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateRow
|
||||
*/
|
||||
|
||||
@@ -55,6 +55,23 @@ class DatabasesCustomServerTest extends Scope
|
||||
$this->assertEquals($test1['body']['$id'], $databases['body']['databases'][0]['$id']);
|
||||
$this->assertEquals($test2['body']['$id'], $databases['body']['databases'][1]['$id']);
|
||||
|
||||
/**
|
||||
* Test for SUCCESS with total=false
|
||||
*/
|
||||
$databasesWithIncludeTotalFalse = $this->client->call(Client::METHOD_GET, '/tablesdb', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'total' => false
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $databasesWithIncludeTotalFalse['headers']['status-code']);
|
||||
$this->assertIsArray($databasesWithIncludeTotalFalse['body']);
|
||||
$this->assertIsArray($databasesWithIncludeTotalFalse['body']['databases']);
|
||||
$this->assertIsInt($databasesWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertEquals(0, $databasesWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertGreaterThan(0, count($databasesWithIncludeTotalFalse['body']['databases']));
|
||||
|
||||
$base = array_reverse($databases['body']['databases']);
|
||||
|
||||
$databases = $this->client->call(Client::METHOD_GET, '/tablesdb', array_merge([
|
||||
|
||||
@@ -6,6 +6,7 @@ use Tests\E2E\Client;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Operator;
|
||||
use Utopia\Database\Query;
|
||||
|
||||
trait TransactionsBase
|
||||
@@ -5561,4 +5562,395 @@ trait TransactionsBase
|
||||
$this->assertEquals('Updated after upsert', $response['body']['name']);
|
||||
$this->assertEquals(20, $response['body']['counter']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test array operators in transactions using updateRow with transactionId
|
||||
* This tests the fix for operators not being parsed when stored in transaction logs
|
||||
*/
|
||||
public function testArrayOperatorsWithUpdateRow(): void
|
||||
{
|
||||
// Create database
|
||||
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'ArrayOperatorsTestDB'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
// Create table with array column
|
||||
$table = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'Items',
|
||||
'permissions' => [
|
||||
Permission::create(Role::any()),
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$tableId = $table['body']['$id'];
|
||||
|
||||
// Create array column
|
||||
$column = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/string", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'key' => 'items',
|
||||
'size' => 255,
|
||||
'required' => false,
|
||||
'array' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $column['headers']['status-code']);
|
||||
sleep(2); // Wait for column to be created
|
||||
|
||||
// Create initial row with some items
|
||||
$row = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'rowId' => 'test-row',
|
||||
'data' => [
|
||||
'items' => ['item1', 'item2', 'item3', 'item4']
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $row['headers']['status-code']);
|
||||
$this->assertEquals(['item1', 'item2', 'item3', 'item4'], $row['body']['items']);
|
||||
|
||||
// Create transaction
|
||||
$transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(201, $transaction['headers']['status-code']);
|
||||
$transactionId = $transaction['body']['$id'];
|
||||
|
||||
// Test arrayRemove operator
|
||||
$updateResponse = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/test-row", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'transactionId' => $transactionId,
|
||||
'data' => [
|
||||
'items' => Operator::arrayRemove('item2')->toString()
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $updateResponse['headers']['status-code']);
|
||||
|
||||
// Test arrayInsert operator
|
||||
$updateResponse = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/test-row", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'transactionId' => $transactionId,
|
||||
'data' => [
|
||||
'items' => Operator::arrayInsert(2, 'newItem')->toString()
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $updateResponse['headers']['status-code']);
|
||||
|
||||
// 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']);
|
||||
|
||||
// Verify the operations were applied correctly
|
||||
$row = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/test-row", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $row['headers']['status-code']);
|
||||
// After removing item2: ['item1', 'item3', 'item4']
|
||||
// After inserting 'newItem' at index 2: ['item1', 'item3', 'newItem', 'item4']
|
||||
$this->assertEquals(['item1', 'item3', 'newItem', 'item4'], $row['body']['items']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test array operators in transactions using createOperations
|
||||
* This tests the fix for operators not being parsed in bulk operation creation
|
||||
*/
|
||||
public function testArrayOperatorsWithCreateOperations(): void
|
||||
{
|
||||
// Create database
|
||||
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'ArrayOperatorsBulkTestDB'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
// Create table with array column
|
||||
$table = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'Tags',
|
||||
'permissions' => [
|
||||
Permission::create(Role::any()),
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$tableId = $table['body']['$id'];
|
||||
|
||||
// Create array column
|
||||
$column = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/string", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'key' => 'tags',
|
||||
'size' => 255,
|
||||
'required' => false,
|
||||
'array' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $column['headers']['status-code']);
|
||||
sleep(2); // Wait for column to be created
|
||||
|
||||
// Create initial row
|
||||
$row = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'rowId' => 'doc1',
|
||||
'data' => [
|
||||
'tags' => ['php', 'javascript', 'python', 'ruby']
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $row['headers']['status-code']);
|
||||
|
||||
// Create transaction
|
||||
$transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(201, $transaction['headers']['status-code']);
|
||||
$transactionId = $transaction['body']['$id'];
|
||||
|
||||
// Create operations using bulk createOperations endpoint with array operators
|
||||
$operations = $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' => [
|
||||
[
|
||||
'action' => 'update',
|
||||
'databaseId' => $databaseId,
|
||||
'tableId' => $tableId,
|
||||
'rowId' => 'doc1',
|
||||
'data' => [
|
||||
'tags' => Operator::arrayRemove('javascript')->toString()
|
||||
]
|
||||
],
|
||||
[
|
||||
'action' => 'update',
|
||||
'databaseId' => $databaseId,
|
||||
'tableId' => $tableId,
|
||||
'rowId' => 'doc1',
|
||||
'data' => [
|
||||
'tags' => Operator::arrayAppend(['go', 'rust'])->toString()
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $operations['headers']['status-code']);
|
||||
$this->assertEquals(2, $operations['body']['operations']);
|
||||
|
||||
// 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']);
|
||||
|
||||
// Verify the operations were applied correctly
|
||||
$row = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc1", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $row['headers']['status-code']);
|
||||
// After removing 'javascript': ['php', 'python', 'ruby']
|
||||
// After appending ['go', 'rust']: ['php', 'python', 'ruby', 'go', 'rust']
|
||||
$this->assertEquals(['php', 'python', 'ruby', 'go', 'rust'], $row['body']['tags']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test multiple array operators in a single transaction
|
||||
* This tests all common array operators to ensure comprehensive coverage
|
||||
*/
|
||||
public function testMultipleArrayOperators(): void
|
||||
{
|
||||
// Create database
|
||||
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'MultipleOperatorsTestDB'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
// Create table
|
||||
$table = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'tableId' => ID::unique(),
|
||||
'name' => 'Arrays',
|
||||
'permissions' => [
|
||||
Permission::create(Role::any()),
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $table['headers']['status-code']);
|
||||
$tableId = $table['body']['$id'];
|
||||
|
||||
// Create multiple array columns
|
||||
$columns = [
|
||||
['columnId' => 'list1', 'name' => 'List1'],
|
||||
['columnId' => 'list2', 'name' => 'List2'],
|
||||
['columnId' => 'list3', 'name' => 'List3'],
|
||||
];
|
||||
|
||||
foreach ($columns as $col) {
|
||||
$column = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/string", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'key' => $col['columnId'],
|
||||
'size' => 255,
|
||||
'required' => false,
|
||||
'array' => true,
|
||||
]);
|
||||
$this->assertEquals(202, $column['headers']['status-code']);
|
||||
}
|
||||
|
||||
sleep(2); // Wait for columns to be created
|
||||
|
||||
// Create initial row
|
||||
$row = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'rowId' => 'multi-ops',
|
||||
'data' => [
|
||||
'list1' => ['a', 'b', 'c'],
|
||||
'list2' => ['x', 'y', 'z'],
|
||||
'list3' => ['1', '2', '3', '4', '5']
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $row['headers']['status-code']);
|
||||
|
||||
// Create transaction
|
||||
$transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(201, $transaction['headers']['status-code']);
|
||||
$transactionId = $transaction['body']['$id'];
|
||||
|
||||
// Test arrayPrepend
|
||||
$this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/multi-ops", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'transactionId' => $transactionId,
|
||||
'data' => [
|
||||
'list1' => Operator::arrayPrepend(['z'])->toString()
|
||||
]
|
||||
]);
|
||||
|
||||
// Test arrayAppend
|
||||
$this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/multi-ops", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'transactionId' => $transactionId,
|
||||
'data' => [
|
||||
'list2' => Operator::arrayAppend(['w'])->toString()
|
||||
]
|
||||
]);
|
||||
|
||||
// Test arrayRemove
|
||||
$this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/multi-ops", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'transactionId' => $transactionId,
|
||||
'data' => [
|
||||
'list3' => Operator::arrayRemove('3')->toString()
|
||||
]
|
||||
]);
|
||||
|
||||
// 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']);
|
||||
|
||||
// Verify all operations were applied correctly
|
||||
$row = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/multi-ops", array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $row['headers']['status-code']);
|
||||
$this->assertEquals(['z', 'a', 'b', 'c'], $row['body']['list1'], 'arrayPrepend should add element at the beginning');
|
||||
$this->assertEquals(['x', 'y', 'z', 'w'], $row['body']['list2'], 'arrayAppend should add element at the end');
|
||||
$this->assertEquals(['1', '2', '4', '5'], $row['body']['list3'], 'arrayRemove should remove the element');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,6 +271,29 @@ trait FunctionsBase
|
||||
return $template;
|
||||
}
|
||||
|
||||
protected function helperGetLatestCommit(string $owner, string $repository): ?string
|
||||
{
|
||||
$ch = curl_init("https://api.github.com/repos/{$owner}/{$repository}/commits/main");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'User-Agent: Appwrite',
|
||||
'Accept: application/vnd.github.v3+json'
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode === 200) {
|
||||
$commitData = json_decode($response, true);
|
||||
if (isset($commitData['sha'])) {
|
||||
return $commitData['sha'];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function createExecution(string $functionId, mixed $params = []): mixed
|
||||
{
|
||||
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([
|
||||
|
||||
@@ -376,6 +376,23 @@ class FunctionsCustomClientTest extends Scope
|
||||
$this->assertGreaterThan(0, $templates['body']['total']);
|
||||
$this->assertIsArray($templates['body']['templates']);
|
||||
|
||||
/**
|
||||
* Test for SUCCESS with total=false
|
||||
*/
|
||||
$templatesWithIncludeTotalFalse = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'total' => false
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $templatesWithIncludeTotalFalse['headers']['status-code']);
|
||||
$this->assertIsArray($templatesWithIncludeTotalFalse['body']);
|
||||
$this->assertIsArray($templatesWithIncludeTotalFalse['body']['templates']);
|
||||
$this->assertIsInt($templatesWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertEquals(0, $templatesWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertGreaterThan(0, count($templatesWithIncludeTotalFalse['body']['templates']));
|
||||
|
||||
foreach ($templates['body']['templates'] as $template) {
|
||||
$this->assertArrayHasKey('name', $template);
|
||||
$this->assertArrayHasKey('id', $template);
|
||||
|
||||
@@ -361,7 +361,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$starterTemplate = $this->getTemplate('starter');
|
||||
$this->assertEquals(200, $starterTemplate['headers']['status-code']);
|
||||
|
||||
$phpRuntime = array_values(array_filter($starterTemplate['body']['runtimes'], function ($runtime) {
|
||||
$runtime = array_values(array_filter($starterTemplate['body']['runtimes'], function ($runtime) {
|
||||
return $runtime['name'] === 'node-22';
|
||||
}))[0];
|
||||
|
||||
@@ -374,15 +374,15 @@ class FunctionsCustomServerTest extends Scope
|
||||
'name' => $starterTemplate['body']['name'],
|
||||
'runtime' => 'node-22',
|
||||
'execute' => $starterTemplate['body']['permissions'],
|
||||
'entrypoint' => $phpRuntime['entrypoint'],
|
||||
'entrypoint' => $runtime['entrypoint'],
|
||||
'events' => $starterTemplate['body']['events'],
|
||||
'schedule' => $starterTemplate['body']['cron'],
|
||||
'timeout' => $starterTemplate['body']['timeout'],
|
||||
'commands' => $phpRuntime['commands'],
|
||||
'commands' => $runtime['commands'],
|
||||
'scopes' => $starterTemplate['body']['scopes'],
|
||||
'templateRepository' => $starterTemplate['body']['providerRepositoryId'],
|
||||
'templateOwner' => $starterTemplate['body']['providerOwner'],
|
||||
'templateRootDirectory' => $phpRuntime['providerRootDirectory'],
|
||||
'templateRootDirectory' => $runtime['providerRootDirectory'],
|
||||
'templateVersion' => $starterTemplate['body']['providerVersion'],
|
||||
]
|
||||
);
|
||||
@@ -399,38 +399,51 @@ class FunctionsCustomServerTest extends Scope
|
||||
'activate' => true,
|
||||
'repository' => $starterTemplate['body']['providerRepositoryId'],
|
||||
'owner' => $starterTemplate['body']['providerOwner'],
|
||||
'rootDirectory' => $phpRuntime['providerRootDirectory'],
|
||||
'version' => $starterTemplate['body']['providerVersion'],
|
||||
'rootDirectory' => $runtime['providerRootDirectory'],
|
||||
'type' => 'tag',
|
||||
'reference' => $starterTemplate['body']['providerVersion'],
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(202, $deployment['headers']['status-code']);
|
||||
$this->assertNotEmpty($deployment['body']['$id']);
|
||||
|
||||
$deployment = $this->getDeployment($functionId, $deployment['body']['$id']);
|
||||
// Wait for deployment to be ready
|
||||
$deploymentId = $deployment['body']['$id'];
|
||||
$this->assertEventually(function () use ($functionId, $deploymentId) {
|
||||
$deployment = $this->getDeployment($functionId, $deploymentId);
|
||||
$this->assertEquals('ready', $deployment['body']['status']);
|
||||
}, 50000, 500);
|
||||
|
||||
// Verify deployment sizes
|
||||
$deployment = $this->getDeployment($functionId, $deploymentId);
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertEquals(0, $deployment['body']['sourceSize']);
|
||||
$this->assertEquals(0, $deployment['body']['buildSize']);
|
||||
$this->assertEquals(0, $deployment['body']['totalSize']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['sourceSize']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['buildSize']);
|
||||
$totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize'];
|
||||
$this->assertEquals($totalSize, $deployment['body']['totalSize']);
|
||||
|
||||
$deployments = $this->listDeployments($functionId);
|
||||
|
||||
$this->assertEquals(200, $deployments['headers']['status-code']);
|
||||
$this->assertEquals(1, $deployments['body']['total']);
|
||||
|
||||
/**
|
||||
* Test for SUCCESS with total=false
|
||||
*/
|
||||
$deploymentsWithIncludeTotalFalse = $this->listDeployments($functionId, ['total' => false]);
|
||||
|
||||
$this->assertEquals(200, $deploymentsWithIncludeTotalFalse['headers']['status-code']);
|
||||
$this->assertIsArray($deploymentsWithIncludeTotalFalse['body']);
|
||||
$this->assertIsArray($deploymentsWithIncludeTotalFalse['body']['deployments']);
|
||||
$this->assertIsInt($deploymentsWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertEquals(0, $deploymentsWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertGreaterThan(0, count($deploymentsWithIncludeTotalFalse['body']['deployments']));
|
||||
|
||||
$lastDeployment = $deployments['body']['deployments'][0];
|
||||
|
||||
$this->assertNotEmpty($lastDeployment['$id']);
|
||||
$this->assertEquals(0, $lastDeployment['sourceSize']);
|
||||
|
||||
$deploymentId = $lastDeployment['$id'];
|
||||
|
||||
$this->assertEventually(function () use ($functionId, $deploymentId) {
|
||||
$deployment = $this->getDeployment($functionId, $deploymentId);
|
||||
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertEquals('ready', $deployment['body']['status']);
|
||||
}, 50000, 1000);
|
||||
$this->assertGreaterThan(0, $lastDeployment['sourceSize']);
|
||||
|
||||
$function = $this->getFunction($functionId);
|
||||
|
||||
@@ -499,7 +512,144 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertEquals($deployment['body']['$id'], $function['body']['deploymentId']);
|
||||
$this->assertEquals($deployment['body']['$createdAt'], $function['body']['deploymentCreatedAt']);
|
||||
|
||||
$function = $this->cleanupFunction($functionId);
|
||||
$this->cleanupFunction($functionId);
|
||||
}
|
||||
|
||||
public function testCreateFunctionAndDeploymentFromTemplateBranch()
|
||||
{
|
||||
$starterTemplate = $this->getTemplate('starter');
|
||||
$this->assertEquals(200, $starterTemplate['headers']['status-code']);
|
||||
|
||||
$runtime = array_values(array_filter($starterTemplate['body']['runtimes'], function ($runtime) {
|
||||
return $runtime['name'] === 'node-22';
|
||||
}))[0];
|
||||
|
||||
// If this fails, the template has variables, and this test needs to be updated
|
||||
$this->assertEmpty($starterTemplate['body']['variables']);
|
||||
|
||||
$function = $this->createFunction(
|
||||
[
|
||||
'functionId' => ID::unique(),
|
||||
'name' => $starterTemplate['body']['name'] . ' - Branch Test',
|
||||
'runtime' => 'node-22',
|
||||
'execute' => $starterTemplate['body']['permissions'],
|
||||
'entrypoint' => $runtime['entrypoint'],
|
||||
'events' => $starterTemplate['body']['events'],
|
||||
'schedule' => $starterTemplate['body']['cron'],
|
||||
'timeout' => $starterTemplate['body']['timeout'],
|
||||
'commands' => $runtime['commands'],
|
||||
'scopes' => $starterTemplate['body']['scopes'],
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(201, $function['headers']['status-code']);
|
||||
$this->assertNotEmpty($function['body']['$id']);
|
||||
|
||||
$functionId = $function['body']['$id'] ?? '';
|
||||
|
||||
// Deploy using branch
|
||||
$deployment = $this->createTemplateDeployment(
|
||||
$functionId,
|
||||
[
|
||||
'resourceId' => ID::unique(),
|
||||
'activate' => true,
|
||||
'repository' => $starterTemplate['body']['providerRepositoryId'],
|
||||
'owner' => $starterTemplate['body']['providerOwner'],
|
||||
'rootDirectory' => $runtime['providerRootDirectory'],
|
||||
'type' => 'branch',
|
||||
'reference' => 'main',
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(202, $deployment['headers']['status-code']);
|
||||
$this->assertNotEmpty($deployment['body']['$id']);
|
||||
|
||||
$deploymentId = $deployment['body']['$id'];
|
||||
$this->assertEventually(function () use ($functionId, $deploymentId) {
|
||||
$deployment = $this->getDeployment($functionId, $deploymentId);
|
||||
$this->assertEquals('ready', $deployment['body']['status']);
|
||||
}, 50000, 500);
|
||||
|
||||
$deployment = $this->getDeployment($functionId, $deploymentId);
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['sourceSize']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['buildSize']);
|
||||
$totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize'];
|
||||
$this->assertEquals($totalSize, $deployment['body']['totalSize']);
|
||||
|
||||
$this->cleanupFunction($functionId);
|
||||
}
|
||||
|
||||
public function testCreateFunctionAndDeploymentFromTemplateCommit()
|
||||
{
|
||||
$starterTemplate = $this->getTemplate('starter');
|
||||
$this->assertEquals(200, $starterTemplate['headers']['status-code']);
|
||||
|
||||
// Get latest commit using helper function
|
||||
$latestCommit = $this->helperGetLatestCommit(
|
||||
$starterTemplate['body']['providerOwner'],
|
||||
$starterTemplate['body']['providerRepositoryId']
|
||||
);
|
||||
$this->assertNotNull($latestCommit);
|
||||
|
||||
$runtime = array_values(array_filter($starterTemplate['body']['runtimes'], function ($runtime) {
|
||||
return $runtime['name'] === 'node-22';
|
||||
}))[0];
|
||||
|
||||
// If this fails, the template has variables, and this test needs to be updated
|
||||
$this->assertEmpty($starterTemplate['body']['variables']);
|
||||
|
||||
$function = $this->createFunction(
|
||||
[
|
||||
'functionId' => ID::unique(),
|
||||
'name' => $starterTemplate['body']['name'] . ' - Commit Test',
|
||||
'runtime' => 'node-22',
|
||||
'execute' => $starterTemplate['body']['permissions'],
|
||||
'entrypoint' => $runtime['entrypoint'],
|
||||
'events' => $starterTemplate['body']['events'],
|
||||
'schedule' => $starterTemplate['body']['cron'],
|
||||
'timeout' => $starterTemplate['body']['timeout'],
|
||||
'commands' => $runtime['commands'],
|
||||
'scopes' => $starterTemplate['body']['scopes'],
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(201, $function['headers']['status-code']);
|
||||
$this->assertNotEmpty($function['body']['$id']);
|
||||
|
||||
$functionId = $function['body']['$id'] ?? '';
|
||||
|
||||
// Deploy using commit
|
||||
$deployment = $this->createTemplateDeployment(
|
||||
$functionId,
|
||||
[
|
||||
'resourceId' => ID::unique(),
|
||||
'activate' => true,
|
||||
'repository' => $starterTemplate['body']['providerRepositoryId'],
|
||||
'owner' => $starterTemplate['body']['providerOwner'],
|
||||
'rootDirectory' => $runtime['providerRootDirectory'],
|
||||
'type' => 'commit',
|
||||
'reference' => $latestCommit,
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(202, $deployment['headers']['status-code']);
|
||||
$this->assertNotEmpty($deployment['body']['$id']);
|
||||
|
||||
$deploymentId = $deployment['body']['$id'];
|
||||
$this->assertEventually(function () use ($functionId, $deploymentId) {
|
||||
$deployment = $this->getDeployment($functionId, $deploymentId);
|
||||
$this->assertEquals('ready', $deployment['body']['status']);
|
||||
}, 50000, 500);
|
||||
|
||||
$deployment = $this->getDeployment($functionId, $deploymentId);
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['sourceSize']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['buildSize']);
|
||||
$totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize'];
|
||||
$this->assertEquals($totalSize, $deployment['body']['totalSize']);
|
||||
|
||||
$this->cleanupFunction($functionId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1004,6 +1154,18 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertCount(1, $executions['body']['executions']);
|
||||
$this->assertEquals($data['deploymentId'], $executions['body']['executions'][0]['deploymentId']);
|
||||
|
||||
/**
|
||||
* Test for SUCCESS with total=false
|
||||
*/
|
||||
$executionsWithIncludeTotalFalse = $this->listExecutions($data['functionId'], ['total' => false]);
|
||||
|
||||
$this->assertEquals(200, $executionsWithIncludeTotalFalse['headers']['status-code']);
|
||||
$this->assertIsArray($executionsWithIncludeTotalFalse['body']);
|
||||
$this->assertIsArray($executionsWithIncludeTotalFalse['body']['executions']);
|
||||
$this->assertIsInt($executionsWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertEquals(0, $executionsWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertGreaterThan(0, count($executionsWithIncludeTotalFalse['body']['executions']));
|
||||
|
||||
$executions = $this->listExecutions($data['functionId'], [
|
||||
'queries' => [
|
||||
Query::equal('deploymentId', [$data['deploymentId']])->toString(),
|
||||
|
||||
@@ -173,4 +173,214 @@ class AvatarsTest extends Scope
|
||||
|
||||
return $initials['body'];
|
||||
}
|
||||
|
||||
public function testGetScreenshot()
|
||||
{
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::GET_SCREENSHOT);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'url' => 'https://appwrite.io',
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
],
|
||||
];
|
||||
|
||||
$screenshot = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $screenshot['headers']['status-code']);
|
||||
$this->assertNotEmpty($screenshot['body']);
|
||||
|
||||
// Debug: Print the actual response if it's not an image
|
||||
if (!str_contains($screenshot['headers']['content-type'], 'image/')) {
|
||||
echo "Response content-type: " . $screenshot['headers']['content-type'] . "\n";
|
||||
echo "Response body: " . print_r($screenshot['body'], true) . "\n";
|
||||
}
|
||||
|
||||
$this->assertStringContainsString('image/', $screenshot['headers']['content-type']);
|
||||
|
||||
return $screenshot['body'];
|
||||
}
|
||||
|
||||
public function testGetScreenshotWithOriginalDimensions()
|
||||
{
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::GET_SCREENSHOT);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'url' => 'https://appwrite.io',
|
||||
'width' => 0,
|
||||
'height' => 0,
|
||||
],
|
||||
];
|
||||
|
||||
$screenshot = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $screenshot['headers']['status-code']);
|
||||
$this->assertNotEmpty($screenshot['body']);
|
||||
// Debug: Print the actual response if it's not an image
|
||||
if (!str_contains($screenshot['headers']['content-type'], 'image/')) {
|
||||
echo "Response content-type: " . $screenshot['headers']['content-type'] . "\n";
|
||||
echo "Response body: " . print_r($screenshot['body'], true) . "\n";
|
||||
}
|
||||
|
||||
$this->assertStringContainsString('image/', $screenshot['headers']['content-type']);
|
||||
|
||||
return $screenshot['body'];
|
||||
}
|
||||
|
||||
public function testGetScreenshotWithNewParameters()
|
||||
{
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::GET_SCREENSHOT);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'url' => 'https://appwrite.io',
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'viewportWidth' => 1920,
|
||||
'viewportHeight' => 1080,
|
||||
'scale' => 1.5,
|
||||
'theme' => 'dark',
|
||||
'userAgent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
||||
'fullpage' => true,
|
||||
'locale' => 'en-US',
|
||||
'timezone' => 'America/New_York',
|
||||
'latitude' => 40.7128,
|
||||
'longitude' => -74.0060,
|
||||
'accuracy' => 100,
|
||||
'touch' => true,
|
||||
'permissions' => [
|
||||
'geolocation',
|
||||
'camera',
|
||||
'microphone',
|
||||
'notifications',
|
||||
'clipboard-read',
|
||||
'clipboard-write'
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$screenshot = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $screenshot['headers']['status-code']);
|
||||
$this->assertNotEmpty($screenshot['body']);
|
||||
|
||||
// Debug: Print the actual response if it's not an image
|
||||
if (!str_contains($screenshot['headers']['content-type'], 'image/')) {
|
||||
echo "Response content-type: " . $screenshot['headers']['content-type'] . "\n";
|
||||
echo "Response body: " . print_r($screenshot['body'], true) . "\n";
|
||||
}
|
||||
|
||||
$this->assertStringContainsString('image/', $screenshot['headers']['content-type']);
|
||||
|
||||
return $screenshot['body'];
|
||||
}
|
||||
|
||||
public function testGetScreenshotWithViewportParameters()
|
||||
{
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::GET_SCREENSHOT);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'url' => 'https://appwrite.io',
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'viewportWidth' => 1920,
|
||||
'viewportHeight' => 1080,
|
||||
],
|
||||
];
|
||||
|
||||
$screenshot = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $screenshot['headers']['status-code']);
|
||||
$this->assertNotEmpty($screenshot['body']);
|
||||
$this->assertStringContainsString('image/', $screenshot['headers']['content-type']);
|
||||
|
||||
return $screenshot['body'];
|
||||
}
|
||||
|
||||
public function testGetScreenshotWithPermissions()
|
||||
{
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::GET_SCREENSHOT);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'url' => 'https://appwrite.io',
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'permissions' => [
|
||||
'geolocation',
|
||||
'camera',
|
||||
'microphone',
|
||||
'notifications'
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$screenshot = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $screenshot['headers']['status-code']);
|
||||
$this->assertNotEmpty($screenshot['body']);
|
||||
// Debug: Print the actual response if it's not an image
|
||||
if (!str_contains($screenshot['headers']['content-type'], 'image/')) {
|
||||
echo "Response content-type: " . $screenshot['headers']['content-type'] . "\n";
|
||||
echo "Response body: " . print_r($screenshot['body'], true) . "\n";
|
||||
}
|
||||
|
||||
$this->assertStringContainsString('image/', $screenshot['headers']['content-type']);
|
||||
|
||||
return $screenshot['body'];
|
||||
}
|
||||
|
||||
public function testGetScreenshotWithInvalidPermissions()
|
||||
{
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::GET_SCREENSHOT);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'url' => 'https://appwrite.io',
|
||||
'width' => 800,
|
||||
'height' => 600,
|
||||
'permissions' => [
|
||||
'geolocation',
|
||||
'invalid-permission',
|
||||
'camera'
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$screenshot = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(200, $screenshot['headers']['status-code']);
|
||||
$this->assertArrayHasKey('errors', $screenshot['body']);
|
||||
$this->assertNotEmpty($screenshot['body']['errors']);
|
||||
$this->assertStringContainsString('Invalid `permissions` param', $screenshot['body']['errors'][0]['message']);
|
||||
|
||||
return $screenshot['body'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,10 +288,12 @@ trait Base
|
||||
public const string GET_FAVICON = 'get_favicon';
|
||||
public const string GET_QRCODE = 'get_qrcode';
|
||||
public const string GET_USER_INITIALS = 'get_user_initials';
|
||||
public const string GET_SCREENSHOT = 'get_screenshot';
|
||||
|
||||
// Providers
|
||||
public const string CREATE_MAILGUN_PROVIDER = 'create_mailgun_provider';
|
||||
public const string CREATE_SENDGRID_PROVIDER = 'create_sendgrid_provider';
|
||||
public const string CREATE_RESEND_PROVIDER = 'create_resend_provider';
|
||||
public const string CREATE_SMTP_PROVIDER = 'create_smtp_provider';
|
||||
public const string CREATE_TWILIO_PROVIDER = 'create_twilio_provider';
|
||||
public const string CREATE_TELESIGN_PROVIDER = 'create_telesign_provider';
|
||||
@@ -304,6 +306,7 @@ trait Base
|
||||
public const string GET_PROVIDER = 'get_provider';
|
||||
public const string UPDATE_MAILGUN_PROVIDER = 'update_mailgun_provider';
|
||||
public const string UPDATE_SENDGRID_PROVIDER = 'update_sendgrid_provider';
|
||||
public const string UPDATE_RESEND_PROVIDER = 'update_resend_provider';
|
||||
public const string UPDATE_SMTP_PROVIDER = 'update_smtp_provider';
|
||||
public const string UPDATE_TWILIO_PROVIDER = 'update_twilio_provider';
|
||||
public const string UPDATE_TELESIGN_PROVIDER = 'update_telesign_provider';
|
||||
@@ -1779,6 +1782,12 @@ trait Base
|
||||
status
|
||||
}
|
||||
}';
|
||||
case self::GET_SCREENSHOT:
|
||||
return 'query getScreenshot($url: String!, $width: Int, $height: Int, $viewportWidth: Int, $viewportHeight: Int, $scale: Float, $theme: String, $userAgent: String, $fullpage: Boolean, $locale: String, $timezone: String, $latitude: Float, $longitude: Float, $accuracy: Float, $touch: Boolean, $permissions: [String!]) {
|
||||
avatarsGetScreenshot(url: $url, width: $width, height: $height, viewportWidth: $viewportWidth, viewportHeight: $viewportHeight, scale: $scale, theme: $theme, userAgent: $userAgent, fullpage: $fullpage, locale: $locale, timezone: $timezone, latitude: $latitude, longitude: $longitude, accuracy: $accuracy, touch: $touch, permissions: $permissions) {
|
||||
status
|
||||
}
|
||||
}';
|
||||
case self::GET_ACCOUNT:
|
||||
return 'query getAccount {
|
||||
accountGet {
|
||||
@@ -2494,6 +2503,16 @@ trait Base
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
case self::CREATE_RESEND_PROVIDER:
|
||||
return 'mutation createResendProvider($providerId: String!, $name: String!, $apiKey: String!, $fromName: String!, $fromEmail: String!, $replyToName: String, $replyToEmail: String) {
|
||||
messagingCreateResendProvider(providerId: $providerId, name: $name, apiKey: $apiKey, fromName: $fromName, fromEmail: $fromEmail, replyToName: $replyToName, replyToEmail: $replyToEmail) {
|
||||
_id
|
||||
name
|
||||
provider
|
||||
type
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
case self::CREATE_SMTP_PROVIDER:
|
||||
return 'mutation createSmtpProvider($providerId: String!, $name: String!, $host: String!, $port: Int!, $username: String!, $password: String!, $encryption: String!, $autoTLS: Boolean! $fromName: String!, $fromEmail: String!, $replyToName: String, $replyToEmail: String) {
|
||||
messagingCreateSmtpProvider(providerId: $providerId, name: $name, host: $host, port: $port, username: $username, password: $password, encryption: $encryption, autoTLS: $autoTLS, fromName: $fromName, fromEmail: $fromEmail, replyToName: $replyToName, replyToEmail: $replyToEmail) {
|
||||
@@ -2618,6 +2637,16 @@ trait Base
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
case self::UPDATE_RESEND_PROVIDER:
|
||||
return 'mutation messagingUpdateResendProvider($providerId: String!, $name: String!, $apiKey: String!, $enabled: Boolean, $fromName: String, $fromEmail: String, $replyToName: String, $replyToEmail: String) {
|
||||
messagingUpdateResendProvider(providerId: $providerId, name: $name, apiKey: $apiKey, enabled: $enabled, fromName: $fromName, fromEmail: $fromEmail, replyToName: $replyToName, replyToEmail: $replyToEmail) {
|
||||
_id
|
||||
name
|
||||
provider
|
||||
type
|
||||
enabled
|
||||
}
|
||||
}';
|
||||
case self::UPDATE_SMTP_PROVIDER:
|
||||
return 'mutation updateSmtpProvider($providerId: String!, $name: String!, $host: String!, $port: Int!, $username: String!, $password: String!, $encryption: String!, $autoTLS: Boolean!, $fromName: String, $fromEmail: String, $enabled: Boolean) {
|
||||
messagingUpdateSmtpProvider(providerId: $providerId, name: $name, host: $host, port: $port, username: $username, password: $password, encryption: $encryption, autoTLS: $autoTLS, fromName: $fromName, fromEmail: $fromEmail, enabled: $enabled) {
|
||||
|
||||
@@ -26,6 +26,13 @@ class MessagingTest extends Scope
|
||||
'fromName' => 'Sender Name',
|
||||
'fromEmail' => 'sender-email@my-domain.com',
|
||||
],
|
||||
'Resend' => [
|
||||
'providerId' => ID::unique(),
|
||||
'name' => 'Resend1',
|
||||
'apiKey' => 'my-apikey',
|
||||
'fromName' => 'Sender Name',
|
||||
'fromEmail' => 'sender-email@my-domain.com',
|
||||
],
|
||||
'Mailgun' => [
|
||||
'providerId' => ID::unique(),
|
||||
'name' => 'Mailgun1',
|
||||
@@ -123,45 +130,50 @@ class MessagingTest extends Scope
|
||||
'name' => 'Sengrid2',
|
||||
'apiKey' => 'my-apikey',
|
||||
],
|
||||
'Mailgun' => [
|
||||
'Resend' => [
|
||||
'providerId' => $providers[1]['_id'],
|
||||
'name' => 'Resend2',
|
||||
'apiKey' => 'my-apikey',
|
||||
],
|
||||
'Mailgun' => [
|
||||
'providerId' => $providers[2]['_id'],
|
||||
'name' => 'Mailgun2',
|
||||
'apiKey' => 'my-apikey',
|
||||
'domain' => 'my-domain',
|
||||
],
|
||||
'Twilio' => [
|
||||
'providerId' => $providers[2]['_id'],
|
||||
'providerId' => $providers[3]['_id'],
|
||||
'name' => 'Twilio2',
|
||||
'accountSid' => 'my-accountSid',
|
||||
'authToken' => 'my-authToken',
|
||||
],
|
||||
'Telesign' => [
|
||||
'providerId' => $providers[3]['_id'],
|
||||
'providerId' => $providers[4]['_id'],
|
||||
'name' => 'Telesign2',
|
||||
'customerId' => 'my-username',
|
||||
'apiKey' => 'my-password',
|
||||
],
|
||||
'Textmagic' => [
|
||||
'providerId' => $providers[4]['_id'],
|
||||
'providerId' => $providers[5]['_id'],
|
||||
'name' => 'Textmagic2',
|
||||
'username' => 'my-username',
|
||||
'apiKey' => 'my-apikey',
|
||||
],
|
||||
'Msg91' => [
|
||||
'providerId' => $providers[5]['_id'],
|
||||
'providerId' => $providers[6]['_id'],
|
||||
'name' => 'Ms91-2',
|
||||
'senderId' => 'my-senderid',
|
||||
'authKey' => 'my-authkey',
|
||||
'templateId' => '123456',
|
||||
],
|
||||
'Vonage' => [
|
||||
'providerId' => $providers[6]['_id'],
|
||||
'providerId' => $providers[7]['_id'],
|
||||
'name' => 'Vonage2',
|
||||
'apiKey' => 'my-apikey',
|
||||
'apiSecret' => 'my-apisecret',
|
||||
],
|
||||
'Fcm' => [
|
||||
'providerId' => $providers[7]['_id'],
|
||||
'providerId' => $providers[8]['_id'],
|
||||
'name' => 'FCM2',
|
||||
'serviceAccountJSON' => [
|
||||
'type' => 'service_account',
|
||||
@@ -171,7 +183,7 @@ class MessagingTest extends Scope
|
||||
]
|
||||
],
|
||||
'Apns' => [
|
||||
'providerId' => $providers[8]['_id'],
|
||||
'providerId' => $providers[9]['_id'],
|
||||
'name' => 'APNS2',
|
||||
'authKey' => 'my-authkey',
|
||||
'authKeyId' => 'my-authkeyid',
|
||||
@@ -205,7 +217,7 @@ class MessagingTest extends Scope
|
||||
], [
|
||||
'query' => $this->getQuery('update_mailgun_provider'),
|
||||
'variables' => [
|
||||
'providerId' => $providers[1]['_id'],
|
||||
'providerId' => $providers[2]['_id'],
|
||||
'name' => 'Mailgun2',
|
||||
'apiKey' => 'my-apikey',
|
||||
'domain' => 'my-domain',
|
||||
@@ -213,7 +225,7 @@ class MessagingTest extends Scope
|
||||
'enabled' => false,
|
||||
]
|
||||
]);
|
||||
$providers[1] = $response['body']['data']['messagingUpdateMailgunProvider'];
|
||||
$providers[2] = $response['body']['data']['messagingUpdateMailgunProvider'];
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('Mailgun2', $response['body']['data']['messagingUpdateMailgunProvider']['name']);
|
||||
$this->assertEquals(false, $response['body']['data']['messagingUpdateMailgunProvider']['enabled']);
|
||||
|
||||
@@ -25,6 +25,13 @@ trait MessagingBase
|
||||
'apiKey' => 'my-apikey',
|
||||
'from' => 'sender-email@my-domain.com',
|
||||
],
|
||||
'resend' => [
|
||||
'providerId' => ID::unique(),
|
||||
'name' => 'Resend1',
|
||||
'apiKey' => 'my-apikey',
|
||||
'fromName' => 'Sender Name',
|
||||
'fromEmail' => 'sender-email@my-domain.com',
|
||||
],
|
||||
'mailgun' => [
|
||||
'providerId' => ID::unique(),
|
||||
'name' => 'Mailgun1',
|
||||
@@ -132,6 +139,10 @@ trait MessagingBase
|
||||
'name' => 'Sengrid2',
|
||||
'apiKey' => 'my-apikey',
|
||||
],
|
||||
'resend' => [
|
||||
'name' => 'Resend2',
|
||||
'apiKey' => 'my-apikey',
|
||||
],
|
||||
'mailgun' => [
|
||||
'name' => 'Mailgun2',
|
||||
'apiKey' => 'my-apikey',
|
||||
@@ -210,7 +221,7 @@ trait MessagingBase
|
||||
$providers[$index] = $response['body'];
|
||||
}
|
||||
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/messaging/providers/mailgun/' . $providers[1]['$id'], [
|
||||
$response = $this->client->call(Client::METHOD_PATCH, '/messaging/providers/mailgun/' . $providers[2]['$id'], [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
@@ -226,7 +237,7 @@ trait MessagingBase
|
||||
$this->assertEquals('Mailgun2', $response['body']['name']);
|
||||
$this->assertEquals(false, $response['body']['enabled']);
|
||||
|
||||
$providers[1] = $response['body'];
|
||||
$providers[2] = $response['body'];
|
||||
|
||||
return $providers;
|
||||
}
|
||||
@@ -269,7 +280,7 @@ trait MessagingBase
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(11, \count($response['body']['providers']));
|
||||
$this->assertEquals(12, \count($response['body']['providers']));
|
||||
|
||||
return $providers;
|
||||
}
|
||||
@@ -633,6 +644,24 @@ trait MessagingBase
|
||||
$this->assertEquals(1, $response['body']['total']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for SUCCESS with total=false
|
||||
*/
|
||||
$subscribersWithIncludeTotalFalse = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $data['topicId'] . '/subscribers', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), [
|
||||
'total' => false
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $subscribersWithIncludeTotalFalse['headers']['status-code']);
|
||||
$this->assertIsArray($subscribersWithIncludeTotalFalse['body']);
|
||||
$this->assertIsArray($subscribersWithIncludeTotalFalse['body']['subscribers']);
|
||||
$this->assertIsInt($subscribersWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertEquals(0, $subscribersWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertGreaterThan(0, count($subscribersWithIncludeTotalFalse['body']['subscribers']));
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
@@ -900,7 +900,7 @@ trait MigrationsBase
|
||||
/**
|
||||
* Import documents from a CSV file.
|
||||
*/
|
||||
public function testCreateCsvMigration(): void
|
||||
public function testCreateCSVImport(): void
|
||||
{
|
||||
// Make a database
|
||||
$response = $this->client->call(Client::METHOD_POST, '/databases', [
|
||||
@@ -1194,4 +1194,165 @@ trait MigrationsBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test CSV export with email notification
|
||||
*/
|
||||
public function testCreateCSVExport(): void
|
||||
{
|
||||
// Create a database
|
||||
$database = $this->client->call(Client::METHOD_POST, '/databases', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Test Export Database'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $database['headers']['status-code']);
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
// Create a collection
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'Test Export Collection',
|
||||
'permissions' => []
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$collectionId = $collection['body']['$id'];
|
||||
|
||||
// Create a simple attribute like the basic test
|
||||
$name = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'name',
|
||||
'size' => 255,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $name['headers']['status-code']);
|
||||
|
||||
// Create a simple attribute like the basic test
|
||||
$email = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'key' => 'email',
|
||||
'size' => 255,
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $email['headers']['status-code']);
|
||||
|
||||
\sleep(3);
|
||||
|
||||
// Create sample documents
|
||||
for ($i = 1; $i <= 10; $i++) {
|
||||
$doc = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'name' => 'Test User ' . $i,
|
||||
'email' => 'user' . $i . '@appwrite.io'
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $doc['headers']['status-code'], 'Failed to create document ' . $i);
|
||||
}
|
||||
|
||||
// Verify documents were created
|
||||
$docs = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $docs['headers']['status-code']);
|
||||
$this->assertEquals(10, $docs['body']['total'], 'Expected 10 documents but got ' . $docs['body']['total']);
|
||||
|
||||
// Perform CSV export with notification enabled (uses internal bucket)
|
||||
$migration = $this->client->call(Client::METHOD_POST, '/migrations/csv/exports', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()), [
|
||||
'resourceId' => $databaseId . ':' . $collectionId,
|
||||
'filename' => 'test-export',
|
||||
'columns' => [],
|
||||
'delimiter' => ',',
|
||||
'enclosure' => '"',
|
||||
'escape' => '\\',
|
||||
'header' => true,
|
||||
'notify' => true
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $migration['headers']['status-code']);
|
||||
$this->assertNotEmpty($migration['body']['$id']);
|
||||
$migrationId = $migration['body']['$id'];
|
||||
|
||||
$this->assertEventually(function () use ($migrationId) {
|
||||
$response = $this->client->call(Client::METHOD_GET, '/migrations/' . $migrationId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals('finished', $response['body']['stage']);
|
||||
$this->assertEquals('completed', $response['body']['status']);
|
||||
$this->assertEquals('Appwrite', $response['body']['source']);
|
||||
$this->assertEquals('CSV', $response['body']['destination']);
|
||||
|
||||
return true;
|
||||
}, 30000, 500);
|
||||
|
||||
// Check that email was sent with download link
|
||||
$lastEmail = $this->getLastEmail();
|
||||
$this->assertNotEmpty($lastEmail);
|
||||
$this->assertEquals('Your CSV export is ready', $lastEmail['subject']);
|
||||
$this->assertStringContainsStringIgnoringCase('Your data export has been completed successfully', $lastEmail['text']);
|
||||
|
||||
// Extract download URL from email HTML
|
||||
\preg_match('/href="([^"]*\/storage\/buckets\/[^"]*\/push[^"]*)"/', $lastEmail['html'], $matches);
|
||||
$this->assertNotEmpty($matches[1], 'Download URL not found in email');
|
||||
$downloadUrl = html_entity_decode($matches[1]);
|
||||
|
||||
// Parse the URL to extract components
|
||||
$components = \parse_url($downloadUrl);
|
||||
$this->assertNotEmpty($components);
|
||||
\parse_str($components['query'] ?? '', $queryParams);
|
||||
$this->assertArrayHasKey('jwt', $queryParams, 'JWT not found in download URL');
|
||||
$this->assertNotEmpty($queryParams['jwt']);
|
||||
$this->assertArrayHasKey('project', $queryParams, 'Project not found in download URL');
|
||||
$this->assertStringContainsString('/storage/buckets/default/files/', $downloadUrl);
|
||||
|
||||
// Test download with JWT
|
||||
$path = \str_replace('/v1', '', $components['path']);
|
||||
$downloadWithJwt = $this->client->call(Client::METHOD_GET, $path . '?project=' . $queryParams['project'] . '&jwt=' . $queryParams['jwt']);
|
||||
$this->assertEquals(200, $downloadWithJwt['headers']['status-code'], 'Failed to download file with JWT');
|
||||
|
||||
// Verify the downloaded content is valid CSV
|
||||
$csvData = $downloadWithJwt['body'];
|
||||
$this->assertNotEmpty($csvData, 'CSV export should not be empty');
|
||||
$this->assertStringContainsString('name', $csvData, 'CSV should contain the name column header');
|
||||
$this->assertStringContainsString('email', $csvData, 'CSV should contain the email column header');
|
||||
$this->assertStringContainsString('Test User 1', $csvData, 'CSV should contain test data');
|
||||
|
||||
// Cleanup
|
||||
$this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, [
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,9 +329,33 @@ trait SitesBase
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]);
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
protected function helperGetLatestCommit(string $owner, string $repository): ?string
|
||||
{
|
||||
$ch = curl_init("https://api.github.com/repos/{$owner}/{$repository}/commits/main");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'User-Agent: Appwrite',
|
||||
'Accept: application/vnd.github.v3+json'
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode === 200) {
|
||||
$commitData = json_decode($response, true);
|
||||
if (isset($commitData['sha'])) {
|
||||
return $commitData['sha'];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function deleteSite(string $siteId): mixed
|
||||
{
|
||||
$site = $this->client->call(Client::METHOD_DELETE, '/sites/' . $siteId, array_merge([
|
||||
|
||||
@@ -131,8 +131,8 @@ class SitesCustomClientTest extends Scope
|
||||
$this->assertEquals('github', $template['body']['vcsProvider']);
|
||||
$this->assertEquals('Simple React application integrated with Appwrite SDK.', $template['body']['tagline']);
|
||||
$this->assertIsArray($template['body']['frameworks']);
|
||||
$this->assertEquals('http://'. $hostname . '/images/sites/templates/starter-for-react-dark.png', $template['body']['screenshotDark']);
|
||||
$this->assertEquals('http://' . $hostname . '/images/sites/templates/starter-for-react-light.png', $template['body']['screenshotLight']);
|
||||
$this->assertStringContainsString('/images/sites/templates/starter-for-react-dark.png', $template['body']['screenshotDark']);
|
||||
$this->assertStringContainsString('/images/sites/templates/starter-for-react-light.png', $template['body']['screenshotLight']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
|
||||
@@ -634,18 +634,20 @@ class SitesCustomServerTest extends Scope
|
||||
'buildRuntime' => 'node-22',
|
||||
'fallbackFile' => '',
|
||||
'framework' => 'other',
|
||||
'name' => 'Test Site',
|
||||
'name' => 'Test List Sites',
|
||||
'outputDirectory' => './',
|
||||
'providerBranch' => 'main',
|
||||
'providerRootDirectory' => './',
|
||||
'siteId' => ID::unique()
|
||||
]);
|
||||
|
||||
$sites = $this->listSites();
|
||||
$sites = $this->listSites([
|
||||
'search' => 'Test List Sites',
|
||||
]);
|
||||
|
||||
$this->assertEquals($sites['headers']['status-code'], 200);
|
||||
$this->assertCount(1, $sites['body']['sites']);
|
||||
$this->assertEquals($sites['body']['sites'][0]['name'], 'Test Site');
|
||||
$this->assertEquals($sites['body']['sites'][0]['name'], 'Test List Sites');
|
||||
|
||||
// Test pagination limit
|
||||
$sites = $this->listSites([
|
||||
@@ -689,7 +691,7 @@ class SitesCustomServerTest extends Scope
|
||||
|
||||
// Test search name
|
||||
$sites = $this->listSites([
|
||||
'search' => 'Test'
|
||||
'search' => 'Test List Sites'
|
||||
]);
|
||||
|
||||
$this->assertEquals($sites['headers']['status-code'], 200);
|
||||
@@ -712,21 +714,23 @@ class SitesCustomServerTest extends Scope
|
||||
'buildRuntime' => 'node-22',
|
||||
'fallbackFile' => '',
|
||||
'framework' => 'other',
|
||||
'name' => 'Test Site 2',
|
||||
'name' => 'Test List Sites 2',
|
||||
'outputDirectory' => './',
|
||||
'providerBranch' => 'main',
|
||||
'providerRootDirectory' => './',
|
||||
'siteId' => ID::unique()
|
||||
]);
|
||||
|
||||
$sites = $this->listSites();
|
||||
$sites = $this->listSites([
|
||||
'search' => 'Test List Sites',
|
||||
]);
|
||||
|
||||
$this->assertEquals($sites['headers']['status-code'], 200);
|
||||
$this->assertEquals($sites['body']['total'], 2);
|
||||
$this->assertIsArray($sites['body']['sites']);
|
||||
$this->assertCount(2, $sites['body']['sites']);
|
||||
$this->assertEquals($sites['body']['sites'][0]['name'], 'Test Site');
|
||||
$this->assertEquals($sites['body']['sites'][1]['name'], 'Test Site 2');
|
||||
$this->assertEquals($sites['body']['sites'][0]['name'], 'Test List Sites');
|
||||
$this->assertEquals($sites['body']['sites'][1]['name'], 'Test List Sites 2');
|
||||
|
||||
$sites1 = $this->listSites([
|
||||
'queries' => [
|
||||
@@ -736,7 +740,7 @@ class SitesCustomServerTest extends Scope
|
||||
|
||||
$this->assertEquals($sites1['headers']['status-code'], 200);
|
||||
$this->assertCount(1, $sites1['body']['sites']);
|
||||
$this->assertEquals($sites1['body']['sites'][0]['name'], 'Test Site 2');
|
||||
$this->assertEquals($sites1['body']['sites'][0]['name'], 'Test List Sites 2');
|
||||
|
||||
$sites2 = $this->listSites([
|
||||
'queries' => [
|
||||
@@ -746,7 +750,7 @@ class SitesCustomServerTest extends Scope
|
||||
|
||||
$this->assertEquals($sites2['headers']['status-code'], 200);
|
||||
$this->assertCount(1, $sites2['body']['sites']);
|
||||
$this->assertEquals($sites2['body']['sites'][0]['name'], 'Test Site');
|
||||
$this->assertEquals($sites2['body']['sites'][0]['name'], 'Test List Sites');
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
@@ -1563,7 +1567,157 @@ class SitesCustomServerTest extends Scope
|
||||
'repository' => $template['providerRepositoryId'],
|
||||
'owner' => $template['providerOwner'],
|
||||
'rootDirectory' => $template['frameworks'][0]['providerRootDirectory'],
|
||||
'version' => $template['providerVersion'],
|
||||
'type' => 'tag',
|
||||
'reference' => $template['providerVersion'],
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $deployment['headers']['status-code']);
|
||||
$this->assertNotEmpty($deployment['body']['$id']);
|
||||
|
||||
$deployment = $this->getDeployment($siteId, $deployment['body']['$id']);
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertEquals(0, $deployment['body']['sourceSize']);
|
||||
$this->assertEquals(0, $deployment['body']['buildSize']);
|
||||
$this->assertEquals(0, $deployment['body']['totalSize']);
|
||||
|
||||
$this->assertEventually(function () use ($siteId) {
|
||||
$site = $this->getSite($siteId);
|
||||
$this->assertNotEmpty($site['body']['deploymentId']);
|
||||
}, 50000, 500);
|
||||
|
||||
$domain = $this->setupSiteDomain($siteId);
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://' . $domain);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/');
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString("Astro Blog", $response['body']);
|
||||
$this->assertStringContainsString("Hello, Astronaut!", $response['body']);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/about');
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString("Astro Blog", $response['body']);
|
||||
$this->assertStringContainsString("About Me", $response['body']);
|
||||
|
||||
$deployment = $this->getDeployment($siteId, $deployment['body']['$id']);
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['sourceSize']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['buildSize']);
|
||||
$totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize'];
|
||||
$this->assertEquals($totalSize, $deployment['body']['totalSize']);
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
}
|
||||
|
||||
public function testCreateSiteFromTemplateBranch()
|
||||
{
|
||||
$template = $this->getTemplate('playground-for-astro');
|
||||
$this->assertEquals(200, $template['headers']['status-code']);
|
||||
|
||||
$template = $template['body'];
|
||||
|
||||
$siteId = $this->setupSite([
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'Astro Blog - Branch Test',
|
||||
'framework' => $template['frameworks'][0]['key'],
|
||||
'adapter' => $template['frameworks'][0]['adapter'],
|
||||
'buildRuntime' => $template['frameworks'][0]['buildRuntime'],
|
||||
'outputDirectory' => $template['frameworks'][0]['outputDirectory'],
|
||||
'buildCommand' => $template['frameworks'][0]['buildCommand'],
|
||||
'installCommand' => $template['frameworks'][0]['installCommand'],
|
||||
'fallbackFile' => $template['frameworks'][0]['fallbackFile'],
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($siteId);
|
||||
|
||||
// Deploy using branch
|
||||
$deployment = $this->createTemplateDeployment($siteId, [
|
||||
'repository' => $template['providerRepositoryId'],
|
||||
'owner' => $template['providerOwner'],
|
||||
'rootDirectory' => $template['frameworks'][0]['providerRootDirectory'],
|
||||
'type' => 'branch',
|
||||
'reference' => 'main',
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $deployment['headers']['status-code']);
|
||||
$this->assertNotEmpty($deployment['body']['$id']);
|
||||
|
||||
$deployment = $this->getDeployment($siteId, $deployment['body']['$id']);
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertEquals(0, $deployment['body']['sourceSize']);
|
||||
$this->assertEquals(0, $deployment['body']['buildSize']);
|
||||
$this->assertEquals(0, $deployment['body']['totalSize']);
|
||||
|
||||
$this->assertEventually(function () use ($siteId) {
|
||||
$site = $this->getSite($siteId);
|
||||
$this->assertNotEmpty($site['body']['deploymentId']);
|
||||
}, 50000, 500);
|
||||
|
||||
$domain = $this->setupSiteDomain($siteId);
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://' . $domain);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/');
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString("Astro Blog", $response['body']);
|
||||
$this->assertStringContainsString("Hello, Astronaut!", $response['body']);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/about');
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString("Astro Blog", $response['body']);
|
||||
$this->assertStringContainsString("About Me", $response['body']);
|
||||
|
||||
$deployment = $this->getDeployment($siteId, $deployment['body']['$id']);
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['sourceSize']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['buildSize']);
|
||||
$totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize'];
|
||||
$this->assertEquals($totalSize, $deployment['body']['totalSize']);
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
}
|
||||
|
||||
public function testCreateSiteFromTemplateCommit()
|
||||
{
|
||||
$template = $this->getTemplate('playground-for-astro');
|
||||
$this->assertEquals(200, $template['headers']['status-code']);
|
||||
|
||||
// Get latest commit using helper function
|
||||
$latestCommit = $this->helperGetLatestCommit(
|
||||
$template['body']['providerOwner'],
|
||||
$template['body']['providerRepositoryId']
|
||||
);
|
||||
$this->assertNotNull($latestCommit);
|
||||
|
||||
$template = $template['body'];
|
||||
|
||||
$siteId = $this->setupSite([
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'Astro Blog - Commit Test',
|
||||
'framework' => $template['frameworks'][0]['key'],
|
||||
'adapter' => $template['frameworks'][0]['adapter'],
|
||||
'buildRuntime' => $template['frameworks'][0]['buildRuntime'],
|
||||
'outputDirectory' => $template['frameworks'][0]['outputDirectory'],
|
||||
'buildCommand' => $template['frameworks'][0]['buildCommand'],
|
||||
'installCommand' => $template['frameworks'][0]['installCommand'],
|
||||
'fallbackFile' => $template['frameworks'][0]['fallbackFile'],
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($siteId);
|
||||
|
||||
// Deploy using commit
|
||||
$deployment = $this->createTemplateDeployment($siteId, [
|
||||
'repository' => $template['providerRepositoryId'],
|
||||
'owner' => $template['providerOwner'],
|
||||
'rootDirectory' => $template['frameworks'][0]['providerRootDirectory'],
|
||||
'type' => 'commit',
|
||||
'reference' => $latestCommit,
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ trait StorageBase
|
||||
'name' => 'Test Bucket',
|
||||
'fileSecurity' => true,
|
||||
'maximumFileSize' => 2000000, //2MB
|
||||
'allowedFileExtensions' => ['jpg', 'png', 'jfif'],
|
||||
'allowedFileExtensions' => ['jpg', 'png', 'jfif', 'webp'],
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::any()),
|
||||
@@ -263,7 +263,39 @@ trait StorageBase
|
||||
$this->assertEquals(400, $res['headers']['status-code']);
|
||||
$this->assertEquals(Exception::STORAGE_INVALID_APPWRITE_ID, $res['body']['type']);
|
||||
|
||||
return ['bucketId' => $bucketId, 'fileId' => $file['body']['$id'], 'largeFileId' => $largeFile['body']['$id'], 'largeBucketId' => $bucket2['body']['$id']];
|
||||
/**
|
||||
* Test for SUCCESS - Upload and view webp image
|
||||
*/
|
||||
$webpFile = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', array_merge([
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/image.webp'), 'image/webp', 'image.webp'),
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
$this->assertEquals(201, $webpFile['headers']['status-code']);
|
||||
$this->assertNotEmpty($webpFile['body']['$id']);
|
||||
$this->assertEquals('image.webp', $webpFile['body']['name']);
|
||||
$this->assertEquals('image/webp', $webpFile['body']['mimeType']);
|
||||
|
||||
$webpFileId = $webpFile['body']['$id'];
|
||||
|
||||
// View webp file
|
||||
$webpView = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $webpFileId . '/view', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $webpView['headers']['status-code']);
|
||||
$this->assertEquals('image/webp', $webpView['headers']['content-type']);
|
||||
$this->assertNotEmpty($webpView['body']);
|
||||
|
||||
return ['bucketId' => $bucketId, 'fileId' => $file['body']['$id'], 'largeFileId' => $largeFile['body']['$id'], 'largeBucketId' => $bucket2['body']['$id'], 'webpFileId' => $webpFileId];
|
||||
}
|
||||
|
||||
public function testCreateBucketFileZstdCompression(): array
|
||||
@@ -379,6 +411,23 @@ trait StorageBase
|
||||
$this->assertGreaterThan(0, $files['body']['total']);
|
||||
$this->assertGreaterThan(0, count($files['body']['files']));
|
||||
|
||||
/**
|
||||
* Test for SUCCESS with total=false
|
||||
*/
|
||||
$filesWithIncludeTotalFalse = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $data['bucketId'] . '/files', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'total' => false
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $filesWithIncludeTotalFalse['headers']['status-code']);
|
||||
$this->assertIsArray($filesWithIncludeTotalFalse['body']);
|
||||
$this->assertIsArray($filesWithIncludeTotalFalse['body']['files']);
|
||||
$this->assertIsInt($filesWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertEquals(0, $filesWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertGreaterThan(0, count($filesWithIncludeTotalFalse['body']['files']));
|
||||
|
||||
$files = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $data['bucketId'] . '/files', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
@@ -399,7 +448,7 @@ trait StorageBase
|
||||
],
|
||||
]);
|
||||
$this->assertEquals(200, $files['headers']['status-code']);
|
||||
$this->assertEquals(0, count($files['body']['files']));
|
||||
$this->assertEquals(1, count($files['body']['files']));
|
||||
|
||||
$files = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $data['bucketId'] . '/files', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
@@ -852,6 +901,31 @@ trait StorageBase
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateBucketFile
|
||||
*/
|
||||
public function testFilePreview(array $data): array
|
||||
{
|
||||
$bucketId = $data['bucketId'];
|
||||
$fileId = $data['fileId'];
|
||||
|
||||
// Preview PNG as webp
|
||||
$preview = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId . '/preview', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'width' => 300,
|
||||
'height' => 300,
|
||||
'output' => 'webp',
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $preview['headers']['status-code']);
|
||||
$this->assertEquals('image/webp', $preview['headers']['content-type']);
|
||||
$this->assertNotEmpty($preview['body']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testUpdateBucketFile
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Tests\E2E\Services\Storage;
|
||||
|
||||
use CURLFile;
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
@@ -107,4 +108,56 @@ class StorageConsoleClientTest extends Scope
|
||||
$this->assertIsArray($response['body']['imageTransformations']);
|
||||
$this->assertIsNumeric($response['body']['imageTransformationsTotal']);
|
||||
}
|
||||
public function testCreateBucketTransformationsDisabledConsole(): void
|
||||
{
|
||||
// Create a bucket with default settings
|
||||
$bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'bucketId' => ID::unique(),
|
||||
'name' => 'Test Console Bucket Transformations Disabled',
|
||||
]);
|
||||
$this->assertEquals(201, $bucket['headers']['status-code']);
|
||||
|
||||
// Create a file in the bucket
|
||||
$file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucket['body']['$id'] . '/files', array_merge([
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'transformations.png'),
|
||||
]);
|
||||
$this->assertEquals(201, $file['headers']['status-code']);
|
||||
|
||||
// Try to get the file preview
|
||||
$preview = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucket['body']['$id'] . '/files/' . $file['body']['$id'] . '/preview', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
$this->assertEquals(200, $preview['headers']['status-code']);
|
||||
|
||||
// Update the bucket to disable transformations
|
||||
$bucket = $this->client->call(Client::METHOD_PUT, '/storage/buckets/' . $bucket['body']['$id'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'Test Bucket Transformations Disabled',
|
||||
'transformations' => false,
|
||||
]);
|
||||
|
||||
// Try to get the file preview again
|
||||
$preview = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucket['body']['$id'] . '/files/' . $file['body']['$id'] . '/preview', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
$this->assertEquals(200, $preview['headers']['status-code']); // Returns 200 since image transformations are not counted for console requests
|
||||
|
||||
// Delete the bucket
|
||||
$response = $this->client->call(Client::METHOD_DELETE, '/storage/buckets/' . $bucket['body']['$id'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1386,4 +1386,65 @@ class StorageCustomClientTest extends Scope
|
||||
$this->assertStringContainsString('users', $file['body']['message']);
|
||||
$this->assertStringContainsString('user:' . $this->getUser()['$id'], $file['body']['message']);
|
||||
}
|
||||
|
||||
public function testCreateBucketTransformationsDisabled(): void
|
||||
{
|
||||
// Create a bucket with default settings
|
||||
$bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'bucketId' => ID::unique(),
|
||||
'name' => 'Test Bucket Transformations Disabled',
|
||||
'permissions' => [
|
||||
Permission::read(Role::any())
|
||||
],
|
||||
]);
|
||||
$this->assertEquals(201, $bucket['headers']['status-code']);
|
||||
|
||||
// Create a file in the bucket
|
||||
$file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucket['body']['$id'] . '/files', [
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'transformations.png'),
|
||||
]);
|
||||
$this->assertEquals(201, $file['headers']['status-code']);
|
||||
|
||||
// Try to get the file preview
|
||||
$preview = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucket['body']['$id'] . '/files/' . $file['body']['$id'] . '/preview', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]);
|
||||
$this->assertEquals(200, $preview['headers']['status-code']);
|
||||
|
||||
// Update the bucket to disable transformations
|
||||
$bucket = $this->client->call(Client::METHOD_PUT, '/storage/buckets/' . $bucket['body']['$id'], [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'name' => 'Test Bucket Transformations Disabled',
|
||||
'transformations' => false,
|
||||
]);
|
||||
|
||||
// Try to get the file preview again
|
||||
$preview = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucket['body']['$id'] . '/files/' . $file['body']['$id'] . '/preview', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]);
|
||||
$this->assertEquals(403, $preview['headers']['status-code']);
|
||||
$this->assertStringContainsString('Image transformations are disabled for the requested bucket.', $preview['body']['message']);
|
||||
|
||||
// Delete the bucket
|
||||
$response = $this->client->call(Client::METHOD_DELETE, '/storage/buckets/' . $bucket['body']['$id'], [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,6 +322,23 @@ trait TeamsBase
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Test for SUCCESS with total=false
|
||||
*/
|
||||
$teamsWithIncludeTotalFalse = $this->client->call(Client::METHOD_GET, '/teams', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'total' => false
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $teamsWithIncludeTotalFalse['headers']['status-code']);
|
||||
$this->assertIsArray($teamsWithIncludeTotalFalse['body']);
|
||||
$this->assertIsArray($teamsWithIncludeTotalFalse['body']['teams']);
|
||||
$this->assertIsInt($teamsWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertEquals(0, $teamsWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertGreaterThan(0, count($teamsWithIncludeTotalFalse['body']['teams']));
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
@@ -63,10 +62,23 @@ class TokensConsoleClientTest extends Scope
|
||||
|
||||
$fileId = $file['body']['$id'];
|
||||
|
||||
// Failure case: Expire date is in the past
|
||||
$token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()));
|
||||
], $this->getHeaders()), [
|
||||
'expire' => '2022-11-02',
|
||||
]);
|
||||
$this->assertEquals(400, $token['headers']['status-code']);
|
||||
$this->assertStringContainsString('Value must be valid date in the future', $token['body']['message']);
|
||||
|
||||
// Success case: No expire date
|
||||
$token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()), [
|
||||
'expire' => null,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $token['headers']['status-code']);
|
||||
$this->assertEquals('files', $token['body']['resourceType']);
|
||||
@@ -107,8 +119,19 @@ class TokensConsoleClientTest extends Scope
|
||||
{
|
||||
$tokenId = $data['tokenId'];
|
||||
|
||||
// Failure case: Expire date is in the past
|
||||
$token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'expire' => '2022-11-02',
|
||||
]);
|
||||
$this->assertEquals(400, $token['headers']['status-code']);
|
||||
$this->assertStringContainsString('Value must be valid date in the future', $token['body']['message']);
|
||||
|
||||
// Finite expiry
|
||||
$expiry = DateTime::addSeconds(new \DateTime(), 3600);
|
||||
$expiry = date('Y-m-d', strtotime("tomorrow"));
|
||||
$token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
|
||||
@@ -7,7 +7,6 @@ use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
@@ -61,6 +60,17 @@ class TokensCustomServerTest extends Scope
|
||||
|
||||
$fileId = $file['body']['$id'];
|
||||
|
||||
// Failure case: Expire date is in the past
|
||||
$token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()), [
|
||||
'expire' => '2022-11-02',
|
||||
]);
|
||||
$this->assertEquals(400, $token['headers']['status-code']);
|
||||
$this->assertStringContainsString('Value must be valid date in the future', $token['body']['message']);
|
||||
|
||||
// Success case: No expire date
|
||||
$token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
@@ -83,8 +93,19 @@ class TokensCustomServerTest extends Scope
|
||||
{
|
||||
$tokenId = $data['tokenId'];
|
||||
|
||||
// Finite expiry
|
||||
$expiry = DateTime::addSeconds(new \DateTime(), 3600);
|
||||
// Failure case: Expire date is in the past
|
||||
$token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'expire' => '2022-11-02',
|
||||
]);
|
||||
$this->assertEquals(400, $token['headers']['status-code']);
|
||||
$this->assertStringContainsString('Value must be valid date in the future', $token['body']['message']);
|
||||
|
||||
// Success case: Finite expiry
|
||||
$expiry = date('Y-m-d', strtotime("tomorrow"));
|
||||
$token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
@@ -94,9 +115,10 @@ class TokensCustomServerTest extends Scope
|
||||
]);
|
||||
|
||||
$dateValidator = new DatetimeValidator();
|
||||
$this->assertEquals(200, $token['headers']['status-code']);
|
||||
$this->assertTrue($dateValidator->isValid($token['body']['expire']));
|
||||
|
||||
// Infinite expiry
|
||||
// Success case: Infinite expiry
|
||||
$token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
||||
@@ -802,6 +802,23 @@ trait UsersBase
|
||||
$this->assertIsInt($users['body']['total']);
|
||||
$this->assertGreaterThan(0, $users['body']['total']);
|
||||
|
||||
/**
|
||||
* Test for SUCCESS with total=false
|
||||
*/
|
||||
$usersWithIncludeTotalFalse = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'total' => false
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $usersWithIncludeTotalFalse['headers']['status-code']);
|
||||
$this->assertIsArray($usersWithIncludeTotalFalse['body']);
|
||||
$this->assertIsArray($usersWithIncludeTotalFalse['body']['users']);
|
||||
$this->assertIsInt($usersWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertEquals(0, $usersWithIncludeTotalFalse['body']['total']);
|
||||
$this->assertGreaterThan(0, count($usersWithIncludeTotalFalse['body']['users']));
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 634 B |
@@ -5,30 +5,20 @@ namespace Tests\Unit\Network\Validators;
|
||||
use Appwrite\Network\Validator\DNS;
|
||||
use Appwrite\Tests\Retry;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Utopia\DNS\Message\Record;
|
||||
|
||||
/*
|
||||
DNS Setup (on Appwrite Labs digital ocean team, network tab):
|
||||
|
||||
certainly.caa.appwrite.org: CAA 0 issue "certainly.com"
|
||||
certainly-full.caa.appwrite.org: CAA 128 issuewild "certainly.com;account=123456;validationmethods=dns-01"
|
||||
letsencrypt.certainly.caa.appwrite.org: CAA 0 issue "letsencrypt.org"
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* DNS Setup (on Appwrite Labs digital ocean team, network tab):
|
||||
*
|
||||
* certainly.caa.appwrite.org: CAA 0 issue "certainly.com"
|
||||
* certainly-full.caa.appwrite.org: CAA 128 issuewild "certainly.com;account=123456;validationmethods=dns-01"
|
||||
* letsencrypt.certainly.caa.appwrite.org: CAA 0 issue "letsencrypt.org"
|
||||
*/
|
||||
class DNSTest extends TestCase
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function testCNAME(): void
|
||||
{
|
||||
$validator = new DNS('appwrite.io', DNS::RECORD_CNAME);
|
||||
$validator = new DNS('appwrite.io', Record::TYPE_CNAME);
|
||||
$this->assertEquals($validator->isValid(''), false);
|
||||
$this->assertEquals($validator->isValid(null), false);
|
||||
$this->assertEquals($validator->isValid(false), false);
|
||||
@@ -39,7 +29,7 @@ class DNSTest extends TestCase
|
||||
public function testA(): void
|
||||
{
|
||||
// IPv4 for documentation purposes
|
||||
$validator = new DNS('203.0.113.1', DNS::RECORD_A);
|
||||
$validator = new DNS('203.0.113.1', Record::TYPE_A);
|
||||
$this->assertEquals($validator->isValid(''), false);
|
||||
$this->assertEquals($validator->isValid(null), false);
|
||||
$this->assertEquals($validator->isValid(false), false);
|
||||
@@ -50,7 +40,7 @@ class DNSTest extends TestCase
|
||||
public function testAAAA(): void
|
||||
{
|
||||
// IPv6 for documentation purposes
|
||||
$validator = new DNS('2001:db8::1', DNS::RECORD_AAAA);
|
||||
$validator = new DNS('2001:db8::1', Record::TYPE_AAAA);
|
||||
$this->assertEquals($validator->isValid(''), false);
|
||||
$this->assertEquals($validator->isValid(null), false);
|
||||
$this->assertEquals($validator->isValid(false), false);
|
||||
@@ -61,8 +51,10 @@ class DNSTest extends TestCase
|
||||
#[Retry(count: 5)]
|
||||
public function testCAA(): void
|
||||
{
|
||||
$certainly = new DNS('certainly.com', DNS::RECORD_CAA, 'ns1.digitalocean.com');
|
||||
$letsencrypt = new DNS('letsencrypt.org', DNS::RECORD_CAA, 'ns1.digitalocean.com');
|
||||
$digitalOceanIp = '172.64.52.210'; // ping ns1.digitalocean.com
|
||||
|
||||
$certainly = new DNS('certainly.com', Record::TYPE_CAA, $digitalOceanIp);
|
||||
$letsencrypt = new DNS('letsencrypt.org', Record::TYPE_CAA, $digitalOceanIp);
|
||||
|
||||
// No CAA record succeeds on main domain & subdomains for any issuer
|
||||
$this->assertEquals($certainly->isValid('caa.appwrite.org'), true);
|
||||
@@ -78,11 +70,11 @@ class DNSTest extends TestCase
|
||||
$this->assertEquals($letsencrypt->isValid('certainly-full.caa.appwrite.org'), false);
|
||||
|
||||
// Custom flags&tag are not allowed if validator includes specific flags&tag
|
||||
$certainlyFull = new DNS('0 issue "certainly.com"', DNS::RECORD_CAA);
|
||||
$certainlyFull = new DNS('0 issue "certainly.com"', Record::TYPE_CAA);
|
||||
$this->assertEquals($certainlyFull->isValid('certainly-full.caa.appwrite.org'), false);
|
||||
|
||||
// Custom flags&tag still allows if they match exactly
|
||||
$certainlyFull = new DNS('128 issuewild "certainly.com;account=123456;validationmethods=dns-01"', DNS::RECORD_CAA);
|
||||
$certainlyFull = new DNS('128 issuewild "certainly.com;account=123456;validationmethods=dns-01"', Record::TYPE_CAA);
|
||||
$this->assertEquals($certainlyFull->isValid('certainly-full.caa.appwrite.org'), true);
|
||||
|
||||
// Certainly CAA allows Certainly, but not LetsEncrypt; Same for subdomains
|
||||
|
||||
Reference in New Issue
Block a user