From f4a868012ee17384e9f7635831a654b6ba26e7fd Mon Sep 17 00:00:00 2001
From: omkar podey <58183-omkar.podey@users.noreply.drupalcode.org>
Date: Fri, 9 Dec 2022 02:35:52 +0000
Subject: [PATCH] Issue #3321256 by omkar.podey, tedbow, Wim Leers: Fix race
 condition in \Drupal\automatic_updates_test_cron\Enabler

---
 automatic_updates.install                     | 32 ++++++++++++++-
 .../Validator/UpdateReleaseValidatorTest.php  |  5 +++
 config/install/automatic_updates.settings.yml |  2 +-
 src/CronUpdater.php                           | 12 ------
 .../automatic_updates_test_cron.module        |  4 ++
 .../automatic_updates_test_cron.services.yml  |  5 ---
 .../src/Enabler.php                           | 39 -------------------
 tests/src/Build/UpdateTestBase.php            |  7 +++-
 .../AutomaticUpdatesFunctionalTestBase.php    |  5 ++-
 tests/src/Functional/StatusCheckTest.php      | 11 ++++--
 tests/src/Functional/UpdatePathTest.php       |  5 +++
 .../Kernel/AutomaticUpdatesKernelTestBase.php | 20 +++++++---
 tests/src/Kernel/CronUpdaterTest.php          | 19 +++------
 .../StatusCheckFailureEmailTest.php           |  2 +
 .../Kernel/StatusCheck/StatusCheckerTest.php  |  6 +++
 .../ValidationResultDisplayTraitTest.php      |  5 +++
 16 files changed, 94 insertions(+), 85 deletions(-)
 delete mode 100644 tests/modules/automatic_updates_test_cron/automatic_updates_test_cron.services.yml
 delete mode 100644 tests/modules/automatic_updates_test_cron/src/Enabler.php

diff --git a/automatic_updates.install b/automatic_updates.install
index 20e7e83776..c0aadef4dc 100644
--- a/automatic_updates.install
+++ b/automatic_updates.install
@@ -7,7 +7,9 @@
 
 declare(strict_types = 1);
 
+use Drupal\automatic_updates\CronUpdater;
 use Drupal\automatic_updates\Validation\StatusCheckRequirements;
+use Drupal\system\SystemManager;
 
 /**
  * Implements hook_uninstall().
@@ -24,7 +26,19 @@ function automatic_updates_requirements($phase) {
     // Check that site is ready to perform automatic updates.
     /** @var \Drupal\automatic_updates\Validation\StatusCheckRequirements $status_check_requirement */
     $status_check_requirement = \Drupal::classResolver(StatusCheckRequirements::class);
-    return $status_check_requirement->getRequirements();
+    $requirements = $status_check_requirement->getRequirements();
+
+    // Check that site has cron updates enabled or not.
+    // @todo Remove in https://www.drupal.org/project/automatic_updates/issues/3284443
+    if (\Drupal::configFactory()->get('automatic_updates.settings')->get('cron') !== CronUpdater::DISABLED) {
+      $requirements['automatic_updates_cron'] = [
+        'title' => t('Cron installs updates automatically'),
+        'severity' => SystemManager::REQUIREMENT_WARNING,
+        'value' => t('Enabled. This is NOT an officially supported feature of the Automatic Updates module at this time. Use at your own risk.'),
+      ];
+    }
+
+    return $requirements;
   }
 }
 
@@ -38,3 +52,19 @@ function automatic_updates_update_9001(): void {
   $key_value->rename('readiness_validation_last_run', 'status_check_last_run');
   $key_value->rename('readiness_check_timestamp', 'status_check_timestamp');
 }
