From de577fda9bd1693f4cefb3fae6ac1fe92e6421b6 Mon Sep 17 00:00:00 2001
From: phenaproxima <phenaproxima@205645.no-reply.drupal.org>
Date: Fri, 29 Oct 2021 18:33:51 +0000
Subject: [PATCH] Issue #3246673 by phenaproxima, tedbow: Move
 PendingUpdatesValidator into Package Manager

---
 automatic_updates.services.yml                |  6 +-
 package_manager/package_manager.services.yml  |  8 ++
 .../PendingUpdatesValidator.php               | 16 ++--
 package_manager/tests/fixtures/db_update.php  | 14 ++++
 .../ComposerExecutableValidatorTest.php       | 30 +------
 .../src/Kernel/DiskSpaceValidatorTest.php     | 17 +---
 .../Kernel/PackageManagerKernelTestBase.php   | 82 +++++++++++++++++++
 .../Kernel/PendingUpdatesValidatorTest.php    | 81 ++++++++++++++++++
 tests/fixtures/db_update.php                  | 12 ---
 tests/fixtures/post_update.php                | 12 ---
 .../PackageManagerReadinessChecksTest.php     |  2 +
 .../PendingUpdatesValidatorTest.php           | 53 ------------
 12 files changed, 199 insertions(+), 134 deletions(-)
 rename {src/Validator => package_manager/src/EventSubscriber}/PendingUpdatesValidator.php (78%)
 create mode 100644 package_manager/tests/fixtures/db_update.php
 create mode 100644 package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php
 create mode 100644 package_manager/tests/src/Kernel/PendingUpdatesValidatorTest.php
 delete mode 100644 tests/fixtures/db_update.php
 delete mode 100644 tests/fixtures/post_update.php
 delete mode 100644 tests/src/Kernel/ReadinessValidation/PendingUpdatesValidatorTest.php

diff --git a/automatic_updates.services.yml b/automatic_updates.services.yml
index a592916bf7..dc587e01ce 100644
--- a/automatic_updates.services.yml
+++ b/automatic_updates.services.yml
@@ -59,11 +59,9 @@ services:
     tags:
       - { name: event_subscriber }
   automatic_updates.pending_updates_validator:
-    class: Drupal\automatic_updates\Validator\PendingUpdatesValidator
+    class: Drupal\automatic_updates\Validator\PackageManagerReadinessCheck
     arguments:
-      - '%app.root%'
-      - '@update.post_update_registry'
-      - '@string_translation'
+      - '@package_manager.validator.pending_updates'
     tags:
       - { name: event_subscriber }
   automatic_updates.validator.file_system_permissions:
diff --git a/package_manager/package_manager.services.yml b/package_manager/package_manager.services.yml
index fdcd9d9605..7ee8a28e62 100644
--- a/package_manager/package_manager.services.yml
+++ b/package_manager/package_manager.services.yml
@@ -100,3 +100,11 @@ services:
       - '@string_translation'
     tags:
       - { name: event_subscriber }
+  package_manager.validator.pending_updates:
+    class: Drupal\package_manager\EventSubscriber\PendingUpdatesValidator
+    arguments:
+      - '%app.root%'
+      - '@update.post_update_registry'
+      - '@string_translation'
+    tags:
+      - { name: event_subscriber }
diff --git a/src/Validator/PendingUpdatesValidator.php b/package_manager/src/EventSubscriber/PendingUpdatesValidator.php
similarity index 78%
rename from src/Validator/PendingUpdatesValidator.php
rename to package_manager/src/EventSubscriber/PendingUpdatesValidator.php
index f82c2793b0..241a75d191 100644
--- a/src/Validator/PendingUpdatesValidator.php
+++ b/package_manager/src/EventSubscriber/PendingUpdatesValidator.php
@@ -1,8 +1,7 @@
 <?php
 
-namespace Drupal\automatic_updates\Validator;
+namespace Drupal\package_manager\EventSubscriber;
 
