Skip to content

Commit

Permalink
Merge pull request #41 from pact-foundation/extra-downloads-plugin
Browse files Browse the repository at this point in the history
refactor(2.0): Add ExtraDownloadsPlugin
  • Loading branch information
tienvx authored Apr 24, 2024
2 parents ea4f80f + f1b66a2 commit 1074fc4
Show file tree
Hide file tree
Showing 4 changed files with 257 additions and 2 deletions.
92 changes: 92 additions & 0 deletions src/Composer/Plugin/ExtraDownloadsPlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php

namespace LastCall\DownloadsPlugin\Composer\Plugin;

use Composer\Composer;
use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\DependencyResolver\Operation\UpdateOperation;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\Installer\PackageEvent;
use Composer\Installer\PackageEvents;
use Composer\IO\IOInterface;
use Composer\Plugin\PluginInterface;
use Composer\Script\Event;
use Composer\Script\ScriptEvents;
use LastCall\DownloadsPlugin\Composer\Installer\ArchiveInstaller;
use LastCall\DownloadsPlugin\Composer\Installer\FileInstaller;
use LastCall\DownloadsPlugin\Exception\OutOfRangeException;
use LastCall\DownloadsPlugin\Handler\PackageHandler;
use LastCall\DownloadsPlugin\Handler\PackageHandlerInterface;

class ExtraDownloadsPlugin implements PluginInterface, EventSubscriberInterface
{
private const EVENT_PRIORITY = 10;

public function __construct(private ?PackageHandlerInterface $handler = null)
{
}

public static function getSubscribedEvents(): array
{
return [
PackageEvents::POST_PACKAGE_INSTALL => ['handlePackageEvent', self::EVENT_PRIORITY],
PackageEvents::POST_PACKAGE_UPDATE => ['handlePackageEvent', self::EVENT_PRIORITY],
ScriptEvents::POST_INSTALL_CMD => ['handleScriptEvent', self::EVENT_PRIORITY],
ScriptEvents::POST_UPDATE_CMD => ['handleScriptEvent', self::EVENT_PRIORITY],
];
}

public function handleScriptEvent(Event $event): void
{
$rootPackage = $event->getComposer()->getPackage();
$this->getHandler($event->getComposer(), $event->getIO())->handle($rootPackage);

// Ensure that any other packages are properly reconciled.
$localRepo = $event->getComposer()->getRepositoryManager()->getLocalRepository();
foreach ($localRepo->getCanonicalPackages() as $package) {
$this->getHandler($event->getComposer(), $event->getIO())->handle($package);
}
}

public function handlePackageEvent(PackageEvent $event): void
{
$package = match (\get_class($event->getOperation())) {
InstallOperation::class => $event->getOperation()->getPackage(),
UpdateOperation::class => $event->getOperation()->getTargetPackage(),
default => throw new OutOfRangeException(sprintf('Operation %s not supported', $event->getOperation()->getOperationType()))
};
$this->getHandler($event->getComposer(), $event->getIO())->handle($package);
}

public function activate(Composer $composer, IOInterface $io): void
{
$this->addInstallers($composer, $io);
}

public function deactivate(Composer $composer, IOInterface $io): void
{
}

public function uninstall(Composer $composer, IOInterface $io): void
{
}

private function addInstallers(Composer $composer, IOInterface $io): void
{
$installers = [
new ArchiveInstaller($io, $composer),
new FileInstaller($io, $composer),
];
$installationManager = $composer->getInstallationManager();
foreach ($installers as $installer) {
$installationManager->addInstaller($installer);
}
}

private function getHandler(Composer $composer, IOInterface $io): PackageHandlerInterface
{
$this->handler ??= new PackageHandler($composer, $io);

return $this->handler;
}
}
2 changes: 1 addition & 1 deletion src/Exception/OutOfRangeException.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

namespace LastCall\DownloadsPlugin\Exception;

class OutOfRangeException extends \OutOfRangeException
class OutOfRangeException extends BaseException
{
}
2 changes: 1 addition & 1 deletion src/Exception/UnexpectedValueException.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

namespace LastCall\DownloadsPlugin\Exception;

class UnexpectedValueException extends \UnexpectedValueException
class UnexpectedValueException extends BaseException
{
}
163 changes: 163 additions & 0 deletions tests/Unit/Composer/Plugin/ExtraDownloadsPluginTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<?php

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

