"""Read-only смоук docker-инсталляции: ничего не создаёт в живой базе. Проверяет: health, SPA, вход, настройки/доски, поиск (ответ), admin-тик, FTS, счётчики и связку с автономным ML-сервисом. Файловые проверки MinIO — в opt-in режиме SMOKE_MUTATE=1 (создают проект, который надо удалять вручную). """ import os import sys import httpx BASE = "http://localhost:8000" MUTATE = os.getenv("SMOKE_MUTATE", "") == "1" ok = True def check(name, cond, extra=""): global ok if not cond: ok = False print("FAIL:", name, extra) else: print("ok:", name) with httpx.Client(base_url=BASE, timeout=30) as c: check("health", c.get("/api/health").status_code == 200) page = c.get("/") check("spa", page.status_code == 200 and 'id="app"' in page.text) r = c.post("/api/auth/login", json={"login": "admin", "password": "admin"}) check("login", r.status_code == 200 and c.cookies.get("leadradar_session")) boards = c.get("/api/boards").json() check("boards api", isinstance(boards, list)) settings = c.get("/api/settings").json() check("settings", settings.get("mlEnabled") is True and settings.get("targetCurrency") == "RUB") # демо-механика в проде выключена demo = c.post("/api/demo/simulate-lead") check("demo disabled", demo.status_code == 404) # поиск отвечает (лиды создаются только из Telegram) q = c.get("/api/search", params={"q": "Python"}).json() check("search", isinstance(q.get("leads"), list)) # тик (правила хранения + очередь), FTS, счётчики tick = c.post("/api/admin/tick").json() check("tick", "pipeline" in tick and "queue" in tick) check("fts rebuild", c.post("/api/admin/fts/rebuild").json().get("ready") is True) counts = c.get("/api/leads/counts").json() check("counts", "ml" in counts and "ai" in counts and "learning" in counts) # автономный ML-сервис (отдельный контейнер) через API основного приложения ml = c.get("/api/ml/status").json() check("ml reachable", ml.get("reachable") is True, ml) pred = c.post("/api/ml/predict", json={"text": "python backend fastapi бот телеграм"}).json() check("ml predict", isinstance(pred.get("scores"), dict)) if MUTATE: # opt-in: проект + файл через MinIO (после проверки проект надо удалить) card = c.post("/api/projects", json={"title": "SMOKE-TMP-удалить"}).json() check("project", bool(card.get("id"))) r = c.post(f"/api/projects/{card['id']}/files", files=[("files", ("smoke.txt", b"docker smoke", "text/plain"))]) check("upload to MinIO", r.status_code == 200 and len(r.json().get("items", [])) == 1, r.text) fid = r.json()["items"][0]["id"] dl = c.get(f"/api/projects/{card['id']}/files/{fid}/download") check("download from MinIO", dl.status_code == 200 and dl.content == b"docker smoke", dl.text[:100]) check("unauth me", c.get("/api/auth/me").status_code == 200) # кука жива print("DOCKER SMOKE OK" if ok else "DOCKER SMOKE FAILED") sys.exit(0 if ok else 1)