test(e2e): migrate access-control and inference-endpoint regression tests - #32016
Conversation
…ests Move the access-control and non-chat inference-endpoint cases from litellm-regression-tests onto the shared e2e harness so a regression in either fails here first access_control/ asserts the gateway's authorization and error-shape contract: a key limited to one model is denied 403 (key_model_access_denied) when it calls another, a key scoped to allowed_routes=["llm_api_routes"] is forbidden 403 from a management route, and an unknown model is rejected 400 before any provider is called. The source asserted 401 for the disallowed-model case against an older proxy; the live contract is now a 403, so the guard tracks current behavior llm_translation/ gains one file per non-chat inference endpoint (/v1/responses, /v1/messages, /embeddings, /v1/rerank, /v1/audio/speech, /v1/images/generations). Each test registers the deployment it needs through /model/new, drives real provider traffic, asserts the parsed body carries real content instead of just a 200, then deletes the model on teardown, so nothing is hardcoded into the gateway config
Greptile SummaryThis PR migrates access-control and non-chat inference endpoint regression tests from the external
Confidence Score: 4/5Safe to merge after fixing the missing The tests/e2e/llm_translation/endpoints_client.py — the
|
| Filename | Overview |
|---|---|
| tests/e2e/llm_translation/endpoints_client.py | New client for non-chat inference endpoints; is_ok is used in delete_model but not imported from e2e_http, causing a NameError in teardown when model deletion fails. |
| tests/e2e/access_control/test_access_control_e2e.py | New access-control suite asserting 403 for model-access denial, 403 for route-permission denial, and 400 for unknown models — well-structured with clear error messages. |
| tests/e2e/llm_translation/test_custom_pricing_e2e.py | Refactored to dynamically provision models via /model/new instead of reading from static gateway config; rates are now constants rather than parsed from YAML, removing the config-file dependency. |
| tests/e2e/models.py | Adds LiteLLMParamsBody, ModelInfoBody, ModelNewBody, ModelNewResponse, ModelDeleteBody, and allowed_routes field to KeyGenerateBody; all additions are clean and well-documented. |
| tests/e2e/llm_translation/test_rerank_e2e.py | New live e2e for /v1/rerank against Cohere; registers deployment at runtime and asserts scored results within the requested top_n. |
| tests/e2e/llm_translation/test_responses_e2e.py | New live e2e for /v1/responses against OpenAI; parses the typed response body and asserts non-empty text. |
| tests/e2e/llm_translation/test_messages_e2e.py | New live e2e for the Anthropic Messages endpoint via the gateway; asserts role and non-empty text. |
| tests/e2e/llm_translation/test_embeddings_endpoint_e2e.py | New live e2e for /embeddings; asserts a non-empty, non-zero vector from the OpenAI embedding model. |
| tests/e2e/llm_translation/test_audio_speech_e2e.py | New live e2e for /v1/audio/speech; asserts an audio content-type and non-empty binary body. |
| tests/e2e/llm_translation/test_image_generation_e2e.py | New live e2e for /v1/images/generations; asserts at least one image item with either url or b64_json. |
| tests/e2e/llm_translation/conftest.py | Adds endpoints_client session fixture alongside the existing client fixture. |
| tests/e2e/access_control/access_control_client.py | New client for access-control suite; clean delegation to the shared gateway/transport layer. |
| tests/e2e/access_control/conftest.py | Minimal conftest that wires up the AccessControlClient session fixture for the new suite. |
| tests/e2e/CLAUDE.md | Updated suite directory descriptions to include the new access_control/ suite and the expanded llm_translation/ scope. |
Reviews (2): Last reviewed commit: "test(e2e): provision custom-pricing depl..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
The custom-pricing tests relied on custom-priced-flash and a gemini-2.5-flash sibling being present in the static gateway config, so they inherited whatever pricing state the shared proxy already had. They now register the deployment(s) each case needs through /model/new and delete them on teardown, matching the rest of the llm_translation suite, so billing, /model/info reporting, and sibling isolation are checked against models the test controls end to end. LiteLLMParamsBody gains input_cost_per_token and output_cost_per_token so a custom-priced deployment can be created at runtime; left None they are dropped from the body and the deployment keeps the backend's canonical rate.
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
…ests (BerriAI#32016) * test(e2e): migrate access-control and inference-endpoint regression tests Move the access-control and non-chat inference-endpoint cases from litellm-regression-tests onto the shared e2e harness so a regression in either fails here first access_control/ asserts the gateway's authorization and error-shape contract: a key limited to one model is denied 403 (key_model_access_denied) when it calls another, a key scoped to allowed_routes=["llm_api_routes"] is forbidden 403 from a management route, and an unknown model is rejected 400 before any provider is called. The source asserted 401 for the disallowed-model case against an older proxy; the live contract is now a 403, so the guard tracks current behavior llm_translation/ gains one file per non-chat inference endpoint (/v1/responses, /v1/messages, /embeddings, /v1/rerank, /v1/audio/speech, /v1/images/generations). Each test registers the deployment it needs through /model/new, drives real provider traffic, asserts the parsed body carries real content instead of just a 200, then deletes the model on teardown, so nothing is hardcoded into the gateway config * Update endpoints_client.py Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Relevant issues
Migrates the access-control and non-chat inference-endpoint checks from the standalone
litellm-regression-testssuite onto the in-repo e2e harness undertests/e2e/, so a regression in either surface fails in CI here rather than only in the external suiteLinear ticket
N/A
Pre-Submission checklist
basedpyright,py_compile, and pytest collection are green locally, and the live e2e run needs the deployed proxy plus provider keys@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewScreenshots / Proof of Fix
These are live e2e tests that run against the deployed proxy with real provider keys and cost real money, exactly as a consumer would hit the endpoints. To exercise them once a proxy is up:
The rerank case registers a
cohere/rerank-v3.5deployment, soCOHERE_API_KEYmust be present in the proxy environment; the others useOPENAI_API_KEYandANTHROPIC_API_KEY, which the gateway config already relies on. I will attach the run output against the staging proxyType
✅ Test
Changes
access_control/is a new suite that asserts the gateway's authorization and error-shape contract. A key limited to one model is denied a 403key_model_access_deniedwhen it calls another, a key scoped toallowed_routes=["llm_api_routes"]is forbidden a 403 from a management route, and a syntactically valid request naming a non-existent model is rejected with a 400 before any provider is called. The source suite asserted a 401 for the disallowed-model case against an older proxy; the live contract is now a 403 (litellm/proxy/auth/auth_checks.py), so the guard tracks current behavior, and the original unknown-route smoke is replaced by the stronger route-permission checkllm_translation/gains one file per non-chat inference endpoint:/v1/responses,/v1/messages,/embeddings,/v1/rerank,/v1/audio/speech, and/v1/images/generations. Rather than hardcoding models into the gateway config, each test registers the deployment it needs through/model/new(with the provider key passed as anos.environ/...reference the proxy resolves), drives real provider traffic, parses the provider-native body with a typed model and asserts it carries real content instead of just a 200, then deletes the model on teardown. All HTTP goes through the shared transport, request and response bodies are typed pydantic models inmodels.pyand the suite client, and the two new typed model-management bodies plus theallowed_routeskey field are the only additions to shared harness codeThe
guardrailandrate_limitscases from the source suite are intentionally out of scope: the rate limiter is already covered byrouter/test_rate_limiter.py, and the guardrail case is deferred until a guardrail is wired into the e2e gateway config