From c4dfbbe2ffe720391ef696859de82e39fa7c0d01 Mon Sep 17 00:00:00 2001
From: Lee Rowlands <lee.rowlands@previousnext.com.au>
Date: Thu, 19 Dec 2024 12:35:24 +1000
Subject: [PATCH] Issue #3488835 by mcdruid, atul_ghate, benjifisher, catch,
 cilefen, zengenuity, larowlan, poker10, longwave, damienmckenna, greggles,
 kristiaanvandeneynde: Status report confuses null email with duplicate email

(cherry picked from commit 9ed8a79f214e7dbef3165d7c16aff36e32c3ec77)
---
 .../tests/src/Kernel/UserRequirementsTest.php | 65 +++++++++++++++++++
 .../tests/src/Kernel/UserValidationTest.php   |  8 +++
 core/modules/user/user.install                |  1 +
 3 files changed, 74 insertions(+)
 create mode 100644 core/modules/user/tests/src/Kernel/UserRequirementsTest.php

diff --git a/core/modules/user/tests/src/Kernel/UserRequirementsTest.php b/core/modules/user/tests/src/Kernel/UserRequirementsTest.php
new file mode 100644
index 000000000000..88b406bc30e4
--- /dev/null
+++ b/core/modules/user/tests/src/Kernel/UserRequirementsTest.php
@@ -0,0 +1,65 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\user\Kernel;
+
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\Tests\user\Traits\UserCreationTrait;
+
+/**
+ * Tests user_requirements().
+ *
+ * @group user
+ */
+class UserRequirementsTest extends KernelTestBase {
+
+  use UserCreationTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['user'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+    $this->container->get('module_handler')->loadInclude('user', 'install');
+    $this->installEntitySchema('user');
+  }
+
+  /**
+   * Tests that the requirements check can detect conflicting user emails.
+   *
+   * @see \Drupal\Tests\user\Kernel\UserValidationTest::testValidation
+   */
+  public function testConflictingUserEmails(): void {
+
+    $output = \user_requirements('runtime');
+    $this->assertArrayNotHasKey('conflicting emails', $output);
+
+    $this->createUser([], 'User A', FALSE, ['mail' => 'unique@example.com']);
+    $this->createUser([], 'User B', FALSE, ['mail' => 'UNIQUE@example.com']);
+
+    $output = \user_requirements('runtime');
+    $this->assertArrayHasKey('conflicting emails', $output);
+  }
+
+  /**
+   * Tests that the requirements check does not incorrectly flag blank emails.
+   */
+  public function testBlankUserEmails(): void {
+
+    $output = \user_requirements('runtime');
+    $this->assertArrayNotHasKey('conflicting emails', $output);
+
+    $this->createUser([], 'User A', FALSE, ['mail' => '']);
+    $this->createUser([], 'User B', FALSE, ['mail' => '']);
+
+    $output = \user_requirements('runtime');
+    $this->assertArrayNotHasKey('conflicting emails', $output);
+  }
+
+}
diff --git a/core/modules/user/tests/src/Kernel/UserValidationTest.php b/core/modules/user/tests/src/Kernel/UserValidationTest.php
index 77cf851cf605..6ad1d2475bf2 100644
--- a/core/modules/user/tests/src/Kernel/UserValidationTest.php
+++ b/core/modules/user/tests/src/Kernel/UserValidationTest.php
@@ -137,6 +137,14 @@ public function testValidation(): void {
     $this->assertCount(1, $violations, 'Violation found when email already exists.');
     $this->assertEquals('mail', $violations[0]->getPropertyPath());
     $this->assertEquals('The email address existing@example.com is already taken.', $violations[0]->getMessage());
+
+    // Ensure case-insensitive uniqueness of email.
+    $user->set('mail', 'EXISTING@example.com');
+    $violations = $user->validate();
+    $this->assertCount(1, $violations, 'Violation found when email already exists.');
+    $this->assertEquals('mail', $violations[0]->getPropertyPath());
+    $this->assertEquals('The email address EXISTING@example.com is already taken.', $violations[0]->getMessage());
+
     $user->set('mail', NULL);
     $violations = $user->validate();
     $this->assertCount(1, $violations, 'Email addresses may not be removed');
diff --git a/core/modules/user/user.install b/core/modules/user/user.install
index 35ff4fe9d0e2..8ef18ab67150 100644
--- a/core/modules/user/user.install
+++ b/core/modules/user/user.install
@@ -120,6 +120,7 @@ function user_requirements($phase): array {
 
   $query = \Drupal::database()->select('users_field_data');
   $query->addExpression('LOWER(mail)', 'lower_mail');
+  $query->isNotNull('mail');
   $query->groupBy('lower_mail');
   $query->having('COUNT(uid) > :matches', [':matches' => 1]);
   $conflicts = $query->countQuery()->execute()->fetchField();
-- 
GitLab