Полный пересмотр логики поиска по email с стратегиями

Проблема: Поиск "team_x3m@" неправильно находит клиента "Наталья natulj@bk.ru"
Причина: Использовался простой icontains для всех случаев

Решение: Добавлена функция determine_search_strategy() которая определяет
стратегию поиска на основе содержимого query:

1. email_prefix: query заканчивается на @ (например "team_x3m@")
   → Используется istartswith вместо icontains
   → Найдёт только email, начинающиеся с "team_x3m@"
   → НЕ найдёт "natulj@bk.ru" ✓

2. email_domain: query начинается с @ (например "@bk")
   → Использует icontains для поиска по домену
   → Найдёт все *@bk.ru, *@bk.com и т.д.

3. email_full: query содержит обе части (например "test@bk.ru")
   → Поиск по полному email адресу

4. universal: query без @, 3+ символов (например "natul")
   → Поиск везде: по имени И по email
   → Это позволит найти "Наталья" и "natulj@bk.ru"

5. name_only: очень короткие запросы (1-2 символа)
   → Только поиск по имени (чтобы не было ложных срабатываний)

Добавлены 23 unit-теста для покрытия всех сценариев:
- email_prefix cases: team_x3m@, user_name@, test123@
- email_domain cases: @bk, @bk.ru, @mail.google.com
- email_full cases: test@bk.ru, test@bk, user.name@mail.example.com
- universal cases: natul, abc, наталь, Test123
- name_only cases: t, te, на
- edge cases: пустая строка, @, множественные @

Все 23 теста проходят успешно ✓

Примеры работы после изменения:
- team_x3m@ → ищет email^=team_x3m (НЕ найдёт natulj@bk.ru)
- @bk → ищет все *@bk.*
- natul → ищет везде (имя + email)
- te → ищет только по имени (2 символа мало для email)
- test@bk.ru → ищет test@bk.ru

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-11 00:49:43 +03:00
parent b44ea1677f
commit 9fc0af2c2e
2 changed files with 233 additions and 14 deletions

View File

