Skip to content

Commit

Permalink
Merge pull request #40 from pact-foundation/file-installer
Browse files Browse the repository at this point in the history
refactor(2.0): Add FileInstaller
  • Loading branch information
tienvx authored Apr 19, 2024
2 parents d0b7d6b + f766715 commit ea4f80f
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 11 deletions.
58 changes: 58 additions & 0 deletions src/Composer/Installer/FileInstaller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace LastCall\DownloadsPlugin\Composer\Installer;

use Composer\Composer;
use Composer\IO\IOInterface;
use Composer\Package\PackageInterface;
use Composer\Repository\InstalledRepositoryInterface;
use Composer\Util\Filesystem;
use LastCall\DownloadsPlugin\Composer\Package\ExtraDownloadInterface;
use LastCall\DownloadsPlugin\Enum\PackageType;
use LastCall\DownloadsPlugin\Installer\ExecutableInstallerInterface;
use React\Promise\PromiseInterface;
use Symfony\Component\Finder\Finder;

class FileInstaller extends AbstractInstaller
{
public const TMP_PREFIX = '.composer-extra-tmp-';

protected Filesystem $filesystem;

public function __construct(
IOInterface $io,
Composer $composer,
?Filesystem $filesystem = null,
?ExecutableInstallerInterface $executableInstaller = null,
) {
parent::__construct($io, $composer, $executableInstaller);
$this->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);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
22 changes: 12 additions & 10 deletions tests/Unit/Composer/Installer/ArchiveInstallerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -30,24 +39,17 @@ public function testInstallExtraDownload(): void
});
}

public function getInstallExtraArchiveTests(): array
{
return [
[true],
[false],
];
}

/**
* @dataProvider getInstallExtraArchiveTests
* @testWith [true]
* [false]
*/
public function testInstallExtraArchive(bool $hasPackage): void
{
$this->composer
->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')
Expand Down
97 changes: 97 additions & 0 deletions tests/Unit/Composer/Installer/FileInstallerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

namespace LastCall\DownloadsPlugin\Tests\Unit\Composer\Installer;

use Composer\Installer\InstallerInterface;
use Composer\Util\Filesystem;
use LastCall\DownloadsPlugin\Composer\Installer\FileInstaller;
use LastCall\DownloadsPlugin\Composer\Package\ExtraDownloadInterface;
use PHPUnit\Framework\MockObject\MockObject;
use React\Promise\Promise;

class FileInstallerTest extends AbstractInstallerTestCase
{
private Filesystem|MockObject $filesystem;
private string $tmpDir;
private string $fileName = 'file.ext';
private string $fileContents = 'file contents';

protected function setUp(): void
{
$this->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);
});
}
}

0 comments on commit ea4f80f

Please sign in to comment.