diff --git a/composer.json b/composer.json index 25f48225..dfc7b70c 100644 --- a/composer.json +++ b/composer.json @@ -30,8 +30,6 @@ "php": "8.1.*||8.2.*||8.3.*||8.4.*", "ext-zip": "*", "composer/composer": "^2.8.2", - "guzzlehttp/guzzle": "^7.8", - "guzzlehttp/psr7": "^2.6", "illuminate/container": "^10.47", "psr/http-message": "^2.0", "symfony/console": "^6.4", diff --git a/composer.lock b/composer.lock index 363d4fa6..6f37b0e0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fc8374dc9a7a4fb1f2a230d5a2cbb622", + "content-hash": "70b72907e89a5c18540dfa56ea943fb6", "packages": [ { "name": "composer/ca-bundle", @@ -644,331 +644,6 @@ ], "time": "2024-05-06T16:37:16+00:00" }, - { - "name": "guzzlehttp/guzzle", - "version": "7.9.2", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "d281ed313b989f213357e3be1a179f02196ac99b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", - "reference": "d281ed313b989f213357e3be1a179f02196ac99b", - "shasum": "" - }, - "require": { - "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.3", - "guzzlehttp/psr7": "^2.7.0", - "php": "^7.2.5 || ^8.0", - "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" - }, - "provide": { - "psr/http-client-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "ext-curl": "*", - "guzzle/client-integration-tests": "3.0.2", - "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.39 || ^9.6.20", - "psr/log": "^1.1 || ^2.0 || ^3.0" - }, - "suggest": { - "ext-curl": "Required for CURL handler support", - "ext-intl": "Required for Internationalized Domain Name (IDN) support", - "psr/log": "Required for using the Log middleware" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "GuzzleHttp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Jeremy Lindblom", - "email": "jeremeamia@gmail.com", - "homepage": "https://github.com/jeremeamia" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle is a PHP HTTP client library", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "psr-18", - "psr-7", - "rest", - "web service" - ], - "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.9.2" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", - "type": "tidelift" - } - ], - "time": "2024-07-24T11:22:20+00:00" - }, - { - "name": "guzzlehttp/promises", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", - "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ], - "support": { - "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", - "type": "tidelift" - } - ], - "time": "2024-10-17T10:06:22+00:00" - }, - { - "name": "guzzlehttp/psr7", - "version": "2.7.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", - "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.1 || ^2.0", - "ralouphie/getallheaders": "^3.0" - }, - "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" - ], - "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.7.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", - "type": "tidelift" - } - ], - "time": "2024-07-18T11:15:46+00:00" - }, { "name": "illuminate/container", "version": "v10.48.23", @@ -1186,113 +861,6 @@ }, "time": "2021-11-05T16:47:00+00:00" }, - { - "name": "psr/http-client", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0 || ^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Client\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP clients", - "homepage": "https://github.com/php-fig/http-client", - "keywords": [ - "http", - "http-client", - "psr", - "psr-18" - ], - "support": { - "source": "https://github.com/php-fig/http-client" - }, - "time": "2023-09-23T14:17:50+00:00" - }, - { - "name": "psr/http-factory", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-factory.git", - "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", - "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", - "shasum": "" - }, - "require": { - "php": ">=7.1", - "psr/http-message": "^1.0 || ^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", - "keywords": [ - "factory", - "http", - "message", - "psr", - "psr-17", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-factory" - }, - "time": "2024-04-15T12:06:14+00:00" - }, { "name": "psr/http-message", "version": "2.0", @@ -1447,50 +1015,6 @@ }, "time": "2021-10-29T13:26:27+00:00" }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "time": "2019-03-08T08:55:37+00:00" - }, { "name": "react/promise", "version": "v3.2.0", diff --git a/src/ComposerIntegration/OverrideWindowsUrlInstallListener.php b/src/ComposerIntegration/OverrideWindowsUrlInstallListener.php index 5c3213f9..43e064a3 100644 --- a/src/ComposerIntegration/OverrideWindowsUrlInstallListener.php +++ b/src/ComposerIntegration/OverrideWindowsUrlInstallListener.php @@ -11,6 +11,7 @@ use Composer\IO\IOInterface; use Composer\Package\CompletePackageInterface; use Composer\Util\AuthHelper; +use Composer\Util\HttpDownloader; use Php\Pie\DependencyResolver\Package; use Php\Pie\Downloading\PackageReleaseAssets; use Php\Pie\Platform\OperatingSystem; @@ -65,6 +66,7 @@ public function __invoke(InstallerEvent $installerEvent): void $this->composerRequest->targetPlatform, Package::fromComposerCompletePackage($composerPackage), new AuthHelper($this->io, $this->composer->getConfig()), + new HttpDownloader($this->io, $this->composer->getConfig()), ); $this->composerRequest->pieOutput->writeln('Found prebuilt archive: ' . $url); diff --git a/src/Container.php b/src/Container.php index 3254a14c..263fd418 100644 --- a/src/Container.php +++ b/src/Container.php @@ -5,9 +5,6 @@ namespace Php\Pie; use Composer\Util\Platform; -use GuzzleHttp\Client; -use GuzzleHttp\ClientInterface; -use GuzzleHttp\RequestOptions; use Illuminate\Container\Container as IlluminateContainer; use Php\Pie\Building\Build; use Php\Pie\Building\UnixBuild; @@ -58,12 +55,6 @@ public static function factory(): ContainerInterface $container->alias(ResolveDependencyWithComposer::class, DependencyResolver::class); - $container->bind( - ClientInterface::class, - static function (): ClientInterface { - return new Client([RequestOptions::HTTP_ERRORS => false]); - }, - ); $container->alias(GithubPackageReleaseAssets::class, PackageReleaseAssets::class); $container->when(GithubPackageReleaseAssets::class) ->needs('$githubApiBaseUrl') diff --git a/src/Downloading/AddAuthenticationHeader.php b/src/Downloading/AddAuthenticationHeader.php deleted file mode 100644 index ffd85f0e..00000000 --- a/src/Downloading/AddAuthenticationHeader.php +++ /dev/null @@ -1,43 +0,0 @@ -downloadUrl === null) { - throw new RuntimeException(sprintf('The package %s does not have a download URL', $package->name)); - } - - $authHeaders = $authHelper->addAuthenticationHeader([], 'github.com', $package->downloadUrl); - array_walk( - $authHeaders, - static function (string $v) use (&$request): void { - $headerParts = array_map('trim', explode(':', $v, 2)); - - if (count($headerParts) !== 2 || ! $headerParts[0] || ! $headerParts[1]) { - throw new RuntimeException('Authorization header is malformed, it should contain a non-empty key and a non-empty value.'); - } - - $request = $request->withHeader($headerParts[0], $headerParts[1]); - }, - ); - - return $request; - } -} diff --git a/src/Downloading/AssertHttp.php b/src/Downloading/AssertHttp.php deleted file mode 100644 index d4639d0b..00000000 --- a/src/Downloading/AssertHttp.php +++ /dev/null @@ -1,28 +0,0 @@ -getStatusCode(); - if ($actualStatusCode !== $expectedStatusCode) { - throw new InvalidArgumentException(sprintf( - 'Expected HTTP %d response, got %d - response: %s', - $expectedStatusCode, - $actualStatusCode, - $response->getBody()->__toString(), - )); - } - } -} diff --git a/src/Downloading/GithubPackageReleaseAssets.php b/src/Downloading/GithubPackageReleaseAssets.php index 677436d4..69f338d0 100644 --- a/src/Downloading/GithubPackageReleaseAssets.php +++ b/src/Downloading/GithubPackageReleaseAssets.php @@ -4,31 +4,23 @@ namespace Php\Pie\Downloading; +use Composer\Downloader\TransportException; use Composer\Util\AuthHelper; -use GuzzleHttp\ClientInterface; -use GuzzleHttp\Psr7\Request; -use GuzzleHttp\RequestOptions; +use Composer\Util\HttpDownloader; use Php\Pie\DependencyResolver\Package; use Php\Pie\Platform\TargetPlatform; use Php\Pie\Platform\WindowsExtensionAssetName; -use Psr\Http\Message\ResponseInterface; use Webmozart\Assert\Assert; use function array_map; -use function assert; use function in_array; -use function json_decode; use function strtolower; -use const JSON_BIGINT_AS_STRING; -use const JSON_THROW_ON_ERROR; - /** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */ final class GithubPackageReleaseAssets implements PackageReleaseAssets { /** @psalm-api */ public function __construct( - private readonly ClientInterface $client, private readonly string $githubApiBaseUrl, ) { } @@ -38,11 +30,12 @@ public function findWindowsDownloadUrlForPackage( TargetPlatform $targetPlatform, Package $package, AuthHelper $authHelper, + HttpDownloader $httpDownloader, ): string { $releaseAsset = $this->selectMatchingReleaseAsset( $targetPlatform, $package, - $this->getReleaseAssetsForPackage($package, $authHelper), + $this->getReleaseAssetsForPackage($package, $authHelper, $httpDownloader), ); return $releaseAsset['browser_download_url']; @@ -76,41 +69,33 @@ private function selectMatchingReleaseAsset(TargetPlatform $targetPlatform, Pack } /** @return list */ - private function getReleaseAssetsForPackage(Package $package, AuthHelper $authHelper): array - { - $request = AddAuthenticationHeader::withAuthHeaderFromComposer( - new Request('GET', $this->githubApiBaseUrl . '/repos/' . $package->githubOrgAndRepository() . '/releases/tags/' . $package->version), - $package, - $authHelper, - ); + private function getReleaseAssetsForPackage( + Package $package, + AuthHelper $authHelper, + HttpDownloader $httpDownloader, + ): array { + Assert::notNull($package->downloadUrl); - $response = $this->client - ->sendAsync( - $request, + try { + $decodedRepsonse = $httpDownloader->get( + $this->githubApiBaseUrl . '/repos/' . $package->githubOrgAndRepository() . '/releases/tags/' . $package->version, [ - RequestOptions::ALLOW_REDIRECTS => true, - RequestOptions::HTTP_ERRORS => false, - RequestOptions::SYNCHRONOUS => true, + 'retry-auth-failure' => false, + 'http' => [ + 'method' => 'GET', + 'header' => $authHelper->addAuthenticationHeader([], $this->githubApiBaseUrl, $package->downloadUrl), + ], ], - ) - ->wait(); - assert($response instanceof ResponseInterface); + )->decodeJson(); + } catch (TransportException $t) { + /** @link https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#get-a-release-by-tag-name */ + if ($t->getStatusCode() === 404) { + throw Exception\CouldNotFindReleaseAsset::forPackageWithMissingTag($package); + } - /** @link https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#get-a-release-by-tag-name */ - if ($response->getStatusCode() === 404) { - throw Exception\CouldNotFindReleaseAsset::forPackageWithMissingTag($package); + throw $t; } - AssertHttp::responseStatusCode(200, $response); - - /** @var mixed $decodedRepsonse */ - $decodedRepsonse = json_decode( - (string) $response->getBody(), - true, - 512, - JSON_BIGINT_AS_STRING | JSON_THROW_ON_ERROR, - ); - Assert::isArray($decodedRepsonse); Assert::keyExists($decodedRepsonse, 'assets'); Assert::isList($decodedRepsonse['assets']); diff --git a/src/Downloading/PackageReleaseAssets.php b/src/Downloading/PackageReleaseAssets.php index c1538e10..5f79cc37 100644 --- a/src/Downloading/PackageReleaseAssets.php +++ b/src/Downloading/PackageReleaseAssets.php @@ -5,6 +5,7 @@ namespace Php\Pie\Downloading; use Composer\Util\AuthHelper; +use Composer\Util\HttpDownloader; use Php\Pie\DependencyResolver\Package; use Php\Pie\Platform\TargetPlatform; @@ -16,5 +17,6 @@ public function findWindowsDownloadUrlForPackage( TargetPlatform $targetPlatform, Package $package, AuthHelper $authHelper, + HttpDownloader $httpDownloader, ): string; } diff --git a/test/integration/Downloading/GithubPackageReleaseAssetsTest.php b/test/integration/Downloading/GithubPackageReleaseAssetsTest.php new file mode 100644 index 00000000..ae0d56c0 --- /dev/null +++ b/test/integration/Downloading/GithubPackageReleaseAssetsTest.php @@ -0,0 +1,70 @@ +createMock(PhpBinaryPath::class); + $phpBinaryPath->expects(self::any()) + ->method('majorMinorVersion') + ->willReturn('8.3'); + + $targetPlatform = new TargetPlatform( + OperatingSystem::Windows, + $phpBinaryPath, + Architecture::x86_64, + ThreadSafetyMode::ThreadSafe, + 1, + WindowsCompiler::VS16, + ); + + $package = new Package( + $this->createMock(CompletePackage::class), + ExtensionType::PhpModule, + ExtensionName::normaliseFromString('example_pie_extension'), + 'asgrim/example-pie-extension', + '2.0.2', + 'https://api.github.com/repos/asgrim/example-pie-extension/zipball/f9ed13ea95dada34c6cc5a052da258dbda059d27', + [], + true, + true, + ); + + $io = $this->createMock(IOInterface::class); + $config = new Config(); + + self::assertSame( + 'https://github.com/asgrim/example-pie-extension/releases/download/2.0.2/php_example_pie_extension-2.0.2-8.3-ts-vs16-x86_64.zip', + (new GithubPackageReleaseAssets('https://api.github.com')) + ->findWindowsDownloadUrlForPackage( + $targetPlatform, + $package, + new AuthHelper($io, $config), + new HttpDownloader($io, $config), + ), + ); + } +} diff --git a/test/unit/Downloading/AddAuthenticationHeaderTest.php b/test/unit/Downloading/AddAuthenticationHeaderTest.php deleted file mode 100644 index ae23589c..00000000 --- a/test/unit/Downloading/AddAuthenticationHeaderTest.php +++ /dev/null @@ -1,111 +0,0 @@ -createMock(AuthHelper::class); - $authHelper->expects(self::once()) - ->method('addAuthenticationHeader') - ->with([], 'github.com', $downloadUrl) - ->willReturn(['Authorization: whatever ABC123']); - - $requestWithAuthHeader = (new AddAuthenticationHeader())->withAuthHeaderFromComposer( - $request, - $this->createDummyPackage($downloadUrl), - $authHelper, - ); - - self::assertSame('whatever ABC123', $requestWithAuthHeader->getHeaderLine('Authorization')); - } - - #[DataProvider('provideInvalidAuthorizationHeaders')] - public function testEmptyValueInAuthorizationHeaderThrowsException(string $rawHeader): void - { - $downloadUrl = 'http://test-uri/' . uniqid('path', true); - - $request = new Request('GET', $downloadUrl); - - $authHelper = $this->createMock(AuthHelper::class); - $authHelper->expects(self::once()) - ->method('addAuthenticationHeader') - ->with([], 'github.com', $downloadUrl) - ->willReturn([$rawHeader]); - - $addAuthenticationHeader = new AddAuthenticationHeader(); - - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Authorization header is malformed, it should contain a non-empty key and a non-empty value.'); - $addAuthenticationHeader->withAuthHeaderFromComposer($request, $this->createDummyPackage($downloadUrl), $authHelper); - } - - /** - * @return Generator - * - * @psalm-suppress PossiblyUnusedMethod https://github.com/psalm/psalm-plugin-phpunit/issues/131 - */ - public static function provideInvalidAuthorizationHeaders(): Generator - { - yield ['Authorization:']; - yield [': Bearer']; - yield [' : Bearer']; - yield ['Authorization: ']; - yield [':']; - yield ['Authorization MyToken']; - } - - public function testExceptionIsThrownWhenPackageDoesNotHaveDownloadUrl(): void - { - $downloadUrl = 'http://test-uri/' . uniqid('path', true); - - $request = new Request('GET', $downloadUrl); - - $authHelper = $this->createMock(AuthHelper::class); - - $addAuthenticationHeader = new AddAuthenticationHeader(); - $package = $this->createDummyPackage(); - - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The package foo/bar does not have a download URL'); - $addAuthenticationHeader->withAuthHeaderFromComposer($request, $package, $authHelper); - } - - private function createDummyPackage(string|null $downloadUrl = null): Package - { - return new Package( - $this->createMock(CompletePackage::class), - ExtensionType::PhpModule, - ExtensionName::normaliseFromString('foo'), - 'foo/bar', - '1.2.3', - $downloadUrl, - [], - true, - true, - ); - } -} diff --git a/test/unit/Downloading/AssertHttpTest.php b/test/unit/Downloading/AssertHttpTest.php deleted file mode 100644 index 7568b511..00000000 --- a/test/unit/Downloading/AssertHttpTest.php +++ /dev/null @@ -1,24 +0,0 @@ -expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Expected HTTP 201 response, got 404 - response: some body content'); - AssertHttp::responseStatusCode(201, $response); - } -} diff --git a/test/unit/Downloading/GithubPackageReleaseAssetsTest.php b/test/unit/Downloading/GithubPackageReleaseAssetsTest.php index 08ca52b4..3be25fe2 100644 --- a/test/unit/Downloading/GithubPackageReleaseAssetsTest.php +++ b/test/unit/Downloading/GithubPackageReleaseAssetsTest.php @@ -4,12 +4,11 @@ namespace Php\PieUnitTest\Downloading; +use Composer\Downloader\TransportException; use Composer\Package\CompletePackage; use Composer\Util\AuthHelper; -use GuzzleHttp\Client; -use GuzzleHttp\Handler\MockHandler; -use GuzzleHttp\HandlerStack; -use GuzzleHttp\Psr7\Response; +use Composer\Util\Http\Response; +use Composer\Util\HttpDownloader; use Php\Pie\DependencyResolver\Package; use Php\Pie\Downloading\Exception\CouldNotFindReleaseAsset; use Php\Pie\Downloading\GithubPackageReleaseAssets; @@ -24,7 +23,6 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use function json_encode; use function uniqid; #[CoversClass(GithubPackageReleaseAssets::class)] @@ -48,26 +46,28 @@ public function testUrlIsReturnedWhenFindingWindowsDownloadUrl(): void $authHelper = $this->createMock(AuthHelper::class); - $mockHandler = new MockHandler([ - new Response( - 200, - [], - json_encode([ - 'assets' => [ - [ - 'name' => 'php_foo-1.2.3-8.3-vc14-nts-x86.zip', - 'browser_download_url' => 'wrong_download_url', - ], - [ - 'name' => 'php_foo-1.2.3-8.3-vc14-ts-x86.zip', - 'browser_download_url' => 'actual_download_url', - ], + $httpDownloaderResponse = $this->createMock(Response::class); + $httpDownloaderResponse + ->expects(self::once()) + ->method('decodeJson') + ->willReturn([ + 'assets' => [ + [ + 'name' => 'php_foo-1.2.3-8.3-vc14-nts-x86.zip', + 'browser_download_url' => 'wrong_download_url', ], - ]), - ), - ]); + [ + 'name' => 'php_foo-1.2.3-8.3-vc14-ts-x86.zip', + 'browser_download_url' => 'actual_download_url', + ], + ], + ]); - $guzzleMockClient = new Client(['handler' => HandlerStack::create($mockHandler)]); + $httpDownloader = $this->createMock(HttpDownloader::class); + $httpDownloader + ->expects(self::once()) + ->method('get') + ->willReturn($httpDownloaderResponse); $package = new Package( $this->createMock(CompletePackage::class), @@ -81,9 +81,9 @@ public function testUrlIsReturnedWhenFindingWindowsDownloadUrl(): void true, ); - $releaseAssets = new GithubPackageReleaseAssets($guzzleMockClient, 'https://test-github-api-base-url.thephp.foundation'); + $releaseAssets = new GithubPackageReleaseAssets('https://test-github-api-base-url.thephp.foundation'); - self::assertSame('actual_download_url', $releaseAssets->findWindowsDownloadUrlForPackage($targetPlatform, $package, $authHelper)); + self::assertSame('actual_download_url', $releaseAssets->findWindowsDownloadUrlForPackage($targetPlatform, $package, $authHelper, $httpDownloader)); } public function testUrlIsReturnedWhenFindingWindowsDownloadUrlWithCompilerAndThreadSafetySwapped(): void @@ -104,26 +104,28 @@ public function testUrlIsReturnedWhenFindingWindowsDownloadUrlWithCompilerAndThr $authHelper = $this->createMock(AuthHelper::class); - $mockHandler = new MockHandler([ - new Response( - 200, - [], - json_encode([ - 'assets' => [ - [ - 'name' => 'php_foo-1.2.3-8.3-nts-vc14-x86.zip', - 'browser_download_url' => 'wrong_download_url', - ], - [ - 'name' => 'php_foo-1.2.3-8.3-ts-vc14-x86.zip', - 'browser_download_url' => 'actual_download_url', - ], + $httpDownloaderResponse = $this->createMock(Response::class); + $httpDownloaderResponse + ->expects(self::once()) + ->method('decodeJson') + ->willReturn([ + 'assets' => [ + [ + 'name' => 'php_foo-1.2.3-8.3-nts-vc14-x86.zip', + 'browser_download_url' => 'wrong_download_url', + ], + [ + 'name' => 'php_foo-1.2.3-8.3-ts-vc14-x86.zip', + 'browser_download_url' => 'actual_download_url', ], - ]), - ), - ]); + ], + ]); - $guzzleMockClient = new Client(['handler' => HandlerStack::create($mockHandler)]); + $httpDownloader = $this->createMock(HttpDownloader::class); + $httpDownloader + ->expects(self::once()) + ->method('get') + ->willReturn($httpDownloaderResponse); $package = new Package( $this->createMock(CompletePackage::class), @@ -137,9 +139,9 @@ public function testUrlIsReturnedWhenFindingWindowsDownloadUrlWithCompilerAndThr true, ); - $releaseAssets = new GithubPackageReleaseAssets($guzzleMockClient, 'https://test-github-api-base-url.thephp.foundation'); + $releaseAssets = new GithubPackageReleaseAssets('https://test-github-api-base-url.thephp.foundation'); - self::assertSame('actual_download_url', $releaseAssets->findWindowsDownloadUrlForPackage($targetPlatform, $package, $authHelper)); + self::assertSame('actual_download_url', $releaseAssets->findWindowsDownloadUrlForPackage($targetPlatform, $package, $authHelper, $httpDownloader)); } public function testFindWindowsDownloadUrlForPackageThrowsExceptionWhenAssetNotFound(): void @@ -155,17 +157,14 @@ public function testFindWindowsDownloadUrlForPackageThrowsExceptionWhenAssetNotF $authHelper = $this->createMock(AuthHelper::class); - $mockHandler = new MockHandler([ - new Response( - 200, - [], - json_encode([ - 'assets' => [], - ]), - ), - ]); + $e = new TransportException('not found', 404); + $e->setStatusCode(404); - $guzzleMockClient = new Client(['handler' => HandlerStack::create($mockHandler)]); + $httpDownloader = $this->createMock(HttpDownloader::class); + $httpDownloader + ->expects(self::once()) + ->method('get') + ->willThrowException($e); $package = new Package( $this->createMock(CompletePackage::class), @@ -179,9 +178,9 @@ public function testFindWindowsDownloadUrlForPackageThrowsExceptionWhenAssetNotF true, ); - $releaseAssets = new GithubPackageReleaseAssets($guzzleMockClient, 'https://test-github-api-base-url.thephp.foundation'); + $releaseAssets = new GithubPackageReleaseAssets('https://test-github-api-base-url.thephp.foundation'); $this->expectException(CouldNotFindReleaseAsset::class); - $releaseAssets->findWindowsDownloadUrlForPackage($targetPlatform, $package, $authHelper); + $releaseAssets->findWindowsDownloadUrlForPackage($targetPlatform, $package, $authHelper, $httpDownloader); } }