mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
551 lines
20 KiB
PHP
551 lines
20 KiB
PHP
<?php
|
|
|
|
namespace Tests\E2E\Services\Project;
|
|
|
|
use Tests\E2E\Client;
|
|
use Utopia\Database\Query;
|
|
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
|
|
|
trait MockPhonesBase
|
|
{
|
|
// Create mock phone tests
|
|
|
|
public function testCreateMockPhone(): void
|
|
{
|
|
$number = $this->uniquePhoneNumber();
|
|
|
|
$response = $this->createMockPhone($number, '123456');
|
|
|
|
$this->assertSame(201, $response['headers']['status-code']);
|
|
$this->assertSame($number, $response['body']['number']);
|
|
$this->assertSame('123456', $response['body']['otp']);
|
|
|
|
$dateValidator = new DatetimeValidator();
|
|
$this->assertTrue($dateValidator->isValid($response['body']['$createdAt']));
|
|
$this->assertTrue($dateValidator->isValid($response['body']['$updatedAt']));
|
|
|
|
// Verify via GET
|
|
$get = $this->getMockPhone($number);
|
|
$this->assertSame(200, $get['headers']['status-code']);
|
|
$this->assertSame($number, $get['body']['number']);
|
|
$this->assertSame('123456', $get['body']['otp']);
|
|
|
|
// Verify via LIST
|
|
$list = $this->listMockPhones();
|
|
$this->assertSame(200, $list['headers']['status-code']);
|
|
$numbers = \array_column($list['body']['mockNumbers'], 'number');
|
|
$this->assertContains($number, $numbers);
|
|
|
|
// Cleanup
|
|
$this->deleteMockPhone($number);
|
|
}
|
|
|
|
public function testCreateMockPhoneAlreadyExists(): void
|
|
{
|
|
$number = $this->uniquePhoneNumber();
|
|
|
|
$first = $this->createMockPhone($number, '123456');
|
|
$this->assertSame(201, $first['headers']['status-code']);
|
|
|
|
$duplicate = $this->createMockPhone($number, '654321');
|
|
$this->assertSame(409, $duplicate['headers']['status-code']);
|
|
$this->assertSame('mock_number_already_exists', $duplicate['body']['type']);
|
|
|
|
// Original OTP must remain unchanged
|
|
$get = $this->getMockPhone($number);
|
|
$this->assertSame(200, $get['headers']['status-code']);
|
|
$this->assertSame('123456', $get['body']['otp']);
|
|
|
|
// Cleanup
|
|
$this->deleteMockPhone($number);
|
|
}
|
|
|
|
public function testCreateMockPhoneInvalidNumber(): void
|
|
{
|
|
// Missing `+` prefix — Phone validator rejects.
|
|
$response = $this->createMockPhone('16555551234', '123456');
|
|
$this->assertSame(400, $response['headers']['status-code']);
|
|
}
|
|
|
|
public function testCreateMockPhoneNumberTooLong(): void
|
|
{
|
|
// 16 digits exceeds the E.164 15-digit maximum.
|
|
$response = $this->createMockPhone('+1234567890987654', '123456');
|
|
$this->assertSame(400, $response['headers']['status-code']);
|
|
}
|
|
|
|
public function testCreateMockPhoneInvalidOtpTooShort(): void
|
|
{
|
|
$response = $this->createMockPhone($this->uniquePhoneNumber(), '123');
|
|
$this->assertSame(400, $response['headers']['status-code']);
|
|
}
|
|
|
|
public function testCreateMockPhoneInvalidOtpTooLong(): void
|
|
{
|
|
$response = $this->createMockPhone($this->uniquePhoneNumber(), '1234567');
|
|
$this->assertSame(400, $response['headers']['status-code']);
|
|
}
|
|
|
|
public function testCreateMockPhoneInvalidOtpNonNumeric(): void
|
|
{
|
|
$response = $this->createMockPhone($this->uniquePhoneNumber(), 'abc123');
|
|
$this->assertSame(400, $response['headers']['status-code']);
|
|
}
|
|
|
|
public function testCreateMockPhoneMissingNumber(): void
|
|
{
|
|
$response = $this->createMockPhone(null, '123456');
|
|
$this->assertSame(400, $response['headers']['status-code']);
|
|
}
|
|
|
|
public function testCreateMockPhoneMissingOtp(): void
|
|
{
|
|
$response = $this->createMockPhone($this->uniquePhoneNumber(), null);
|
|
$this->assertSame(400, $response['headers']['status-code']);
|
|
}
|
|
|
|
public function testCreateMockPhoneWithoutAuthentication(): void
|
|
{
|
|
$response = $this->createMockPhone($this->uniquePhoneNumber(), '123456', authenticated: false);
|
|
$this->assertSame(401, $response['headers']['status-code']);
|
|
}
|
|
|
|
// Get mock phone tests
|
|
|
|
public function testGetMockPhone(): void
|
|
{
|
|
$number = $this->uniquePhoneNumber();
|
|
$create = $this->createMockPhone($number, '987654');
|
|
$this->assertSame(201, $create['headers']['status-code']);
|
|
|
|
$response = $this->getMockPhone($number);
|
|
|
|
$this->assertSame(200, $response['headers']['status-code']);
|
|
$this->assertSame($number, $response['body']['number']);
|
|
$this->assertSame('987654', $response['body']['otp']);
|
|
|
|
$dateValidator = new DatetimeValidator();
|
|
$this->assertTrue($dateValidator->isValid($response['body']['$createdAt']));
|
|
$this->assertTrue($dateValidator->isValid($response['body']['$updatedAt']));
|
|
|
|
// Cleanup
|
|
$this->deleteMockPhone($number);
|
|
}
|
|
|
|
public function testGetMockPhoneNotFound(): void
|
|
{
|
|
$response = $this->getMockPhone($this->uniquePhoneNumber());
|
|
|
|
$this->assertSame(404, $response['headers']['status-code']);
|
|
$this->assertSame('mock_number_not_found', $response['body']['type']);
|
|
}
|
|
|
|
public function testGetMockPhoneInvalidNumber(): void
|
|
{
|
|
// Path param is still validated with the Phone validator.
|
|
$response = $this->getMockPhone('not-a-phone');
|
|
$this->assertSame(400, $response['headers']['status-code']);
|
|
}
|
|
|
|
public function testGetMockPhoneWithoutAuthentication(): void
|
|
{
|
|
$number = $this->uniquePhoneNumber();
|
|
$create = $this->createMockPhone($number, '123456');
|
|
$this->assertSame(201, $create['headers']['status-code']);
|
|
|
|
$response = $this->getMockPhone($number, authenticated: false);
|
|
$this->assertSame(401, $response['headers']['status-code']);
|
|
|
|
// Cleanup
|
|
$this->deleteMockPhone($number);
|
|
}
|
|
|
|
// Update mock phone tests
|
|
|
|
public function testUpdateMockPhone(): void
|
|
{
|
|
$number = $this->uniquePhoneNumber();
|
|
$create = $this->createMockPhone($number, '111111');
|
|
$this->assertSame(201, $create['headers']['status-code']);
|
|
|
|
$createdAt = $create['body']['$createdAt'];
|
|
|
|
// Sleep a bit so $updatedAt shifts noticeably — makes the assertion below meaningful.
|
|
\sleep(1);
|
|
|
|
$update = $this->updateMockPhone($number, '222222');
|
|
|
|
$this->assertSame(200, $update['headers']['status-code']);
|
|
$this->assertSame($number, $update['body']['number']);
|
|
$this->assertSame('222222', $update['body']['otp']);
|
|
$this->assertSame($createdAt, $update['body']['$createdAt']);
|
|
$this->assertNotSame($createdAt, $update['body']['$updatedAt']);
|
|
|
|
// Verify persistence via GET
|
|
$get = $this->getMockPhone($number);
|
|
$this->assertSame(200, $get['headers']['status-code']);
|
|
$this->assertSame('222222', $get['body']['otp']);
|
|
|
|
// Cleanup
|
|
$this->deleteMockPhone($number);
|
|
}
|
|
|
|
public function testUpdateMockPhoneNotFound(): void
|
|
{
|
|
$response = $this->updateMockPhone($this->uniquePhoneNumber(), '123456');
|
|
|
|
$this->assertSame(404, $response['headers']['status-code']);
|
|
$this->assertSame('mock_number_not_found', $response['body']['type']);
|
|
}
|
|
|
|
public function testUpdateMockPhoneInvalidOtp(): void
|
|
{
|
|
$number = $this->uniquePhoneNumber();
|
|
$create = $this->createMockPhone($number, '123456');
|
|
$this->assertSame(201, $create['headers']['status-code']);
|
|
|
|
$response = $this->updateMockPhone($number, 'abc123');
|
|
$this->assertSame(400, $response['headers']['status-code']);
|
|
|
|
// Original OTP must remain unchanged
|
|
$get = $this->getMockPhone($number);
|
|
$this->assertSame('123456', $get['body']['otp']);
|
|
|
|
// Cleanup
|
|
$this->deleteMockPhone($number);
|
|
}
|
|
|
|
public function testUpdateMockPhoneMissingOtp(): void
|
|
{
|
|
$number = $this->uniquePhoneNumber();
|
|
$create = $this->createMockPhone($number, '123456');
|
|
$this->assertSame(201, $create['headers']['status-code']);
|
|
|
|
$response = $this->updateMockPhone($number, null);
|
|
$this->assertSame(400, $response['headers']['status-code']);
|
|
|
|
// Cleanup
|
|
$this->deleteMockPhone($number);
|
|
}
|
|
|
|
public function testUpdateMockPhoneWithoutAuthentication(): void
|
|
{
|
|
$number = $this->uniquePhoneNumber();
|
|
$create = $this->createMockPhone($number, '123456');
|
|
$this->assertSame(201, $create['headers']['status-code']);
|
|
|
|
$response = $this->updateMockPhone($number, '654321', authenticated: false);
|
|
$this->assertSame(401, $response['headers']['status-code']);
|
|
|
|
// Verify it's unchanged
|
|
$get = $this->getMockPhone($number);
|
|
$this->assertSame('123456', $get['body']['otp']);
|
|
|
|
// Cleanup
|
|
$this->deleteMockPhone($number);
|
|
}
|
|
|
|
// List mock phones tests
|
|
|
|
public function testListMockPhones(): void
|
|
{
|
|
$number1 = $this->uniquePhoneNumber();
|
|
$number2 = $this->uniquePhoneNumber();
|
|
$number3 = $this->uniquePhoneNumber();
|
|
|
|
$this->assertSame(201, $this->createMockPhone($number1, '111111')['headers']['status-code']);
|
|
$this->assertSame(201, $this->createMockPhone($number2, '222222')['headers']['status-code']);
|
|
$this->assertSame(201, $this->createMockPhone($number3, '333333')['headers']['status-code']);
|
|
|
|
$response = $this->listMockPhones();
|
|
|
|
$this->assertSame(200, $response['headers']['status-code']);
|
|
$this->assertArrayHasKey('mockNumbers', $response['body']);
|
|
$this->assertArrayHasKey('total', $response['body']);
|
|
$this->assertIsArray($response['body']['mockNumbers']);
|
|
$this->assertIsInt($response['body']['total']);
|
|
$this->assertGreaterThanOrEqual(3, $response['body']['total']);
|
|
$this->assertGreaterThanOrEqual(3, \count($response['body']['mockNumbers']));
|
|
|
|
// Verify shape of each entry
|
|
foreach ($response['body']['mockNumbers'] as $entry) {
|
|
$this->assertArrayHasKey('number', $entry);
|
|
$this->assertArrayHasKey('otp', $entry);
|
|
$this->assertArrayHasKey('$createdAt', $entry);
|
|
$this->assertArrayHasKey('$updatedAt', $entry);
|
|
}
|
|
|
|
// All three seeded phones must be in the list
|
|
$numbers = \array_column($response['body']['mockNumbers'], 'number');
|
|
$this->assertContains($number1, $numbers);
|
|
$this->assertContains($number2, $numbers);
|
|
$this->assertContains($number3, $numbers);
|
|
|
|
// Cleanup
|
|
$this->deleteMockPhone($number1);
|
|
$this->deleteMockPhone($number2);
|
|
$this->deleteMockPhone($number3);
|
|
}
|
|
|
|
public function testListMockPhonesTotalFalse(): void
|
|
{
|
|
$number = $this->uniquePhoneNumber();
|
|
$create = $this->createMockPhone($number, '123456');
|
|
$this->assertSame(201, $create['headers']['status-code']);
|
|
|
|
$response = $this->listMockPhones(total: false);
|
|
|
|
$this->assertSame(200, $response['headers']['status-code']);
|
|
$this->assertSame(0, $response['body']['total']);
|
|
$this->assertGreaterThanOrEqual(1, \count($response['body']['mockNumbers']));
|
|
|
|
// Cleanup
|
|
$this->deleteMockPhone($number);
|
|
}
|
|
|
|
public function testListMockPhonesTotalMatchesCount(): void
|
|
{
|
|
$number = $this->uniquePhoneNumber();
|
|
$create = $this->createMockPhone($number, '123456');
|
|
$this->assertSame(201, $create['headers']['status-code']);
|
|
|
|
$response = $this->listMockPhones();
|
|
|
|
$this->assertSame(200, $response['headers']['status-code']);
|
|
$this->assertSame(\count($response['body']['mockNumbers']), $response['body']['total']);
|
|
|
|
// Cleanup
|
|
$this->deleteMockPhone($number);
|
|
}
|
|
|
|
public function testListMockPhonesWithLimit(): void
|
|
{
|
|
$number1 = $this->uniquePhoneNumber();
|
|
$number2 = $this->uniquePhoneNumber();
|
|
|
|
$this->assertSame(201, $this->createMockPhone($number1, '111111')['headers']['status-code']);
|
|
$this->assertSame(201, $this->createMockPhone($number2, '222222')['headers']['status-code']);
|
|
|
|
$response = $this->listMockPhones([
|
|
Query::limit(1)->toString(),
|
|
]);
|
|
|
|
$this->assertSame(200, $response['headers']['status-code']);
|
|
$this->assertCount(1, $response['body']['mockNumbers']);
|
|
$this->assertGreaterThanOrEqual(2, $response['body']['total']);
|
|
|
|
// Cleanup
|
|
$this->deleteMockPhone($number1);
|
|
$this->deleteMockPhone($number2);
|
|
}
|
|
|
|
public function testListMockPhonesWithOffset(): void
|
|
{
|
|
$number1 = $this->uniquePhoneNumber();
|
|
$number2 = $this->uniquePhoneNumber();
|
|
|
|
$this->assertSame(201, $this->createMockPhone($number1, '111111')['headers']['status-code']);
|
|
$this->assertSame(201, $this->createMockPhone($number2, '222222')['headers']['status-code']);
|
|
|
|
$listAll = $this->listMockPhones();
|
|
$this->assertSame(200, $listAll['headers']['status-code']);
|
|
$totalAll = \count($listAll['body']['mockNumbers']);
|
|
|
|
$listOffset = $this->listMockPhones([
|
|
Query::offset(1)->toString(),
|
|
]);
|
|
|
|
$this->assertSame(200, $listOffset['headers']['status-code']);
|
|
$this->assertCount($totalAll - 1, $listOffset['body']['mockNumbers']);
|
|
$this->assertSame($listAll['body']['total'], $listOffset['body']['total']);
|
|
|
|
// Cleanup
|
|
$this->deleteMockPhone($number1);
|
|
$this->deleteMockPhone($number2);
|
|
}
|
|
|
|
public function testListMockPhonesWithoutAuthentication(): void
|
|
{
|
|
$response = $this->listMockPhones(authenticated: false);
|
|
$this->assertSame(401, $response['headers']['status-code']);
|
|
}
|
|
|
|
// Delete mock phone tests
|
|
|
|
public function testDeleteMockPhone(): void
|
|
{
|
|
$number = $this->uniquePhoneNumber();
|
|
$create = $this->createMockPhone($number, '123456');
|
|
$this->assertSame(201, $create['headers']['status-code']);
|
|
|
|
// Confirm it exists
|
|
$this->assertSame(200, $this->getMockPhone($number)['headers']['status-code']);
|
|
|
|
$response = $this->deleteMockPhone($number);
|
|
$this->assertSame(204, $response['headers']['status-code']);
|
|
$this->assertEmpty($response['body']);
|
|
|
|
// Confirm it is gone
|
|
$get = $this->getMockPhone($number);
|
|
$this->assertSame(404, $get['headers']['status-code']);
|
|
$this->assertSame('mock_number_not_found', $get['body']['type']);
|
|
}
|
|
|
|
public function testDeleteMockPhoneNotFound(): void
|
|
{
|
|
$response = $this->deleteMockPhone($this->uniquePhoneNumber());
|
|
|
|
$this->assertSame(404, $response['headers']['status-code']);
|
|
$this->assertSame('mock_number_not_found', $response['body']['type']);
|
|
}
|
|
|
|
public function testDeleteMockPhoneDoubleDelete(): void
|
|
{
|
|
$number = $this->uniquePhoneNumber();
|
|
$this->assertSame(201, $this->createMockPhone($number, '123456')['headers']['status-code']);
|
|
|
|
$first = $this->deleteMockPhone($number);
|
|
$this->assertSame(204, $first['headers']['status-code']);
|
|
|
|
$second = $this->deleteMockPhone($number);
|
|
$this->assertSame(404, $second['headers']['status-code']);
|
|
$this->assertSame('mock_number_not_found', $second['body']['type']);
|
|
}
|
|
|
|
public function testDeleteMockPhoneRemovedFromList(): void
|
|
{
|
|
$number = $this->uniquePhoneNumber();
|
|
$create = $this->createMockPhone($number, '123456');
|
|
$this->assertSame(201, $create['headers']['status-code']);
|
|
|
|
$before = $this->listMockPhones();
|
|
$this->assertSame(200, $before['headers']['status-code']);
|
|
$this->assertContains($number, \array_column($before['body']['mockNumbers'], 'number'));
|
|
$countBefore = $before['body']['total'];
|
|
|
|
$delete = $this->deleteMockPhone($number);
|
|
$this->assertSame(204, $delete['headers']['status-code']);
|
|
|
|
$after = $this->listMockPhones();
|
|
$this->assertSame(200, $after['headers']['status-code']);
|
|
$this->assertSame($countBefore - 1, $after['body']['total']);
|
|
$this->assertNotContains($number, \array_column($after['body']['mockNumbers'], 'number'));
|
|
}
|
|
|
|
public function testDeleteMockPhoneWithoutAuthentication(): void
|
|
{
|
|
$number = $this->uniquePhoneNumber();
|
|
$create = $this->createMockPhone($number, '123456');
|
|
$this->assertSame(201, $create['headers']['status-code']);
|
|
|
|
$response = $this->deleteMockPhone($number, authenticated: false);
|
|
$this->assertSame(401, $response['headers']['status-code']);
|
|
|
|
// Still present
|
|
$this->assertSame(200, $this->getMockPhone($number)['headers']['status-code']);
|
|
|
|
// Cleanup
|
|
$this->deleteMockPhone($number);
|
|
}
|
|
|
|
// Helpers
|
|
|
|
protected function createMockPhone(?string $number, ?string $otp, bool $authenticated = true): mixed
|
|
{
|
|
$headers = [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
];
|
|
|
|
if ($authenticated) {
|
|
$headers = \array_merge($headers, $this->getHeaders());
|
|
}
|
|
|
|
$params = [];
|
|
if ($number !== null) {
|
|
$params['number'] = $number;
|
|
}
|
|
if ($otp !== null) {
|
|
$params['otp'] = $otp;
|
|
}
|
|
|
|
return $this->client->call(Client::METHOD_POST, '/project/mock-phones', $headers, $params);
|
|
}
|
|
|
|
protected function getMockPhone(string $number, bool $authenticated = true): mixed
|
|
{
|
|
$headers = [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
];
|
|
|
|
if ($authenticated) {
|
|
$headers = \array_merge($headers, $this->getHeaders());
|
|
}
|
|
|
|
return $this->client->call(Client::METHOD_GET, '/project/mock-phones/' . $number, $headers);
|
|
}
|
|
|
|
protected function updateMockPhone(string $number, ?string $otp, bool $authenticated = true): mixed
|
|
{
|
|
$headers = [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
];
|
|
|
|
if ($authenticated) {
|
|
$headers = \array_merge($headers, $this->getHeaders());
|
|
}
|
|
|
|
$params = [];
|
|
if ($otp !== null) {
|
|
$params['otp'] = $otp;
|
|
}
|
|
|
|
return $this->client->call(Client::METHOD_PUT, '/project/mock-phones/' . $number, $headers, $params);
|
|
}
|
|
|
|
protected function listMockPhones(?array $queries = null, ?bool $total = null, bool $authenticated = true): mixed
|
|
{
|
|
$headers = [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
];
|
|
|
|
if ($authenticated) {
|
|
$headers = \array_merge($headers, $this->getHeaders());
|
|
}
|
|
|
|
$params = [];
|
|
if ($queries !== null) {
|
|
$params['queries'] = $queries;
|
|
}
|
|
if ($total !== null) {
|
|
$params['total'] = $total;
|
|
}
|
|
|
|
return $this->client->call(Client::METHOD_GET, '/project/mock-phones', $headers, $params);
|
|
}
|
|
|
|
protected function deleteMockPhone(string $number, bool $authenticated = true): mixed
|
|
{
|
|
$headers = [
|
|
'content-type' => 'application/json',
|
|
'x-appwrite-project' => $this->getProject()['$id'],
|
|
];
|
|
|
|
if ($authenticated) {
|
|
$headers = \array_merge($headers, $this->getHeaders());
|
|
}
|
|
|
|
return $this->client->call(Client::METHOD_DELETE, '/project/mock-phones/' . $number, $headers);
|
|
}
|
|
|
|
protected function uniquePhoneNumber(): string
|
|
{
|
|
// E.164: leading '+', first digit 1-9, 10 more digits. Randomised to avoid
|
|
// collisions between interleaved tests that all live in the same project.
|
|
return '+1' . \random_int(2000000000, 9999999999);
|
|
}
|
|
}
|