Skip to content

test(proxy): make model_info endpoint tests hermetic to kill an order/merge-skew flake - #34037

Merged
mateo-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_fix_model_info_unpack_flake
Jul 20, 2026
Merged

test(proxy): make model_info endpoint tests hermetic to kill an order/merge-skew flake#34037
mateo-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_fix_model_info_unpack_flake

Conversation

@mateo-berri

@mateo-berri mateo-berri commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Relevant issues

Fixes the proxy-endpoints and proxy-server CI flake first observed on #33807 (TestModelInfoEndpoint failing with ValueError: not enough values to unpack (expected 2, got 0))

Linear ticket

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • My PR passes all CI/CD checks (e.g., lint, format, unit tests)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Screenshots / Proof of Fix

This is a test-only change (no file under litellm/ is touched), so there is no runtime/product behavior to curl against a live proxy; the meaningful proof is the deterministic before/after of the flaky tests themselves, captured at specific commits

BEFORE (the exact merge-skew window, 8536e3b80e = product refactor #33721 landed, test not yet updated) — the two tests from #33807's CI, in isolation:

$ git checkout 8536e3b80e -- litellm/proxy/utils.py tests/.../test_model_management_endpoints.py
$ pytest -p no:randomly \
    "tests/.../test_model_management_endpoints.py::TestModelInfoEndpoint::test_model_info_accessible_model_success" \
    "tests/.../test_model_management_endpoints.py::TestModelInfoEndpoint::test_model_info_team_model_access"

    configured_input, configured_output = llm_router.get_configured_token_limits(model_id)
E   ValueError: not enough values to unpack (expected 2, got 0)
2 failed

BEFORE (current tip, the two sibling manifestations of the same root cause) — deterministic under an enterprise license (premium_user is True), order-dependent otherwise:

$ pytest -p no:randomly \
    "tests/.../test_model_management_endpoints.py::TestGetModelInfoWithIdBlocked::test_get_model_info_with_id_propagates_blocked_true"
E   pydantic_core._pydantic_core.ValidationError: 2 validation errors for ModelInfo
E   updated_by  Input should be a valid string [input_value=<MagicMock name='mock.updated_by'>]
1 failed

$ pytest -n 2 tests/test_litellm/proxy/proxy_server        # 3x, deterministic
FAILED .../test_proxy_config.py::test_ProxyConfig_get_model_info_with_id_missing_model_id_raises
1 failed, 578 passed   (x3)

AFTER (this PR, 44604620a4) — the two shards that went red, run the way CI runs them (-n 2), repeatedly:

# tests/test_litellm/proxy/management_endpoints  (6x deterministic + 4x random order)
1856 passed
# tests/test_litellm/proxy/proxy_server          (4x -n 2)
579 passed

The rewritten TestModelInfoEndpoint also passes against BOTH the pre-#33721 (get_model_group_info) and post-#33721 (get_configured_token_limits) endpoint, so a future merge-skew cannot reproduce the original failure

Type

✅ Test

🐛 Bug Fix

Changes

Root cause (full RCA linked below). The model_info / get_model_info_with_id endpoint unit tests drove refactored endpoints with bare, unspec'd MagicMock() routers and models. Because the mocks were unspec'd, any attribute or method the refactored endpoints newly read auto-materialized a child MagicMock, and whether that child was reached depended on process-global state the endpoints branch on: premium_user, and the real get_available_models_for_user chain reading litellm globals, both of which sibling tests in the same xdist worker mutate. When reached, the child MagicMock either unpacked to empty (a, b = mock.method() raises not enough values to unpack (expected 2, got 0), since a bare MagicMock's default __iter__ yields nothing) or leaked into RouterModelInfo(**model_info) and failed Pydantic's str validation. The tests pass in isolation and fail under xdist / CI merge-skew

The specific #33807 failure was this class surfaced by merge timing: #33721 (2026-07-17 20:04) added a get_configured_token_limits unpack to create_model_info_response; #33742 (21:17) band-aided the test with get_configured_token_limits.return_value = (None, None); #33807's branch was based at 19:28 (before #33721), so GitHub's PR-merge CI ran #33721's product code against the un-updated bare-mock test, deterministically hitting the unpack. Locally the branch stayed green because its pinned tree still used the old get_model_group_info path

The fix is test-only, no product change:

  • TestModelInfoEndpoint: mock the real seam the refactored endpoint uses (litellm.proxy.utils.get_available_models_for_user), return a real Deployment from the router's get_deployment_by_model_group_name, configure the exact router methods the endpoint calls, pin general_settings, and delete the dead proxy_server.get_key_models / get_team_models / get_complete_model_list patches the refactor had stranded (the endpoint imports those fresh from auth.model_checks, so the patches were silent no-ops)
  • TestGetModelInfoWithIdBlocked: MagicMock(spec=["model_id", "model_info", "blocked"]) so unset enterprise columns read as None instead of child mocks
  • test_ProxyConfig_get_model_info_with_id_missing_model_id_raises: pin premium_user=False so the asserted AttributeError no longer flips to a TypeError with the ambient license global

Same root-cause class still lives (out of scope for this isolated PR, flagged in the RCA for follow-up) in tests/test_litellm/proxy/proxy_server/test_team_model_name_translation.py::test_model_info_v1_* and tests/test_litellm/proxy/guardrails/test_guardrail_coverage.py::test_secret_detection_*, both of which I observed failing under xdist ordering and neither of which this PR touches

RCA: https://app.notion.com/p/3a343b8acdab81e49681cf3defd58a52

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

…/merge-skew flake

The model_info / get_model_info_with_id endpoint tests drove refactored
endpoints with bare, unspec'd MagicMock routers and models. Because the
mocks were unspec'd, any attribute or method the (refactored) endpoints
newly read auto-materialized a child MagicMock, and whether that child
was reached depended on process-global state (premium_user, and the real
get_available_models_for_user chain reading litellm globals) that sibling
tests in the same xdist worker mutate. When reached, the MagicMock either
unpacked to empty (a, b = mock.method() -> 'not enough values to unpack
(expected 2, got 0)') or leaked into RouterModelInfo(**model_info) and
failed Pydantic str validation. Pass in isolation, fail under xdist.

The original TestModelInfoEndpoint failure (#33807 CI) was the same class
surfaced by merge skew: #33721 added a get_configured_token_limits unpack
to create_model_info_response, and CI's merge commit ran that against the
un-updated bare-mock test before the #33742 band-aid landed.

Fix (test-only, no product change):
- TestModelInfoEndpoint: mock the real seam (get_available_models_for_user),
  configure the router methods the endpoint actually calls, return a real
  Deployment, and drop the dead proxy_server.get_key_models/get_team_models/
  get_complete_model_list patches the refactor had stranded.
- TestGetModelInfoWithIdBlocked: spec the model mock so unset enterprise
  columns read as None instead of child MagicMocks.
- test_ProxyConfig_get_model_info_with_id_missing_model_id_raises: pin
  premium_user so the asserted AttributeError no longer flips with the
  ambient license global.
@greptile-apps

greptile-apps Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This test-only PR makes three flaky tests hermetic by fixing the underlying root cause: tests were driving refactored endpoints through bare, unspec'd MagicMock routers that auto-materialized child mocks, whose behaviour depended on process-global state (premium_user, litellm globals) mutated by sibling xdist workers.

  • TestModelInfoEndpoint: replaces silent no-op patches (get_key_models, get_team_models, get_complete_model_list) with the real seam the current endpoint reads (litellm.proxy.utils.get_available_models_for_user), swaps bare mock returns for real Deployment objects, and pins general_settings.
  • TestGetModelInfoWithIdBlocked: adds spec=[\"model_id\", \"model_info\", \"blocked\"] to prevent un-spec'd child mocks from reaching RouterModelInfo(**model.model_info) and failing Pydantic's str validation.
  • test_ProxyConfig_get_model_info_with_id_missing_model_id_raises: pins premium_user=False via monkeypatch so the asserted AttributeError is never displaced by a TypeError from the ambient enterprise-license global.

Confidence Score: 5/5

Test-only change that moves patches to the correct import locations and tightens MagicMock specs; no production code is touched and all existing assertions are preserved or made more precise.

All three fixes target genuine root causes (wrong patch targets, un-spec'd mocks leaking into Pydantic, global state bleed) rather than masking failures by relaxing assertions. The success-path tests add stronger real-type constraints via concrete Deployment objects, and the monkeypatch approach isolates global state correctly.

No files require special attention; both changed files are test utilities only.

Important Files Changed

Filename Overview
tests/test_litellm/proxy/management_endpoints/test_model_management_endpoints.py TestModelInfoEndpoint now patches the real seam and uses concrete Deployment objects; TestGetModelInfoWithIdBlocked adds a spec to prevent MagicMock child attributes from leaking into Pydantic validation.
tests/test_litellm/proxy/proxy_server/test_proxy_config.py Pins premium_user=False via monkeypatch so the expected AttributeError is reliably raised instead of a TypeError when the premium_user global is True from a sibling test.

Reviews (1): Last reviewed commit: "test(proxy): make model_info endpoint te..." | Re-trigger Greptile

@codecov

codecov Bot commented Jul 20, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@codspeed-hq

codspeed-hq Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing litellm_fix_model_info_unpack_flake (4460462) with litellm_internal_staging (214945a)1

Open in CodSpeed

Footnotes

  1. No successful run was found on litellm_internal_staging (68be053) during the generation of this report, so 214945a was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@mateo-berri
mateo-berri merged commit 885a637 into litellm_internal_staging Jul 20, 2026
77 of 78 checks passed
@mateo-berri
mateo-berri deleted the litellm_fix_model_info_unpack_flake branch July 20, 2026 22:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants