mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-02 07:13:02 +00:00
* test: delete unconditionally skipped legacy tests * test: move whole-unit legacy test files into tests/unit * test: keep moved legacy tests free of import-time global state * test: keep the module-level invocation scan pointed at tests/local_testing * ci: drop the agent_testing CircleCI job emptied by the move * test: fix moved-test isolation and router coverage * test: add Tinyfish search package marker * test: isolate moved tests from logger state leaks * test: isolate Helicone logging fixture state * test: isolate Vertex pass-through credentials between moved tests * test: cancel S3 periodic flush tasks started by moved tests * ci: restore CircleCI assistant test selection after move * test: prevent Datadog datetime import shadowing * ci: drop the litellm_assistants_api_testing CircleCI job emptied by the move * test: deduplicate imports in rebased unit tests * test: remove duplicate passthrough router patch import * test: remove moved legacy source files after rebase * test: align moved tests with rebased main * test: carry main's legacy-file edits into moved destinations * test: make the moved cost map fallback tests assert the fetch and the backup The four fallback cases only checked the result was non-empty, so they still passed with integrity validation disabled. They now inject a mock client, assert one fetch happened, and assert the result is exactly the local backup with the fallback reason recorded. --------- Co-authored-by: yuneng <yuneng@berri.ai>
141 lines
4.5 KiB
Python
141 lines
4.5 KiB
Python
import asyncio
|
|
import importlib
|
|
import re
|
|
from pathlib import Path
|
|
from typing import Final
|
|
|
|
import httpx
|
|
import httpx2
|
|
import pytest
|
|
|
|
import litellm
|
|
from tests._vcr_conftest_common import (
|
|
detect_vcr_patch_leak,
|
|
guard_vcr_patch_points,
|
|
install_live_call_probe,
|
|
record_vcr_outcome,
|
|
restore_vcr_patch_points,
|
|
rewound_new_episodes_cassette,
|
|
)
|
|
|
|
_ORIGINAL_MOCK_HANDLE_ASYNC_REQUEST: Final = httpx.MockTransport.handle_async_request
|
|
_ORIGINAL_HTTPX2_MOCK_HANDLE_ASYNC_REQUEST: Final = httpx2.MockTransport.handle_async_request
|
|
|
|
|
|
@pytest.fixture
|
|
def leaked_cassette_dir(tmp_path: Path):
|
|
context: Final = rewound_new_episodes_cassette(tmp_path)
|
|
context.__enter__()
|
|
yield tmp_path
|
|
context.__exit__(None, None, None)
|
|
|
|
|
|
def test_no_leak_when_no_cassette_is_active():
|
|
assert detect_vcr_patch_leak() is None
|
|
|
|
|
|
def test_leaked_cassette_is_detected_named_and_restorable(leaked_cassette_dir: Path):
|
|
leak: Final = detect_vcr_patch_leak()
|
|
|
|
assert leak is not None
|
|
assert {"httpx.MockTransport.handle_async_request", "aiohttp.client.ClientSession._request"} <= set(
|
|
leak.patch_points
|
|
)
|
|
assert leak.cassette_paths == (str(leaked_cassette_dir / "rewound_owner.yaml"),)
|
|
|
|
restore_vcr_patch_points()
|
|
|
|
assert detect_vcr_patch_leak() is None
|
|
assert httpx.MockTransport.handle_async_request is _ORIGINAL_MOCK_HANDLE_ASYNC_REQUEST
|
|
|
|
|
|
def test_leak_is_detected_on_every_transport_family_vcrpy_patches(leaked_cassette_dir: Path):
|
|
leak: Final = detect_vcr_patch_leak()
|
|
|
|
assert leak is not None
|
|
assert "httpx2.MockTransport.handle_async_request" in leak.patch_points
|
|
assert httpx2.MockTransport.handle_async_request is not _ORIGINAL_HTTPX2_MOCK_HANDLE_ASYNC_REQUEST
|
|
|
|
restore_vcr_patch_points()
|
|
|
|
assert httpx2.MockTransport.handle_async_request is _ORIGINAL_HTTPX2_MOCK_HANDLE_ASYNC_REQUEST
|
|
|
|
|
|
def test_guard_fails_the_leaking_test_and_restores_the_originals(request, leaked_cassette_dir: Path):
|
|
with pytest.raises(pytest.fail.Exception, match=re.escape(request.node.nodeid)) as failure:
|
|
guard_vcr_patch_points(request.node, teardown_failed=False)
|
|
|
|
assert str(leaked_cassette_dir / "rewound_owner.yaml") in str(failure.value)
|
|
assert detect_vcr_patch_leak() is None
|
|
|
|
|
|
def test_guard_restores_silently_when_the_teardown_already_failed(request, leaked_cassette_dir: Path):
|
|
guard_vcr_patch_points(request.node, teardown_failed=True)
|
|
|
|
assert detect_vcr_patch_leak() is None
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _vcr_outcome_gate(request, vcr):
|
|
install_live_call_probe(request, vcr)
|
|
yield
|
|
record_vcr_outcome(request, vcr)
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def event_loop():
|
|
try:
|
|
loop = asyncio.get_running_loop()
|
|
except RuntimeError:
|
|
loop = asyncio.new_event_loop()
|
|
yield loop
|
|
loop.close()
|
|
|
|
|
|
@pytest.fixture(scope="function", autouse=True)
|
|
def setup_and_teardown(event_loop):
|
|
import litellm
|
|
|
|
original_state = {}
|
|
for attr in (
|
|
"callbacks",
|
|
"success_callback",
|
|
"failure_callback",
|
|
"_async_success_callback",
|
|
"_async_failure_callback",
|
|
):
|
|
if hasattr(litellm, attr):
|
|
val = getattr(litellm, attr)
|
|
original_state[attr] = val.copy() if val else []
|
|
for attr in _SCALAR_DEFAULTS:
|
|
if hasattr(litellm, attr):
|
|
original_state[attr] = getattr(litellm, attr)
|
|
from litellm.litellm_core_utils.logging_worker import GLOBAL_LOGGING_WORKER
|
|
|
|
asyncio.run(GLOBAL_LOGGING_WORKER.clear_queue())
|
|
importlib.reload(litellm)
|
|
asyncio.set_event_loop(event_loop)
|
|
yield
|
|
for attr, original_value in original_state.items():
|
|
if hasattr(litellm, attr):
|
|
setattr(litellm, attr, original_value)
|
|
pending = asyncio.all_tasks(event_loop)
|
|
for task in pending:
|
|
task.cancel()
|
|
if pending:
|
|
event_loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True))
|
|
|
|
|
|
_SCALAR_DEFAULTS = {
|
|
"num_retries": getattr(litellm, "num_retries", None),
|
|
"set_verbose": getattr(litellm, "set_verbose", False),
|
|
"cache": getattr(litellm, "cache", None),
|
|
"allowed_fails": getattr(litellm, "allowed_fails", 3),
|
|
"disable_aiohttp_transport": getattr(litellm, "disable_aiohttp_transport", False),
|
|
"force_ipv4": getattr(litellm, "force_ipv4", False),
|
|
"drop_params": getattr(litellm, "drop_params", None),
|
|
"modify_params": getattr(litellm, "modify_params", False),
|
|
"api_base": getattr(litellm, "api_base", None),
|
|
"api_key": getattr(litellm, "api_key", None),
|
|
"cohere_key": getattr(litellm, "cohere_key", None),
|
|
}
|