test: полная симуляция пользователя — 38 проверок, 8 сценариев

ТЕСТ 1: загрузка страницы, сервисы, инстансы
ТЕСТ 2: create 9 сервисов (Болванка, PostgreSQL, Redis, ...)
ТЕСТ 3: переключение DEV/TEST/PROD
ТЕСТ 4: Облако/Эмуляция туда-обратно
ТЕСТ 5: CRUD сценария + запуск + повтор + seed
ТЕСТ 6: история + проверка дубликатов
ТЕСТ 7: логи
ТЕСТ 8: валидация входных данных
This commit is contained in:
2026-08-04 09:10:19 +04:00
parent 2954f5429b
commit 31cc75177c
+247
View File
@@ -0,0 +1,247 @@
"""
Полное моделирование поведения пользователя автотеста.
Бьёт в atest.pythonk8s.dev.nubes.ru, эмуляция + облако, все стенды,
ручные операции + сценарии + переключения + краевые случаи.
Запуск: python3 tests/test_user_simulation.py
"""
import time, requests, json, sys
BASE = "https://atest.pythonk8s.dev.nubes.ru"
PASS = FAIL = 0
def cookie(mode="polygon", stand="test"):
return {"mode": mode, "polygon_stand": stand}
def log(ok, msg):
global PASS, FAIL
mark = "OK" if ok else "FAIL"
if ok: PASS += 1
else: FAIL += 1
print(f" [{mark}] {msg}")
if not ok:
sys.stdout.flush()
def get(path, cookies=None, code=200):
r = requests.get(f"{BASE}{path}", cookies=cookies or cookie(), timeout=15)
assert r.status_code == code, f"GET {path}: {r.status_code}"
return r.json() if r.text.strip() else {}
def post(path, data, cookies=None, code=200):
r = requests.post(f"{BASE}{path}", json=data, cookies=cookies or cookie(), timeout=15)
assert r.status_code == code, f"POST {path}: {r.status_code} {r.text[:100]}"
return r.json() if r.text.strip() else {}
def poll(op_uid, cookies=None, timeout=15):
deadline = time.time() + timeout
while time.time() < deadline:
d = get(f"/api/test/status/{op_uid}", cookies)
if d.get("done") or d.get("status") in ("OK", "FAIL"):
return d
time.sleep(0.5)
return {"status": "TIMEOUT"}
def create_instance(sid, name=None, cookies=None):
r = post("/api/test",
{"serviceId": sid, "operation": "create", "svcOperationId": 18,
"params": {}, "displayName": name or f"autotest-user-{sid}"},
cookies)
return r["opUid"], r.get("instanceUid")
def run_scenario(def_id, cookies=None):
r = post("/api/scenario/run", {"definition_id": def_id}, cookies, code=202)
return r["run_id"]
def poll_scenario(run_id, cookies=None, timeout=20):
deadline = time.time() + timeout
while time.time() < deadline:
d = get(f"/api/scenario/run/{run_id}", cookies)
if d.get("status") != "RUNNING":
return d
time.sleep(1)
return {"status": "TIMEOUT"}
# ═══════════════════════════════════════════════════════════
print("=" * 60)
print("ТЕСТ 1: Загрузка страницы, сервисы, эмуляция")
print("=" * 60)
# 1.1 Главная страница рендерится
r = requests.get(BASE, cookies=cookie(), timeout=10)
log(r.status_code == 200, "GET / -> 200")
log("Эмуляция" in r.text, "страница содержит 'Эмуляция'")
log("v1.2" in r.text, "версия присутствует")
# 1.2 Список сервисов
svc = get("/api/services")
log(len(svc) > 30, f"сервисов: {len(svc)}")
log(any(s["svcId"] == 1 for s in svc), "Болванка есть")
# 1.3 Инстансы
inst = get("/api/instances/list")
log(isinstance(inst, list), f"инстансов: {len(inst)}")
print(f"\n{'='*60}")
print("ТЕСТ 2: Ручные операции (create + modify + delete)")
print("=" * 60)
for sid in [1, 90, 91, 92, 93, 28, 21, 25, 99]:
sname = next((s["svc"] for s in svc if s["svcId"] == sid), "?")
try:
op_uid, iuid = create_instance(sid, f"autotest-user-{sid}-{int(time.time())}")
result = poll(op_uid)
log(result.get("status") == "OK", f"create {sid} {sname} -> {iuid[:8] if iuid else '?'}")
except Exception as e:
log(False, f"create {sid} {sname} -> {str(e)[:60]}")
print(f"\n{'='*60}")
print("ТЕСТ 3: Переключение стендов в эмуляции")
print("=" * 60)
for stand in ["dev", "test", "prod"]:
svc_s = get("/api/services", cookie("polygon", stand))
cnt = len(svc_s)
log(cnt > 0, f"стенд {stand}: {cnt} сервисов")
print(f"\n{'='*60}")
print("ТЕСТ 4: Переключение Облако / Эмуляция")
print("=" * 60)
# 4.1 Переключиться в облако
r = requests.post(BASE, data={"action": "set_mode", "mode": "cloud", "polygon_stand": "test"},
cookies=cookie(), allow_redirects=False, timeout=10)
log(r.status_code in (302, 200), "POST set_mode cloud -> редирект")
# 4.2 В облаке без токена — ошибка
c = cookie("cloud", "test")
r = requests.get(BASE, cookies=c, timeout=10)
log("Токен невалиден" in r.text or "Token" in r.text or r.status_code in (200, 302),
"облако без токена: ожидаемая ошибка")
# 4.3 Обратно в эмуляцию
r = requests.post(BASE, data={"action": "set_mode", "mode": "polygon", "polygon_stand": "test"},
cookies=c, allow_redirects=False, timeout=10)
log(r.status_code in (302, 200), "POST set_mode polygon -> редирект")
# 4.4 После возврата — сервисы снова есть
svc2 = get("/api/services", cookie())
log(len(svc2) > 30, f"после возврата: {len(svc2)} сервисов")
print(f"\n{'='*60}")
print("ТЕСТ 5: Сценарии — CRUD + запуск")
print("=" * 60)
# 5.1 Список сценариев
scenarios = get("/api/scenarios")
log(len(scenarios) > 0, f"сценариев: {len(scenarios)}")
# 5.2 Создать сценарий
name = f"user-test-{int(time.time())}"
steps = [
{"service_id": 1, "operation": "create", "params": {}, "output": "u1"},
{"service_id": 1, "operation": "modify", "instance_ref": "u1", "params": {}},
{"service_id": 1, "operation": "delete", "instance_ref": "u1", "params": {}},
]
r = requests.post(f"{BASE}/api/scenario/definitions",
json={"name": name, "steps": steps},
cookies=cookie(), timeout=10)
sc_def = r.json()
log("id" in sc_def, f"создан сценарий {name} (id={sc_def.get('id')})")
if "id" in sc_def:
# 5.3 Запустить
run_id = run_scenario(sc_def["id"])
log(run_id is not None, f"запущен run_id={run_id}")
# 5.4 Поллинг
result = poll_scenario(run_id)
log(result.get("status") == "OK", f"сценарий: {result.get('status')} (шаг {result.get('current_step')}/{result.get('total_steps')})")
# 5.5 Запустить ещё раз (после завершения должно работать)
run_id2 = run_scenario(sc_def["id"])
result2 = poll_scenario(run_id2)
log(result2.get("status") == "OK", f"повторный запуск: {result2.get('status')}")
# 5.6 Удалить
r = requests.delete(f"{BASE}/api/scenario/definitions/{sc_def['id']}", cookies=cookie(), timeout=10)
log(r.status_code == 200, f"удалён сценарий {sc_def['id']}")
# 5.7 Seed-сценарий запустить
seed = next((s for s in scenarios if s.get("is_seed")), None)
if seed:
run_id = run_scenario(seed["id"])
result = poll_scenario(run_id, timeout=20)
log(result.get("status") == "OK", f"seed-сценарий '{seed['name']}': {result.get('status')}")
else:
# Ищем dummy_test среди обычных
for s in scenarios:
if "dummy" in s.get("name", "").lower():
run_id = run_scenario(s["id"])
result = poll_scenario(run_id, timeout=20)
log(result.get("status") == "OK", f"сценарий '{s['name']}': {result.get('status')}")
break
else:
log(False, "нет сценария для запуска")
print(f"\n{'='*60}")
print("ТЕСТ 6: История")
print("=" * 60)
rows = get("/api/history")
log(len(rows) > 0, f"записей в истории: {len(rows)}")
log(any(r["status"] == "OK" for r in rows), "есть OK-записи")
# Проверка что нет дубликатов RUNNING/OK (фикс v1.2.39)
op_uids = [r["op_uid"] for r in rows if r.get("op_uid")]
dup_count = len(op_uids) - len(set(op_uids))
log(dup_count == 0, f"дубликатов op_uid: {dup_count} (0 = хорошо)")
print(f"\n{'='*60}")
print("ТЕСТ 7: Логи")
print("=" * 60)
logs = get("/api/log")
log(isinstance(logs, list), f"строк лога: {len(logs)}")
log(any("api_test" in l or "save_run" in l or "OK" in l for l in logs),
"логи содержат операции")
print(f"\n{'='*60}")
print("ТЕСТ 8: Валидация входных данных")
print("=" * 60)
# 8.1 Невалидный serviceId
r = requests.post(f"{BASE}/api/test",
json={"serviceId": "abc", "operation": "create", "svcOperationId": 18, "params": {}},
cookies=cookie(), timeout=10)
log(r.status_code == 400, f"serviceId='abc' -> 400")
# 8.2 Пустой operation
r = requests.post(f"{BASE}/api/test",
json={"serviceId": 1, "operation": "", "svcOperationId": 18, "params": {}},
cookies=cookie(), timeout=10)
log(r.status_code == 400, f"operation='' -> 400")
# 8.3 Невалидный instanceUid
r = requests.post(f"{BASE}/api/test",
json={"serviceId": 1, "operation": "delete", "svcOperationId": 71,
"instanceUid": "not-a-uuid", "params": {}},
cookies=cookie(), timeout=10)
log(r.status_code == 400, f"instanceUid='not-a-uuid' -> 400")
# 8.4 Невалидный определение сценария
r = requests.post(f"{BASE}/api/scenario/run",
json={"definition_id": 99999}, cookies=cookie(), timeout=10)
log(r.status_code == 404, f"run definition_id=99999 -> 404")
print(f"\n{'='*60}")
print(f"ИТОГО: PASS={PASS} FAIL={FAIL}")
print(f"{'='*60}")
sys.exit(0 if FAIL == 0 else 1)