diff --git a/tests/test_cache_writes.py b/tests/test_cache_writes.py index b4800faeb..0cea79a16 100644 --- a/tests/test_cache_writes.py +++ b/tests/test_cache_writes.py @@ -106,7 +106,7 @@ def test_on_windows_the_lock_is_waited_for_while_another_writer_holds_it(tmp_pat import errno import sys - fake, calls = _fake_msvcrt([OSError(errno.EDEADLOCK, "held"), None]) + fake, calls = _fake_msvcrt([OSError(errno.EDEADLK, "held"), None]) monkeypatch.setitem(sys.modules, "msvcrt", fake) with open(tmp_path / "log.md.lock", "a+b") as handle, files._hold_windows(handle): diff --git a/tests/test_chatgpt_auth.py b/tests/test_chatgpt_auth.py index d2a11d916..1c8729a7d 100644 --- a/tests/test_chatgpt_auth.py +++ b/tests/test_chatgpt_auth.py @@ -6,6 +6,7 @@ check of the sign-in (signature, issuer, audience, nonce, plan scope) runs for r import base64 import json +import os import socket import stat import threading @@ -165,7 +166,8 @@ def test_a_token_near_expiry_is_renewed_with_the_stored_refresh_token(home, open assert url == auth.TOKEN_URL and sent["refresh_token"] == "r1" and sent["client_id"] == CLIENT stored = json.loads(auth.AUTH_FILE.read_text()) assert stored["tokens"]["refresh_token"] == "r2" and stored["account"]["subject"] == SUBJECT - assert stat.S_IMODE(auth.AUTH_FILE.stat().st_mode) == 0o600 + if os.name != "nt": # Windows keeps no POSIX mode; the profile's ACL guards it + assert stat.S_IMODE(auth.AUTH_FILE.stat().st_mode) == 0o600 @pytest.mark.unit @@ -245,7 +247,8 @@ def test_signing_in_by_pasting_the_redirect_saves_a_private_sign_in(home, openai stored = json.loads(auth.AUTH_FILE.read_text()) assert stored["account"] == {"client_id": CLIENT, "email": "a@example.com", "subject": SUBJECT} assert stored["host_id"].startswith("urn:uuid:") - assert stat.S_IMODE(auth.AUTH_FILE.stat().st_mode) == 0o600 + if os.name != "nt": # Windows keeps no POSIX mode; the profile's ACL guards it + assert stat.S_IMODE(auth.AUTH_FILE.stat().st_mode) == 0o600 asked = dict(urllib.parse.parse_qsl(urllib.parse.urlparse(printed[1]).query)) assert asked["client_id"] == auth.DYNAMIC_CLIENT_ID and asked["agent_name_hint"] == "TradingAgents" assert asked["code_challenge_method"] == "S256" and asked["redirect_uri"].startswith("http://127.0.0.1:") diff --git a/tests/test_cli_commands.py b/tests/test_cli_commands.py index 0999a5349..eda137fad 100644 --- a/tests/test_cli_commands.py +++ b/tests/test_cli_commands.py @@ -83,12 +83,15 @@ class _Summary: def test_every_command_is_registered_when_run_as_a_module(): """README documents `python -m cli.main`, which executes the file top to bottom, so a command defined after the __main__ block would not exist.""" + import os import re import subprocess import sys + # Help draws boxes in UTF-8; read it as such whatever the locale (cp1252 on Windows). out = subprocess.run([sys.executable, "-m", "cli.main", "backtest", "--help"], - capture_output=True, text=True, timeout=120) + capture_output=True, text=True, encoding="utf-8", errors="replace", + env={**os.environ, "PYTHONIOENCODING": "utf-8"}, timeout=120) assert out.returncode == 0, out.stderr[-400:] # Where the terminal takes colour, help styles each option and splits # "--start" across escape sequences, so read the text without them. diff --git a/tests/test_cli_updates.py b/tests/test_cli_updates.py index 3e508dd2a..cb920c610 100644 --- a/tests/test_cli_updates.py +++ b/tests/test_cli_updates.py @@ -242,6 +242,7 @@ def _dist(direct=None, installer="pip"): @pytest.mark.unit +@pytest.mark.skipif(os.name == "nt", reason="the upgrade commands are POSIX shell") def test_the_upgrade_command_follows_the_install(tmp_path, monkeypatch): clone = tmp_path / "TradingAgents" (clone / ".git").mkdir(parents=True) @@ -314,6 +315,7 @@ def test_remote_text_cannot_carry_c1_or_direction_controls(): @pytest.mark.unit +@pytest.mark.skipif(os.name == "nt", reason="the upgrade commands are POSIX shell") def test_a_uv_git_install_is_upgraded_with_uv(monkeypatch): monkeypatch.delenv("TRADINGAGENTS_IN_DOCKER", raising=False) monkeypatch.setattr(updates.sys, "executable", "/venv/bin/python") diff --git a/tests/test_suite_isolation.py b/tests/test_suite_isolation.py index a14590d43..710737c3d 100644 --- a/tests/test_suite_isolation.py +++ b/tests/test_suite_isolation.py @@ -34,12 +34,16 @@ def test_a_test_saves_cli_selections_to_its_own_directory(tmp_path): @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.""" + """yfinance caches each symbol's time zone on disk, in the home directory by default. + + The suite's own temporary directory may itself sit under the home (``%TEMP%`` + on Windows), so the check is that the cache is the suite's, not where it is. + """ from pathlib import Path from yfinance.cache import _TzDBManager - assert not Path(_TzDBManager.get_location()).is_relative_to(Path.home()) + assert Path(_TzDBManager.get_location()).name.startswith("tradingagents-tests-yf-") @pytest.mark.unit diff --git a/tradingagents/dataflows/files.py b/tradingagents/dataflows/files.py index 3c77fe07e..d8b37fb28 100644 --- a/tradingagents/dataflows/files.py +++ b/tradingagents/dataflows/files.py @@ -74,12 +74,12 @@ def _hold_windows(handle, wait: bool = True) -> Iterator[bool]: msvcrt.locking(handle.fileno(), msvcrt.LK_LOCK if wait else msvcrt.LK_NBLCK, 1) break except OSError as exc: - if not wait and exc.errno in (errno.EACCES, errno.EDEADLOCK): + if not wait and exc.errno in (errno.EACCES, errno.EDEADLK): yield False # another writer holds it return # LK_LOCK gives up after about ten seconds of another writer's hold; # any other failure is not a wait. - if exc.errno != errno.EDEADLOCK: + if exc.errno != errno.EDEADLK: raise try: yield True