test(streaming): cover the three failure branches of the abandoned-stream flush

codecov/patch was at 77.41% against an 87.23% target. The uncovered lines were
all error paths, so this adds a test per branch rather than reshaping the happy
path:

- nothing streamed (`wrapper.chunks` empty): the flush must not assemble or
  record anything, and must not mark the stream as logged;
- `litellm.stream_chunk_builder` raising: the failure is logged and swallowed,
  no half-built response is recorded, and the stream still counts as logged so
  a second close does not retry;
- the provider stream's own `close()` raising: that must not mask the caller's
  error, and the reference is still dropped.

`test_close_survives_provider_stream_raising` uses a real object whose `close`
raises rather than a mock with a configured side effect, so the assertion is
about the outcome (reference dropped, no propagation) instead of the call.
This commit is contained in:
JingHao-Leon 2026-10-11 18:40:06 +08:00
parent e06e7321b0
commit 49980db0b9

View file

@ -6210,3 +6210,41 @@ class TestCloseFlushesAbandonedStream:
# the provider stream was closed and the reference dropped
assert released == [True]
assert wrapper.completion_stream is None
def test_no_flush_when_nothing_was_streamed(self):
wrapper, logging_obj = self._wrapper_with_chunk()
wrapper.chunks = []
wrapper.close_stream()
# nothing was served, so there is nothing to bill or log
assert "async_complete_streaming_response" not in logging_obj.model_call_details
assert wrapper._abandonment_logged is False
def test_flush_survives_stream_chunk_builder_raising(self):
wrapper, logging_obj = self._wrapper_with_chunk()
def _boom(**_kwargs):
raise RuntimeError("builder exploded")
with patch("litellm.stream_chunk_builder", _boom):
wrapper.close_stream()
# the failure is logged, not raised, and nothing half-built is recorded
assert "async_complete_streaming_response" not in logging_obj.model_call_details
assert wrapper._abandonment_logged is True
def test_close_survives_provider_stream_raising(self):
wrapper, _ = self._wrapper_with_chunk()
class _Angry:
def close(inner_self):
raise OSError("already closed")
wrapper.completion_stream = _Angry()
wrapper.close_stream()
# a provider that fails to close must not mask the caller's own error,
# and the reference is still dropped
assert wrapper.completion_stream is None