test(ldd): конкурентность, нагрузка и release gate

This commit is contained in:
2026-10-28 00:40:08 +03:00
parent 05f628a52b
commit 85d34e78cb
7 changed files with 395 additions and 1 deletions

View File

@@ -78,8 +78,11 @@ mkdir -p "$(dirname "$EVIDENCE")"
log "Проверяю HTTP-сценарии..."
python3 - "$EVIDENCE" <<'PY'
import concurrent.futures
import json
import statistics
import sys
import time
import urllib.error
import urllib.request
import uuid
@@ -216,6 +219,22 @@ try:
token=admin_token,
detail="create lab",
)
student = expect(
"runtime.course_flow",
"POST",
gateway + "/api/v1/groups/" + group["id"] + "/students",
200,
data={
"studentNo": "ldd-student-" + suffix,
"lastName": "Тестов",
"firstName": "Студент",
"middleName": "",
"basis": "бюджет",
"note": "",
},
token=admin_token,
detail="create student",
)
teacher_token = expect(
"runtime.course_flow",
"POST",
@@ -283,6 +302,44 @@ try:
raise AssertionError("audit actions")
record("runtime.audit_visible", "passed", f"аудит содержит {sorted(required)}")
# 5b. Нагрузочный smoke чтения: курс, лабы, профиль и аудит.
load_paths = [
(gateway + "/api/v1/courses/" + course["id"], teacher_token),
(gateway + "/api/v1/labs?courseId=" + course["id"], teacher_token),
(gateway + "/api/v1/courses/" + course["id"] + "/students/" + student["id"] + "/profile", teacher_token),
(gateway + "/api/v1/audit?limit=100", admin_token),
]
def timed_get(path, token):
started = time.perf_counter()
status, _ = req("GET", path, token=token)
return status, time.perf_counter() - started
latencies = []
errors = 0
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as pool:
futures = [
pool.submit(timed_get, path, token)
for _ in range(25)
for path, token in load_paths
]
for future in concurrent.futures.as_completed(futures):
status, latency = future.result()
latencies.append(latency)
if status != 200:
errors += 1
latencies.sort()
p95 = latencies[min(len(latencies) - 1, int(len(latencies) * 0.95))]
average = statistics.mean(latencies)
load_ok = errors == 0 and p95 < 2.0
record(
"runtime.load_gradebook_profile",
"passed" if load_ok else "failed",
f"requests={len(latencies)}, errors={errors}, avg={average:.3f}s, p95={p95:.3f}s",
)
if not load_ok:
raise AssertionError("load smoke")
# 6. Web-страница входа и публичное дерево.
status, _ = req("GET", web + "/login")
if status != 200: