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