fix(e2e): name the saved credential in its recorded step
Some checks are pending
LiteLLM Rust / rust-lint (push) Waiting to run
LiteLLM Rust / rust-test (push) Waiting to run
LiteLLM Rust / rust-wheel (push) Waiting to run
Terraform Provider / gofmt, vet, build, test (push) Waiting to run
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Waiting to run

The create_credential label read credential_info, which defaults to {} and is never set by the live callers, so the step printed nothing after 'for'. It now reads the required credential_name, and a guard fails on any label that reads a field with a default
This commit is contained in:
ryan-crabbe-berri 2026-09-30 18:20:54 -07:00
parent 1eaac9ce09
commit 098c188ddf
2 changed files with 23 additions and 6 deletions

View file

@ -26,6 +26,7 @@ import pytest
from e2e_metadata import MASK, MAX_STEPS, STEP_FRAMES, STEPS, StepRecorder, environment_secrets, step
from proxy_client import ProxyClient
from pydantic import BaseModel, Field
from pydantic.fields import FieldInfo
@pytest.fixture(autouse=True)
@ -156,16 +157,19 @@ def _dotted_placeholders(owner: type) -> Iterator[tuple[str, str]]:
return ((method, field) for method, field in _placeholders(owner) if "." in field)
def _resolves(owner: type, method: str, field: str) -> bool:
def _fields_read(owner: type, method: str, field: str) -> tuple[FieldInfo, ...] | None:
"""The model fields a dotted placeholder reads, outermost first, or None if one doesn't exist."""
root, *attributes = field.split(".")
wrapped: Final = cast("Callable[..., object]", getattr(owner, method))
hints: Final[Mapping[str, object]] = get_type_hints(inspect.unwrap(wrapped))
current: object = _field_type(hints[root]) # rebind-ok: walks one type per attribute
read: tuple[FieldInfo, ...] = () # rebind-ok: grows one field per attribute
for attribute in attributes:
if not (isinstance(current, type) and issubclass(current, BaseModel) and attribute in current.model_fields):
return False
current = _field_type(current.model_fields[attribute].annotation) # rebind-ok: walks one type per attribute
return True
return None
read = (*read, current.model_fields[attribute]) # rebind-ok: grows one field per attribute
current = _field_type(read[-1].annotation) # rebind-ok: walks one type per attribute
return read
SECRET_NAME: Final = re.compile(
@ -259,7 +263,20 @@ class TestLabelTemplates:
request model doesn't have would fail the test calling it, not the label."""
placeholders: Final = tuple(_dotted_placeholders(owner))
assert placeholders
assert [f"{method}: {field}" for method, field in placeholders if not _resolves(owner, method, field)] == []
assert [
f"{method}: {field}" for method, field in placeholders if _fields_read(owner, method, field) is None
] == []
@pytest.mark.parametrize("owner", [ProxyClient], ids=["ProxyClient"])
def test_every_dotted_placeholder_in_the_harness_reads_a_field_the_caller_must_set(self, owner: type) -> None:
"""A field with a default is usually left unset, and an unset field prints
nothing, so the step would read "Save a provider credential for "."""
unset: Final = tuple(
f"{method}: {field}"
for method, field in _dotted_placeholders(owner)
if not all(info.is_required() for info in _fields_read(owner, method, field) or ())
)
assert unset == ()
@pytest.mark.parametrize("owner", [ProxyClient], ids=["ProxyClient"])
def test_every_secret_field_a_label_can_print_is_hidden(self, owner: type) -> None:

View file

@ -913,7 +913,7 @@ class ProxyClient:
if not is_ok(result):
warnings.warn(f"delete_search_tool({search_tool_id!r}) failed: {result}", stacklevel=2 + STEP_FRAMES)
@step("Save a provider credential for {body.credential_info}")
@step("Save the provider credential {body.credential_name}")
def create_credential(self, body: CredentialCreateBody) -> None:
unwrap(
self.transport.post(