Skip to content

Commit 41dce05

Browse files
Fix test isolation using subprocess to avoid env pollution
The original tests used module reloading which polluted sys.modules for other tests. Using subprocesses ensures complete isolation. Co-authored-by: openhands <openhands@all-hands.dev>
1 parent d088cfa commit 41dce05

File tree

1 file changed

+72
-69
lines changed

1 file changed

+72
-69
lines changed
Lines changed: 72 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,91 @@
1-
"""Tests for EXTENSIONS_REF environment variable support."""
1+
"""Tests for EXTENSIONS_REF environment variable support.
22
3-
import os
4-
import sys
5-
from unittest import mock
3+
These tests use subprocess to run each test in an isolated Python process,
4+
avoiding module state pollution that would affect other tests.
5+
"""
66

7+
import subprocess
8+
import sys
79

8-
def test_extensions_ref_default():
9-
"""PUBLIC_SKILLS_BRANCH should default to 'main' when EXTENSIONS_REF is not set."""
10-
# Clear EXTENSIONS_REF if set
11-
with mock.patch.dict(os.environ, {}, clear=False):
12-
if "EXTENSIONS_REF" in os.environ:
13-
del os.environ["EXTENSIONS_REF"]
1410

15-
# Force reload of the module to pick up environment variable
16-
if "openhands.sdk.context.skills.skill" in sys.modules:
17-
del sys.modules["openhands.sdk.context.skills.skill"]
11+
def _run_in_subprocess(test_code: str, env_extra: dict | None = None) -> None:
12+
"""Run test code in a subprocess with the given environment variables."""
13+
import os
1814

19-
from openhands.sdk.context.skills.skill import PUBLIC_SKILLS_BRANCH
15+
env = os.environ.copy()
16+
if env_extra:
17+
env.update(env_extra)
2018

