Add unit tests for scopedProjectId and source fields in Key::decode()

This commit is contained in:
Prem Palanisamy
2026-03-23 13:46:58 +00:00
parent b0a0c1552e
commit 374e1f8782
3 changed files with 69 additions and 18 deletions
+1 -1
View File
@@ -697,7 +697,7 @@ Http::get('/v1/migrations/:migrationId')
Http::get('/v1/migrations/appwrite/console-key')
->groups(['api', 'migrations'])
->desc('Generate console API key for migration')
->label('scope', 'migrations.read')
->label('scope', 'migrations.write')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
@@ -1173,13 +1173,32 @@ trait MigrationsBase
/**
* Integrations
*/
public function testAppwriteMigrationPlatform(): void
public function testGetAppwriteConsoleKey(): void
{
// Create platform on source project
$response = $this->client->call(Client::METHOD_POST, '/projects/' . $this->getProject()['$id'] . '/platforms', array_merge([
$response = $this->client->call(Client::METHOD_GET, '/migrations/appwrite/console-key', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['key']);
$this->assertStringStartsWith('dynamic_', $response['body']['key']);
$this->assertNotEmpty($response['body']['expire']);
$this->assertGreaterThan(new \DateTime(), new \DateTime($response['body']['expire']));
}
public function testAppwriteMigrationPlatform(): void
{
$consoleSessionHeaders = [
'content-type' => 'application/json',
'x-appwrite-project' => 'console',
'origin' => 'http://localhost',
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
];
// Create platform on source project
$response = $this->client->call(Client::METHOD_POST, '/projects/' . $this->getProject()['$id'] . '/platforms', $consoleSessionHeaders, [
'type' => 'web',
'name' => 'Test Platform',
'hostname' => 'localhost',
@@ -1209,11 +1228,22 @@ trait MigrationsBase
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_PLATFORM]['processing']);
$this->assertEquals(0, $result['statusCounters'][Resource::TYPE_PLATFORM]['warning']);
// Verify platform on destination project
$response = $this->client->call(Client::METHOD_GET, '/projects/' . $this->getDestinationProject()['$id'] . '/platforms', array_merge([
// Get a console key for the destination project to access console-scoped endpoints
$consoleKeyResponse = $this->client->call(Client::METHOD_GET, '/migrations/appwrite/console-key', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
'x-appwrite-project' => $this->getDestinationProject()['$id'],
'x-appwrite-key' => $this->getDestinationProject()['apiKey'],
]);
$this->assertEquals(200, $consoleKeyResponse['headers']['status-code']);
$destConsoleKey = $consoleKeyResponse['body']['key'];
// Verify platform on destination project using console key
$response = $this->client->call(Client::METHOD_GET, '/projects/' . $this->getDestinationProject()['$id'] . '/platforms', [
'content-type' => 'application/json',
'x-appwrite-project' => 'console',
'x-appwrite-key' => $destConsoleKey,
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']);
@@ -1234,17 +1264,15 @@ trait MigrationsBase
$this->assertEquals('Test Platform', $foundPlatform['name']);
$this->assertEquals('localhost', $foundPlatform['hostname']);
// Cleanup on destination
$this->client->call(Client::METHOD_DELETE, '/projects/' . $this->getDestinationProject()['$id'] . '/platforms/' . $foundPlatform['$id'], array_merge([
// Cleanup on destination using console key
$this->client->call(Client::METHOD_DELETE, '/projects/' . $this->getDestinationProject()['$id'] . '/platforms/' . $foundPlatform['$id'], [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), []);
'x-appwrite-project' => 'console',
'x-appwrite-key' => $destConsoleKey,
]);
// Cleanup on source
$this->client->call(Client::METHOD_DELETE, '/projects/' . $this->getProject()['$id'] . '/platforms/' . $platform['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), []);
// Cleanup on source using console project + session auth
$this->client->call(Client::METHOD_DELETE, '/projects/' . $this->getProject()['$id'] . '/platforms/' . $platform['$id'], $consoleSessionHeaders);
}
/**
+23
View File
@@ -49,6 +49,7 @@ class KeyTest extends TestCase
'projectCheckDisabled' => true,
'previewAuthDisabled' => true,
'deploymentStatusIgnored' => true,
'source' => KEY_SOURCE_MIGRATION,
];
$key = static::generateKey($projectId, $usage, $scopes, extra: $extra);
$decoded = Key::decode(
@@ -70,6 +71,28 @@ class KeyTest extends TestCase
$this->assertEquals(true, $decoded->isProjectCheckDisabled());
$this->assertEquals(true, $decoded->isPreviewAuthDisabled());
$this->assertEquals(true, $decoded->isDeploymentStatusIgnored());
$this->assertEquals(KEY_SOURCE_MIGRATION, $decoded->getSource());
// Decode dynamic key with scopedProjectId — scopes must NOT be merged with role scopes
$scopedProjectId = 'scoped-project-123';
$extra = [
'scopedProjectId' => $scopedProjectId,
'source' => KEY_SOURCE_MIGRATION,
];
$key = static::generateKey('console', $usage, $scopes, extra: $extra);
$decoded = Key::decode(
project: new Document(['$id' => 'console']),
team: new Document(),
user: new Document(),
key: $key,
);
$this->assertEquals('console', $decoded->getProjectId());
$this->assertEquals(API_KEY_DYNAMIC, $decoded->getType());
$this->assertEquals(User::ROLE_APPS, $decoded->getRole());
$this->assertEquals($scopes, $decoded->getScopes());
$this->assertNotEquals(\array_merge($scopes, $roleScopes), $decoded->getScopes());
$this->assertEquals($scopedProjectId, $decoded->getScopedProjectId());
$this->assertEquals(KEY_SOURCE_MIGRATION, $decoded->getSource());
// Decode invalid dynamic key
$invalidKey = API_KEY_DYNAMIC . '_invalid_jwt_token';