Загрузка данных
мод во тест
удленное есть весь класс с заменой
@dataclass
class BaseSuiteConfig:
"""
Структура:
1. Метаданные набора (имя, id, архив)
2. Технологический участок (из enum TU)
"""
# ===== Метаданные набора =====
suite_name: str
suite_data_id: int
archive_name: str = "" # Автоматически вычисляется из suite_name
# ===== Технологический участок =====
technological_unit: TU = TU.TIKHORETSK_NOVOROSSIYSK_3
# ===== LDS Configurator (Администрирование) =====
use_lds_configurator: bool = False
admin_tu_name: str = ""
resolved_tu_id: Optional[int] = None
# ===== Правила конвертации единиц измерения давления на стенде =====
measure_conversion_rules: Optional[MeasureConversionRule] = None
# ===== Общие константы (можно переопределить) =====
allowed_distance_diff_meters: int = BaseTN3Constants.ALLOWED_DISTANCE_DIFF_METERS
precision: int = BaseTN3Constants.PRECISION
basic_message_timeout: float = BaseTN3Constants.BASIC_MESSAGE_TIMEOUT
mask_message_timeout: float = BaseTN3Constants.MASK_MESSAGE_TIMEOUT
mask_du_name: Optional[str] = None
main_pipe_line: Optional[str] = None
mask_du_event: Optional[str] = None
unmask_du_event: Optional[str] = None
# ===== Свойства для удобства =====
@property
def tu_name(self) -> str:
"""Имя ТУ для бизнес-тестов (журнал, BasicInfo) — из enum TU."""
return self.technological_unit.description
@property
def tu_id(self) -> int:
"""ID технологического участка из enum TU (имитатор, WS-тесты)."""
return self.technological_unit.id
@property
def configurator_tu_id(self) -> int:
"""tuId из Администрирования после infra setup (LaunchLds/StopLds)."""
if self.resolved_tu_id is None:
raise RuntimeError(
"resolved_tu_id не установлен — выполните lds_configurator_admin_setup перед операциями Администрирования"
)
return self.resolved_tu_id
@property
def infra_tu_id(self) -> int:
"""legacy: id ТУ для инфра-setup (имитатор, tags.txt, file_name)."""
return self.technological_unit.id
@property
def has_multiple_leaks(self) -> bool:
return False
тест конст
MAIN_PAGE_SYNC_TIMEOUT_SECONDS: float = 30.0
LAUNCHED_AT_TOLERANCE_SECONDS: float = 120.0
датасет
SELECT_6_CONFIG = SmokeSuiteConfig(
# ===== Метаданные =====
suite_name=SUITE_NAME,
suite_data_id=SUITE_DATA_ID,
archive_name=ARCHIVE_NAME,
technological_unit=TECHNOLOGICAL_UNIT,
main_pipeline=MAIN_PIPELINE,
# ===== LDS Configurator =====
use_lds_configurator=True,
admin_tu_name="Тихорецк-Новороссийск-3-Автотест",
test_scenarios\lds_configurator_scenarios.py
"""
Сценарии setup/teardown СОУ через раздел Администрирование (LDS Configurator).
"""
from __future__ import annotations
import logging
from typing import Any, Dict, Optional
from pytest import fail
from clients.websocket_client import WebSocketClient
from constants.enums import SouAdminStatus
from constants.test_constants import LdsConfiguratorConstants as LdsCfgConst
from test_config.models_for_tests import BaseSuiteConfig
from utils.helpers import lds_configurator_utils as lds_utils
from utils.helpers import ws_test_utils as t_utils
from utils.helpers.ws_message_parser import ws_message_parser as parser
logger = logging.getLogger(__name__)
def _save_group_state(group_state: Optional[Dict[str, Any]], cfg: BaseSuiteConfig, tu_id: int) -> None:
"""
Сохраняет resolved tu_id и флаги в group_state для teardown в conftest.
"""
if group_state is None:
return
group_state["use_lds_configurator"] = cfg.use_lds_configurator
group_state["resolved_tu_id"] = tu_id
group_state["admin_tu_name"] = cfg.admin_tu_name
async def lds_configurator_admin_setup(
ws_client: WebSocketClient,
cfg: BaseSuiteConfig,
group_state: Optional[Dict[str, Any]] = None,
) -> None:
"""
Холодный запуск СОУ через Администрирование до старта имитатора.
1. Получить tu_id по admin_tu_name из GetBasicInfoAdmin.
2. При необходимости остановить уже запущенную СОУ.
3. LaunchLdsRequest и ожидание status=включена.
4. Подтвердить launchedAt в GetTusInformation.
"""
tu_id: int
sou_status: SouAdminStatus
logger.info("[SETUP] Получение ТУ '%s' из Администрирования", cfg.admin_tu_name)
admin_reply = await lds_utils.get_basic_info_admin_with_retry(ws_client, parser)
admin_tu = lds_utils.find_tu_by_name(admin_reply, cfg.admin_tu_name)
lds_utils.validate_admin_tu(admin_tu)
sou_status = SouAdminStatus(admin_tu.status)
tu_id = admin_tu.tuId
cfg.resolved_tu_id = tu_id
_save_group_state(group_state, cfg, tu_id)
logger.info(
"[SETUP] Найден ТУ: tuId=%s, tuName=%r, status=%s (%s)",
tu_id,
admin_tu.tuName,
sou_status,
SouAdminStatus.report_text_by_value(admin_tu.status),
)
if sou_status == SouAdminStatus.RUNNING:
logger.info("[SETUP] Остановка СОУ перед холодным запуском (уже была включена)")
await lds_utils.invoke_lds_command(ws_client, parser, LdsCfgConst.STOP_LDS_REQUEST, tu_id)
if not await lds_utils.poll_admin_tu_status(ws_client, parser, tu_id, SouAdminStatus.STOPPED):
fail(
"Не удалось перезапустить СОУ: статус в Администрировании не стал 'выключена' за 2 минуты",
pytrace=False,
)
launch_checkpoint = t_utils.moscow_now()
logger.info(
"[SETUP] Момент фиксации времени перед LaunchLds: %s",
t_utils.format_datetime_moscow(launch_checkpoint),
)
logger.info("[SETUP] Холодный запуск СОУ (LaunchLdsRequest) для tuId=%s", tu_id)
await lds_utils.invoke_lds_command(ws_client, parser, LdsCfgConst.LAUNCH_LDS_REQUEST, tu_id)
logger.info("[SETUP] Ожидание включения СОУ в Администрировании")
if not await lds_utils.poll_admin_tu_status(ws_client, parser, tu_id, SouAdminStatus.RUNNING):
fail(
"Не удалось запустить СОУ: статус в Администрировании не стал 'включена' за 2 минуты",
pytrace=False,
)
logger.info("[SETUP] Подтверждение времени запуска (GetTusInformation)")
await lds_utils.verify_launched_at(ws_client, parser, tu_id, launch_checkpoint)
async def lds_configurator_verify_after_core(
ws_client: WebSocketClient,
cfg: BaseSuiteConfig,
) -> None:
"""
Проверка готовности стенда после запуска lds-core.
1. Актуальный статус СОУ из Администрирования.
2. Сверка с Состоянием МТ (MainPageInfoContent).
3. Ожидание появления ТУ в Состоянии МТ при status=включена.
"""
tu_id = cfg.configurator_tu_id
logger.info("[SETUP] Получение актуального статуса СОУ для tuId=%s", tu_id)
admin_reply = await lds_utils.get_basic_info_admin_with_retry(ws_client, parser)
sou_status = lds_utils.get_admin_tu_status(admin_reply, tu_id)
if sou_status is None:
fail(
f"ТУ tuId={tu_id} ('{cfg.admin_tu_name}') не найден в GetBasicInfoAdminResponse",
pytrace=False,
)
logger.info("[SETUP] Сверка статуса СОУ: Администрирование vs Состояние МТ")
is_on_main_page = await lds_utils.is_tu_present_on_main_page(ws_client, parser, tu_id)
lds_utils.check_sou_status_sync(sou_status, is_on_main_page, tu_id, cfg.admin_tu_name)
if sou_status == SouAdminStatus.RUNNING:
logger.info("[SETUP] Ожидание появления ТУ в Состоянии МТ")
if not await lds_utils.poll_main_page_tu_presence(ws_client, tu_id, expect_present=True):
fail(
"СОУ не отображается в Состоянии МТ: ТУ не появилась за 2 минуты после запуска core",
pytrace=False,
)
async def lds_configurator_teardown(
ws_client: WebSocketClient,
tu_id: int,
admin_tu_name: str,
) -> None:
"""
Teardown набора: остановка СОУ после прогона.
Некритичные отклонения логируются без падения прогона.
"""
logger.info("[TEARDOWN] Проверка статуса СОУ (tuId=%s, «%s»)", tu_id, admin_tu_name)
try:
admin_reply = await lds_utils.get_basic_info_admin(ws_client, parser)
except Exception as error:
lds_utils.attach_allure_alert(
f"Не удалось получить статус СОУ при teardown: {error}. "
f"tuId={tu_id}, adminTuName={admin_tu_name!r}"
)
return
sou_status = lds_utils.get_admin_tu_status(admin_reply, tu_id)
if sou_status != SouAdminStatus.RUNNING:
lds_utils.attach_allure_alert(
f"СОУ не в статусе 'включена' при teardown (status={sou_status}), остановка пропущена. "
f"tuId={tu_id}, adminTuName='{admin_tu_name}'"
)
return
logger.info("[TEARDOWN] Остановка СОУ (StopLdsRequest) для tuId=%s", tu_id)
try:
await lds_utils.invoke_lds_command(ws_client, parser, LdsCfgConst.STOP_LDS_REQUEST, tu_id)
except Exception as error:
lds_utils.attach_allure_alert(
f"Ошибка при StopLdsRequest: {error}. tuId={tu_id}, adminTuName={admin_tu_name!r}"
)
return
logger.info("[TEARDOWN] Ожидание выключения СОУ в Администрировании")
if not await lds_utils.poll_admin_tu_status(ws_client, parser, tu_id, SouAdminStatus.STOPPED):
lds_utils.attach_allure_alert(
f"СОУ не выключилась за 2 минуты после StopLdsRequest. "
f"tuId={tu_id}, adminTuName={admin_tu_name!r}. Проверить вручную."
)
конфтест
from test_scenarios import lds_configurator_scenarios
from utils.helpers import lds_configurator_utils as lds_cfg_utils
"resolved_tu_id": None,
"admin_tu_name": None,
"suite_infra_ready": False,
удалить:
'test_lds_configurator_verify': 'lds_configurator_verify_test',
'test_lds_configurator_verify': 'lds_configurator_verify_test',
if test_name == 'test_lds_configurator_verify':
suite_config = params['config']
if not suite_config.use_lds_configurator:
return None
if not suite_config.tu_name.strip():
pytest.fail(
f"Набор '{suite_config.suite_name}': tu_name обязателен к заполнению в датасете набора при use_lds_configurator=True"
)
return suite_config.lds_configurator_verify_test
pytest.fail(
"Не удалось вычислить imitator_duration: в тестовом модуле одновременно отсутствуют "
"и @pytest.mark.offset(), и pytest.mark.imitator_duration()"
)
@pytest.fixture(autouse=True)
def require_suite_infra(request):
"""
Блокирует тесты набора, если infra-setup не завершился успешно.
"""
if not request.node.get_closest_marker("test_suite_name"):
return
cfg = request.config.group_state
if cfg.get("current_suite") and not cfg.get("suite_infra_ready"):
pytest.exit(
"[SETUP] [ERROR] Инфраструктура набора не готова (suite_infra_ready=False). "
"Тесты не запускаются."
)
# stop old
_run_lds_configurator_teardown_if_needed(cfg)
cfg["suite_start_time"] = None
cfg["suite_infra_ready"] = False
measure_conversion_rules = suite_config.measure_conversion_rules if suite_config is not None else None
if suite_config is not None and suite_config.use_lds_configurator:
if not suite_config.admin_tu_name.strip():
pytest.exit(
f"[SETUP] [ERROR] Набор '{suite_config.suite_name}': admin_tu_name обязателен "
"при use_lds_configurator=True"
)
_run_lds_admin_setup(suite_config, cfg)
except BaseException as error:
# Сохраняем время старта имитатора для расчёта интервалов утечек в тестах
cfg["imitator_start_time"] = stand_manager.start_time
if suite_config is not None and suite_config.use_lds_configurator:
try:
_run_lds_verify_after_core(suite_config)
except BaseException as error:
pytest.exit(f"[SETUP] [ERROR] LDS Configurator проверка после запуска ядра: {error}")
cfg["suite_infra_ready"] = True
yield # pytest продолжит выполнение теста
def _run_lds_infra(coro_factory) -> None:
"""Запускает async infra-сценарий с логированием вместо Allure."""
lds_cfg_utils.set_infra_mode(True)
try:
asyncio.run(coro_factory())
finally:
lds_cfg_utils.set_infra_mode(False)
async with WebSocketClient(ws_host, token) as client:
await lds_configurator_scenarios.lds_configurator_admin_setup(
client, suite_config, group_state
)
_run_lds_infra(_admin_setup)
def _run_lds_verify_after_core(suite_config) -> None:
"""
WS-проверка готовности стенда после запуска lds-core.
"""
async def _verify() -> None:
ws_host = get_ws_host()
token = get_token()
async with WebSocketClient(ws_host, token) as client:
await lds_configurator_scenarios.lds_configurator_verify_after_core(client, suite_config)
_run_lds_infra(_verify)
tu_id = cfg["resolved_tu_id"]
admin_tu_name = cfg.get("admin_tu_name") or ""
async with WebSocketClient(ws_host, token) as client:
await lds_configurator_scenarios.lds_configurator_teardown(client, tu_id, admin_tu_name)
await lds_configurator_scenarios.lds_configurator_teardown(client, tu_id, admin_tu_name)
try:
_run_lds_infra(_teardown)
cfg["use_lds_configurator"] = False
cfg["admin_tu_name"] = None
if next_suite != cfg["current_suite"]:
удалить строку
if stand_manager := cfg["stand_manager"]:
cfg["imitator_start_time"] = None
cfg["suite_infra_ready"] = False
тест смоук
удален импорт
from clients.websocket_client import WebSocketClient
from constants.test_constants import BaseTN3Constants as Base_const
from test_config.datasets import ALL_SMOKE_CONFIGS
from test_config.models_for_tests import CaseMarkers, LeakTestConfig, SmokeSuiteConfig
from test_scenarios import lds_status_scenarios
from test_scenarios import smoke_scenarios as scenarios
удалить
@pytest.mark.asyncio
@pytest.mark.critical_stop
async def test_lds_configurator_verify(
self, ws_client: WebSocketClient, config: SmokeSuiteConfig
) -> None:
tag = "LdsConfigurator"
title = f"[{tag}] Проверка наличия запускаемого ТУ на ЭФ Состояние МТ сразу после запуска core"
_apply_allure_markers(config.lds_configurator_verify_test, tag, title)
await lds_configurator_scenarios.lds_configurator_verify_after_core(ws_client, config)
тетс бэйсик сделать крит стоп
тест лдс статус регрес
удален импорт
from clients.websocket_client import WebSocketClient
from test_config.datasets import ALL_LDS_STATUS_CONFIGS
from test_config.models_for_tests import CaseMarkers, LDSStatusConfig
from test_scenarios import scenarios
удалить
@pytest.mark.asyncio
@pytest.mark.critical_stop
async def test_lds_configurator_verify(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
tag = "LdsConfigurator"
title = f"[{tag}] Проверка наличия запускаемого ТУ на ЭФ Состояние МТ сразу после запуска core"
_apply_allure_markers(config.lds_configurator_verify_test, tag, title)
await lds_configurator_scenarios.lds_configurator_verify_after_core(ws_client, config)
сделть бэйсик критом
енам
class TU(Enum):
"""Технологический участок. id - для имитатора (tn{id}_tags.txt) и WS-тестов."""