deer-flow/backend/tests/test_docker_sandbox_mode_detection.py
天魔 276481371e
fix(docker): set DEER_FLOW_ROOT for log commands (#4658)
* fix(docker): set DEER_FLOW_ROOT for log commands

* fix(docker): set root before restart

* docs(docker): clarify log command
2026-08-04 22:45:10 +08:00

123 lines
3.7 KiB
Python

"""Regression tests for docker sandbox mode detection logic."""
from __future__ import annotations
import subprocess
import tempfile
from pathlib import Path
from shutil import which
import pytest
REPO_ROOT = Path(__file__).resolve().parents[2]
SCRIPT_PATH = REPO_ROOT / "scripts" / "docker.sh"
BASH_CANDIDATES = [
Path(r"C:\Program Files\Git\bin\bash.exe"),
Path(which("bash")) if which("bash") else None,
]
BASH_EXECUTABLE = next(
(str(path) for path in BASH_CANDIDATES if path is not None and path.exists() and "WindowsApps" not in str(path)),
None,
)
if BASH_EXECUTABLE is None:
pytestmark = pytest.mark.skip(reason="bash is required for docker.sh detection tests")
def _detect_mode_with_config(config_content: str) -> str:
"""Write config content into a temp project root and execute detect_sandbox_mode."""
with tempfile.TemporaryDirectory() as tmpdir:
tmp_root = Path(tmpdir)
(tmp_root / "config.yaml").write_text(config_content, encoding="utf-8")
command = f"source '{SCRIPT_PATH}' && PROJECT_ROOT='{tmp_root}' && detect_sandbox_mode"
output = subprocess.check_output(
[BASH_EXECUTABLE, "-lc", command],
text=True,
encoding="utf-8",
).strip()
return output
def test_detect_mode_defaults_to_local_when_config_missing():
"""No config file should default to local mode."""
with tempfile.TemporaryDirectory() as tmpdir:
command = f"source '{SCRIPT_PATH}' && PROJECT_ROOT='{tmpdir}' && detect_sandbox_mode"
output = subprocess.check_output(
[BASH_EXECUTABLE, "-lc", command],
text=True,
encoding="utf-8",
).strip()
assert output == "local"
def test_detect_mode_local_provider():
"""Local sandbox provider should map to local mode."""
config = """
sandbox:
use: deerflow.sandbox.local:LocalSandboxProvider
""".strip()
assert _detect_mode_with_config(config) == "local"
def test_detect_mode_aio_without_provisioner_url():
"""AIO sandbox without provisioner_url should map to aio mode."""
config = """
sandbox:
use: deerflow.community.aio_sandbox:AioSandboxProvider
""".strip()
assert _detect_mode_with_config(config) == "aio"
def test_detect_mode_provisioner_with_url():
"""AIO sandbox with provisioner_url should map to provisioner mode."""
config = """
sandbox:
use: deerflow.community.aio_sandbox:AioSandboxProvider
provisioner_url: http://provisioner:8002
""".strip()
assert _detect_mode_with_config(config) == "provisioner"
def test_detect_mode_ignores_commented_provisioner_url():
"""Commented provisioner_url should not activate provisioner mode."""
config = """
sandbox:
use: deerflow.community.aio_sandbox:AioSandboxProvider
# provisioner_url: http://provisioner:8002
""".strip()
assert _detect_mode_with_config(config) == "aio"
def test_detect_mode_unknown_provider_falls_back_to_local():
"""Unknown sandbox provider should default to local mode."""
config = """
sandbox:
use: custom.module:UnknownProvider
""".strip()
assert _detect_mode_with_config(config) == "local"
@pytest.mark.parametrize("docker_command", ["logs --gateway", "restart"])
def test_compose_commands_set_deer_flow_root_before_compose(docker_command):
"""Log and restart commands should resolve mounts from the repository root."""
with tempfile.TemporaryDirectory() as tmpdir:
command = f"""
source '{SCRIPT_PATH}'
PROJECT_ROOT='{tmpdir}'
DOCKER_DIR='{tmpdir}'
COMPOSE_CMD=capture_compose
capture_compose() {{ test "${{DEER_FLOW_ROOT:-}}" = "$PROJECT_ROOT"; }}
unset DEER_FLOW_ROOT
{docker_command}
"""
subprocess.check_call([BASH_EXECUTABLE, "-lc", command])