Загрузка данных
test_lds_status_regress
"""
Архитектура параметризации:
- SUITE_PARAMS: тесты уровня набора (один раз на набор данных LDSStatusConfig)
Для добавления нового набора:
1. Создать файл в test_config/datasets/ с LDSStatusConfig
2. Добавить маппинг новых тестов в conftest.py -> LDS_STATUS_SUITE_LEVEL_MAPPING
3. Тесты подхватятся автоматически
Запуск:
- Все тесты: pytest tests/test_lds_status_regress.py
- Один набор: pytest tests/test_lds_status_regress.py --suites=Lds_status_regress_in_flow
- Несколько наборов: pytest tests/test_lds_status_regress.py --suites=Lds_status_regress_in_flow,Lds_status_regress_2
"""
import datetime
from typing import Any, List, Optional
import allure
import pytest
from clients.http_client import StandHttpClient
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 lds_status_scenarios as scenarios
from test_scenarios import smoke_scenarios
# ===== ГЕНЕРАЦИЯ ПАРАМЕТРОВ =====
def _get_suite_markers(config: LDSStatusConfig) -> List[pytest.MarkDecorator]:
"""Маркеры для тестового набора."""
return [
pytest.mark.test_suite_name(config.suite_name),
pytest.mark.test_suite_data_id(config.suite_data_id),
pytest.mark.test_data_name(config.archive_name),
pytest.mark.tu_id(config.technological_unit.id),
pytest.mark.ost_name(config.ost_name),
]
def _generate_suite_params() -> List[Any]:
"""
Генерирует параметры для тестов уровня набора данных.
Один параметр на каждый config.
"""
return [
pytest.param(config, id=config.suite_name, marks=_get_suite_markers(config))
for config in ALL_LDS_STATUS_CONFIGS
]
# ===== ПАРАМЕТРЫ ДЛЯ ТЕСТОВ =====
SUITE_PARAMS: List[Any] = _generate_suite_params()
# ===== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ =====
def _apply_allure_markers(test_config: CaseMarkers, tag: str, title: str, description: Optional[str] = None) -> None:
"""Применяет allure-маркеры из конфига теста."""
if not test_config:
pytest.skip("Не заполнена конфигурация теста: тест пропущен")
allure.dynamic.tag(tag)
allure.dynamic.tag("REGRESS")
allure.dynamic.title(title)
if description:
allure.dynamic.description(description)
# ===== ТЕСТЫ УРОВНЯ НАБОРА =====
# Запускаются один раз для каждого config
@pytest.mark.parametrize("config", SUITE_PARAMS)
class TestSuiteScenarios:
"""
Тесты уровня набора данных.
Запускаются один раз для каждого конфига.
"""
@pytest.mark.critical_stop
def test_lds_status_basic_info(self, http_client: StandHttpClient, config: LDSStatusConfig) -> None:
"""[BasicInfo] Проверка базовой информации СОУ: список ТУ"""
tag = "BasicInfo"
title = f"[{tag}] Проверка списка ТУ. ЭФ: Главная страница"
_apply_allure_markers(config.lds_status_basic_info_test, tag, title)
smoke_scenarios.basic_info(http_client, config)
@pytest.mark.asyncio
async def test_lds_status_init_accumulation_data(self, ws_client: WebSocketClient, config: LDSStatusConfig) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Инициализация,
Причина: Накопление данных
"""
tag = "CommonScheme"
title = f"[{tag}] Проверка режима работы СОУ: 'Инициализация', по причине: 'Накопление данных'. ЭФ: Схема"
_apply_allure_markers(
config.init_accumulation_data_test,
tag,
title,
(
f"Проверка режима работы СОУ на базовых ДУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
f"Время проведения проверки : {config.init_accumulation_data_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Инициализация\n "
"Ожидаемая причина режима работы СОУ: Накопление данных"
),
)
test_data = config.init_accumulation_data_test_data
await scenarios.lds_status_check_with_reasons(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_init_cold_start(self, ws_client: WebSocketClient, config: LDSStatusConfig) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Инициализация,
Причина: Холодный пуск
"""
tag = "CommonScheme"
title = f"[{tag}] Проверка режима работы СОУ: 'Инициализация', по причине: 'Холодный пуск'. ЭФ: Схема"
_apply_allure_markers(
config.init_cold_start_test,
tag,
title,
(
f"Проверка режима работы СОУ на базовых ДУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
f"Время проведения проверки : {config.init_cold_start_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Инициализация\n Ожидаемая причина режима работы СОУ: Холодный пуск"
),
)
test_data = config.init_cold_start_test_data
await scenarios.lds_status_check_on_longest_flow_area(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_init_switching_shut_off(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Инициализация,
Причина: Переключение запорной арматуры
"""
tag = "CommonScheme"
title = (
f"[{tag}] Проверка режима работы СОУ: 'Инициализация', "
"по причине: 'Переключение запорной арматуры'. ЭФ: Схема"
)
_apply_allure_markers(
config.init_switching_shut_off_test,
tag,
title,
(
f"Проверка режима работы СОУ на базовых ДУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
f"Время проведения проверки : {config.init_switching_shut_off_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Инициализация\n "
"Ожидаемая причина режима работы СОУ: Переключение запорной арматуры"
),
)
test_data = config.init_switching_shut_off_test_data
await scenarios.lds_status_check_with_reasons(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_serviceable_after_cold_start(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Исправна.
Установка режима Исправна после Инициализации, по причине Холодный пуск.
"""
tag = "CommonScheme"
title = f"[{tag}] Проверка режима работы СОУ: 'Исправна' после Иницализации. ЭФ: Схема"
_apply_allure_markers(
config.serviceable_after_cold_start_test,
tag,
title,
(
"Проверка режима работы СОУ после Инициализации, по причине: Холодный пуск, на показательных ДУ"
f" на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
f"Время проведения проверки: {config.serviceable_after_cold_start_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Исправна"
),
)
test_data = config.serviceable_all_test_data
await scenarios.lds_status_check_on_multiple_diagnostic_areas(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_serviceable_after_deg_additive_injectors_operation(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Исправна.
Установка режима Исправна после режима Ухудшение характеристик.
"""
tag = "CommonScheme"
title = f"[{tag}] Проверка режима работы СОУ: 'Исправна' после режима Ухудшение характеристик. ЭФ: Схема"
_apply_allure_markers(
config.serviceable_after_deg_additive_injectors_operation_test,
tag,
title,
(
"Проверка режима работы СОУ, после режима Ухудшение характеристик, "
"на показательных ДУ,"
f" на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.technological_unit.description}\n"
"Время проведения проверки: "
f"{config.serviceable_after_deg_additive_injectors_operation_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Исправна"
),
)
test_data = config.serviceable_all_test_data
await scenarios.lds_status_check_on_multiple_diagnostic_areas(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_serviceable_after_deg_absence_min_pressure_sensors(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Исправна.
Установка режима Исправна после режима Ухудшение характеристик.
"""
tag = "CommonScheme"
title = f"[{tag}] Проверка режима работы СОУ: 'Исправна' после режима Ухудшение характеристик. ЭФ: Схема"
_apply_allure_markers(
config.serviceable_after_deg_absence_min_pressure_sensors_test,
tag,
title,
(
"Проверка режима работы СОУ, после режима Ухудшение характеристик, "
"на показательных ДУ,"
f" на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки: "
f"{config.serviceable_after_deg_absence_min_pressure_sensors_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Исправна"
),
)
test_data = config.serviceable_all_test_data
await scenarios.lds_status_check_on_multiple_diagnostic_areas(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_serviceable_after_deg_exceeding_distance_between_flow_meters(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Исправна.
Установка режима Исправна после режима Ухудшение характеристик.
"""
tag = "CommonScheme"
title = f"[{tag}] Проверка режима работы СОУ: 'Исправна' после режима Ухудшение характеристик. ЭФ: Схема"
_apply_allure_markers(
config.serviceable_after_deg_exceeding_distance_between_flow_meters_test,
tag,
title,
(
"Проверка режима работы СОУ, после режима Ухудшение характеристик, "
"на показательных ДУ,"
f" на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.technological_unit.description}\n"
"Время проведения проверки: "
f"{config.serviceable_after_deg_exceeding_distance_between_flow_meters_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Исправна"
),
)
test_data = config.serviceable_all_test_data
await scenarios.lds_status_check_on_multiple_diagnostic_areas(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_serviceable_after_deg_starting_pumping_out_pumps(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Исправна.
Установка режима Исправна после режима Ухудшение характеристик.
"""
tag = "CommonScheme"
title = f"[{tag}] Проверка режима работы СОУ: 'Исправна' после режима Ухудшение характеристик. ЭФ: Схема"
_apply_allure_markers(
config.serviceable_after_deg_starting_pumping_out_pumps_test,
tag,
title,
(
"Проверка режима работы СОУ, после режима Ухудшение характеристик, "
"на показательных ДУ,"
f" на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки: "
f"{config.serviceable_after_deg_starting_pumping_out_pumps_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Исправна"
),
)
test_data = config.serviceable_all_test_data
await scenarios.lds_status_check_on_multiple_diagnostic_areas(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_serviceable_after_deg_faulty_pressure_sensors_at_pump(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Исправна.
Установка режима Исправна после режима Ухудшение характеристик.
"""
tag = "CommonScheme"
title = f"[{tag}] Проверка режима работы СОУ: 'Исправна' после режима Ухудшение характеристик. ЭФ: Схема"
_apply_allure_markers(
config.serviceable_after_deg_faulty_pressure_sensors_at_pump_test,
tag,
title,
(
"Проверка режима работы СОУ, после режима Ухудшение характеристик, "
"на показательных ДУ,"
f" на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки: "
f"{config.serviceable_after_deg_faulty_pressure_sensors_at_pump_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Исправна"
),
)
test_data = config.serviceable_all_test_data
await scenarios.lds_status_check_on_multiple_diagnostic_areas(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_serviceable_after_faulty(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Исправна.
Установка режима Исправна после Инициализации, по причине выхода из Неисправности.
"""
tag = "CommonScheme"
title = f"[{tag}] Проверка режима работы СОУ: 'Исправна', после Неисправности. ЭФ: Схема"
_apply_allure_markers(
config.serviceable_after_faulty_test,
tag,
title,
(
"Проверка режима работы СОУ, после Неисправности, на показательных ДУ,"
f" на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
f"Время проведения проверки: {config.serviceable_after_faulty_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Исправна"
),
)
test_data = config.serviceable_all_test_data
await scenarios.lds_status_check_on_multiple_diagnostic_areas(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_serviceable_after_switching_shut_off(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Исправна.
Установка режима Исправна после Инициализации, по причине Переключение запорной арматуры.
"""
tag = "CommonScheme"
title = f"[{tag}] Проверка режима работы СОУ: 'Исправна', после Инициализации. ЭФ: Схема"
_apply_allure_markers(
config.serviceable_after_switching_shut_off_test,
tag,
title,
(
"Проверка режима работы СОУ, после Инициализации по причине Переключение запорной арматуры"
f" на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки: "
f"{config.serviceable_after_switching_shut_off_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Исправна"
),
)
test_data = config.serviceable_all_test_data
await scenarios.lds_status_check_on_multiple_diagnostic_areas(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_degradation_additive_injectors_operation(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Ухудшение характеристик
Причина: Наличие ПТП
"""
tag = "CommonScheme"
title = f"[{tag}] Проверка режима работы СОУ: 'Ухудшение характеристик', по причине: 'Наличие ПТП'. ЭФ: Схема"
_apply_allure_markers(
config.deg_additive_injectors_operation_test,
tag,
title,
(
f"Проверка режима работы СОУ на выбранном ДУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.deg_additive_injectors_operation_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Ухудшение характеристик\n "
"Ожидаемая причина режима работы СОУ: Наличие ПТП"
),
)
test_data = config.deg_additive_injectors_operation_test_data
await scenarios.lds_status_check_with_reasons(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_degradation_exceeding_distance_between_pressure_sensors(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Ухудшение характеристик
Причина: расстояние между СИ давления более 50 км
"""
tag = "CommonScheme"
title = (
f"[{tag}] Проверка режима работы СОУ: 'Ухудшение характеристик', "
"по причине: 'Расстояние между СИ давления более 50 км'. ЭФ: Схема"
)
_apply_allure_markers(
config.deg_exceeding_distance_between_pressure_sensors_test,
tag,
title,
(
f"Проверка режима работы СОУ на выбранном ДУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.deg_exceeding_distance_between_pressure_sensors_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Ухудшение характеристик\n "
"Ожидаемая причина режима работы СОУ: Расстояние между СИ давления более 50 км"
),
)
test_data = config.deg_exceeding_distance_between_pressure_sensors_test_data
await scenarios.lds_status_check_with_reasons(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_degradation_absence_min_pressure_sensors(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Ухудшение характеристик
Причина: менее 4 исправных СИ давления
"""
tag = "CommonScheme"
title = (
f"[{tag}] Проверка режима работы СОУ: 'Ухудшение характеристик', "
"по причине: 'менее 4 исправных СИ давления'. ЭФ: Схема"
)
_apply_allure_markers(
config.deg_absence_min_pressure_sensors_test,
tag,
title,
(
f"Проверка режима работы СОУ на выбранном ДУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.deg_absence_min_pressure_sensors_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Ухудшение характеристик\n "
"Ожидаемая причина режима работы СОУ: Ухудшение При наличии менее четырех (3, 6) исправных СИ давления "
"на разных КП ЛЧ МТ/НПС на диагностическом участке (кроме случая нахождения трубопровода "
"в режиме остановленной перекачки)"
),
)
test_data = config.deg_absence_min_pressure_sensors_test_data
await scenarios.lds_status_check_with_reasons(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_degradation_faulty_pressure_sensors_at_pump_station(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Ухудшение характеристик
Причина: Отказ СИ давления на входе/выходе НПС
"""
tag = "CommonScheme"
title = (
f"[{tag}] Проверка режима работы СОУ: 'Ухудшение характеристик', "
"по причине: 'Отказ СИ давления на входе/выходе НПС'. ЭФ: Схема"
)
_apply_allure_markers(
config.deg_faulty_pressure_sensors_at_pump_station_test,
tag,
title,
(
f"Проверка режима работы СОУ на выбранном ДУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.deg_faulty_pressure_sensors_at_pump_station_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Ухудшение характеристик\n "
"Ожидаемая причина режима работы СОУ: Отказ СИ давления на входе/выходе НПС"
),
)
test_data = config.deg_faulty_pressure_sensors_at_pump_station_test_data
await scenarios.lds_status_check_with_reasons(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_degradation_pig_sensor_passage(
self, ws_client: WebSocketClient, http_client: StandHttpClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Ухудшение характеристик
Причина: прохождение СОД
"""
tag = "CommonScheme"
title = f"[{tag}] Проверка режима работы СОУ: 'Ухудшение характеристик', по причине: 'прохождение СОД'"
_apply_allure_markers(
config.deg_pig_sensor_passage_test,
tag,
title,
(
f"Проверка режима работы СОУ на выбранном ДУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
f"Время проведения проверки : {config.deg_pig_sensor_passage_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Ухудшение характеристик\n "
"Ожидаемая причина режима работы СОУ: прохождение СОД "
),
)
test_data = config.deg_pig_sensor_passage_test_data
await scenarios.lds_status_check_degradation_pig_sensor_passage(ws_client, http_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_degradation_gravity_section_pumping(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Ухудшение характеристик
Причина: Наличие самотечного участка/участка с неполным сечением
"""
tag = "CommonScheme"
title = (
f"[{tag}] Проверка режима работы СОУ: 'Ухудшение характеристик', "
"по причине: 'Наличие самотечного участка/участка с неполным сечением'"
)
_apply_allure_markers(
config.deg_gravity_section_pumping_test,
tag,
title,
(
f"Проверка режима работы СОУ на выбранном ДУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
f"Время проведения проверки : {config.deg_gravity_section_pumping_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Ухудшение характеристик\n "
"Ожидаемая причина режима работы СОУ: Наличие самотечного участка/участка с неполным сечением "
),
)
test_data = config.deg_gravity_section_pumping_test_data
await scenarios.lds_status_check_with_reasons(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_degradation_gravity_section_pumping_in_stopping(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Ухудшение характеристик
Причина: Наличие самотечного участка в режиме остановленной перекачки
"""
tag = "CommonScheme"
title = (
f"[{tag}] Проверка режима работы СОУ: 'Ухудшение характеристик', "
"по причине: 'Наличие самотечного участка в режиме остановленной перекачки'"
)
_apply_allure_markers(
config.deg_gravity_section_pumping_in_stopping_test,
tag,
title,
(
f"Проверка режима работы СОУ на всей ЛЧ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.deg_gravity_section_pumping_in_stopping_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Ухудшение характеристик\n "
"Ожидаемая причина режима работы СОУ: Наличие самотечного участка в режиме остановленной перекачки "
),
)
test_data = config.deg_gravity_section_pumping_in_stopping_test_data
await scenarios.lds_status_check_on_longest_flow_area(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_degradation_starting_pumping_out_pumps(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ и МТ
Причина: Работа насосов откачки
"""
tag = "CommonScheme"
title = f"[{tag}] Проверка режимов работы СОУ и МТ, по причине: 'Работа насосов откачки'. ЭФ: Схема"
_apply_allure_markers(
config.deg_starting_pumping_out_pumps_test,
tag,
title,
(
f"Проверка режимов работы СОУ и МТ на выбранном ДУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.deg_starting_pumping_out_pumps_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Ухудшение характеристик\n "
"Ожидаемый режим работы МТ: Нестационарный\n "
"Ожидаемая причина режима работы СОУ: Работа насосов откачки"
"Ожидаемая причина режима работы МТ: Работа насосов откачки"
),
)
test_data = config.deg_starting_pumping_out_pumps_test_data
await scenarios.lds_and_stationary_status_check_with_reasons(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_degradation_exceeding_distance_between_flow_meters(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Ухудшение характеристик
Причина: Расстояние между СИ расхода на пути перекачки более 200 км
"""
tag = "CommonScheme"
title = (
f"[{tag}] Проверка режима работы СОУ: 'Ухудшение характеристик', "
"по причине: 'Расстояние между СИ расхода на пути перекачки более 200 км'. ЭФ: Схема"
)
_apply_allure_markers(
config.deg_exceeding_distance_between_flow_meters_test,
tag,
title,
(
f"Проверка режима работы СОУ на выбранном ДУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.deg_exceeding_distance_between_flow_meters_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Ухудшение характеристик\n "
"Ожидаемая причина режима работы СОУ: Расстояние между СИ расхода на пути перекачки более 200 км"
),
)
test_data = config.deg_exceeding_distance_between_flow_meters_test_data
await scenarios.lds_status_check_with_reasons(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_init_exiting_faulty(self, ws_client: WebSocketClient, config: LDSStatusConfig) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Инициализация,
Причина: Выход СОУ из режима «Неисправна»
"""
tag = "CommonScheme"
title = (
f"[{tag}] Проверка режима работы СОУ: 'Инициализация', "
"по причине: 'Выход СОУ из режима «Неисправна»'. ЭФ: Схема"
)
_apply_allure_markers(
config.init_exiting_faulty_test,
tag,
title,
(
f"Проверка режима работы СОУ на выбранном ДУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
f"Время проведения проверки : {config.init_exiting_faulty_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Инициализация\n "
"Ожидаемая причина режима работы СОУ: Выход СОУ из режима «Неисправна»"
),
)
test_data = config.init_exiting_faulty_test_data
await scenarios.lds_status_check_with_reasons(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_degradation_rejection_temperature_sensor_on_du_2(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Ухудшение характеристик
Причина: Отказ СИ температуры
"""
tag = "CommonScheme"
title = (
f"[{tag}] Проверка режима работы СОУ на ДУ 2: 'Ухудшение характеристик', "
"по причине: 'Отказ СИ температуры'. ЭФ: Схема"
)
_apply_allure_markers(
config.deg_rejection_temperature_sensor_on_du_2_test,
tag,
title,
(
f"Проверка режима работы СОУ на ДУ 2, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.deg_rejection_temperature_sensor_on_du_2_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Ухудшение характеристик\n "
"Ожидаемая причина режима работы СОУ: Отказ СИ температуры"
),
)
test_data = config.deg_rejection_temperature_sensor_on_du_2_test_data
await scenarios.lds_status_check_with_reasons(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_degradation_rejection_temperature_sensor_on_du_3(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Ухудшение характеристик
Причина: Отказ СИ температуры
"""
tag = "CommonScheme"
title = (
f"[{tag}] Проверка режима работы СОУ на ДУ 3: 'Ухудшение характеристик', "
"по причине: 'Отказ СИ температуры'. ЭФ: Схема"
)
_apply_allure_markers(
config.deg_rejection_temperature_sensor_on_du_3_test,
tag,
title,
(
f"Проверка режима работы СОУ на ДУ 3, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.deg_rejection_temperature_sensor_on_du_3_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Ухудшение характеристик\n "
"Ожидаемая причина режима работы СОУ: Отказ СИ температуры"
),
)
test_data = config.deg_rejection_temperature_sensor_on_du_3_test_data
await scenarios.lds_status_check_with_reasons(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_degradation_rejection_temperature_sensor_on_du_5(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Ухудшение характеристик
Причина: Отказ СИ температуры
"""
tag = "CommonScheme"
title = (
f"[{tag}] Проверка режима работы СОУ на ДУ 5: 'Ухудшение характеристик', "
"по причине: 'Отказ СИ температуры'. ЭФ: Схема"
)
_apply_allure_markers(
config.deg_rejection_temperature_sensor_on_du_5_test,
tag,
title,
(
f"Проверка режима работы СОУ на ДУ 5, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.deg_rejection_temperature_sensor_on_du_5_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Ухудшение характеристик\n "
"Ожидаемая причина режима работы СОУ: Отказ СИ температуры"
),
)
test_data = config.deg_rejection_temperature_sensor_on_du_5_test_data
await scenarios.lds_status_check_with_reasons(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_degradation_rejection_density_and_viscosity_on_du_2(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Ухудшение характеристик
Причины: Отказ СИ плотности и СИ вязкости
"""
tag = "CommonScheme"
title = (
f"[{tag}] Проверка режима работы СОУ на ДУ 2: 'Ухудшение характеристик', "
"по причинам: 'Отказ СИ плотности' и 'Отказ СИ вязкости'. ЭФ: Схема"
)
_apply_allure_markers(
config.deg_rejection_density_and_viscosity_on_du_2_test,
tag,
title,
(
f"Проверка режима работы СОУ на ДУ 2, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.deg_rejection_density_and_viscosity_on_du_2_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Ухудшение характеристик\n "
"Ожидаемая причина режима работы СОУ: Отказ СИ плотности"
"Ожидаемая причина режима работы СОУ: Отказ СИ вязкости"
),
)
test_data = config.deg_rejection_density_and_viscosity_on_du_2_test_data
await scenarios.lds_status_check_with_2_reasons(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_degradation_rejection_density_and_viscosity_on_du_3(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Ухудшение характеристик
Причины: Отказ СИ плотности и СИ вязкости
"""
tag = "CommonScheme"
title = (
f"[{tag}] Проверка режима работы СОУ на ДУ 3: 'Ухудшение характеристик', "
"по причинам: 'Отказ СИ плотности' и 'Отказ СИ вязкости'. ЭФ: Схема"
)
_apply_allure_markers(
config.deg_rejection_density_and_viscosity_on_du_3_test,
tag,
title,
(
f"Проверка режима работы СОУ на ДУ 3, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.deg_rejection_density_and_viscosity_on_du_3_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Ухудшение характеристик\n "
"Ожидаемая причина режима работы СОУ: Отказ СИ плотности"
"Ожидаемая причина режима работы СОУ: Отказ СИ вязкости"
),
)
test_data = config.deg_rejection_density_and_viscosity_on_du_3_test_data
await scenarios.lds_status_check_with_2_reasons(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_degradation_rejection_density_and_viscosity_on_du_5(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Ухудшение характеристик
Причины: Отказ СИ плотности и СИ вязкости
"""
tag = "CommonScheme"
title = (
f"[{tag}] Проверка режима работы СОУ на ДУ 5: 'Ухудшение характеристик', "
"по причинам: 'Отказ СИ плотности' и 'Отказ СИ вязкости'. ЭФ: Схема"
)
_apply_allure_markers(
config.deg_rejection_density_and_viscosity_on_du_5_test,
tag,
title,
(
f"Проверка режима работы СОУ на ДУ 5, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.deg_rejection_density_and_viscosity_on_du_5_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Ухудшение характеристик\n "
"Ожидаемая причина режима работы СОУ: Отказ СИ плотности"
"Ожидаемая причина режима работы СОУ: Отказ СИ вязкости"
),
)
test_data = config.deg_rejection_density_and_viscosity_on_du_5_test_data
await scenarios.lds_status_check_with_2_reasons(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_faulty_absence_min_flow_meters(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Неисправна
Причина: Отсутствие минимального количества СИ Расхода
"""
tag = "CommonScheme"
title = (
f"[{tag}] Проверка режима работы СОУ: 'Неисправна', "
"по причине: 'Отсутствие минимального количества СИ Расхода'. ЭФ: Схема"
)
_apply_allure_markers(
config.faulty_absence_min_flow_meters_test,
tag,
title,
(
f"Проверка режима работы СОУ на выбранном ДУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.faulty_absence_min_flow_meters_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Неисправна\n "
"Ожидаемая причина режима работы СОУ: При одновременном выполнении следующих условий:"
"- отсутствие достоверных показаний граничного на диагностическом участке СИ расхода (кроме "
"отсеченных от рассматриваемого участка СИ расхода для трубопровода в режиме остановленной перекачки);"
"- отсутствие смежного с данным СИ расхода диагностического участка "
"с достоверными показаниями СИ расхода"
),
)
test_data = config.faulty_absence_min_flow_meters_test_data
await scenarios.lds_status_check_with_reasons(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_faulty_absence_min_flow_meters_continuous(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Неисправна
Причина: Отсутствие минимального количества СИ Расхода
После устранения причины неисправности
"""
tag = "CommonScheme"
title = (
f"[{tag}] Проверка режима работы СОУ: 'Неисправна', "
"по причине: 'Отсутствие минимального количества СИ Расхода'. "
"После устранения причины неисправности. ЭФ: Схема"
)
_apply_allure_markers(
config.faulty_absence_min_flow_meters_continuous_test,
tag,
title,
(
f"Проверка режима работы СОУ на выбранном ДУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.faulty_absence_min_flow_meters_continuous_test.offset} мин.\n"
"После устранения причины неисправности (Заменяет Инициализацию при непродолжительной неисправности)\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Неисправна\n "
"Ожидаемая причина режима работы СОУ: При одновременном выполнении следующих условий:"
"- отсутствие достоверных показаний граничного на диагностическом участке СИ расхода (кроме "
"отсеченных от рассматриваемого участка СИ расхода для трубопровода в режиме остановленной перекачки);"
"- отсутствие смежного с данным СИ расхода диагностического участка "
"с достоверными показаниями СИ расхода"
),
)
test_data = config.faulty_absence_min_flow_meters_test_data
await scenarios.lds_status_check_with_reasons(ws_client, config, test_data)
@pytest.mark.asyncio
async def test_lds_status_faulty_absence_min_pressure_sensors(
self, ws_client: WebSocketClient, config: LDSStatusConfig
) -> None:
"""
[CommonScheme] Проверка режима работы СОУ: Неисправна
Причина: Менее 4 КП с достоверными СИ давления
"""
tag = "CommonScheme"
title = (
f"[{tag}] Проверка режима работы СОУ: 'Неисправна', "
"по причине: 'Менее 4 КП с достоверными СИ давления'. ЭФ: Схема"
)
_apply_allure_markers(
config.faulty_absence_min_pressure_sensors_test,
tag,
title,
(
f"Проверка режима работы СОУ на выбранном ДУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.faulty_absence_min_pressure_sensors_test.offset} мин.\n"
"Подписка на сообщения типа: CommonScheme\n"
"Ожидаемый режим работы СОУ: Неисправна\n "
"Ожидаемая причина режима работы СОУ: Менее 4 КП с достоверными СИ давления"
),
)
test_data = config.faulty_absence_min_pressure_sensors_test_data
await scenarios.lds_status_check_with_reasons(ws_client, config, test_data)
def test_lds_status_init_accumulation_data_in_journal(
self, http_client: StandHttpClient, config: LDSStatusConfig
) -> None:
"""
[MessagesInfo] Проверка режима работы СОУ в журнале: Инициализация,
Причина: Накопление данных
"""
tag = "MessagesInfo"
title = (
f"[{tag}] Проверка записи в журнале о режиме работы СОУ: "
"'Инициализация', по причине: 'Накопление данных'. ЭФ: Журнал. Реальное время"
)
_apply_allure_markers(
config.init_accumulation_data_in_journal_test,
tag,
title,
(
f"Проверка записи в журнале о режиме работы СОУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
f"Время проведения проверки : {config.init_accumulation_data_in_journal_test.offset} мин.\n"
"Синхронный запрос типа: MessagesInfo с фильтром messageTypes=LDS_STATUS\n"
"Ожидаемый режим работы СОУ: Инициализация\n Ожидаемая причина режима работы СОУ: Накопление данных"
),
)
test_data = config.init_accumulation_data_in_journal_test_data
scenarios.lds_status_in_journal(http_client, config, test_data)
@pytest.mark.asyncio
def test_lds_status_init_cold_start_in_journal(
self, http_client: StandHttpClient, config: LDSStatusConfig, imitator_start_time: datetime
) -> None:
"""
[MessagesInfo] Проверка режима работы СОУ в журнале: Инициализация,
Причина: Холодный пуск
"""
tag = "MessagesInfo"
title = (
f"[{tag}] Проверка записи в журнале о режиме работы СОУ: "
"'Инициализация', по причине: 'Холодный пуск'. ЭФ: Журнал. Реальное время"
)
_apply_allure_markers(
config.init_cold_start_in_journal_test,
tag,
title,
(
f"Проверка записи в журнале о режиме работы СОУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
f"Время проведения проверки : {config.init_cold_start_in_journal_test.offset} мин.\n"
"Синхронный запрос типа: MessagesInfo с фильтром messageTypes=LDS_STATUS\n"
"Ожидаемый режим работы СОУ: Инициализация\n Ожидаемая причина режима работы СОУ: Холодный пуск"
),
)
smoke_scenarios.lds_status_init_in_journal(http_client, config, imitator_start_time)
def test_lds_status_init_switching_shut_off_in_journal(
self, http_client: StandHttpClient, config: LDSStatusConfig
) -> None:
"""
[MessagesInfo] Проверка режима работы СОУ в журнале: Инициализация,
Причина: Переключение запорной арматуры
"""
tag = "MessagesInfo"
title = (
f"[{tag}] Проверка записи в журнале о режиме работы СОУ: "
"'Инициализация', по причине: 'Переключение запорной арматуры'. ЭФ: Журнал. Реальное время"
)
_apply_allure_markers(
config.init_switching_shut_off_in_journal_test,
tag,
title,
(
f"Проверка записи в журнале о режиме работы СОУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.init_switching_shut_off_in_journal_test.offset} мин.\n"
"Синхронный запрос типа: MessagesInfo с фильтром messageTypes=LDS_STATUS\n"
"Ожидаемый режим работы СОУ: Инициализация\n "
"Ожидаемая причина режима работы СОУ: Переключение запорной арматуры"
),
)
test_data = config.init_switching_shut_off_in_journal_test_data
scenarios.lds_status_in_journal(http_client, config, test_data)
def test_lds_status_serviceable_after_cold_start_in_journal(
self,
http_client: StandHttpClient,
config: LDSStatusConfig,
) -> None:
"""
[MessagesInfo] Проверка режима работы СОУ в журнале: Исправна.
Установка режима Исправна после Инициализации, по причине Холодный пуск.
"""
tag = "MessagesInfo"
title = (
f"[{tag}] Проверка записи в журнале о режиме работы СОУ: "
"'Исправна', после Инициализации. ЭФ: Журнал. Реальное время"
)
_apply_allure_markers(
config.serviceable_after_cold_start_in_journal_test,
tag,
title,
(
f"Проверка записи в журнале о режиме работы СОУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.serviceable_after_cold_start_in_journal_test.offset} мин.\n"
"Синхронный запрос типа: MessagesInfo с фильтром messageTypes=LDS_STATUS\n"
"Ожидаемый режим работы СОУ: Исправна\n"
),
)
test_data = config.serviceable_all_in_journal_test_data
scenarios.lds_status_in_journal(http_client, config, test_data)
def test_lds_status_serviceable_after_switching_shut_off_in_journal(
self,
http_client: StandHttpClient,
config: LDSStatusConfig,
) -> None:
"""
[MessagesInfo] Проверка режима работы СОУ в журнале: Исправна.
Установка режима Исправна после Инициализации, по причине Переключение запорной арматуры.
"""
tag = "MessagesInfo"
title = (
f"[{tag}] Проверка записи в журнале о режиме работы СОУ: "
"'Исправна', после Инициализации. ЭФ: Журнал. Реальное время"
)
_apply_allure_markers(
config.serviceable_after_switching_shut_off_in_journal_test,
tag,
title,
(
f"Проверка записи в журнале о режиме работы СОУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.serviceable_after_switching_shut_off_in_journal_test.offset} мин.\n"
"Синхронный запрос типа: MessagesInfo с фильтром messageTypes=LDS_STATUS\n"
"Ожидаемый режим работы СОУ: Исправна\n"
),
)
test_data = config.serviceable_all_in_journal_test_data
scenarios.lds_status_in_journal(http_client, config, test_data)
def test_lds_status_serviceable_after_deg_faulty_pressure_sensors_at_pump_in_journal(
self,
http_client: StandHttpClient,
config: LDSStatusConfig,
) -> None:
"""
[MessagesInfo] Проверка режима работы СОУ в журнале: Исправна.
Установка режима Исправна после режима Ухудшение характеристик.
"""
tag = "MessagesInfo"
title = (
f"[{tag}] Проверка записи в журнале о режиме работы СОУ: "
"'Исправна', после режима Ухудшение характеристик. ЭФ: Журнал. Реальное время"
)
_apply_allure_markers(
config.serviceable_after_deg_faulty_pressure_sensors_at_pump_in_journal_test,
tag,
title,
(
f"Проверка записи в журнале о режиме работы СОУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.serviceable_after_deg_faulty_pressure_sensors_at_pump_in_journal_test.offset} мин.\n"
"Синхронный запрос типа: MessagesInfo с фильтром messageTypes=LDS_STATUS\n"
"Ожидаемый режим работы СОУ: Исправна\n"
),
)
test_data = config.serviceable_all_in_journal_test_data
scenarios.lds_status_in_journal(http_client, config, test_data)
def test_lds_status_degradation_gravity_section_pumping_in_stopping_in_journal(
self,
http_client: StandHttpClient,
config: LDSStatusConfig,
) -> None:
"""
[MessagesInfo] Проверка режима работы СОУ в журнале: Ухудшение характеристик
Причина: Наличие самотечного участка в режиме остановленной перекачки
"""
tag = "MessagesInfo"
title = (
f"[{tag}] Проверка записи в журнале о режиме работы СОУ: 'Ухудшение характеристик', "
"по причине: 'Наличие самотечного участка в режиме остановленной перекачки'"
)
_apply_allure_markers(
config.deg_gravity_section_pumping_in_stopping_in_journal_test,
tag,
title,
(
f"Проверка записи в журнале о режиме работы СОУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.deg_gravity_section_pumping_in_stopping_in_journal_test.offset} мин.\n"
"Синхронный запрос типа: MessagesInfo с фильтром messageTypes=LDS_STATUS\n"
"Ожидаемый режим работы СОУ: Ухудшение характеристик\n "
"Ожидаемая причина режима работы СОУ: Наличие самотечного участка в режиме остановленной перекачки "
),
)
test_data = config.deg_gravity_section_pumping_in_stopping_in_journal_test_data
scenarios.lds_status_in_journal(http_client, config, test_data)
def test_lds_status_degradation_exceeding_distance_between_pressure_sensors_in_journal(
self,
http_client: StandHttpClient,
config: LDSStatusConfig,
) -> None:
"""
[MessagesInfo] Проверка режима работы СОУ в журнале: Ухудшение характеристик
Причина: расстояние между СИ давления более 50 км
"""
tag = "MessagesInfo"
title = (
f"[{tag}] Проверка записи в журнале о режиме работы СОУ: 'Ухудшение характеристик', "
"по причине: 'Расстояние между СИ давления более 50 км'"
)
_apply_allure_markers(
config.deg_exceeding_distance_between_pressure_sensors_in_journal_test,
tag,
title,
(
f"Проверка записи в журнале о режиме работы СОУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.deg_exceeding_distance_between_pressure_sensors_in_journal_test.offset} мин.\n"
"Синхронный запрос типа: MessagesInfo с фильтром messageTypes=LDS_STATUS\n"
"Ожидаемый режим работы СОУ: Ухудшение характеристик\n "
"Ожидаемая причина режима работы СОУ: Расстояние между СИ давления более 50 км "
),
)
test_data = config.deg_exceeding_distance_between_pressure_sensors_in_journal_test_data
scenarios.lds_status_in_journal(http_client, config, test_data)
def test_lds_status_degradation_faulty_pressure_sensors_at_pump_station_in_journal(
self,
http_client: StandHttpClient,
config: LDSStatusConfig,
) -> None:
"""
[MessagesInfo] Проверка режима работы СОУ в журнале: Ухудшение характеристик
Причина: Отказ СИ давления на входе/выходе НПС
"""
tag = "MessagesInfo"
title = (
f"[{tag}] Проверка записи в журнале о режиме работы СОУ: 'Ухудшение характеристик', "
"по причине: 'Отказ СИ давления на входе/выходе НПС'"
)
_apply_allure_markers(
config.deg_faulty_pressure_sensors_at_pump_station_in_journal_test,
tag,
title,
(
f"Проверка записи в журнале о режиме работы СОУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.deg_faulty_pressure_sensors_at_pump_station_in_journal_test.offset} мин.\n"
"Синхронный запрос типа: MessagesInfo с фильтром messageTypes=LDS_STATUS\n"
"Ожидаемый режим работы СОУ: Ухудшение характеристик\n "
"Ожидаемая причина режима работы СОУ: Отказ СИ давления на входе/выходе НПС "
),
)
test_data = config.deg_faulty_pressure_sensors_at_pump_station_in_journal_test_data
scenarios.lds_status_in_journal(http_client, config, test_data)
def test_lds_status_faulty_absence_min_pressure_sensors_in_journal(
self,
http_client: StandHttpClient,
config: LDSStatusConfig,
) -> None:
"""
[MessagesInfo] Проверка режима работы СОУ в журнале: Неисправна
Причина: Менее 4 КП с достоверными СИ давления
"""
tag = "MessagesInfo"
title = (
f"[{tag}] Проверка записи в журнале о режиме работы СОУ: 'Неисправна', "
"по причине: 'Менее 4 КП с достоверными СИ давления'"
)
_apply_allure_markers(
config.faulty_absence_min_pressure_sensors_in_journal_test,
tag,
title,
(
f"Проверка записи в журнале о режиме работы СОУ, на наборе данных {config.suite_name}, \n"
f"на технологическом участке {config.tu_name}\n"
"Время проведения проверки : "
f"{config.faulty_absence_min_pressure_sensors_in_journal_test.offset} мин.\n"
"Синхронный запрос типа: MessagesInfo с фильтром messageTypes=LDS_STATUS\n"
"Ожидаемый режим работы СОУ: Неисправна\n "
"Ожидаемая причина режима работы СОУ: Менее 4 КП с достоверными СИ давления "
),
)
test_data = config.faulty_absence_min_pressure_sensors_in_journal_test_data
scenarios.lds_status_in_journal(http_client, config, test_data)
conftest
import asyncio
import glob
import os
import shutil
import threading
import time
import allure
import pytest
import pytest_asyncio
from clients.testops_client import AllureResultsUploader, logger
from constants.architecture_constants import ImitatorConstants as ImConst
from constants.enums import RejectionSensorTag
from constants.test_constants import BaseTN3Constants
from infra.stand_setup_manager import StandSetupManager
from test_config.datasets import get_config_by_name
from test_scenarios import lds_configurator_scenarios
from utils.helpers import lds_configurator_utils as lds_cfg_utils
from utils.helpers.pytest_auth import (
clear_suite_auth,
ensure_auth_for_fixture,
ensure_suite_auth,
init_http_stand_client,
init_ws_stand_client,
)
from utils.helpers.ws_message_parser import ws_message_parser as lds_ws_parser
def pytest_addoption(parser):
"""
Добавляет кастомные опции командной строки pytest.
"""
parser.addoption(
"--suites",
action="store",
default=None,
help="Запустить только указанные наборы данных. Пример: --suites=select_4,select_19_20",
)
def _find_config_by_suite_name(suite_name: str):
"""Находит конфиг по имени набора данных."""
try:
return get_config_by_name(suite_name)
except ValueError:
return None
@pytest.fixture(autouse=True)
def allure_suite_hierarchy(request):
"""
Автоматически устанавливает иерархию Allure для группировки тестов по наборам данных.
В Allure отчёте тесты группируются:
- Parent Suite: SingleLeakSuite / MultiLeakSuite (тип набора)
- Suite: select_4 / select_6 / ... (имя набора данных)
Работает как с параметризованными тестами (config в параметрах),
так и с обычными тестами (через маркер test_suite_name).
"""
config = None
suite_name = None
# Пробуем получить конфиг из параметризации
if hasattr(request, 'fixturenames') and 'config' in request.fixturenames:
try:
config = request.getfixturevalue('config')
suite_name = config.suite_name
except Exception:
pass
# Если не нашли, пробуем найти конфиг по маркеру test_suite_name
if not config:
marker = request.node.get_closest_marker('test_suite_name')
if marker:
suite_name = marker.args[0]
config = _find_config_by_suite_name(suite_name)
if config and suite_name:
parent_suite = "MultiLeakSuite" if config.has_multiple_leaks else "SingleLeakSuite"
allure.dynamic.parent_suite(parent_suite)
allure.dynamic.suite(suite_name)
def pytest_configure(config):
"""
Храним состояние сессии
"""
config.addinivalue_line("markers", "critical_stop: если тест упал, останавливаем дальнейшее выполнение сессии")
config.group_state = {
"current_suite": None,
"suite_start_time": None,
"stand_manager": None,
"imitator_start_time": None, # datetime объект времени старта имитатора для расчёта интервалов утечек
"is_target_tu_active": False,
"use_lds_configurator": True,
"resolved_tu_id": None,
"admin_tu_name": None,
"pre_run_running_tus": None,
"suite_infra_ready": False,
"suite_setup_failure": None,
"stand_host": None,
"auth_token": None,
"x_user_id": None,
"auth_suite": None,
}
def _update_sensor_ids(stand_manager: StandSetupManager) -> None:
"""
Для тестов датчиков обновляет sensor_id по address из конфигурации стенда.
"""
sensor_ids_by_address = stand_manager.get_sensor_ids_by_address()
BaseTN3Constants.SENSOR_IDS_BY_ADDRESS.update(sensor_ids_by_address)
RejectionSensorTag.update_ids_from_config(sensor_ids_by_address)
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
"""
Делает падение критического теста с маркировкой critical_stop однозначным:
- рисуем fail для теста
- после него прекращаем запуск остальных тестов
"""
outcome = yield
report = outcome.get_result()
if report.when == "call" and report.failed and item.get_closest_marker("critical_stop"):
item.session.shouldstop = f"Критическая проверка упала: {item.nodeid}"
# ===== Маппинг имён тестов на атрибуты конфига для получения маркеров =====
# Используется для добавления offset и test_case_id маркеров во время сбора тестов
# Smoke-тесты уровня набора (маркеры из SmokeSuiteConfig)
SMOKE_SUITE_LEVEL_MAPPING = {
'test_basic_info': 'basic_info_test',
'test_journal_info': 'journal_info_test',
'test_imitate_pressure_sensor_signal': 'imitate_pressure_sensor_signal_test',
'test_imitate_flowmeter_signal': 'imitate_flowmeter_signal_test',
'test_lds_status_initialization': 'lds_status_initialization_test',
'test_lds_status_init_in_journal': 'lds_status_init_in_journal_test',
'test_main_page_info': 'main_page_info_test',
'test_main_page_info_signals': 'main_page_info_signals_test',
'test_mask_signal': 'mask_signal_test',
'test_mask_info_in_journal': 'mask_info_in_journal_test',
'test_lds_status_initialization_out': 'lds_status_initialization_out_test',
'test_lds_status_init_out_in_journal': 'lds_status_init_out_in_journal_test',
'test_main_page_info_unstationary': 'main_page_info_unstationary_test',
'test_mask_du_on_mini_scheme': 'mask_du_on_mini_scheme_test',
'test_unmask_du_on_mini_scheme': 'unmask_du_on_mini_scheme_test',
'test_lds_status_after_confirming_leak': 'lds_status_after_confirming_leak_test',
'test_lds_status_completed_leak': 'lds_status_completed_leak_test',
'test_diagnostics_of_signals_after_initialization': 'diagnostics_of_signals_after_initialization_test',
'test_mode_mt_in_journal': 'mode_mt_in_journal_test',
'test_export_lds_status_report': 'export_lds_status_report_test',
'test_export_mt_mode_report': 'export_mt_mode_report_test',
}
# Regress-тесты режимов СОУ (маркеры из LDSStatusConfig)
LDS_STATUS_SUITE_LEVEL_MAPPING = {
'test_lds_status_basic_info': 'lds_status_basic_info_test',
'test_lds_status_init_accumulation_data': 'init_accumulation_data_test',
'test_lds_status_init_accumulation_data_in_journal': 'init_accumulation_data_in_journal_test',
'test_lds_status_init_cold_start': 'init_cold_start_test',
'test_lds_status_init_cold_start_in_journal': 'init_cold_start_in_journal_test',
'test_lds_status_init_exiting_faulty': 'init_exiting_faulty_test',
'test_lds_status_init_switching_shut_off': 'init_switching_shut_off_test',
'test_lds_status_init_switching_shut_off_in_journal': 'init_switching_shut_off_in_journal_test',
'test_lds_status_serviceable_after_cold_start': 'serviceable_after_cold_start_test',
'test_lds_status_serviceable_after_cold_start_in_journal': 'serviceable_after_cold_start_in_journal_test',
'test_lds_status_serviceable_after_switching_shut_off': 'serviceable_after_switching_shut_off_test',
'test_lds_status_serviceable_after_switching_shut_off_in_journal': 'serviceable_after_switching_shut_off_in_journal_test', # noqa: E501
'test_lds_status_serviceable_after_deg_absence_min_pressure_sensors': 'serviceable_after_deg_absence_min_pressure_sensors_test', # noqa: E501
'test_lds_status_serviceable_after_deg_additive_injectors_operation': 'serviceable_after_deg_additive_injectors_operation_test', # noqa: E501
'test_lds_status_serviceable_after_deg_exceeding_distance_between_flow_meters': 'serviceable_after_deg_exceeding_distance_between_flow_meters_test', # noqa: E501
'test_lds_status_serviceable_after_deg_starting_pumping_out_pumps': 'serviceable_after_deg_starting_pumping_out_pumps_test', # noqa: E501
'test_lds_status_serviceable_after_deg_faulty_pressure_sensors_at_pump': 'serviceable_after_deg_faulty_pressure_sensors_at_pump_test', # noqa: E501
'test_lds_status_serviceable_after_deg_faulty_pressure_sensors_at_pump_in_journal': 'serviceable_after_deg_faulty_pressure_sensors_at_pump_in_journal_test', # noqa: E501
'test_lds_status_serviceable_after_faulty': 'serviceable_after_faulty_test',
'test_lds_status_degradation_additive_injectors_operation': 'deg_additive_injectors_operation_test',
'test_lds_status_degradation_exceeding_distance_between_pressure_sensors': 'deg_exceeding_distance_between_pressure_sensors_test', # noqa: E501
'test_lds_status_degradation_exceeding_distance_between_pressure_sensors_in_journal': 'deg_exceeding_distance_between_pressure_sensors_in_journal_test', # noqa: E501
'test_lds_status_degradation_absence_min_pressure_sensors': 'deg_absence_min_pressure_sensors_test',
'test_lds_status_degradation_faulty_pressure_sensors_at_pump_station': 'deg_faulty_pressure_sensors_at_pump_station_test', # noqa: E501
'test_lds_status_degradation_faulty_pressure_sensors_at_pump_station_in_journal': 'deg_faulty_pressure_sensors_at_pump_station_in_journal_test', # noqa: E501
'test_lds_status_degradation_gravity_section_pumping': 'deg_gravity_section_pumping_test',
'test_lds_status_degradation_gravity_section_pumping_in_stopping': 'deg_gravity_section_pumping_in_stopping_test',
'test_lds_status_degradation_gravity_section_pumping_in_stopping_in_journal': 'deg_gravity_section_pumping_in_stopping_in_journal_test', # noqa: E501
'test_lds_status_degradation_pig_sensor_passage': 'deg_pig_sensor_passage_test',
'test_lds_status_degradation_starting_pumping_out_pumps': 'deg_starting_pumping_out_pumps_test',
'test_lds_status_degradation_exceeding_distance_between_flow_meters': 'deg_exceeding_distance_between_flow_meters_test', # noqa: E501
'test_lds_status_degradation_rejection_temperature_sensor_on_du_2': 'deg_rejection_temperature_sensor_on_du_2_test',
'test_lds_status_degradation_rejection_temperature_sensor_on_du_3': 'deg_rejection_temperature_sensor_on_du_3_test',
'test_lds_status_degradation_rejection_temperature_sensor_on_du_5': 'deg_rejection_temperature_sensor_on_du_5_test',
'test_lds_status_degradation_rejection_density_and_viscosity_on_du_2': 'deg_rejection_density_and_viscosity_on_du_2_test', # noqa: E501
'test_lds_status_degradation_rejection_density_and_viscosity_on_du_3': 'deg_rejection_density_and_viscosity_on_du_3_test', # noqa: E501
'test_lds_status_degradation_rejection_density_and_viscosity_on_du_5': 'deg_rejection_density_and_viscosity_on_du_5_test', # noqa: E501
'test_lds_status_faulty_absence_min_flow_meters_continuous': 'faulty_absence_min_flow_meters_continuous_test',
'test_lds_status_faulty_absence_min_flow_meters': 'faulty_absence_min_flow_meters_test',
'test_lds_status_faulty_absence_min_pressure_sensors': 'faulty_absence_min_pressure_sensors_test',
'test_lds_status_faulty_absence_min_pressure_sensors_in_journal': 'faulty_absence_min_pressure_sensors_in_journal_test', # noqa: E501
}
# Тесты уровня утечки (маркеры из LeakTestConfig - параметр leak)
LEAK_LEVEL_TEST_MAPPING = {
'test_leaks_content': 'leaks_content_test',
'test_all_leaks_info': 'all_leaks_info_test',
'test_tu_leaks_info': 'tu_leaks_info_test',
'test_leak_info_in_journal': 'leak_info_in_journal',
'test_possible_leak_in_journal': 'possible_leak_in_journal_test',
'test_acknowledge_leak_info': 'acknowledge_leak_test',
'test_acknowledge_leak_in_journal': 'acknowledge_leak_in_journal_test',
'test_output_signals': 'output_signals_test',
'test_lds_status_during_leak': 'lds_status_during_leak_test',
'test_balance_algorithm_leak_waiting': 'balance_algorithm_leak_waiting_test',
'test_balance_algorithm_leak_detected': 'balance_algorithm_leak_detected_test',
'test_the_leak_is_complete_on_kg': 'the_leak_is_complete_on_kg_test',
'test_leak_is_complete_in_output_signals': 'leak_is_complete_in_output_signals_test',
'test_complete_tu_leaks_info_content': 'complete_tu_leaks_info_content_test',
'test_all_leaks_is_empty': 'all_leaks_is_empty_test',
'test_leak_is_confirm_on_main_page': 'leak_is_confirm_on_main_page_test',
'test_leak_is_complete_on_main_page': 'leak_is_complete_on_main_page_test',
'test_balance_algorithm_leak_completed': 'balance_algorithm_leak_completed_test',
'test_completed_leak_info_in_journal': 'completed_leak_info_in_journal_test',
'test_export_leaks_report': 'export_leaks_report_test',
}
STATIONARY_STATUS_SUITE_LEVEL_MAPPING = {
'test_stationary_status_basic_info': 'stationary_status_basic_info_test',
'test_stationary_status_common_scheme': 'stationary_status_common_scheme_test',
'test_stationary_status_journal': 'stationary_status_journal_test',
'test_stationary_status_main_page_info': 'stationary_status_main_page_info_test',
}
# Тесты уровня отбраковки (маркеры из RejectionTestCase - параметр rejection_case)
IS_REJECTED_LEVEL_TEST_MAPPING = {
'test_rejection_input_signals': 'rejection_input_signals_test',
'test_rejection_journal': 'rejection_journal_test',
'test_rejection_main_page': 'rejection_main_page_test',
'test_rejection_scheme_signals_state': 'rejection_scheme_signals_state_test',
}
# Suite-level тесты отбраковки (маркеры из IsRejectedConfig - параметр config)
IS_REJECTED_SUITE_LEVEL_MAPPING = {
'test_rejection_report': 'rejection_report_test',
}
# Мержим все вместе чтобы не переписывать логику коллектора айтемов (тестов)
SUITE_LEVEL_TEST_MAPPING = {
**SMOKE_SUITE_LEVEL_MAPPING,
**LDS_STATUS_SUITE_LEVEL_MAPPING,
**STATIONARY_STATUS_SUITE_LEVEL_MAPPING,
**IS_REJECTED_SUITE_LEVEL_MAPPING,
}
def _get_test_markers_config(item, test_name):
"""
Получает конфигурацию с маркерами для теста.
Для leak-level тестов: маркеры берутся из параметра leak
Для suite-level тестов: маркеры берутся из config
:return: CaseMarkers объект или None
"""
if not hasattr(item, 'callspec'):
return None
params = item.callspec.params
# Проверяем, есть ли параметр leak (для leak-level тестов)
if 'leak' in params and test_name in LEAK_LEVEL_TEST_MAPPING:
leak = params['leak']
attr_name = LEAK_LEVEL_TEST_MAPPING[test_name]
return getattr(leak, attr_name, None)
# Проверяем, есть ли параметр rejection_case для тестов отбраковки
if 'rejection_case' in params and test_name in IS_REJECTED_LEVEL_TEST_MAPPING:
rejection_case = params['rejection_case']
attr_name = IS_REJECTED_LEVEL_TEST_MAPPING[test_name]
return getattr(rejection_case, attr_name, None)
# Suite-level тесты отбраковки (без rejection_case)
if 'config' in params and 'rejection_case' not in params and test_name in IS_REJECTED_SUITE_LEVEL_MAPPING:
suite_config = params['config']
attr_name = IS_REJECTED_SUITE_LEVEL_MAPPING[test_name]
return getattr(suite_config, attr_name, None)
# Для suite-level тестов берём из config
if 'config' in params:
if test_name in SMOKE_SUITE_LEVEL_MAPPING:
suite_config = params['config']
attr_name = SMOKE_SUITE_LEVEL_MAPPING[test_name]
return getattr(suite_config, attr_name, None)
if test_name in LDS_STATUS_SUITE_LEVEL_MAPPING:
suite_config = params['config']
attr_name = LDS_STATUS_SUITE_LEVEL_MAPPING[test_name]
return getattr(suite_config, attr_name, None)
if test_name in STATIONARY_STATUS_SUITE_LEVEL_MAPPING:
suite_config = params['config']
attr_name = STATIONARY_STATUS_SUITE_LEVEL_MAPPING[test_name]
return getattr(suite_config, attr_name, None)
return None
def pytest_collection_modifyitems(session, config, items):
"""
1. Фильтрует тесты по --suites (если указано)
2. Исключает тесты, у которых конфиг = None (тест отключён для этого набора данных)
3. Добавляет маркеры offset и test_case_id из конфига к каждому параметризованному тесту
4. Сортирует тесты по test_suite_name для группировки по наборам данных
"""
# Получаем список выбранных наборов из --suites
suites_option = config.getoption("--suites")
selected_suites = None
if suites_option:
# Парсим список наборов: "select_4,select_19_20" -> ["select_4", "select_19_20"]
selected_suites = [s.strip().lower() for s in suites_option.split(",")]
selected_items = []
deselected_items = []
for item in items:
# Фильтрация по --suites
if selected_suites:
suite_marker = item.get_closest_marker("test_suite_name")
if suite_marker:
suite_name = suite_marker.args[0].lower()
# Проверяем, содержит ли имя набора одну из выбранных подстрок
if not any(selected in suite_name for selected in selected_suites):
deselected_items.append(item)
continue
# Получаем имя функции теста (без параметров)
test_name = item.originalname or item.name.split('[')[0]
# Получаем конфиг с маркерами для теста
test_config = _get_test_markers_config(item, test_name)
if test_config is not None:
# Добавляем маркер offset
if hasattr(test_config, 'offset') and test_config.offset is not None:
item.add_marker(pytest.mark.offset(test_config.offset))
# Добавляем маркер test_case_id
if hasattr(test_config, 'test_case_id') and test_config.test_case_id is not None:
item.add_marker(pytest.mark.test_case_id(test_config.test_case_id))
elif (
test_name in SUITE_LEVEL_TEST_MAPPING
or test_name in LEAK_LEVEL_TEST_MAPPING
or test_name in IS_REJECTED_LEVEL_TEST_MAPPING
):
# Конфиг теста = None - исключаем тест из прогона
deselected_items.append(item)
continue
selected_items.append(item)
# Уведомляем pytest об исключённых тестах
if deselected_items:
config.hook.pytest_deselected(items=deselected_items)
# Заменяем список тестов на отфильтрованный
items[:] = selected_items
# Сортировка тестов по test_suite_name и offset
# Цель: обеспечить запуск тестов строго по offset строго внутри набора данных
# При равных offset сохраняем исходный порядок коллекции, чтобы порядок параметризации не перескакивал
def suite_offset_key(item):
"""
Сортировка тестов по test_suite_name и offset (без падения на None).
"""
test_suite_name_marker = item.get_closest_marker("test_suite_name")
test_suite_name = test_suite_name_marker.args[0] if test_suite_name_marker else ""
offset_marker = item.get_closest_marker("offset")
if offset_marker:
try:
offset_value = float(offset_marker.args[0])
except Exception:
offset_value = float("inf")
else:
offset_value = float("inf")
original_index = getattr(item, "_collection_index", 0)
# Возвращаем тройку ключей сортировки
# 1) test_suite_name - группировка по набору
# 2) offset_value - порядок внутри набора по времени
# 3) original_index - стабильность при равных offset
return test_suite_name, offset_value, original_index
# Сохраняем исходный порядок коллекции для стабильной сортировки
for index, item in enumerate(items):
item._collection_index = index
# по кортежу питон сортирует слева направо, благодаря этому сортировка по offset идет строго внутри test_suite_name
items.sort(key=suite_offset_key)
for item in items:
if hasattr(item, "_collection_index"):
delattr(item, "_collection_index")
@pytest.fixture(autouse=True)
def allure_tms_link(request):
"""
Allure TMS‑линки по test_case_id
"""
if test_case_id_marker := request.node.get_closest_marker("test_case_id"):
test_case_id = test_case_id_marker.args[0]
allure.dynamic.link(
f"https://{os.environ['TESTOPS_BASE_URL']}/testcases?selected_id={test_case_id}",
name=f"TestCase-{test_case_id}",
link_type="tms",
)
@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.skip("[SETUP] [ERROR] Набор пропущен: инфраструктура не готова")
@pytest.fixture(autouse=True)
def offset_wait(request):
"""
Offset‑ожидание перед каждым тестом относительно фактического старта core
"""
cfg = request.config.group_state
if cfg.get("current_suite") and not cfg.get("suite_infra_ready"):
return
if offset_marker := request.node.get_closest_marker("offset"):
offset_sec = float(offset_marker.args[0]) * BaseTN3Constants.SEC_PER_MIN
start = request.config.group_state["suite_start_time"] or 0
elapsed = time.monotonic() - start
to_wait = max(0, offset_sec - elapsed)
if to_wait:
time.sleep(to_wait)
def compute_imitator_duration(item, current_test_suite: str) -> float:
"""
Вычисляет длительность для имитатора (в минутах).
Правило:
- Собирает все тесты (item.session.items) с меткой test_suite_name == current_test_suite
- Извлекает все значения @pytest.mark.offset(...) (в минутах)
- Если offsets найдены: возвращает max(offsets) + IMITATOR_FINISH_DELAY задержка остановки имитатора
- Иначе: если у текущего item есть @pytest.mark.imitator_duration — используется как fallback и логируется
- Если ничего не найдено — pytest.fail с понятным текстом
"""
suite_items = [
suite_item
for suite_item in item.session.items
if (marker := suite_item.get_closest_marker("test_suite_name")) and marker.args[0] == current_test_suite
]
offsets = []
for suite_item in suite_items:
offset_marker = suite_item.get_closest_marker("offset")
if offset_marker:
try:
offsets.append(float(offset_marker.args[0]))
except Exception:
continue
if offsets:
max_offset = max(offsets)
imitator_duration = float(max_offset) + ImConst.IMITATOR_FINISH_DELAY_MINUTE
return imitator_duration
else:
# fallback- если все еще задан старый маркер imitator_duration, то используем его
if imitator_mark := item.get_closest_marker("imitator_duration"):
imitator_duration = float(imitator_mark.args[0])
logger.warning(
"[DEPRECATED] использован pytest.mark.imitator_duration()"
f"рекомендуется убрать и полагаться на max_offset + {ImConst.IMITATOR_FINISH_DELAY_MINUTE}"
)
return imitator_duration
pytest.fail(
"Не удалось вычислить imitator_duration: в тестовом модуле одновременно отсутствуют "
"и @pytest.mark.offset(), и pytest.mark.imitator_duration()"
)
def _skip_current_suite_after_setup_failure(cfg: dict, message: str) -> None:
"""
Пропускает текущий набор после ошибки setup: cleanup частичной инфраструктуры и pytest.skip.
"""
logger.error(message)
try:
allure.attach(message, name="Ошибка setup набора", attachment_type=allure.attachment_type.TEXT)
except Exception:
logger.debug("Не удалось прикрепить ошибку setup к Allure", exc_info=True)
logger.info("[TEARDOWN] LDS Configurator очистка после ошибки setup набора")
_run_lds_configurator_teardown_if_needed(cfg)
if stand_manager := cfg.get("stand_manager"):
try:
stand_manager.stop_imitator_wrapper()
except Exception:
logger.exception("[SETUP] Ошибка остановки имитатора после неудачного setup набора")
stand_manager.server_test_data_remover()
cfg["suite_infra_ready"] = False
cfg["suite_setup_failure"] = message
cfg["suite_start_time"] = None
pytest.skip("Набор пропущен: ошибка подготовки инфраструктуры")
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_setup(item):
"""
Перезапуск имитатора при смене test_suite_name
"""
cfg = item.config.group_state
test_suite_marker = item.get_closest_marker("test_suite_name")
if not test_suite_marker:
pytest.fail("Тест без @pytest.mark.test_suite_name")
current_test_suite = test_suite_marker.args[0]
if current_test_suite != cfg["current_suite"]:
# stop old
if stand_manager := cfg["stand_manager"]:
try:
stand_manager.stop_imitator_wrapper()
try:
stand_manager.restore_signal_unit_conversion_rules()
except Exception:
logger.exception(
"[ERROR] [SETUP] Ошибка при восстановлении signal_unit_conversion_rules.json "
"перед запуском нового набора"
)
stand_manager.server_test_data_remover()
finally:
cfg["stand_manager"] = None
# start new
cfg["current_suite"] = current_test_suite
cfg["suite_start_time"] = None
cfg["suite_infra_ready"] = False
cfg["suite_setup_failure"] = None
clear_suite_auth(cfg)
data_id = item.get_closest_marker("test_suite_data_id").args[0]
test_data_name = item.get_closest_marker("test_data_name").args[0]
# legacy: id из enum TU для имитатора (tn{id}_tags.txt), не resolved_tu_id из Администрирования
tu_id = item.get_closest_marker("tu_id").args[0]
ost_name = item.get_closest_marker("ost_name").args[0]
imitator_duration = compute_imitator_duration(item, current_test_suite)
suite_config = _find_config_by_suite_name(current_test_suite)
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"
)
stand_manager = StandSetupManager(
duration_m=imitator_duration,
test_data_id=data_id,
test_data_name=test_data_name,
tu_id=tu_id,
ost_name=ost_name,
measure_conversion_rules=measure_conversion_rules,
)
cfg["stand_manager"] = stand_manager
try:
stand_manager.check_opc_server_status()
except RuntimeError as error:
msg = (
"[SETUP] [ERROR] OPC сервер недоступен. Имитатор и автотесты не запущены. "
f"Ошибка при проверке статуса OPC: {error}"
)
allure.attach(msg, name="OPC сервер недоступен", attachment_type=allure.attachment_type.TEXT)
_skip_current_suite_after_setup_failure(cfg, msg)
try:
stand_manager.setup_stand_for_imitator_run()
except Exception as error:
_skip_current_suite_after_setup_failure(cfg, f"[SETUP] [ERROR] ошибка при подготовке стенда: {error}")
try:
ensure_suite_auth(cfg, current_test_suite)
except BaseException as error:
_skip_current_suite_after_setup_failure(cfg, f"[SETUP] [ERROR] не удалось инициализировать auth: {error}")
try:
_update_sensor_ids(stand_manager)
except Exception as error:
_skip_current_suite_after_setup_failure(
cfg,
f"[SETUP] [ERROR] ошибка обновления id датчиков отбраковки из конфигурации: {error}",
)
if suite_config is not None and suite_config.use_lds_configurator:
try:
_run_lds_admin_setup(suite_config, cfg)
except BaseException as error:
_skip_current_suite_after_setup_failure(cfg, f"[SETUP] [ERROR] LDS Configurator admin setup: {error}")
imitator_thread = threading.Thread(
target=stand_manager.start_imitator, name=f"imitator->{current_test_suite}", daemon=True
)
core_thread = threading.Thread(target=stand_manager.start_core)
try:
imitator_thread.start()
except Exception as error:
_skip_current_suite_after_setup_failure(cfg, f"[SETUP] [ERROR] ошибка запуска имитатора: {error}")
time.sleep(ImConst.CORE_START_DELAY_S)
try:
cfg["suite_start_time"] = time.monotonic()
core_thread.start()
core_thread.join(timeout=5)
except Exception as error:
_skip_current_suite_after_setup_failure(cfg, f"[SETUP] [ERROR] ошибка запуска СORE контейнеров: {error}")
# Сохраняем время старта имитатора для расчёта интервалов утечек в тестах
cfg["imitator_start_time"] = stand_manager.start_time
if suite_config is not None and suite_config.use_lds_configurator:
if suite_config.admin_tu is None:
_skip_current_suite_after_setup_failure(
cfg,
f"[SETUP] [ERROR] Набор '{suite_config.suite_name}': admin_tu обязателен "
"при use_lds_configurator=True",
)
try:
_run_lds_verify_after_core(suite_config, cfg)
except BaseException as error:
_skip_current_suite_after_setup_failure(
cfg, f"[SETUP] [ERROR] LDS Configurator проверка после запуска ядра: {error}"
)
cfg["suite_infra_ready"] = True
yield # pytest продолжит выполнение теста
def _run_lds_configurator_ws(coro_factory) -> None:
"""Запускает async WS-сценарий lds-configurator"""
lds_cfg_utils.set_configurator_flow_active(True)
lds_ws_parser.suppress_recv_logging = True
try:
asyncio.run(coro_factory())
finally:
lds_ws_parser.suppress_recv_logging = False
lds_cfg_utils.set_configurator_flow_active(False)
def _run_lds_admin_setup(suite_config, group_state: dict) -> None:
"""
Setup СОУ через Администрирование до старта имитатора.
"""
async def _admin_setup() -> None:
http_client = init_http_stand_client(group_state)
http_client.suppress_recv_logging = True
await lds_configurator_scenarios.lds_configurator_admin_setup(http_client, suite_config, group_state)
_run_lds_configurator_ws(_admin_setup)
def _run_lds_verify_after_core(suite_config, group_state: dict) -> None:
"""
WS-проверка готовности стенда после запуска lds-core.
"""
async def _verify() -> None:
websocket_client = init_ws_stand_client(group_state)
http_client = init_http_stand_client(group_state)
http_client.suppress_recv_logging = True
async with websocket_client as ws_client:
ws_client.suppress_recv_logging = True
await lds_configurator_scenarios.lds_configurator_verify_after_core(ws_client, http_client, suite_config)
_run_lds_configurator_ws(_verify)
def _run_lds_configurator_teardown_if_needed(cfg: dict) -> None:
"""
Выполняет WS-teardown СОУ при смене набора или завершении сессии.
Ошибки не прерывают pytest: логируются и оформляются как ALERT внутри сценария.
Состояние group_state сбрасывается в finally.
"""
if not cfg.get("use_lds_configurator") or not cfg.get("resolved_tu_id"):
return
tu_id = cfg["resolved_tu_id"]
admin_tu_name = cfg.get("admin_tu_name") or ""
pre_run_running_tus = cfg.get("pre_run_running_tus") or []
async def _teardown() -> None:
http_client = init_http_stand_client(cfg)
http_client.suppress_recv_logging = True
await lds_configurator_scenarios.lds_configurator_teardown(
http_client, tu_id, admin_tu_name, cfg, pre_run_running_tus
)
try:
_run_lds_configurator_ws(_teardown)
except BaseException as error:
logger.warning(
"[TEARDOWN] [ALERT] LDS Configurator teardown завершился с ошибкой для tuId=%s: %s",
tu_id,
error,
)
finally:
cfg["resolved_tu_id"] = None
cfg["use_lds_configurator"] = False
cfg["is_target_tu_active"] = False
cfg["admin_tu_name"] = None
cfg["pre_run_running_tus"] = None
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_teardown(item, nextitem):
"""
Teardown имитатора при выходе из группы
"""
yield
cfg = item.config.group_state
next_marker = nextitem.get_closest_marker("test_suite_name") if nextitem else None
next_suite = next_marker.args[0] if next_marker else None
if next_suite != cfg["current_suite"]:
if stand_manager := cfg["stand_manager"]:
try:
if cfg.get("suite_infra_ready"):
stand_manager.stop_imitator_wrapper()
try:
stand_manager.restore_signal_unit_conversion_rules()
except Exception:
logger.exception("[ERROR] [TEARDOWN] Ошибка при восстановлении signal_unit_conversion_rules.json")
stand_manager.server_test_data_remover()
finally:
cfg["stand_manager"] = None
cfg["current_suite"] = None
cfg["suite_start_time"] = None
cfg["imitator_start_time"] = None
cfg["suite_infra_ready"] = False
# опционально дождаться завершения потока (если не daemon) — безопасный join
imitator_thread = cfg.get("imitator_thread")
if imitator_thread and not getattr(imitator_thread, "daemon", False):
try:
imitator_thread.join(timeout=5)
except RuntimeError:
logger.exception("Ошибка при join() фона имитатора")
@pytest_asyncio.fixture
async def ws_client(request):
"""
Фикстура для работы с websocket клиентом.
request - даёт доступ к request.config.group_state (общий кэш auth на dataset: stand_host, auth_token, x_user_id).
:return: Объект wss соединения
"""
cfg = request.config.group_state
ensure_auth_for_fixture(cfg)
ws_client = init_ws_stand_client(cfg)
async with ws_client as client:
yield client
@pytest.fixture
def http_client(request):
"""
Фикстура для работы с http клиентом.
request - даёт доступ к request.config.group_state (общий кэш auth на dataset: stand_host, auth_token).
:return: экземпляр класса для выполнения http запросов к стенду
"""
cfg = request.config.group_state
ensure_auth_for_fixture(cfg)
return init_http_stand_client(cfg)
@pytest.fixture
def imitator_start_time(request):
"""
Фикстура для получения времени старта имитатора (datetime объект).
Используется для точного расчёта времени обнаружения утечек:
- leak_start_time = imitator_start_time + timedelta(seconds=LEAK_START_INTERVAL)
- leak_end_time = imitator_start_time + timedelta(seconds=LEAK_START_INTERVAL + ALLOWED_TIME_DIFF_SECONDS)
"""
start_time = request.config.group_state.get("imitator_start_time")
if start_time is None:
pytest.fail("imitator_start_time не установлен. Убедитесь что тест запущен после инициализации имитатора.")
return start_time
def pytest_sessionfinish(session, exitstatus):
"""
В завершении сессии — отправляем единый Allure‑отчёт в TestOps.
"""
# 1) teardown стенда: LDS Configurator + остановка имитатора
try:
group_state = getattr(session.config, "group_state", {})
_run_lds_configurator_teardown_if_needed(group_state)
stand_manager = group_state.get("stand_manager")
if stand_manager:
try:
stand_manager.stop_imitator_wrapper()
except Exception:
logger.exception("[ERROR] [TEARDOWN] Ошибка при остановке имитатора")
try:
stand_manager.restore_signal_unit_conversion_rules()
except Exception:
logger.exception("[ERROR] [TEARDOWN] Ошибка при восстановлении signal_unit_conversion_rules.json")
try:
stand_manager.server_test_data_remover()
except Exception:
logger.exception("[ERROR] [TEARDOWN] Ошибка при удалении тестового набора данных со стенда")
except Exception:
logger.exception("[ERROR] [TEARDOWN] Ошибка при получении stand_manager из group_state")
# 2) Выгрузка allure-results в TestOps
try:
uploader = AllureResultsUploader()
logger.info("[INFO] [TEARDOWN] Uploading Allure results to TestOps")
uploader.upload_allure_results()
except Exception:
logger.exception("[ERROR] [TEARDOWN] Ошибка при выгрузке allure-results в TestOps")
# 3) Удаление локальных архивов с данными
shutil.rmtree("allure-results")
project_root = os.path.dirname(os.path.abspath(__file__))
files_for_drop = glob.glob(os.path.join(project_root, "*.tar.gz"))
if not files_for_drop:
logger.warning("[WARNING] [TEARDOWN] Не нашлось архивов .tar.gz с данным для удаления")
else:
for file in files_for_drop:
os.remove(file)
conftest