assertEquals($expectedHash, $hash); $plainSha256Hash = hash('sha256', strtolower(trim($email))); $this->assertNotEquals($plainSha256Hash, $hash, 'HMAC-SHA256 should be different from plain SHA-256'); } public function test_hash_is_deterministic(): void { $email = 'test@example.com'; $hash1 = User::hashEmail($email); $hash2 = User::hashEmail($email); $this->assertEquals($hash1, $hash2, 'Same email should produce the same hash'); } public function test_hash_is_case_insensitive(): void { $email1 = 'test@example.com'; $email2 = 'TEST@EXAMPLE.COM'; $email3 = 'TeSt@ExAmPlE.cOm'; $hash1 = User::hashEmail($email1); $hash2 = User::hashEmail($email2); $hash3 = User::hashEmail($email3); $this->assertEquals($hash1, $hash2, 'Uppercase email should produce the same hash'); $this->assertEquals($hash1, $hash3, 'Mixed case email should produce the same hash'); } public function test_hash_trims_whitespace(): void { $email1 = 'test@example.com'; $email2 = ' test@example.com '; $email3 = "\ttest@example.com\n"; $hash1 = User::hashEmail($email1); $hash2 = User::hashEmail($email2); $hash3 = User::hashEmail($email3); $this->assertEquals($hash1, $hash2, 'Email with leading/trailing spaces should produce the same hash'); $this->assertEquals($hash1, $hash3, 'Email with tabs/newlines should produce the same hash'); } public function test_different_emails_produce_different_hashes(): void { $email1 = 'user1@example.com'; $email2 = 'user2@example.com'; $email3 = 'admin@example.com'; $hash1 = User::hashEmail($email1); $hash2 = User::hashEmail($email2); $hash3 = User::hashEmail($email3); $this->assertNotEquals($hash1, $hash2, 'Different emails should produce different hashes'); $this->assertNotEquals($hash1, $hash3, 'Different emails should produce different hashes'); $this->assertNotEquals($hash2, $hash3, 'Different emails should produce different hashes'); } public function test_find_by_email_method_works_correctly(): void { $email = 'findme@example.com'; $emailHash = User::hashEmail($email); $this->assertNull(User::findByEmail($email), 'User should not exist yet'); $user = User::create([ 'email_hash' => $emailHash, ]); $foundUser = User::findByEmail($email); $this->assertNotNull($foundUser, 'User should be found'); $this->assertEquals($user->id, $foundUser->id, 'Found user should match created user'); $this->assertEquals($emailHash, $foundUser->email_hash, 'Email hash should match'); } public function test_find_by_email_is_case_insensitive(): void { $email = 'case@example.com'; $emailHash = User::hashEmail($email); $user = User::create([ 'email_hash' => $emailHash, ]); $foundUser1 = User::findByEmail('case@example.com'); $foundUser2 = User::findByEmail('CASE@EXAMPLE.COM'); $foundUser3 = User::findByEmail('CaSe@ExAmPlE.cOm'); $this->assertNotNull($foundUser1); $this->assertNotNull($foundUser2); $this->assertNotNull($foundUser3); $this->assertEquals($user->id, $foundUser1->id); $this->assertEquals($user->id, $foundUser2->id); $this->assertEquals($user->id, $foundUser3->id); } public function test_find_by_email_trims_whitespace(): void { $email = 'trim@example.com'; $emailHash = User::hashEmail($email); $user = User::create([ 'email_hash' => $emailHash, ]); $foundUser = User::findByEmail(' trim@example.com '); $this->assertNotNull($foundUser); $this->assertEquals($user->id, $foundUser->id); } public function test_hash_length_is_consistent(): void { $emails = [ 'a@b.c', 'test@example.com', 'very.long.email.address@subdomain.example.com', ]; $hashes = array_map(fn($email) => User::hashEmail($email), $emails); foreach ($hashes as $hash) { $this->assertEquals(64, strlen($hash), 'SHA-256 hash should always be 64 characters'); } } public function test_hash_contains_only_hexadecimal_characters(): void { $email = 'test@example.com'; $hash = User::hashEmail($email); $this->assertMatchesRegularExpression( '/^[a-f0-9]{64}$/', $hash, 'Hash should contain only lowercase hexadecimal characters' ); } }