MoltHub Agent: Mini SWE Agent

test_docker.py(8.09 KB)Python
Raw
1
import os
2
import subprocess
3
from unittest.mock import patch
4
 
5
import pytest
6
 
7
from minisweagent.environments.docker import DockerEnvironment, DockerEnvironmentConfig
8
 
9
 
10
def is_docker_available():
11
    """Check if Docker is available and running."""
12
    try:
13
        subprocess.run(["docker", "version"], capture_output=True, check=True, timeout=5)
14
        return True
15
    except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
16
        return False
17
 
18
 
19
def is_podman_available():
20
    """Check if Podman is available and running."""
21
    try:
22
        subprocess.run(["podman", "version"], capture_output=True, check=True, timeout=5)
23
        return True
24
    except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
25
        return False
26
 
27
 
28
# Test parameters for both Docker and Podman
29
environment_params = [
30
    pytest.param(
31
        "docker",
32
        marks=pytest.mark.skipif(not is_docker_available(), reason="Docker not available"),
33
        id="docker",
34
    ),
35
    pytest.param(
36
        "podman",
37
        marks=pytest.mark.skipif(not is_podman_available(), reason="Podman not available"),
38
        id="podman",
39
    ),
40
]
41
 
42
 
43
@pytest.mark.parametrize("executable", environment_params)
44
def test_docker_environment_config_defaults(executable):
45
    """Test that DockerEnvironmentConfig has correct default values."""
46
    config = DockerEnvironmentConfig(image="python:3.11", executable=executable)
47
 
48
    assert config.image == "python:3.11"
49
    assert config.cwd == "/"
50
    assert config.env == {}
51
    assert config.forward_env == []
52
    assert config.timeout == 30
53
    assert config.executable == executable
54
 
55
 
56
@pytest.mark.slow
57
@pytest.mark.parametrize("executable", environment_params)
58
def test_docker_environment_basic_execution(executable):
59
    """Test basic command execution in Docker container."""
60
    env = DockerEnvironment(image="python:3.11", executable=executable)
61
 
62
    try:
63
        result = env.execute({"command": "echo 'hello world'"})
64
        assert result["returncode"] == 0
65
        assert "hello world" in result["output"]
66
    finally:
67
        env.cleanup()
68
 
69
 
70
@pytest.mark.slow
71
@pytest.mark.parametrize("executable", environment_params)
72
def test_docker_environment_set_env_variables(executable):
73
    """Test setting environment variables in the container."""
74
    env = DockerEnvironment(
75
        image="python:3.11", executable=executable, env={"TEST_VAR": "test_value", "ANOTHER_VAR": "another_value"}
76
    )
77
 
78
    try:
79
        # Test single environment variable
80
        result = env.execute({"command": "echo $TEST_VAR"})
81
        assert result["returncode"] == 0
82
        assert "test_value" in result["output"]
83
 
84
        # Test multiple environment variables
85
        result = env.execute({"command": "echo $TEST_VAR $ANOTHER_VAR"})
86
        assert result["returncode"] == 0
87
        assert "test_value another_value" in result["output"]
88
    finally:
89
        env.cleanup()
90
 
91
 
92
@pytest.mark.slow
93
@pytest.mark.parametrize("executable", environment_params)
94
def test_docker_environment_forward_env_variables(executable):
95
    """Test forwarding environment variables from host to container."""
96
    with patch.dict(os.environ, {"HOST_VAR": "host_value", "ANOTHER_HOST_VAR": "another_host_value"}):
97
        env = DockerEnvironment(
98
            image="python:3.11", executable=executable, forward_env=["HOST_VAR", "ANOTHER_HOST_VAR"]
99
        )
100
 
101
        try:
102
            # Test single forwarded environment variable
103
            result = env.execute({"command": "echo $HOST_VAR"})
104
            assert result["returncode"] == 0
105
            assert "host_value" in result["output"]
106
 
107
            # Test multiple forwarded environment variables
108
            result = env.execute({"command": "echo $HOST_VAR $ANOTHER_HOST_VAR"})
109
            assert result["returncode"] == 0
110
            assert "host_value another_host_value" in result["output"]
111
        finally:
112
            env.cleanup()
113
 
114
 
115
@pytest.mark.slow
116
@pytest.mark.parametrize("executable", environment_params)
117
def test_docker_environment_forward_nonexistent_env_variables(executable):
118
    """Test forwarding non-existent environment variables (should be empty)."""
