Skip to content

Commit

Permalink
test pull command
Browse files Browse the repository at this point in the history
  • Loading branch information
danepowell committed Apr 22, 2024
1 parent 9374ed9 commit e29bd2d
Show file tree
Hide file tree
Showing 5 changed files with 263 additions and 229 deletions.
26 changes: 0 additions & 26 deletions tests/phpunit/src/Commands/Pull/PullCodeCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -289,30 +289,4 @@ protected function mockExecuteGitClone(
->shouldBeCalled();
}

protected function mockExecuteGitFetchAndCheckout(
ObjectProphecy $localMachineHelper,
ObjectProphecy $process,
mixed $cwd,
mixed $vcsPath
): void {
$localMachineHelper->execute([
'git',
'fetch',
'--all',
], Argument::type('callable'), $cwd, FALSE)
->willReturn($process->reveal())
->shouldBeCalled();
$this->mockExecuteGitCheckout($localMachineHelper, $vcsPath, $cwd, $process);
}

protected function mockExecuteGitCheckout(ObjectProphecy $localMachineHelper, mixed $vcsPath, mixed $cwd, ObjectProphecy $process): void {
$localMachineHelper->execute([
'git',
'checkout',
$vcsPath,
], Argument::type('callable'), $cwd, FALSE)
->willReturn($process->reveal())
->shouldBeCalled();
}

}
50 changes: 45 additions & 5 deletions tests/phpunit/src/Commands/Pull/PullCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,13 @@
use Acquia\Cli\Command\Pull\PullCommand;
use Acquia\Cli\Exception\AcquiaCliException;
use GuzzleHttp\Client;
use Prophecy\Argument;

