feat(gui): traduzione italiana, logo Cerbero, saldi live e Forza ciclo
* Localizzazione italiana di tutte le pagine (Stato, Audit, Equity,
Storico, Posizione) e della home; date relative ("5s fa", "12m fa").
* Logo Cerbero (cane a tre teste) in src/cerbero_bite/gui/assets/
cerbero_logo.png — sostituisce l'emoji 🐺 (lupo, semanticamente
errata) sia come favicon (`page_icon`) sia in sidebar e header.
* Caricamento automatico di `.env` dal CWD all'avvio della CLI (skip
sotto pytest tramite PYTEST_CURRENT_TEST), evitando di doversi
esportare manualmente le 4 URL MCP. Aggiunto python-dotenv come
dipendenza, `.env.example` committato come template, `.env` resta
ignorato da git.
* Pagina Stato: nuovo pannello "Saldi exchange" che fa fetch live
via gateway MCP (Deribit USDC + USDT, Hyperliquid USDC + opzionale
USDT spot) con cache TTL 60s e bottone refresh; tile riassuntivi
totale USD / EUR / cambio.
* Pagina Stato: nuovo pannello "Forza ciclo" con tre bottoni
(entry/monitor/health) che accodano azioni `run_cycle` nella tabella
manual_actions; il consumer dell'engine — quando in esecuzione —
dispatcha al `Orchestrator.run_*` corrispondente.
* manual_actions: nuovo `kind="run_cycle"` nello schema
ManualAction; consumer accetta dict di cycle_runners che
l'orchestrator popola in install_scheduler. 3 nuovi test (dispatch
entry, ciclo sconosciuto, fallback senza runner).
* gui/live_data.py — modulo dedicato al fetch MCP dalla GUI
(relax controllato della regola "no MCP from GUI" solo per i saldi,
non per i dati di trading).
363/363 tests pass; ruff clean; mypy strict src clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -157,3 +157,49 @@ async def test_empty_queue_returns_zero(tmp_path: Path) -> None:
|
||||
ctx = _ctx(tmp_path)
|
||||
n = await consume_manual_actions(ctx, now=_now())
|
||||
assert n == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_cycle_dispatches_to_runner(tmp_path: Path) -> None:
|
||||
ctx = _ctx(tmp_path)
|
||||
calls: list[str] = []
|
||||
|
||||
async def _entry() -> None:
|
||||
calls.append("entry")
|
||||
|
||||
aid = _enqueue(ctx, "run_cycle", {"cycle": "entry"})
|
||||
n = await consume_manual_actions(
|
||||
ctx, cycle_runners={"entry": _entry}, now=_now()
|
||||
)
|
||||
assert n == 1
|
||||
assert calls == ["entry"]
|
||||
row = _fetch_action(ctx, aid)
|
||||
assert row["result"] == "ok: ran entry"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_cycle_unknown_marked_error(tmp_path: Path) -> None:
|
||||
ctx = _ctx(tmp_path)
|
||||
|
||||
async def _entry() -> None:
|
||||
raise AssertionError("should not run")
|
||||
|
||||
aid = _enqueue(ctx, "run_cycle", {"cycle": "monitor"})
|
||||
n = await consume_manual_actions(
|
||||
ctx, cycle_runners={"entry": _entry}, now=_now()
|
||||
)
|
||||
assert n == 1
|
||||
row = _fetch_action(ctx, aid)
|
||||
assert "unknown cycle" in (row["result"] or "")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_cycle_without_runners_marks_not_supported(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
ctx = _ctx(tmp_path)
|
||||
aid = _enqueue(ctx, "run_cycle", {"cycle": "entry"})
|
||||
n = await consume_manual_actions(ctx, now=_now())
|
||||
assert n == 1
|
||||
row = _fetch_action(ctx, aid)
|
||||
assert row["result"] == "not_supported"
|
||||
|
||||
Reference in New Issue
Block a user