test: replace test_add_and_delete_models integration test with mock

Skip the integration test that requires a live proxy and OPENAI_API_KEY
(removed from CI/CD). Add deterministic mock test covering the same
add → delete → double-delete-fails lifecycle.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yuneng Jiang 2026-03-30 21:30:57 -07:00
parent cbd6253f9c
commit b48c037712
No known key found for this signature in database
2 changed files with 101 additions and 0 deletions

View File

@ -14,6 +14,7 @@ sys.path.insert(
) # Adds the parent directory to the system path
from litellm.proxy._types import (
LiteLLM_ModelTable,
LiteLLM_ProxyModelTable,
LiteLLM_TeamTable,
LitellmUserRoles,
Member,
@ -1176,3 +1177,100 @@ class TestModelInfoEndpoint:
assert result["id"] == "team-model-1"
assert result["object"] == "model"
assert result["owned_by"] == "custom"
class TestAddAndDeleteModelLifecycle:
"""
Mock replacement for test_add_and_delete_models in tests/test_models.py.
The original integration test required a live proxy + OPENAI_API_KEY.
This test verifies the same lifecycle (add delete double-delete fails)
by calling the endpoint handlers directly with mocked DB.
"""
@pytest.mark.asyncio
async def test_add_then_delete_model(self):
"""
- Add model via add_new_model returns model_id
- Delete model via delete_model returns success
- Delete same model again raises (model not found)
"""
from litellm.proxy.management_endpoints.model_management_endpoints import (
add_new_model,
delete_model as delete_model_endpoint,
)
from litellm.proxy.management_endpoints.model_management_endpoints import (
ModelInfoDelete,
)
model_id = "lifecycle-test-model-123"
admin_user = UserAPIKeyAuth(
user_id="test-admin", user_role=LitellmUserRoles.PROXY_ADMIN
)
# Build a real LiteLLM_ProxyModelTable for the DB mock to return
db_row = LiteLLM_ProxyModelTable(
model_id=model_id,
model_name="lifecycle-model",
litellm_params={"model": "openai/gpt-4.1-nano"},
model_info={"id": model_id},
created_by="test-admin",
updated_by="test-admin",
)
mock_prisma = MagicMock()
mock_prisma.db = MagicMock()
mock_prisma.db.litellm_proxymodeltable = AsyncMock()
mock_prisma.db.litellm_proxymodeltable.create = AsyncMock(return_value=db_row)
mock_prisma.db.litellm_proxymodeltable.find_unique = AsyncMock(
return_value=db_row
)
mock_prisma.db.litellm_proxymodeltable.delete = AsyncMock(return_value=db_row)
mock_proxy_config = MagicMock()
mock_proxy_config.add_deployment = AsyncMock()
mock_router = MagicMock()
mock_router.delete_deployment = MagicMock()
_PS = "litellm.proxy.proxy_server"
with patch(f"{_PS}.prisma_client", mock_prisma), \
patch(f"{_PS}.store_model_in_db", True), \
patch(f"{_PS}.proxy_config", mock_proxy_config), \
patch(f"{_PS}.proxy_logging_obj", MagicMock()), \
patch(f"{_PS}.general_settings", {}), \
patch(f"{_PS}.premium_user", True), \
patch(f"{_PS}.llm_router", mock_router):
# --- ADD ---
add_result = await add_new_model(
model_params=Deployment(
model_name="lifecycle-model",
litellm_params=LiteLLM_Params(
model="openai/gpt-4.1-nano", api_key="fake-key"
),
model_info={"id": model_id},
),
user_api_key_dict=admin_user,
)
assert add_result.model_id == model_id
# --- DELETE ---
delete_result = await delete_model_endpoint(
model_info=ModelInfoDelete(id=model_id),
user_api_key_dict=admin_user,
)
assert "deleted successfully" in delete_result["message"]
# --- DELETE again should fail (model not found) ---
mock_prisma.db.litellm_proxymodeltable.find_unique = AsyncMock(
return_value=None
)
from litellm.proxy.proxy_server import ProxyException
with pytest.raises(ProxyException) as exc_info:
await delete_model_endpoint(
model_info=ModelInfoDelete(id=model_id),
user_api_key_dict=admin_user,
)
assert str(exc_info.value.code) == "400"

View File

@ -268,6 +268,9 @@ async def delete_model(session, model_id="123", key="sk-1234"):
return await response.json()
@pytest.mark.skip(
reason="Requires live proxy + OPENAI_API_KEY. Deterministic mock version in tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py::TestAddAndDeleteModelLifecycle"
)
@pytest.mark.asyncio
async def test_add_and_delete_models():
"""