155 lines
5.9 KiB
Python
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()
|