mirror of
https://github.com/BerriAI/litellm.git
synced 2026-08-28 05:25:59 +00:00
test(e2e): drop proxy_client model-servable unit tests
Keep the create_model DB-sync wait in the harness; the pure-function unit file is not needed for this PR
This commit is contained in:
parent
7d1ee2ff86
commit
89204651d1
1 changed files with 0 additions and 241 deletions
|
|
@ -1,241 +0,0 @@
|
|||
"""Harness coverage for the bounded wait after /model/new (no live proxy).
|
||||
|
||||
create_model must wait for the product default DB reload interval of continuous
|
||||
listing so multi-worker gateways finish add_deployment before callers use the
|
||||
model. Clock and sleep are injected so these assert the deadline arithmetic
|
||||
without wall-clock waits.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from e2e_http import NetworkError, Result, Success
|
||||
from models import ModelListEntry, ModelsListResponse
|
||||
from proxy_client import (
|
||||
MODEL_SERVABLE_DB_SYNC_SECONDS,
|
||||
MODEL_SERVABLE_REQUEST_TIMEOUT,
|
||||
MODEL_SERVABLE_TIMEOUT,
|
||||
NotServable,
|
||||
Servable,
|
||||
await_servable,
|
||||
servable_timeout_message,
|
||||
)
|
||||
|
||||
|
||||
def _listing(*model_names: str) -> Result[ModelsListResponse]:
|
||||
return Success(
|
||||
status_code=200,
|
||||
data=ModelsListResponse(data=tuple(ModelListEntry(id=name) for name in model_names)),
|
||||
)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class FakeClock:
|
||||
seconds: float = 0.0
|
||||
slept: list[float] = field(default_factory=list) # mutable-ok: records calls for assertions
|
||||
|
||||
def now(self) -> float:
|
||||
return self.seconds
|
||||
|
||||
def sleep(self, duration: float) -> None:
|
||||
self.slept.append(duration)
|
||||
self.seconds += duration
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class FakeModelList:
|
||||
responses: tuple[Result[ModelsListResponse], ...]
|
||||
calls: int = 0
|
||||
timeouts: list[float] = field(default_factory=list) # mutable-ok: records call timeouts
|
||||
|
||||
def __call__(self, request_timeout: float) -> Result[ModelsListResponse]:
|
||||
self.timeouts.append(request_timeout)
|
||||
response = self.responses[min(self.calls, len(self.responses) - 1)]
|
||||
self.calls += 1
|
||||
return response
|
||||
|
||||
|
||||
def test_returns_on_first_listing_when_db_sync_is_zero() -> None:
|
||||
clock = FakeClock()
|
||||
list_models = FakeModelList(responses=(_listing("my-model"),))
|
||||
|
||||
outcome = await_servable(
|
||||
list_models,
|
||||
model_name="my-model",
|
||||
timeout=40.0,
|
||||
interval=2.0,
|
||||
request_timeout=5.0,
|
||||
db_sync_seconds=0.0,
|
||||
now=clock.now,
|
||||
sleep=clock.sleep,
|
||||
)
|
||||
|
||||
assert outcome == Servable()
|
||||
assert list_models.calls == 1
|
||||
assert clock.slept == []
|
||||
|
||||
|
||||
def test_requires_continuous_listing_for_default_db_sync_interval() -> None:
|
||||
clock = FakeClock()
|
||||
list_models = FakeModelList(responses=(_listing("my-model"),))
|
||||
|
||||
outcome = await_servable(
|
||||
list_models,
|
||||
model_name="my-model",
|
||||
timeout=40.0,
|
||||
interval=2.0,
|
||||
request_timeout=5.0,
|
||||
db_sync_seconds=MODEL_SERVABLE_DB_SYNC_SECONDS,
|
||||
now=clock.now,
|
||||
sleep=clock.sleep,
|
||||
)
|
||||
|
||||
assert outcome == Servable()
|
||||
assert clock.seconds >= MODEL_SERVABLE_DB_SYNC_SECONDS
|
||||
assert list_models.calls >= 2
|
||||
|
||||
|
||||
def test_resets_db_sync_window_when_a_poll_misses() -> None:
|
||||
clock = FakeClock()
|
||||
list_models = FakeModelList(
|
||||
responses=(
|
||||
_listing("my-model"),
|
||||
_listing("my-model"),
|
||||
_listing("other"),
|
||||
_listing("my-model"),
|
||||
)
|
||||
)
|
||||
|
||||
outcome = await_servable(
|
||||
list_models,
|
||||
model_name="my-model",
|
||||
timeout=40.0,
|
||||
interval=2.0,
|
||||
request_timeout=5.0,
|
||||
db_sync_seconds=6.0,
|
||||
now=clock.now,
|
||||
sleep=clock.sleep,
|
||||
)
|
||||
|
||||
assert outcome == Servable()
|
||||
assert list_models.calls >= 4
|
||||
assert clock.seconds >= 6.0
|
||||
|
||||
|
||||
def test_polls_until_the_model_first_appears() -> None:
|
||||
clock = FakeClock()
|
||||
list_models = FakeModelList(responses=(_listing("other"), _listing("other"), _listing("other", "my-model")))
|
||||
|
||||
outcome = await_servable(
|
||||
list_models,
|
||||
model_name="my-model",
|
||||
timeout=40.0,
|
||||
interval=2.0,
|
||||
request_timeout=5.0,
|
||||
db_sync_seconds=0.0,
|
||||
now=clock.now,
|
||||
sleep=clock.sleep,
|
||||
)
|
||||
|
||||
assert outcome == Servable()
|
||||
assert list_models.calls == 3
|
||||
assert clock.seconds == 4.0
|
||||
|
||||
|
||||
def test_gives_up_if_first_listing_never_arrives() -> None:
|
||||
clock = FakeClock()
|
||||
list_models = FakeModelList(responses=(_listing("other"),))
|
||||
|
||||
outcome = await_servable(
|
||||
list_models,
|
||||
model_name="my-model",
|
||||
timeout=10.0,
|
||||
interval=2.0,
|
||||
request_timeout=5.0,
|
||||
db_sync_seconds=30.0,
|
||||
now=clock.now,
|
||||
sleep=clock.sleep,
|
||||
)
|
||||
|
||||
assert isinstance(outcome, NotServable)
|
||||
assert clock.seconds == 8.0
|
||||
assert list_models.calls == 5
|
||||
|
||||
|
||||
def test_clamps_request_timeout_to_remaining_deadline() -> None:
|
||||
clock = FakeClock()
|
||||
timeouts: list[float] = []
|
||||
|
||||
def list_models(request_timeout: float) -> Result[ModelsListResponse]:
|
||||
timeouts.append(request_timeout)
|
||||
clock.seconds += request_timeout
|
||||
return _listing("other")
|
||||
|
||||
outcome = await_servable(
|
||||
list_models,
|
||||
model_name="my-model",
|
||||
timeout=10.0,
|
||||
interval=2.0,
|
||||
request_timeout=5.0,
|
||||
db_sync_seconds=30.0,
|
||||
now=clock.now,
|
||||
sleep=clock.sleep,
|
||||
)
|
||||
|
||||
assert isinstance(outcome, NotServable)
|
||||
assert timeouts[0] == 5.0
|
||||
assert any(timeout < 5.0 for timeout in timeouts)
|
||||
assert clock.seconds <= 10.0
|
||||
|
||||
|
||||
def test_does_not_wait_past_first_listing_budget_when_missing() -> None:
|
||||
clock = FakeClock()
|
||||
|
||||
outcome = await_servable(
|
||||
FakeModelList(responses=(_listing("other"),)),
|
||||
model_name="my-model",
|
||||
timeout=MODEL_SERVABLE_TIMEOUT,
|
||||
interval=2.0,
|
||||
request_timeout=MODEL_SERVABLE_REQUEST_TIMEOUT,
|
||||
db_sync_seconds=MODEL_SERVABLE_DB_SYNC_SECONDS,
|
||||
now=clock.now,
|
||||
sleep=clock.sleep,
|
||||
)
|
||||
|
||||
assert isinstance(outcome, NotServable)
|
||||
assert clock.seconds <= MODEL_SERVABLE_TIMEOUT
|
||||
|
||||
|
||||
def test_reports_a_failed_read_distinctly_from_a_missing_model() -> None:
|
||||
clock = FakeClock()
|
||||
unreachable: Result[ModelsListResponse] = NetworkError(message="connection refused")
|
||||
|
||||
outcome = await_servable(
|
||||
FakeModelList(responses=(unreachable,)),
|
||||
model_name="my-model",
|
||||
timeout=1.0,
|
||||
interval=0.5,
|
||||
request_timeout=5.0,
|
||||
db_sync_seconds=0.0,
|
||||
now=clock.now,
|
||||
sleep=clock.sleep,
|
||||
)
|
||||
|
||||
assert outcome == NotServable(last_result=unreachable)
|
||||
message = servable_timeout_message(
|
||||
model_name="my-model",
|
||||
timeout=1.0,
|
||||
db_sync_seconds=0.0,
|
||||
last_result=unreachable,
|
||||
)
|
||||
assert "connection refused" in message
|
||||
|
||||
listed_without_model = _listing("other")
|
||||
propagation_message = servable_timeout_message(
|
||||
model_name="my-model",
|
||||
timeout=1.0,
|
||||
db_sync_seconds=30.0,
|
||||
last_result=listed_without_model,
|
||||
)
|
||||
assert "did not succeed" not in propagation_message
|
||||
Loading…
Add table
Reference in a new issue