1
0
mirror of https://github.com/php/pie.git synced 2026-03-23 23:12:17 +01:00
Files
archived-pie/test/integration/Command/InstallExtensionsForProjectCommandTest.php

219 lines
9.7 KiB
PHP

<?php
declare(strict_types=1);
namespace Php\PieIntegrationTest\Command;
use Composer\Composer;
use Composer\IO\IOInterface;
use Composer\Package\Link;
use Composer\Package\RootPackage;
use Composer\Repository\InstalledArrayRepository;
use Composer\Repository\RepositoryManager;
use Composer\Semver\Constraint\Constraint;
use Composer\Semver\Constraint\MultiConstraint;
use Php\Pie\Command\InstallExtensionsForProjectCommand;
use Php\Pie\ComposerIntegration\MinimalHelperSet;
use Php\Pie\ComposerIntegration\PieJsonEditor;
use Php\Pie\ComposerIntegration\QuieterConsoleIO;
use Php\Pie\Container;
use Php\Pie\ExtensionType;
use Php\Pie\Installing\InstallForPhpProject\ComposerFactoryForProject;
use Php\Pie\Installing\InstallForPhpProject\DetermineExtensionsRequired;
use Php\Pie\Installing\InstallForPhpProject\FindMatchingPackages;
use Php\Pie\Installing\InstallForPhpProject\InstallPiePackageFromPath;
use Php\Pie\Installing\InstallForPhpProject\InstallSelectedPackage;
use Php\Pie\Platform\InstalledPiePackages;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\BufferedOutput;
use function getcwd;
#[CoversClass(InstallExtensionsForProjectCommand::class)]
final class InstallExtensionsForProjectCommandTest extends TestCase
{
private CommandTester $commandTester;
private ComposerFactoryForProject&MockObject $composerFactoryForProject;
private FindMatchingPackages&MockObject $findMatchingPackages;
private InstalledPiePackages&MockObject $installedPiePackages;
private InstallSelectedPackage&MockObject $installSelectedPackage;
private QuestionHelper&MockObject $questionHelper;
private InstallPiePackageFromPath&MockObject $installPiePackage;
public function setUp(): void
{
parent::setUp();
$container = $this->createMock(ContainerInterface::class);
$container->method('get')->willReturnCallback(
/** @param class-string $service */
function (string $service): mixed {
switch ($service) {
case QuieterConsoleIO::class:
return new QuieterConsoleIO(
new ArrayInput([]),
new BufferedOutput(),
new MinimalHelperSet(['question' => new QuestionHelper()]),
);
default:
return $this->createMock($service);
}
},
);
$this->composerFactoryForProject = $this->createMock(ComposerFactoryForProject::class);
$this->findMatchingPackages = $this->createMock(FindMatchingPackages::class);
$this->installedPiePackages = $this->createMock(InstalledPiePackages::class);
$this->installSelectedPackage = $this->createMock(InstallSelectedPackage::class);
$this->installPiePackage = $this->createMock(InstallPiePackageFromPath::class);
$this->questionHelper = $this->createMock(QuestionHelper::class);
Container::testFactory();
$cmd = new InstallExtensionsForProjectCommand(
$this->composerFactoryForProject,
new DetermineExtensionsRequired(),
$this->installedPiePackages,
$this->findMatchingPackages,
$this->installSelectedPackage,
$this->installPiePackage,
$container,
Container::testBuffer(),
);
$cmd->setHelperSet(new HelperSet([
'question' => $this->questionHelper,
]));
$this->commandTester = new CommandTester($cmd);
}
public function testInstallingExtensionsForPhpProject(): void
{
$rootPackage = new RootPackage('my/project', '1.2.3.0', '1.2.3');
$rootPackage->setRequires([
'ext-standard' => new Link('my/project', 'ext-standard', new Constraint('=', '*'), Link::TYPE_REQUIRE, '*'),
'ext-spl' => new Link('my/project', 'ext-spl', new Constraint('=', '*'), Link::TYPE_REQUIRE, '*'),
'ext-foobar' => new Link('my/project', 'ext-foobar', new MultiConstraint([
new Constraint('>=', '1.2.0.0-dev'),
new Constraint('<', '2.0.0.0-dev'),
]), Link::TYPE_REQUIRE, '^1.2'),
]);
$this->composerFactoryForProject->method('rootPackage')->willReturn($rootPackage);
$installedRepository = new InstalledArrayRepository([$rootPackage]);
$repositoryManager = $this->createMock(RepositoryManager::class);
$repositoryManager->method('getLocalRepository')->willReturn($installedRepository);
$composer = $this->createMock(Composer::class);
$composer->method('getPackage')->willReturn($rootPackage);
$composer->method('getRepositoryManager')->willReturn($repositoryManager);
$this->composerFactoryForProject->method('composer')->willReturn($composer);
$this->findMatchingPackages->method('for')->willReturn([
['name' => 'vendor1/foobar', 'description' => 'The official foobar implementation'],
]);
$this->questionHelper->method('ask')->willReturn('vendor1/foobar: The official foobar implementation');
$this->installSelectedPackage->expects(self::once())
->method('withPieCli')
->with('vendor1/foobar:^1.2');
$this->commandTester->execute(
['--allow-non-interactive-project-install' => true],
['verbosity' => BufferedOutput::VERBOSITY_VERY_VERBOSE],
);
$outputString = $this->commandTester->getDisplay();
$this->commandTester->assertCommandIsSuccessful($outputString);
self::assertStringContainsString('Checking extensions for your project my/project', $outputString);
self::assertStringContainsString('requires: ext-standard:* ✅ Already installed', $outputString);
self::assertStringContainsString('requires: ext-spl:* ✅ Already installed', $outputString);
self::assertStringContainsString('requires: ext-foobar:^1.2 🚫 Missing', $outputString);
}
public function testInstallingExtensionsForPhpProjectWithMultipleMatches(): void
{
$rootPackage = new RootPackage('my/project', '1.2.3.0', '1.2.3');
$rootPackage->setRequires([
'ext-standard' => new Link('my/project', 'ext-standard', new Constraint('=', '*'), Link::TYPE_REQUIRE, '*'),
'ext-foobar' => new Link('my/project', 'ext-foobar', new MultiConstraint([
new Constraint('>=', '1.2.0.0-dev'),
new Constraint('<', '2.0.0.0-dev'),
]), Link::TYPE_REQUIRE, '^1.2'),
]);
$this->composerFactoryForProject->method('rootPackage')->willReturn($rootPackage);
$installedRepository = new InstalledArrayRepository([$rootPackage]);
$repositoryManager = $this->createMock(RepositoryManager::class);
$repositoryManager->method('getLocalRepository')->willReturn($installedRepository);
$composer = $this->createMock(Composer::class);
$composer->method('getPackage')->willReturn($rootPackage);
$composer->method('getRepositoryManager')->willReturn($repositoryManager);
$this->composerFactoryForProject->method('composer')->willReturn($composer);
$this->findMatchingPackages->method('for')->willReturn([
['name' => 'vendor1/foobar', 'description' => 'The official foobar implementation'],
['name' => 'vendor2/afoobar', 'description' => 'An improved async foobar extension'],
]);
$this->questionHelper->method('ask')->willReturn('vendor1/foobar: The official foobar implementation');
$this->installSelectedPackage->expects(self::never())
->method('withPieCli');
$this->commandTester->execute(
['--allow-non-interactive-project-install' => true],
['verbosity' => BufferedOutput::VERBOSITY_VERY_VERBOSE],
);
$outputString = $this->commandTester->getDisplay();
self::assertSame(Command::FAILURE, $this->commandTester->getStatusCode());
self::assertStringContainsString('Checking extensions for your project my/project', $outputString);
self::assertStringContainsString('requires: ext-standard:* ✅ Already installed', $outputString);
self::assertStringContainsString('requires: ext-foobar:^1.2 🚫 Missing', $outputString);
self::assertStringContainsString('Multiple packages were found for ext-foobar', $outputString);
}
public function testInstallingExtensionsForPieProject(): void
{
$rootPackage = new RootPackage('my/project', '1.2.3.0', '1.2.3');
$rootPackage->setType(ExtensionType::PhpModule->value);
$this->composerFactoryForProject->method('rootPackage')->willReturn($rootPackage);
$this->installPiePackage
->expects(self::once())
->method('__invoke')
->with(
self::isInstanceOf(InstallExtensionsForProjectCommand::class),
getcwd(),
$rootPackage,
self::isInstanceOf(PieJsonEditor::class),
self::isInstanceOf(InputInterface::class),
self::isInstanceOf(IOInterface::class),
)
->willReturn(Command::SUCCESS);
$this->commandTester->execute(
[],
['verbosity' => BufferedOutput::VERBOSITY_VERY_VERBOSE],
);
$this->commandTester->assertCommandIsSuccessful();
}
}