Commit 5771e644 authored by Ted Bowman's avatar Ted Bowman Committed by Tim Plunkett
Browse files

Issue #3306722 by tedbow, omkar.podey: Update Installer service to work...

Issue #3306722 by tedbow, omkar.podey: Update Installer service to work without requiring to specify the package version
parent 63f5d824
Loading
Loading
Loading
Loading
+0 −64
Original line number Diff line number Diff line
@@ -18,70 +18,6 @@ use Drupal\project_browser\Exception\InstallException;
 */
final class Installer extends Stage {

  /**
   * Begins the install operation.
   *
   * @param string[] $package_versions
   *   The versions of the package versions to install, keyed by package name.
   * @param int|null $timeout
   *   (optional) How long to allow the file copying operation to run before
   *   timing out, in seconds, or NULL to never time out. Defaults to 300
   *   seconds.
   *
   * @return string
   *   The unique ID of the stage.
   *
   * @throws \InvalidArgumentException
   *   Thrown if no package versions are provided.
   */
  public function begin(array $package_versions, ?int $timeout = 300): string {
    if (empty($package_versions)) {
      throw new \InvalidArgumentException("No packages to begin the install");
    }

    // Ensure that package versions are available to pre-create event
    // subscribers. We can't use ::setMetadata() here because it requires the
    // stage to be claimed, but that only happens during ::create().
    $this->tempStore->set(static::TEMPSTORE_METADATA_KEY, [
      'packages' => [
        'production' => $package_versions,
        'dev' => [],
      ],
    ]);
    return $this->create($timeout);
  }

  /**
   * Returns the package versions that will be required during the install.
   *
   * @return string[][]
   *   An array with two sub-arrays: 'production' and 'dev'. Each is a set of
   *   package versions, where the keys are package names and the values are
   *   version constraints understood by Composer.
   */
  public function getPackageVersions(): array {
    return $this->getMetadata('packages');
  }

  /**
   * Stages the install.
   */
  public function stage(): void {
    $this->checkOwnership();

    // Convert an associative array of package versions, keyed by name, to
    // command-line arguments in the form `vendor/name:version`.
    $map = function (array $versions): array {
      $requirements = [];
      foreach ($versions as $package => $version) {
        $requirements[] = "$package:$version";
      }
      return $requirements;
    };
    $versions = array_map($map, $this->getPackageVersions());
    $this->require($versions['production']);
  }

  /**
   * {@inheritdoc}
   */
+6 −83
Original line number Diff line number Diff line
@@ -45,81 +45,6 @@ class InstallerTest extends PackageManagerKernelTestBase {

  }

  /**
   * Tests that correct versions are staged after calling ::begin().
   */
  public function testCorrectVersionsStaged(): void {

    // Create a user who will own the stage even after the container is rebuilt.
    $user = $this->createUser([], NULL, TRUE, ['uid' => 2]);
    $this->setCurrentUser($user);

    $id = $this->container->get('project_browser.installer')->begin([
      'drupal/my_module' => '9.8.1',
      'other/my_package' => '1.2.3',
    ]);
    // Rebuild the container to ensure the package versions are persisted.
    /** @var \Drupal\Core\DrupalKernel $kernel */
    $kernel = $this->container->get('kernel');
    $kernel->rebuildContainer();
    $this->container = $kernel->getContainer();
    $this->setCurrentUser($user);

    $installer = $this->container->get('project_browser.installer');

    // Ensure that the target package versions are what we expect.
    $expected_versions = [
      'production' => [
        'drupal/my_module' => '9.8.1',
        'other/my_package' => '1.2.3',
      ],
      'dev' => [],
    ];
    $this->assertSame($expected_versions, $installer->claim($id)->getPackageVersions());

    // When we call Installer::stage(), the stored project versions
    // should be read from state and passed to Composer Stager's Stager service,
    // in the form of a Composer command. This is done using
    // package_manager_bypass's invocation recorder, rather than a regular mock,
    // in order to test that the invocation recorder itself works. The
    // production requirements are changed first, followed by the dev
    // requirements. Then the installed packages are updated. This is tested
    // functionally in Package Manager.
    // @see \Drupal\Tests\package_manager\Build\StagedUpdateTest
    $expected_arguments = [
      [
        'require',
        '--no-update',
        'drupal/my_module:9.8.1',
        'other/my_package:1.2.3',
      ],
      [
        'update',
        '--with-all-dependencies',
        'drupal/my_module:9.8.1',
        'other/my_package:1.2.3',
      ],
    ];
    $installer->stage();

    $actual_arguments = $this->container->get('package_manager.stager')
      ->getInvocationArguments();

    $this->assertSame(count($expected_arguments), count($actual_arguments));
    foreach ($actual_arguments as $i => [$arguments]) {
      $this->assertSame($expected_arguments[$i], $arguments);
    }
  }

  /**
   * Tests that an exception is thrown when calling begin() with no projects.
   */
  public function testNoProjectsInBegin(): void {
    $this->expectException('InvalidArgumentException');
    $this->expectExceptionMessage('No packages to begin the install');
    $this->container->get('project_browser.installer')->begin([]);
  }

  /**
   * Data provider for testCommitException().
   *
@@ -154,11 +79,10 @@ class InstallerTest extends PackageManagerKernelTestBase {
   * @dataProvider providerCommitException
   */
  public function testCommitException(string $thrown_class, string $expected_class = NULL): void {
    /** @var \Drupal\project_browser\ComposerInstaller\Installer $installer */
    $installer = $this->container->get('project_browser.installer');
    $installer->begin([
      'drupal/my_module' => '9.8.1',
    ]);
    $installer->stage();
    $installer->create();
    $installer->require(['org/package-name']);
    $thrown_message = 'A very bad thing happened';
    Committer::setException(new $thrown_class($thrown_message, 123));
    $this->expectException($expected_class);
@@ -176,11 +100,10 @@ class InstallerTest extends PackageManagerKernelTestBase {
   * @covers ::dispatch
   */
  public function testInstallException() {
    /** @var \Drupal\project_browser\ComposerInstaller\Installer $installer */
    $installer = $this->container->get('project_browser.installer');
    $installer->begin([
      'drupal/my_module' => '9.8.1',
    ]);
    $installer->stage();
    $installer->create();
    $installer->require(['org/package-name']);
    $results = [
      ValidationResult::createError([t('These are not the projects you are looking for.')]),
    ];