test(cli): model keyring's null backend in the vault test double

FakeSecretVault could only stand in for a discarding backend by passing
KeyringDiscardsWrites as its `failure`, which also made read() and erase()
hand it back. Neither SecretRead nor SecretErase admits that outcome and the
real KeyringVault never produces it there, so the login path's match was
falling through on a value it can never see. Give the double a `discards`
flag that reports it from write() alone, which is what the null backend does.

Also widen lint-format-check-changed's pathspec. Git wildmatch runs without
FNM_PATHNAME here, so 'litellm/**/*.py' still requires an intermediate
directory and silently skipped all 21 top-level modules, litellm/__init__.py
and litellm/main.py among them. All 21 already pass ruff format.
This commit is contained in:
mateo-berri 2026-08-20 05:28:19 -07:00
parent fb69fcf765
commit 1f2baf509e
3 changed files with 9 additions and 4 deletions

View file

@ -146,7 +146,7 @@ lint-install:
# only the litellm Python files changed vs the base are checked, so a pre-existing
# format issue elsewhere doesn't block an unrelated commit.
lint-format-check-changed: $(LINT_DEP_INSTALL) $(LINT_DEP_BASE)
@files=$$(git diff --name-only --diff-filter=ACMR origin/litellm_internal_staging...HEAD -- 'litellm/**/*.py' | grep -v '^litellm/enterprise/' || true); \
@files=$$(git diff --name-only --diff-filter=ACMR origin/litellm_internal_staging...HEAD -- 'litellm/*.py' | grep -v '^litellm/enterprise/' || true); \
if [ -z "$$files" ]; then \
echo "No changed litellm Python files to format-check."; \
else \

View file

@ -23,6 +23,7 @@ from litellm import router as litellm_router_module
from litellm import utils as litellm_utils_module
from litellm._logging import ALL_LOGGERS
from litellm.litellm_core_utils.cli_keyring import (
KeyringDiscardsWrites,
KeyringUnreachable,
KeyringUnusable,
SecretErase,
@ -132,7 +133,8 @@ class FakeSecretVault:
`available=False` models a keychain that is locked or has no backend, `writable=False` one that
refuses to store, `erasable=False` one that will not release what it already holds, and `failure`
picks which unusable state those report.
picks which unusable state those report. `discards=True` is keyring's null backend, which answers
reads and erases like any other yet keeps nothing it is given, so only writes report it.
"""
def __init__(
@ -142,12 +144,14 @@ class FakeSecretVault:
available: bool = True,
writable: bool = True,
erasable: bool = True,
discards: bool = False,
failure: KeyringUnusable = KeyringUnreachable(),
) -> None:
self.blob: str | None = blob
self.available: bool = available
self.writable: bool = writable
self.erasable: bool = erasable
self.discards: bool = discards
self.failure: KeyringUnusable = failure
self.reads: int = 0
self.writes: list[str] = []
@ -163,6 +167,8 @@ class FakeSecretVault:
self.writes.append(blob)
if not (self.available and self.writable):
return self.failure
if self.discards:
return KeyringDiscardsWrites()
self.blob = blob
return SecretStored()

View file

@ -16,7 +16,6 @@ from litellm.constants import CLI_JWT_EXPIRATION_HOURS
from litellm.litellm_core_utils.cli_keyring import (
DISABLE_KEYRING_ENV_VAR,
KeyringDisabled,
KeyringDiscardsWrites,
KeyringNotInstalled,
)
from litellm.litellm_core_utils.cli_token_utils import CliTokenRecord, save_cli_token
@ -1068,7 +1067,7 @@ class TestKeychainBackedCommands:
):
"""A backend that accepts writes and stores nothing must not be reported as keychain
storage, because the file is then told to drop the only remaining copy."""
result = self._login(secret_vault_factory(available=False, failure=KeyringDiscardsWrites()))
result = self._login(secret_vault_factory(discards=True))
token_file = isolated_home / ".litellm" / "token.json"
assert result.exit_code == 0