411b747e93
Sei interventi mirati sui rischi operativi rilevati nell'audit post-Fase 4. 317 test pass, mypy strict pulito, ruff clean. 1. status CLI: legge SQLite reale e mostra kill_switch, posizioni aperte, environment, config_version, last_health_check, started_at. Sostituisce il placeholder "phase 0 skeleton". 2. Lock file single-instance: runtime/lockfile.py acquisisce data/.lockfile via fcntl.flock al boot di run_forever; un secondo container fallisce subito con LockError. 3. Backup orario nello scheduler: nuovo job APScheduler 0 * * * * chiama scripts.backup.backup_database + prune_backups. 4. config_hash enforce su start: il CLI start verifica l'integrità del file (enforce_hash=True). Mismatch → exit 1 prima di toccare stato. dry-run resta enforce_hash=False per debug. 5. Connection pooling MCP: RuntimeContext espone un httpx.AsyncClient long-lived condiviso da tutti i wrapper (limits 20/10 connections/keepalive). aclose() chiamato in run_forever finale. 6. Bias direzionale reale: deribit.historical_close + deribit.adx_14 popolano TrendContext con spot a 30 giorni e ADX(14) effettivi. Sblocca bull_put e bear_call. Quando i dati storici mancano l'engine emette alert MEDIUM e cade su no_entry in modo deterministico. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
97 lines
2.6 KiB
Python
97 lines
2.6 KiB
Python
"""Smoke tests: package importable, version exposed, CLI invokable."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from click.testing import CliRunner
|
|
|
|
import cerbero_bite
|
|
from cerbero_bite import __version__
|
|
from cerbero_bite.cli import main as cli_main
|
|
|
|
|
|
def test_package_version_string() -> None:
|
|
assert isinstance(__version__, str)
|
|
assert __version__.count(".") == 2
|
|
|
|
|
|
def test_package_module_attribute() -> None:
|
|
assert cerbero_bite.__version__ == __version__
|
|
|
|
|
|
def test_cli_help_lists_status_command() -> None:
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli_main, ["--help"])
|
|
assert result.exit_code == 0
|
|
assert "status" in result.output
|
|
|
|
|
|
def test_cli_status_when_state_missing(tmp_data_dir: Path) -> None:
|
|
runner = CliRunner()
|
|
result = runner.invoke(
|
|
cli_main,
|
|
[
|
|
"--log-dir",
|
|
str(tmp_data_dir / "log"),
|
|
"status",
|
|
"--db",
|
|
str(tmp_data_dir / "missing.sqlite"),
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
assert "Cerbero Bite" in result.output
|
|
assert "never started" in result.output
|
|
|
|
|
|
def test_cli_status_after_kill_switch_arm(tmp_data_dir: Path) -> None:
|
|
runner = CliRunner()
|
|
db_path = tmp_data_dir / "state.sqlite"
|
|
audit_path = tmp_data_dir / "audit.log"
|
|
runner.invoke(
|
|
cli_main,
|
|
[
|
|
"--log-dir", str(tmp_data_dir / "log"),
|
|
"kill-switch", "arm",
|
|
"--reason", "smoke",
|
|
"--db", str(db_path),
|
|
"--audit", str(audit_path),
|
|
],
|
|
)
|
|
result = runner.invoke(cli_main, ["status", "--db", str(db_path)])
|
|
assert result.exit_code == 0
|
|
assert "ARMED" in result.output
|
|
assert "open positions: 0" in result.output
|
|
|
|
|
|
def test_cli_kill_switch_arm_persists_state(tmp_data_dir: Path) -> None:
|
|
runner = CliRunner()
|
|
db_path = tmp_data_dir / "state.sqlite"
|
|
audit_path = tmp_data_dir / "audit.log"
|
|
result = runner.invoke(
|
|
cli_main,
|
|
[
|
|
"--log-dir",
|
|
str(tmp_data_dir / "log"),
|
|
"kill-switch",
|
|
"arm",
|
|
"--reason",
|
|
"smoke",
|
|
"--db",
|
|
str(db_path),
|
|
"--audit",
|
|
str(audit_path),
|
|
],
|
|
)
|
|
assert result.exit_code == 0, result.output
|
|
assert "ARMED" in result.output
|
|
assert db_path.exists()
|
|
assert audit_path.exists()
|
|
|
|
|
|
def test_cli_version_flag() -> None:
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli_main, ["--version"])
|
|
assert result.exit_code == 0
|
|
assert __version__ in result.output
|