diff --git a/automatic_updates.services.yml b/automatic_updates.services.yml index a592916bf7584623a500729171f0bbc74f56284b..dc587e01cec962cd812c435cfae147c31fd52f2b 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 fdcd9d96055a0de162de2cc5837211e8cabd6775..7ee8a28e629e5ee8793cf8bcc2c7f36c412caa11 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 f82c2793b0766288028a32e1e4e137b4c5160032..241a75d19169080aa2f9c7ff35aa2fac6522012a 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 0000000000000000000000000000000000000000..7adacfb2e2e8d8bcc498735e7472f6fde2577340 --- /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 73379cbf9e03a9dde13e9ac528bccac072e430dc..8905f788eddac081411dcadc5088121e7658ff96 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 9e33cdc73adaf9fd584571b4c18396674f7a75e6..75def576670e91cfdc36ac0fe57455077d5b56bb 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 0000000000000000000000000000000000000000..012914fecba57475211eda8139dc9b676e24cf31 --- /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 0000000000000000000000000000000000000000..a1a26042637c3f7730493ee2e4937e3721804172 --- /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 dcebeff41012ad40820b3e17880b40c74f31eb3c..0000000000000000000000000000000000000000 --- 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 a77909b6d4db212fd19041e4a379d68dcfae4b5c..0000000000000000000000000000000000000000 --- 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 003e4ea86b6419a61fe993a4ca6ed5db43774da2..94a8060c9856c86fbbab3aca02f4d77da13f6bd7 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 c0ddd3b224f5b7fbf00dea0a4b39e9d02e081178..0000000000000000000000000000000000000000 --- 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); - } - -}