From bf697bb97796cfc5c73c05072e454ed4fd402eb6 Mon Sep 17 00:00:00 2001
From: "omkar.podey" <omkar.podey@3685158.no-reply.drupal.org>
Date: Mon, 5 Dec 2022 04:10:41 +0000
Subject: [PATCH] Issue #3309602 by omkar.podey, tedbow, Wim Leers,
 phenaproxima, Idoni, TravisCarden: Error: Composer could not find the config
 file

---
 package_manager/package_manager.services.yml  |  7 ++
 .../Validator/ComposerJsonExistsValidator.php | 73 +++++++++++++++++++
 .../ComposerJsonExistsValidatorTest.php       | 42 +++++++++++
 .../EnvironmentSupportValidatorTest.php       | 47 ++++--------
 4 files changed, 136 insertions(+), 33 deletions(-)
 create mode 100644 package_manager/src/Validator/ComposerJsonExistsValidator.php
 create mode 100644 package_manager/tests/src/Kernel/ComposerJsonExistsValidatorTest.php

diff --git a/package_manager/package_manager.services.yml b/package_manager/package_manager.services.yml
index 410e3ccce8..48f9775b9f 100644
--- a/package_manager/package_manager.services.yml
+++ b/package_manager/package_manager.services.yml
@@ -141,6 +141,13 @@ services:
       - '@extension.list.theme'
     tags:
       - { name: event_subscriber }
+  package_manager.validator.composer_json_exists:
+    class: Drupal\package_manager\Validator\ComposerJsonExistsValidator
+    arguments:
+      - '@package_manager.path_locator'
+      - '@string_translation'
+    tags:
+      - { name: event_subscriber }
   package_manager.test_site_excluder:
     class: Drupal\package_manager\PathExcluder\TestSiteExcluder
     arguments:
diff --git a/package_manager/src/Validator/ComposerJsonExistsValidator.php b/package_manager/src/Validator/ComposerJsonExistsValidator.php
new file mode 100644
index 0000000000..9542c70004
--- /dev/null
+++ b/package_manager/src/Validator/ComposerJsonExistsValidator.php
@@ -0,0 +1,73 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\package_manager\Validator;
+
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Drupal\package_manager\Event\PreCreateEvent;
+use Drupal\package_manager\Event\PreOperationStageEvent;
+use Drupal\package_manager\Event\StatusCheckEvent;
+use Drupal\package_manager\PathLocator;
+
+/**
+ * Validates that the active composer.json file exists.
+ *
+ * @internal
+ *   This is an internal part of Package Manager and may be changed or removed
+ *   at any time without warning. External code should not interact with this
+ *   class.
+ */
+final class ComposerJsonExistsValidator implements EventSubscriberInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * The path locator service.
+   *
+   * @var \Drupal\package_manager\PathLocator
+   */
+  protected $pathLocator;
+
+  /**
+   * Constructs a ComposerJsonExistsValidator object.
+   *
+   * @param \Drupal\package_manager\PathLocator $path_locator
+   *   The path locator service.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $translation
+   *   The string translation service.
+   */
+  public function __construct(PathLocator $path_locator, TranslationInterface $translation) {
+    $this->pathLocator = $path_locator;
+    $this->setStringTranslation($translation);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents(): array {
+    // Set priority to 190 which puts it just after EnvironmentSupportValidator.
+    // @see \Drupal\package_manager\Validator\EnvironmentSupportValidator
+    return [
+      PreCreateEvent::class => ['validateComposerJson', 190],
+      StatusCheckEvent::class => ['validateComposerJson', 190],
+    ];
+  }
+
+  /**
+   * Validates that the active composer.json file exists.
+   *
+   * @param \Drupal\package_manager\Event\PreOperationStageEvent $event
+   *   The event.
+   */
+  public function validateComposerJson(PreOperationStageEvent $event): void {
+    $project_root = $this->pathLocator->getProjectRoot();
+    if (!file_exists($project_root . '/composer.json')) {
+      $event->addError([$this->t('No composer.json file can be found at @project_root', ['@project_root' => $project_root])]);
+      $event->stopPropagation();
+    }
+  }
+
+}
diff --git a/package_manager/tests/src/Kernel/ComposerJsonExistsValidatorTest.php b/package_manager/tests/src/Kernel/ComposerJsonExistsValidatorTest.php
new file mode 100644
index 0000000000..0bb39c908c
--- /dev/null
+++ b/package_manager/tests/src/Kernel/ComposerJsonExistsValidatorTest.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\Tests\package_manager\Kernel;
+
+use Drupal\package_manager\Event\PreCreateEvent;
+use Drupal\package_manager\Event\StatusCheckEvent;
+use Drupal\package_manager\ValidationResult;
+
+/**
+ * @covers \Drupal\package_manager\Validator\ComposerJsonExistsValidator
+ * @group package_manager
+ */
+class ComposerJsonExistsValidatorTest extends PackageManagerKernelTestBase {
+
+  /**
+   * Tests validation when the active composer.json is not present.
+   */
+  public function testComposerRequirement(): void {
+    unlink($this->container->get('package_manager.path_locator')
+      ->getProjectRoot() . '/composer.json');
+    $result = ValidationResult::createError([
+      'No composer.json file can be found at vfs://root/active',
+    ]);
+    foreach ([PreCreateEvent::class, StatusCheckEvent::class] as $event_class) {
+      $this->container->get('event_dispatcher')->addListener(
+        $event_class,
+        function () use ($event_class): void {
+          $this->fail('Event propagation should have been stopped during ' . $event_class . '.');
+        },
+        // Execute this listener immediately after the tested validator, which
+        // uses priority 190. This ensures informative test failures.
+        // @see \Drupal\package_manager\Validator\ComposerJsonExistsValidator::getSubscribedEvents()
+        189
+      );
+    }
+    $this->assertStatusCheckResults([$result]);
+    $this->assertResults([$result], PreCreateEvent::class);
+  }
+
+}
diff --git a/package_manager/tests/src/Kernel/EnvironmentSupportValidatorTest.php b/package_manager/tests/src/Kernel/EnvironmentSupportValidatorTest.php
index 9ad3aecf5a..7ebb78ab26 100644
--- a/package_manager/tests/src/Kernel/EnvironmentSupportValidatorTest.php
+++ b/package_manager/tests/src/Kernel/EnvironmentSupportValidatorTest.php
@@ -3,47 +3,16 @@
 namespace Drupal\Tests\package_manager\Kernel;
 
 use Drupal\package_manager\Event\PreCreateEvent;