+
+/**
+ * Sets cron setting to \Drupal\automatic_updates\CronUpdater::DISABLED.
+ *
+ * This is necessary until TUF support is available on drupal.org. This used to
+ * be hardcoded, but that caused complex race conditions in tests. Hence, it is
+ * now just configured.
+ *
+ * @see automatic_updates_requirements()
+ * @see https://www.drupal.org/project/automatic_updates/issues/3284443
+ */
+function automatic_updates_update_9002(): void {
+  /** @var \Drupal\Core\Config\Config $config */
+  $config = \Drupal::service('config.factory')->getEditable('automatic_updates.settings');
+  $config->set('cron', CronUpdater::DISABLED)->save();
+}
diff --git a/automatic_updates_extensions/tests/src/Kernel/Validator/UpdateReleaseValidatorTest.php b/automatic_updates_extensions/tests/src/Kernel/Validator/UpdateReleaseValidatorTest.php
index c075b8e389..fa5bdb51a6 100644
--- a/automatic_updates_extensions/tests/src/Kernel/Validator/UpdateReleaseValidatorTest.php
+++ b/automatic_updates_extensions/tests/src/Kernel/Validator/UpdateReleaseValidatorTest.php
@@ -16,6 +16,11 @@ use Drupal\Tests\automatic_updates_extensions\Kernel\AutomaticUpdatesExtensionsK
  */
 class UpdateReleaseValidatorTest extends AutomaticUpdatesExtensionsKernelTestBase {
 
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['automatic_updates'];
+
   /**
    * Data provider for testPreCreateException().
    *
diff --git a/config/install/automatic_updates.settings.yml b/config/install/automatic_updates.settings.yml
index 31043e05ce..2649653a1e 100644
--- a/config/install/automatic_updates.settings.yml
+++ b/config/install/automatic_updates.settings.yml
@@ -1,3 +1,3 @@
-cron: security
+cron: disable
 allow_core_minor_updates: false
 status_check_mail: errors_only
diff --git a/src/CronUpdater.php b/src/CronUpdater.php
index c7b2836cd6..c46932b70c 100644
--- a/src/CronUpdater.php
+++ b/src/CronUpdater.php
@@ -26,15 +26,6 @@ use Symfony\Component\HttpFoundation\Response;
  */
 class CronUpdater extends Updater {
 
-  /**
-   * Whether or not cron updates are hard-disabled.
-   *
-   * @var bool
-   *
-   * @todo Remove this when TUF integration is stable.
-   */
-  private static $disabled = TRUE;
-
   /**
    * All automatic updates are disabled.
    *
@@ -337,9 +328,6 @@ class CronUpdater extends Updater {
    *     during cron.
    */
   final public function getMode(): string {
-    if (self::$disabled) {
-      return static::DISABLED;
-    }
     $mode = $this->configFactory->get('automatic_updates.settings')->get('cron');
     return $mode ?: CronUpdater::SECURITY;
   }
diff --git a/tests/modules/automatic_updates_test_cron/automatic_updates_test_cron.module b/tests/modules/automatic_updates_test_cron/automatic_updates_test_cron.module
index af8284e440..2716bb75f6 100644
--- a/tests/modules/automatic_updates_test_cron/automatic_updates_test_cron.module
+++ b/tests/modules/automatic_updates_test_cron/automatic_updates_test_cron.module
@@ -7,6 +7,8 @@
  * @todo Move into automatic_updates when TUF integration is stable.
  */
 
+declare(strict_types=1);
+
 use Drupal\package_manager\ProjectInfo;
 use Drupal\automatic_updates\CronUpdater;
 use Drupal\Core\Extension\ExtensionVersion;
@@ -15,6 +17,8 @@ use Drupal\update\ProjectSecurityData;
 
 /**
  * Implements hook_form_FORM_ID_alter() for 'update_settings' form.
+ *
+ * @todo Move to automatic_updates in https://www.drupal.org/i/3322361
  */
 function automatic_updates_test_cron_form_update_settings_alter(array &$form, FormStateInterface $form_state, string $form_id) {
   $project_info = new ProjectInfo('drupal');
diff --git a/tests/modules/automatic_updates_test_cron/automatic_updates_test_cron.services.yml b/tests/modules/automatic_updates_test_cron/automatic_updates_test_cron.services.yml
deleted file mode 100644
index b8e5c8bddd..0000000000
--- a/tests/modules/automatic_updates_test_cron/automatic_updates_test_cron.services.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-services:
-  automatic_updates_test_cron.enabler:
-    class: Drupal\automatic_updates_test_cron\Enabler
-    tags:
-      - { name: event_subscriber }
diff --git a/tests/modules/automatic_updates_test_cron/src/Enabler.php b/tests/modules/automatic_updates_test_cron/src/Enabler.php
deleted file mode 100644
index da799f7e3e..0000000000
--- a/tests/modules/automatic_updates_test_cron/src/Enabler.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-declare(strict_types = 1);
-
-namespace Drupal\automatic_updates_test_cron;
-
-use Drupal\automatic_updates\CronUpdater;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use Symfony\Component\HttpKernel\KernelEvents;
-
-/**
- * Enables automatic updates during cron.
- *
- * @todo Remove this when TUF integration is stable.
- */
-class Enabler implements EventSubscriberInterface {
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function getSubscribedEvents(): array {
-    return [
-      KernelEvents::REQUEST => 'enableCron',
-    ];
-  }
-
-  /**
-   * Enables automatic updates during cron.
-   */
-  public function enableCron(): void {
-    if (class_exists(CronUpdater::class)) {
-      $reflector = new \ReflectionClass(CronUpdater::class);
-      $reflector = $reflector->getProperty('disabled');
-      $reflector->setAccessible(TRUE);
-      $reflector->setValue(NULL, FALSE);
-    }
-  }
-
-}
diff --git a/tests/src/Build/UpdateTestBase.php b/tests/src/Build/UpdateTestBase.php
index ddafd13da5..839b3f5553 100644
--- a/tests/src/Build/UpdateTestBase.php
+++ b/tests/src/Build/UpdateTestBase.php
@@ -19,12 +19,15 @@ abstract class UpdateTestBase extends TemplateProjectTestBase {
    */
   protected function createTestProject(string $template): void {
     parent::createTestProject($template);
-
+    // @todo Remove in https://www.drupal.org/project/automatic_updates/issues/3284443
+    $code = <<<END
+\$config['automatic_updates.settings']['cron'] = 'security';
+END;
+    $this->writeSettings($code);
     // Install Automatic Updates, and other modules needed for testing.
     $this->installModules([
       'automatic_updates',
       'automatic_updates_test_api',
-      'automatic_updates_test_cron',
     ]);
   }
 
diff --git a/tests/src/Functional/AutomaticUpdatesFunctionalTestBase.php b/tests/src/Functional/AutomaticUpdatesFunctionalTestBase.php
index fdd355e03d..5bf4eeb488 100644
--- a/tests/src/Functional/AutomaticUpdatesFunctionalTestBase.php
+++ b/tests/src/Functional/AutomaticUpdatesFunctionalTestBase.php
@@ -4,6 +4,7 @@ declare(strict_types = 1);
 
 namespace Drupal\Tests\automatic_updates\Functional;
 
+use Drupal\automatic_updates\CronUpdater;
 use Drupal\Core\Site\Settings;
 use Drupal\package_manager_bypass\Beginner;
 use Drupal\package_manager_bypass\Stager;
@@ -23,7 +24,7 @@ abstract class AutomaticUpdatesFunctionalTestBase extends BrowserTestBase {
    * {@inheritdoc}
    */
   protected static $modules = [
-    'automatic_updates_test_cron',
+    'automatic_updates',
     'automatic_updates_test_disable_validators',
     'package_manager_bypass',
   ];
@@ -54,6 +55,8 @@ abstract class AutomaticUpdatesFunctionalTestBase extends BrowserTestBase {
     parent::setUp();
     $this->disableValidators($this->disableValidators);
     $this->useFixtureDirectoryAsActive(__DIR__ . '/../../../package_manager/tests/fixtures/fake_site');
+    // @todo Remove in https://www.drupal.org/project/automatic_updates/issues/3284443
+    $this->config('automatic_updates.settings')->set('cron', CronUpdater::SECURITY)->save();
   }
 
   /**
diff --git a/tests/src/Functional/StatusCheckTest.php b/tests/src/Functional/StatusCheckTest.php
index 84db352a40..bfa9e3c59c 100644
--- a/tests/src/Functional/StatusCheckTest.php
+++ b/tests/src/Functional/StatusCheckTest.php
@@ -5,6 +5,7 @@ declare(strict_types = 1);
 namespace Drupal\Tests\automatic_updates\Functional;
 
 use Behat\Mink\Element\NodeElement;
+use Drupal\automatic_updates\CronUpdater;
 use Drupal\automatic_updates\StatusCheckMailer;
 use Drupal\automatic_updates_test\Datetime\TestTime;
 use Drupal\automatic_updates_test\EventSubscriber\TestSubscriber1;
@@ -330,10 +331,9 @@ class StatusCheckTest extends AutomaticUpdatesFunctionalTestBase {
 
     // Confirm status check messages are not displayed when cron updates are
     // disabled.
-    $this->drupalGet(Url::fromRoute('update.settings'));
-    $edit['automatic_updates_cron'] = 'disable';
-    $this->submitForm($edit, 'Save configuration');
-    $this->drupalGet(Url::fromRoute($admin_route));
+    $this->config('automatic_updates.settings')->set('cron', CronUpdater::DISABLED)->save();
+    $this->drupalGet('admin/structure');
+    $this->checkForMetaRefresh();
     $assert->pageTextNotContains(static::$warningsExplanation);
     $assert->pageTextNotContains($expected_results[0]->getMessages()[0]);
   }
@@ -344,6 +344,7 @@ class StatusCheckTest extends AutomaticUpdatesFunctionalTestBase {
   public function testStatusCheckAfterInstall(): void {
     $assert = $this->assertSession();
     $this->drupalLogin($this->checkerRunnerUser);
+    $this->container->get('module_installer')->uninstall(['automatic_updates']);
 
     $this->drupalGet('admin/reports/status');
     $assert->pageTextNotContains('Update readiness checks');
@@ -351,6 +352,8 @@ class StatusCheckTest extends AutomaticUpdatesFunctionalTestBase {
     // We have to install the automatic_updates_test module because it provides
     // the functionality to retrieve our fake release history metadata.
     $this->container->get('module_installer')->install(['automatic_updates', 'automatic_updates_test']);
+    // @todo Remove in https://www.drupal.org/project/automatic_updates/issues/3284443
+    $this->config('automatic_updates.settings')->set('cron', CronUpdater::SECURITY)->save();
     $this->drupalGet('admin/reports/status');
     $this->assertNoErrors(TRUE);
 
diff --git a/tests/src/Functional/UpdatePathTest.php b/tests/src/Functional/UpdatePathTest.php
index 2f5139acb8..b697f42c18 100644
--- a/tests/src/Functional/UpdatePathTest.php
+++ b/tests/src/Functional/UpdatePathTest.php
@@ -4,6 +4,7 @@ declare(strict_types = 1);
 
 namespace Drupal\Tests\automatic_updates\Functional;
 
+use Drupal\automatic_updates\CronUpdater;
 use Drupal\automatic_updates\StatusCheckMailer;
 use Drupal\FunctionalTests\Update\UpdatePathTestBase;
 
@@ -55,8 +56,12 @@ class UpdatePathTest extends UpdatePathTestBase {
     }
     $this->assertEmpty($this->config('automatic_updates.settings')->get('status_check_mail'));
 
+    $this->assertSame(CronUpdater::SECURITY, $this->config('automatic_updates.settings')->get('cron'));
+
     $this->runUpdates();
 
+    $this->assertSame(CronUpdater::DISABLED, $this->config('automatic_updates.settings')->get('cron'));
+
     // TRICKY: we do expect `readiness_validation_last_run` to have been renamed
     // to `status_check_last_run`, but then
     // automatic_updates_post_update_create_status_check_mail_config() should
diff --git a/tests/src/Kernel/AutomaticUpdatesKernelTestBase.php b/tests/src/Kernel/AutomaticUpdatesKernelTestBase.php
index ad69a1c284..aa8670c2af 100644
--- a/tests/src/Kernel/AutomaticUpdatesKernelTestBase.php
+++ b/tests/src/Kernel/AutomaticUpdatesKernelTestBase.php
@@ -23,10 +23,18 @@ abstract class AutomaticUpdatesKernelTestBase extends PackageManagerKernelTestBa
 
   /**
    * {@inheritdoc}
+   *
+   * TRICKY: due to the way that automatic_updates forcibly disables cron-based
+   * updating for the end user, we need to override the current default
+   * configuration BEFORE the module is installed. This triggers config schema
+   * exceptions. Since none of these tests are interacting with configuration
+   * anyway, this is a reasonable temporary workaround.
+   *
+   * @see ::setUp()
+   * @see https://www.drupal.org/project/automatic_updates/issues/3284443
+   * @todo Remove in https://www.drupal.org/project/automatic_updates/issues/3284443
    */
-  protected static $modules = [
-    'automatic_updates_test_cron',
-  ];
+  protected $strictConfigSchema = FALSE;
 
   /**
    * {@inheritdoc}
@@ -46,6 +54,9 @@ abstract class AutomaticUpdatesKernelTestBase extends PackageManagerKernelTestBa
     $this->disableValidators[] = 'automatic_updates.validator.xdebug';
     $this->disableValidators[] = 'package_manager.validator.xdebug';
     parent::setUp();
+    // Enable cron updates, which will eventually be the default.
+    // @todo Remove in https://www.drupal.org/project/automatic_updates/issues/3284443
+    $this->config('automatic_updates.settings')->set('cron', CronUpdater::SECURITY)->save();
 
     // By default, pretend we're running Drupal core 9.8.0 and a non-security
     // update to 9.8.1 is available.
@@ -56,9 +67,6 @@ abstract class AutomaticUpdatesKernelTestBase extends PackageManagerKernelTestBa
     // from a sane state.
     // @see \Drupal\automatic_updates\Validator\CronFrequencyValidator
     $this->container->get('state')->set('system.cron_last', time());
-
-    // @todo Remove this when TUF integration is stable.
-    $this->container->get('automatic_updates_test_cron.enabler')->enableCron();
   }
 
   /**
diff --git a/tests/src/Kernel/CronUpdaterTest.php b/tests/src/Kernel/CronUpdaterTest.php
index 9078970cf6..8a6b30252c 100644
--- a/tests/src/Kernel/CronUpdaterTest.php
+++ b/tests/src/Kernel/CronUpdaterTest.php
@@ -7,7 +7,6 @@ namespace Drupal\Tests\automatic_updates\Kernel;
 use Drupal\automatic_updates\CronUpdater;
 use Drupal\automatic_updates_test\EventSubscriber\TestSubscriber1;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
-use Drupal\Core\Form\FormState;
 use Drupal\Core\Logger\RfcLogLevel;
 use Drupal\Core\Url;
 use Drupal\package_manager\Event\PostApplyEvent;
@@ -24,9 +23,8 @@ use Drupal\package_manager_bypass\Committer;
 use Drupal\Tests\automatic_updates\Traits\EmailNotificationsTestTrait;
 use Drupal\Tests\package_manager\Traits\PackageManagerBypassTestTrait;
 use Drupal\Tests\user\Traits\UserCreationTrait;
-use Drupal\update\UpdateSettingsForm;
-use ColinODell\PsrTestLogger\TestLogger;
 use Prophecy\Argument;
+use ColinODell\PsrTestLogger\TestLogger;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
@@ -53,7 +51,7 @@ class CronUpdaterTest extends AutomaticUpdatesKernelTestBase {
   /**
    * The test logger.
    *
-   * @var ColinODell\PsrTestLogger\TestLogger
+   * @var \ColinODell\PsrTestLogger\TestLogger
    */
   private $logger;
 
@@ -151,16 +149,7 @@ class CronUpdaterTest extends AutomaticUpdatesKernelTestBase {
     $this->setCoreVersion('9.8.0');
     update_get_available(TRUE);
 
-    // Submit the configuration form programmatically, to prove our alterations
-    // work as expected.
-    $form_builder = $this->container->get('form_builder');
-    $form_state = new FormState();
-    $form = $form_builder->buildForm(UpdateSettingsForm::class, $form_state);
-    // Ensure that the version ranges in the setting's description, which are
-    // computed dynamically, look correct.
-    $this->assertStringContainsString('Automatic updates are only supported for 9.8.x versions of Drupal core. Drupal 9.8 will receive security updates until 9.10.0 is released.', (string) $form['automatic_updates_cron']['#description']);
-    $form_state->setValue('automatic_updates_cron', $setting);
-    $form_builder->submitForm(UpdateSettingsForm::class, $form_state);
+    $this->config('automatic_updates.settings')->set('cron', $setting)->save();
 
     // Since we're just trying to ensure that all of Package Manager's services
     // are called as expected, disable validation by replacing the event
@@ -258,6 +247,8 @@ class CronUpdaterTest extends AutomaticUpdatesKernelTestBase {
    */
   public function testStageDestroyedOnError(string $event_class, string $exception_class): void {
     $this->installConfig('automatic_updates');
+    // @todo Remove in https://www.drupal.org/project/automatic_updates/issues/3284443
+    $this->config('automatic_updates.settings')->set('cron', CronUpdater::SECURITY)->save();
     $this->setCoreVersion('9.8.0');
     // Ensure that there is a security release to which we should update.
     $this->setReleaseMetadata([
diff --git a/tests/src/Kernel/StatusCheck/StatusCheckFailureEmailTest.php b/tests/src/Kernel/StatusCheck/StatusCheckFailureEmailTest.php
index b6e37b0173..61e631f7ff 100644
--- a/tests/src/Kernel/StatusCheck/StatusCheckFailureEmailTest.php
+++ b/tests/src/Kernel/StatusCheck/StatusCheckFailureEmailTest.php
@@ -53,6 +53,8 @@ class StatusCheckFailureEmailTest extends AutomaticUpdatesKernelTestBase {
     $this->installSchema('user', ['users_data']);
 
     $this->installConfig('automatic_updates');
+    // @todo Remove in https://www.drupal.org/project/automatic_updates/issues/3284443
+    $this->config('automatic_updates.settings')->set('cron', CronUpdater::SECURITY)->save();
     $this->setUpEmailRecipients();
 
     // Allow stored available update data to live for a very, very long time.
diff --git a/tests/src/Kernel/StatusCheck/StatusCheckerTest.php b/tests/src/Kernel/StatusCheck/StatusCheckerTest.php
index 4199491448..c08a0d3b60 100644
--- a/tests/src/Kernel/StatusCheck/StatusCheckerTest.php
+++ b/tests/src/Kernel/StatusCheck/StatusCheckerTest.php
@@ -36,6 +36,12 @@ class StatusCheckerTest extends AutomaticUpdatesKernelTestBase {
     $this->setCoreVersion('9.8.2');
     $this->installEntitySchema('user');
     $this->installSchema('user', ['users_data']);
+
+    // Undoes the override in parent::setUp(), to allow the module to be
+    // installed, which every other test methods in this class does. Without
+    // this \Drupal\Core\Config\PreExistingConfigException is thrown.
+    // @todo Remove in https://www.drupal.org/project/automatic_updates/issues/3284443
+    $this->config('automatic_updates.settings')->delete();
   }
 
   /**
diff --git a/tests/src/Kernel/ValidationResultDisplayTraitTest.php b/tests/src/Kernel/ValidationResultDisplayTraitTest.php
index ee2b4bfdbe..7bdbec570d 100644
--- a/tests/src/Kernel/ValidationResultDisplayTraitTest.php
+++ b/tests/src/Kernel/ValidationResultDisplayTraitTest.php
@@ -20,6 +20,11 @@ class ValidationResultDisplayTraitTest extends AutomaticUpdatesKernelTestBase {
   use ValidationResultDisplayTrait;
   use StringTranslationTrait;
 
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['automatic_updates'];
+
   /**
    * @covers ::displayResults
    */
-- 
GitLab