appwrite/tests/e2e/Services/Project/ProtocolsBase.php

289 lines
10 KiB
PHP
Raw Normal View History

2026-04-09 13:58:28 +00:00
<?php
namespace Tests\E2E\Services\Project;
use Tests\E2E\Client;
trait ProtocolsBase
{
protected static array $protocols = ['rest', 'graphql', 'websocket'];
// Success flow
public function testDisableProtocol(): void
{
foreach (self::$protocols as $protocol) {
$response = $this->updateProtocolStatus($protocol, false);
$this->assertSame(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertSame(false, $response['body']['protocolStatusFor' . ucfirst($protocol)]);
}
// Cleanup
foreach (self::$protocols as $protocol) {
$this->updateProtocolStatus($protocol, true);
}
}
public function testEnableProtocol(): void
{
// Disable first
foreach (self::$protocols as $protocol) {
$this->updateProtocolStatus($protocol, false);
}
// Re-enable
foreach (self::$protocols as $protocol) {
$response = $this->updateProtocolStatus($protocol, true);
$this->assertSame(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertSame(true, $response['body']['protocolStatusFor' . ucfirst($protocol)]);
}
}
public function testDisableProtocolIdempotent(): void
{
$first = $this->updateProtocolStatus('rest', false);
$this->assertSame(200, $first['headers']['status-code']);
$this->assertSame(false, $first['body']['protocolStatusForRest']);
$second = $this->updateProtocolStatus('rest', false);
$this->assertSame(200, $second['headers']['status-code']);
$this->assertSame(false, $second['body']['protocolStatusForRest']);
// Cleanup
$this->updateProtocolStatus('rest', true);
}
public function testEnableProtocolIdempotent(): void
{
$first = $this->updateProtocolStatus('rest', true);
$this->assertSame(200, $first['headers']['status-code']);
$this->assertSame(true, $first['body']['protocolStatusForRest']);
$second = $this->updateProtocolStatus('rest', true);
$this->assertSame(200, $second['headers']['status-code']);
$this->assertSame(true, $second['body']['protocolStatusForRest']);
}
public function testDisabledRestBlocksClientRequest(): void
{
$this->updateProtocolStatus('rest', false);
2026-04-09 14:08:11 +00:00
$response = $this->client->call(Client::METHOD_GET, '/locale/countries', [
2026-04-09 13:58:28 +00:00
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]);
$this->assertSame(403, $response['headers']['status-code']);
$this->assertSame('general_api_disabled', $response['body']['type']);
// Cleanup
$this->updateProtocolStatus('rest', true);
}
public function testEnabledRestAllowsClientRequest(): void
{
$this->updateProtocolStatus('rest', false);
$this->updateProtocolStatus('rest', true);
2026-04-09 14:08:11 +00:00
$response = $this->client->call(Client::METHOD_GET, '/locale/countries', [
2026-04-09 13:58:28 +00:00
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
2026-04-09 14:08:11 +00:00
]);
2026-04-09 13:58:28 +00:00
$this->assertSame(200, $response['headers']['status-code']);
}
public function testDisabledGraphqlBlocksClientRequest(): void
{
$this->updateProtocolStatus('graphql', false);
$response = $this->client->call(Client::METHOD_POST, '/graphql', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], [
'query' => '{ localeListCountries { total } }',
]);
$this->assertSame(403, $response['headers']['status-code']);
$this->assertSame('general_api_disabled', $response['body']['type']);
// Cleanup
$this->updateProtocolStatus('graphql', true);
}
public function testDisableOneProtocolDoesNotAffectOther(): void
{
$this->updateProtocolStatus('graphql', false);
// REST should still work
2026-04-09 14:08:11 +00:00
$response = $this->client->call(Client::METHOD_GET, '/locale/countries', [
2026-04-09 13:58:28 +00:00
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
2026-04-09 14:08:11 +00:00
]);
2026-04-09 13:58:28 +00:00
$this->assertSame(200, $response['headers']['status-code']);
// Cleanup
$this->updateProtocolStatus('graphql', true);
}
2026-04-09 14:57:44 +00:00
public function testDisabledRestBlocksAllServiceEndpoints(): void
{
$endpoints = [
'account' => '/account',
'teams' => '/teams',
'databases' => '/databases',
'storage' => '/storage/buckets',
'functions' => '/functions',
'sites' => '/sites',
'locale' => '/locale',
'health' => '/health',
'users' => '/users',
'messaging' => '/messaging/providers',
'migrations' => '/migrations',
];
$this->updateProtocolStatus('rest', false);
foreach ($endpoints as $service => $path) {
$response = $this->client->call(Client::METHOD_GET, $path, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]);
$this->assertSame(403, $response['headers']['status-code'], 'Disabled REST protocol should block ' . $service . ' endpoint (got ' . $response['headers']['status-code'] . ')');
$this->assertSame('general_api_disabled', $response['body']['type'], 'Disabled REST protocol should return general_api_disabled for ' . $service);
}
// Cleanup
$this->updateProtocolStatus('rest', true);
}
public function testReenabledRestAllowsAllServiceEndpoints(): void
{
$endpoints = [
'teams' => '/teams',
'databases' => '/databases',
'functions' => '/functions',
'locale' => '/locale',
];
$this->updateProtocolStatus('rest', false);
$this->updateProtocolStatus('rest', true);
foreach ($endpoints as $service => $path) {
$response = $this->client->call(Client::METHOD_GET, $path, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertNotEquals(403, $response['headers']['status-code'], 'Re-enabled REST protocol should not block ' . $service . ' endpoint');
}
}
public function testDisabledGraphqlBlocksMutationRequest(): void
{
$this->updateProtocolStatus('graphql', false);
$response = $this->client->call(Client::METHOD_POST, '/graphql', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], [
'query' => 'mutation { teamsCreate(teamId: "unique()", name: "Test") { _id } }',
]);
$this->assertSame(403, $response['headers']['status-code']);
$this->assertSame('general_api_disabled', $response['body']['type']);
// Cleanup
$this->updateProtocolStatus('graphql', true);
}
2026-04-09 13:58:28 +00:00
public function testResponseModel(): void
{
$response = $this->updateProtocolStatus('rest', false);
$this->assertSame(200, $response['headers']['status-code']);
$this->assertArrayHasKey('$id', $response['body']);
$this->assertArrayHasKey('name', $response['body']);
$this->assertArrayHasKey('protocolStatusForRest', $response['body']);
$this->assertArrayHasKey('protocolStatusForGraphql', $response['body']);
$this->assertArrayHasKey('protocolStatusForWebsocket', $response['body']);
// Cleanup
$this->updateProtocolStatus('rest', true);
}
// Failure flow
public function testUpdateProtocolWithoutAuthentication(): void
{
$response = $this->updateProtocolStatus('rest', false, false);
$this->assertSame(401, $response['headers']['status-code']);
}
public function testUpdateProtocolInvalidProtocolId(): void
{
$response = $this->updateProtocolStatus('invalid', false);
$this->assertSame(400, $response['headers']['status-code']);
}
public function testUpdateProtocolEmptyProtocolId(): void
{
$response = $this->updateProtocolStatus('', false);
2026-04-09 14:08:11 +00:00
$this->assertSame(404, $response['headers']['status-code']);
2026-04-09 13:58:28 +00:00
}
2026-04-17 11:19:20 +00:00
// Backwards compatibility
public function testUpdateProtocolLegacyStatusPath(): void
{
$headers = array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders());
// Disable via the legacy `/status` alias
$response = $this->client->call(Client::METHOD_PATCH, '/project/protocols/rest/status', $headers, [
'enabled' => false,
]);
$this->assertSame(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertSame(false, $response['body']['protocolStatusForRest']);
// Re-enable via the legacy `/status` alias
$response = $this->client->call(Client::METHOD_PATCH, '/project/protocols/rest/status', $headers, [
'enabled' => true,
]);
$this->assertSame(200, $response['headers']['status-code']);
$this->assertSame(true, $response['body']['protocolStatusForRest']);
}
2026-04-09 13:58:28 +00:00
// Helpers
protected function updateProtocolStatus(string $protocolId, bool $enabled, bool $authenticated = true): mixed
{
$headers = [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
];
if ($authenticated) {
$headers = array_merge($headers, $this->getHeaders());
}
2026-04-17 11:19:20 +00:00
return $this->client->call(Client::METHOD_PATCH, '/project/protocols/' . $protocolId, $headers, [
2026-04-09 13:58:28 +00:00
'enabled' => $enabled,
]);
}
}