Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions scripts/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,20 @@ fi
# ── Run in hermetic env ──────────────────────────────────────────────────────
# env -i: start with empty environment, opt-in only what we need.
# No credential var can leak — you'd have to explicitly add it here.
#
# On Windows, Python's pathlib.Path.home() does not use the POSIX HOME alias;
# it resolves USERPROFILE or HOMEDRIVE+HOMEPATH. Preserve only those non-secret
# home-resolution variables so env -i remains hermetic without creating a
# literal '~' path or breaking Windows tests.
WINDOWS_HOME_ENV=()
if [[ -n "${USERPROFILE:-}" || -n "${HOMEDRIVE:-}${HOMEPATH:-}" ]]; then
for name in USERPROFILE HOMEDRIVE HOMEPATH; do
if [[ -n "${!name:-}" ]]; then
WINDOWS_HOME_ENV+=("$name=${!name}")
fi
done
fi

echo "▶ running per-file parallel test suite via run_tests_parallel.py"
echo " (TZ=UTC LANG=C.UTF-8 PYTHONHASHSEED=0; clean env)"

Expand All @@ -94,6 +108,7 @@ echo "▶ launching test runner"
exec env -i \
PATH="$PATH" \
HOME="$HOME" \
"${WINDOWS_HOME_ENV[@]}" \
TZ=UTC \
LANG=C.UTF-8 \
LC_ALL=C.UTF-8 \
Expand Down
11 changes: 9 additions & 2 deletions tests/plugins/memory/test_hindsight_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import re
import stat
import sys
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import AsyncMock, MagicMock

Expand All @@ -36,8 +37,8 @@


@pytest.fixture(autouse=True)
def _clean_env(monkeypatch):
"""Ensure no stale env vars leak between tests."""
def _clean_env(tmp_path, monkeypatch):
"""Ensure no stale env vars or Windows home state leak between tests."""
for key in (
"HINDSIGHT_API_KEY", "HINDSIGHT_API_URL", "HINDSIGHT_BANK_ID",
"HINDSIGHT_BUDGET", "HINDSIGHT_MODE", "HINDSIGHT_TIMEOUT",
Expand All @@ -48,6 +49,12 @@ def _clean_env(monkeypatch):
):
monkeypatch.delenv(key, raising=False)

# On Windows pathlib.Path.home() resolves USERPROFILE/HOMEDRIVE+HOMEPATH,
# not the POSIX HOME alias that these tests historically monkeypatched.
# Patch the actual API and keep all legacy profile writes in tmp_path.
isolated_home = tmp_path / "user-home"
monkeypatch.setattr(Path, "home", classmethod(lambda cls: isolated_home))


def _make_mock_client():
"""Create a mock Hindsight client with async methods."""
Expand Down
15 changes: 15 additions & 0 deletions tests/test_run_tests_shell_env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""Windows regression tests for the canonical hermetic runner."""

import os
from pathlib import Path

import pytest


@pytest.mark.skipif(os.name != "nt", reason="Windows home resolution is the behavior under test")
def test_runner_preserves_windows_home_resolution():
"""Path.home() must resolve when run under scripts/run_tests.sh."""
actual = Path.home()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion exercises only USERPROFILE. Please add a runner-level case with USERPROFILE absent and HOMEDRIVE plus HOMEPATH present, so the fallback branch added to scripts/run_tests.sh is covered.

expected = os.environ.get("USERPROFILE")
assert expected, "canonical runner dropped USERPROFILE"
assert actual == Path(expected)