Corp/backend/tests/refactor_tests.py

155 lines
5.9 KiB
Python

#!/usr/bin/env python3
"""
Скрипт для автоматического рефакторинга тестов.
Заменяет дублирующиеся функции на импорты из conftest.py.
"""
import re
from pathlib import Path
# Базовая директория
BASE_DIR = Path(__file__).parent
# Файлы для обновления
FILES = [
'api/v1/employees/test_ldap_optional_register.py',
'api/v1/employees/test_ldap_optional_groups.py',
'api/v1/employees/test_role_assignment.py',
'api/v1/employees/test_employees.py',
'api/v1/employees/test_employees_fields_in_list.py',
'api/v1/employees/test_email_verification_security.py',
'api/v1/employees/test_department_roles_extra.py',
'api/v1/employees/test_department_roles.py',
'api/v1/employees/test_department_membership_separation.py',
'api/v1/employees/test_department_head_rights.py',
'api/v1/employees/test_departments.py',
'api/v1/documents/test_documents_api.py',
'api/v1/feed/test_posts.py',
'api/v1/calendar_app/conftest.py',
'api/v1/requests_app/conftest.py',
]
def add_imports(content: str) -> str:
"""Добавляет импорты если их нет."""
lines = content.split('\n')
# Находим последнюю строку импортов
last_import_idx = 0
for i, line in enumerate(lines):
if line.startswith('import ') or line.startswith('from '):
last_import_idx = i
# Проверяем наличие импортов
has_unique_phone = '_unique_phone' in content and 'from tests.conftest import' in content
has_password = 'DEFAULT_PASSWORD' in content and 'from tests.test_config import' in content
imports_to_add = []
if not has_unique_phone and '_unique_phone()' in content:
imports_to_add.append('from tests.conftest import _unique_phone')
if not has_password and 'password="pass"' in content:
imports_to_add.append('from tests.test_config import DEFAULT_PASSWORD')
if imports_to_add:
# Вставляем после последнего импорта
for imp in reversed(imports_to_add):
lines.insert(last_import_idx + 1, imp)
return '\n'.join(lines)
def remove_duplicate_functions(content: str) -> str:
"""Удаляет дублирующиеся функции."""
# Удаление _unique_phone
patterns = [
r'_phone_seq = count\(\d+\)\s*\n\s*\n\s*def _unique_phone\(\)[^\n]*:\s*return f"\+7999\{next\(_phone_seq\):[^}]+\}"',
r'def _unique_phone\(\)[^\n]*:\s*(?:import random\s*)?return f"\+7999000\{[^}]+\}"',
r'_seq = \d+\s*\n\s*def _uniq_phone\(\):\s*global _seq\s*_seq \+= 1\s*return f"\+7999\{_seq:[^}]+\}"',
]
for pattern in patterns:
content = re.sub(pattern, '', content, flags=re.MULTILINE)
# Удаление _uniq_email
pattern = r'def _uniq_email\([^)]*\):\s*global _seq\s*_seq \+= 1\s*return f"[^"]+"'
content = re.sub(pattern, '', content, flags=re.MULTILINE)
# Очистка лишних пустых строк
content = re.sub(r'\n\n\n+', '\n\n', content)
return content
def replace_function_calls(content: str) -> str:
"""Заменяет вызовы функций."""
replacements = [
(r'_uniq_email\(\)', '_unique_email()'),
(r'_uniq_phone\(\)', '_unique_phone()'),
(r'password="pass"', 'password=DEFAULT_PASSWORD'),
]
for old, new in replacements:
content = content.replace(old, new)
return content
def convert_make_user_to_fixture(content: str, filename: str) -> str:
"""Конвертирует функцию make_user в fixture."""
# Если есть функция make_user, оборачиваем в fixture
if 'def make_user(' in content and '@pytest.fixture' not in content.split('def make_user(')[0].split('\n')[-2]:
# Находим определение make_user
pattern = r'(def make_user\([^)]*\)[^\n]*:)'
match = re.search(pattern, content)
if match:
# Добавляем fixture перед функцией
old_def = match.group(0)
new_def = f"@pytest.fixture\n{old_def}\n \"\"\"Fixture для создания пользователей.\"\"\""
content = content.replace(old_def, new_def, 1)
return content
def process_file(file_path: Path) -> bool:
"""Обрабатывает один файл."""
if not file_path.exists():
print(f" ⏭️ Файл не найден: {file_path}")
return False
try:
content = file_path.read_text(encoding='utf-8')
original = content
# Применяем трансформации
content = add_imports(content)
content = remove_duplicate_functions(content)
content = replace_function_calls(content)
content = convert_make_user_to_fixture(content, file_path.name)
if content != original:
file_path.write_text(content, encoding='utf-8')
print(f" ✅ {file_path.name}")
return True
else:
print(f" ⏭️ {file_path.name} (без изменений)")
return False
except Exception as e:
print(f" ❌ {file_path.name}: {e}")
return False
def main():
print("🔄 Автоматический рефакторинг тестов\n")
print("=" * 60)
updated = 0
for file_rel in FILES:
file_path = BASE_DIR / file_rel
if process_file(file_path):
updated += 1
print("=" * 60)
print(f"\n✅ Обновлено: {updated}/{len(FILES)} файлов")
print("\n📝 Рекомендуется проверить тесты:")
print(" .venv/Scripts/python -m pytest backend/tests/api/v1/employees/ -v")
if __name__ == '__main__':
main()