Files
2026-07-13 13:22:34 +08:00

231 lines
6.9 KiB
Python

from pathlib import Path
import pytest
from tests.gateway.tools import Gateway, save_yaml
BASE_ROUTE = "/api/2.0/endpoints/"
@pytest.fixture
def basic_config_dict():
return {
"endpoints": [
{
"name": "completions-gpt4",
"endpoint_type": "llm/v1/completions",
"model": {
"name": "gpt-4",
"provider": "openai",
"config": {
"openai_api_key": "mykey",
"openai_api_base": "https://api.openai.com/v1",
"openai_api_version": "2023-05-15",
"openai_api_type": "openai",
},
},
},
{
"name": "embeddings-gpt4",
"endpoint_type": "llm/v1/embeddings",
"model": {
"name": "gpt-4",
"provider": "openai",
"config": {
"openai_api_key": "mykey",
"openai_api_base": "https://api.openai.com/v1",
"openai_api_version": "2023-05-15",
"openai_api_type": "openai",
},
},
},
]
}
@pytest.fixture
def basic_routes():
return [
{
"name": "completions-gpt4",
"endpoint_type": "llm/v1/completions",
"endpoint_url": "/gateway/completions-gpt4/invocations",
"model": {
"name": "gpt-4",
"provider": "openai",
},
"limit": None,
},
{
"name": "embeddings-gpt4",
"endpoint_type": "llm/v1/embeddings",
"endpoint_url": "/gateway/embeddings-gpt4/invocations",
"model": {
"name": "gpt-4",
"provider": "openai",
},
"limit": None,
},
]
@pytest.fixture
def update_config_dict():
return {
"endpoints": [
{
"name": "chat-gpt4",
"endpoint_type": "llm/v1/chat",
"model": {
"name": "gpt-4",
"provider": "openai",
"config": {
"openai_api_key": "mykey",
"openai_api_base": "https://api.openai.com/v1",
"openai_api_version": "2023-05-15",
"openai_api_type": "openai",
},
},
"limit": None,
},
]
}
@pytest.fixture
def update_routes():
return [
{
"name": "chat-gpt4",
"endpoint_type": "llm/v1/chat",
"endpoint_url": "/gateway/chat-gpt4/invocations",
"model": {
"name": "gpt-4",
"provider": "openai",
},
"limit": None,
},
]
@pytest.fixture
def invalid_config_dict():
return {
"endpoints": [
{
"invalid_name": "invalid",
"endpoint_type": "llm/v1/chat",
"model": {"invalidkey": "invalid", "invalid_provider": "invalid"},
}
]
}
def test_server_update(
tmp_path: Path, basic_config_dict, update_config_dict, basic_routes, update_routes
):
config = tmp_path / "config.yaml"
save_yaml(config, basic_config_dict)
with Gateway(config) as gateway:
response = gateway.get(BASE_ROUTE)
assert response.json()["endpoints"] == basic_routes
# push an update to the config file
save_yaml(config, update_config_dict)
# Ensure there is no server downtime
gateway.assert_health()
# Wait for the app to restart
gateway.wait_reload()
response = gateway.get(BASE_ROUTE)
assert response.json()["endpoints"] == update_routes
# push the original file back
save_yaml(config, basic_config_dict)
gateway.assert_health()
gateway.wait_reload()
response = gateway.get(BASE_ROUTE)
assert response.json()["endpoints"] == basic_routes
def test_server_update_with_invalid_config(
tmp_path: Path, basic_config_dict, invalid_config_dict, basic_routes
):
config = tmp_path / "config.yaml"
save_yaml(config, basic_config_dict)
with Gateway(config) as gateway:
response = gateway.get(BASE_ROUTE)
assert response.json()["endpoints"] == basic_routes
# Give filewatch a moment to cycle
gateway.wait_reload()
# push an invalid config
save_yaml(config, invalid_config_dict)
gateway.assert_health()
# ensure that filewatch has run through the aborted config change logic
gateway.wait_reload()
gateway.assert_health()
response = gateway.get(BASE_ROUTE)
assert response.json()["endpoints"] == basic_routes
def test_server_update_config_removed_then_recreated(
tmp_path: Path, basic_config_dict, basic_routes
):
config = tmp_path / "config.yaml"
save_yaml(config, basic_config_dict)
with Gateway(config) as gateway:
response = gateway.get(BASE_ROUTE)
assert response.json()["endpoints"] == basic_routes
# Give filewatch a moment to cycle
gateway.wait_reload()
# remove config
config.unlink()
gateway.wait_reload()
gateway.assert_health()
save_yaml(config, {"endpoints": basic_config_dict["endpoints"][1:]})
gateway.wait_reload()
response = gateway.get(BASE_ROUTE)
assert response.json()["endpoints"] == basic_routes[1:]
def test_server_static_endpoints(tmp_path, basic_config_dict, basic_routes):
config = tmp_path / "config.yaml"
save_yaml(config, basic_config_dict)
with Gateway(config) as gateway:
response = gateway.get(BASE_ROUTE)
assert response.json()["endpoints"] == basic_routes
for route in ["docs", "redoc"]:
response = gateway.get(route)
assert response.status_code == 200
for index, route in enumerate(basic_config_dict["endpoints"]):
response = gateway.get(f"{BASE_ROUTE}{route['name']}")
assert response.json() == basic_routes[index]
def test_request_invalid_route(tmp_path, basic_config_dict):
config = tmp_path / "config.yaml"
save_yaml(config, basic_config_dict)
with Gateway(config) as gateway:
# Test get
response = gateway.get(f"{BASE_ROUTE}invalid/")
assert response.status_code == 404
assert response.json() == {
"detail": "The endpoint 'invalid' is not present or active on the server. Please "
"verify the endpoint name."
}
# Test post
response = gateway.post(f"{BASE_ROUTE}invalid", json={"input": "should fail"})
assert response.status_code == 405
assert response.json() == {"detail": "Method Not Allowed"}