From cdee22d7ca58e0ec9e9e3cd9e656dd2e0ad91fed Mon Sep 17 00:00:00 2001
From: phenaproxima <phenaproxima@205645.no-reply.drupal.org>
Date: Thu, 5 May 2022 18:57:18 +0000
Subject: [PATCH] Issue #3279086 by phenaproxima:
 StagedDatabaseUpdateValidatorTest should use a virtual project

---
 .../fake_site/vendor/composer/installed.json  |  8 +++++
 .../src/EventSubscriber/FixtureStager.php     | 26 ++++++++++++---
 .../src/Kernel/LockFileValidatorTest.php      | 14 ++++----
 .../Kernel/PackageManagerKernelTestBase.php   |  9 ++++-
 .../StagedDatabaseUpdateValidatorTest.php     | 33 +++++++++----------
 5 files changed, 60 insertions(+), 30 deletions(-)
 create mode 100644 package_manager/tests/fixtures/fake_site/vendor/composer/installed.json

diff --git a/package_manager/tests/fixtures/fake_site/vendor/composer/installed.json b/package_manager/tests/fixtures/fake_site/vendor/composer/installed.json
new file mode 100644
index 0000000000..ad9a32852d
--- /dev/null
+++ b/package_manager/tests/fixtures/fake_site/vendor/composer/installed.json
@@ -0,0 +1,8 @@
+{
+  "packages": [
+    {
+      "name": "drupal/core",
+      "version": "9.8.0"
+    }
+  ]
+}
diff --git a/package_manager/tests/modules/package_manager_test_fixture/src/EventSubscriber/FixtureStager.php b/package_manager/tests/modules/package_manager_test_fixture/src/EventSubscriber/FixtureStager.php
index 11f8552ac6..fd5ecb7124 100644
--- a/package_manager/tests/modules/package_manager_test_fixture/src/EventSubscriber/FixtureStager.php
+++ b/package_manager/tests/modules/package_manager_test_fixture/src/EventSubscriber/FixtureStager.php
@@ -57,12 +57,27 @@ class FixtureStager implements EventSubscriberInterface {
    * @see \Drupal\Tests\automatic_updates\Functional\AutomaticUpdatesFunctionalTestBase::tearDown()
    */
   public function copyFilesFromFixture(PostRequireEvent $event): void {
-    $fixturePath = $this->state->get(static::class);
+    [$fixturePath, $changeLock] = $this->state->get(static::class);
+
     if ($fixturePath && is_dir($fixturePath)) {
-      $this->fileSystem->mirror($fixturePath, $event->getStage()->getStageDirectory(), NULL, [
+      $destination = $event->getStage()->getStageDirectory();
+
+      $this->fileSystem->mirror($fixturePath, $destination, NULL, [
         'override' => TRUE,
         'delete' => TRUE,
       ]);
+
+      // Modify the lock file in the staging area, to simulate that a package
+      // was added, updated, or removed. Otherwise, tests must remember to
+      // disable the lock file validator.
+      // @see \Drupal\package_manager\Validator\LockFileValidator
+      $lock = $destination . '/composer.lock';
+      if ($changeLock && file_exists($lock)) {
+        $data = file_get_contents($lock);
+        $data = json_decode($data);
+        $data->_time = microtime();
+        file_put_contents($lock, json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
+      }
     }
   }
 
@@ -80,9 +95,12 @@ class FixtureStager implements EventSubscriberInterface {
    *
    * @param string $path
    *   The path of the fixture to copy into the staging area.
+   * @param bool $change_lock
+   *   (optional) Whether to change the lock file, in order to simulate the
+   *   addition, updating, or removal of a package. Defaults to TRUE.
    */
-  public static function setFixturePath(string $path): void {
-    \Drupal::state()->set(static::class, $path);
+  public static function setFixturePath(string $path, bool $change_lock = TRUE): void {
+    \Drupal::state()->set(static::class, [$path, $change_lock]);
   }
 
 }
diff --git a/package_manager/tests/src/Kernel/LockFileValidatorTest.php b/package_manager/tests/src/Kernel/LockFileValidatorTest.php
index 757481d81b..0d3e5c6af6 100644
--- a/package_manager/tests/src/Kernel/LockFileValidatorTest.php
+++ b/package_manager/tests/src/Kernel/LockFileValidatorTest.php
@@ -2,12 +2,12 @@
 
 namespace Drupal\Tests\package_manager\Kernel;
 
-use Drupal\package_manager\Event\PostRequireEvent;
 use Drupal\package_manager\Event\PreApplyEvent;
 use Drupal\package_manager\Event\PreCreateEvent;
 use Drupal\package_manager\Event\PreRequireEvent;
 use Drupal\package_manager\Validator\LockFileValidator;
 use Drupal\package_manager\ValidationResult;
+use Drupal\package_manager_test_fixture\EventSubscriber\FixtureStager;
 
 /**
  * @coversDefaultClass \Drupal\package_manager\Validator\LockFileValidator
@@ -56,7 +56,7 @@ class LockFileValidatorTest extends PackageManagerKernelTestBase {
 
     // Change the lock file to ensure the stored hash of the previous version
     // has been deleted.
-    file_put_contents($this->activeDir . '/composer.lock', 'changed');
+    file_put_contents($this->activeDir . '/composer.lock', '{"changed": true}');
     $this->assertResults([]);
   }
 
@@ -124,11 +124,11 @@ class LockFileValidatorTest extends PackageManagerKernelTestBase {
    * Tests validation when the staged and active lock files are identical.
    */
   public function testApplyWithNoChange(): void {
-    $this->addListener(PostRequireEvent::class, function (PostRequireEvent $event) {
-      $stage_dir = $event->getStage()->getStageDirectory();
-      mkdir($stage_dir);
-      copy("$this->activeDir/composer.lock", "$stage_dir/composer.lock");
-    });
+    // Ensure the lock file is not changed when the active directory is copied
+    // into the virtual staging area.
+    // @see \Drupal\package_manager_test_fixture\EventSubscriber\FixtureStager
+    FixtureStager::setFixturePath($this->activeDir, FALSE);
+
     $result = ValidationResult::createError([
       'There are no pending Composer operations.',
     ]);
diff --git a/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php b/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php
index cc2e2e4d4e..3a04735d0c 100644
--- a/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php
+++ b/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php
@@ -10,6 +10,7 @@ use Drupal\package_manager\Exception\StageException;
 use Drupal\package_manager\Exception\StageValidationException;
 use Drupal\package_manager\PathLocator;
 use Drupal\package_manager\Stage;
+use Drupal\package_manager_test_fixture\EventSubscriber\FixtureStager;
 use Drupal\Tests\package_manager\Traits\ValidationTestTrait;
 use org\bovigo\vfs\vfsStream;
 use org\bovigo\vfs\vfsStreamDirectory;
@@ -29,6 +30,7 @@ abstract class PackageManagerKernelTestBase extends KernelTestBase {
   protected static $modules = [
     'package_manager',
     'package_manager_bypass',
+    'package_manager_test_fixture',
   ];
 
   /**
@@ -188,7 +190,12 @@ abstract class PackageManagerKernelTestBase extends KernelTestBase {
     $this->vfsRoot->addChild($stage_dir);
     static::$testStagingRoot = $stage_dir->url();
 
-    $path_locator = $this->mockPathLocator($active_dir->url());
+    $active_dir = $active_dir->url();
+    $path_locator = $this->mockPathLocator($active_dir);
+
+    // Ensure that the active directory is copied into the virtual staging area,
+    // even if Package Manager's operations are bypassed.
+    FixtureStager::setFixturePath($active_dir);
 
     // Since the path locator now points to a virtual file system, we need to
     // replace the disk space validator with a test-only version that bypasses
diff --git a/tests/src/Kernel/ReadinessValidation/StagedDatabaseUpdateValidatorTest.php b/tests/src/Kernel/ReadinessValidation/StagedDatabaseUpdateValidatorTest.php
index ff06b1a4eb..1269a0cee0 100644
--- a/tests/src/Kernel/ReadinessValidation/StagedDatabaseUpdateValidatorTest.php
+++ b/tests/src/Kernel/ReadinessValidation/StagedDatabaseUpdateValidatorTest.php
@@ -29,40 +29,37 @@ class StagedDatabaseUpdateValidatorTest extends AutomaticUpdatesKernelTestBase {
    * {@inheritdoc}
    */
   protected function setUp(): void {
-    // In this test, we want to disable the lock file validator because, even
-    // though both the active and stage directories will have a valid lock file,
-    // this validator will complain because they don't differ at all.
-    $this->disableValidators[] = 'package_manager.validator.lock_file';
     parent::setUp();
 
-    static::$testStagingRoot = $this->vfsRoot->url();
+    $this->createTestProject();
 
     /** @var \Drupal\Tests\automatic_updates\Kernel\TestCronUpdater $updater */
     $updater = $this->container->get('automatic_updates.cron_updater');
     $updater->begin(['drupal' => '9.8.2']);
     $updater->stage();
+  }
 
-    $stage_dir = $updater->getStageDirectory();
-    mkdir($stage_dir);
+  /**
+   * {@inheritdoc}
+   */
+  protected function createTestProject(): void {
+    parent::createTestProject();
 
-    // To satisfy StagedProjectsValidator, copy the active Composer files into
-    // the staging area.
-    $active_dir = $this->getDrupalRoot();
-    @copy("$active_dir/composer.json", "$stage_dir/composer.json");
-    @copy("$active_dir/composer.lock", "$stage_dir/composer.lock");
-    mkdir("$stage_dir/vendor/composer", 0777, TRUE);
-    @copy("$active_dir/vendor/composer/installed.json", "$stage_dir/vendor/composer/installed.json");
+    $drupal_root = $this->getDrupalRoot();
+    $virtual_active_dir = $this->container->get('package_manager.path_locator')
+      ->getProjectRoot();
 
-    // Copy the .install and .post_update.php files from every installed module
-    // into the staging directory.
+    // Copy the .install and .post_update.php files from every installed module,
+    // in the *actual* Drupal code base that is running this test, into the
+    // virtual project (i.e., the active directory).
     $module_list = $this->container->get('module_handler')->getModuleList();
     foreach ($module_list as $name => $module) {
       $path = $module->getPath();
-      @mkdir("$stage_dir/$path", 0777, TRUE);
+      @mkdir("$virtual_active_dir/$path", 0777, TRUE);
 
       foreach (static::SUFFIXES as $suffix) {
         // If the source file doesn't exist, silence the warning it raises.
-        @copy("$active_dir/$path/$name.$suffix", "$stage_dir/$path/$name.$suffix");
+        @copy("$drupal_root/$path/$name.$suffix", "$virtual_active_dir/$path/$name.$suffix");
       }
     }
   }
-- 
GitLab