-use Drupal\automatic_updates\Event\ReadinessCheckEvent;
 use Drupal\package_manager\Event\PreCreateEvent;
 use Drupal\package_manager\Event\StageEvent;
 use Drupal\package_manager\ValidationResult;
@@ -10,12 +9,11 @@ use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\StringTranslation\TranslationInterface;
 use Drupal\Core\Update\UpdateRegistry;
 use Drupal\Core\Url;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
  * Validates that there are no pending database updates.
  */
-class PendingUpdatesValidator implements EventSubscriberInterface {
+class PendingUpdatesValidator implements StageValidatorInterface {
 
   use StringTranslationTrait;
 
@@ -50,12 +48,9 @@ class PendingUpdatesValidator implements EventSubscriberInterface {
   }
 
   /**
-   * Validates that there are no pending database updates.
-   *
-   * @param \Drupal\package_manager\Event\StageEvent $event
-   *   The event object.
+   * {@inheritdoc}
    */
-  public function checkPendingUpdates(StageEvent $event): void {
+  public function validateStage(StageEvent $event): void {
     require_once $this->appRoot . '/core/includes/install.inc';
     require_once $this->appRoot . '/core/includes/update.inc';
 
@@ -77,8 +72,7 @@ class PendingUpdatesValidator implements EventSubscriberInterface {
    */
   public static function getSubscribedEvents() {
     return [
-      PreCreateEvent::class => 'checkPendingUpdates',
-      ReadinessCheckEvent::class => 'checkPendingUpdates',
+      PreCreateEvent::class => 'validateStage',
     ];
   }
 
diff --git a/package_manager/tests/fixtures/db_update.php b/package_manager/tests/fixtures/db_update.php
new file mode 100644
index 0000000000..7adacfb2e2
--- /dev/null
+++ b/package_manager/tests/fixtures/db_update.php
@@ -0,0 +1,14 @@
+<?php
+
+/**
+ * @file
+ * Contains a fake database update function for testing.
+ */
+
+/**
+ * Here is a fake update hook.
+ *
+ * The schema version is the maximum possible value for a 32-bit integer.
+ */
+function package_manager_update_2147483647() {
+}
diff --git a/package_manager/tests/src/Kernel/ComposerExecutableValidatorTest.php b/package_manager/tests/src/Kernel/ComposerExecutableValidatorTest.php
index 73379cbf9e..8905f788ed 100644
--- a/package_manager/tests/src/Kernel/ComposerExecutableValidatorTest.php
+++ b/package_manager/tests/src/Kernel/ComposerExecutableValidatorTest.php
@@ -2,11 +2,9 @@
 
 namespace Drupal\Tests\package_manager\Kernel;
 
-use Drupal\KernelTests\KernelTestBase;
 use Drupal\package_manager\Event\PreCreateEvent;
 use Drupal\package_manager\EventSubscriber\ComposerExecutableValidator;
 use Drupal\package_manager\ValidationResult;
-use Drupal\Tests\package_manager\Traits\ValidationTestTrait;
 use PhpTuf\ComposerStager\Exception\IOException;
 use PhpTuf\ComposerStager\Infrastructure\Process\ExecutableFinderInterface;
 use Prophecy\Argument;
@@ -16,29 +14,7 @@ use Prophecy\Argument;
  *
  * @group package_manager
  */
-class ComposerExecutableValidatorTest extends KernelTestBase {
-
-  use ValidationTestTrait;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected static $modules = ['package_manager'];
-
-  /**
-   * Runs the validator under test, and asserts its results match expectations.
-   *
-   * @param \Drupal\package_manager\ValidationResult[] $expected_results
-   *   The expected validation results.
-   */
-  private function assertResults(array $expected_results): void {
-    $stage = $this->prophesize('\Drupal\package_manager\Stage');
-    $event = new PreCreateEvent($stage->reveal());
-    $this->container->get('package_manager.validator.composer_executable')
-      ->validateStage($event);
-
-    $this->assertValidationResultsEqual($expected_results, $event->getResults());
-  }
+class ComposerExecutableValidatorTest extends PackageManagerKernelTestBase {
 
   /**
    * Tests that an error is raised if the Composer executable isn't found.
@@ -58,7 +34,7 @@ class ComposerExecutableValidatorTest extends KernelTestBase {
     $error = ValidationResult::createError([
       $exception->getMessage(),
     ]);
-    $this->assertResults([$error]);
+    $this->assertResults([$error], PreCreateEvent::class);
   }
 
   /**
@@ -155,7 +131,7 @@ class ComposerExecutableValidatorTest extends KernelTestBase {
 
     // If the validator can't find a recognized, supported version of Composer,
     // it should produce errors.
-    $this->assertResults($expected_results);
+    $this->assertResults($expected_results, PreCreateEvent::class);
   }
 
 }
diff --git a/package_manager/tests/src/Kernel/DiskSpaceValidatorTest.php b/package_manager/tests/src/Kernel/DiskSpaceValidatorTest.php
index 9e33cdc73a..75def57667 100644
--- a/package_manager/tests/src/Kernel/DiskSpaceValidatorTest.php
+++ b/package_manager/tests/src/Kernel/DiskSpaceValidatorTest.php
@@ -2,26 +2,17 @@
 
 namespace Drupal\Tests\package_manager\Kernel;
 
-use Drupal\KernelTests\KernelTestBase;
 use Drupal\package_manager\Event\PreCreateEvent;
 use Drupal\package_manager\EventSubscriber\DiskSpaceValidator;
 use Drupal\package_manager\ValidationResult;
 use Drupal\Component\Utility\Bytes;
-use Drupal\Tests\package_manager\Traits\ValidationTestTrait;
 
 /**
  * @covers \Drupal\package_manager\EventSubscriber\DiskSpaceValidator
  *
  * @group package_manager
  */
-class DiskSpaceValidatorTest extends KernelTestBase {
-
-  use ValidationTestTrait;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected static $modules = ['package_manager'];
+class DiskSpaceValidatorTest extends PackageManagerKernelTestBase {
 
   /**
    * Data provider for ::testDiskSpaceValidation().
@@ -201,11 +192,7 @@ class DiskSpaceValidatorTest extends KernelTestBase {
     $validator->sharedDisk = $shared_disk;
     $validator->freeSpace = array_map([Bytes::class, 'toNumber'], $free_space);
 
-    $stage = $this->prophesize('\Drupal\package_manager\Stage');
-    $event = new PreCreateEvent($stage->reveal());
-    $validator->validateStage($event);
-
-    $this->assertValidationResultsEqual($expected_results, $event->getResults());
+    $this->assertResults($expected_results, PreCreateEvent::class);
   }
 
 }
diff --git a/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php b/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php
new file mode 100644
index 0000000000..012914fecb
--- /dev/null
+++ b/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace Drupal\Tests\package_manager\Kernel;
+
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\package_manager\Event\StageEvent;
+use Drupal\package_manager\Stage;
+use Drupal\package_manager\StageException;
+use Drupal\Tests\package_manager\Traits\ValidationTestTrait;
+
+/**
+ * Base class for kernel tests of Package Manager's functionality.
+ */
+abstract class PackageManagerKernelTestBase extends KernelTestBase {
+
+  use ValidationTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = [
+    'package_manager',
+    'package_manager_bypass',
+  ];
+
+  /**
+   * Asserts validation results are returned from a stage life cycle event.
+   *
+   * @param \Drupal\package_manager\ValidationResult[] $expected_results
+   *   The expected validation results.
+   * @param string $event_class
+   *   The class of the event which should return the results.
+   */
+  protected function assertResults(array $expected_results, string $event_class): void {
+    $stage = new TestStage(
+      $this->container->get('package_manager.path_locator'),
+      $this->container->get('package_manager.beginner'),
+      $this->container->get('package_manager.stager'),
+      $this->container->get('package_manager.committer'),
+      $this->container->get('package_manager.cleaner'),
+      $this->container->get('event_dispatcher'),
+    );
+    try {
+      $stage->create();
+      $stage->require(['drupal/core:9.8.1']);
+      $stage->apply();
+      $stage->destroy();
+    }
+    catch (StageException $e) {
+      $this->assertValidationResultsEqual($expected_results, $e->getResults());
+      // TestStage::dispatch() attaches the event object to the exception so
+      // that we can analyze it.
+      $this->assertInstanceOf($event_class, $e->event);
+    }
+    // If no errors are raised, we won't have asserted anything and the test
+    // will be marked as risky. To prevent that, assert an eternal truth.
+    $this->assertTrue(TRUE);
+  }
+
+}
+
+/**
+ * Defines a stage specifically for testing purposes.
+ */
+class TestStage extends Stage {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function dispatch(StageEvent $event): void {
+    try {
+      parent::dispatch($event);
+    }
+    catch (StageException $e) {
+      // Attach the event object to the exception so that test code can verify
+      // that the exception was thrown when a specific event was dispatched.
+      $e->event = $event;
+      throw $e;
+    }
+  }
+
+}
diff --git a/package_manager/tests/src/Kernel/PendingUpdatesValidatorTest.php b/package_manager/tests/src/Kernel/PendingUpdatesValidatorTest.php
new file mode 100644
index 0000000000..a1a2604263
--- /dev/null
+++ b/package_manager/tests/src/Kernel/PendingUpdatesValidatorTest.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Drupal\Tests\package_manager\Kernel;
+
+use Drupal\package_manager\Event\PreCreateEvent;
+use Drupal\package_manager\ValidationResult;
+
+/**
+ * @covers \Drupal\package_manager\EventSubscriber\PendingUpdatesValidator
+ *
+ * @group package_manager
+ */
+class PendingUpdatesValidatorTest extends PackageManagerKernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['system'];
+
+  /**
+   * Registers all of the System module's post-update functions.
+   *
+   * Since kernel tests don't normally install modules and register their
+   * updates, this method makes sure that the validator is tested from a clean,
+   * fully up-to-date state.
+   */
+  private function registerPostUpdateFunctions(): void {
+    $updates = $this->container->get('update.post_update_registry')
+      ->getPendingUpdateFunctions();
+
+    $this->container->get('keyvalue')
+      ->get('post_update')
+      ->set('existing_updates', $updates);
+  }
+
+  /**
+   * Tests that no error is raised if there are no pending updates.
+   */
+  public function testNoPendingUpdates(): void {
+    $this->registerPostUpdateFunctions();
+    $this->assertResults([], PreCreateEvent::class);
+  }
+
+  /**
+   * Tests that an error is raised if there are pending schema updates.
+   *
+   * @depends testNoPendingUpdates
+   */
+  public function testPendingUpdateHook(): void {
+    // Register the System module's post-update functions, so that any detected
+    // pending updates are guaranteed to be schema updates.
+    $this->registerPostUpdateFunctions();
+
+    // Set the installed schema version of Package Manager to its default value
+    // and import an empty update hook which is numbered much higher than will
+    // ever exist in the real world.
+    $this->container->get('keyvalue')
+      ->get('system.schema')
+      ->set('package_manager', \Drupal::CORE_MINIMUM_SCHEMA_VERSION);
+
+    require_once __DIR__ . '/../../fixtures/db_update.php';
+
+    $result = ValidationResult::createError([
+      'Some modules have database schema updates to install. You should run the <a href="/update.php">database update script</a> immediately.',
+    ]);
+    $this->assertResults([$result], PreCreateEvent::class);
+  }
+
+  /**
+   * Tests that an error is raised if there are pending post-updates.
+   */
+  public function testPendingPostUpdate(): void {
+    // The System module's post-update functions have not been registered, so
+    // the update registry will think they're pending.
+    $result = ValidationResult::createError([
+      'Some modules have database schema updates to install. You should run the <a href="/update.php">database update script</a> immediately.',
+    ]);
+    $this->assertResults([$result], PreCreateEvent::class);
+  }
+
+}
diff --git a/tests/fixtures/db_update.php b/tests/fixtures/db_update.php
deleted file mode 100644
index dcebeff410..0000000000
--- a/tests/fixtures/db_update.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains a fake database update function for testing.
- */
-
-/**
- * Here is a fake update.
- */
-function automatic_updates_update_50000() {
-}
diff --git a/tests/fixtures/post_update.php b/tests/fixtures/post_update.php
deleted file mode 100644
index a77909b6d4..0000000000
--- a/tests/fixtures/post_update.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains a fake post-update function for testing.
- */
-
-/**
- * Here is a fake post-update.
- */
-function automatic_updates_post_update_test() {
-}
diff --git a/tests/src/Kernel/ReadinessValidation/PackageManagerReadinessChecksTest.php b/tests/src/Kernel/ReadinessValidation/PackageManagerReadinessChecksTest.php
index 003e4ea86b..94a8060c98 100644
--- a/tests/src/Kernel/ReadinessValidation/PackageManagerReadinessChecksTest.php
+++ b/tests/src/Kernel/ReadinessValidation/PackageManagerReadinessChecksTest.php
@@ -16,6 +16,7 @@ use Prophecy\Argument;
  *
  * @see \Drupal\Tests\package_manager\Kernel\ComposerExecutableValidatorTest
  * @see \Drupal\Tests\package_manager\Kernel\DiskSpaceValidatorTest
+ * @see \Drupal\Tests\package_manager\Kernel\PendingUpdatesValidatorTest
  */
 class PackageManagerReadinessChecksTest extends AutomaticUpdatesKernelTestBase {
 
@@ -37,6 +38,7 @@ class PackageManagerReadinessChecksTest extends AutomaticUpdatesKernelTestBase {
     return [
       ['package_manager.validator.composer_executable'],
       ['package_manager.validator.disk_space'],
+      ['package_manager.validator.pending_updates'],
     ];
   }
 
diff --git a/tests/src/Kernel/ReadinessValidation/PendingUpdatesValidatorTest.php b/tests/src/Kernel/ReadinessValidation/PendingUpdatesValidatorTest.php
deleted file mode 100644
index c0ddd3b224..0000000000
--- a/tests/src/Kernel/ReadinessValidation/PendingUpdatesValidatorTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-namespace Drupal\Tests\automatic_updates\Kernel\ReadinessValidation;
-
-use Drupal\package_manager\ValidationResult;
-use Drupal\Tests\automatic_updates\Kernel\AutomaticUpdatesKernelTestBase;
-
-/**
- * @covers \Drupal\automatic_updates\Validator\PendingUpdatesValidator
- *
- * @group automatic_updates
- */
-class PendingUpdatesValidatorTest extends AutomaticUpdatesKernelTestBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  protected static $modules = [
-    'automatic_updates',
-    'package_manager',
-  ];
-
-  /**
-   * Tests that no error is raised if there are no pending updates.
-   */
-  public function testNoPendingUpdates(): void {
-    $this->assertCheckerResultsFromManager([], TRUE);
-  }
-
-  /**
-   * Tests that an error is raised if there are pending schema updates.
-   */
-  public function testPendingUpdateHook(): void {
-    require __DIR__ . '/../../../fixtures/db_update.php';
-
-    $this->container->get('keyvalue')
-      ->get('system.schema')
-      ->set('automatic_updates', \Drupal::CORE_MINIMUM_SCHEMA_VERSION);
-
-    $result = ValidationResult::createError(['Some modules have database schema updates to install. You should run the <a href="/update.php">database update script</a> immediately.']);
-    $this->assertCheckerResultsFromManager([$result], TRUE);
-  }
-
-  /**
-   * Tests that an error is raised if there are pending post-updates.
-   */
-  public function testPendingPostUpdate(): void {
-    require __DIR__ . '/../../../fixtures/post_update.php';
-    $result = ValidationResult::createError(['Some modules have database schema updates to install. You should run the <a href="/update.php">database update script</a> immediately.']);
-    $this->assertCheckerResultsFromManager([$result], TRUE);
-  }
-
-}
-- 
GitLab