|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
from pydantic import SecretStr
|
|
|
|
from openhands.core.config.sandbox_config import SandboxConfig
|
|
from openhands.server.app import app
|
|
from openhands.server.settings import Settings
|
|
|
|
|
|
@pytest.fixture
|
|
def test_client():
|
|
|
|
class MockMiddleware:
|
|
def __init__(self, app):
|
|
self.app = app
|
|
|
|
async def __call__(self, scope, receive, send):
|
|
if scope['type'] == 'http':
|
|
scope['state'] = {'github_token': 'test-token'}
|
|
await self.app(scope, receive, send)
|
|
|
|
|
|
app.middleware_stack = None
|
|
app.add_middleware(MockMiddleware)
|
|
|
|
return TestClient(app)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_settings_store():
|
|
with patch('openhands.server.routes.settings.SettingsStoreImpl') as mock:
|
|
store_instance = MagicMock()
|
|
mock.get_instance = AsyncMock(return_value=store_instance)
|
|
store_instance.load = AsyncMock()
|
|
store_instance.store = AsyncMock()
|
|
yield store_instance
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_settings_api_runtime_factor(test_client, mock_settings_store):
|
|
|
|
mock_settings_store.load.return_value = None
|
|
|
|
|
|
settings_data = {
|
|
'language': 'en',
|
|
'agent': 'test-agent',
|
|
'max_iterations': 100,
|
|
'security_analyzer': 'default',
|
|
'confirmation_mode': True,
|
|
'llm_model': 'test-model',
|
|
'llm_api_key': 'test-key',
|
|
'llm_base_url': 'https://test.com',
|
|
'remote_runtime_resource_factor': 2,
|
|
}
|
|
|
|
|
|
|
|
|
|
response = test_client.post('/api/settings', json=settings_data)
|
|
assert response.status_code == 200
|
|
|
|
|
|
stored_settings = mock_settings_store.store.call_args[0][0]
|
|
assert stored_settings.remote_runtime_resource_factor == 2
|
|
|
|
|
|
mock_settings_store.load.return_value = Settings(**settings_data)
|
|
|
|
|
|
response = test_client.get('/api/settings')
|
|
assert response.status_code == 200
|
|
assert response.json()['remote_runtime_resource_factor'] == 2
|
|
|
|
|
|
with patch('openhands.server.shared.config') as mock_config:
|
|
mock_config.sandbox = SandboxConfig()
|
|
response = test_client.get('/api/settings')
|
|
assert response.status_code == 200
|
|
|
|
|
|
mock_settings_store.store.assert_called()
|
|
stored_settings = mock_settings_store.store.call_args[0][0]
|
|
assert stored_settings.remote_runtime_resource_factor == 2
|
|
|
|
assert isinstance(stored_settings.llm_api_key, SecretStr)
|
|
assert stored_settings.llm_api_key.get_secret_value() == 'test-key'
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_settings_llm_api_key(test_client, mock_settings_store):
|
|
|
|
mock_settings_store.load.return_value = None
|
|
|
|
|
|
settings_data = {'llm_api_key': 'test-key'}
|
|
|
|
|
|
|
|
|
|
response = test_client.post('/api/settings', json=settings_data)
|
|
assert response.status_code == 200
|
|
|
|
|
|
stored_settings = mock_settings_store.store.call_args[0][0]
|
|
assert isinstance(stored_settings.llm_api_key, SecretStr)
|
|
assert stored_settings.llm_api_key.get_secret_value() == 'test-key'
|
|
|
|
|
|
mock_settings_store.load.return_value = Settings(**settings_data)
|
|
|
|
|
|
response = test_client.get('/api/settings')
|
|
assert response.status_code == 200
|
|
|
|
|
|
assert 'test-key' not in response.json()
|
|
|