1
0
Fork 0
composer/tests/Composer/Test/Util/GitTest.php

310 lines
12 KiB
PHP
Raw Normal View History

2022-02-23 15:58:18 +00:00
<?php declare(strict_types=1);
2020-11-22 13:48:56 +00:00
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Test\Util;
use Composer\Config;
use Composer\IO\IOInterface;
use Composer\Test\Mock\HttpDownloaderMock;
use Composer\Util\Filesystem;
use Composer\Util\Git;
use Composer\Test\Mock\ProcessExecutorMock;
2020-02-07 03:18:45 +00:00
use Composer\Test\TestCase;
class GitTest extends TestCase
{
/** @var Git */
private $git;
/** @var IOInterface&\PHPUnit\Framework\MockObject\MockObject */
private $io;
/** @var Config&\PHPUnit\Framework\MockObject\MockObject */
private $config;
/** @var ProcessExecutorMock */
private $process;
/** @var Filesystem&\PHPUnit\Framework\MockObject\MockObject */
private $fs;
2021-12-08 16:03:05 +00:00
protected function setUp(): void
{
$this->io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
$this->config = $this->getMockBuilder('Composer\Config')->disableOriginalConstructor()->getMock();
$this->process = $this->getProcessExecutorMock();
$this->fs = $this->getMockBuilder('Composer\Util\Filesystem')->disableOriginalConstructor()->getMock();
$this->git = new Git($this->io, $this->config, $this->process, $this->fs);
}
/**
* @dataProvider publicGithubNoCredentialsProvider
*/
2022-02-22 15:47:09 +00:00
public function testRunCommandPublicGitHubRepositoryNotInitialClone(string $protocol, string $expectedUrl): void
{
2022-02-21 12:42:28 +00:00
$commandCallable = function ($url) use ($expectedUrl): string {
self::assertSame($expectedUrl, $url);
return 'git command';
};
$this->mockConfig($protocol);
2022-08-17 12:20:07 +00:00
$this->process->expects(['git command'], true);
// @phpstan-ignore method.deprecated
$this->git->runCommand($commandCallable, 'https://github.com/acme/repo', null, true);
}
public static function publicGithubNoCredentialsProvider(): array
{
2022-08-17 12:20:07 +00:00
return [
['ssh', 'git@github.com:acme/repo'],
['https', 'https://github.com/acme/repo'],
];
}
public function testRunCommandPrivateGitHubRepositoryNotInitialCloneNotInteractiveWithoutAuthentication(): void
{
2021-12-09 19:55:26 +00:00
self::expectException('RuntimeException');
2022-02-21 12:42:28 +00:00
$commandCallable = function ($url): string {
self::assertSame('https://github.com/acme/repo', $url);
return 'git command';
};
$this->mockConfig('https');
2022-08-17 12:20:07 +00:00
$this->process->expects([
['cmd' => 'git command', 'return' => 1],
['cmd' => ['git', '--version'], 'return' => 0],
2022-08-17 12:20:07 +00:00
], true);
// @phpstan-ignore method.deprecated
$this->git->runCommand($commandCallable, 'https://github.com/acme/repo', null, true);
}
/**
* @dataProvider privateGithubWithCredentialsProvider
*/
2022-02-22 15:47:09 +00:00
public function testRunCommandPrivateGitHubRepositoryNotInitialCloneNotInteractiveWithAuthentication(string $gitUrl, string $protocol, string $gitHubToken, string $expectedUrl, int $expectedFailuresBeforeSuccess): void
{
2022-08-17 12:20:07 +00:00
$commandCallable = static function ($url) use ($expectedUrl): string {
if ($url !== $expectedUrl) {
return 'git command failing';
}
return 'git command ok';
};
$this->mockConfig($protocol);
2022-08-17 12:20:07 +00:00
$expectedCalls = array_fill(0, $expectedFailuresBeforeSuccess, ['cmd' => 'git command failing', 'return' => 1]);
$expectedCalls[] = ['cmd' => 'git command ok', 'return' => 0];
$this->process->expects($expectedCalls, true);
$this->io
->method('isInteractive')
->willReturn(false);
$this->io
->expects($this->atLeastOnce())
->method('hasAuthentication')
->with($this->equalTo('github.com'))
->willReturn(true);
$this->io
->expects($this->atLeastOnce())
->method('getAuthentication')
->with($this->equalTo('github.com'))
2022-08-17 12:20:07 +00:00
->willReturn(['username' => 'token', 'password' => $gitHubToken]);
// @phpstan-ignore method.deprecated
$this->git->runCommand($commandCallable, $gitUrl, null, true);
}
/**
* @dataProvider privateBitbucketWithCredentialsProvider
*/
public function testRunCommandPrivateBitbucketRepositoryNotInitialCloneNotInteractiveWithAuthentication(string $gitUrl, ?string $bitbucketToken, string $expectedUrl, int $expectedFailuresBeforeSuccess, int $bitbucket_git_auth_calls = 0): void
{
$commandCallable = static function ($url) use ($expectedUrl): string {
if ($url !== $expectedUrl) {
return 'git command failing';
}
return 'git command ok';
};
$this->config
->method('get')
->willReturnMap([
['gitlab-domains', 0, ['gitlab.com']],
['github-domains', 0, ['github.com']],
]);
$expectedCalls = array_fill(0, $expectedFailuresBeforeSuccess, ['cmd' => 'git command failing', 'return' => 1]);
if ($bitbucket_git_auth_calls > 0) {
// When we are testing what happens without auth saved, and URLs
// with https, there will also be an attempt to find the token in
// the git config for the folder and repo, locally.
$additional_calls = array_fill(0, $bitbucket_git_auth_calls, ['cmd' => ['git', 'config', 'bitbucket.accesstoken'], 'return' => 1]);
foreach ($additional_calls as $call) {
$expectedCalls[] = $call;
}
}
$expectedCalls[] = ['cmd' => 'git command ok', 'return' => 0];
$this->process->expects($expectedCalls, true);
$this->io
->method('isInteractive')
->willReturn(false);
if (null !== $bitbucketToken) {
$this->io
->expects($this->atLeastOnce())
->method('hasAuthentication')
->with($this->equalTo('bitbucket.org'))
->willReturn(true);
$this->io
->expects($this->atLeastOnce())
->method('getAuthentication')
->with($this->equalTo('bitbucket.org'))
->willReturn(['username' => 'token', 'password' => $bitbucketToken]);
}
// @phpstan-ignore method.deprecated
$this->git->runCommand($commandCallable, $gitUrl, null, true);
}
/**
* @dataProvider privateBitbucketWithOauthProvider
*
* @param string $gitUrl
* @param string $expectedUrl
* @param array{'username': string, 'password': string}[] $initial_config
*/
public function testRunCommandPrivateBitbucketRepositoryNotInitialCloneInteractiveWithOauth(string $gitUrl, string $expectedUrl, array $initial_config = []): void
{
$commandCallable = static function ($url) use ($expectedUrl): string {
if ($url !== $expectedUrl) {
return 'git command failing';
}
return 'git command ok';
};
$expectedCalls = [];
$expectedCalls[] = ['cmd' => 'git command failing', 'return' => 1];
if (count($initial_config) > 0) {
$expectedCalls[] = ['cmd' => 'git command failing', 'return' => 1];
} else {
$expectedCalls[] = ['cmd' => ['git', 'config', 'bitbucket.accesstoken'], 'return' => 1];
}
$expectedCalls[] = ['cmd' => 'git command ok', 'return' => 0];
$this->process->expects($expectedCalls, true);
$this->config
->method('get')
->willReturnMap([
['gitlab-domains', 0, ['gitlab.com']],
['github-domains', 0, ['github.com']],
]);
$this->io
->method('isInteractive')
->willReturn(true);
$this->io
->method('askConfirmation')
->willReturnCallback(function () {
return true;
});
$this->io->method('askAndHideAnswer')
->willReturnCallback(function ($question) {
switch ($question) {
case 'Consumer Key (hidden): ':
return 'my-consumer-key';
case 'Consumer Secret (hidden): ':
return 'my-consumer-secret';
}
return '';
});
$this->io
->method('hasAuthentication')
->with($this->equalTo('bitbucket.org'))
->willReturnCallback(function ($repositoryName) use (&$initial_config) {
return isset($initial_config[$repositoryName]);
});
$this->io
->method('setAuthentication')
->willReturnCallback(function (string $repositoryName, string $username, ?string $password = null) use (&$initial_config) {
$initial_config[$repositoryName] = ['username' => $username, 'password' => $password];
});
$this->io
->method('getAuthentication')
->willReturnCallback(function (string $repositoryName) use (&$initial_config) {
if (isset($initial_config[$repositoryName])) {
return $initial_config[$repositoryName];
}
return ['username' => null, 'password' => null];
});
$downloader_mock = $this->getHttpDownloaderMock();
$downloader_mock->expects([
['url' => 'https://bitbucket.org/site/oauth2/access_token', 'status' => 200, 'body' => '{"expires_in": 600, "access_token": "my-access-token"}']
]);
$this->git->setHttpDownloader($downloader_mock);
// @phpstan-ignore method.deprecated
$this->git->runCommand($commandCallable, $gitUrl, null, true);
}
public static function privateBitbucketWithOauthProvider(): array
{
return [
['git@bitbucket.org:acme/repo.git', 'https://x-token-auth:my-access-token@bitbucket.org/acme/repo.git'],
['https://bitbucket.org/acme/repo.git', 'https://x-token-auth:my-access-token@bitbucket.org/acme/repo.git'],
['https://bitbucket.org/acme/repo', 'https://x-token-auth:my-access-token@bitbucket.org/acme/repo.git'],
['git@bitbucket.org:acme/repo.git', 'https://x-token-auth:my-access-token@bitbucket.org/acme/repo.git', ['bitbucket.org' => ['username' => 'someuseralsoswappedfortoken', 'password' => 'little green men']]],
];
}
public static function privateBitbucketWithCredentialsProvider(): array
{
return [
['git@bitbucket.org:acme/repo.git', 'MY_BITBUCKET_TOKEN', 'https://token:MY_BITBUCKET_TOKEN@bitbucket.org/acme/repo.git', 1],
['https://bitbucket.org/acme/repo', 'MY_BITBUCKET_TOKEN', 'https://token:MY_BITBUCKET_TOKEN@bitbucket.org/acme/repo.git', 1],
['https://bitbucket.org/acme/repo.git', 'MY_BITBUCKET_TOKEN', 'https://token:MY_BITBUCKET_TOKEN@bitbucket.org/acme/repo.git', 1],
['git@bitbucket.org:acme/repo.git', null, 'git@bitbucket.org:acme/repo.git', 0],
['https://bitbucket.org/acme/repo', null, 'git@bitbucket.org:acme/repo.git', 1, 1],
['https://bitbucket.org/acme/repo.git', null, 'git@bitbucket.org:acme/repo.git', 1, 1],
];
}
public static function privateGithubWithCredentialsProvider(): array
{
2022-08-17 12:20:07 +00:00
return [
['git@github.com:acme/repo.git', 'ssh', 'MY_GITHUB_TOKEN', 'https://token:MY_GITHUB_TOKEN@github.com/acme/repo.git', 1],
['https://github.com/acme/repo', 'https', 'MY_GITHUB_TOKEN', 'https://token:MY_GITHUB_TOKEN@github.com/acme/repo.git', 2],
];
}
2022-02-22 15:47:09 +00:00
private function mockConfig(string $protocol): void
{
$this->config
->method('get')
2022-08-17 12:20:07 +00:00
->willReturnMap([
['github-domains', 0, ['github.com']],
['github-protocols', 0, [$protocol]],
]);
}
}