From 1b07b0a0100518f2d0623929e1041380583a6411 Mon Sep 17 00:00:00 2001
From: "lee.5151" <lee.5151@osu.edu>
Date: Mon, 13 Mar 2023 13:06:22 -0400
Subject: [PATCH] Upgrading drupal/queue_mail (1.4.0 => 1.5.0)

---
 composer.lock                                 | 52 +++++++--------
 vendor/composer/ClassLoader.php               | 12 ++--
 vendor/composer/autoload_real.php             |  6 +-
 vendor/composer/installed.json                | 64 +++++++++----------
 vendor/composer/installed.php                 | 32 +++++-----
 .../symfony/string/AbstractUnicodeString.php  |  4 +-
 vendor/symfony/string/LICENSE                 |  2 +-
 vendor/symfony/var-dumper/Caster/Caster.php   |  2 +-
 .../var-dumper/Caster/ReflectionCaster.php    |  2 +-
 vendor/symfony/var-dumper/LICENSE             |  2 +-
 vendor/symfony/var-exporter/LICENSE           |  2 +-
 vendor/symfony/var-exporter/VarExporter.php   |  2 +-
 web/modules/queue_mail/composer.json          |  2 +-
 .../queue_mail_language.info.yml              |  8 +--
 .../LanguageAwareSendMailQueueWorker.php      |  5 +-
 .../src/QueueMailLanguageNegotiator.php       |  1 -
 web/modules/queue_mail/queue_mail.info.yml    |  8 +--
 .../src/Form/QueueMailSettingsForm.php        | 10 ++-
 .../QueueWorker/SendMailQueueWorker.php       | 47 +++++++++-----
 .../queue_mail_test/queue_mail_test.info.yml  |  8 +--
 .../Functional/QueueMailConfigurationTest.php | 44 ++++++++++---
 .../Functional/QueueMailFunctionalTest.php    | 63 ++++++++++++++++++
 22 files changed, 247 insertions(+), 131 deletions(-)

