diff --git a/package_manager/tests/fixtures/fake_site/vendor/composer/installed.json b/package_manager/tests/fixtures/fake_site/vendor/composer/installed.json index dd37230ed63052850405b84a216d0236b9108371..db52c02689e3a14b87ea68804a0a80d9aac9fd47 100644 --- a/package_manager/tests/fixtures/fake_site/vendor/composer/installed.json +++ b/package_manager/tests/fixtures/fake_site/vendor/composer/installed.json @@ -20,5 +20,6 @@ "name": "drupal/core-dev", "version": "9.8.0" } - ] + ], + "dev-package-names": [] } diff --git a/package_manager/tests/src/Kernel/FixtureUtilityTraitTest.php b/package_manager/tests/src/Kernel/FixtureUtilityTraitTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7b0e2ddd32fbe9a3f59563ed14e41c07b22eb85b --- /dev/null +++ b/package_manager/tests/src/Kernel/FixtureUtilityTraitTest.php @@ -0,0 +1,179 @@ +<?php + +namespace Drupal\Tests\package_manager\Kernel; + +use Drupal\Tests\package_manager\Traits\FixtureUtilityTrait; +use PHPUnit\Framework\AssertionFailedError; + +/** + * @coversDefaultClass \Drupal\Tests\package_manager\Traits\FixtureUtilityTrait + * + * @group package_manager + */ +class FixtureUtilityTraitTest extends PackageManagerKernelTestBase { + + use FixtureUtilityTrait; + + /** + * The root directory of the virtual project. + * + * @var string + */ + private string $dir; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + $this->dir = $this->container->get('package_manager.path_locator') + ->getProjectRoot(); + + $this->addPackage($this->dir, ['name' => 'my/package']); + $this->addPackage($this->dir, [ + 'name' => 'my/dev-package', + 'version' => '2.1.0', + 'dev_requirement' => TRUE, + ]); + } + + /** + * @covers ::addPackage + */ + public function testAddPackage(): void { + // Packages cannot be added without a name. + try { + $this->addPackage($this->dir, ['type' => 'unknown']); + $this->fail('Adding an anonymous package should raise an error.'); + } + catch (AssertionFailedError $e) { + $this->assertSame("Failed asserting that an array has the key 'name'.", $e->getMessage()); + } + + // We should not be able to add an existing package. + try { + $this->addPackage($this->dir, ['name' => 'my/package']); + $this->fail('Trying to add an existing package should raise an error.'); + } + catch (AssertionFailedError $e) { + $this->assertStringContainsString("Expected package 'my/package' to not be installed, but it was.", $e->getMessage()); + } + + $expected_packages = [ + 'my/package' => [ + 'name' => 'my/package', + ], + 'my/dev-package' => [ + 'name' => 'my/dev-package', + 'version' => '2.1.0', + 'dev_requirement' => TRUE, + ], + ]; + [$installed_json, $installed_php] = $this->getData(); + $installed_json['packages'] = array_intersect_key($installed_json['packages'], $expected_packages); + $this->assertSame($expected_packages, $installed_json['packages']); + $this->assertContains('my/dev-package', $installed_json['dev-package-names']); + $this->assertNotContains('my/package', $installed_json['dev-package-names']); + $this->assertSame($expected_packages, $installed_php); + } + + /** + * @covers ::modifyPackage + */ + public function testModifyPackage(): void { + // We should not be able to modify a non-existent package. + try { + $this->modifyPackage($this->dir, 'junk/drawer', ['type' => 'library']); + $this->fail('Modifying a non-existent package should raise an error.'); + } + catch (AssertionFailedError $e) { + $this->assertStringContainsString("Expected package 'junk/drawer' to be installed, but it wasn't.", $e->getMessage()); + } + + // Add a key to an existing package. + $this->modifyPackage($this->dir, 'my/package', ['type' => 'metapackage']); + // Change a key in an existing package. + $this->modifyPackage($this->dir, 'my/dev-package', ['version' => '3.2.1']); + // Move an existing package to dev requirements. + $this->addPackage($this->dir, [ + 'name' => 'my/other-package', + 'type' => 'library', + ]); + $this->modifyPackage($this->dir, 'my/other-package', ['dev_requirement' => TRUE]); + + $expected_packages = [ + 'my/package' => [ + 'name' => 'my/package', + 'type' => 'metapackage', + ], + 'my/dev-package' => [ + 'name' => 'my/dev-package', + 'version' => '3.2.1', + 'dev_requirement' => TRUE, + ], + 'my/other-package' => [ + 'name' => 'my/other-package', + 'type' => 'library', + 'dev_requirement' => TRUE, + ], + ]; + + [$installed_json, $installed_php] = $this->getData(); + $installed_json['packages'] = array_intersect_key($installed_json['packages'], $expected_packages); + $this->assertSame($expected_packages, $installed_json['packages']); + $this->assertContains('my/dev-package', $installed_json['dev-package-names']); + $this->assertContains('my/other-package', $installed_json['dev-package-names']); + $this->assertNotContains('my/package', $installed_json['dev-package-names']); + $this->assertSame($expected_packages, $installed_php); + } + + /** + * @covers ::removePackage + */ + public function testRemovePackage(): void { + // We should not be able to remove a package that's not installed. + try { + $this->removePackage($this->dir, 'junk/drawer'); + $this->fail('Removing a non-existent package should raise an error.'); + } + catch (AssertionFailedError $e) { + $this->assertStringContainsString("Expected package 'junk/drawer' to be installed, but it wasn't.", $e->getMessage()); + } + + $this->removePackage($this->dir, 'my/package'); + $this->removePackage($this->dir, 'my/dev-package'); + + foreach (['json', 'php'] as $extension) { + $contents = file_get_contents("$this->dir/vendor/composer/installed.$extension"); + $this->assertStringNotContainsString('my/package', $contents); + $this->assertStringNotContainsString('my/dev-package', $contents); + } + } + + /** + * Returns the data from installed.php and installed.json. + * + * @return array[] + * An array of two arrays. The first array will be the contents of + * installed.json, with the `packages` array keyed by package name. The + * second array will be the `versions` array from installed.php. + */ + private function getData(): array { + $installed_json = file_get_contents("$this->dir/vendor/composer/installed.json"); + $installed_json = json_decode($installed_json, TRUE, 512, JSON_THROW_ON_ERROR); + + $keyed_packages = []; + foreach ($installed_json['packages'] as $package) { + $keyed_packages[$package['name']] = $package; + } + $installed_json['packages'] = $keyed_packages; + + $installed_php = require "$this->dir/vendor/composer/installed.php"; + return [ + $installed_json, + $installed_php['versions'], + ]; + } + +} diff --git a/package_manager/tests/src/Traits/FixtureUtilityTrait.php b/package_manager/tests/src/Traits/FixtureUtilityTrait.php index e7b66acb6b948c51cdd7ebcb5036f1e07f04747b..1ca27372acaf305b1e130486a13d141b6d66a500 100644 --- a/package_manager/tests/src/Traits/FixtureUtilityTrait.php +++ b/package_manager/tests/src/Traits/FixtureUtilityTrait.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\package_manager\Traits; +use Drupal\Component\Utility\NestedArray; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator; @@ -76,4 +77,126 @@ trait FixtureUtilityTrait { } } + /** + * Adds a package. + * + * @param string $dir + * The root Composer-managed directory (e.g., the project root or staging + * area). + * @param array $package + * The package info that should be added to installed.json and + * installed.php. Must include a `name` key. + */ + protected function addPackage(string $dir, array $package): void { + $this->assertArrayHasKey('name', $package); + $this->setPackage($dir, $package['name'], $package, FALSE); + } + + /** + * Modifies a package's installed info. + * + * @param string $dir + * The root Composer-managed directory (e.g., the project root or staging + * area). + * @param string $name + * The name of the package to modify. + * @param array $package + * The package info that should be updated in installed.json and + * installed.php. + */ + protected function modifyPackage(string $dir, string $name, array $package): void { + $this->setPackage($dir, $name, $package, TRUE); + } + + /** + * Removes a package. + * + * @param string $dir + * The root Composer-managed directory (e.g., the project root or staging + * area). + * @param string $name + * The name of the package to remove. + */ + protected function removePackage(string $dir, string $name): void { + $this->setPackage($dir, $name, NULL, TRUE); + } + + /** + * Changes a package's installation information in a particular directory. + * + * This function is internal and should not be called directly. Use + * ::addPackage(), ::modifyPackage(), and ::removePackage() instead. + * + * @param string $dir + * The root Composer-managed directory (e.g., the project root or staging + * area). + * @param string $name + * The name of the package to add, update, or remove. + * @param array|null $package + * The package information to be set in installed.json and installed.php, or + * NULL to remove it. Will be merged into the existing information if the + * package is already installed. + * @param bool $should_exist + * Whether or not the package is expected to already be installed. + */ + private function setPackage(string $dir, string $name, ?array $package, bool $should_exist): void { + $file = $dir . '/vendor/composer/installed.json'; + $this->assertFileIsWritable($file); + + $data = file_get_contents($file); + $data = json_decode($data, TRUE, 512, JSON_THROW_ON_ERROR); + + // If the package is already installed, find its numerical index. + $position = NULL; + for ($i = 0; $i < count($data['packages']); $i++) { + if ($data['packages'][$i]['name'] === $name) { + $position = $i; + break; + } + } + // Ensure that we actually expect to find the package already installed (or + // not). + $message = $should_exist + ? "Expected package '$name' to be installed, but it wasn't." + : "Expected package '$name' to not be installed, but it was."; + $this->assertSame($should_exist, isset($position), $message); + + if (isset($position)) { + // If we're going to be updating the package data, merge the incoming data + // into what we already have. + if ($package) { + $package = NestedArray::mergeDeep($data['packages'][$position], $package); + } + + // Remove the existing package; the array will be re-keyed by + // array_splice(). + array_splice($data['packages'], $position, 1); + $data['dev-package-names'] = array_diff($data['dev-package-names'], [$name]); + $data['dev-package-names'] = array_values($data['dev-package-names']); + } + // Add the package back to the list, if we have data for it. + if ($package) { + $package['name'] = $name; + $data['packages'][] = $package; + + if (!empty($package['dev_requirement'])) { + $data['dev-package-names'][] = $name; + } + } + file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + + $file = $dir . '/vendor/composer/installed.php'; + $this->assertFileIsWritable($file); + + $data = require $file; + if ($package) { + $data['versions'][$name] = $package; + } + else { + unset($data['versions'][$name]); + } + $data = var_export($data, TRUE); + file_put_contents($file, "<?php\nreturn $data;"); + } + } diff --git a/tests/fixtures/StagedProjectsValidatorTest/README.md b/tests/fixtures/StagedProjectsValidatorTest/README.md deleted file mode 100644 index b29def63dd8293e7831b00c2dccca4007279ec6d..0000000000000000000000000000000000000000 --- a/tests/fixtures/StagedProjectsValidatorTest/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# `StagedProjectsValidatorTest` Fixtures - -### new_project_added -Simulates a scenario where, while updating Drupal core in a site with no non-core dependencies, a new contrib module and -a new custom module are unexpectedly installed (as runtime and dev dependencies, respectively). Additionally, two new -non-Drupal packages are installed (again, one as a runtime dependency, the other dev). - -**Expectation**: The validator should complain about the new modules; the new non-Drupal packages are ignored. - -### no_errors -Simulates a scenario where, while updating Drupal core in a site with two unpinned contrib dependencies (one runtime and -one dev), no Drupal packages are updated, but two non-Drupal libraries are removed (again, one a runtime dependency, the -other dev), two are updated (same arrangement), and two are added (ditto). - -**Expectation**: The validator to raise no errors; changes to non-Drupal packages are ignored. - -### project_removed -Simulates a scenario where, while updating Drupal core in a site with no non-core dependencies, an installed contrib -theme and an installed custom theme are unexpectedly removed (from runtime and dev dependencies, respectively). -Additionally, two installed non-Drupal packages are removed (again, one from a runtime dependency, the other dev). The -existing contrib dependencies' installed versions are unchanged. - -**Expectation**: The validator should complain about the removed themes; the removed non-Drupal packages are ignored. - -### version_changed -Simulates a scenario where, while updating Drupal core in a site with two unpinned contrib dependencies (one runtime and -one dev), the contrib modules are unexpectedly updated, as are two installed non-Drupal packages (again, one a runtime -dependency, the other dev). - -**Expectation**: The validator should complain about the updated modules; the updated non-Drupal packages are ignored. diff --git a/tests/fixtures/StagedProjectsValidatorTest/new_project_added/staged/vendor/composer/installed.json b/tests/fixtures/StagedProjectsValidatorTest/new_project_added/staged/vendor/composer/installed.json deleted file mode 100644 index acd9250ec7417f1acd9c5b5f2e63b970bc6f5d1e..0000000000000000000000000000000000000000 --- a/tests/fixtures/StagedProjectsValidatorTest/new_project_added/staged/vendor/composer/installed.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "_readme": [ - "This file simulates a list of packages installed in a virtual staging area.", - "It will be compared against active.installed.json.", - "See \\Drupal\\Tests\\automatic_updates\\Kernel\\StatusCheck\\StagedProjectsValidatorTest::testErrors()" - ], - "packages": [ - { - "name": "drupal/core", - "version": "9.8.1", - "type": "drupal-core", - "extra": { - "drupal-scaffold": { - "file-mapping": {} - } - } - }, - { - "name": "drupal/test_module", - "version": "1.3.0", - "type": "drupal-module" - }, - { - "name": "drupal/test_module2", - "version": "1.3.1", - "type": "drupal-module" - }, - { - "name": "other/new_project", - "description": "This is newly added project but there should be no error because it is not a drupal project", - "version": "1.3.1", - "type": "library" - }, - { - "name": "drupal/dev-test_module", - "version": "1.3.0", - "type": "drupal-module" - }, - { - "name": "drupal/dev-test_module2", - "version": "1.3.1", - "type": "drupal-custom-module" - }, - { - "name": "other/dev-new_project", - "description": "This is newly added project but there should be no error because it is not a drupal project", - "version": "1.3.1", - "type": "library" - } - ], - "dev": true, - "dev-package-names": [ - "drupal/dev-test_module", - "drupal/dev-test_module2", - "other/dev-new_project" - ] -} diff --git a/tests/fixtures/StagedProjectsValidatorTest/new_project_added/staged/vendor/composer/installed.php b/tests/fixtures/StagedProjectsValidatorTest/new_project_added/staged/vendor/composer/installed.php deleted file mode 100644 index 8871c50abbdf000b8512f6056dfd601dbc0d3bc7..0000000000000000000000000000000000000000 --- a/tests/fixtures/StagedProjectsValidatorTest/new_project_added/staged/vendor/composer/installed.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/** - * @file - */ - -$projects_dir = __DIR__ . '/../../modules'; -return [ - 'versions' => [ - 'drupal/test_module' => [ - 'type' => 'drupal-module', - 'install_path' => $projects_dir . '/test_module', - ], - 'drupal/dev-test_module' => [ - 'type' => 'drupal-module', - 'install_path' => $projects_dir . '/dev-test_module', - ], - 'drupal/test_module2' => [ - 'type' => 'drupal-module', - 'install_path' => $projects_dir . '/test_module2', - ], - 'drupal/dev-test_module2' => [ - 'type' => 'drupal-module', - 'install_path' => $projects_dir . '/dev-test_module2', - ], - 'other/new_project' => [ - 'type' => 'library', - 'install_path' => __DIR__ . '/../../new_project', - ], - 'other/dev-new_project' => [ - 'type' => 'library', - 'install_path' => __DIR__ . '/../../dev-new_project', - ], - ], -]; diff --git a/tests/fixtures/StagedProjectsValidatorTest/new_project_added/active/vendor/composer/installed.json b/tests/fixtures/StagedProjectsValidatorTest/new_project_added/vendor/composer/installed.json similarity index 100% rename from tests/fixtures/StagedProjectsValidatorTest/new_project_added/active/vendor/composer/installed.json rename to tests/fixtures/StagedProjectsValidatorTest/new_project_added/vendor/composer/installed.json diff --git a/tests/fixtures/StagedProjectsValidatorTest/new_project_added/active/vendor/composer/installed.php b/tests/fixtures/StagedProjectsValidatorTest/new_project_added/vendor/composer/installed.php similarity index 56% rename from tests/fixtures/StagedProjectsValidatorTest/new_project_added/active/vendor/composer/installed.php rename to tests/fixtures/StagedProjectsValidatorTest/new_project_added/vendor/composer/installed.php index 249cef05ef6c642431cdb14d90f3906ec4abb8d2..0111e9a997ed25cb6f8a8738a90d5b2d80111a44 100644 --- a/tests/fixtures/StagedProjectsValidatorTest/new_project_added/active/vendor/composer/installed.php +++ b/tests/fixtures/StagedProjectsValidatorTest/new_project_added/vendor/composer/installed.php @@ -4,16 +4,15 @@ * @file */ -$projects_dir = __DIR__ . '/../../modules'; return [ 'versions' => [ 'drupal/test_module' => [ 'type' => 'drupal-module', - 'install_path' => $projects_dir . '/test_module', + 'install_path' => '../../modules/test_module', ], 'drupal/dev-test_module' => [ 'type' => 'drupal-module', - 'install_path' => $projects_dir . '/dev-test_module', + 'install_path' => '../../modules/dev-test_module', ], ], ]; diff --git a/tests/fixtures/StagedProjectsValidatorTest/no_errors/staged/vendor/composer/installed.json b/tests/fixtures/StagedProjectsValidatorTest/no_errors/staged/vendor/composer/installed.json deleted file mode 100644 index 73ab701d96ca313a200680b99203ae68fbfde5d2..0000000000000000000000000000000000000000 --- a/tests/fixtures/StagedProjectsValidatorTest/no_errors/staged/vendor/composer/installed.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "_readme": [ - "This file simulates a list of packages installed in a virtual staging area.", - "It will be compared against active.installed.json.", - "See \\Drupal\\Tests\\automatic_updates\\Kernel\\StatusCheck\\StagedProjectsValidatorTest::testErrors()" - ], - "packages": [ - { - "name": "drupal/core", - "version": "9.8.1", - "type": "drupal-core", - "extra": { - "drupal-scaffold": { - "file-mapping": {} - } - } - }, - { - "name": "drupal/test_module", - "version": "1.3.0", - "type": "drupal-module" - }, - { - "name": "other/new_project", - "description": "This is newly added project but there should be no error because it is not a drupal project", - "version": "1.3.1", - "type": "library" - }, - { - "name": "other/changed", - "description": "This project version is changed but there should be no error because it is not a Drupal project.", - "version": "1.3.2", - "type": "library" - }, - { - "name": "drupal/dev-test_module", - "version": "1.3.0", - "type": "drupal-module" - }, - { - "name": "other/dev-new_project", - "description": "This is newly added project but there should be no error because it is not a drupal project", - "version": "1.3.1", - "type": "library" - }, - { - "name": "other/dev-changed", - "description": "This project version is changed but there should be no error because it is not a Drupal project.", - "version": "1.3.2", - "type": "library" - } - ], - "dev": true, - "dev-package-names": [ - "drupal/dev-test_module", - "other/dev-new_project", - "other/dev-changed" - ] -} diff --git a/tests/fixtures/StagedProjectsValidatorTest/no_errors/staged/vendor/composer/installed.php b/tests/fixtures/StagedProjectsValidatorTest/no_errors/staged/vendor/composer/installed.php deleted file mode 100644 index 706c6fd4632f0d344dae40097c4aae3fade04d70..0000000000000000000000000000000000000000 --- a/tests/fixtures/StagedProjectsValidatorTest/no_errors/staged/vendor/composer/installed.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -/** - * @file - * Simulates that 2 packages are installed in virtual staging area. - */ - -$projects_dir = __DIR__ . '/../../'; -return [ - 'versions' => [ - 'other/new_project' => [ - 'type' => 'library', - 'install_path' => $projects_dir . '/other/new_project', - ], - 'other/dev-new_project' => [ - 'type' => 'library', - 'install_path' => $projects_dir . '/other/dev-new_project', - ], - ], -]; diff --git a/tests/fixtures/StagedProjectsValidatorTest/no_errors/active/vendor/composer/installed.json b/tests/fixtures/StagedProjectsValidatorTest/no_errors/vendor/composer/installed.json similarity index 100% rename from tests/fixtures/StagedProjectsValidatorTest/no_errors/active/vendor/composer/installed.json rename to tests/fixtures/StagedProjectsValidatorTest/no_errors/vendor/composer/installed.json diff --git a/tests/fixtures/StagedProjectsValidatorTest/project_removed/staged/vendor/composer/installed.json b/tests/fixtures/StagedProjectsValidatorTest/project_removed/staged/vendor/composer/installed.json deleted file mode 100644 index d981720fe56b12bfb67cb950baaa87081384ad5f..0000000000000000000000000000000000000000 --- a/tests/fixtures/StagedProjectsValidatorTest/project_removed/staged/vendor/composer/installed.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "_readme": [ - "This file simulates a list of packages installed in a virtual staging area.", - "It will be compared against active.installed.json.", - "See \\Drupal\\Tests\\automatic_updates\\Kernel\\StatusCheck\\StagedProjectsValidatorTest::testErrors()" - ], - "packages": [ - { - "name": "drupal/core", - "version": "9.8.1", - "type": "drupal-core", - "extra": { - "drupal-scaffold": { - "file-mapping": {} - } - } - }, - { - "name": "drupal/test_module2", - "version": "1.3.1", - "type": "drupal-module" - }, - { - "name": "drupal/dev-test_module2", - "version": "1.3.1", - "type": "drupal-module" - } - ], - "dev": true, - "dev-package-names": [ - "drupal/dev-test_module2" - ] -} diff --git a/tests/fixtures/StagedProjectsValidatorTest/project_removed/active/vendor/composer/installed.json b/tests/fixtures/StagedProjectsValidatorTest/project_removed/vendor/composer/installed.json similarity index 100% rename from tests/fixtures/StagedProjectsValidatorTest/project_removed/active/vendor/composer/installed.json rename to tests/fixtures/StagedProjectsValidatorTest/project_removed/vendor/composer/installed.json diff --git a/tests/fixtures/StagedProjectsValidatorTest/version_changed/staged/vendor/composer/installed.json b/tests/fixtures/StagedProjectsValidatorTest/version_changed/staged/vendor/composer/installed.json deleted file mode 100644 index 574c593b4245a11881a682ff8a84828c8e6f2772..0000000000000000000000000000000000000000 --- a/tests/fixtures/StagedProjectsValidatorTest/version_changed/staged/vendor/composer/installed.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "_readme": [ - "This file simulates a list of packages installed in a virtual staging area.", - "It will be compared against active.installed.json.", - "See \\Drupal\\Tests\\automatic_updates\\Kernel\\StatusCheck\\StagedProjectsValidatorTest::testErrors()" - ], - "packages": [ - { - "name": "drupal/core", - "version": "9.8.1", - "type": "drupal-core", - "extra": { - "drupal-scaffold": { - "file-mapping": {} - } - } - }, - { - "name": "drupal/test_module", - "version": "1.3.1", - "type": "drupal-module" - }, - { - "name": "other/changed", - "description": "This project version is changed but there should be no error because it is not a Drupal project.", - "version": "1.3.2", - "type": "library" - }, - { - "name": "drupal/dev-test_module", - "version": "1.3.1", - "type": "drupal-module" - }, - { - "name": "other/dev-changed", - "description": "This project version is changed but there should be no error because it is not a Drupal project.", - "version": "1.3.2", - "type": "library" - } - ], - "dev": true, - "dev-package-names": [ - "drupal/dev-test_module", - "other/dev-changed" - ] -} diff --git a/tests/fixtures/StagedProjectsValidatorTest/version_changed/active/vendor/composer/installed.json b/tests/fixtures/StagedProjectsValidatorTest/version_changed/vendor/composer/installed.json similarity index 100% rename from tests/fixtures/StagedProjectsValidatorTest/version_changed/active/vendor/composer/installed.json rename to tests/fixtures/StagedProjectsValidatorTest/version_changed/vendor/composer/installed.json diff --git a/tests/src/Kernel/StatusCheck/StagedProjectsValidatorTest.php b/tests/src/Kernel/StatusCheck/StagedProjectsValidatorTest.php index 778f5f3282acab6b6af9c477880811f92509edab..13d7f2b6a99df7acd0bac6b5099be4c01061b07a 100644 --- a/tests/src/Kernel/StatusCheck/StagedProjectsValidatorTest.php +++ b/tests/src/Kernel/StatusCheck/StagedProjectsValidatorTest.php @@ -6,6 +6,7 @@ use Drupal\package_manager\Event\PreApplyEvent; use Drupal\package_manager\Exception\StageValidationException; use Drupal\package_manager\ValidationResult; use Drupal\Tests\automatic_updates\Kernel\AutomaticUpdatesKernelTestBase; +use Drupal\Tests\package_manager\Traits\FixtureUtilityTrait; /** * @covers \Drupal\automatic_updates\Validator\StagedProjectsValidator @@ -14,6 +15,8 @@ use Drupal\Tests\automatic_updates\Kernel\AutomaticUpdatesKernelTestBase; */ class StagedProjectsValidatorTest extends AutomaticUpdatesKernelTestBase { + use FixtureUtilityTrait; + /** * {@inheritdoc} */ @@ -29,28 +32,6 @@ class StagedProjectsValidatorTest extends AutomaticUpdatesKernelTestBase { parent::setUp(); } - /** - * Asserts a set of validation results when staged changes are applied. - * - * @param \Drupal\package_manager\ValidationResult[] $expected_results - * The expected validation results. - */ - private function validate(array $expected_results): void { - /** @var \Drupal\automatic_updates\Updater $updater */ - $updater = $this->container->get('automatic_updates.updater'); - $updater->begin(['drupal' => '9.8.1']); - $updater->stage(); - - try { - $updater->apply(); - // If no exception occurs, ensure we weren't expecting any errors. - $this->assertEmpty($expected_results); - } - catch (StageValidationException $e) { - $this->assertValidationResultsEqual($expected_results, $e->getResults()); - } - } - /** * Tests that exceptions are turned into validation errors. */ @@ -72,75 +53,188 @@ class StagedProjectsValidatorTest extends AutomaticUpdatesKernelTestBase { $this->container->get('event_dispatcher') ->addListener(PreApplyEvent::class, $listener, PHP_INT_MAX); - $this->validate([ - ValidationResult::createError(["Composer could not find the config file: $composer_json\n"]), + /** @var \Drupal\automatic_updates\Updater $updater */ + $updater = $this->container->get('automatic_updates.updater'); + $updater->begin(['drupal' => '9.8.1']); + $updater->stage(); + + $error = ValidationResult::createError(["Composer could not find the config file: $composer_json\n"]); + try { + $updater->apply(); + $this->fail('Expected an error, but none was raised.'); + } + catch (StageValidationException $e) { + $this->assertValidationResultsEqual([$error], $e->getResults()); + } + } + + /** + * Tests that an error is raised if Drupal extensions are unexpectedly added. + */ + public function testProjectsAdded(): void { + $this->copyFixtureFolderToActiveDirectory(__DIR__ . '/../../../fixtures/StagedProjectsValidatorTest/new_project_added'); + + $updater = $this->container->get('automatic_updates.updater'); + $updater->begin(['drupal' => '9.8.1']); + $updater->stage(); + + $stage_dir = $updater->getStageDirectory(); + $this->addPackage($stage_dir, [ + 'name' => 'drupal/test_module2', + 'version' => '1.3.1', + 'type' => 'drupal-module', + 'install_path' => '../../modules/test_module2', + ]); + $this->addPackage($stage_dir, [ + 'name' => 'drupal/dev-test_module2', + 'version' => '1.3.1', + 'type' => 'drupal-custom-module', + 'dev_requirement' => TRUE, + 'install_path' => '../../modules/dev-test_module2', + ]); + // The validator shouldn't complain about these packages being added or + // removed, since it only cares about Drupal modules and themes. + $this->addPackage($stage_dir, [ + 'name' => 'other/new_project', + 'version' => '1.3.1', + 'type' => 'library', + 'install_path' => '../other/new_project', ]); + $this->addPackage($stage_dir, [ + 'name' => 'other/dev-new_project', + 'version' => '1.3.1', + 'type' => 'library', + 'dev_requirement' => TRUE, + 'install_path' => '../other/dev-new_project', + ]); + $this->removePackage($stage_dir, 'other/removed'); + $this->removePackage($stage_dir, 'other/dev-removed'); + + $messages = [ + "module 'drupal/test_module2' installed.", + "custom module 'drupal/dev-test_module2' installed.", + ]; + $error = ValidationResult::createError($messages, t('The update cannot proceed because the following Drupal projects were installed during the update.')); + try { + $updater->apply(); + $this->fail('Expected an error, but none was raised.'); + } + catch (StageValidationException $e) { + $this->assertValidationResultsEqual([$error], $e->getResults()); + } } /** - * Tests validation errors, or lack thereof. - * - * @param string $root_fixture_directory - * A directory containing to fixtures sub-directories, 'active' and - * 'staged'. - * @param string|null $expected_summary - * The expected error summary, or NULL if no errors are expected. - * @param string[] $expected_messages - * The expected error messages, if any. - * - * @dataProvider providerErrors + * Tests that errors are raised if Drupal extensions are unexpectedly removed. */ - public function testErrors(string $root_fixture_directory, ?string $expected_summary, array $expected_messages): void { - $this->copyFixtureFolderToActiveDirectory("$root_fixture_directory/active"); - $this->copyFixtureFolderToStageDirectoryOnApply("$root_fixture_directory/staged"); - - $expected_results = []; - if ($expected_messages) { - // @codingStandardsIgnoreLine - $expected_results[] = ValidationResult::createError($expected_messages, t($expected_summary)); + public function testProjectsRemoved(): void { + $this->copyFixtureFolderToActiveDirectory(__DIR__ . '/../../../fixtures/StagedProjectsValidatorTest/project_removed'); + + $updater = $this->container->get('automatic_updates.updater'); + $updater->begin(['drupal' => '9.8.1']); + $updater->stage(); + + $stage_dir = $updater->getStageDirectory(); + $this->removePackage($stage_dir, 'drupal/test_theme'); + $this->removePackage($stage_dir, 'drupal/dev-test_theme'); + // The validator shouldn't complain about these packages being removed, + // since it only cares about Drupal modules and themes. + $this->removePackage($stage_dir, 'other/removed'); + $this->removePackage($stage_dir, 'other/dev-removed'); + + $messages = [ + "theme 'drupal/test_theme' removed.", + "custom theme 'drupal/dev-test_theme' removed.", + ]; + $error = ValidationResult::createError($messages, t('The update cannot proceed because the following Drupal projects were removed during the update.')); + try { + $updater->apply(); + $this->fail('Expected an error, but none was raised.'); + } + catch (StageValidationException $e) { + $this->assertValidationResultsEqual([$error], $e->getResults()); } - $this->validate($expected_results); } /** - * Data provider for testErrors(). - * - * @return \string[][] - * The test cases. + * Tests that errors are raised if Drupal extensions are unexpectedly updated. */ - public function providerErrors(): array { - $fixtures_folder = __DIR__ . '/../../../fixtures/StagedProjectsValidatorTest'; - return [ - 'new_project_added' => [ - "$fixtures_folder/new_project_added", - 'The update cannot proceed because the following Drupal projects were installed during the update.', - [ - "module 'drupal/test_module2' installed.", - "custom module 'drupal/dev-test_module2' installed.", - ], - ], - 'project_removed' => [ - "$fixtures_folder/project_removed", - 'The update cannot proceed because the following Drupal projects were removed during the update.', - [ - "theme 'drupal/test_theme' removed.", - "custom theme 'drupal/dev-test_theme' removed.", - ], - ], - 'version_changed' => [ - "$fixtures_folder/version_changed", - 'The update cannot proceed because the following Drupal projects were unexpectedly updated. Only Drupal Core updates are currently supported.', - [ - "module 'drupal/test_module' from 1.3.0 to 1.3.1.", - "module 'drupal/dev-test_module' from 1.3.0 to 1.3.1.", - ], - ], - 'no_errors' => [ - "$fixtures_folder/no_errors", - NULL, - [], - ], + public function testVersionsChanged(): void { + $this->copyFixtureFolderToActiveDirectory(__DIR__ . '/../../../fixtures/StagedProjectsValidatorTest/version_changed'); + + $updater = $this->container->get('automatic_updates.updater'); + $updater->begin(['drupal' => '9.8.1']); + $updater->stage(); + + $stage_dir = $updater->getStageDirectory(); + $this->modifyPackage($stage_dir, 'drupal/test_module', [ + 'version' => '1.3.1', + ]); + $this->modifyPackage($stage_dir, 'drupal/dev-test_module', [ + 'version' => '1.3.1', + ]); + // The validator shouldn't complain about these packages being updated, + // because it only cares about Drupal modules and themes. + $this->modifyPackage($stage_dir, 'other/changed', [ + 'version' => '1.3.2', + ]); + $this->modifyPackage($stage_dir, 'other/dev-changed', [ + 'version' => '1.3.2', + ]); + + $messages = [ + "module 'drupal/test_module' from 1.3.0 to 1.3.1.", + "module 'drupal/dev-test_module' from 1.3.0 to 1.3.1.", ]; + $error = ValidationResult::createError($messages, t('The update cannot proceed because the following Drupal projects were unexpectedly updated. Only Drupal Core updates are currently supported.')); + try { + $updater->apply(); + $this->fail('Expected an error, but none was raised.'); + } + catch (StageValidationException $e) { + $this->assertValidationResultsEqual([$error], $e->getResults()); + } + } + + /** + * Tests that no errors occur if only core and its dependencies are updated. + */ + public function testNoErrors(): void { + $this->copyFixtureFolderToActiveDirectory(__DIR__ . '/../../../fixtures/StagedProjectsValidatorTest/no_errors'); + + $updater = $this->container->get('automatic_updates.updater'); + $updater->begin(['drupal' => '9.8.1']); + $updater->stage(); + + $stage_dir = $updater->getStageDirectory(); + $this->modifyPackage($stage_dir, 'drupal/core', [ + 'version' => '9.8.1', + ]); + // The validator shouldn't care what happens to these packages, since it + // only concerns itself with Drupal modules and themes. + $this->addPackage($stage_dir, [ + 'name' => 'other/new_project', + 'version' => '1.3.1', + 'type' => 'library', + 'install_path' => '../other/new_project', + ]); + $this->addPackage($stage_dir, [ + 'name' => 'other/dev-new_project', + 'version' => '1.3.1', + 'type' => 'library', + 'dev_requirement' => TRUE, + 'install_path' => '../other/dev-new_project', + ]); + $this->modifyPackage($stage_dir, 'other/changed', [ + 'version' => '1.3.2', + ]); + $this->modifyPackage($stage_dir, 'other/dev-changed', [ + 'version' => '1.3.2', + ]); + $this->removePackage($stage_dir, 'other/removed'); + $this->removePackage($stage_dir, 'other/dev-removed'); + + $updater->apply(); } }