litellm/tests/integration/_support/provider.py
devin-ai-integration[bot] 47afd1ba0b
test: fix shared-provider discovery, Codex catalog size and generated master key mismatches in CircleCI suites (#44905)
* test(integration): isolate Codex catalog and provider discovery fixtures

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(integration): keep model discovery constant import-safe

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test: align test keys and CI env with generated master keys

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: mateo <mateo@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-07 00:11:25 +00:00

50 lines
1.8 KiB
Python

"""The fake provider shared by every integration test that takes the `provider` fixture.
Deployments in `proxy_config.yaml` point at `PROVIDER_URL`, so one server answers for all of them. A test
queues the replies it expects with `expect` and reads what the proxy sent with `received`. Tests run one at a
time against it; the `provider` fixture checks nothing is left over between tests.
The proxy's model-info refresh is answered here.
"""
from __future__ import annotations
from collections import deque
from collections.abc import Iterator
from contextlib import contextmanager
from dataclasses import dataclass, field
from typing import Final
from tests.integration._support.wire import Reply, Request, Wire, wire_server
PROVIDER_PORT: Final = 8191
PROVIDER_URL: Final = f"http://127.0.0.1:{PROVIDER_PORT}"
MODEL_DISCOVERY: Final = ("GET", "/v1/models")
_UNQUEUED: Final = Reply(status=500, body=b'{"error": "the shared fake provider has no reply queued for this request"}')
@dataclass(slots=True)
class SharedProvider:
wire: Wire
replies: deque[Reply]
last_test: str | None = field(default=None)
def expect(self, *replies: Reply) -> None:
self.replies.extend(replies)
def received(self) -> tuple[Request, ...]:
return tuple(
request for request in self.wire.drain() if (request.method, request.target) != MODEL_DISCOVERY
)
@contextmanager
def shared_provider() -> Iterator[SharedProvider]:
replies: Final[deque[Reply]] = deque()
def respond(request: Request) -> Reply:
if (request.method, request.target) == MODEL_DISCOVERY:
return Reply(body=b'{"object":"list","data":[]}')
return replies.popleft() if replies else _UNQUEUED
with wire_server(respond, port=PROVIDER_PORT) as wire:
yield SharedProvider(wire, replies)