Files
wehub-resource-sync 0418dc5cf9
CI / Python tests (3.10) (push) Has been cancelled
CI / Python tests (3.11) (push) Has been cancelled
CI / Python quality (push) Has been cancelled
CI / Frontend typecheck (push) Has been cancelled
Autopilot Pages / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:45:00 +08:00

283 lines
9.7 KiB
Python

"""Tests for subprocess teammate spawning."""
from __future__ import annotations
from pathlib import Path
import sys
import pytest
from openharness.tasks.manager import BackgroundTaskManager
from openharness.tasks.types import TaskRecord
from openharness.swarm.subprocess_backend import SubprocessBackend
from openharness.swarm.types import TeammateSpawnConfig
def _argv_str(captured: dict) -> str:
"""Join captured argv into a string for substring assertions."""
argv = captured.get("argv") or []
return " ".join(argv)
@pytest.mark.asyncio
async def test_subprocess_backend_forwards_system_prompt_in_command(monkeypatch, tmp_path: Path):
captured: dict[str, object] = {}
async def _fake_create_agent_task(self, **kwargs):
captured.update(kwargs)
return TaskRecord(
id="task_123",
type="local_agent",
status="running",
description=str(kwargs["description"]),
cwd=str(kwargs["cwd"]),
output_file=tmp_path / "task_123.log",
argv=list(kwargs.get("argv") or []),
)
monkeypatch.setattr(BackgroundTaskManager, "create_agent_task", _fake_create_agent_task)
monkeypatch.setattr("openharness.swarm.subprocess_backend.get_teammate_command", lambda: "/usr/bin/python3")
backend = SubprocessBackend()
config = TeammateSpawnConfig(
name="reviewer",
team="default",
prompt="Review the code changes.",
cwd=str(tmp_path),
parent_session_id="sess-001",
system_prompt="You are a careful code reviewer.",
task_type="local_agent",
)
result = await backend.spawn(config)
assert result.success is True
argv_str = _argv_str(captured)
assert "--system-prompt" in argv_str
assert "You are a careful code reviewer." in argv_str
@pytest.mark.asyncio
async def test_subprocess_backend_forwards_append_system_prompt_mode(monkeypatch, tmp_path: Path):
captured: dict[str, object] = {}
async def _fake_create_agent_task(self, **kwargs):
captured.update(kwargs)
return TaskRecord(
id="task_234",
type="local_agent",
status="running",
description=str(kwargs["description"]),
cwd=str(kwargs["cwd"]),
output_file=tmp_path / "task_234.log",
argv=list(kwargs.get("argv") or []),
)
monkeypatch.setattr(BackgroundTaskManager, "create_agent_task", _fake_create_agent_task)
monkeypatch.setattr("openharness.swarm.subprocess_backend.get_teammate_command", lambda: "/usr/bin/python3")
backend = SubprocessBackend()
config = TeammateSpawnConfig(
name="reviewer",
team="default",
prompt="Review the code changes.",
cwd=str(tmp_path),
parent_session_id="sess-001",
system_prompt="Project-specific addendum.",
system_prompt_mode="append",
task_type="local_agent",
)
result = await backend.spawn(config)
assert result.success is True
argv_str = _argv_str(captured)
assert "--append-system-prompt" in argv_str
assert "Project-specific addendum." in argv_str
@pytest.mark.asyncio
async def test_subprocess_backend_passes_argv_list_not_shell_command(
monkeypatch, tmp_path: Path
):
"""Regression test for #230: teammate spawn must use the direct-exec
``argv`` path, not the shell-evaluated ``command`` path. Shell routing
via Git Bash on Windows cannot reliably exec Windows-pathed binaries
(e.g. ``C:\\Users\\...\\python.exe``) when bash is itself launched via
``asyncio.create_subprocess_exec`` — the same call that works
interactively returns ``command not found``. Bypassing the shell
sidesteps the entire class of cross-platform quoting bug."""
captured: dict[str, object] = {}
async def _fake_create_agent_task(self, **kwargs):
captured.update(kwargs)
return TaskRecord(
id="task_argv",
type="local_agent",
status="running",
description=str(kwargs["description"]),
cwd=str(kwargs["cwd"]),
output_file=tmp_path / "task_argv.log",
argv=list(kwargs.get("argv") or []),
)
monkeypatch.setattr(BackgroundTaskManager, "create_agent_task", _fake_create_agent_task)
backend = SubprocessBackend()
config = TeammateSpawnConfig(
name="worker",
team="default",
prompt="hi",
cwd=str(tmp_path),
parent_session_id="sess-001",
task_type="local_agent",
)
result = await backend.spawn(config)
assert result.success is True
# Spawn must hand off as argv, not command.
assert captured.get("command") is None
argv = captured.get("argv")
assert isinstance(argv, list) and argv, "expected non-empty argv list"
# First element is the exact Python interpreter selected for teammate spawn.
assert argv[0] == sys.executable
# Tail must include the worker invocation.
assert "--task-worker" in argv
@pytest.mark.asyncio
async def test_subprocess_backend_argv_preserves_windows_backslashes(
monkeypatch, tmp_path: Path
):
"""When ``get_teammate_command()`` returns a Windows path with
backslashes, those backslashes must arrive at the manager intact.
Using a list (``argv``) instead of a shell-evaluated string is what
makes that guarantee — there is no shell escape parser between us
and ``asyncio.create_subprocess_exec``."""
captured: dict[str, object] = {}
async def _fake_create_agent_task(self, **kwargs):
captured.update(kwargs)
return TaskRecord(
id="task_winargv",
type="local_agent",
status="running",
description=str(kwargs["description"]),
cwd=str(kwargs["cwd"]),
output_file=tmp_path / "task_winargv.log",
argv=list(kwargs.get("argv") or []),
)
win_path = r"C:\Users\simu\AppData\Roaming\uv\tools\openharness-ai\Scripts\python.exe"
monkeypatch.setattr(BackgroundTaskManager, "create_agent_task", _fake_create_agent_task)
# Use the public override env var rather than monkeypatching the
# function symbol — proved fragile under full-suite pytest module
# attribute resolution.
monkeypatch.setenv("OPENHARNESS_TEAMMATE_COMMAND", win_path)
backend = SubprocessBackend()
config = TeammateSpawnConfig(
name="worker",
team="default",
prompt="hi",
cwd=str(tmp_path),
parent_session_id="sess-001",
task_type="local_agent",
)
result = await backend.spawn(config)
assert result.success is True
argv = captured.get("argv") or []
assert argv[0] == win_path, f"backslashed path mangled: {argv[0]!r}"
@pytest.mark.asyncio
async def test_subprocess_backend_passes_env_via_kwarg(
monkeypatch, tmp_path: Path
):
"""Inherited teammate env vars must flow through ``env=`` on
``create_agent_task`` and never be embedded in argv as ``KEY=val``
tokens (which the OS would just pass to the child as plain args)."""
captured: dict[str, object] = {}
async def _fake_create_agent_task(self, **kwargs):
captured.update(kwargs)
return TaskRecord(
id="task_env",
type="local_agent",
status="running",
description=str(kwargs["description"]),
cwd=str(kwargs["cwd"]),
output_file=tmp_path / "task_env.log",
argv=list(kwargs.get("argv") or []),
)
monkeypatch.setattr(BackgroundTaskManager, "create_agent_task", _fake_create_agent_task)
monkeypatch.setattr(
"openharness.swarm.subprocess_backend.get_teammate_command",
lambda: "/usr/bin/python3",
)
backend = SubprocessBackend()
config = TeammateSpawnConfig(
name="worker",
team="default",
prompt="hi",
cwd=str(tmp_path),
parent_session_id="sess-001",
task_type="local_agent",
)
result = await backend.spawn(config)
assert result.success is True
argv = captured.get("argv") or []
# No element of argv should look like a shell env-prefix token.
for token in argv:
assert "OPENHARNESS_AGENT_TEAMS=" not in token
assert "CLAUDE_CODE_COORDINATOR_MODE=" not in token
env = captured.get("env")
assert isinstance(env, dict)
assert env.get("OPENHARNESS_AGENT_TEAMS") == "1"
@pytest.mark.asyncio
async def test_subprocess_backend_does_not_inject_env_when_command_overridden(
monkeypatch, tmp_path: Path
):
"""When the caller supplies ``config.command``, we do not silently
inject inherited env vars and we use the shell-evaluated path (not
the argv path). This preserves caller intent and pre-fix behaviour
for code that already builds its own shell-quoted command string."""
captured: dict[str, object] = {}
async def _fake_create_agent_task(self, **kwargs):
captured.update(kwargs)
return TaskRecord(
id="task_override",
type="local_agent",
status="running",
description=str(kwargs["description"]),
cwd=str(kwargs["cwd"]),
output_file=tmp_path / "task_override.log",
command=str(kwargs.get("command") or ""),
)
monkeypatch.setattr(BackgroundTaskManager, "create_agent_task", _fake_create_agent_task)
backend = SubprocessBackend()
config = TeammateSpawnConfig(
name="worker",
team="default",
prompt="hi",
cwd=str(tmp_path),
parent_session_id="sess-001",
command="custom-script --do-it",
task_type="local_agent",
)
result = await backend.spawn(config)
assert result.success is True
assert captured["command"] == "custom-script --do-it"
assert captured.get("argv") is None
assert captured.get("env") is None