21-
assert PUBLIC_SKILLS_BRANCH == "main", (
22-
f"Expected 'main' but got '{PUBLIC_SKILLS_BRANCH}'"
19+
result = subprocess.run(
20+
[sys.executable, "-c", test_code],
21+
env=env,
22+
capture_output=True,
23+
text=True,
24+
)
25+
if result.returncode != 0:
26+
raise AssertionError(
27+
f"Subprocess test failed:\nstdout: {result.stdout}\nstderr: {result.stderr}"
2328
)
2429

2530

26-
def test_extensions_ref_custom_branch():
27-
"""PUBLIC_SKILLS_BRANCH should use EXTENSIONS_REF when set."""
28-
# Set EXTENSIONS_REF
29-
with mock.patch.dict(os.environ, {"EXTENSIONS_REF": "feature-branch"}, clear=False):
30-
# Force reload of the module to pick up environment variable
31-
if "openhands.sdk.context.skills.skill" in sys.modules:
32-
del sys.modules["openhands.sdk.context.skills.skill"]
31+
def test_extensions_ref_default():
32+
"""PUBLIC_SKILLS_BRANCH should default to 'main' when EXTENSIONS_REF is not set."""
33+
code = """
34+
import os
35+
if "EXTENSIONS_REF" in os.environ:
36+
del os.environ["EXTENSIONS_REF"]
37+
from openhands.sdk.context.skills.skill import PUBLIC_SKILLS_BRANCH
38+
assert PUBLIC_SKILLS_BRANCH == "main", (
39+
f"Expected 'main' but got '{PUBLIC_SKILLS_BRANCH}'"
40+
)
41+
"""
42+
_run_in_subprocess(code)
3343

34-
from openhands.sdk.context.skills.skill import PUBLIC_SKILLS_BRANCH
3544

36-
assert PUBLIC_SKILLS_BRANCH == "feature-branch", (
37-
f"Expected 'feature-branch' but got '{PUBLIC_SKILLS_BRANCH}'"
38-
)
45+
def test_extensions_ref_custom_branch():
46+
"""PUBLIC_SKILLS_BRANCH should use EXTENSIONS_REF when set."""
47+
code = """
48+
from openhands.sdk.context.skills.skill import PUBLIC_SKILLS_BRANCH
49+
assert PUBLIC_SKILLS_BRANCH == "feature-branch", (
50+
f"Expected 'feature-branch' but got '{PUBLIC_SKILLS_BRANCH}'"
51+
)
52+
"""
53+
_run_in_subprocess(code, {"EXTENSIONS_REF": "feature-branch"})
3954

4055

4156
def test_extensions_ref_with_load_public_skills():
4257
"""load_public_skills should respect EXTENSIONS_REF environment variable."""
43-
with mock.patch.dict(os.environ, {"EXTENSIONS_REF": "test-branch"}, clear=False):
44-
# Force reload of the module
45-
if "openhands.sdk.context.skills.skill" in sys.modules:
46-
del sys.modules["openhands.sdk.context.skills.skill"]
47-
48-
from openhands.sdk.context.skills.skill import (
49-
PUBLIC_SKILLS_BRANCH,
50-
load_public_skills,
51-
)
52-
53-
# Verify the constant is set correctly
54-
assert PUBLIC_SKILLS_BRANCH == "test-branch"
55-
56-
# Mock the actual git operations and verify branch is passed
57-
with mock.patch(
58-
"openhands.sdk.context.skills.skill.update_skills_repository"
59-
) as mock_update:
60-
mock_update.return_value = (
61-
None # Simulate failed clone (expected behavior for test)
62-
)
63-
64-
load_public_skills()
65-
66-
# Verify the branch was passed to update_skills_repository
67-
mock_update.assert_called_once()
68-
call_args = mock_update.call_args
69-
# branch is 2nd positional arg: (repo_url, branch, cache_dir)
70-
assert call_args[0][1] == "test-branch", (
71-
f"Expected branch='test-branch' but got {call_args[0][1]}"
72-
)
58+
code = """
59+
from unittest import mock
60+
from openhands.sdk.context.skills.skill import (
61+
PUBLIC_SKILLS_BRANCH,
62+
load_public_skills,
63+
)
64+
assert PUBLIC_SKILLS_BRANCH == "test-branch", (
65+
f"Expected 'test-branch' but got '{PUBLIC_SKILLS_BRANCH}'"
66+
)
67+
with mock.patch(
68+
"openhands.sdk.context.skills.skill.update_skills_repository"
69+
) as mock_update:
70+
mock_update.return_value = None
71+
load_public_skills()
72+
mock_update.assert_called_once()
73+
call_args = mock_update.call_args
74+
# branch is 2nd positional arg: (repo_url, branch, cache_dir)
75+
assert call_args[0][1] == "test-branch", (
76+
f"Expected branch='test-branch' but got {call_args[0][1]}"
77+
)
78+
"""
79+
_run_in_subprocess(code, {"EXTENSIONS_REF": "test-branch"})
7380

7481

7582
def test_extensions_ref_empty_string():
7683
"""Empty EXTENSIONS_REF should fall back to 'main'."""
77-
with mock.patch.dict(os.environ, {"EXTENSIONS_REF": ""}, clear=False):
78-
# Force reload of the module
79-
if "openhands.sdk.context.skills.skill" in sys.modules:
80-
del sys.modules["openhands.sdk.context.skills.skill"]
81-
82-
from openhands.sdk.context.skills.skill import PUBLIC_SKILLS_BRANCH
83-
84-
# Empty string should fall back to 'main' via os.environ.get default
85-
assert PUBLIC_SKILLS_BRANCH == "", (
86-
"Empty EXTENSIONS_REF should result in empty string "
87-
"(os.environ.get behavior)"
88-
)
84+
code = """
85+
from openhands.sdk.context.skills.skill import PUBLIC_SKILLS_BRANCH
86+
# Empty string returns empty string per os.environ.get behavior
87+
assert PUBLIC_SKILLS_BRANCH == "", (
88+
f"Expected '' but got '{PUBLIC_SKILLS_BRANCH}'"
89+
)
90+
"""
91+
_run_in_subprocess(code, {"EXTENSIONS_REF": ""})

0 commit comments

Comments
 (0)