/**
* @property \Acquia\Cli\Command\Pull\PullCommand $command
*/
class PullCommandTest extends PullCommandTestBase {

public function setUp(): void {
parent::setUp();
$this->setupFsFixture();
}

protected function createCommand(): CommandBase {
$this->httpClientProphecy = $this->prophet->prophesize(Client::class);

Expand All @@ -37,7 +33,51 @@ protected function createCommand(): CommandBase {
);
}

public function testPull(): void {
// Pull code.
$environment = $this->mockGetEnvironment();
$this->createMockGitConfigFile();
$localMachineHelper = $this->mockLocalMachineHelper();
$localMachineHelper->checkRequiredBinariesExist(["git"])->shouldBeCalled();
$finder = $this->mockFinder();
$localMachineHelper->getFinder()->willReturn($finder->reveal());
$process = $this->mockProcess();
$this->mockExecuteGitFetchAndCheckout($localMachineHelper, $process, $this->projectDir, $environment->vcs->path);
$this->mockExecuteGitStatus(FALSE, $localMachineHelper, $this->projectDir);

// Pull files.
$sshHelper = $this->mockSshHelper();
$this->mockGetCloudSites($sshHelper, $environment);
$this->mockGetFilesystem($localMachineHelper);
$parts = explode('.', $environment->ssh_url);
$sitegroup = reset($parts);
$this->mockExecuteRsync($localMachineHelper, $environment, '/mnt/files/' . $sitegroup . '.' . $environment->name . '/sites/bar/files/', $this->projectDir . '/docroot/sites/bar/files');
$this->command->sshHelper = $sshHelper->reveal();

// Pull database.
$this->mockExecuteMySqlConnect($localMachineHelper, TRUE);
$this->mockGetBackup($environment);
$this->mockExecuteMySqlListTables($localMachineHelper, 'drupal');
$process = $this->mockProcess();
$localMachineHelper
->execute(Argument::type('array'), Argument::type('callable'), NULL, FALSE, NULL, ['MYSQL_PWD' => $this->dbPassword])
->willReturn($process->reveal())
->shouldBeCalled();
$this->mockExecuteMySqlImport($localMachineHelper, TRUE, TRUE, 'my_db', 'my_dbdev', 'drupal');
$this->executeCommand([
'--no-scripts' => TRUE,
], self::inputChooseEnvironment());

$output = $this->getDisplay();

$this->assertStringContainsString('Select a Cloud Platform application:', $output);
$this->assertStringContainsString('[0] Sample application 1', $output);
$this->assertStringContainsString('Choose a Cloud Platform environment', $output);
$this->assertStringContainsString('[0] Dev, dev (vcs: master)', $output);
}

public function testMissingLocalRepo(): void {
$this->setupFsFixture();
// Unset repo root. Mimics failing to find local git repo. Command must be re-created
// to re-inject the parameter into the command.
$this->acliRepoRoot = '';
Expand Down
218 changes: 218 additions & 0 deletions tests/phpunit/src/Commands/Pull/PullCommandTestBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,19 @@

namespace Acquia\Cli\Tests\Commands\Pull;

use Acquia\Cli\Helpers\LocalMachineHelper;
use Acquia\Cli\Helpers\SshHelper;
use Acquia\Cli\Tests\Commands\Ide\IdeRequiredTestTrait;
use Acquia\Cli\Tests\CommandTestBase;
use AcquiaCloudApi\Response\BackupsResponse;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Psr7\Uri;
use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
use Symfony\Component\Filesystem\Path;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Process\Process;

Expand All @@ -18,7 +26,13 @@ abstract class PullCommandTestBase extends CommandTestBase {

protected Client|ObjectProphecy $httpClientProphecy;

protected string $dbUser = 'drupal';
protected string $dbPassword = 'drupal';
protected string $dbHost = 'localhost';
protected string $dbName = 'drupal';

public function setUp(): void {
self::unsetEnvVars(['ACLI_DB_HOST', 'ACLI_DB_USER', 'ACLI_DB_PASSWORD', 'ACLI_DB_NAME']);
parent::setUp();
}

Expand Down Expand Up @@ -159,4 +173,208 @@ protected function mockFinder(): ObjectProphecy {
return $finder;
}

protected function mockExecuteGitFetchAndCheckout(
ObjectProphecy $localMachineHelper,
ObjectProphecy $process,
string $cwd,
string $vcsPath
): void {
$localMachineHelper->execute([
'git',
'fetch',
'--all',
], Argument::type('callable'), $cwd, FALSE)
->willReturn($process->reveal())
->shouldBeCalled();
$this->mockExecuteGitCheckout($localMachineHelper, $vcsPath, $cwd, $process);
}

protected function mockExecuteGitCheckout(ObjectProphecy $localMachineHelper, string $vcsPath, string $cwd, ObjectProphecy $process): void {
$localMachineHelper->execute([
'git',
'checkout',
$vcsPath,
], Argument::type('callable'), $cwd, FALSE)
->willReturn($process->reveal())
->shouldBeCalled();
}

protected function mockExecuteRsync(
LocalMachineHelper|ObjectProphecy $localMachineHelper,
mixed $environment,
string $sourceDir,
string $destinationDir
): void {
$process = $this->mockProcess();
$localMachineHelper->checkRequiredBinariesExist(['rsync'])->shouldBeCalled();
$command = [
'rsync',
'-avPhze',
'ssh -o StrictHostKeyChecking=no',
$environment->ssh_url . ':' . $sourceDir,
$destinationDir,
];
$localMachineHelper->execute($command, Argument::type('callable'), NULL, TRUE)
->willReturn($process->reveal())
->shouldBeCalled();
}

protected function mockExecuteMySqlConnect(
ObjectProphecy $localMachineHelper,
bool $success
): void {
$localMachineHelper->checkRequiredBinariesExist(["mysql"])->shouldBeCalled();
$process = $this->mockProcess($success);
$localMachineHelper
->execute([
'mysql',
'--host',
$this->dbHost,
'--user',
'drupal',
'drupal',
], Argument::type('callable'), NULL, FALSE, NULL, ['MYSQL_PWD' => 'drupal'])
->willReturn($process->reveal())
->shouldBeCalled();
}

protected function mockExecuteMySqlListTables(
LocalMachineHelper|ObjectProphecy $localMachineHelper,
string $dbName = 'jxr5000596dev',
): void {
$localMachineHelper->checkRequiredBinariesExist(["mysql"])->shouldBeCalled();
$process = $this->mockProcess();
$process->getOutput()->willReturn('table1');
$command = [
'mysql',
'--host',
'localhost',
'--user',
'drupal',
$dbName,
'--silent',
'-e',
'SHOW TABLES;',
];
$localMachineHelper
->execute($command, Argument::type('callable'), NULL, FALSE, NULL, ['MYSQL_PWD' => $this->dbPassword])
->willReturn($process->reveal())
->shouldBeCalled();
}

protected function mockExecuteMySqlDropDb(
LocalMachineHelper|ObjectProphecy $localMachineHelper,
bool $success,
ObjectProphecy $fs
): void {
$localMachineHelper->checkRequiredBinariesExist(["mysql"])->shouldBeCalled();
$process = $this->mockProcess($success);
$fs->tempnam(Argument::type('string'), 'acli_drop_table_', '.sql')->willReturn('something')->shouldBeCalled();
$fs->dumpfile('something', Argument::type('string'))->shouldBeCalled();
$localMachineHelper
->execute(Argument::type('array'), Argument::type('callable'), NULL, FALSE, NULL, ['MYSQL_PWD' => $this->dbPassword])
->willReturn($process->reveal())
->shouldBeCalled();
}

protected function mockExecuteMySqlImport(
ObjectProphecy $localMachineHelper,
bool $success,
bool $pvExists,
string $dbName = 'jxr5000596dev',
string $dbMachineName = 'db554675',
string $localDbName = 'jxr5000596dev'
): void {
$localMachineHelper->checkRequiredBinariesExist(['gunzip', 'mysql'])->shouldBeCalled();
$this->mockExecutePvExists($localMachineHelper, $pvExists);
$process = $this->mockProcess($success);
$filePath = Path::join(sys_get_temp_dir(), "dev-$dbName-$dbMachineName-2012-05-15T12:00:00Z.sql.gz");
$command = $pvExists ? "pv $filePath --bytes --rate | gunzip | MYSQL_PWD=drupal mysql --host=localhost --user=drupal $localDbName" : "gunzip -c $filePath | MYSQL_PWD=drupal mysql --host=localhost --user=drupal $localDbName";
// MySQL import command.
$localMachineHelper
->executeFromCmd($command, Argument::type('callable'),
NULL, TRUE, NULL)
->willReturn($process->reveal())
->shouldBeCalled();
}

protected function mockDownloadMySqlDump(ObjectProphecy $localMachineHelper, mixed $success): void {
$this->mockProcess($success);
$localMachineHelper->writeFile(
Argument::containingString("dev-profserv2-profserv201dev-something.sql.gz"),
'backupfilecontents'
)
->shouldBeCalled();
}

protected function mockSettingsFiles(ObjectProphecy $fs): void {
$fs->remove(Argument::type('string'))
->willReturn()
->shouldBeCalled();
}

protected function mockListSites(SshHelper|ObjectProphecy $sshHelper): void {
$process = $this->mockProcess();
$process->getOutput()->willReturn('default')->shouldBeCalled();
$sshHelper->executeCommand(Argument::type('object'), ['ls', '/mnt/files/site.dev/sites'], FALSE)
->willReturn($process->reveal())->shouldBeCalled();
}

public function mockGetBackup(mixed $environment): void {
$databases = $this->mockRequest('getEnvironmentsDatabases', $environment->id);
$tamper = function ($backups): void {
$backups[0]->completedAt = $backups[0]->completed_at;
};
$backups = new BackupsResponse(
$this->mockRequest('getEnvironmentsDatabaseBackups', [
$environment->id,
'my_db',
], NULL, NULL, $tamper)
);
$this->mockDownloadBackup($databases[0], $environment, $backups[0]);
}

protected function mockDownloadBackup(object $database, object $environment, object $backup, int $curlCode = 0): object {
if ($curlCode) {
$this->prophet->prophesize(StreamInterface::class);
/** @var RequestException|ObjectProphecy $requestException */
$requestException = $this->prophet->prophesize(RequestException::class);
$requestException->getHandlerContext()->willReturn(['errno' => $curlCode]);
$this->clientProphecy->stream('get', "/environments/{$environment->id}/databases/{$database->name}/backups/1/actions/download", [])
->willThrow($requestException->reveal())
->shouldBeCalled();
$response = $this->prophet->prophesize(ResponseInterface::class);
$this->httpClientProphecy->request('GET', 'https://other.example.com/download-backup', Argument::type('array'))->willReturn($response->reveal())->shouldBeCalled();
$domainsResponse = $this->getMockResponseFromSpec('/environments/{environmentId}/domains', 'get', 200);
$this->clientProphecy->request('get', "/environments/{$environment->id}/domains")->willReturn($domainsResponse->_embedded->items);
$this->command->setBackupDownloadUrl(new Uri( 'https://www.example.com/download-backup'));
}
else {
$this->mockDownloadBackupResponse($environment, $database->name, 1);
}
if ($database->flags->default) {
$dbMachineName = $database->name . $environment->name;
}
else {
$dbMachineName = 'db' . $database->id;
}
$filename = implode('-', [
$environment->name,
$database->name,
$dbMachineName,
$backup->completedAt,
]) . '.sql.gz';
$localFilepath = Path::join(sys_get_temp_dir(), $filename);
$this->clientProphecy->addOption('sink', $localFilepath)->shouldBeCalled();
$this->clientProphecy->addOption('curl.options', [
'CURLOPT_FILE' => $localFilepath,
'CURLOPT_RETURNTRANSFER' => FALSE,
])->shouldBeCalled();
$this->clientProphecy->addOption('progress', Argument::type('Closure'))->shouldBeCalled();
$this->clientProphecy->addOption('on_stats', Argument::type('Closure'))->shouldBeCalled();
$this->clientProphecy->getOptions()->willReturn([]);

return $database;
}

}
Loading

0 comments on commit e29bd2d

Please sign in to comment.