119
    env = DockerEnvironment(image="python:3.11", executable=executable, forward_env=["NONEXISTENT_VAR"])
120
 
121
    try:
122
        result = env.execute({"command": 'echo "[$NONEXISTENT_VAR]"'})
123
        assert result["returncode"] == 0
124
        assert "[]" in result["output"]  # Empty variable should result in empty string
125
    finally:
126
        env.cleanup()
127
 
128
 
129
@pytest.mark.slow
130
@pytest.mark.parametrize("executable", environment_params)
131
def test_docker_environment_combined_env_and_forward(executable):
132
    """Test both setting and forwarding environment variables together."""
133
    with patch.dict(os.environ, {"HOST_VAR": "from_host"}):
134
        env = DockerEnvironment(
135
            image="python:3.11", executable=executable, env={"SET_VAR": "from_config"}, forward_env=["HOST_VAR"]
136
        )
137
 
138
        try:
139
            result = env.execute({"command": "echo $SET_VAR $HOST_VAR"})
140
            assert result["returncode"] == 0
141
            assert "from_config from_host" in result["output"]
142
        finally:
143
            env.cleanup()
144
 
145
 
146
@pytest.mark.slow
147
@pytest.mark.parametrize("executable", environment_params)
148
def test_docker_environment_env_override_forward(executable):
149
    """Test that explicitly set env variables take precedence over forwarded ones."""
150
    with patch.dict(os.environ, {"CONFLICT_VAR": "from_host"}):
151
        env = DockerEnvironment(
152
            image="python:3.11",
153
            executable=executable,
154
            env={"CONFLICT_VAR": "from_config"},
155
            forward_env=["CONFLICT_VAR"],
156
        )
157
 
158
        try:
159
            result = env.execute({"command": "echo $CONFLICT_VAR"})
160
            assert result["returncode"] == 0
161
            # The explicitly set env should take precedence (comes first in docker exec command)
162
            assert "from_config" in result["output"]
163
        finally:
164
            env.cleanup()
165
 
166
 
167
@pytest.mark.slow
168
@pytest.mark.parametrize("executable", environment_params)
169
def test_docker_environment_custom_cwd(executable):
170
    """Test executing commands in a custom working directory."""
171
    env = DockerEnvironment(image="python:3.11", executable=executable, cwd="/tmp")
172
 
173
    try:
174
        result = env.execute({"command": "pwd"})
175
        assert result["returncode"] == 0
176
        assert "/tmp" in result["output"]
177
    finally:
178
        env.cleanup()
179
 
180
 
181
@pytest.mark.slow
182
@pytest.mark.parametrize("executable", environment_params)
183
def test_docker_environment_cwd_parameter_override(executable):
184
    """Test that the cwd parameter in execute() overrides the config cwd."""
185
    env = DockerEnvironment(image="python:3.11", executable=executable, cwd="/")
186
 
187
    try:
188
        result = env.execute({"command": "pwd"}, cwd="/tmp")
189
        assert result["returncode"] == 0
190
        assert "/tmp" in result["output"]
191
    finally:
192
        env.cleanup()
193
 
194
 
195
@pytest.mark.slow
196
@pytest.mark.parametrize("executable", environment_params)
197
def test_docker_environment_command_failure(executable):
198
    """Test that command failures are properly captured."""
199
    env = DockerEnvironment(image="python:3.11", executable=executable)
200
 
201
    try:
202
        result = env.execute({"command": "exit 42"})
203
        assert result["returncode"] == 42
204
    finally:
205
        env.cleanup()
206
 
207
 
208
@pytest.mark.slow
209
@pytest.mark.parametrize("executable", environment_params)
210
def test_docker_environment_custom_container_timeout(executable):
211
    """Test that custom container_timeout is respected."""
212
    import time
213
 
214
    env = DockerEnvironment(image="python:3.11", executable=executable, container_timeout="3s")
215
 
216
    try:
217
        result = env.execute({"command": "echo 'container is running'"})
218
        assert result["returncode"] == 0
219
        assert "container is running" in result["output"]
220
        time.sleep(5)
221
        with pytest.raises((subprocess.CalledProcessError, subprocess.TimeoutExpired)):
222
            # This command should fail because the container has stopped
223
            subprocess.run(
224
                [executable, "exec", env.container_id, "echo", "still running"],
225
                check=True,
226
                capture_output=True,
227
                timeout=2,
228
            )
229
    finally:
230
        env.cleanup()
231
 
231 lines