from __future__ import annotations import pytest from mcp_common.environment import ( EnvironmentInfo, EnvironmentMismatchError, consistency_check, resolve_environment, ) def test_env_var_overrides_secret(monkeypatch): monkeypatch.setenv("DERIBIT_TESTNET", "false") creds = {"testnet": True, "base_url_live": "L", "base_url_testnet": "T"} info = resolve_environment( creds, env_var="DERIBIT_TESTNET", flag_key="testnet", exchange="deribit", ) assert info.environment == "mainnet" assert info.source == "env" assert info.env_value == "false" assert info.base_url == "L" def test_secret_used_when_env_missing(monkeypatch): monkeypatch.delenv("DERIBIT_TESTNET", raising=False) creds = {"testnet": True, "base_url_live": "L", "base_url_testnet": "T"} info = resolve_environment( creds, env_var="DERIBIT_TESTNET", flag_key="testnet", exchange="deribit", ) assert info.environment == "testnet" assert info.source == "credentials" assert info.env_value is None assert info.base_url == "T" def test_default_when_both_missing(monkeypatch): monkeypatch.delenv("FOO_TESTNET", raising=False) creds = {"base_url_live": "L", "base_url_testnet": "T"} info = resolve_environment( creds, env_var="FOO_TESTNET", flag_key="testnet", exchange="foo", ) assert info.environment == "testnet" assert info.source == "default" assert info.env_value is None @pytest.mark.parametrize("raw,expected", [ ("1", "testnet"), ("true", "testnet"), ("yes", "testnet"), ("on", "testnet"), ("TRUE", "testnet"), ("0", "mainnet"), ("false", "mainnet"), ("no", "mainnet"), ("off", "mainnet"), ("garbage", "mainnet"), ]) def test_env_value_truthy_parsing(monkeypatch, raw, expected): monkeypatch.setenv("X_TESTNET", raw) info = resolve_environment( {"base_url_live": "L", "base_url_testnet": "T"}, env_var="X_TESTNET", flag_key="testnet", exchange="x", ) assert info.environment == expected def test_default_base_urls_applied_when_creds_missing(monkeypatch): monkeypatch.delenv("X_TESTNET", raising=False) creds: dict = {} info = resolve_environment( creds, env_var="X_TESTNET", flag_key="testnet", exchange="x", default_base_url_live="https://live.example", default_base_url_testnet="https://test.example", ) assert info.base_url == "https://test.example" assert creds["base_url_live"] == "https://live.example" assert creds["base_url_testnet"] == "https://test.example" def test_creds_base_urls_override_defaults(monkeypatch): monkeypatch.delenv("X_TESTNET", raising=False) creds = {"base_url_live": "L", "base_url_testnet": "T"} info = resolve_environment( creds, env_var="X_TESTNET", flag_key="testnet", exchange="x", default_base_url_live="https://live.example", default_base_url_testnet="https://test.example", ) assert info.base_url == "T" assert creds["base_url_live"] == "L" def test_alpaca_paper_flag_key(monkeypatch): """Alpaca usa 'paper' invece di 'testnet' nel secret.""" monkeypatch.delenv("ALPACA_PAPER", raising=False) creds = {"paper": False, "base_url_live": "L", "base_url_testnet": "T"} info = resolve_environment( creds, env_var="ALPACA_PAPER", flag_key="paper", exchange="alpaca", ) assert info.environment == "mainnet" assert info.source == "credentials" # ───────── consistency_check ───────── def _info(env: str, exchange: str = "deribit") -> EnvironmentInfo: """Helper costruisce EnvironmentInfo per test.""" return EnvironmentInfo( exchange=exchange, environment=env, source="env", env_value="false" if env == "mainnet" else "true", base_url=f"https://api.{exchange}.com" if env == "mainnet" else f"https://test.{exchange}.com", ) def test_consistency_check_testnet_no_confirmation_ok(): """Testnet senza conferma esplicita → ok, ritorna []. Default safe.""" info = _info("testnet") creds = {"api_key": "k", "api_secret": "s"} warnings = consistency_check(info, creds) assert warnings == [] def test_consistency_check_mainnet_no_confirmation_raises(): """Mainnet senza creds['environment']='mainnet' esplicito → fail-fast.""" info = _info("mainnet") creds = {"api_key": "k", "api_secret": "s"} with pytest.raises(EnvironmentMismatchError, match="mainnet.*explicit confirmation"): consistency_check(info, creds) def test_consistency_check_mainnet_with_confirmation_ok(): info = _info("mainnet") creds = {"api_key": "k", "api_secret": "s", "environment": "mainnet"} warnings = consistency_check(info, creds) assert warnings == [] def test_consistency_check_explicit_mismatch_raises(): """Secret dichiara mainnet ma resolver risolve testnet → fail-fast.""" info = _info("testnet") creds = {"environment": "mainnet"} with pytest.raises(EnvironmentMismatchError, match="declared.*resolved"): consistency_check(info, creds) def test_consistency_check_strict_mainnet_disabled(): """Con strict_mainnet=False mainnet senza conferma logga warning ma non raise.""" info = _info("mainnet") creds = {"api_key": "k", "api_secret": "s"} warnings = consistency_check(info, creds, strict_mainnet=False) assert any("mainnet" in w for w in warnings) def test_consistency_check_url_does_not_match_environment_warns(): """Base URL contiene 'test' ma environment='mainnet' → warning.""" from mcp_common.environment import EnvironmentInfo info = EnvironmentInfo( exchange="bybit", environment="mainnet", source="env", env_value="false", base_url="https://api-testnet.bybit.com", # url DICE testnet ma resolver MAINNET ) creds = {"environment": "mainnet"} warnings = consistency_check(info, creds) assert any("base_url" in w.lower() for w in warnings)