@@ -0,0 +1,160 @@
from django.test import TestCase
from .views import determine_search_strategy
class DetermineSearchStrategyTestCase(TestCase):
"""
Тесты для функции determine_search_strategy().
Проверяют, что функция правильно определяет стратегию поиска
на основе содержимого query.
"""
# ===== email_prefix: query заканчивается на @ =====
def test_email_prefix_simple(self):
"""Query "team_x3m@" должен вернуть ('email_prefix', 'team_x3m')"""
strategy, search_value = determine_search_strategy('team_x3m@')
self.assertEqual(strategy, 'email_prefix')
self.assertEqual(search_value, 'team_x3m')
def test_email_prefix_with_domain_symbol(self):
"""Query "user_name@" должен вернуть ('email_prefix', 'user_name')"""
strategy, search_value = determine_search_strategy('user_name@')
self.assertEqual(strategy, 'email_prefix')
self.assertEqual(search_value, 'user_name')
def test_email_prefix_with_numbers(self):
"""Query "test123@" должен вернуть ('email_prefix', 'test123')"""
strategy, search_value = determine_search_strategy('test123@')
self.assertEqual(strategy, 'email_prefix')
self.assertEqual(search_value, 'test123')
# ===== email_domain: query начинается с @ =====
def test_email_domain_simple(self):
"""Query "@bk" должен вернуть ('email_domain', 'bk')"""
strategy, search_value = determine_search_strategy('@bk')
self.assertEqual(strategy, 'email_domain')
self.assertEqual(search_value, 'bk')
def test_email_domain_with_extension(self):
"""Query "@bk.ru" должен вернуть ('email_domain', 'bk.ru')"""
strategy, search_value = determine_search_strategy('@bk.ru')
self.assertEqual(strategy, 'email_domain')
self.assertEqual(search_value, 'bk.ru')
def test_email_domain_with_multiple_dots(self):
"""Query "@mail.google.com" должен вернуть ('email_domain', 'mail.google.com')"""
strategy, search_value = determine_search_strategy('@mail.google.com')
self.assertEqual(strategy, 'email_domain')
self.assertEqual(search_value, 'mail.google.com')
# ===== email_full: query содержит и локальную часть, и домен =====
def test_email_full_simple(self):
"""Query "test@bk.ru" должен вернуть ('email_full', 'test@bk.ru')"""
strategy, search_value = determine_search_strategy('test@bk.ru')
self.assertEqual(strategy, 'email_full')
self.assertEqual(search_value, 'test@bk.ru')
def test_email_full_partial(self):
"""Query "test@bk" должен вернуть ('email_full', 'test@bk')"""
strategy, search_value = determine_search_strategy('test@bk')
self.assertEqual(strategy, 'email_full')
self.assertEqual(search_value, 'test@bk')
def test_email_full_complex(self):
"""Query "user.name@mail.example.com" должен вернуть ('email_full', ...)"""
strategy, search_value = determine_search_strategy('user.name@mail.example.com')
self.assertEqual(strategy, 'email_full')
self.assertEqual(search_value, 'user.name@mail.example.com')
# ===== universal: query без @, 3+ символов =====
def test_universal_three_chars(self):
"""Query "natul" (5 символов) должен вернуть ('universal', 'natul')"""
strategy, search_value = determine_search_strategy('natul')
self.assertEqual(strategy, 'universal')
self.assertEqual(search_value, 'natul')
def test_universal_three_chars_exact(self):
"""Query "abc" (3 символа) должен вернуть ('universal', 'abc')"""
strategy, search_value = determine_search_strategy('abc')
self.assertEqual(strategy, 'universal')
self.assertEqual(search_value, 'abc')
def test_universal_cyrillic(self):
"""Query "наталь" (6 символов) должен вернуть ('universal', 'наталь')"""
strategy, search_value = determine_search_strategy('наталь')
self.assertEqual(strategy, 'universal')
self.assertEqual(search_value, 'наталь')
def test_universal_mixed(self):
"""Query "Test123" (7 символов) должен вернуть ('universal', 'Test123')"""
strategy, search_value = determine_search_strategy('Test123')
self.assertEqual(strategy, 'universal')
self.assertEqual(search_value, 'Test123')
# ===== name_only: очень короткие запросы (< 3 символов без @) =====
def test_name_only_single_char(self):
"""Query "t" должен вернуть ('name_only', 't')"""
strategy, search_value = determine_search_strategy('t')
self.assertEqual(strategy, 'name_only')
self.assertEqual(search_value, 't')
def test_name_only_two_chars(self):
"""Query "te" должен вернуть ('name_only', 'te')"""
strategy, search_value = determine_search_strategy('te')
self.assertEqual(strategy, 'name_only')
self.assertEqual(search_value, 'te')
def test_name_only_two_chars_cyrillic(self):
"""Query "на" (2 символа) должен вернуть ('name_only', 'на')"""
strategy, search_value = determine_search_strategy('на')
self.assertEqual(strategy, 'name_only')
self.assertEqual(search_value, 'на')
# ===== edge cases =====
def test_empty_string(self):
"""Query "" должен вернуть ('name_only', '')"""
strategy, search_value = determine_search_strategy('')
self.assertEqual(strategy, 'name_only')
self.assertEqual(search_value, '')
def test_only_at_symbol(self):
"""Query "@" должен вернуть ('email_domain', '')"""
strategy, search_value = determine_search_strategy('@')
self.assertEqual(strategy, 'email_domain')
self.assertEqual(search_value, '')
def test_multiple_at_symbols(self):
"""Query "test@example@com" должен обработать первый @"""
strategy, search_value = determine_search_strategy('test@example@com')
self.assertEqual(strategy, 'email_full')
self.assertEqual(search_value, 'test@example@com')
def test_spaces_in_query(self):
"""Query "Ivan Petrov" должен вернуть ('universal', 'Ivan Petrov')"""
strategy, search_value = determine_search_strategy('Ivan Petrov')
self.assertEqual(strategy, 'universal')
self.assertEqual(search_value, 'Ivan Petrov')
# ===== real-world examples =====
def test_real_world_problematic_case(self):
"""
Real-world case: query "team_x3m@" не должен найти "natulj@bk.ru"
Используется email_prefix со istartswith вместо icontains
"""
strategy, search_value = determine_search_strategy('team_x3m@')
self.assertEqual(strategy, 'email_prefix')
# Важно: стратегия email_prefix, не universal или email_full
self.assertNotEqual(strategy, 'universal')
def test_real_world_domain_search(self):
"""Real-world case: query "@bk" должен найти все @bk.ru"""
strategy, search_value = determine_search_strategy('@bk')
self.assertEqual(strategy, 'email_domain')
self.assertEqual(search_value, 'bk')
def test_real_world_name_search(self):
"""Real-world case: query "natul" должен найти "Наталья" и "natulj@bk.ru" """
strategy, search_value = determine_search_strategy('natul')
self.assertEqual(strategy, 'universal')
self.assertEqual(search_value, 'natul')