Skip to content
Snippets Groups Projects
Commit c3f1ced2 authored by Ted Bowman's avatar Ted Bowman Committed by Ted Bowman
Browse files

Issue #3437023: Copy RequestedUpdateValidator to automatic_updates_extensions

parent d80722de
No related branches found
No related tags found
1 merge request!1100Issue #3436993: Ensure exceptions are shown if updating extensions and there...
Showing
with 231 additions and 1 deletion
......@@ -6,3 +6,6 @@ services:
Drupal\automatic_updates_extensions\Validator\UpdateReleaseValidator:
tags:
- { name: event_subscriber }
Drupal\automatic_updates_extensions\Validator\RequestedUpdateValidator:
tags:
- { name: event_subscriber }
<?php
declare(strict_types=1);
namespace Drupal\automatic_updates_extensions\Validator;
use Composer\Semver\Semver;
use Drupal\automatic_updates_extensions\ExtensionUpdateStage;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\package_manager\ComposerInspector;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\Event\StatusCheckEvent;
use Drupal\package_manager\PathLocator;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Validates that requested packages have been updated.
*
* @internal
* This is an internal part of Automatic Updates and may be changed or removed
* at any time without warning. External code should not interact with this
* class.
*/
final class RequestedUpdateValidator implements EventSubscriberInterface {
use StringTranslationTrait;
/**
* Constructs a RequestedUpdateValidator object.
*
* @param \Drupal\package_manager\ComposerInspector $composerInspector
* The Composer inspector service.
* @param \Drupal\package_manager\PathLocator $pathLocator
* The path locator service.
*/
public function __construct(
private readonly ComposerInspector $composerInspector,
private readonly PathLocator $pathLocator,
) {}
/**
* Validates that requested packages have been updated to the right version.
*
* @param \Drupal\package_manager\Event\PreApplyEvent|\Drupal\package_manager\Event\StatusCheckEvent $event
* The pre-apply event.
*/
public function checkRequestedStagedVersion(PreApplyEvent|StatusCheckEvent $event): void {
$stage = $event->stage;
if ($stage->getType() !== 'automatic_updates_extensions:attended' || !$stage->stageDirectoryExists()) {
return;
}
$requested_package_versions = $stage->getPackageVersions();
$active = $this->composerInspector->getInstalledPackagesList($this->pathLocator->getProjectRoot());
$staged = $this->composerInspector->getInstalledPackagesList($event->stage->getStageDirectory());
$changed_stage_packages = $staged->getPackagesWithDifferentVersionsIn($active)->getArrayCopy();
if (empty($changed_stage_packages)) {
$event->addError([$this->t('No updates detected in the staging area.')]);
return;
}
// Check for all changed the packages if they are updated to the requested
// version.
foreach (['production', 'dev'] as $package_type) {
foreach ($requested_package_versions[$package_type] as $requested_package_name => $requested_version) {
if (array_key_exists($requested_package_name, $changed_stage_packages)) {
$staged_version = $changed_stage_packages[$requested_package_name]->version;
if (!Semver::satisfies($staged_version, $requested_version)) {
$event->addError([
$this->t(
"The requested update to '@requested_package_name' to version '@requested_version' does not match the actual staged update to '@staged_version'.",
[
'@requested_package_name' => $requested_package_name,
'@requested_version' => $requested_version,
'@staged_version' => $staged_version,
]
),
]);
}
}
else {
$event->addError([
$this->t(
"The requested update to '@requested_package_name' to version '@requested_version' was not performed.",
[
'@requested_package_name' => $requested_package_name,
'@requested_version' => $requested_version,
]
),
]);
}
}
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
$events[StatusCheckEvent::class][] = ['checkRequestedStagedVersion'];
$events[PreApplyEvent::class][] = ['checkRequestedStagedVersion'];
return $events;
}
}
......@@ -48,6 +48,7 @@ class StatusCheckerRunAfterUpdateTest extends UpdaterFormTestBase {
$assert_session->pageTextNotContains(static::$warningsExplanation);
$this->assertTableShowsUpdates('Semver Test', '8.1.0', '8.1.1');
$this->getStageFixtureManipulator()->setVersion('drupal/semver_test', '8.1.1');
$this->assertUpdatesCount(1);
$page->checkField('projects[semver_test]');
$page->pressButton('Update');
......
......@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Drupal\Tests\automatic_updates_extensions\Functional;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\LegacyVersionUtility;
use Drupal\package_manager_test_validation\StagedDatabaseUpdateValidator;
/**
......@@ -60,6 +61,7 @@ class SuccessfulUpdateTest extends UpdaterFormTestBase {
$path_to_fixtures_folder = $project_name === 'aaa_update_test' ? '/../../../../package_manager/tests' : '/../..';
$this->setReleaseMetadata(__DIR__ . $path_to_fixtures_folder . '/fixtures/release-history/' . $project_name . '.1.1.xml');
$this->setProjectInstalledVersion([$project_name => $installed_version]);
$this->getStageFixtureManipulator()->setVersion('drupal/' . $project_name, LegacyVersionUtility::convertToSemanticVersion($target_version));
$this->checkForUpdates();
$state = $this->container->get('state');
$state->set('system.maintenance_mode', $maintenance_mode_on);
......
......@@ -31,6 +31,7 @@ class UnsuccessfulUpdateTest extends UpdaterFormTestBase {
$this->drupalGet('/admin/reports/updates');
$this->clickLink('Update Extensions');
$this->assertTableShowsUpdates('Semver Test', '8.1.0', '8.1.1');
$this->getStageFixtureManipulator()->setVersion('drupal/semver_test', '8.1.1');
$this->assertUpdatesCount(1);
$this->checkForMetaRefresh();
$assert->pageTextNotContains(static::$errorsExplanation);
......
......@@ -32,6 +32,7 @@ class UpdateErrorTest extends UpdaterFormTestBase {
$assert_session->pageTextNotContains(static::$warningsExplanation);
$this->assertTableShowsUpdates('Semver Test', '8.1.0', '8.1.1');
$this->getStageFixtureManipulator()->setVersion('drupal/semver_test', '8.1.1');
$this->assertUpdatesCount(1);
$page->checkField('projects[semver_test]');
$page->pressButton('Update');
......
......@@ -56,7 +56,7 @@ abstract class AutomaticUpdatesExtensionsKernelTestBase extends AutomaticUpdates
], TRUE)
->addPackage([
"name" => "drupal/semver_test",
"version" => "1.0.0",
"version" => "8.1.0",
"type" => "drupal-module",
])
->addPackage([
......
<?php
declare(strict_types=1);
namespace Drupal\Tests\automatic_updates_extensions\Kernel\Validator;
use Drupal\automatic_updates_extensions\ExtensionUpdateStage;
use Drupal\fixture_manipulator\ActiveFixtureManipulator;
use Drupal\package_manager\Exception\StageEventException;
use Drupal\package_manager\ValidationResult;
use Drupal\Tests\automatic_updates_extensions\Kernel\AutomaticUpdatesExtensionsKernelTestBase;
/**
* @coversDefaultClass \Drupal\automatic_updates_extensions\Validator\RequestedUpdateValidator
* @group automatic_updates_extensions
* @internal
*/
class RequestedUpdateValidatorTest extends AutomaticUpdatesExtensionsKernelTestBase {
/**
* Tests error messages if requested updates were not staged.
*
* @param array $staged_versions
* An array of the staged versions where the keys are the package names and
* the values are the package versions.
* @param array $expected_results
* The expected validation results.
*
* @dataProvider providerTestErrorMessage
*/
public function testErrorMessage(array $staged_versions, array $expected_results): void {
if ($staged_versions) {
// If we are going to stage updates to Drupal packages also update a
// non-Drupal. The validator should ignore the non-Drupal packages.
(new ActiveFixtureManipulator())
->addPackage([
"name" => 'vendor/non-drupal-package',
"version" => "1.0.0",
"type" => "drupal-module",
])
->commitChanges();
$this->getStageFixtureManipulator()->setVersion('vendor/non-drupal-package', '1.0.1');
foreach ($staged_versions as $package => $version) {
$this->getStageFixtureManipulator()->setVersion($package, $version);
}
}
$this->setReleaseMetadata([
'semver_test' => __DIR__ . '/../../../fixtures/release-history/semver_test.1.1.xml',
'drupal' => __DIR__ . '/../../../../../package_manager/tests/fixtures/release-history/drupal.9.8.2.xml',
'aaa_update_test' => __DIR__ . "/../../../../../package_manager/tests/fixtures/release-history/aaa_update_test.1.1.xml",
]);
// Set the project version to '8.0.1' so that there 2 versions of above this
// that will be in the list of supported releases, 8.1.0 and 8.1.1.
(new ActiveFixtureManipulator())
->setVersion('drupal/semver_test', '8.0.1')
->commitChanges();
// @todo Replace with use of the trait from the Update module in https://drupal.org/i/3348234.
$module_info = ['version' => '8.0.1', 'project' => 'semver_test'];
$this->config('update_test.settings')
->set("system_info.semver_test", $module_info)
->save();
$stage = $this->container->get(ExtensionUpdateStage::class);
$stage->begin([
'semver_test' => '8.1.1',
'aaa_update_test' => '8.x-1.1',
]);
$stage->stage();
$this->assertStatusCheckResults($expected_results, $stage);
try {
$stage->apply();
$this->fail('Expecting an exception.');
}
catch (StageEventException $exception) {
$this->assertExpectedResultsFromException($expected_results, $exception);
}
}
/**
* Data provider for testErrorMessage().
*
* @return mixed[]
* The test cases.
*/
public function providerTestErrorMessage() {
return [
'no updates' => [
[],
[
ValidationResult::createError([t('No updates detected in the staging area.')]),
],
],
'1 project not updated' => [
[
'drupal/aaa_update_test' => '1.1.0',
],
[
ValidationResult::createError([t("The requested update to 'drupal/semver_test' to version '8.1.1' was not performed.")]),
],
],
'project updated to wrong version' => [
[
'drupal/semver_test' => '8.1.0',
'drupal/aaa_update_test' => '1.1.0',
],
[
ValidationResult::createError([t("The requested update to 'drupal/semver_test' to version '8.1.1' does not match the actual staged update to '8.1.0'.")]),
],
],
];
}
}
......@@ -79,6 +79,9 @@ class UpdateReleaseValidatorTest extends AutomaticUpdatesExtensionsKernelTestBas
];
}
else {
// Ensure the correct version of the package is staged because the update
// is expected to succeed.
$this->getStageFixtureManipulator()->setVersion("drupal/$project", LegacyVersionUtility::convertToSemanticVersion($target_version));
$expected_results = [];
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment