diff --git a/automatic_updates_extensions/tests/src/Functional/UpdaterFormTest.php b/automatic_updates_extensions/tests/src/Functional/UpdaterFormTest.php index 10b2988dd1a94cbc10924629953c1f8acf0d6fe4..524d19f98730184fa6e7ed6a1e30c027006b238b 100644 --- a/automatic_updates_extensions/tests/src/Functional/UpdaterFormTest.php +++ b/automatic_updates_extensions/tests/src/Functional/UpdaterFormTest.php @@ -73,6 +73,7 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase { 'administer site configuration', 'administer software updates', 'access site in maintenance mode', + 'access administration pages', ]); // We need this fixture as only projects installed via composer will show up // on the form. @@ -214,6 +215,80 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase { $this->assertNotSame($pre_apply_time, $post_apply_time); } + /** + * Data provider for testStatusCheckerRunAfterUpdate(). + * + * @return bool[][] + * The test cases. + */ + public function providerStatusCheckerRunAfterUpdate(): array { + return [ + 'has database updates' => [TRUE], + 'does not have database updates' => [FALSE], + ]; + } + + /** + * Tests status checks are run after an update. + * + * @param bool $has_database_updates + * Whether the site has database updates or not. + * + * @dataProvider providerStatusCheckerRunAfterUpdate + */ + public function testStatusCheckerRunAfterUpdate(bool $has_database_updates): void { + $this->useFixtureDirectoryAsStaged(__DIR__ . '/../../fixtures/stage_composer/semver_test'); + $this->setReleaseMetadata(__DIR__ . '/../../fixtures/release-history/semver_test.1.1.xml'); + $this->setProjectInstalledVersion(['semver_test' => '8.1.0']); + $this->checkForUpdates(); + $page = $this->getSession()->getPage(); + // Navigate to the automatic updates form. + $this->drupalGet('/admin/modules/automatic-update-extensions'); + $assert_session = $this->assertSession(); + $assert_session->pageTextNotContains(static::$errorsExplanation); + $assert_session->pageTextNotContains(static::$warningsExplanation); + + $this->assertTableShowsUpdates('Semver Test', '8.1.0', '8.1.1'); + $this->assertUpdatesCount(1); + $page->checkField('projects[semver_test]'); + $page->pressButton('Update'); + $this->checkForMetaRefresh(); + $this->assertUpdateStagedTimes(1); + // Set an error before completing the update. This error should be visible + // on admin pages after completing the update without having to explicitly + // run the status checks. + TestSubscriber1::setTestResult([ValidationResult::createError(['Error before continue.'])], StatusCheckEvent::class); + if ($has_database_updates) { + // Simulate a staged database update in the automatic_updates_test module. + // We must do this after the update has started, because the pending + // updates validator will prevent an update from starting. + $this->container->get('state')->set('automatic_updates_test.new_update', TRUE); + $page->pressButton('Continue'); + $this->checkForMetaRefresh(); + $this->assertSession()->addressEquals('/update.php'); + $assert_session->pageTextNotContains('Possible database updates have been detected in the following extensions.'); + $assert_session->pageTextContainsOnce('Please apply database updates to complete the update process.'); + $page->clickLink('Continue'); + // @see automatic_updates_update_1191934() + $assert_session->pageTextContains('Dynamic automatic_updates_update_1191934'); + $page->clickLink('Apply pending updates'); + $this->checkForMetaRefresh(); + $assert_session->pageTextContains('Updates were attempted.'); + } + else { + $page->pressButton('Continue'); + $this->checkForMetaRefresh(); + $assert_session->addressEquals('/admin/reports/updates'); + $assert_session->pageTextContainsOnce('Update complete!'); + } + // Status checks should display errors on admin page. + $this->drupalGet('/admin'); + // Confirm that the status checks were run and the new error is displayed. + $assert_session->statusMessageContains('Error before continue.', 'error'); + $assert_session->statusMessageContains(static::$errorsExplanation, 'error'); + $assert_session->pageTextNotContains('Your site has not recently run an update readiness check. Run readiness checks now.'); + } + /** * Tests that an exception is thrown if a previous apply failed. */ diff --git a/src/Controller/UpdateController.php b/src/Controller/UpdateController.php index dddbf90695c8a21981c3c3cc058e430899fb5bd5..362e52297fd71df21db7021d28bfa1594235ce1b 100644 --- a/src/Controller/UpdateController.php +++ b/src/Controller/UpdateController.php @@ -3,6 +3,7 @@ namespace Drupal\automatic_updates\Controller; use Drupal\automatic_updates\BatchProcessor; +use Drupal\automatic_updates\Validation\StatusChecker; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Routing\RouteMatchInterface; @@ -35,6 +36,13 @@ final class UpdateController extends ControllerBase { */ protected $routeMatch; + /** + * The status checker. + * + * @var \Drupal\automatic_updates\Validation\StatusChecker + */ + protected $statusChecker; + /** * Constructs an UpdateController object. * @@ -44,11 +52,14 @@ final class UpdateController extends ControllerBase { * The state service. * @param \Drupal\Core\Routing\RouteMatchInterface $route_match * The current route match. + * @param \Drupal\automatic_updates\Validation\StatusChecker $status_checker + * The status checker service. */ - public function __construct(PendingUpdatesValidator $pending_updates_validator, StateInterface $state, RouteMatchInterface $route_match) { + public function __construct(PendingUpdatesValidator $pending_updates_validator, StateInterface $state, RouteMatchInterface $route_match, StatusChecker $status_checker) { $this->pendingUpdatesValidator = $pending_updates_validator; $this->stateService = $state; $this->routeMatch = $route_match; + $this->statusChecker = $status_checker; } /** @@ -58,7 +69,8 @@ final class UpdateController extends ControllerBase { return new static( $container->get('package_manager.validator.pending_updates'), $container->get('state'), - $container->get('current_route_match') + $container->get('current_route_match'), + $container->get('automatic_updates.status_checker') ); } @@ -76,6 +88,7 @@ final class UpdateController extends ControllerBase { * A redirect to the appropriate destination. */ public function onFinish(Request $request): RedirectResponse { + $this->statusChecker->run(); if ($this->pendingUpdatesValidator->updatesExist()) { $message = $this->t('Please apply database updates to complete the update process.'); $url = Url::fromRoute('system.db_update'); diff --git a/tests/src/Functional/UpdaterFormTest.php b/tests/src/Functional/UpdaterFormTest.php index ad6f42d3c77d6310969437ee328f6a9873c38c7d..722d5e99885a8d1367ce4ebabc7aef8707127d2f 100644 --- a/tests/src/Functional/UpdaterFormTest.php +++ b/tests/src/Functional/UpdaterFormTest.php @@ -722,6 +722,74 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase { $this->assertNotSame($pre_apply_time, $post_apply_time); } + /** + * Data provider for testStatusCheckerRunAfterUpdate(). + * + * @return bool[][] + * The test cases. + */ + public function providerStatusCheckerRunAfterUpdate(): array { + return [ + 'has database updates' => [TRUE], + 'does not have database updates' => [FALSE], + ]; + } + + /** + * Tests status checks are run after an update. + * + * @param bool $has_database_updates + * Whether the site has database updates or not. + * + * @dataProvider providerStatusCheckerRunAfterUpdate + */ + public function testStatusCheckerRunAfterUpdate(bool $has_database_updates) { + $assert_session = $this->assertSession(); + $this->setCoreVersion('9.8.0'); + $this->checkForUpdates(); + $page = $this->getSession()->getPage(); + // Navigate to the automatic updates form. + $this->drupalGet('/admin/modules/update'); + Stager::setFixturePath(__DIR__ . '/../../fixtures/drupal-9.8.1-installed'); + $page->pressButton('Update to 9.8.1'); + $this->checkForMetaRefresh(); + $this->assertUpdateStagedTimes(1); + $this->assertUpdateReady('9.8.1'); + // Set an error before completing the update. This error should be visible + // on admin pages after completing the update without having to explicitly + // run the status checks. + TestSubscriber1::setTestResult([ValidationResult::createError(['Error before continue.'])], StatusCheckEvent::class); + if ($has_database_updates) { + // Simulate a staged database update in the automatic_updates_test module. + // We must do this after the update has started, because the pending + // updates validator will prevent an update from starting. + $this->container->get('state')->set('automatic_updates_test.new_update', TRUE); + $page->pressButton('Continue'); + $this->checkForMetaRefresh(); + $this->assertSession()->addressEquals('/update.php'); + $assert_session->pageTextNotContains('Possible database updates have been detected in the following extension'); + $assert_session->pageTextContainsOnce('Please apply database updates to complete the update process.'); + $page->clickLink('Continue'); + // @see automatic_updates_update_1191934() + $assert_session->pageTextContains('Dynamic automatic_updates_update_1191934'); + $page->clickLink('Apply pending updates'); + $this->checkForMetaRefresh(); + $assert_session->pageTextContains('Updates were attempted.'); + } + else { + $page->pressButton('Continue'); + $this->checkForMetaRefresh(); + $assert_session->addressEquals('/admin/reports/updates'); + $assert_session->pageTextContainsOnce('Update complete!'); + } + // Status checks should display errors on admin page. + $this->drupalGet('/admin'); + // Confirm that the status checks were run and the new error is displayed. + $assert_session->statusMessageContains('Error before continue.', 'error'); + $assert_session->statusMessageContains(static::$errorsExplanation, 'error'); + $assert_session->pageTextNotContains('Your site has not recently run an update readiness check. Run readiness checks now.'); + } + /** * Data provider for testUpdateCompleteMessage(). *