2020-01-11 13:58:02 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace Tests\E2E\Scopes;
|
|
|
|
|
|
2025-06-02 16:09:03 +00:00
|
|
|
use Appwrite\Tests\Async;
|
2022-08-26 09:21:08 +00:00
|
|
|
use Appwrite\Tests\Retryable;
|
2020-01-11 13:58:02 +00:00
|
|
|
use PHPUnit\Framework\TestCase;
|
2024-03-06 17:34:21 +00:00
|
|
|
use Tests\E2E\Client;
|
2022-12-14 15:42:25 +00:00
|
|
|
use Utopia\Database\Helpers\ID;
|
2025-09-01 10:09:30 +00:00
|
|
|
use Utopia\System\System;
|
2020-01-11 13:58:02 +00:00
|
|
|
|
|
|
|
|
abstract class Scope extends TestCase
|
|
|
|
|
{
|
2022-08-26 09:21:08 +00:00
|
|
|
use Retryable;
|
2025-06-02 16:09:03 +00:00
|
|
|
use Async;
|
2022-08-26 09:21:08 +00:00
|
|
|
|
2025-06-26 16:07:59 +00:00
|
|
|
public const REQUEST_TYPE_WEBHOOK = 'webhook';
|
|
|
|
|
public const REQUEST_TYPE_SMS = 'sms';
|
|
|
|
|
|
2022-12-19 11:25:01 +00:00
|
|
|
protected ?Client $client = null;
|
2026-02-03 14:14:00 +00:00
|
|
|
protected string $endpoint = 'http://appwrite/v1';
|
2026-02-04 07:53:37 +00:00
|
|
|
protected string $webEndpoint = 'http://appwrite.test/v1';
|
2022-12-15 08:51:27 +00:00
|
|
|
|
2020-01-11 13:58:02 +00:00
|
|
|
protected function setUp(): void
|
|
|
|
|
{
|
|
|
|
|
$this->client = new Client();
|
2026-02-03 10:43:37 +00:00
|
|
|
$this->client->setEndpoint($this->endpoint);
|
2025-09-01 10:09:30 +00:00
|
|
|
|
|
|
|
|
$format = System::getEnv('_APP_E2E_RESPONSE_FORMAT');
|
|
|
|
|
if (!empty($format)) {
|
2025-09-01 10:41:30 +00:00
|
|
|
if (
|
|
|
|
|
!\preg_match('/^\d+\.\d+\.\d+$/', $format) ||
|
|
|
|
|
!\version_compare($format, APP_VERSION_STABLE, '<=')
|
|
|
|
|
) {
|
|
|
|
|
throw new \Exception('E2E response format must be ' . APP_VERSION_STABLE . ' or lower.');
|
|
|
|
|
}
|
2025-09-01 10:09:30 +00:00
|
|
|
$this->client->setResponseFormat($format);
|
|
|
|
|
}
|
2020-01-11 13:58:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function tearDown(): void
|
|
|
|
|
{
|
|
|
|
|
$this->client = null;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-15 11:00:33 +00:00
|
|
|
protected function getLastEmail(int $limit = 1): array
|
2020-01-11 13:58:02 +00:00
|
|
|
{
|
2022-01-18 23:23:19 +00:00
|
|
|
sleep(3);
|
2021-10-22 16:19:02 +00:00
|
|
|
|
2021-01-03 21:22:41 +00:00
|
|
|
$emails = json_decode(file_get_contents('http://maildev:1080/email'), true);
|
2020-01-11 13:58:02 +00:00
|
|
|
|
2020-10-27 19:48:38 +00:00
|
|
|
if ($emails && is_array($emails)) {
|
2024-01-15 11:02:05 +00:00
|
|
|
if ($limit === 1) {
|
2024-01-15 11:00:33 +00:00
|
|
|
return end($emails);
|
|
|
|
|
} else {
|
2025-03-29 12:51:25 +00:00
|
|
|
return array_slice($emails, -1 * $limit);
|
2024-01-15 11:00:33 +00:00
|
|
|
}
|
2020-01-11 13:58:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-29 12:52:59 +00:00
|
|
|
protected function extractQueryParamsFromEmailLink(string $html): array
|
2025-03-29 11:49:46 +00:00
|
|
|
{
|
2025-03-29 12:51:25 +00:00
|
|
|
foreach (['/join-us?', '/verification?', '/recovery?'] as $prefix) {
|
|
|
|
|
$linkStart = strpos($html, $prefix);
|
|
|
|
|
if ($linkStart !== false) {
|
|
|
|
|
$hrefStart = strrpos(substr($html, 0, $linkStart), 'href="');
|
|
|
|
|
if ($hrefStart === false) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$hrefStart += 6;
|
|
|
|
|
$hrefEnd = strpos($html, '"', $hrefStart);
|
|
|
|
|
if ($hrefEnd === false || $hrefStart >= $hrefEnd) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$link = substr($html, $hrefStart, $hrefEnd - $hrefStart);
|
2025-03-29 13:23:51 +00:00
|
|
|
$link = strtok($link, '#'); // Remove `#title`
|
2025-03-29 12:51:25 +00:00
|
|
|
$queryStart = strpos($link, '?');
|
|
|
|
|
if ($queryStart === false) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$queryString = substr($link, $queryStart + 1);
|
|
|
|
|
parse_str(html_entity_decode($queryString), $queryParams);
|
|
|
|
|
return $queryParams;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-03-29 11:49:46 +00:00
|
|
|
|
2025-03-29 12:51:25 +00:00
|
|
|
return [];
|
|
|
|
|
}
|
2025-03-29 11:49:46 +00:00
|
|
|
|
2025-06-26 16:07:59 +00:00
|
|
|
protected function assertLastRequest(callable $probe, string $type, $timeoutMs = 20_000, $waitMs = 500): array
|
2025-06-02 16:09:03 +00:00
|
|
|
{
|
2025-06-26 16:07:59 +00:00
|
|
|
$hostname = match ($type) {
|
|
|
|
|
'webhook' => 'request-catcher-webhook',
|
2025-06-26 16:33:22 +00:00
|
|
|
'sms' => 'request-catcher-sms',
|
2025-06-26 16:07:59 +00:00
|
|
|
default => throw new \Exception('Invalid request catcher type.'),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$this->assertEventually(function () use (&$request, $probe, $hostname) {
|
|
|
|
|
$request = json_decode(file_get_contents('http://' . $hostname . ':5000/__last_request__'), true);
|
2025-06-02 16:09:03 +00:00
|
|
|
$request['data'] = json_decode($request['data'], true);
|
|
|
|
|
|
|
|
|
|
call_user_func($probe, $request);
|
|
|
|
|
}, $timeoutMs, $waitMs);
|
|
|
|
|
|
|
|
|
|
return $request;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-09 05:47:40 +00:00
|
|
|
protected function assertSamePixels(string $expectedImagePath, string $actualImageBlob): void
|
|
|
|
|
{
|
|
|
|
|
$expected = new \Imagick($expectedImagePath);
|
|
|
|
|
$actual = new \Imagick();
|
|
|
|
|
$actual->readImageBlob($actualImageBlob);
|
|
|
|
|
|
|
|
|
|
foreach ([$expected, $actual] as $image) {
|
|
|
|
|
$image->setImageFormat('PNG');
|
|
|
|
|
$image->stripImage();
|
|
|
|
|
$image->setOption('png:exclude-chunks', 'date,time,iCCP,sRGB,gAMA,cHRM');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->assertSame($expected->getImageSignature(), $actual->getImageSignature());
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-26 16:07:59 +00:00
|
|
|
/**
|
|
|
|
|
* @deprecated Use assertLastRequest instead. Used only historically in webhook tests
|
|
|
|
|
*/
|
2022-05-23 14:54:50 +00:00
|
|
|
protected function getLastRequest(): array
|
2020-10-30 19:53:27 +00:00
|
|
|
{
|
2025-06-26 16:07:59 +00:00
|
|
|
$hostname = 'request-catcher-webhook';
|
|
|
|
|
|
2022-01-18 23:23:19 +00:00
|
|
|
sleep(2);
|
2022-05-23 14:54:50 +00:00
|
|
|
|
2025-06-26 16:07:59 +00:00
|
|
|
$request = json_decode(file_get_contents('http://' . $hostname . ':5000/__last_request__'), true);
|
2023-05-30 06:26:17 +00:00
|
|
|
$request['data'] = json_decode($request['data'], true);
|
2022-05-23 14:54:50 +00:00
|
|
|
|
2023-05-30 06:26:17 +00:00
|
|
|
return $request;
|
2020-10-30 19:53:27 +00:00
|
|
|
}
|
|
|
|
|
|
2020-01-11 13:58:02 +00:00
|
|
|
/**
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
2025-01-28 14:29:45 +00:00
|
|
|
abstract public function getHeaders(bool $devKey = true): array;
|
2020-01-11 13:58:02 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
2022-05-23 14:54:50 +00:00
|
|
|
abstract public function getProject(): array;
|
2020-01-12 21:28:26 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @var array
|
|
|
|
|
*/
|
|
|
|
|
protected static $root = [];
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public function getRoot(): array
|
|
|
|
|
{
|
2020-10-27 19:48:38 +00:00
|
|
|
if ((self::$root)) {
|
2020-01-12 21:28:26 +00:00
|
|
|
return self::$root;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-23 14:54:50 +00:00
|
|
|
$email = uniqid() . 'user@localhost.test';
|
2020-01-12 21:28:26 +00:00
|
|
|
$password = 'password';
|
|
|
|
|
$name = 'User Name';
|
|
|
|
|
|
|
|
|
|
$root = $this->client->call(Client::METHOD_POST, '/account', [
|
|
|
|
|
'origin' => 'http://localhost',
|
|
|
|
|
'content-type' => 'application/json',
|
|
|
|
|
'x-appwrite-project' => 'console',
|
|
|
|
|
], [
|
2022-08-14 10:33:36 +00:00
|
|
|
'userId' => ID::unique(),
|
2020-01-12 21:28:26 +00:00
|
|
|
'email' => $email,
|
|
|
|
|
'password' => $password,
|
|
|
|
|
'name' => $name,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$this->assertEquals(201, $root['headers']['status-code']);
|
|
|
|
|
|
2022-06-14 08:17:50 +00:00
|
|
|
$session = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [
|
2020-01-12 21:28:26 +00:00
|
|
|
'origin' => 'http://localhost',
|
|
|
|
|
'content-type' => 'application/json',
|
|
|
|
|
'x-appwrite-project' => 'console',
|
|
|
|
|
], [
|
|
|
|
|
'email' => $email,
|
|
|
|
|
'password' => $password,
|
|
|
|
|
]);
|
|
|
|
|
|
2023-12-08 23:17:13 +00:00
|
|
|
$session = $session['cookies']['a_session_console'];
|
2020-01-12 21:28:26 +00:00
|
|
|
|
|
|
|
|
self::$root = [
|
2022-08-14 10:33:36 +00:00
|
|
|
'$id' => ID::custom($root['body']['$id']),
|
2020-01-12 21:28:26 +00:00
|
|
|
'name' => $root['body']['name'],
|
2020-01-16 14:06:28 +00:00
|
|
|
'email' => $root['body']['email'],
|
2020-01-12 21:28:26 +00:00
|
|
|
'session' => $session,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return self::$root;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @var array
|
|
|
|
|
*/
|
|
|
|
|
protected static $user = [];
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
2025-08-05 07:59:38 +00:00
|
|
|
public function getUser(bool $fresh = false): array
|
2020-01-12 21:28:26 +00:00
|
|
|
{
|
2025-08-05 07:59:38 +00:00
|
|
|
if (!$fresh && isset(self::$user[$this->getProject()['$id']])) {
|
2020-02-17 07:16:11 +00:00
|
|
|
return self::$user[$this->getProject()['$id']];
|
2020-01-12 21:28:26 +00:00
|
|
|
}
|
|
|
|
|
|
2022-05-23 14:54:50 +00:00
|
|
|
$email = uniqid() . 'user@localhost.test';
|
2020-01-12 21:28:26 +00:00
|
|
|
$password = 'password';
|
|
|
|
|
$name = 'User Name';
|
|
|
|
|
|
|
|
|
|
$user = $this->client->call(Client::METHOD_POST, '/account', [
|
|
|
|
|
'origin' => 'http://localhost',
|
|
|
|
|
'content-type' => 'application/json',
|
2020-02-17 07:16:11 +00:00
|
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
2020-01-12 21:28:26 +00:00
|
|
|
], [
|
2022-08-14 10:33:36 +00:00
|
|
|
'userId' => ID::unique(),
|
2020-01-12 21:28:26 +00:00
|
|
|
'email' => $email,
|
|
|
|
|
'password' => $password,
|
|
|
|
|
'name' => $name,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$this->assertEquals(201, $user['headers']['status-code']);
|
|
|
|
|
|
2022-06-14 08:17:50 +00:00
|
|
|
$session = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [
|
2020-01-12 21:28:26 +00:00
|
|
|
'origin' => 'http://localhost',
|
|
|
|
|
'content-type' => 'application/json',
|
2020-02-17 07:16:11 +00:00
|
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
2020-01-12 21:28:26 +00:00
|
|
|
], [
|
|
|
|
|
'email' => $email,
|
|
|
|
|
'password' => $password,
|
|
|
|
|
]);
|
|
|
|
|
|
2023-12-08 23:17:13 +00:00
|
|
|
$token = $session['cookies']['a_session_' . $this->getProject()['$id']];
|
2020-01-12 21:28:26 +00:00
|
|
|
|
2020-02-17 07:16:11 +00:00
|
|
|
self::$user[$this->getProject()['$id']] = [
|
2022-08-14 10:33:36 +00:00
|
|
|
'$id' => ID::custom($user['body']['$id']),
|
2020-01-12 21:28:26 +00:00
|
|
|
'name' => $user['body']['name'],
|
2020-01-16 14:06:28 +00:00
|
|
|
'email' => $user['body']['email'],
|
2022-06-30 09:23:38 +00:00
|
|
|
'session' => $token,
|
|
|
|
|
'sessionId' => $session['body']['$id'],
|
2020-01-12 21:28:26 +00:00
|
|
|
];
|
|
|
|
|
|
2020-02-17 07:16:11 +00:00
|
|
|
return self::$user[$this->getProject()['$id']];
|
2020-01-12 21:28:26 +00:00
|
|
|
}
|
2022-01-18 23:23:19 +00:00
|
|
|
}
|