feat(discord-bridge): comanda /infra cu confirmare prin butoane
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KddsXCqEbKMhdFJDYbAsx8
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
|
||||
import pytest
|
||||
|
||||
import bot
|
||||
@@ -345,3 +347,156 @@ async def test_sync_fara_arbore_sau_fara_guilduri_nu_arunca():
|
||||
|
||||
assert await commands_slash.sync_guilds(None, ["100"]) == {}
|
||||
assert await commands_slash.sync_guilds(_FakeTree(), []) == {}
|
||||
|
||||
|
||||
# --------------------------------------------------------------------- /infra
|
||||
import infra_actions # noqa: E402
|
||||
|
||||
|
||||
def _st(**over) -> dict:
|
||||
base = {
|
||||
"quorum": {"quorate": True, "votes": 3, "expected": 3},
|
||||
"guests": {"running": 2, "total": 3},
|
||||
"ha": [], "replication": {"ok": 0, "total": 0, "last": 0, "failed": []},
|
||||
"ups": None, "backup": None, "ssl": None, "vm109": None,
|
||||
"dr_failover_active": None, "maint": 0, "jobs": [], "errors": {},
|
||||
"nodes": [{"name": "pvemini", "ip": "10.0.20.201", "online": True,
|
||||
"cpu": 0.0, "mem": 0, "maxmem": 0, "uptime": 0, "guests": 2}],
|
||||
}
|
||||
base.update(over)
|
||||
return base
|
||||
|
||||
|
||||
def test_slash_args_infra():
|
||||
assert bot.slash_args("infra", {"actiune": "oprire", "simulare": None}) == ["oprire"]
|
||||
assert bot.slash_args("infra", {"actiune": "oprire", "simulare": False}) == ["oprire", "real"]
|
||||
assert bot.slash_args("infra", {"actiune": "oprire", "simulare": True}) == ["oprire", "simulare"]
|
||||
|
||||
|
||||
async def test_infra_actiune_necunoscuta_fara_subprocess(bridge, monkeypatch):
|
||||
import subprocess
|
||||
|
||||
monkeypatch.setattr(subprocess, "run", lambda *a, **k: pytest.fail("nu trebuia sa ruleze nimic"))
|
||||
_, ch, _ = await slash(bridge, "infra", actiune="nu-exista", simulare=None)
|
||||
assert "necunoscuta" in ch.all_text
|
||||
|
||||
|
||||
async def test_infra_fara_actiune(bridge):
|
||||
_, ch, _ = await slash(bridge, "infra", actiune="", simulare=None)
|
||||
assert "necunoscuta" in ch.all_text or "Foloseste" in ch.all_text
|
||||
|
||||
|
||||
async def test_infra_stare(bridge, monkeypatch):
|
||||
st = _st(ups={"status": "OL", "charge": 100.0, "runtime_s": 1200})
|
||||
monkeypatch.setattr(infra_actions, "status", lambda fresh=False: st)
|
||||
_, ch, _ = await slash(bridge, "infra", actiune="stare", simulare=None)
|
||||
text = ch.all_text
|
||||
assert "cvorum" in text and "3/3" in text and "guest-uri" in text and "pvemini" in text
|
||||
assert "UPS" in text and "OL" in text
|
||||
|
||||
|
||||
async def test_infra_sarcini_fara_joburi(bridge, monkeypatch):
|
||||
monkeypatch.setattr(infra_actions, "status", lambda fresh=False: _st(jobs=[]))
|
||||
_, ch, _ = await slash(bridge, "infra", actiune="sarcini", simulare=None)
|
||||
assert "Niciun job activ" in ch.all_text
|
||||
|
||||
|
||||
async def test_infra_sarcini_cu_job_activ(bridge, monkeypatch):
|
||||
jobs = [{"id": "oprire", "host": "pvemini", "active": True, "log": "/var/log/x.log"}]
|
||||
monkeypatch.setattr(infra_actions, "status", lambda fresh=False: _st(jobs=jobs))
|
||||
monkeypatch.setattr(infra_actions, "log_tail", lambda aid, n=60: {"file": "/x", "lines": ["linia 1"]})
|
||||
_, ch, _ = await slash(bridge, "infra", actiune="sarcini", simulare=None)
|
||||
assert "oprire" in ch.all_text and "linia 1" in ch.all_text
|
||||
|
||||
|
||||
async def test_infra_precondition_esuata(bridge, monkeypatch):
|
||||
monkeypatch.setattr(infra_actions, "status", lambda fresh=False: _st())
|
||||
monkeypatch.setattr(infra_actions, "check", lambda aid, st, dry: (False, "motiv de test"))
|
||||
apelat = []
|
||||
monkeypatch.setattr(infra_actions, "run", lambda *a, **k: apelat.append(a) or {"ok": True, "code": 200})
|
||||
_, ch, _ = await slash(bridge, "infra", actiune="wol", simulare=None)
|
||||
assert "⛔" in ch.all_text and "motiv de test" in ch.all_text
|
||||
assert apelat == []
|
||||
|
||||
|
||||
async def test_infra_fara_confirmare_ruleaza_direct(bridge, monkeypatch):
|
||||
"""`wol` nu cere confirmare: ruleaza imediat daca precondiția trece."""
|
||||
monkeypatch.setattr(infra_actions, "status", lambda fresh=False: _st())
|
||||
monkeypatch.setattr(infra_actions, "check", lambda aid, st, dry: (True, ""))
|
||||
apelat = []
|
||||
|
||||
def _run(aid, dry):
|
||||
apelat.append((aid, dry))
|
||||
return {"ok": True, "code": 200, "host": "oracle-prod-admin"}
|
||||
|
||||
monkeypatch.setattr(infra_actions, "run", _run)
|
||||
_, ch, _ = await slash(bridge, "infra", actiune="wol", simulare=None)
|
||||
assert apelat == [("wol", False)] # "wol" nu are simulare -> dry mereu False
|
||||
assert "pornit" in ch.all_text
|
||||
|
||||
|
||||
async def test_infra_cu_confirmare_arata_intrebarea_si_nu_ruleaza(bridge, monkeypatch):
|
||||
monkeypatch.setattr(infra_actions, "status", lambda fresh=False: _st())
|
||||
monkeypatch.setattr(infra_actions, "check", lambda aid, st, dry: (True, ""))
|
||||
apelat = []
|
||||
monkeypatch.setattr(infra_actions, "run", lambda *a, **k: apelat.append(a) or {"ok": True, "code": 200})
|
||||
_, ch, _ = await slash(bridge, "infra", actiune="oprire", simulare=False) # "real" -> dry=False
|
||||
assert "confirmi" in ch.all_text.lower()
|
||||
assert apelat == [] # butonul nu a fost apasat, comanda nu a rulat nimic
|
||||
|
||||
|
||||
async def test_infra_actiune_de_simulare_are_dry_true(bridge, monkeypatch):
|
||||
vazute = []
|
||||
monkeypatch.setattr(infra_actions, "status", lambda fresh=False: _st())
|
||||
|
||||
def _check(aid, st, dry):
|
||||
vazute.append(dry)
|
||||
return (True, "")
|
||||
|
||||
monkeypatch.setattr(infra_actions, "check", _check)
|
||||
monkeypatch.setattr(infra_actions, "run", lambda *a, **k: {"ok": True, "code": 200})
|
||||
await slash(bridge, "infra", actiune="oprire", simulare=None) # implicit: simulare
|
||||
await slash(bridge, "infra", actiune="oprire", simulare=False) # explicit: real
|
||||
assert vazute == [True, False]
|
||||
|
||||
|
||||
def test_infra_view_e_none_fara_discordpy(bridge):
|
||||
if bot.discord is None:
|
||||
assert bridge.infra_view("oprire", True) is None
|
||||
assert bridge.infra_boot_view() is None
|
||||
|
||||
|
||||
async def test_infra_boot_check_anunta_mentenanta_apoi_se_opreste(bridge, monkeypatch):
|
||||
ch = FakeChannel()
|
||||
monkeypatch.setenv("DISCORD_CHANNEL_IDS", str(ch.id))
|
||||
bridge.get_channel = lambda cid: ch if str(cid) == str(ch.id) else None
|
||||
|
||||
st = _st(maint=1)
|
||||
monkeypatch.setattr(infra_actions, "status", lambda fresh=False: st)
|
||||
monkeypatch.setattr(infra_actions, "maintenance_pending", lambda s: True)
|
||||
|
||||
await bridge.infra_boot_check()
|
||||
assert "guest-uri de pornit" in ch.all_text
|
||||
|
||||
|
||||
async def test_infra_boot_check_iese_daca_nu_e_in_mentenanta(bridge, monkeypatch):
|
||||
st = _st(maint=0)
|
||||
monkeypatch.setattr(infra_actions, "status", lambda fresh=False: st)
|
||||
apelat = []
|
||||
monkeypatch.setattr(infra_actions, "maintenance_pending", lambda s: apelat.append(s) or False)
|
||||
await bridge.infra_boot_check()
|
||||
assert apelat == [] # a iesit la prima citire (maint == 0), fara sa mai verifice
|
||||
|
||||
|
||||
async def test_infra_boot_check_nu_doboara_botul_la_eroare(bridge, monkeypatch):
|
||||
"""`status()` care arunca nu are voie sa opreasca bucla sau botul."""
|
||||
|
||||
def _boom(fresh=False):
|
||||
raise RuntimeError("retea picata")
|
||||
|
||||
async def _fast_sleep(_):
|
||||
return None
|
||||
|
||||
monkeypatch.setattr(infra_actions, "status", _boom)
|
||||
monkeypatch.setattr(asyncio, "sleep", _fast_sleep)
|
||||
await bridge.infra_boot_check() # 20 incercari esuate, nici o exceptie in afara
|
||||
|
||||
Reference in New Issue
Block a user