Files
app-autotest/tests/test_scenario_full.py
T
naeel b89c0138e4 test: +13 тестов — save_run UPSERT, auth режимы/URL, сценарии
test_db_save_run.py (4 теста): INSERT/UPDATE/UPSERT/без БД
test_auth.py (9 тестов): URL полигона, get_stand, whitelist, режимы
test_scenario_full.py (8 тестов): CRUD + запуск (требует polygon + БД)

Итого: 35 тестов (22 старых + 13 новых)
2026-08-04 08:48:36 +04:00

197 lines
7.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Интеграционные тесты сценариев с полигоном.
Требует polygon_server (conftest.py, session scope).
"""
import json
import time
# ── Хелперы ──
def _create_scenario(app_client, name, steps):
resp = app_client.post("/api/scenario/definitions",
json={"name": name, "steps": steps},
headers={"Content-Type": "application/json"})
return resp.get_json()
def _delete_scenario(app_client, def_id):
app_client.delete(f"/api/scenario/definitions/{def_id}")
def _run_scenario(app_client, def_id):
return app_client.post("/api/scenario/run",
json={"definition_id": def_id},
headers={"Content-Type": "application/json"})
def _poll_scenario(app_client, run_id, timeout=15):
deadline = time.time() + timeout
while time.time() < deadline:
resp = app_client.get(f"/api/scenario/run/{run_id}")
data = resp.get_json()
if data and data.get("status") != "RUNNING":
return data
time.sleep(0.5)
return {"status": "TIMEOUT"}
# ── Тесты ──
class TestScenarioLifecycle:
"""Полный цикл: создать определение -> запустить -> проверить результат."""
def test_list_scenarios(self, app_client):
resp = app_client.get("/api/scenarios")
assert resp.status_code == 200
data = resp.get_json()
assert isinstance(data, list)
def test_create_and_run_simple_scenario(self, app_client):
"""Сценарий create -> delete. Проверяем что оба шага OK."""
steps = [
{"service_id": 1, "operation": "create", "params": {}, "output": "d1"},
{"service_id": 1, "operation": "delete", "instance_ref": "d1", "params": {}},
]
name = f"test-simple-{int(time.time())}"
created = _create_scenario(app_client, name, steps)
assert "id" in created, f"Create failed: {created}"
resp = _run_scenario(app_client, created["id"])
assert resp.status_code == 202, f"Run failed: {resp.get_json()}"
run_data = resp.get_json()
assert run_data["status"] == "RUNNING"
run_id = run_data["run_id"]
result = _poll_scenario(app_client, run_id)
assert result["status"] == "OK", f"Scenario failed: {result}"
assert result["current_step"] == 2
assert result["total_steps"] == 2
_delete_scenario(app_client, created["id"])
def test_scenario_404_unknown_id(self, app_client):
resp = _run_scenario(app_client, 99999)
assert resp.status_code == 404
def test_scenario_lock_prevents_second_run(self, app_client):
"""Запуск во время выполнения -> 409."""
steps = [
{"service_id": 1, "operation": "create", "params": {}, "output": "lock1"},
{"service_id": 1, "operation": "delete", "instance_ref": "lock1", "params": {}},
]
name = f"test-lock-{int(time.time())}"
created = _create_scenario(app_client, name, steps)
# Первый запуск
resp1 = _run_scenario(app_client, created["id"])
assert resp1.status_code == 202
# Второй запуск — должен получить 409
resp2 = _run_scenario(app_client, created["id"])
assert resp2.status_code == 409, f"Expected 409, got {resp2.status_code}: {resp2.get_json()}"
# Ждём завершения первого
run_id = resp1.get_json()["run_id"]
_poll_scenario(app_client, run_id)
_delete_scenario(app_client, created["id"])
def test_scenario_can_run_after_completion(self, app_client):
"""После завершения можно запустить снова."""
steps = [
{"service_id": 1, "operation": "create", "params": {}, "output": "again"},
{"service_id": 1, "operation": "delete", "instance_ref": "again", "params": {}},
]
name = f"test-again-{int(time.time())}"
created = _create_scenario(app_client, name, steps)
# Первый прогон
resp1 = _run_scenario(app_client, created["id"])
run1_id = resp1.get_json()["run_id"]
result1 = _poll_scenario(app_client, run1_id)
assert result1["status"] == "OK"
# Второй прогон — должен работать
resp2 = _run_scenario(app_client, created["id"])
assert resp2.status_code == 202, f"Second run failed: {resp2.get_json()}"
run2_id = resp2.get_json()["run_id"]
result2 = _poll_scenario(app_client, run2_id)
assert result2["status"] == "OK"
_delete_scenario(app_client, created["id"])
def test_scenario_status_endpoint(self, app_client):
"""GET /api/scenario/status возвращает список."""
resp = app_client.get("/api/scenario/status")
assert resp.status_code == 200
data = resp.get_json()
assert isinstance(data, list)
class TestScenarioCRUD:
"""CRUD определения сценариев."""
def test_create_and_get_definition(self, app_client):
name = f"test-crud-{int(time.time())}"
steps = [{"service_id": 1, "operation": "create", "params": {}}]
created = _create_scenario(app_client, name, steps)
assert "id" in created
resp = app_client.get(f"/api/scenario/definitions/{created['id']}")
data = resp.get_json()
assert data["name"] == name
assert data["version"] == 1
_delete_scenario(app_client, created["id"])
def test_update_increments_version(self, app_client):
name = f"test-ver-{int(time.time())}"
created = _create_scenario(app_client, name,
[{"service_id": 1, "operation": "create", "params": {}}])
resp = app_client.put(f"/api/scenario/definitions/{created['id']}",
json={"name": name, "steps": [
{"service_id": 1, "operation": "create", "params": {}},
{"service_id": 1, "operation": "delete", "params": {}}
], "version": 1},
headers={"Content-Type": "application/json"})
data = resp.get_json()
assert data.get("ok"), f"Update failed: {data}"
assert data["version"] == 2
_delete_scenario(app_client, created["id"])
def test_version_conflict_returns_409(self, app_client):
name = f"test-conflict-{int(time.time())}"
created = _create_scenario(app_client, name,
[{"service_id": 1, "operation": "create", "params": {}}])
# Обновляем с version=1 -> OK
app_client.put(f"/api/scenario/definitions/{created['id']}",
json={"name": name, "steps": [
{"service_id": 1, "operation": "create", "params": {}}
], "version": 1},
headers={"Content-Type": "application/json"})
# Ещё раз с version=1 -> conflict
resp = app_client.put(f"/api/scenario/definitions/{created['id']}",
json={"name": name, "steps": [
{"service_id": 1, "operation": "create", "params": {}}
], "version": 1},
headers={"Content-Type": "application/json"})
data = resp.get_json()
assert data.get("conflict"), f"Expected conflict, got: {data}"
_delete_scenario(app_client, created["id"])
def test_soft_delete(self, app_client):
name = f"test-del-{int(time.time())}"
created = _create_scenario(app_client, name,
[{"service_id": 1, "operation": "create", "params": {}}])
resp = app_client.delete(f"/api/scenario/definitions/{created['id']}")
assert resp.status_code == 200
assert resp.get_json().get("ok")