diff --git a/composer.lock b/composer.lock
index a6c2f36a09..b747b53f95 100644
--- a/composer.lock
+++ b/composer.lock
@@ -6317,26 +6317,26 @@
         },
         {
             "name": "drupal/queue_mail",
-            "version": "1.4.0",
+            "version": "1.5.0",
             "source": {
                 "type": "git",
                 "url": "https://git.drupalcode.org/project/queue_mail.git",
-                "reference": "8.x-1.4"
+                "reference": "8.x-1.5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://ftp.drupal.org/files/projects/queue_mail-8.x-1.4.zip",
-                "reference": "8.x-1.4",
-                "shasum": "ccc3603d084017ec4d3aa67fa60da171ef8d63b8"
+                "url": "https://ftp.drupal.org/files/projects/queue_mail-8.x-1.5.zip",
+                "reference": "8.x-1.5",
+                "shasum": "8ee8580c5c76cbde614c955ff2d866f97d7e6e11"
             },
             "require": {
-                "drupal/core": "^8.8.0 || ^9.0"
+                "drupal/core": "^9.4 || ^10.0"
             },
             "type": "drupal-module",
             "extra": {
                 "drupal": {
-                    "version": "8.x-1.4",
-                    "datestamp": "1632316348",
+                    "version": "8.x-1.5",
+                    "datestamp": "1671458599",
                     "security-coverage": {
                         "status": "covered",
                         "message": "Covered by Drupal's security advisory policy"
@@ -15488,16 +15488,16 @@
         },
         {
             "name": "symfony/string",
-            "version": "v5.4.19",
+            "version": "v5.4.21",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/string.git",
-                "reference": "0a01071610fd861cc160dfb7e2682ceec66064cb"
+                "reference": "edac10d167b78b1d90f46a80320d632de0bd9f2f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/string/zipball/0a01071610fd861cc160dfb7e2682ceec66064cb",
-                "reference": "0a01071610fd861cc160dfb7e2682ceec66064cb",
+                "url": "https://api.github.com/repos/symfony/string/zipball/edac10d167b78b1d90f46a80320d632de0bd9f2f",
+                "reference": "edac10d167b78b1d90f46a80320d632de0bd9f2f",
                 "shasum": ""
             },
             "require": {
@@ -15554,7 +15554,7 @@
                 "utf8"
             ],
             "support": {
-                "source": "https://github.com/symfony/string/tree/v5.4.19"
+                "source": "https://github.com/symfony/string/tree/v5.4.21"
             },
             "funding": [
                 {
@@ -15570,7 +15570,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-01-01T08:32:19+00:00"
+            "time": "2023-02-22T08:00:55+00:00"
         },
         {
             "name": "symfony/translation",
@@ -15847,16 +15847,16 @@
         },
         {
             "name": "symfony/var-dumper",
-            "version": "v5.4.19",
+            "version": "v5.4.21",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-dumper.git",
-                "reference": "2944bbc23f5f8da2b962fbcbf7c4a6109b2f4b7b"
+                "reference": "6c5ac3a1be8b849d59a1a77877ee110e1b55eb74"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2944bbc23f5f8da2b962fbcbf7c4a6109b2f4b7b",
-                "reference": "2944bbc23f5f8da2b962fbcbf7c4a6109b2f4b7b",
+                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/6c5ac3a1be8b849d59a1a77877ee110e1b55eb74",
+                "reference": "6c5ac3a1be8b849d59a1a77877ee110e1b55eb74",
                 "shasum": ""
             },
             "require": {
@@ -15916,7 +15916,7 @@
                 "dump"
             ],
             "support": {
-                "source": "https://github.com/symfony/var-dumper/tree/v5.4.19"
+                "source": "https://github.com/symfony/var-dumper/tree/v5.4.21"
             },
             "funding": [
                 {
@@ -15932,20 +15932,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-01-16T10:52:33+00:00"
+            "time": "2023-02-23T10:00:28+00:00"
         },
         {
             "name": "symfony/var-exporter",
-            "version": "v5.4.19",
+            "version": "v5.4.21",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-exporter.git",
-                "reference": "2a1d06fcf2b30829d6c01dae8e6e188424d1f8f6"
+                "reference": "be74908a6942fdd331554b3cec27ff41b45ccad4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/var-exporter/zipball/2a1d06fcf2b30829d6c01dae8e6e188424d1f8f6",
-                "reference": "2a1d06fcf2b30829d6c01dae8e6e188424d1f8f6",
+                "url": "https://api.github.com/repos/symfony/var-exporter/zipball/be74908a6942fdd331554b3cec27ff41b45ccad4",
+                "reference": "be74908a6942fdd331554b3cec27ff41b45ccad4",
                 "shasum": ""
             },
             "require": {
@@ -15989,7 +15989,7 @@
                 "serialize"
             ],
             "support": {
-                "source": "https://github.com/symfony/var-exporter/tree/v5.4.19"
+                "source": "https://github.com/symfony/var-exporter/tree/v5.4.21"
             },
             "funding": [
                 {
@@ -16005,7 +16005,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-01-12T16:39:29+00:00"
+            "time": "2023-02-21T19:46:44+00:00"
         },
         {
             "name": "symfony/yaml",
diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php
index fd56bd7d84..a72151c77c 100644
--- a/vendor/composer/ClassLoader.php
+++ b/vendor/composer/ClassLoader.php
@@ -429,7 +429,8 @@ public function unregister()
     public function loadClass($class)
     {
         if ($file = $this->findFile($class)) {
-            (self::$includeFile)($file);
+            $includeFile = self::$includeFile;
+            $includeFile($file);
 
             return true;
         }
@@ -560,7 +561,10 @@ private function findFileWithExtension($class, $ext)
         return false;
     }
 
-    private static function initializeIncludeClosure(): void
+    /**
+     * @return void
+     */
+    private static function initializeIncludeClosure()
     {
         if (self::$includeFile !== null) {
             return;
@@ -574,8 +578,8 @@ private static function initializeIncludeClosure(): void
          * @param  string $file
          * @return void
          */
-        self::$includeFile = static function($file) {
+        self::$includeFile = \Closure::bind(static function($file) {
             include $file;
-        };
+        }, null, null);
     }
 }
diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php
index 57194dcb99..5d97330b68 100644
--- a/vendor/composer/autoload_real.php
+++ b/vendor/composer/autoload_real.php
@@ -38,15 +38,15 @@ public static function getLoader()
         $loader->register(true);
 
         $filesToLoad = \Composer\Autoload\ComposerStaticInit5c689ffcd54b9e495ed983fdce09b530::$files;
-        $requireFile = static function ($fileIdentifier, $file) {
+        $requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
             if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
                 $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
 
                 require $file;
             }
-        };
+        }, null, null);
         foreach ($filesToLoad as $fileIdentifier => $file) {
-            ($requireFile)($fileIdentifier, $file);
+            $requireFile($fileIdentifier, $file);
         }
 
         return $loader;
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index 95e9043ebe..208127320a 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -6568,27 +6568,27 @@
         },
         {
             "name": "drupal/queue_mail",
-            "version": "1.4.0",
-            "version_normalized": "1.4.0.0",
+            "version": "1.5.0",
+            "version_normalized": "1.5.0.0",
             "source": {
                 "type": "git",
                 "url": "https://git.drupalcode.org/project/queue_mail.git",
-                "reference": "8.x-1.4"
+                "reference": "8.x-1.5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://ftp.drupal.org/files/projects/queue_mail-8.x-1.4.zip",
-                "reference": "8.x-1.4",
-                "shasum": "ccc3603d084017ec4d3aa67fa60da171ef8d63b8"
+                "url": "https://ftp.drupal.org/files/projects/queue_mail-8.x-1.5.zip",
+                "reference": "8.x-1.5",
+                "shasum": "8ee8580c5c76cbde614c955ff2d866f97d7e6e11"
             },
             "require": {
-                "drupal/core": "^8.8.0 || ^9.0"
+                "drupal/core": "^9.4 || ^10.0"
             },
             "type": "drupal-module",
             "extra": {
                 "drupal": {
-                    "version": "8.x-1.4",
-                    "datestamp": "1632316348",
+                    "version": "8.x-1.5",
+                    "datestamp": "1671458599",
                     "security-coverage": {
                         "status": "covered",
                         "message": "Covered by Drupal's security advisory policy"
@@ -6612,8 +6612,8 @@
                     "role": "Maintainer"
                 },
                 {
-                    "name": "sinn",
-                    "homepage": "https://www.drupal.org/user/682600"
+                    "name": "Steven Jones",
+                    "homepage": "https://www.drupal.org/user/99644"
                 },
                 {
                     "name": "wafaa",
@@ -16117,17 +16117,17 @@
         },
         {
             "name": "symfony/string",
-            "version": "v5.4.19",
-            "version_normalized": "5.4.19.0",
+            "version": "v5.4.21",
+            "version_normalized": "5.4.21.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/string.git",
-                "reference": "0a01071610fd861cc160dfb7e2682ceec66064cb"
+                "reference": "edac10d167b78b1d90f46a80320d632de0bd9f2f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/string/zipball/0a01071610fd861cc160dfb7e2682ceec66064cb",
-                "reference": "0a01071610fd861cc160dfb7e2682ceec66064cb",
+                "url": "https://api.github.com/repos/symfony/string/zipball/edac10d167b78b1d90f46a80320d632de0bd9f2f",
+                "reference": "edac10d167b78b1d90f46a80320d632de0bd9f2f",
                 "shasum": ""
             },
             "require": {
@@ -16147,7 +16147,7 @@
                 "symfony/translation-contracts": "^1.1|^2",
                 "symfony/var-exporter": "^4.4|^5.0|^6.0"
             },
-            "time": "2023-01-01T08:32:19+00:00",
+            "time": "2023-02-22T08:00:55+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -16186,7 +16186,7 @@
                 "utf8"
             ],
             "support": {
-                "source": "https://github.com/symfony/string/tree/v5.4.19"
+                "source": "https://github.com/symfony/string/tree/v5.4.21"
             },
             "funding": [
                 {
@@ -16488,17 +16488,17 @@
         },
         {
             "name": "symfony/var-dumper",
-            "version": "v5.4.19",
-            "version_normalized": "5.4.19.0",
+            "version": "v5.4.21",
+            "version_normalized": "5.4.21.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-dumper.git",
-                "reference": "2944bbc23f5f8da2b962fbcbf7c4a6109b2f4b7b"
+                "reference": "6c5ac3a1be8b849d59a1a77877ee110e1b55eb74"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2944bbc23f5f8da2b962fbcbf7c4a6109b2f4b7b",
-                "reference": "2944bbc23f5f8da2b962fbcbf7c4a6109b2f4b7b",
+                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/6c5ac3a1be8b849d59a1a77877ee110e1b55eb74",
+                "reference": "6c5ac3a1be8b849d59a1a77877ee110e1b55eb74",
                 "shasum": ""
             },
             "require": {
@@ -16522,7 +16522,7 @@
                 "ext-intl": "To show region name in time zone dump",
                 "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script"
             },
-            "time": "2023-01-16T10:52:33+00:00",
+            "time": "2023-02-23T10:00:28+00:00",
             "bin": [
                 "Resources/bin/var-dump-server"
             ],
@@ -16560,7 +16560,7 @@
                 "dump"
             ],
             "support": {
-                "source": "https://github.com/symfony/var-dumper/tree/v5.4.19"
+                "source": "https://github.com/symfony/var-dumper/tree/v5.4.21"
             },
             "funding": [
                 {
@@ -16580,17 +16580,17 @@
         },
         {
             "name": "symfony/var-exporter",
-            "version": "v5.4.19",
-            "version_normalized": "5.4.19.0",
+            "version": "v5.4.21",
+            "version_normalized": "5.4.21.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-exporter.git",
-                "reference": "2a1d06fcf2b30829d6c01dae8e6e188424d1f8f6"
+                "reference": "be74908a6942fdd331554b3cec27ff41b45ccad4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/var-exporter/zipball/2a1d06fcf2b30829d6c01dae8e6e188424d1f8f6",
-                "reference": "2a1d06fcf2b30829d6c01dae8e6e188424d1f8f6",
+                "url": "https://api.github.com/repos/symfony/var-exporter/zipball/be74908a6942fdd331554b3cec27ff41b45ccad4",
+                "reference": "be74908a6942fdd331554b3cec27ff41b45ccad4",
                 "shasum": ""
             },
             "require": {
@@ -16600,7 +16600,7 @@
             "require-dev": {
                 "symfony/var-dumper": "^4.4.9|^5.0.9|^6.0"
             },
-            "time": "2023-01-12T16:39:29+00:00",
+            "time": "2023-02-21T19:46:44+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -16636,7 +16636,7 @@
                 "serialize"
             ],
             "support": {
-                "source": "https://github.com/symfony/var-exporter/tree/v5.4.19"
+                "source": "https://github.com/symfony/var-exporter/tree/v5.4.21"
             },
             "funding": [
                 {
diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php
index 53a62c282f..e4f71a6ede 100644
--- a/vendor/composer/installed.php
+++ b/vendor/composer/installed.php
@@ -3,7 +3,7 @@
         'name' => 'osu-asc-webservices/d8-upstream',
         'pretty_version' => 'dev-master',
         'version' => 'dev-master',
-        'reference' => 'd77ab674c1d51a3c0baffc1dd084fb68d5302c80',
+        'reference' => '4ca0b45218d2ba104458c074545b32b16e93dc76',
         'type' => 'project',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
@@ -1076,9 +1076,9 @@
             'dev_requirement' => false,
         ),
         'drupal/queue_mail' => array(
-            'pretty_version' => '1.4.0',
-            'version' => '1.4.0.0',
-            'reference' => '8.x-1.4',
+            'pretty_version' => '1.5.0',
+            'version' => '1.5.0.0',
+            'reference' => '8.x-1.5',
             'type' => 'drupal-module',
             'install_path' => __DIR__ . '/../../web/modules/queue_mail',
             'aliases' => array(),
@@ -1588,7 +1588,7 @@
         'osu-asc-webservices/d8-upstream' => array(
             'pretty_version' => 'dev-master',
             'version' => 'dev-master',
-            'reference' => 'd77ab674c1d51a3c0baffc1dd084fb68d5302c80',
+            'reference' => '4ca0b45218d2ba104458c074545b32b16e93dc76',
             'type' => 'project',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),
@@ -1693,8 +1693,8 @@
         'psr/container-implementation' => array(
             'dev_requirement' => false,
             'provided' => array(
-                0 => '1.0',
-                1 => '^1.0',
+                0 => '^1.0',
+                1 => '1.0',
             ),
         ),
         'psr/event-dispatcher-implementation' => array(
@@ -2448,9 +2448,9 @@
             ),
         ),
         'symfony/string' => array(
-            'pretty_version' => 'v5.4.19',
-            'version' => '5.4.19.0',
-            'reference' => '0a01071610fd861cc160dfb7e2682ceec66064cb',
+            'pretty_version' => 'v5.4.21',
+            'version' => '5.4.21.0',
+            'reference' => 'edac10d167b78b1d90f46a80320d632de0bd9f2f',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/string',
             'aliases' => array(),
@@ -2490,18 +2490,18 @@
             'dev_requirement' => false,
         ),
         'symfony/var-dumper' => array(
-            'pretty_version' => 'v5.4.19',
-            'version' => '5.4.19.0',
-            'reference' => '2944bbc23f5f8da2b962fbcbf7c4a6109b2f4b7b',
+            'pretty_version' => 'v5.4.21',
+            'version' => '5.4.21.0',
+            'reference' => '6c5ac3a1be8b849d59a1a77877ee110e1b55eb74',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/var-dumper',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'symfony/var-exporter' => array(
-            'pretty_version' => 'v5.4.19',
-            'version' => '5.4.19.0',
-            'reference' => '2a1d06fcf2b30829d6c01dae8e6e188424d1f8f6',
+            'pretty_version' => 'v5.4.21',
+            'version' => '5.4.21.0',
+            'reference' => 'be74908a6942fdd331554b3cec27ff41b45ccad4',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/var-exporter',
             'aliases' => array(),
diff --git a/vendor/symfony/string/AbstractUnicodeString.php b/vendor/symfony/string/AbstractUnicodeString.php
index 1bc6f88fda..80b8326aee 100644
--- a/vendor/symfony/string/AbstractUnicodeString.php
+++ b/vendor/symfony/string/AbstractUnicodeString.php
@@ -37,8 +37,8 @@ abstract class AbstractUnicodeString extends AbstractString
     private const ASCII = "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F";
 
     // the subset of folded case mappings that is not in lower case mappings
-    private const FOLD_FROM = ['İ', 'µ', 'ſ', "\xCD\x85", 'ς', 'ϐ', 'ϑ', 'ϕ', 'ϖ', 'ϰ', 'ϱ', 'ϵ', 'ẛ', "\xE1\xBE\xBE", 'ß', 'İ', 'ʼn', 'ǰ', 'ΐ', 'ΰ', 'և', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'ẚ', 'ẞ', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'ᾐ', 'ᾑ', 'ᾒ', 'ᾓ', 'ᾔ', 'ᾕ', 'ᾖ', 'ᾗ', 'ᾘ', 'ᾙ', 'ᾚ', 'ᾛ', 'ᾜ', 'ᾝ', 'ᾞ', 'ᾟ', 'ᾠ', 'ᾡ', 'ᾢ', 'ᾣ', 'ᾤ', 'ᾥ', 'ᾦ', 'ᾧ', 'ᾨ', 'ᾩ', 'ᾪ', 'ᾫ', 'ᾬ', 'ᾭ', 'ᾮ', 'ᾯ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'ᾼ', 'ῂ', 'ῃ', 'ῄ', 'ῆ', 'ῇ', 'ῌ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'ῢ', 'ΰ', 'ῤ', 'ῦ', 'ῧ', 'ῲ', 'ῳ', 'ῴ', 'ῶ', 'ῷ', 'ῼ', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'ſt', 'st', 'ﬓ', 'ﬔ', 'ﬕ', 'ﬖ', 'ﬗ'];
-    private const FOLD_TO = ['i̇', 'μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', 'ṡ', 'ι', 'ss', 'i̇', 'ʼn', 'ǰ', 'ΐ', 'ΰ', 'եւ', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'aʾ', 'ss', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ἀι', 'ἁι', 'ἂι', 'ἃι', 'ἄι', 'ἅι', 'ἆι', 'ἇι', 'ἀι', 'ἁι', 'ἂι', 'ἃι', 'ἄι', 'ἅι', 'ἆι', 'ἇι', 'ἠι', 'ἡι', 'ἢι', 'ἣι', 'ἤι', 'ἥι', 'ἦι', 'ἧι', 'ἠι', 'ἡι', 'ἢι', 'ἣι', 'ἤι', 'ἥι', 'ἦι', 'ἧι', 'ὠι', 'ὡι', 'ὢι', 'ὣι', 'ὤι', 'ὥι', 'ὦι', 'ὧι', 'ὠι', 'ὡι', 'ὢι', 'ὣι', 'ὤι', 'ὥι', 'ὦι', 'ὧι', 'ὰι', 'αι', 'άι', 'ᾶ', 'ᾶι', 'αι', 'ὴι', 'ηι', 'ήι', 'ῆ', 'ῆι', 'ηι', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'ῢ', 'ΰ', 'ῤ', 'ῦ', 'ῧ', 'ὼι', 'ωι', 'ώι', 'ῶ', 'ῶι', 'ωι', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'st', 'st', 'մն', 'մե', 'մի', 'վն', 'մխ'];
+    private const FOLD_FROM = ['İ', 'µ', 'ſ', "\xCD\x85", 'ς', 'ϐ', 'ϑ', 'ϕ', 'ϖ', 'ϰ', 'ϱ', 'ϵ', 'ẛ', "\xE1\xBE\xBE", 'ß', 'ʼn', 'ǰ', 'ΐ', 'ΰ', 'և', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'ẚ', 'ẞ', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'ᾐ', 'ᾑ', 'ᾒ', 'ᾓ', 'ᾔ', 'ᾕ', 'ᾖ', 'ᾗ', 'ᾘ', 'ᾙ', 'ᾚ', 'ᾛ', 'ᾜ', 'ᾝ', 'ᾞ', 'ᾟ', 'ᾠ', 'ᾡ', 'ᾢ', 'ᾣ', 'ᾤ', 'ᾥ', 'ᾦ', 'ᾧ', 'ᾨ', 'ᾩ', 'ᾪ', 'ᾫ', 'ᾬ', 'ᾭ', 'ᾮ', 'ᾯ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'ᾼ', 'ῂ', 'ῃ', 'ῄ', 'ῆ', 'ῇ', 'ῌ', 'ῒ', 'ῖ', 'ῗ', 'ῢ', 'ῤ', 'ῦ', 'ῧ', 'ῲ', 'ῳ', 'ῴ', 'ῶ', 'ῷ', 'ῼ', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'ſt', 'st', 'ﬓ', 'ﬔ', 'ﬕ', 'ﬖ', 'ﬗ'];
+    private const FOLD_TO = ['i̇', 'μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', 'ṡ', 'ι', 'ss', 'ʼn', 'ǰ', 'ΐ', 'ΰ', 'եւ', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'aʾ', 'ss', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ἀι', 'ἁι', 'ἂι', 'ἃι', 'ἄι', 'ἅι', 'ἆι', 'ἇι', 'ἀι', 'ἁι', 'ἂι', 'ἃι', 'ἄι', 'ἅι', 'ἆι', 'ἇι', 'ἠι', 'ἡι', 'ἢι', 'ἣι', 'ἤι', 'ἥι', 'ἦι', 'ἧι', 'ἠι', 'ἡι', 'ἢι', 'ἣι', 'ἤι', 'ἥι', 'ἦι', 'ἧι', 'ὠι', 'ὡι', 'ὢι', 'ὣι', 'ὤι', 'ὥι', 'ὦι', 'ὧι', 'ὠι', 'ὡι', 'ὢι', 'ὣι', 'ὤι', 'ὥι', 'ὦι', 'ὧι', 'ὰι', 'αι', 'άι', 'ᾶ', 'ᾶι', 'αι', 'ὴι', 'ηι', 'ήι', 'ῆ', 'ῆι', 'ηι', 'ῒ', 'ῖ', 'ῗ', 'ῢ', 'ῤ', 'ῦ', 'ῧ', 'ὼι', 'ωι', 'ώι', 'ῶ', 'ῶι', 'ωι', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'st', 'st', 'մն', 'մե', 'մի', 'վն', 'մխ'];
 
     // the subset of upper case mappings that map one code point to many code points
     private const UPPER_FROM = ['ß', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'ſt', 'st', 'և', 'ﬓ', 'ﬔ', 'ﬕ', 'ﬖ', 'ﬗ', 'ʼn', 'ΐ', 'ΰ', 'ǰ', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'ẚ', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ᾶ', 'ῆ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'ῢ', 'ΰ', 'ῤ', 'ῦ', 'ῧ', 'ῶ'];
diff --git a/vendor/symfony/string/LICENSE b/vendor/symfony/string/LICENSE
index 5c7ba0551c..f37c76b591 100644
--- a/vendor/symfony/string/LICENSE
+++ b/vendor/symfony/string/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2019-2023 Fabien Potencier
+Copyright (c) 2019-present Fabien Potencier
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/vendor/symfony/var-dumper/Caster/Caster.php b/vendor/symfony/var-dumper/Caster/Caster.php
index 890f531063..81bfd54e5a 100644
--- a/vendor/symfony/var-dumper/Caster/Caster.php
+++ b/vendor/symfony/var-dumper/Caster/Caster.php
@@ -115,7 +115,7 @@ public static function castObject(object $obj, string $class, bool $hasDebugInfo
      * @param array    $a                The array containing the properties to filter
      * @param int      $filter           A bit field of Caster::EXCLUDE_* constants specifying which properties to filter out
      * @param string[] $listedProperties List of properties to exclude when Caster::EXCLUDE_VERBOSE is set, and to preserve when Caster::EXCLUDE_NOT_IMPORTANT is set
-     * @param int      &$count           Set to the number of removed properties
+     * @param int|null &$count           Set to the number of removed properties
      */
     public static function filter(array $a, int $filter, array $listedProperties = [], ?int &$count = 0): array
     {
diff --git a/vendor/symfony/var-dumper/Caster/ReflectionCaster.php b/vendor/symfony/var-dumper/Caster/ReflectionCaster.php
index 5c644053ad..ef6a85ef0f 100644
--- a/vendor/symfony/var-dumper/Caster/ReflectionCaster.php
+++ b/vendor/symfony/var-dumper/Caster/ReflectionCaster.php
@@ -292,7 +292,7 @@ public static function castParameter(\ReflectionParameter $c, array $a, Stub $st
         if ($c->isOptional()) {
             try {
                 $a[$prefix.'default'] = $v = $c->getDefaultValue();
-                if ($c->isDefaultValueConstant()) {
+                if ($c->isDefaultValueConstant() && !\is_object($v)) {
                     $a[$prefix.'default'] = new ConstStub($c->getDefaultValueConstantName(), $v);
                 }
                 if (null === $v) {
diff --git a/vendor/symfony/var-dumper/LICENSE b/vendor/symfony/var-dumper/LICENSE
index 72412a62b2..29f72d5e95 100644
--- a/vendor/symfony/var-dumper/LICENSE
+++ b/vendor/symfony/var-dumper/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2014-2023 Fabien Potencier
+Copyright (c) 2014-present Fabien Potencier
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/vendor/symfony/var-exporter/LICENSE b/vendor/symfony/var-exporter/LICENSE
index 99757d5171..7536caeae8 100644
--- a/vendor/symfony/var-exporter/LICENSE
+++ b/vendor/symfony/var-exporter/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2018-2023 Fabien Potencier
+Copyright (c) 2018-present Fabien Potencier
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/vendor/symfony/var-exporter/VarExporter.php b/vendor/symfony/var-exporter/VarExporter.php
index 003388e798..8581337813 100644
--- a/vendor/symfony/var-exporter/VarExporter.php
+++ b/vendor/symfony/var-exporter/VarExporter.php
@@ -34,7 +34,7 @@ final class VarExporter
      *
      * @param mixed $value          The value to export
      * @param bool  &$isStaticValue Set to true after execution if the provided value is static, false otherwise
-     * @param bool  &$classes       Classes found in the value are added to this list as both keys and values
+     * @param array &$foundClasses  Classes found in the value are added to this list as both keys and values
      *
      * @throws ExceptionInterface When the provided value cannot be serialized
      */
diff --git a/web/modules/queue_mail/composer.json b/web/modules/queue_mail/composer.json
index e4e8b980b0..4e215070f1 100644
--- a/web/modules/queue_mail/composer.json
+++ b/web/modules/queue_mail/composer.json
@@ -20,6 +20,6 @@
         "source": "https://git.drupalcode.org/project/queue_mail"
     },
     "require": {
-        "drupal/core": "^8.8.0 || ^9.0"
+        "drupal/core": "^9.4 || ^10.0"
     }
 }
diff --git a/web/modules/queue_mail/modules/queue_mail_language/queue_mail_language.info.yml b/web/modules/queue_mail/modules/queue_mail_language/queue_mail_language.info.yml
index 610390bbf7..fac8919ebf 100644
--- a/web/modules/queue_mail/modules/queue_mail_language/queue_mail_language.info.yml
+++ b/web/modules/queue_mail/modules/queue_mail_language/queue_mail_language.info.yml
@@ -1,12 +1,12 @@
 name: Queue Mail Language
 type: module
-core_version_requirement: ^8 || ^9
+core_version_requirement: ^9.4 || ^10
 description: Adds language support for queued mails.
 package: Mail
 dependencies:
   - drupal:language
 
-# Information added by Drupal.org packaging script on 2021-09-22
-version: '8.x-1.4'
+# Information added by Drupal.org packaging script on 2022-12-19
+version: '8.x-1.5'
 project: 'queue_mail'
-datestamp: 1632316351
+datestamp: 1671458592
diff --git a/web/modules/queue_mail/modules/queue_mail_language/src/Plugin/QueueWorker/LanguageAwareSendMailQueueWorker.php b/web/modules/queue_mail/modules/queue_mail_language/src/Plugin/QueueWorker/LanguageAwareSendMailQueueWorker.php
index d71622c1ec..c8b8cc9fa8 100644
--- a/web/modules/queue_mail/modules/queue_mail_language/src/Plugin/QueueWorker/LanguageAwareSendMailQueueWorker.php
+++ b/web/modules/queue_mail/modules/queue_mail_language/src/Plugin/QueueWorker/LanguageAwareSendMailQueueWorker.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\queue_mail_language\Plugin\QueueWorker;
 
+use Drupal\Component\Datetime\TimeInterface;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Logger\LoggerChannelFactoryInterface;
@@ -45,6 +46,7 @@ public static function create(ContainerInterface $container, array $configuratio
       $container->get('config.factory'),
       $container->get('queue'),
       $container->get('module_handler'),
+      $container->get('datetime.time'),
       $container->get('language_manager'),
       $container->get('queue_mail.language_negotiator')
     );
@@ -61,10 +63,11 @@ public function __construct(
     ConfigFactoryInterface $config_factory,
     ContainerAwareInterface $queue_factory,
     ModuleHandlerInterface $module_handler,
+    TimeInterface $time,
     ConfigurableLanguageManagerInterface $language_manager,
     QueueMailLanguageNegotiator $queue_mail_language_negotiator
   ) {
-    parent::__construct($theme_manager, $theme_init, $mail_manager, $logger_factory, $config_factory, $queue_factory, $module_handler);
+    parent::__construct($theme_manager, $theme_init, $mail_manager, $logger_factory, $config_factory, $queue_factory, $module_handler, $time);
     $this->languageManager = $language_manager;
     $this->queueMailLanguageNegotiator = $queue_mail_language_negotiator;
   }
diff --git a/web/modules/queue_mail/modules/queue_mail_language/src/QueueMailLanguageNegotiator.php b/web/modules/queue_mail/modules/queue_mail_language/src/QueueMailLanguageNegotiator.php
index babcc0f4d1..5464c5b776 100644
--- a/web/modules/queue_mail/modules/queue_mail_language/src/QueueMailLanguageNegotiator.php
+++ b/web/modules/queue_mail/modules/queue_mail_language/src/QueueMailLanguageNegotiator.php
@@ -20,7 +20,6 @@ class QueueMailLanguageNegotiator extends LanguageNegotiator {
    * {@inheritdoc}
    */
   public function initializeType($type) {
-    $language = NULL;
     $method_id = static::METHOD_ID;
     $availableLanguages = $this->languageManager->getLanguages();
 
diff --git a/web/modules/queue_mail/queue_mail.info.yml b/web/modules/queue_mail/queue_mail.info.yml
index a7c1cfba71..12b9581b24 100644
--- a/web/modules/queue_mail/queue_mail.info.yml
+++ b/web/modules/queue_mail/queue_mail.info.yml
@@ -1,11 +1,11 @@
 name: Queue Mail
 type: module
-core_version_requirement: ^8 || ^9
+core_version_requirement: ^9.4 || ^10
 description: Queues all mail sent by your Drupal site so that it is sent via cron using the Drupal Queue API. This is helpful for large traffic sites where sending a lot of emails per page request can slow things down considerably.
 package: Mail
 configure: queue_mail.admin_settings
 
-# Information added by Drupal.org packaging script on 2021-09-22
-version: '8.x-1.4'
+# Information added by Drupal.org packaging script on 2022-12-19
+version: '8.x-1.5'
 project: 'queue_mail'
-datestamp: 1632316351
+datestamp: 1671458592
diff --git a/web/modules/queue_mail/src/Form/QueueMailSettingsForm.php b/web/modules/queue_mail/src/Form/QueueMailSettingsForm.php
index 48df6dfb5e..a302269ff2 100644
--- a/web/modules/queue_mail/src/Form/QueueMailSettingsForm.php
+++ b/web/modules/queue_mail/src/Form/QueueMailSettingsForm.php
@@ -97,7 +97,13 @@ public function buildForm(array $form, FormStateInterface $form_state) {
     ];
 
     // Get a list of modules that implement hook_mail.
-    $mail_modules = $this->moduleHandler->getImplementations('mail');
+    $modules = $this->moduleHandler->getModuleList();
+    $mail_modules = [];
+    foreach ($modules as $module => $extension) {
+      if ($this->moduleHandler->hasImplementations('mail', $module)) {
+        $mail_modules[] = $module;
+      }
+    }
 
     $rows = [];
     foreach ($mail_modules as $name) {
@@ -143,7 +149,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#min' => 0,
       '#title' => $this->t('Queue retry threshold'),
       '#default_value' => $config->get('threshold'),
-      '#description' => $this->t('How many times to retry before deleting an item.'),
+      '#description' => $this->t('How many times to retry before deleting an item. Set "0" not to retry. Leave empty for unlimited.'),
     ];
 
     $form['advanced']['requeue_interval'] = [
diff --git a/web/modules/queue_mail/src/Plugin/QueueWorker/SendMailQueueWorker.php b/web/modules/queue_mail/src/Plugin/QueueWorker/SendMailQueueWorker.php
index 7fd80d2c32..500c791ed5 100644
--- a/web/modules/queue_mail/src/Plugin/QueueWorker/SendMailQueueWorker.php
+++ b/web/modules/queue_mail/src/Plugin/QueueWorker/SendMailQueueWorker.php
@@ -2,18 +2,20 @@
 
 namespace Drupal\queue_mail\Plugin\QueueWorker;
 
-use Drupal\Core\Queue\QueueWorkerBase;
+use Drupal\Component\Datetime\TimeInterface;
 use Drupal\Component\Render\PlainTextOutput;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\Core\Theme\ThemeManagerInterface;
-use Drupal\Core\Theme\ThemeInitializationInterface;
-use Drupal\Core\Mail\MailManagerInterface;
-use Drupal\Core\Logger\LoggerChannelFactoryInterface;
 use Drupal\Core\Config\ConfigFactoryInterface;
-use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\Extension\ModuleHandlerInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Logger\LoggerChannelFactoryInterface;
+use Drupal\Core\Mail\MailManagerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Queue\DelayedRequeueException;
+use Drupal\Core\Queue\QueueWorkerBase;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\Theme\ThemeInitializationInterface;
+use Drupal\Core\Theme\ThemeManagerInterface;
 use Symfony\Component\DependencyInjection\ContainerAwareInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Sends emails form queue.
@@ -84,6 +86,13 @@ class SendMailQueueWorker extends QueueWorkerBase implements ContainerFactoryPlu
    */
   protected $moduleHandler;
 
+  /**
+   * The time service.
+   *
+   * @var \Drupal\Component\Datetime\TimeInterface
+   */
+  protected $time;
+
   /**
    * {@inheritdoc}
    */
@@ -95,14 +104,15 @@ public static function create(ContainerInterface $container, array $configuratio
       $container->get('logger.factory'),
       $container->get('config.factory'),
       $container->get('queue'),
-      $container->get('module_handler')
+      $container->get('module_handler'),
+      $container->get('datetime.time')
     );
   }
 
   /**
    * {@inheritdoc}
    */
-  public function __construct(ThemeManagerInterface $theme_manager, ThemeInitializationInterface $theme_init, MailManagerInterface $mail_manager, LoggerChannelFactoryInterface $logger_factory, ConfigFactoryInterface $config_factory, ContainerAwareInterface $queue_factory, ModuleHandlerInterface $module_handler) {
+  public function __construct(ThemeManagerInterface $theme_manager, ThemeInitializationInterface $theme_init, MailManagerInterface $mail_manager, LoggerChannelFactoryInterface $logger_factory, ConfigFactoryInterface $config_factory, ContainerAwareInterface $queue_factory, ModuleHandlerInterface $module_handler, TimeInterface $time) {
     $this->themeManager = $theme_manager;
     $this->themeInitialization = $theme_init;
     $this->activeTheme = $this->themeManager->getActiveTheme();
@@ -111,6 +121,7 @@ public function __construct(ThemeManagerInterface $theme_manager, ThemeInitializ
     $this->config = $config_factory->get('queue_mail.settings');
     $this->queue = $queue_factory->get('queue_mail', TRUE);
     $this->moduleHandler = $module_handler;
+    $this->time = $time;
   }
 
   /**
@@ -121,9 +132,11 @@ public function processItem($message) {
     $interval = $this->config->get('requeue_interval');
 
     // Prevent retrying until specified interval has elapsed.
-    if (isset($message['last_attempt']) && ($message['last_attempt'] + $interval) > time()) {
-      // Skip item.
-      throw new \RuntimeException(sprintf('Sending of mail "%s" is skipped in the mail queue due to requeue interval.', $message['id']));
+    $current_time = $this->time->getCurrentTime();
+    if (isset($message['last_attempt']) && ($message['last_attempt'] + $interval) > $current_time) {
+      // Skip item until interval expires.
+      $delay = $message['last_attempt'] + $interval - $current_time;
+      throw new DelayedRequeueException($delay);
     }
 
     // Invoke hook_queue_mail_send_alter() to allow all modules to alter the
@@ -177,7 +190,7 @@ public function processItem($message) {
       $this->logger->error('Error sending email (from %from to %to with reply-to %reply).', [
         '%from' => $message['from'],
         '%to' => $message['to'],
-        '%reply' => $message['reply-to'] ? $message['reply-to'] : $this->t('not set'),
+        '%reply' => $message['reply-to'] ?: $this->t('not set'),
       ]);
 
       $this->processRetryLimit($original_message);
@@ -271,7 +284,7 @@ protected function waitBetweenSending() {
    *   Original message.
    */
   protected function processRetryLimit(array $original_message) {
-    $original_message['last_attempt'] = time();
+    $original_message['last_attempt'] = $this->time->getCurrentTime();
 
     if (!isset($original_message['fail_count'])) {
       $original_message['fail_count'] = 0;
@@ -281,14 +294,14 @@ protected function processRetryLimit(array $original_message) {
     $threshold = $this->config->get('threshold');
 
     // Add back to the queue with an updated fail count.
-    if ($original_message['fail_count'] < $threshold) {
+    if (!isset($threshold) || $original_message['fail_count'] < $threshold) {
       $this->queue->createItem($original_message);
     }
     else {
       $this->logger->error('Attempt sending email (from %from to %to with reply-to %reply) exceeded retry threshold and was deleted.', [
         '%from' => $original_message['from'],
         '%to' => $original_message['to'],
-        '%reply' => $original_message['reply-to'] ? $original_message['reply-to'] : $this->t('not set'),
+        '%reply' => $original_message['reply-to'] ?: $this->t('not set'),
       ]);
     }
   }
diff --git a/web/modules/queue_mail/tests/modules/queue_mail_test/queue_mail_test.info.yml b/web/modules/queue_mail/tests/modules/queue_mail_test/queue_mail_test.info.yml
index 13b88fac79..4e727aaeb1 100644
--- a/web/modules/queue_mail/tests/modules/queue_mail_test/queue_mail_test.info.yml
+++ b/web/modules/queue_mail/tests/modules/queue_mail_test/queue_mail_test.info.yml
@@ -1,14 +1,14 @@
 name: Queue Mail Test
 type: module
 description: Module for use by the queue mail module tests.
-core_version_requirement: ^8 || ^9
+core_version_requirement: ^9.4 || ^10
 dependencies:
   - queue_mail:queue_mail
 
 package: Testing
 hidden: true
 
-# Information added by Drupal.org packaging script on 2021-09-22
-version: '8.x-1.4'
+# Information added by Drupal.org packaging script on 2022-12-19
+version: '8.x-1.5'
 project: 'queue_mail'
-datestamp: 1632316351
+datestamp: 1671458592
diff --git a/web/modules/queue_mail/tests/src/Functional/QueueMailConfigurationTest.php b/web/modules/queue_mail/tests/src/Functional/QueueMailConfigurationTest.php
index 1940d28277..a6f99fc3df 100644
--- a/web/modules/queue_mail/tests/src/Functional/QueueMailConfigurationTest.php
+++ b/web/modules/queue_mail/tests/src/Functional/QueueMailConfigurationTest.php
@@ -53,10 +53,8 @@ public function testDefaultConfiguration() {
       'threshold' => 50,
       'requeue_interval' => 10800,
     ];
-
-    foreach ($default_values as $field => $value) {
-      $this->assertSession()->fieldValueEquals($field, $value);
-    }
+    $this->assertFieldValues($default_values);
+    $this->assertSettings($default_values);
   }
 
   /**
@@ -74,11 +72,22 @@ public function testChangeConfiguration() {
       'requeue_interval' => 21600,
     ];
     $this->submitForm($edit, 'Save configuration');
-
     $this->drupalGet(static::CONFIGURATION_PATH);
-    foreach ($edit as $field => $value) {
-      $this->assertSession()->fieldValueEquals($field, $value);
-    }
+    $this->assertFieldValues($edit);
+    $this->assertSettings($edit);
+
+    // Unlimited retry threshold.
+    $edit['threshold'] = 0;
+    $this->submitForm($edit, 'Save configuration');
+    $this->assertFieldValues($edit);
+    $this->assertSettings($edit);
+
+    // No retry.
+    $edit['threshold'] = '';
+    $this->submitForm($edit, 'Save configuration');
+    $this->assertFieldValues($edit);
+    $edit['threshold'] = NULL;
+    $this->assertSettings($edit);
   }
 
   /**
@@ -107,4 +116,23 @@ public function testWaitTimePerItemValidation() {
     $this->assertSession()->responseNotContains($validation_text);
   }
 
+  /**
+   * Asserts values on the settings form.
+   */
+  protected function assertFieldValues($values) {
+    foreach ($values as $field => $value) {
+      $this->assertSession()->fieldValueEquals($field, $value);
+    }
+  }
+
+  /**
+   * Asserts settings stored in queue_mail.settings.
+   */
+  protected function assertSettings($values) {
+    $config = \Drupal::config('queue_mail.settings');
+    foreach ($values as $key => $value) {
+      $this->assertSame($value, $config->get($key));
+    }
+  }
+
 }
diff --git a/web/modules/queue_mail/tests/src/Functional/QueueMailFunctionalTest.php b/web/modules/queue_mail/tests/src/Functional/QueueMailFunctionalTest.php
index fdebf703ea..8e32ac486f 100644
--- a/web/modules/queue_mail/tests/src/Functional/QueueMailFunctionalTest.php
+++ b/web/modules/queue_mail/tests/src/Functional/QueueMailFunctionalTest.php
@@ -248,4 +248,67 @@ public function testFailMessage() {
     $this->assertEquals(1, $queue_count_after_adding - $queue_count_init, 'One message has been processed.');
   }
 
+  /**
+   * Tests retry threshold functionality.
+   */
+  public function testRetryThreshold() {
+    $this->setAllEmailsToBeQueued();
+    $queue = _queue_mail_get_queue();
+    $settings = \Drupal::configFactory()->getEditable('queue_mail.settings');
+    // Set requeue interval as 1 to make it possible to process the queue item
+    // on the next cron run.
+    $settings->set('requeue_interval', 1)->save();
+
+    // No retry.
+    $settings->set('threshold', 0)->save();
+    $this->sendQueueMailTest('fail_message', 'fail@example.com', $this->getMessageParams());
+    $this->assertEquals(1, $queue->numberOfItems());
+    $this->cronRun();
+    $this->assertEquals(0, $queue->numberOfItems());
+
+    // Retry threshold is 3 - message is removed from queue after 3 attempts.
+    $settings->set('threshold', 3)->save();
+    $this->sendQueueMailTest('fail_message', 'fail@example.com', $this->getMessageParams());
+    $this->assertEquals(1, $queue->numberOfItems());
+    for ($i = 1; $i <= 2; $i++) {
+      $this->cronRun();
+      $this->assertEquals(1, $queue->numberOfItems());
+      $this->releaseQueueItems();
+      $item = $queue->claimItem();
+      $this->assertEquals($i, $item->data['fail_count']);
+      $this->releaseQueueItems();
+      sleep(1);
+    }
+    $this->cronRun();
+    $this->assertEquals(0, $queue->numberOfItems());
+
+    // Retry threshold is unlimited - message won't be removed from queue.
+    $settings->set('threshold', NULL)->save();
+    $this->sendQueueMailTest('fail_message', 'fail@example.com', $this->getMessageParams());
+    $this->assertEquals(1, $queue->numberOfItems());
+    for ($i = 1; $i <= 10; $i++) {
+      $this->cronRun();
+      $this->assertEquals(1, $queue->numberOfItems());
+      $this->releaseQueueItems();
+      $item = $queue->claimItem();
+      $this->assertEquals($i, $item->data['fail_count']);
+      $this->releaseQueueItems();
+      sleep(1);
+    }
+  }
+
+  /**
+   * Releases queue items between cron run.
+   *
+   * \Drupal\Core\Queue\DatabaseQueue::garbageCollection() uses request time
+   * that don't release items inside few cron runs in one request.
+   */
+  protected function releaseQueueItems() {
+    \Drupal::database()->update('queue')
+      ->fields([
+        'expire' => 0,
+      ])
+      ->execute();
+  }
+
 }
-- 
GitLab