File size: 6,427 Bytes
51ff9e5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
from unittest import mock

import pytest

from openhands.core.config import SandboxConfig,OpenHandsConfig
from openhands.events.action import CmdRunAction
from openhands.resolver.issue_resolver import IssueResolver


def assert_sandbox_config(
    config: SandboxConfig,
    base_container_image=SandboxConfig.model_fields['base_container_image'].default,
    runtime_container_image='ghcr.io/all-hands-ai/runtime:mock-nikolaik',  # Default to mock version
    local_runtime_url=SandboxConfig.model_fields['local_runtime_url'].default,
):
    """Helper function to assert the properties of the SandboxConfig object."""
    assert isinstance(config, SandboxConfig)
    assert config.base_container_image == base_container_image
    assert config.runtime_container_image == runtime_container_image
    assert config.enable_auto_lint is False
    assert config.use_host_network is False
    assert config.timeout == 300
    assert config.local_runtime_url == local_runtime_url


def test_setup_sandbox_config_default():
    """Test default configuration when no images provided and not experimental"""
    with mock.patch('openhands.__version__', 'mock'):
        openhands_config = OpenHandsConfig()

        IssueResolver.update_sandbox_config(
            openhands_config=openhands_config,
            base_container_image=None,
            runtime_container_image=None,
            is_experimental=False,
        )

        assert_sandbox_config(
            openhands_config.sandbox, runtime_container_image='ghcr.io/all-hands-ai/runtime:mock-nikolaik'
        )


def test_setup_sandbox_config_both_images():
    """Test that providing both container images raises ValueError"""
    with pytest.raises(
        ValueError, match='Cannot provide both runtime and base container images.'
    ):
        openhands_config = OpenHandsConfig()

        IssueResolver.update_sandbox_config(
            openhands_config=openhands_config,
            base_container_image='base-image',
            runtime_container_image='runtime-image',
            is_experimental=False,
        )


def test_setup_sandbox_config_base_only():
    """Test configuration when only base_container_image is provided"""
    base_image = 'custom-base-image'
    openhands_config = OpenHandsConfig()

    IssueResolver.update_sandbox_config(
        openhands_config=openhands_config,
        base_container_image=base_image,
        runtime_container_image=None,
        is_experimental=False,
    )

    assert_sandbox_config(
        openhands_config.sandbox, base_container_image=base_image, runtime_container_image=None
    )


def test_setup_sandbox_config_runtime_only():
    """Test configuration when only runtime_container_image is provided"""
    runtime_image = 'custom-runtime-image'
    openhands_config = OpenHandsConfig()

    IssueResolver.update_sandbox_config(
        openhands_config=openhands_config,
        base_container_image=None,
        runtime_container_image=runtime_image,
        is_experimental=False,
    )

    assert_sandbox_config(openhands_config.sandbox, runtime_container_image=runtime_image)


def test_setup_sandbox_config_experimental():
    """Test configuration when experimental mode is enabled"""
    with mock.patch('openhands.__version__', 'mock'):
        openhands_config = OpenHandsConfig()

        IssueResolver.update_sandbox_config(
            openhands_config=openhands_config,
            base_container_image=None,
            runtime_container_image=None,
            is_experimental=True,
        )

        assert_sandbox_config(openhands_config.sandbox, runtime_container_image=None)


@mock.patch('openhands.resolver.issue_resolver.os.getuid', return_value=0)
@mock.patch('openhands.resolver.issue_resolver.get_unique_uid', return_value=1001)
def test_setup_sandbox_config_gitlab_ci(mock_get_unique_uid, mock_getuid):
    """Test GitLab CI specific configuration when running as root"""
    with mock.patch('openhands.__version__', 'mock'):
        with mock.patch.object(IssueResolver, 'GITLAB_CI', True):
            openhands_config = OpenHandsConfig()

            IssueResolver.update_sandbox_config(
                openhands_config=openhands_config,
                base_container_image=None,
                runtime_container_image=None,
                is_experimental=False,
            )

            assert_sandbox_config(openhands_config.sandbox, local_runtime_url='http://localhost')


@mock.patch('openhands.resolver.issue_resolver.os.getuid', return_value=1000)
def test_setup_sandbox_config_gitlab_ci_non_root(mock_getuid):
    """Test GitLab CI configuration when not running as root"""
    with mock.patch('openhands.__version__', 'mock'):
        with mock.patch.object(IssueResolver, 'GITLAB_CI', True):
            openhands_config = OpenHandsConfig()

            IssueResolver.update_sandbox_config(
                openhands_config=openhands_config,
                base_container_image=None,
                runtime_container_image=None,
                is_experimental=False,
            )

            assert_sandbox_config(openhands_config.sandbox, local_runtime_url='http://localhost')


@mock.patch('openhands.events.observation.CmdOutputObservation')
@mock.patch('openhands.runtime.base.Runtime')
def test_initialize_runtime_runs_setup_script_and_git_hooks(
    mock_runtime, mock_cmd_output
):
    """Test that initialize_runtime calls maybe_run_setup_script and maybe_setup_git_hooks"""

    # Create a minimal resolver instance with just the methods we need
    class MinimalResolver:
        def initialize_runtime(self, runtime):
            # This is the method we're testing
            action = CmdRunAction(command='git config --global core.pager ""')
            runtime.run_action(action)

            # Run setup script if it exists
            runtime.maybe_run_setup_script()

            # Setup git hooks if they exist
            runtime.maybe_setup_git_hooks()

    resolver = MinimalResolver()

    # Mock the runtime's run_action method to return a successful CmdOutputObservation
    mock_cmd_output.return_value.exit_code = 0
    mock_runtime.run_action.return_value = mock_cmd_output.return_value

    # Call the method
    resolver.initialize_runtime(mock_runtime)

    # Verify that both methods were called
    mock_runtime.maybe_run_setup_script.assert_called_once()
    mock_runtime.maybe_setup_git_hooks.assert_called_once()