1
0
Fork 0
composer/tests/Composer/Test/Repository/ComposerRepositoryTest.php

434 lines
16 KiB
PHP
Raw Normal View History

2022-02-23 15:58:18 +00:00
<?php declare(strict_types=1);
/*
* 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\Repository;
use Composer\IO\NullIO;
2021-12-08 13:10:42 +00:00
use Composer\Json\JsonFile;
2016-06-21 14:38:52 +00:00
use Composer\Repository\ComposerRepository;
use Composer\Repository\RepositoryInterface;
use Composer\Semver\Constraint\Constraint;
use Composer\Test\Mock\FactoryMock;
use Composer\Test\TestCase;
use Composer\Package\Loader\ArrayLoader;
class ComposerRepositoryTest extends TestCase
{
/**
* @dataProvider loadDataProvider
*
* @param mixed[] $expected
* @param array<string, mixed> $repoPackages
*/
public function testLoadData(array $expected, array $repoPackages): void
{
2022-08-17 12:20:07 +00:00
$repoConfig = [
'url' => 'http://example.org',
2022-08-17 12:20:07 +00:00
];
$repository = $this->getMockBuilder('Composer\Repository\ComposerRepository')
2022-08-17 12:20:07 +00:00
->onlyMethods(['loadRootServerFile'])
->setConstructorArgs([
$repoConfig,
new NullIO,
FactoryMock::createConfig(),
$this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock(),
2020-11-22 13:48:56 +00:00
$this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock(),
2022-08-17 12:20:07 +00:00
])
->getMock();
$repository
->expects($this->exactly(2))
->method('loadRootServerFile')
->will($this->returnValue($repoPackages));
// Triggers initialization
$packages = $repository->getPackages();
// Final sanity check, ensure the correct number of packages were added.
self::assertCount(count($expected), $packages);
2022-01-03 14:40:32 +00:00
foreach ($expected as $index => $pkg) {
self::assertSame($pkg['name'].' '.$pkg['version'], $packages[$index]->getName().' '.$packages[$index]->getPrettyVersion());
}
}
public static function loadDataProvider(): array
{
2022-08-17 12:20:07 +00:00
return [
// Old repository format
2022-08-17 12:20:07 +00:00
[
[
['name' => 'foo/bar', 'version' => '1.0.0'],
],
['foo/bar' => [
'name' => 'foo/bar',
2022-08-17 12:20:07 +00:00
'versions' => [
'1.0.0' => ['name' => 'foo/bar', 'version' => '1.0.0'],
],
]],
],
// New repository format
2022-08-17 12:20:07 +00:00
[
[
['name' => 'bar/foo', 'version' => '3.14'],
['name' => 'bar/foo', 'version' => '3.145'],
],
['packages' => [
'bar/foo' => [
'3.14' => ['name' => 'bar/foo', 'version' => '3.14'],
'3.145' => ['name' => 'bar/foo', 'version' => '3.145'],
],
]],
],
// New repository format but without versions as keys should also be supported
[
[
['name' => 'bar/foo', 'version' => '3.14'],
['name' => 'bar/foo', 'version' => '3.145'],
],
['packages' => [
'bar/foo' => [
['name' => 'bar/foo', 'version' => '3.14'],
['name' => 'bar/foo', 'version' => '3.145'],
],
]],
],
2022-08-17 12:20:07 +00:00
];
}
public function testWhatProvides(): void
{
$repo = $this->getMockBuilder('Composer\Repository\ComposerRepository')
2022-08-17 12:20:07 +00:00
->setConstructorArgs([
['url' => 'https://dummy.test.link'],
2019-02-20 12:41:43 +00:00
new NullIO,
FactoryMock::createConfig(),
$this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock(),
2020-11-22 13:48:56 +00:00
$this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock(),
2022-08-17 12:20:07 +00:00
])
->onlyMethods(['fetchFile'])
->getMock();
$cache = $this->getMockBuilder('Composer\Cache')->disableOriginalConstructor()->getMock();
$cache->expects($this->any())
->method('sha256')
->will($this->returnValue(false));
2022-08-17 12:20:07 +00:00
$properties = [
'cache' => $cache,
'loader' => new ArrayLoader(),
2022-08-17 12:20:07 +00:00
'providerListing' => ['a' => ['sha256' => 'xxx']],
'providersUrl' => 'https://dummy.test.link/to/%package%/file',
2022-08-17 12:20:07 +00:00
];
foreach ($properties as $property => $value) {
$ref = new \ReflectionProperty($repo, $property);
$ref->setAccessible(true);
$ref->setValue($repo, $value);
}
$repo->expects($this->any())
->method('fetchFile')
2022-08-17 12:20:07 +00:00
->will($this->returnValue([
'packages' => [
[[
'uid' => 1,
'name' => 'a',
'version' => 'dev-master',
2022-08-17 12:20:07 +00:00
'extra' => ['branch-alias' => ['dev-master' => '1.0.x-dev']],
]],
[[
'uid' => 2,
'name' => 'a',
'version' => 'dev-develop',
2022-08-17 12:20:07 +00:00
'extra' => ['branch-alias' => ['dev-develop' => '1.1.x-dev']],
]],
[[
'uid' => 3,
'name' => 'a',
'version' => '0.6',
2022-08-17 12:20:07 +00:00
]],
],
]));
$reflMethod = new \ReflectionMethod(ComposerRepository::class, 'whatProvides');
$reflMethod->setAccessible(true);
2020-04-13 10:10:31 +00:00
$packages = $reflMethod->invoke($repo, 'a');
self::assertCount(5, $packages);
self::assertEquals(['1', '1-alias', '2', '2-alias', '3'], array_keys($packages));
self::assertSame($packages['2'], $packages['2-alias']->getAliasOf());
}
2016-06-21 14:38:52 +00:00
public function testSearchWithType(): void
2016-06-21 14:38:52 +00:00
{
2022-08-17 12:20:07 +00:00
$repoConfig = [
2016-06-21 14:38:52 +00:00
'url' => 'http://example.org',
2022-08-17 12:20:07 +00:00
];
2016-06-21 14:38:52 +00:00
2022-08-17 12:20:07 +00:00
$result = [
'results' => [
[
2016-06-21 14:38:52 +00:00
'name' => 'foo',
2017-03-08 14:07:29 +00:00
'description' => null,
2022-08-17 12:20:07 +00:00
],
],
];
2016-06-21 14:38:52 +00:00
$httpDownloader = $this->getHttpDownloaderMock();
$httpDownloader->expects(
[
2022-08-17 12:20:07 +00:00
['url' => 'http://example.org/packages.json', 'body' => JsonFile::encode(['search' => '/search.json?q=%query%&type=%type%'])],
['url' => 'http://example.org/search.json?q=foo&type=composer-plugin', 'body' => JsonFile::encode($result)],
2022-08-17 12:20:07 +00:00
['url' => 'http://example.org/search.json?q=foo&type=library', 'body' => JsonFile::encode([])],
],
true
);
$eventDispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
->disableOriginalConstructor()
->getMock();
$config = FactoryMock::createConfig();
$config->merge(['config' => ['cache-read-only' => true]]);
$repository = new ComposerRepository($repoConfig, new NullIO, $config, $httpDownloader, $eventDispatcher);
2016-06-21 14:38:52 +00:00
self::assertSame(
2022-08-17 12:20:07 +00:00
[['name' => 'foo', 'description' => null]],
2016-06-21 14:38:52 +00:00
$repository->search('foo', RepositoryInterface::SEARCH_FULLTEXT, 'composer-plugin')
);
self::assertEmpty(
2016-06-21 14:38:52 +00:00
$repository->search('foo', RepositoryInterface::SEARCH_FULLTEXT, 'library')
);
}
public function testSearchWithSpecialChars(): void
{
2022-08-17 12:20:07 +00:00
$repoConfig = [
'url' => 'http://example.org',
2022-08-17 12:20:07 +00:00
];
2022-02-04 15:50:01 +00:00
$httpDownloader = $this->getHttpDownloaderMock();
$httpDownloader->expects(
[
2022-08-17 12:20:07 +00:00
['url' => 'http://example.org/packages.json', 'body' => JsonFile::encode(['search' => '/search.json?q=%query%&type=%type%'])],
['url' => 'http://example.org/search.json?q=foo+bar&type=', 'body' => JsonFile::encode([])],
2022-02-04 15:50:01 +00:00
],
true
);
$eventDispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
->disableOriginalConstructor()
->getMock();
2022-02-04 15:50:01 +00:00
$config = FactoryMock::createConfig();
$config->merge(['config' => ['cache-read-only' => true]]);
$repository = new ComposerRepository($repoConfig, new NullIO, $config, $httpDownloader, $eventDispatcher);
self::assertEmpty(
$repository->search('foo bar', RepositoryInterface::SEARCH_FULLTEXT)
);
}
public function testSearchWithAbandonedPackages(): void
{
2022-08-17 12:20:07 +00:00
$repoConfig = [
'url' => 'http://example.org',
2022-08-17 12:20:07 +00:00
];
2022-08-17 12:20:07 +00:00
$result = [
'results' => [
[
'name' => 'foo1',
'description' => null,
'abandoned' => true,
2022-08-17 12:20:07 +00:00
],
[
'name' => 'foo2',
'description' => null,
'abandoned' => 'bar',
2022-08-17 12:20:07 +00:00
],
],
];
$httpDownloader = $this->getHttpDownloaderMock();
$httpDownloader->expects(
[
2022-08-17 12:20:07 +00:00
['url' => 'http://example.org/packages.json', 'body' => JsonFile::encode(['search' => '/search.json?q=%query%'])],
['url' => 'http://example.org/search.json?q=foo', 'body' => JsonFile::encode($result)],
],
true
);
$eventDispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
->disableOriginalConstructor()
->getMock();
$config = FactoryMock::createConfig();
$config->merge(['config' => ['cache-read-only' => true]]);
$repository = new ComposerRepository($repoConfig, new NullIO, $config, $httpDownloader, $eventDispatcher);
self::assertSame(
2022-08-17 12:20:07 +00:00
[
['name' => 'foo1', 'description' => null, 'abandoned' => true],
['name' => 'foo2', 'description' => null, 'abandoned' => 'bar'],
],
$repository->search('foo')
);
}
/**
* @dataProvider provideCanonicalizeUrlTestCases
* @param non-empty-string $url
* @param non-empty-string $repositoryUrl
*/
2022-02-22 15:47:09 +00:00
public function testCanonicalizeUrl(string $expected, string $url, string $repositoryUrl): void
{
$repository = new ComposerRepository(
2022-08-17 12:20:07 +00:00
['url' => $repositoryUrl],
new NullIO(),
2019-07-12 16:34:12 +00:00
FactoryMock::createConfig(),
$this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock(),
$this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock()
);
$object = new \ReflectionObject($repository);
$method = $object->getMethod('canonicalizeUrl');
$method->setAccessible(true);
// ComposerRepository::__construct ensures that the repository URL has a
// protocol, so reset it here in order to test all cases.
$property = $object->getProperty('url');
$property->setAccessible(true);
$property->setValue($repository, $repositoryUrl);
self::assertSame($expected, $method->invoke($repository, $url));
}
public static function provideCanonicalizeUrlTestCases(): array
{
2022-08-17 12:20:07 +00:00
return [
[
'https://example.org/path/to/file',
'/path/to/file',
'https://example.org',
2022-08-17 12:20:07 +00:00
],
[
'https://example.org/canonic_url',
'https://example.org/canonic_url',
'https://should-not-see-me.test',
2022-08-17 12:20:07 +00:00
],
[
'file:///path/to/repository/file',
'/path/to/repository/file',
'file:///path/to/repository',
2022-08-17 12:20:07 +00:00
],
[
// Assert that the repository URL is returned unchanged if it is
// not a URL.
// (Backward compatibility test)
'invalid_repo_url',
'/path/to/file',
'invalid_repo_url',
2022-08-17 12:20:07 +00:00
],
[
// Assert that URLs can contain sequences resembling pattern
// references as understood by preg_replace() without messing up
// the result.
// (Regression test)
'https://example.org/path/to/unusual_$0_filename',
'/path/to/unusual_$0_filename',
'https://example.org',
2022-08-17 12:20:07 +00:00
],
];
}
public function testGetProviderNamesWillReturnPartialPackageNames(): void
{
$httpDownloader = $this->getHttpDownloaderMock();
$httpDownloader->expects(
[
[
'url' => 'http://example.org/packages.json',
2022-08-17 12:20:07 +00:00
'body' => JsonFile::encode([
'providers-lazy-url' => '/foo/p/%package%.json',
2022-08-17 12:20:07 +00:00
'packages' => ['foo/bar' => [
'dev-branch' => ['name' => 'foo/bar'],
'v1.0.0' => ['name' => 'foo/bar'],
]],
]),
],
],
true
);
$repository = new ComposerRepository(
2022-08-17 12:20:07 +00:00
['url' => 'http://example.org/packages.json'],
new NullIO(),
FactoryMock::createConfig(),
2020-01-14 15:20:31 +00:00
$httpDownloader
);
self::assertEquals(['foo/bar'], $repository->getPackageNames());
}
public function testGetSecurityAdvisoriesAssertRepositoryHttpOptionsAreUsed(): void
{
$httpDownloader = $this->getHttpDownloaderMock();
$httpDownloader->expects(
[
[
'url' => 'https://example.org/packages.json',
'body' => JsonFile::encode([
'packages' => ['foo/bar' => [
'dev-branch' => ['name' => 'foo/bar'],
'v1.0.0' => ['name' => 'foo/bar'],
]],
'metadata-url' => 'https://example.org/p2/%package%.json',
'security-advisories' => [
'api-url' => 'https://example.org/security-advisories',
],
]),
'options' => ['http' => ['verify_peer' => false]],
],
[
'url' => 'https://example.org/security-advisories',
'body' => JsonFile::encode(['advisories' => []]),
'options' => ['http' => [
'verify_peer' => false,
'method' => 'POST',
'header' => [
'Content-type: application/x-www-form-urlencoded',
],
'timeout' => 10,
'content' => http_build_query(['packages' => ['foo/bar']]),
]],
]
],
true
);
$repository = new ComposerRepository(
['url' => 'https://example.org/packages.json', 'options' => ['http' => ['verify_peer' => false]]],
new NullIO(),
FactoryMock::createConfig(),
$httpDownloader
);
self::assertSame([
'namesFound' => [],
'advisories' => [],
], $repository->getSecurityAdvisories(['foo/bar' => new Constraint('=', '1.0.0.0')]));
}
}