Commit badd6080 authored by Yash Rode's avatar Yash Rode Committed by Ted Bowman
Browse files

Issue #3320836 by yash.rode, TravisCarden, tedbow: Handle runtime error...

Issue #3320836 by yash.rode, TravisCarden, tedbow: Handle runtime error correctly in ComposerExecutableValidator
parent 0d6f4b70
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -67,6 +67,8 @@ services:
      - '@PhpTuf\ComposerStager\Domain\Service\ProcessRunner\ComposerRunnerInterface'
      - '@module_handler'
      - '@string_translation'
      - '@PhpTuf\ComposerStager\Domain\Service\Precondition\ComposerIsAvailableInterface'
      - '@PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface'
    tags:
      - { name: event_subscriber }
  package_manager.validator.disk_space:
+39 −1
Original line number Diff line number Diff line
@@ -13,8 +13,11 @@ use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\package_manager\Event\StatusCheckEvent;
use PhpTuf\ComposerStager\Domain\Exception\ExceptionInterface;
use PhpTuf\ComposerStager\Domain\Exception\PreconditionException;
use PhpTuf\ComposerStager\Domain\Service\Precondition\ComposerIsAvailableInterface;
use PhpTuf\ComposerStager\Domain\Service\ProcessOutputCallback\ProcessOutputCallbackInterface;
use PhpTuf\ComposerStager\Domain\Service\ProcessRunner\ComposerRunnerInterface;
use PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
@@ -43,6 +46,13 @@ class ComposerExecutableValidator implements EventSubscriberInterface {
   */
  protected $composer;

  /**
   * The "Composer is available" precondition service.
   *
   * @var \PhpTuf\ComposerStager\Domain\Service\Precondition\ComposerIsAvailableInterface
   */
  protected $composerIsAvailable;

  /**
   * The module handler service.
   *
@@ -50,6 +60,13 @@ class ComposerExecutableValidator implements EventSubscriberInterface {
   */
  protected $moduleHandler;

  /**
   * The path factory service.
   *
   * @var \PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface
   */
  protected $pathFactory;

  /**
   * Constructs a ComposerExecutableValidator object.
   *
@@ -59,17 +76,38 @@ class ComposerExecutableValidator implements EventSubscriberInterface {
   *   The module handler service.
   * @param \Drupal\Core\StringTranslation\TranslationInterface $translation
   *   The translation service.
   * @param \PhpTuf\ComposerStager\Domain\Service\Precondition\ComposerIsAvailableInterface $composer_is_available
   *   The "Composer is available" precondition service.
   * @param \PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface $path_factory
   *   The path factory service.
   */
  public function __construct(ComposerRunnerInterface $composer, ModuleHandlerInterface $module_handler, TranslationInterface $translation) {
  public function __construct(ComposerRunnerInterface $composer, ModuleHandlerInterface $module_handler, TranslationInterface $translation, ComposerIsAvailableInterface $composer_is_available, PathFactoryInterface $path_factory) {
    $this->composer = $composer;
    $this->moduleHandler = $module_handler;
    $this->setStringTranslation($translation);
    $this->composerIsAvailable = $composer_is_available;
    $this->pathFactory = $path_factory;
  }

  /**
   * {@inheritdoc}
   */
  public function validateStagePreOperation(PreOperationStageEvent $event): void {
    // Return early if Composer is not available.
    try {
      // The "Composer is available" precondition requires active and staging
      // directories, but they don't actually matter to it, nor do path
      // exclusions, so dummies can be passed for simplicity.
      $active_dir = $this->pathFactory::create(__DIR__);
      $stage_dir = $active_dir;

      $this->composerIsAvailable->assertIsFulfilled($active_dir, $stage_dir);
    }
    catch (PreconditionException $e) {
      $event->addError([$e->getMessage()]);
      return;
    }

    try {
      $output = $this->runCommand();
    }
+39 −0
Original line number Diff line number Diff line
@@ -10,7 +10,10 @@ use Drupal\package_manager\Event\PreCreateEvent;
use Drupal\package_manager\Validator\ComposerExecutableValidator;
use Drupal\package_manager\ValidationResult;
use PhpTuf\ComposerStager\Domain\Exception\IOException;
use PhpTuf\ComposerStager\Domain\Exception\LogicException;
use PhpTuf\ComposerStager\Infrastructure\Service\Finder\ExecutableFinderInterface;
use PHPUnit\Framework\Assert;
use Symfony\Component\DependencyInjection\Reference;

/**
 * @covers \Drupal\package_manager\Validator\ComposerExecutableValidator
@@ -27,6 +30,8 @@ class ComposerExecutableValidatorTest extends PackageManagerKernelTestBase {

    $container->getDefinition('package_manager.validator.composer_executable')
      ->setClass(TestComposerExecutableValidator::class);
    $container
      ->register('test.terrible_composer_finder', TestFailingComposerFinder::class);
  }

  /**
@@ -47,6 +52,26 @@ class ComposerExecutableValidatorTest extends PackageManagerKernelTestBase {
    $this->assertResultsWithHelp([$error], PreCreateEvent::class);
  }

  /**
   * Test RuntimeError is handled correctly.
   */
  public function testComposerNotFound(): void {
    // @see \PhpTuf\ComposerStager\Infrastructure\Service\Precondition\ComposerIsAvailable::getUnfulfilledStatusMessage()
    $exception = new \Exception('Composer cannot be found.');
    TestComposerExecutableValidator::setCommandOutput($exception);

    // Change ComposerRunnerInterface path to throw a LogicException.
    $definition = $this->container->getDefinition('PhpTuf\ComposerStager\Domain\Service\Precondition\ComposerIsAvailableInterface');
    $definition->setArgument(0, new Reference('test.terrible_composer_finder'));

    // The validator should translate that exception into an error.
    $error = ValidationResult::createError([
      $exception->getMessage(),
    ]);
    $this->assertStatusCheckResults([$error]);
    $this->assertResults([$error], PreCreateEvent::class);
  }

  /**
   * Data provider for testComposerVersionValidation().
   *
@@ -212,3 +237,17 @@ class TestComposerExecutableValidator extends ComposerExecutableValidator {
  }

}

/**
 * A test-only version of ExecutableFinderInterface that throws LogicException.
 */
class TestFailingComposerFinder implements ExecutableFinderInterface {

  /**
   * {@inheritdoc}
   */
  public function find(string $name): string {
    throw new LogicException();
  }

}