Files
hermes-agent/tests/tools/test_code_execution_windows_env.py
2026-10-10 15:20:13 +02:00

134 lines
5.2 KiB
Python

"""Windows essentials and the native Winsock control.
Actual production UTF-8 env and Unicode RPC are exercised in
`test_code_execution_modes.py`; never reproduce the production scrubber here.
"""
import os
import subprocess
import sys
import textwrap
import time
import pytest
from tools.code_execution_env import _scrub_child_env
from tools import code_execution_env
def _no_passthrough(_):
return False
@pytest.mark.parametrize("windows", [False, True])
@pytest.mark.parametrize("passthrough", [False, True])
def test_native_essentials_and_passthrough_priority(windows, passthrough):
# Platform is explicit input to this pure policy helper, not a fake host.
essentials = {
"SYSTEMROOT": r"C:\Windows", "SystemRoot": r"C:\Windows",
"SystemDrive": "C:", "WINDIR": r"C:\Windows",
"ComSpec": r"C:\Windows\System32\cmd.exe", "comspec": r"C:\Windows\System32\cmd.exe",
"APPDATA": r"C:\Users\alice\AppData\Roaming",
"LOCALAPPDATA": r"C:\Users\alice\AppData\Local",
}
safe = {"PATH": r"C:\Windows\System32;C:\Python", "HOME": r"C:\Users\alice",
"PATHEXT": ".COM;.EXE;.BAT;.CMD;.PY",
"USERPROFILE": r"C:\Users\alice", "TEMP": r"C:\Users\alice\Temp"}
secret = {"OPENAI_API_KEY": "fake-provider", "GITHUB_TOKEN": "fake-github",
"MY_PASSWORD": "fake-password", "TENOR_API_KEY": "fake-third-party",
"RANDOM_UNKNOWN_VAR": "unknown"}
result = _scrub_child_env({**essentials, **safe, **secret}, is_windows=windows,
is_passthrough=lambda k: passthrough and k == "TENOR_API_KEY")
assert result == {**safe, **(essentials if windows else {}),
**({"TENOR_API_KEY": "fake-third-party"} if passthrough else {})}
# ``platforms("windows")`` rather than ``skipif(sys.platform != "win32")``: the
# dedicated Windows CI job selects its files by grepping for the marker, so a
# bare skipif is invisible to it — the file is never imported there and these
# tests run on no host at all.
@pytest.mark.platforms("windows")
class TestWindowsSocketSmokeTest:
"""Integration-ish smoke test: spawn a child Python with a scrubbed
env and confirm it can create an AF_INET socket. This is the
regression that motivated the fix — without SYSTEMROOT the child
hits WinError 10106 before any RPC is attempted."""
def test_child_can_create_socket_with_scrubbed_env(self):
scrubbed = _scrub_child_env(os.environ, is_passthrough=_no_passthrough)
# Build a tiny child script that simply opens an AF_INET socket.
script = textwrap.dedent("""
import socket, sys
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.close()
print("OK")
sys.exit(0)
except OSError as exc:
print(f"FAIL: {exc}")
sys.exit(1)
""").strip()
result = subprocess.run(
[sys.executable, "-c", script],
env=scrubbed,
capture_output=True,
text=True,
timeout=15,
check=False,
)
assert result.returncode == 0, (
f"Child failed to create socket with scrubbed env:\n"
f" stdout={result.stdout!r}\n"
f" stderr={result.stderr!r}\n"
f" scrubbed keys={sorted(scrubbed.keys())}"
)
assert "OK" in result.stdout
def _configured_timezone_child_env():
return code_execution_env._build_child_env(
rpc_endpoint="socket",
rpc_token="token",
tmpdir="/tmp/hermes-code-execution-test",
child_python=sys.executable,
)
@pytest.mark.platforms("windows")
def test_windows_live_child_offset_matches_os_zone_when_timezone_is_configured(monkeypatch):
"""The user-visible contract of #112233: with ``timezone:`` configured, a real Windows child
must report the OS zone's UTC offset — an IANA name in ``TZ`` made the MSVC runtime derive
``time.timezone == 0`` (+01:00 instead of -07:00) while ``time.tzname`` still read correctly."""
import json
monkeypatch.setattr("hermes_time.get_timezone_name", lambda: "America/Los_Angeles")
child_env = _configured_timezone_child_env()
assert "TZ" not in child_env
# The runner starts Python with TZ=UTC; its cached timezone is not the OS zone.
# Query a fresh control process with TZ removed, independently of the builder.
control_env = os.environ.copy()
control_env.pop("TZ", None)
timestamp = str(time.time()) # Both children observe the same instant across DST changes.
script = (
"import datetime, json, sys, time; "
"instant = datetime.datetime.fromtimestamp(float(sys.argv[1]), datetime.timezone.utc); "
"print(json.dumps([time.timezone, instant.astimezone().utcoffset().total_seconds()]))"
)
def read_timezone(env):
result = subprocess.run(
[sys.executable, "-c", script, timestamp],
env=env, stdin=subprocess.DEVNULL, capture_output=True,
text=True, encoding="utf-8", errors="replace", timeout=30,
check=False,
)
assert result.returncode == 0, result.stderr
return json.loads(result.stdout)
assert read_timezone(child_env) == read_timezone(control_env)