"""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)) 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)) 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", ): 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"): self.assertEqual(by_key[key]["type"], "select") self.assertTrue(by_key[key]["options"]) 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) 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) if __name__ == "__main__": unittest.main()