Corp/backend/tests/refactor_tests.py
2026-09-07 19:43:48 +03:00

169 lines
5.8 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()