"""Per-instance watchdog timeout TOML tests (REQ-076/077, dcy-027, BAT-041). TOML `watchdog_timeout_sec` wins over env TREX_WATCHDOG_TIMEOUT; absent keeps the env value (default 120) so intraday instances are byte-for-byte unaffected. The long-term profile combines this with safety_sell_enabled=false. """ from __future__ import annotations from pathlib import Path import pytest from trex_core.runtime.config import DaemonConfig, DaemonInstanceConfig _REPO = Path(__file__).resolve().parents[2] def _write_toml(tmp_path, body: str, name: str = "inst.toml") -> str: p = tmp_path / name p.write_text(body) return str(p) # --- REQ-076 AC-1: TOML value applied ----------------------------------------- def test_toml_watchdog_timeout_applied(tmp_path, monkeypatch): monkeypatch.setenv("TREX_POSTGRESQL_DSN", "postgresql://x/y") path = _write_toml(tmp_path, 'instance_key = "lt"\nwatchdog_timeout_sec = 1800\n') cfg = DaemonConfig.from_config(path) assert cfg.safety_sell.watchdog_timeout_sec == 1800 def test_toml_wins_over_env(tmp_path, monkeypatch): monkeypatch.setenv("TREX_POSTGRESQL_DSN", "postgresql://x/y") monkeypatch.setenv("TREX_WATCHDOG_TIMEOUT", "300") path = _write_toml(tmp_path, 'instance_key = "lt"\nwatchdog_timeout_sec = 1800\n') assert DaemonConfig.from_config(path).safety_sell.watchdog_timeout_sec == 1800 # --- REQ-076 AC-2: absent -> env fallback (no regression) --------------------- def test_absent_falls_back_to_env(tmp_path, monkeypatch): monkeypatch.setenv("TREX_POSTGRESQL_DSN", "postgresql://x/y") monkeypatch.setenv("TREX_WATCHDOG_TIMEOUT", "300") path = _write_toml(tmp_path, 'instance_key = "day"\n') assert DaemonConfig.from_config(path).safety_sell.watchdog_timeout_sec == 300 def test_absent_and_no_env_keeps_default_120(tmp_path, monkeypatch): monkeypatch.setenv("TREX_POSTGRESQL_DSN", "postgresql://x/y") monkeypatch.delenv("TREX_WATCHDOG_TIMEOUT", raising=False) path = _write_toml(tmp_path, 'instance_key = "day"\n') assert DaemonConfig.from_config(path).safety_sell.watchdog_timeout_sec == 120 # --- REQ-076 AC-3: per-instance independence ---------------------------------- def test_instances_get_independent_timeouts(tmp_path, monkeypatch): monkeypatch.setenv("TREX_POSTGRESQL_DSN", "postgresql://x/y") monkeypatch.delenv("TREX_WATCHDOG_TIMEOUT", raising=False) day = _write_toml(tmp_path, 'instance_key = "day"\n', name="day.toml") lt = _write_toml( tmp_path, 'instance_key = "lt"\nwatchdog_timeout_sec = 1800\n', name="lt.toml" ) assert DaemonConfig.from_config(day).safety_sell.watchdog_timeout_sec == 120 assert DaemonConfig.from_config(lt).safety_sell.watchdog_timeout_sec == 1800 # --- from_toml validation (design: <= 0 rejected) ------------------------------ def test_nonpositive_timeout_rejected(tmp_path): for bad in ("0", "-60"): path = _write_toml(tmp_path, f'instance_key = "x"\nwatchdog_timeout_sec = {bad}\n') with pytest.raises(RuntimeError): DaemonInstanceConfig.from_toml(path) # --- REQ-077 AC-1/AC-4: long-term profile recipe ------------------------------- def test_longterm_example_profile_loads_with_expected_combination(monkeypatch): """The shipped example profile yields: dedicated instance_key, safety-sell worker OFF, watchdog 1800s, capacity cap set.""" monkeypatch.setenv("TREX_POSTGRESQL_DSN", "postgresql://x/y") example = _REPO / "config" / "longterm.example.toml" cfg = DaemonConfig.from_config(str(example)) assert cfg.instance_key == "longterm" # A-2: separate instance scope assert cfg.safety_sell_enabled is False # autonomous liquidation OFF assert cfg.safety_sell.watchdog_timeout_sec == 1800 assert cfg.max_active_slots == 10