use Composer\Composer;
use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\DependencyResolver\Operation\UninstallOperation;
use Composer\DependencyResolver\Operation\UpdateOperation;
use Composer\Installer\InstallationManager;
use Composer\Installer\PackageEvent;
use Composer\Installer\PackageEvents;
use Composer\IO\IOInterface;
use Composer\Package\PackageInterface;
use Composer\Package\RootPackageInterface;
use Composer\Repository\InstalledRepositoryInterface;
use Composer\Repository\RepositoryInterface;
use Composer\Repository\RepositoryManager;
use Composer\Script\Event;
use Composer\Script\ScriptEvents;
use LastCall\DownloadsPlugin\Composer\Installer\ArchiveInstaller;
use LastCall\DownloadsPlugin\Composer\Installer\FileInstaller;
use LastCall\DownloadsPlugin\Composer\Plugin\ExtraDownloadsPlugin;
use LastCall\DownloadsPlugin\Exception\OutOfRangeException;
use LastCall\DownloadsPlugin\Handler\PackageHandlerInterface;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

class ExtraDownloadsPluginTest extends TestCase
{
private PackageHandlerInterface|MockObject $handler;
private ExtraDownloadsPlugin $plugin;
private Composer|MockObject $composer;
private IOInterface|MockObject $io;
private InstallationManager|MockObject $installationManager;

protected function setUp(): void
{
$this->handler = $this->createMock(PackageHandlerInterface::class);
$this->plugin = new ExtraDownloadsPlugin($this->handler);
$this->composer = $this->createMock(Composer::class);
$this->io = $this->createMock(IOInterface::class);
$this->installationManager = $this->createMock(InstallationManager::class);
}

public function testGetSubscribedEvents(): void
{
$this->assertSame([
PackageEvents::POST_PACKAGE_INSTALL => ['handlePackageEvent', 10],
PackageEvents::POST_PACKAGE_UPDATE => ['handlePackageEvent', 10],
ScriptEvents::POST_INSTALL_CMD => ['handleScriptEvent', 10],
ScriptEvents::POST_UPDATE_CMD => ['handleScriptEvent', 10],
], ExtraDownloadsPlugin::getSubscribedEvents());
}

public function testActivate(): void
{
$this->composer
->expects($this->once())
->method('getInstallationManager')
->willReturn($this->installationManager);
$this->installationManager
->expects($this->exactly(2))
->method('addInstaller')
->withConsecutive(
[$this->isInstanceOf(ArchiveInstaller::class)],
[$this->isInstanceOf(FileInstaller::class)],
);
$this->plugin->activate($this->composer, $this->io);
}

public function testDeactivate(): void
{
$this->expectNotToPerformAssertions();
$this->plugin->deactivate($this->composer, $this->io);
}

public function testUninstall(): void
{
$this->expectNotToPerformAssertions();
$this->plugin->uninstall($this->composer, $this->io);
}

public function testHandleScriptEvent(): void
{
$rootPackage = $this->createMock(RootPackageInterface::class);
$this->composer->expects($this->once())->method('getPackage')->willReturn($rootPackage);
$repositoryManager = $this->createMock(RepositoryManager::class);
$this->composer->expects($this->once())->method('getRepositoryManager')->willReturn($repositoryManager);
$localRepository = $this->createMock(InstalledRepositoryInterface::class);
$repositoryManager->expects($this->once())->method('getLocalRepository')->willReturn($localRepository);
$packages = [
$this->createMock(PackageInterface::class),
$this->createMock(PackageInterface::class),
$this->createMock(PackageInterface::class),
];
$localRepository->expects($this->once())->method('getCanonicalPackages')->willReturn($packages);
$this->handler
->expects($this->exactly(\count($packages) + 1))
->method('handle')
->withConsecutive(
[$rootPackage],
...array_map(fn (PackageInterface $package) => [$package], $packages),
);
$event = new Event('name', $this->composer, $this->io);
$this->plugin->handleScriptEvent($event);
}

public function testHandleInstallPackageEvent(): void
{
$package = $this->createMock(PackageInterface::class);
$this->handler
->expects($this->once())
->method('handle')
->with($package);
$event = new PackageEvent(
'install',
$this->composer,
$this->io,
false,
$this->createMock(RepositoryInterface::class),
[],
new InstallOperation($package)
);
$this->plugin->handlePackageEvent($event);
}

public function testHandleUpdatePackageEvent(): void
{
$initial = $this->createMock(PackageInterface::class);
$target = $this->createMock(PackageInterface::class);
$this->handler
->expects($this->once())
->method('handle')
->with($target);
$event = new PackageEvent(
'update',
$this->composer,
$this->io,
false,
$this->createMock(RepositoryInterface::class),
[],
new UpdateOperation($initial, $target)
);
$this->plugin->handlePackageEvent($event);
}

public function testHandleUninstallPackageEvent(): void
{
$this->expectException(OutOfRangeException::class);
$this->expectExceptionMessage('Operation uninstall not supported');
$package = $this->createMock(PackageInterface::class);
$event = new PackageEvent(
'uninstall',
$this->composer,
$this->io,
false,
$this->createMock(RepositoryInterface::class),
[],
new UninstallOperation($package)
);
$this->plugin->handlePackageEvent($event);
}
}

0 comments on commit 1074fc4

Please sign in to comment.