Files
2026-07-13 12:24:33 +08:00

140 lines
4.9 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# Standard
from pathlib import Path
from unittest.mock import MagicMock
import json
# Third Party
from fastapi.testclient import TestClient
import pytest
# First Party
from lmcache.v1.config import LMCacheEngineConfig
from lmcache.v1.internal_api_server.api_server import app
def _get_test_scripts_static():
"""Static function to get test scripts for parametrize."""
script_dir = Path(__file__).parent / "test_scripts"
scripts = []
for script_file in sorted(script_dir.glob("*.py")):
content = script_file.read_text()
# Find TEST_METADATA line
lines = content.split("\n")
metadata_lines = []
in_metadata = False
brace_count = 0
for line in lines:
if line.strip().startswith("# TEST_METADATA: {"):
in_metadata = True
# Extract JSON part after ': '
json_part = line.split("# TEST_METADATA: ", 1)[1]
metadata_lines.append(json_part)
brace_count = json_part.count("{") - json_part.count("}")
elif in_metadata and line.strip().startswith("#"):
# Continuation line in comment
json_part = line.strip()[1:].strip() # Remove '#' and trim
metadata_lines.append(json_part)
brace_count += json_part.count("{") - json_part.count("}")
# Check if we've closed all braces
if brace_count == 0 and "}" in json_part:
in_metadata = False
elif in_metadata:
# Not a comment line, metadata block ended
in_metadata = False
if metadata_lines:
json_str = " ".join(metadata_lines)
metadata = json.loads(json_str)
scripts.append((script_file.name, metadata))
return scripts
class TestRunScriptAPI:
"""Test suite for the /run_script API endpoint."""
@pytest.fixture
def mock_lmcache_adapter_with_config(self):
"""Create a mock LMCacheConnectorV1Impl adapter with config."""
adapter = MagicMock()
config = LMCacheEngineConfig.from_defaults()
adapter.config = config
return adapter
@pytest.fixture
def client_with_adapter(self, mock_lmcache_adapter_with_config):
"""Create a test client with mocked adapter."""
app.state.lmcache_adapter = mock_lmcache_adapter_with_config
app.state.lmcache_engine = mock_lmcache_adapter_with_config.lmcache_engine
client = TestClient(app)
yield client
client.close()
@pytest.fixture
def script_dir(self):
"""Get the test scripts directory."""
return Path(__file__).parent / "test_scripts"
@pytest.mark.parametrize(
"script_name,metadata",
[
pytest.param(name, meta, id=name.replace(".py", ""))
for name, meta in _get_test_scripts_static()
],
)
def test_run_script_from_file(
self,
client_with_adapter,
script_dir,
script_name,
metadata,
):
"""Test script execution by directly uploading script files."""
script_path = script_dir / script_name
# Configure allowed imports
allowed_imports = metadata.get("allowed_imports", [])
client_with_adapter.app.state.lmcache_adapter.config.script_allowed_imports = (
allowed_imports
)
# Upload the script file directly
with open(script_path, "rb") as f:
files = {"script": (script_name, f, "text/plain")}
response = client_with_adapter.post("/run_script", files=files)
# Verify status code
expected_status = metadata.get("expected_status", 200)
assert response.status_code == expected_status, (
f"Expected status {expected_status}, got {response.status_code}"
)
# Verify response content
if "expected_result" in metadata:
assert response.text == metadata["expected_result"], (
f"Expected '{metadata['expected_result']}', got '{response.text}'"
)
if "expected_contains" in metadata:
contains_list = metadata["expected_contains"]
if isinstance(contains_list, str):
contains_list = [contains_list]
for expected_text in contains_list:
assert expected_text in response.text, (
f"Expected '{expected_text}' in response"
)
if "expected_min_length" in metadata:
assert len(response.text) >= metadata["expected_min_length"], (
f"Response too short: {len(response.text)}"
)
def test_run_script_no_file(self, client_with_adapter):
"""Test run_script with no file provided."""
response = client_with_adapter.post("/run_script")
assert response.status_code == 400
assert "No script file provided" in response.text