diff --git a/src/Composer/Installer/FileInstaller.php b/src/Composer/Installer/FileInstaller.php new file mode 100644 index 0000000..630a031 --- /dev/null +++ b/src/Composer/Installer/FileInstaller.php @@ -0,0 +1,58 @@ +filesystem = $filesystem ?: new Filesystem(); + } + + public function supports(string $packageType): bool + { + return $packageType === PackageType::FILE->value; + } + + public function install(InstalledRepositoryInterface $repo, PackageInterface $package): PromiseInterface + { + if (!$package instanceof ExtraDownloadInterface) { + return \React\Promise\resolve(null); + } + + $expectedPath = $package->getInstallPath(); + $tmpDir = \dirname($expectedPath).\DIRECTORY_SEPARATOR.uniqid(self::TMP_PREFIX, true); + $promise = $this->composer->getDownloadManager()->install($package, $tmpDir); + + return $promise->then(function () use ($repo, $package, $expectedPath, $tmpDir) { + $finder = new Finder(); + foreach ($finder->files()->in($tmpDir) as $file) { + $this->filesystem->rename($file, $expectedPath); + break; + } + $this->filesystem->remove($tmpDir); + + return parent::install($repo, $package); + }); + } +} diff --git a/tests/Unit/Composer/Installer/AbstractInstallerTestCase.php b/tests/Unit/Composer/Installer/AbstractInstallerTestCase.php index f9ab7eb..0d2fbc2 100644 --- a/tests/Unit/Composer/Installer/AbstractInstallerTestCase.php +++ b/tests/Unit/Composer/Installer/AbstractInstallerTestCase.php @@ -18,7 +18,7 @@ abstract class AbstractInstallerTestCase extends TestCase { - private ?VirtualFileSystem $fs = null; + protected ?VirtualFileSystem $fs = null; protected Composer|MockObject $composer; protected IOInterface|MockObject $io; protected DownloadManager|MockObject $downloadManager; diff --git a/tests/Unit/Composer/Installer/ArchiveInstallerTest.php b/tests/Unit/Composer/Installer/ArchiveInstallerTest.php index 7172f6a..8780f71 100644 --- a/tests/Unit/Composer/Installer/ArchiveInstallerTest.php +++ b/tests/Unit/Composer/Installer/ArchiveInstallerTest.php @@ -21,6 +21,15 @@ protected function createInstaller(): InstallerInterface return new ArchiveInstaller($this->io, $this->composer, $this->executableInstaller); } + /** + * @testWith ["extra-download:file", false] + * ["extra-download:archive", true] + */ + public function testSupports(string $type, bool $supports): void + { + $this->assertSame($supports, $this->installer->supports($type)); + } + public function testInstallExtraDownload(): void { $package = $this->createMock(ExtraDownloadInterface::class); @@ -30,16 +39,9 @@ public function testInstallExtraDownload(): void }); } - public function getInstallExtraArchiveTests(): array - { - return [ - [true], - [false], - ]; - } - /** - * @dataProvider getInstallExtraArchiveTests + * @testWith [true] + * [false] */ public function testInstallExtraArchive(bool $hasPackage): void { @@ -47,7 +49,7 @@ public function testInstallExtraArchive(bool $hasPackage): void ->expects($this->once()) ->method('getDownloadManager') ->willReturn($this->downloadManager); - $downloaderPromise = new Promise(fn (callable $resolve) => $resolve(null)); + $downloaderPromise = new Promise(fn (callable $resolver) => $resolver(null)); $this->downloadManager ->expects($this->once()) ->method('install') diff --git a/tests/Unit/Composer/Installer/FileInstallerTest.php b/tests/Unit/Composer/Installer/FileInstallerTest.php new file mode 100644 index 0000000..ddfcef2 --- /dev/null +++ b/tests/Unit/Composer/Installer/FileInstallerTest.php @@ -0,0 +1,97 @@ +filesystem = $this->createMock(Filesystem::class); + parent::setUp(); + } + + protected function createInstaller(): InstallerInterface + { + return new FileInstaller($this->io, $this->composer, $this->filesystem, $this->executableInstaller); + } + + /** + * @testWith ["extra-download:file", true] + * ["extra-download:archive", false] + */ + public function testSupports(string $type, bool $supports): void + { + $this->assertSame($supports, $this->installer->supports($type)); + } + + /** + * @testWith [true] + * [false] + */ + public function testInstallExtraDownload(bool $hasPackage): void + { + $this->composer + ->expects($this->once()) + ->method('getDownloadManager') + ->willReturn($this->downloadManager); + $this->downloadManager + ->expects($this->once()) + ->method('install') + ->with($this->extraDownload, $this->callback(function (string $targetDir): bool { + $this->assertTrue(str_contains($targetDir, FileInstaller::TMP_PREFIX)); + + return true; + })) + ->willReturnCallback(function (ExtraDownloadInterface $extraDownload, string $tmpDir) { + $this->tmpDir = $tmpDir; + mkdir($this->tmpDir, 0777, true); + file_put_contents($this->tmpDir.\DIRECTORY_SEPARATOR.$this->fileName, $this->fileContents); + + $this->filesystem + ->expects($this->once()) + ->method('rename') + ->with($this->tmpDir.\DIRECTORY_SEPARATOR.$this->fileName, $this->fs->path($this->installPath)); + $this->filesystem + ->expects($this->once()) + ->method('remove') + ->with($this->tmpDir); + + $downloaderPromise = new Promise(fn (callable $resolve) => $resolve(null)); + + return $downloaderPromise; + }); + $this->extraDownload + ->expects($this->once()) + ->method('getInstallPath') + ->willReturn($this->fs->path($this->installPath)); + $this->executableInstaller + ->expects($this->once()) + ->method('install') + ->with($this->extraDownload); + $this->repository + ->expects($this->once()) + ->method('hasPackage') + ->with($this->extraDownload) + ->willReturn($hasPackage); + $this->repository + ->expects($this->exactly(!$hasPackage)) + ->method('addPackage') + ->with($this->extraDownload); + $installerPromise = $this->installer->install($this->repository, $this->extraDownload); + $installerPromise->then(function ($result) { + $this->assertNull($result); + }); + } +}