diff --git a/tests/conftest.py b/tests/conftest.py index c134581fe..ffd70a3c3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -27,9 +27,33 @@ def _blank_settings_overlay(): _blank_settings_overlay() +def _own_file_locations(): + """Keep the suite's results, cache and memory log in a directory of its own. + + Set before the package is imported, whose defaults put them in the user's + home: a test reading the user's cache would see what their runs fetched. + """ + import tempfile + + home = tempfile.mkdtemp(prefix="tradingagents-tests-") + os.environ["TRADINGAGENTS_RESULTS_DIR"] = os.path.join(home, "logs") + os.environ["TRADINGAGENTS_CACHE_DIR"] = os.path.join(home, "cache") + os.environ["TRADINGAGENTS_MEMORY_LOG_PATH"] = os.path.join(home, "memory", "trading_memory.md") + + +_own_file_locations() + + def pytest_configure(config): for marker in ("unit", "integration", "smoke"): config.addinivalue_line("markers", f"{marker}: {marker}-level tests") + # yfinance caches each symbol's time zone on disk, in the home directory + # by default; the suite keeps it in a directory of its own. + import tempfile + + import yfinance + + yfinance.set_tz_cache_location(tempfile.mkdtemp(prefix="tradingagents-tests-yf-")) @pytest.fixture(autouse=True) @@ -38,11 +62,17 @@ def _no_network(request, monkeypatch): if request.node.get_closest_marker("integration"): return - def refuse(self, address): - raise OSError(f"test tried to reach the network: {address}") + def refuse(*args, **kwargs): + raise OSError(f"test tried to reach the network: {args[1:] or kwargs}") monkeypatch.setattr(socket.socket, "connect", refuse) monkeypatch.setattr(socket.socket, "connect_ex", refuse) + monkeypatch.setattr(socket, "getaddrinfo", lambda *a, **k: refuse(None, *a)) + # yfinance's HTTP client is libcurl, which never touches Python's sockets. + from curl_cffi import requests as curl_requests + + monkeypatch.setattr(curl_requests.Session, "request", refuse) + monkeypatch.setattr(curl_requests.AsyncSession, "request", refuse) @pytest.fixture(autouse=True) diff --git a/tests/test_suite_isolation.py b/tests/test_suite_isolation.py index 7337e232e..a14590d43 100644 --- a/tests/test_suite_isolation.py +++ b/tests/test_suite_isolation.py @@ -9,7 +9,10 @@ import pytest @pytest.mark.parametrize("connect", [ lambda: socket.create_connection(("192.0.2.1", 80), timeout=1), lambda: socket.socket().connect_ex(("192.0.2.1", 80)), -], ids=["connect", "connect_ex"]) + lambda: socket.getaddrinfo("example.com", 443), + # yfinance's HTTP client is libcurl, which never touches Python's sockets. + lambda: __import__("curl_cffi.requests").requests.Session().get("http://192.0.2.1", timeout=1), +], ids=["connect", "connect_ex", "dns", "curl_cffi"]) def test_a_test_cannot_reach_the_network(connect): """A test that silently depends on a live vendor passes or fails with the machine it runs on; conftest refuses the connection instead.""" @@ -27,3 +30,25 @@ def test_a_test_saves_cli_selections_to_its_own_directory(tmp_path): assert prefs._PREFS_PATH.is_relative_to(tmp_path) assert prefs._PREFS_PATH.exists() + + +@pytest.mark.unit +def test_yfinance_keeps_its_cache_out_of_the_users_home(): + """yfinance caches each symbol's time zone on disk, in the home directory by default.""" + from pathlib import Path + + from yfinance.cache import _TzDBManager + + assert not Path(_TzDBManager.get_location()).is_relative_to(Path.home()) + + +@pytest.mark.unit +@pytest.mark.parametrize("key", ["results_dir", "data_cache_dir", "memory_log_path"]) +def test_the_suite_keeps_its_files_out_of_the_users_home(key): + """A test that reads the user's cache sees what their runs fetched, and passes + or fails with it; one that writes there changes their data.""" + from pathlib import Path + + from tradingagents.default_config import DEFAULT_CONFIG + + assert not Path(DEFAULT_CONFIG[key]).is_relative_to(Path.home() / ".tradingagents")