Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,550 changes: 2,143 additions & 407 deletions assets/acquia-spec.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assets/acquia-spec.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
c1a6c6733a1ec7a8d3344cc2a2390f694ab73335
54fba484c9feb5d0ed8677801be31e14d2e3278b
5 changes: 3 additions & 2 deletions src/Command/Api/ApiCommandHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -396,8 +396,9 @@ private function generateApiCommandsFromSpec(array $acquiaCloudSpec, string $com
$command->setHidden(
self::isDeprecated($schema) || self::isPreRelease($schema)
);
if (array_key_exists('servers', $acquiaCloudSpec)) {
$command->setServers($acquiaCloudSpec['servers']);
$servers = $endpoint['servers'] ?? $acquiaCloudSpec['servers'] ?? [];
if ($servers !== []) {
$command->setServers($servers);
}
$command->setPath($path);

Expand Down
131 changes: 131 additions & 0 deletions tests/phpunit/src/Commands/Api/ApiCommandHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Acquia\Cli\Command\CommandBase;
use Acquia\Cli\Tests\CommandTestBase;
use ReflectionMethod;
use ReflectionProperty;
use Symfony\Component\Console\Command\Command;

/**
Expand Down Expand Up @@ -122,6 +123,136 @@ private function invokeApiCommandHelperMethod(string $methodName, array $args =
return $refClass->invokeArgs($commandHelper, $args);
}

/**
* Per-path servers are used when there are no top-level servers (new cx-api-spec format).
*/
public function testPerPathServersUsedWhenNoTopLevelServers(): void
{
$spec = [
'paths' => [
'/test/endpoint' => [
'get' => [
'responses' => ['200' => ['description' => 'OK']],
'summary' => 'Test endpoint',
'x-cli-name' => 'test:endpoint-get',
],
'servers' => [
['url' => 'https://cloud.acquia.com/api', 'description' => 'Cloud API'],
],
],
],
];

$helper = new ApiCommandHelper($this->logger);
$ref = new ReflectionMethod(ApiCommandHelper::class, 'generateApiCommandsFromSpec');
$commands = $ref->invoke($helper, $spec, 'api', $this->getCommandFactory());

$this->assertCount(1, $commands);
$prop = new ReflectionProperty($commands[0], 'servers');
$this->assertSame(
[['url' => 'https://cloud.acquia.com/api', 'description' => 'Cloud API']],
$prop->getValue($commands[0])
);
}

/**
* Per-path servers take priority over top-level servers when both are present.
*/
public function testPerPathServersTakePriorityOverTopLevelServers(): void
{
$spec = [
'paths' => [
'/test/endpoint' => [
'get' => [
'responses' => ['200' => ['description' => 'OK']],
'summary' => 'Test endpoint',
'x-cli-name' => 'test:endpoint-get',
],
'servers' => [
['url' => 'https://per-path.example.com', 'description' => 'Per-path server'],
],
],
],
'servers' => [
['url' => 'https://top-level.example.com', 'description' => 'Top-level server'],
],
];

$helper = new ApiCommandHelper($this->logger);
$ref = new ReflectionMethod(ApiCommandHelper::class, 'generateApiCommandsFromSpec');
$commands = $ref->invoke($helper, $spec, 'api', $this->getCommandFactory());

$this->assertCount(1, $commands);
$prop = new ReflectionProperty($commands[0], 'servers');
$this->assertSame(
[['url' => 'https://per-path.example.com', 'description' => 'Per-path server']],
$prop->getValue($commands[0])
);
}

/**
* Top-level servers are used as fallback when a path has no per-path servers (old spec format).
*/
public function testTopLevelServersFallbackWhenNoPerPathServers(): void
{
$spec = [
'paths' => [
'/test/endpoint' => [
'get' => [
'responses' => ['200' => ['description' => 'OK']],
'summary' => 'Test endpoint',
'x-cli-name' => 'test:endpoint-get',
],
],
],
'servers' => [
['url' => 'https://cloud.acquia.com/api', 'description' => 'Cloud API'],
],
];

$helper = new ApiCommandHelper($this->logger);
$ref = new ReflectionMethod(ApiCommandHelper::class, 'generateApiCommandsFromSpec');
$commands = $ref->invoke($helper, $spec, 'api', $this->getCommandFactory());

$this->assertCount(1, $commands);
$prop = new ReflectionProperty($commands[0], 'servers');
$this->assertSame(
[['url' => 'https://cloud.acquia.com/api', 'description' => 'Cloud API']],
$prop->getValue($commands[0])
);
}

/**
* setServers must NOT be called when neither top-level nor per-path servers exist.
* Kills mutations that remove the `if ($servers !== [])` guard (MethodCallRemoval,
* NotIdentical→Identical, etc.) — those would call setServers([]) and initialize the property.
*/
public function testServersNotSetWhenNoServersDefinedAnywhere(): void
{
$spec = [
'paths' => [
'/test/endpoint' => [
'get' => [
'responses' => ['200' => ['description' => 'OK']],
'summary' => 'Test endpoint',
'x-cli-name' => 'test:endpoint-get',
],
],
],
];

$helper = new ApiCommandHelper($this->logger);
$ref = new ReflectionMethod(ApiCommandHelper::class, 'generateApiCommandsFromSpec');
$commands = $ref->invoke($helper, $spec, 'api', $this->getCommandFactory());

$this->assertCount(1, $commands);
$prop = new ReflectionProperty($commands[0], 'servers');
$this->assertFalse(
$prop->isInitialized($commands[0]),
'servers property must remain uninitialized when no servers are defined'
);
}

/**
* Test that addPostArgumentUsageToExample correctly formats a flat array with a single item.
*/
Expand Down
30 changes: 28 additions & 2 deletions tests/phpunit/src/Commands/Api/ApiCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,32 @@ protected function createCommand(): CommandBase
return $this->injectCommand(ApiBaseCommand::class);
}

public function testTrustedProxiesCommandExecutesHttpGet(): void
{
$environmentId = '24-a47ac10b-58cc-4372-a567-0e02b2c3d470';
$this->clientProphecy->addOption('headers', ['Accept' => 'application/hal+json, version=2'])
->shouldBeCalled();
$mockBody = self::getMockResponseFromSpec(
'/environments/{environmentId}/trusted-proxies',
'get',
'200'
);
$this->clientProphecy->request('get', '/environments/' . $environmentId . '/trusted-proxies')
->willReturn($mockBody)
->shouldBeCalled();

$this->command = $this->getApiCommandByName('api:environments:trusted-proxies');
$this->executeCommand(['environmentId' => $environmentId]);

$output = $this->getDisplay();
$this->assertJson($output);
$decoded = json_decode($output, true);
$this->assertArrayHasKey('is_enabled', $decoded);
$this->assertTrue($decoded['is_enabled']);
$this->assertArrayHasKey('addresses', $decoded);
$this->assertEquals(0, $this->getStatusCode());
}

public function testTaskWait(): void
{
$environmentId = '24-a47ac10b-58cc-4372-a567-0e02b2c3d470';
Expand Down Expand Up @@ -458,7 +484,7 @@ public function testApiCommandExecutionForHttpPut(): void
*/
public static function providerTestApiCommandDefinitionParameters(): array
{
$apiAccountsSshKeysListUsage = '--from="2023-09-01T00:00:00.000Z" --to="2023-09-29T00:00:00.000Z" --sort="field1,-field2" --limit="10" --offset="10"';
$apiAccountsSshKeysListUsage = '--from="2023-09-01" --to="2023-09-29" --sort="field1,-field2" --limit="10" --offset="10"';
return [
[
'0',
Expand Down Expand Up @@ -557,7 +583,7 @@ public static function providerTestApiCommandDefinitionRequestBody(): array
[
'api:private-networks:create',
'post',
['api:private-networks:create \'123e4567-e89b-12d3-a456-426614174000\' \'us-east-1\' \'customer-private-network\' --description=\'Private network for customer\' --label=\'anyLabel\' --isolation=\'{"dedicated_compute":false,"dedicated_network":false}\' --ingress=\'{"drupal_ssh":{"ingress_acls":["test-acls"]}}\' \'{"cidr":"114.7.55.1\/16","private_egress_access":{"drupal":true},"vpns":[{"name":"vpn1","gateway_ip":"10.10.10.10","routes":["127.0.0.1\/32","127.0.0.2\/32"],"tunnel1":{"shared_key":"sharedKey1","internal_cidr":"192.1.1.0\/24","ike_versions":"1","startup_action":"start","dpd_timeout_action":"stop"},"tunnel2":{"shared_key":"sharedKey2","internal_cidr":"192.1.1.0\/14","ike_versions":"1","startup_action":"start","dpd_timeout_action":"stop"}}],"vpc_peers":[{"name":"vpcPeer1","aws_account":"123456789012","vpc_id":"vpc-1234567890abcdef0","vpc_cidr":"120.24.16.1\/24"}]}\''],
['api:private-networks:create \'123e4567-e89b-12d3-a456-426614174000\' \'us-east-1\' \'customer-private-network\' --description=\'Private network for customer\' --label=\'anyLabel\' --isolation=\'{"dedicated_compute":false,"dedicated_network":false}\' --ingress=\'{"drupal_ssh":{"ingress_acls":["test-acls"]}}\' \'{"cidr":"114.7.55.1\/16","private_egress_access":{"drupal":true},"vpns":[{"name":"vpn1","gateway_ip":"10.10.10.10","routes":["127.0.0.1\/32","127.0.0.2\/32"],"tunnel1":{"shared_key":"sharedKey1","internal_cidr":"192.1.1.0\/24","ike_versions":"1","startup_action":"start","dpd_timeout_action":"stop"},"tunnel2":{"shared_key":"sharedKey2","internal_cidr":"192.1.1.0\/14","ike_versions":"1","startup_action":"start","dpd_timeout_action":"stop"}}],"vpc_peers":[{"name":"vpcPeer1","aws_account":"123456789012","vpc_id":"vpc-1234567890abcdef0","vpc_cidr":"120.24.16.1\/24","region":"eu-central-1"}]}\''],
],
[
'api:private-networks:update-isolation',
Expand Down
2 changes: 1 addition & 1 deletion tests/phpunit/src/Commands/Pull/PullCommandTestBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ protected function mockExecuteMySqlImport(
string $dbMachineName = 'db554675',
string $localDbName = 'jxr5000596dev',
string $env = 'dev',
string $createdAt = '2012-05-15T12:00:00.000Z'
string $createdAt = '2012-05-15T12:00:00Z'
): void {
$localMachineHelper->checkRequiredBinariesExist(['gunzip', 'mysql'])
->shouldBeCalled();
Expand Down
8 changes: 4 additions & 4 deletions tests/phpunit/src/Commands/Ssh/SshKeyInfoCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public function testInfo(): void
$this->assertStringContainsString('UUID 02905393-65d7-4bef-873b-24593f73d273', $output);
$this->assertStringContainsString('Label PC Home', $output);
$this->assertStringContainsString('Fingerprint (md5) 5d:23:fb:45:70:df:ef:ad:ca:bf:81:93:cd:50:26:28', $output);
$this->assertStringContainsString('Created at 2017-05-09T20:30:35.000Z', $output);
$this->assertStringContainsString('Created at 2017-05-09T20:30:35+00:00', $output);
$this->assertStringContainsString('Public key', $output);
// Check for the specific separator line after "Public key" and before the SSH key content.
$this->assertStringContainsString("Public key" . "\n" . "----------", $output);
Expand Down Expand Up @@ -94,7 +94,7 @@ public function testInfoWithCloudOnlyKey(): void
$this->assertStringContainsString('Location Cloud', $output);
$this->assertStringContainsString('Label PC Home', $output);
$this->assertStringContainsString('Fingerprint (md5) 5d:23:fb:45:70:df:ef:ad:ca:bf:81:93:cd:50:26:28', $output);
$this->assertStringContainsString('Created at 2017-05-09T20:30:35.000Z', $output);
$this->assertStringContainsString('Created at 2017-05-09T20:30:35+00:00', $output);
$this->assertStringContainsString('Public key', $output);
// Check for the specific separator line after "Public key" and before the SSH key content.
$this->assertStringContainsString("Public key\n----------", $output);
Expand Down Expand Up @@ -188,7 +188,7 @@ public function testInfoWithLocalAndCloudKey(): void
$this->assertStringContainsString('UUID 02905393-65d7-4bef-873b-24593f73d273', $output);
$this->assertStringContainsString('Label PC Home', $output);
$this->assertStringContainsString('Fingerprint (md5) 5d:23:fb:45:70:df:ef:ad:ca:bf:81:93:cd:50:26:28', $output);
$this->assertStringContainsString('Created at 2017-05-09T20:30:35.000Z', $output);
$this->assertStringContainsString('Created at 2017-05-09T20:30:35+00:00', $output);
$this->assertStringContainsString('Public key', $output);
// Check for the specific separator line after "Public key" and before the SSH key content.
$this->assertStringContainsString("Public key\n----------", $output);
Expand Down Expand Up @@ -264,7 +264,7 @@ public function testPromptsDeletionWhenLocalMatchesCloudAndHasRealPath(): void
$this->assertStringContainsString('UUID 02905393-65d7-4bef-873b-24593f73d273', $output);
$this->assertStringContainsString('Label PC Home', $output);
$this->assertStringContainsString('Fingerprint (md5) 5d:23:fb:45:70:df:ef:ad:ca:bf:81:93:cd:50:26:28', $output);
$this->assertStringContainsString('Created at 2017-05-09T20:30:35.000Z', $output);
$this->assertStringContainsString('Created at 2017-05-09T20:30:35+00:00', $output);
$this->assertStringContainsString('Public key', $output);
// Check for the specific separator line after "Public key" and before the SSH key content.
$this->assertStringContainsString("Public key\n----------", $output);
Expand Down
3 changes: 3 additions & 0 deletions tests/phpunit/src/TestBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,9 @@ protected function getPathMethodCodeFromSpec(string $operationId): array
$acquiaCloudSpec = self::getCloudApiSpec();
foreach ($acquiaCloudSpec['paths'] as $path => $methodEndpoint) {
foreach ($methodEndpoint as $method => $endpoint) {
if (!is_array($endpoint) || !array_key_exists('operationId', $endpoint)) {
continue;
}
if ($endpoint['operationId'] === $operationId) {
foreach ($endpoint['responses'] as $code => $response) {
if ($code >= 200 && $code < 300) {
Expand Down
Loading