<?php namespace Drupal\Tests\automatic_updates\Kernel\ReadinessValidation; use Drupal\automatic_updates\CronUpdater; use Drupal\automatic_updates\Event\ReadinessCheckEvent; use Drupal\automatic_updates\Updater; use Drupal\automatic_updates_test\ReadinessChecker\TestChecker1; use Drupal\automatic_updates_test2\ReadinessChecker\TestChecker2; use Drupal\system\SystemManager; use Drupal\Tests\automatic_updates\Kernel\AutomaticUpdatesKernelTestBase; /** * @coversDefaultClass \Drupal\automatic_updates\Validation\ReadinessValidationManager * * @group automatic_updates */ class ReadinessValidationManagerTest extends AutomaticUpdatesKernelTestBase { /** * {@inheritdoc} */ protected static $modules = [ 'automatic_updates_test', 'package_manager', 'package_manager_bypass', 'user', ]; /** * {@inheritdoc} */ protected function setUp(): void { parent::setUp(); $this->installEntitySchema('user'); $this->installSchema('user', ['users_data']); $this->createTestValidationResults(); } /** * @covers ::getResults */ public function testGetResults(): void { $this->enableModules(['automatic_updates', 'automatic_updates_test2']); $this->assertCheckerResultsFromManager([], TRUE); $expected_results = [ array_pop($this->testResults['checker_1']), array_pop($this->testResults['checker_2']), ]; TestChecker1::setTestResult($expected_results[0], ReadinessCheckEvent::class); TestChecker2::setTestResult($expected_results[1], ReadinessCheckEvent::class); $expected_results_all = array_merge($expected_results[0], $expected_results[1]); $this->assertCheckerResultsFromManager($expected_results_all, TRUE); // Define a constant flag that will cause the readiness checker // service priority to be altered. define('PACKAGE_MANAGER_TEST_VALIDATOR_PRIORITY', 1); // Rebuild the container to trigger the service to be altered. $kernel = $this->container->get('kernel'); $this->container = $kernel->rebuildContainer(); // The stored results should be returned, even though the validators' order // has been changed and the container has been rebuilt. $this->assertValidationResultsEqual($expected_results_all, $this->getResultsFromManager()); // Confirm that after calling run() the expected results order has changed. $expected_results_all_reversed = array_reverse($expected_results_all); $this->assertCheckerResultsFromManager($expected_results_all_reversed, TRUE); $expected_results = [ $this->testResults['checker_1']['2 errors 2 warnings'], $this->testResults['checker_2']['2 errors 2 warnings'], ]; TestChecker1::setTestResult($expected_results[0], ReadinessCheckEvent::class); TestChecker2::setTestResult($expected_results[1], ReadinessCheckEvent::class); $expected_results_all = array_merge($expected_results[1], $expected_results[0]); $this->assertCheckerResultsFromManager($expected_results_all, TRUE); // Confirm that filtering by severity works. $warnings_only_results = [ $expected_results[1]['2:warnings'], $expected_results[0]['1:warnings'], ]; $this->assertCheckerResultsFromManager($warnings_only_results, FALSE, SystemManager::REQUIREMENT_WARNING); $errors_only_results = [ $expected_results[1]['2:errors'], $expected_results[0]['1:errors'], ]; $this->assertCheckerResultsFromManager($errors_only_results, FALSE, SystemManager::REQUIREMENT_ERROR); } /** * Tests that the manager is run after modules are installed. */ public function testRunOnInstall(): void { $expected_results = [array_pop($this->testResults['checker_1'])]; TestChecker1::setTestResult($expected_results[0], ReadinessCheckEvent::class); // Confirm that messages from an existing module are displayed when // 'automatic_updates' is installed. $this->container->get('module_installer')->install(['automatic_updates']); $this->assertCheckerResultsFromManager($expected_results[0]); // Confirm that the checkers are run when a module that provides a readiness // checker is installed. $expected_results = [ array_pop($this->testResults['checker_1']), array_pop($this->testResults['checker_2']), ]; TestChecker1::setTestResult($expected_results[0], ReadinessCheckEvent::class); TestChecker2::setTestResult($expected_results[1], ReadinessCheckEvent::class); $this->container->get('module_installer')->install(['automatic_updates_test2']); $expected_results_all = array_merge($expected_results[0], $expected_results[1]); $this->assertCheckerResultsFromManager($expected_results_all); // Confirm that the checkers are run when a module that does not provide a // readiness checker is installed. $expected_results = [ array_pop($this->testResults['checker_1']), array_pop($this->testResults['checker_2']), ]; TestChecker1::setTestResult($expected_results[0], ReadinessCheckEvent::class); TestChecker2::setTestResult($expected_results[1], ReadinessCheckEvent::class); $expected_results_all = array_merge($expected_results[0], $expected_results[1]); $this->container->get('module_installer')->install(['help']); $this->assertCheckerResultsFromManager($expected_results_all); } /** * Tests that the manager is run after modules are uninstalled. */ public function testRunOnUninstall(): void { $expected_results = [ array_pop($this->testResults['checker_1']), array_pop($this->testResults['checker_2']), ]; TestChecker1::setTestResult($expected_results[0], ReadinessCheckEvent::class); TestChecker2::setTestResult($expected_results[1], ReadinessCheckEvent::class); // Confirm that messages from existing modules are displayed when // 'automatic_updates' is installed. $this->container->get('module_installer')->install(['automatic_updates', 'automatic_updates_test2', 'help']); $expected_results_all = array_merge($expected_results[0], $expected_results[1]); $this->assertCheckerResultsFromManager($expected_results_all); // Confirm that the checkers are run when a module that provides a readiness // checker is uninstalled. $expected_results = [ array_pop($this->testResults['checker_1']), ]; TestChecker1::setTestResult($expected_results[0], ReadinessCheckEvent::class); TestChecker2::setTestResult(array_pop($this->testResults['checker_2']), ReadinessCheckEvent::class); $this->container->get('module_installer')->uninstall(['automatic_updates_test2']); $this->assertCheckerResultsFromManager($expected_results[0]); // Confirm that the checkers are run when a module that does not provide a // readiness checker is uninstalled. $expected_results = [ array_pop($this->testResults['checker_1']), ]; TestChecker1::setTestResult($expected_results[0], ReadinessCheckEvent::class); $this->container->get('module_installer')->uninstall(['help']); $this->assertCheckerResultsFromManager($expected_results[0]); } /** * @covers ::runIfNoStoredResults * @covers ::clearStoredResults */ public function testRunIfNeeded(): void { $expected_results = array_pop($this->testResults['checker_1']); TestChecker1::setTestResult($expected_results, ReadinessCheckEvent::class); $this->container->get('module_installer')->install(['automatic_updates', 'automatic_updates_test2']); $this->assertCheckerResultsFromManager($expected_results); $unexpected_results = array_pop($this->testResults['checker_1']); TestChecker1::setTestResult($unexpected_results, ReadinessCheckEvent::class); $manager = $this->container->get('automatic_updates.readiness_validation_manager'); // Confirm that the new results will not be returned because the checkers // will not be run. $manager->runIfNoStoredResults(); $this->assertCheckerResultsFromManager($expected_results); // Confirm that the new results will be returned because the checkers will // be run if the stored results are deleted. $manager->clearStoredResults(); $expected_results = $unexpected_results; $manager->runIfNoStoredResults(); $this->assertCheckerResultsFromManager($expected_results); // Confirm that the results are the same after rebuilding the container. $unexpected_results = array_pop($this->testResults['checker_1']); TestChecker1::setTestResult($unexpected_results, ReadinessCheckEvent::class); /** @var \Drupal\Core\DrupalKernel $kernel */ $kernel = $this->container->get('kernel'); $this->container = $kernel->rebuildContainer(); $this->assertCheckerResultsFromManager($expected_results); } /** * Tests the Automatic Updates cron setting changes which stage class is used. */ public function testCronSetting(): void { $this->enableModules(['automatic_updates']); $stage_class = NULL; $listener = function (ReadinessCheckEvent $event) use (&$stage_class): void { $stage_class = get_class($event->getStage()); }; $event_dispatcher = $this->container->get('event_dispatcher'); $event_dispatcher->addListener(ReadinessCheckEvent::class, $listener); $this->container->get('automatic_updates.readiness_validation_manager')->run(); // By default, updates will be enabled on cron. $this->assertSame(CronUpdater::class, $stage_class); $this->config('automatic_updates.settings') ->set('cron', CronUpdater::DISABLED) ->save(); $this->container->get('automatic_updates.readiness_validation_manager')->run(); $this->assertSame(Updater::class, $stage_class); } /** * Tests that stored validation results are deleted after an update. */ public function testStoredResultsDeletedPostApply(): void { $this->container->get('module_installer') ->install(['automatic_updates']); // Ensure there's a simulated core release to update to. $this->setReleaseMetadata(__DIR__ . '/../../../fixtures/release-history/drupal.9.8.2.xml'); // The readiness checker should raise a warning, so that the update is not // blocked or aborted. $results = $this->testResults['checker_1']['1 warning']; TestChecker1::setTestResult($results, ReadinessCheckEvent::class); // Ensure that the validation manager collects the warning. /** @var \Drupal\automatic_updates\Validation\ReadinessValidationManager $manager */ $manager = $this->container->get('automatic_updates.readiness_validation_manager') ->run(); TestChecker1::setTestResult(NULL, ReadinessCheckEvent::class); // Even though the checker no longer returns any results, the previous // results should be stored. $this->assertValidationResultsEqual($results, $manager->getResults()); // Don't validate staged projects because actual staging operations are // bypassed by package_manager_bypass, which will make this validator // complain that there is no actual Composer data for it to inspect. $validator = $this->container->get('automatic_updates.staged_projects_validator'); $this->container->get('event_dispatcher')->removeSubscriber($validator); /** @var \Drupal\automatic_updates\Updater $updater */ $updater = $this->container->get('automatic_updates.updater'); $updater->begin(['drupal' => '9.8.1']); $updater->stage(); $updater->apply(); $updater->destroy(); // The readiness validation manager shouldn't have any stored results. $this->assertEmpty($manager->getResults()); } }