diff --git a/core/modules/auto_updates/auto_updates.install b/core/modules/auto_updates/auto_updates.install
index 81c78c35b0..6799a1c510 100644
--- a/core/modules/auto_updates/auto_updates.install
+++ b/core/modules/auto_updates/auto_updates.install
@@ -17,53 +17,54 @@ function auto_updates_requirements($phase) {
return [];
}
- $requirements = [];
/** @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManagerInterface $checker_manager */
$checker_manager = \Drupal::service('auto_updates.readiness_checker');
if (!$checker_manager->isEnabled()) {
return [];
}
-
+ $requirements['auto_updates_readiness']['title'] = t('Update readiness checks');
+ $readiness_check = Url::fromRoute('auto_updates.update_readiness');
$last_check_timestamp = $checker_manager->timestamp();
- $requirements['auto_updates_readiness'] = [
- 'title' => t('Update readiness checks'),
- 'severity' => REQUIREMENT_OK,
- 'value' => t('Your site is ready to for automatic updates.', [':readiness_checks' => 'https://www.drupal.org/docs/8/update/automatic-updates#readiness-checks']),
- ];
- $error_results = $checker_manager->getResults(ReadinessCheckerManagerInterface::ERROR);
- $warning_results = $checker_manager->getResults(ReadinessCheckerManagerInterface::WARNING);
- $checker_results = array_merge($error_results, $warning_results);
- if (!empty($checker_results)) {
- $requirements['auto_updates_readiness']['severity'] = $error_results ? REQUIREMENT_ERROR : REQUIREMENT_WARNING;
- $requirements['auto_updates_readiness']['value'] = new PluralTranslatableMarkup(count($checker_results), '@count check failed:', '@count checks failed:');
- $requirements['auto_updates_readiness']['description'] = [
- '#theme' => 'item_list',
- '#items' => $checker_results,
- ];
+ if ($last_check_timestamp === NULL) {
+ $requirements['auto_updates_readiness']['severity'] = REQUIREMENT_ERROR;
+ $requirements['auto_updates_readiness']['value'] = t('Your site has never checked if it is ready to apply automatic updates.', ['@readiness_checks' => 'https://www.drupal.org/docs/8/update/automatic-updates#readiness-checks']);
+ if ($readiness_check->access()) {
+ $requirements['auto_updates_readiness']['description'] = t('Run readiness checks manually.', [
+ '@link' => $readiness_check->toString(),
+ ]);
+ }
}
- if ($last_check_timestamp === NULL || \Drupal::time()->getRequestTime() > $last_check_timestamp + ReadinessCheckerManagerInterface::LAST_CHECKED_WARNING) {
+ elseif (\Drupal::time()->getRequestTime() > $last_check_timestamp + ReadinessCheckerManagerInterface::LAST_CHECKED_WARNING) {
$requirements['auto_updates_readiness']['severity'] = REQUIREMENT_ERROR;
- $readiness_check = Url::fromRoute('auto_updates.update_readiness');
$time_ago = \Drupal::service('date.formatter')->formatTimeDiffSince($last_check_timestamp);
- if ($last_check_timestamp === NULL) {
- $requirements['auto_updates_readiness']['value'] = t('Your site has never checked if it is ready to apply automatic updates.', ['@readiness_checks' => 'https://www.drupal.org/docs/8/update/automatic-updates#readiness-checks']);
+ $requirements['auto_updates_readiness']['value'] = t('Your site has not recently checked if it is ready to apply automatic updates.', ['@readiness_checks' => 'https://www.drupal.org/docs/8/update/automatic-updates#readiness-checks']);
+ if ($readiness_check->access()) {
+ $requirements['auto_updates_readiness']['description'] = t('Readiness checks were last run @time ago. Run readiness checks manually.', [
+ '@time' => $time_ago,
+ '@link' => $readiness_check->toString(),
+ ]);
}
else {
- $requirements['auto_updates_readiness']['value'] = t('Your site has not recently checked if it is ready to apply automatic updates.', ['@readiness_checks' => 'https://www.drupal.org/docs/8/update/automatic-updates#readiness-checks']);
$requirements['auto_updates_readiness']['description'] = t('Readiness checks were last run @time ago.', ['@time' => $time_ago]);
}
- if ($readiness_check->access()) {
- if ($last_check_timestamp === NULL) {
- $requirements['auto_updates_readiness']['description'] = t('Run readiness checks manually.', [
- '@link' => $readiness_check->toString(),
- ]);
- }
- else {
- $requirements['auto_updates_readiness']['description'] = t('Last run @time ago. Run readiness checks manually.', [
- '@time' => $time_ago,
- '@link' => $readiness_check->toString(),
- ]);
- }
+ }
+ else {
+ $error_results = $checker_manager->getResults(ReadinessCheckerManagerInterface::ERROR);
+ $warning_results = $checker_manager->getResults(ReadinessCheckerManagerInterface::WARNING);
+ $checker_results = array_merge($error_results, $warning_results);
+ if (!empty($checker_results)) {
+ $requirements['auto_updates_readiness']['severity'] = $error_results ? REQUIREMENT_ERROR : REQUIREMENT_WARNING;
+ $requirements['auto_updates_readiness']['value'] = new PluralTranslatableMarkup(count($checker_results), '@count check failed:', '@count checks failed:');
+ $requirements['auto_updates_readiness']['description'] = [
+ '#theme' => 'item_list',
+ '#items' => $checker_results,
+ ];
+ }
+ else {
+ $requirements['auto_updates_readiness'] += [
+ 'severity' => REQUIREMENT_OK,
+ 'value' => t('Your site is ready for automatic updates.', [':readiness_checks' => 'https://www.drupal.org/docs/8/update/automatic-updates#readiness-checks']),
+ ];
}
}
return $requirements;
diff --git a/core/modules/auto_updates/tests/src/Functional/ReadinessCheckerTest.php b/core/modules/auto_updates/tests/src/Functional/ReadinessCheckerTest.php
index 79a01454d2..99d81d968e 100644
--- a/core/modules/auto_updates/tests/src/Functional/ReadinessCheckerTest.php
+++ b/core/modules/auto_updates/tests/src/Functional/ReadinessCheckerTest.php
@@ -18,14 +18,32 @@ class ReadinessCheckerTest extends BrowserTestBase {
*/
protected $defaultTheme = 'stark';
+ /**
+ * A user who can view the status report.
+ *
+ * @var bool|\Drupal\user\Entity\User
+ */
+ protected $reportViewerUser;
+
+ /**
+ * A user how can view the status report and run readiness checkers.
+ *
+ * @var bool|\Drupal\user\Entity\User
+ */
+ protected $checkerRunnerUser;
+
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
- $this->drupalLogin($this->createUser([
+ $this->reportViewerUser = $this->createUser([
+ 'administer site configuration',
+ ]);
+ $this->checkerRunnerUser = $this->createUser([
'administer site configuration',
- ]));
+ 'administer software updates',
+ ]);
}
/**
@@ -33,49 +51,50 @@ protected function setUp(): void {
*/
public function testReadinessChecksStatusReport() {
$assert = $this->assertSession();
+
$this->container->get('module_installer')->uninstall(['automated_cron']);
$this->container->get('module_installer')->install(['auto_updates', 'auto_updates_test']);
- $this->drupalGet('admin/reports/status');
- $assert->pageTextContains('Update readiness checks');
- $assert->pageTextContains('Your site is ready to for automatic updates.');
- $assert->pageTextNotContains('Run readiness checks manually');
- $this->drupalGet('admin/config/auto_updates/readiness');
- $assert->pageTextContains("Access denied");
+
+ // If the the site is ready for updates the users will see the same output
+ // regardless of the user has permission to run updates.
+ foreach ([$this->reportViewerUser, $this->checkerRunnerUser] as $user) {
+ $this->drupalLogin($user);
+ $this->drupalGet('admin/reports/status');
+ $this->assertReadinessReportMatches('Your site is ready for automatic updates.');
+ }
// Confirm a user without the permission to run readiness checks does not
// have a link to run the checks when the checks need to run again.
$this->setFakeTime('+2 days');
+ $this->drupalLogin($this->reportViewerUser);
$this->drupalGet('admin/reports/status');
- $assert->pageTextNotContains('Your site is ready to for automatic updates.');
- $assert->pageTextNotContains('Run readiness checks manually');
- $assert->pageTextContains('Your site has not recently checked if it is ready to apply automatic updates');
- $this->assertStringContainsString('Readiness checks were last run', $this->getReadinessReportText());
+ $this->assertReadinessReportMatches('Your site has not recently checked if it is ready to apply automatic updates. Readiness checks were last run %s ago.');
// Confirm a user with the permission to run readiness checks does have a
// link to run the checks when the checks need to run again.
- $this->drupalLogin($this->createUser([
- 'administer site configuration',
- 'administer software updates',
- ]));
+ $this->drupalLogin($this->checkerRunnerUser);
$this->drupalGet('admin/reports/status');
- $assert->pageTextContains('Update readiness checks');
- $assert->pageTextContains('Your site has not recently checked if it is ready to apply automatic updates');
- $this->assertStringContainsString('Last run', $this->getReadinessReportText());
- $assert->pageTextNotContains('Your site is ready to for automatic updates.');
- $this->container->get('state')->set(TestChecker::STATE_KEY, 'OMG 😱. Your site is not ready.');
+ $this->assertReadinessReportMatches('Your site has not recently checked if it is ready to apply automatic updates.'
+ . ' Readiness checks were last run %s ago. Run readiness checks manually.');
+ $this->container->get('state')->set(TestChecker::STATE_KEY, 'OMG 🚒. Your server is on 🔥!');
+
+ // Run the readiness checks.
$this->clickLink('Run readiness checks');
- $assert->pageTextNotContains("Access denied");
- $assert->pageTextContains('Your site is currently failing readiness checks for automatic updates. It cannot be automatically updated until further action is performed:');
- $assert->pageTextContains('OMG 😱. Your site is not ready.');
// @todo If coming from the status report page should you be redirected there?
// This is how 'Run cron" works.
$assert->addressEquals('/admin/config/auto_updates');
-
- // Confirm the error is displayed on the status report page.
- $this->drupalGet('admin/reports/status');
- $assert->pageTextContains('Update readiness checks');
- $assert->pageTextContains('OMG 😱. Your site is not ready.');
- $this->assertStringContainsString('1 check failed', $this->getReadinessReportText());
+ $assert->pageTextNotContains("Access denied");
+ $assert->pageTextContains('Your site is currently failing readiness checks for automatic updates. It cannot be automatically updated until further action is performed:');
+ $assert->pageTextContains('OMG 🚒. Your server is on 🔥!');
+
+ foreach ([$this->reportViewerUser, $this->checkerRunnerUser] as $user) {
+ $this->drupalLogin($user);
+ // Confirm the error is displayed on the status report page.
+ $this->drupalGet('admin/reports/status');
+ $this->assertReadinessReportMatches('1 check failed: OMG 🚒. Your server is on 🔥!');
+ // @todo Should always show when the checks were last run and a link to
+ // run when there is an error?
+ }
}
/**
@@ -83,47 +102,35 @@ public function testReadinessChecksStatusReport() {
*/
public function testReadinessCheckAfterInstall() {
$assert = $this->assertSession();
- $this->drupalLogin($this->createUser([
- 'administer site configuration',
- 'administer software updates',
- ]));
+ $this->drupalLogin($this->checkerRunnerUser);
$this->drupalGet('admin/reports/status');
$assert->pageTextNotContains('Update readiness checks');
$this->container->get('module_installer')->install(['auto_updates']);
$this->drupalGet('admin/reports/status');
- $assert->pageTextContains('Update readiness checks');
- $assert->pageTextContains('Your site is ready to for automatic updates.');
+ $this->assertReadinessReportMatches('Your site is ready for automatic updates.');
- $this->container->get('state')->set(TestChecker::STATE_KEY, 'OMG 😱. Your site is not ready.');
+ $this->container->get('state')->set(TestChecker::STATE_KEY, '😿Oh no! A hacker now owns your files!');
$this->container->get('module_installer')->install(['auto_updates_test']);
$this->drupalGet('admin/reports/status');
- $assert->pageTextContains('Update readiness checks');
- $assert->pageTextContains('OMG 😱. Your site is not ready.');
- $assert->pageTextNotContains('Your site is ready to for automatic updates.');
+ $this->assertReadinessReportMatches('1 check failed: 😿Oh no! A hacker now owns your files!');
- // Confirm that installing a module that does not provider a new checker
- // does not run the checkers on install.
- $this->container->get('state')->set(TestChecker::STATE_KEY, 'OMG 🙀. Your site is not ready for a DIFFERENT reason');
+ // Confirm that installing a module that does not provide a new checker does
+ // not run the checkers on install.
+ $this->container->get('state')->set(TestChecker::STATE_KEY, 'Security has been compromised. "pass123" was a bad password!');
$this->container->get('module_installer')->install(['color']);
$this->drupalGet('admin/reports/status');
- $assert->pageTextContains('Update readiness checks');
- $assert->pageTextNotContains('OMG 🙀. Your site is not read for a DIFFERENT reason');
- $assert->pageTextContains('OMG 😱. Your site is not ready.');
- $assert->pageTextNotContains('Your site is ready to for automatic updates.');
+ $this->assertReadinessReportMatches('1 check failed: 😿Oh no! A hacker now owns your files!');
// Confirm the new message is displayed after running the checkers manually.
$this->drupalGet('admin/config/auto_updates');
$this->clickLink('run the readiness checks');
- $assert->pageTextContains('OMG 🙀. Your site is not ready for a DIFFERENT reason');
- $assert->pageTextNotContains('OMG 😱. Your site is not ready.');
+ $assert->pageTextContains('Security has been compromised. "pass123" was a bad password!');
+ $assert->pageTextNotContains('😿Oh no! A hacker now owns your files!');
$this->drupalGet('admin/reports/status');
- $assert->pageTextContains('Update readiness checks');
- $assert->pageTextContains('OMG 🙀. Your site is not ready for a DIFFERENT reason');
- $assert->pageTextNotContains('OMG 😱. Your site is not ready.');
- $assert->pageTextNotContains('Your site is ready to for automatic updates.');
+ $this->assertReadinessReportMatches('1 check failed: Security has been compromised. "pass123" was a bad password!');
}
/**
@@ -131,29 +138,31 @@ public function testReadinessCheckAfterInstall() {
*/
public function testCronRun() {
$assert = $this->assertSession();
+ $this->drupalLogin($this->reportViewerUser);
$this->container->get('module_installer')->install(['auto_updates', 'auto_updates_test']);
$this->drupalGet('admin/reports/status');
- $assert->pageTextContains('Update readiness checks');
- $assert->pageTextContains('Your site is ready to for automatic updates.');
+ $this->assertReadinessReportMatches('Your site is ready for automatic updates.');
- $this->container->get('state')->set(TestChecker::STATE_KEY, 'OMG 😱. Your site is not ready.');
+ $this->container->get('state')->set(TestChecker::STATE_KEY, 'OMG 🚒. Your server is on 🔥!');
// Tests that running cron within 1 hour of the checkers running will not
// run them again.
$this->setFakeTime('+30 minutes');
$this->clickLink('Run cron');
- $assert->pageTextContains('Update readiness checks');
- $assert->pageTextNotContains('OMG 😱. Your site is not ready.');
- $assert->pageTextContains('Your site is ready to for automatic updates.');
+ $this->assertReadinessReportMatches('Your site is ready for automatic updates.');
// Tests that running cron after 1 hour of the checkers running will run
// them again.
$this->setFakeTime('+65 minutes');
$this->clickLink('Run cron');
- $assert->pageTextContains('Update readiness checks');
- $assert->pageTextContains('OMG 😱. Your site is not ready.');
- $assert->pageTextNotContains('Your site is ready to for automatic updates.');
+ $this->assertReadinessReportMatches('1 check failed: OMG 🚒. Your server is on 🔥!');
+ // Tests that running cron after 1 hour of the checkers running during cron
+ // will run them again.
+ $this->container->get('state')->set(TestChecker::STATE_KEY, 'OMG 💦. Now your server is filled with water!');
+ $this->setFakeTime('+125 minutes');
+ $this->clickLink('Run cron');
+ $this->assertReadinessReportMatches('1 check failed: OMG 💦. Now your server is filled with water!');
}
/**
@@ -162,22 +171,25 @@ public function testCronRun() {
* @param string $offset
* A date/time offset string.
*/
- private function setFakeTime($offset): void {
+ private function setFakeTime(string $offset): void {
$fake_delay = (new \DateTime())->modify($offset)->format(TestTime::TIME_FORMAT);
$this->container->get('state')->set(TestTime::STATE_KEY, $fake_delay);
}
/**
- * Gets the text on status report page of the readiness report item.
+ * Asserts status report readiness report item matches a format.
*
- * @return string
- * The readiness checks status report text.
+ * @param string $format
+ * The string to match.
*/
- private function getReadinessReportText() {
- return $this->getSession()->getPage()->find(
+ private function assertReadinessReportMatches(string $format): void {
+ // Prefix the expected format with the item title which does not change.
+ $format = "Update readiness checks $format";
+ $text = $this->getSession()->getPage()->find(
'css',
'details.system-status-report__entry:contains("Update readiness checks")'
)->getText();
+ $this->assertStringMatchesFormat($format, $text);
}
}