-use Drupal\package_manager\Event\PreOperationStageEvent;
+use Drupal\package_manager\Event\StatusCheckEvent;
 use Drupal\package_manager\ValidationResult;
 use Drupal\package_manager\Validator\EnvironmentSupportValidator;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
  * @covers \Drupal\package_manager\Validator\EnvironmentSupportValidator
  * @group package_manager
  * @internal
  */
-class EnvironmentSupportValidatorTest extends PackageManagerKernelTestBase implements EventSubscriberInterface {
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function setUp(): void {
-    parent::setUp();
-    $this->container->get('event_dispatcher')->addSubscriber($this);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function getSubscribedEvents(): array {
-    $map = function (): string {
-      return 'assertValidationStopped';
-    };
-    return array_map($map, EnvironmentSupportValidator::getSubscribedEvents());
-  }
-
-  /**
-   * Ensures that the validator stops any further validation.
-   *
-   * @param \Drupal\package_manager\Event\PreOperationStageEvent $event
-   *   The event object.
-   */
-  public function assertValidationStopped(PreOperationStageEvent $event): void {
-    if ($event->getResults()) {
-      $this->assertTrue($event->isPropagationStopped());
-    }
-  }
+class EnvironmentSupportValidatorTest extends PackageManagerKernelTestBase {
 
   /**
    * Tests handling of an invalid URL in the environment support variable.
@@ -54,6 +23,18 @@ class EnvironmentSupportValidatorTest extends PackageManagerKernelTestBase imple
     $result = ValidationResult::createError([
       'Package Manager is not supported by your environment.',
     ]);
+    foreach ([PreCreateEvent::class, StatusCheckEvent::class] as $event_class) {
+      $this->container->get('event_dispatcher')->addListener(
+        $event_class,
+        function () use ($event_class): void {
+          $this->fail('Event propagation should have been stopped during ' . $event_class . '.');
+        },
+        // Execute this listener immediately after the tested validator, which
+        // uses priority 200. This ensures informative test failures.
+        // @see \Drupal\package_manager\Validator\EnvironmentSupportValidator::getSubscribedEvents()
+        199
+      );
+    }
     $this->assertStatusCheckResults([$result]);
     $this->assertResults([$result], PreCreateEvent::class);
   }
-- 
GitLab