75f50fe083
Co-authored-by: Cursor <cursoragent@cursor.com>
171 lines
7.5 KiB
Python
171 lines
7.5 KiB
Python
"""instance_display_prefs_lib 与 env_file_lib 单元测试."""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import tempfile
|
|
import unittest
|
|
|
|
from lib.env.env_file_lib import apply_env_updates, env_get, read_env_lines
|
|
from lib.env.env_schema import parse_env_example_schema, validate_env_updates
|
|
from lib.instance.instance_display_prefs_lib import normalize_display_prefs, tab_allowed
|
|
|
|
|
|
class TestInstanceDisplayPrefs(unittest.TestCase):
|
|
def test_normalize_defaults_all_on(self):
|
|
prefs = normalize_display_prefs({})
|
|
self.assertTrue(prefs["show_nav_env_config"])
|
|
self.assertTrue(prefs["show_settings_password"])
|
|
self.assertFalse(prefs["show_nav_dashboard"])
|
|
|
|
def test_tab_allowed_respects_prefs(self):
|
|
prefs = normalize_display_prefs({"show_nav_stats": False})
|
|
self.assertFalse(tab_allowed("stats", prefs))
|
|
self.assertTrue(tab_allowed("trade", prefs))
|
|
self.assertTrue(tab_allowed("key_monitor", prefs))
|
|
|
|
def test_key_monitor_and_trade_nav_can_hide(self):
|
|
prefs = normalize_display_prefs(
|
|
{"show_nav_key_monitor": False, "show_nav_trade": False}
|
|
)
|
|
self.assertFalse(tab_allowed("key_monitor", prefs))
|
|
self.assertFalse(tab_allowed("trade", prefs))
|
|
on = normalize_display_prefs({})
|
|
self.assertTrue(on["show_nav_key_monitor"])
|
|
self.assertTrue(on["show_nav_trade"])
|
|
self.assertTrue(tab_allowed("key_monitor", on))
|
|
self.assertTrue(tab_allowed("trade", on))
|
|
|
|
def test_dashboard_nav_default_off(self):
|
|
prefs = normalize_display_prefs({})
|
|
self.assertFalse(tab_allowed("dashboard", prefs))
|
|
on = normalize_display_prefs({"show_nav_dashboard": True})
|
|
self.assertTrue(tab_allowed("dashboard", on))
|
|
|
|
def test_system_guide_nav_default_off(self):
|
|
prefs = normalize_display_prefs({})
|
|
self.assertFalse(prefs["show_nav_system_guide"])
|
|
self.assertFalse(tab_allowed("system_guide", prefs))
|
|
on = normalize_display_prefs({"show_nav_system_guide": True})
|
|
self.assertTrue(tab_allowed("system_guide", on))
|
|
|
|
|
|
class TestEnvFileLib(unittest.TestCase):
|
|
def test_upsert_and_read(self):
|
|
with tempfile.TemporaryDirectory() as td:
|
|
path = os.path.join(td, ".env")
|
|
with open(path, "w", encoding="utf-8") as f:
|
|
f.write("FOO=1\n")
|
|
changed = apply_env_updates(path, {"FOO": "2", "BAR": "x"})
|
|
self.assertIn("FOO", changed)
|
|
self.assertIn("BAR", changed)
|
|
lines = read_env_lines(path)
|
|
self.assertEqual(env_get(lines, "FOO"), "2")
|
|
self.assertEqual(env_get(lines, "BAR"), "x")
|
|
|
|
|
|
class TestEnvSchema(unittest.TestCase):
|
|
def test_parse_okx_example(self):
|
|
root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
example = os.path.join(root, "crypto_monitor_okx", ".env.example")
|
|
if not os.path.isfile(example):
|
|
self.skipTest("missing okx .env.example")
|
|
groups = parse_env_example_schema(example)
|
|
keys = [f["key"] for g in groups for f in g.get("fields", [])]
|
|
self.assertIn("OKX_API_KEY", keys)
|
|
self.assertIn("MAX_ACTIVE_POSITIONS", keys)
|
|
|
|
def test_validate_unknown_key(self):
|
|
groups = [{"title": "t", "fields": [{"key": "A", "type": "text", "sensitive": False}]}]
|
|
clean, errors = validate_env_updates(groups, {"B": "1"})
|
|
self.assertTrue(errors)
|
|
|
|
def test_select_fields_and_validate(self):
|
|
from lib.env.env_schema import SELECT_OPTIONS, normalize_select_value, select_options_for
|
|
from lib.env.env_ui_manifest import build_env_ui_payload
|
|
|
|
self.assertEqual(normalize_select_value("BINANCE_MARGIN_MODE", "cross_margin"), "cross")
|
|
opts = select_options_for("TRADE_DIRECTION")
|
|
self.assertEqual({o["value"] for o in opts}, {"both", "long_only", "short_only"})
|
|
for key in (
|
|
"OKX_TD_MODE",
|
|
"OKX_POS_MODE",
|
|
"POSITION_SIZING_MODE",
|
|
"TRADE_DIRECTION",
|
|
"OKX_OPTIONS_MARGIN_MODE",
|
|
):
|
|
self.assertIn(key, SELECT_OPTIONS)
|
|
|
|
root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
example = os.path.join(root, "crypto_monitor_okx", ".env.example")
|
|
env_path = os.path.join(root, "crypto_monitor_okx", ".env")
|
|
if not os.path.isfile(example):
|
|
self.skipTest("missing okx .env.example")
|
|
groups = build_env_ui_payload("okx", example, env_path if os.path.isfile(env_path) else example)
|
|
by_key = {f["key"]: f for g in groups for f in g["fields"]}
|
|
for key in (
|
|
"OKX_TD_MODE",
|
|
"OKX_POS_MODE",
|
|
"POSITION_SIZING_MODE",
|
|
"TRADE_DIRECTION",
|
|
"OKX_OPTIONS_MARGIN_MODE",
|
|
):
|
|
self.assertEqual(by_key[key]["type"], "select")
|
|
self.assertTrue(by_key[key]["options"])
|
|
self.assertEqual(
|
|
{o["value"] for o in by_key["OKX_OPTIONS_MARGIN_MODE"]["options"]},
|
|
{"usdc", "coin"},
|
|
)
|
|
self.assertIn("KEY_AUTO_ORDER_ENABLED", by_key)
|
|
self.assertEqual(by_key["KEY_AUTO_ORDER_ENABLED"]["label"], "关键位自动单")
|
|
self.assertEqual(by_key["KEY_AUTO_ORDER_ENABLED"]["type"], "bool")
|
|
self.assertIn("KEY_AUTO_MIN_PLANNED_RR", by_key)
|
|
self.assertIn("OKX_SHOW_PERP_FUNDS", by_key)
|
|
self.assertEqual(by_key["OKX_SHOW_PERP_FUNDS"]["label"], "显示永续资金")
|
|
self.assertEqual(by_key["OKX_SHOW_PERP_FUNDS"]["type"], "bool")
|
|
self.assertTrue(by_key["OKX_SHOW_PERP_FUNDS"].get("hot_reload"))
|
|
self.assertNotIn("OKX_OPTIONS_API_KEY", by_key)
|
|
self.assertNotIn("OKX_SUB_ACCOUNT_NAME", by_key)
|
|
self.assertNotIn("OKX_API_KEY", by_key)
|
|
self.assertNotIn("OKX_API_SECRET", by_key)
|
|
self.assertNotIn("OKX_API_PASSPHRASE", by_key)
|
|
self.assertIn("LIVE_TRADING_ENABLED", by_key)
|
|
|
|
groups_v = [{"title": "t", "fields": [by_key["TRADE_DIRECTION"]]}]
|
|
clean, errors = validate_env_updates(groups_v, {"TRADE_DIRECTION": "long_only"})
|
|
self.assertEqual(errors, [])
|
|
self.assertEqual(clean["TRADE_DIRECTION"], "long_only")
|
|
_, bad = validate_env_updates(groups_v, {"TRADE_DIRECTION": "sideways"})
|
|
self.assertTrue(bad)
|
|
|
|
|
|
class TestShowPerpFunds(unittest.TestCase):
|
|
def test_okx_default_on(self):
|
|
from lib.instance.instance_embed_context_lib import show_perp_funds_enabled
|
|
|
|
old = os.environ.pop("OKX_SHOW_PERP_FUNDS", None)
|
|
try:
|
|
self.assertTrue(show_perp_funds_enabled(exchange_key="okx"))
|
|
os.environ["OKX_SHOW_PERP_FUNDS"] = "false"
|
|
self.assertFalse(show_perp_funds_enabled(exchange_key="okx"))
|
|
self.assertTrue(show_perp_funds_enabled(exchange_key="gate"))
|
|
finally:
|
|
if old is None:
|
|
os.environ.pop("OKX_SHOW_PERP_FUNDS", None)
|
|
else:
|
|
os.environ["OKX_SHOW_PERP_FUNDS"] = old
|
|
|
|
def test_options_funding_label_usdc_only(self):
|
|
from lib.instance.instance_embed_context_lib import options_funding_label, trading_account_label
|
|
|
|
self.assertEqual(options_funding_label(12.5, 99.0), "12.50 USDC")
|
|
self.assertEqual(options_funding_label(0.0, 50.0), "0.00 USDC")
|
|
self.assertEqual(options_funding_label(None, 10.0), "—")
|
|
self.assertEqual(
|
|
trading_account_label(20.0, 0.01, 0.001, margin_mode="coin"),
|
|
"20.00 USDT\n0.01 ETH\n0.001 BTC",
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|