Coverage for tests / test_dependency_after_yield_streaming.py: 100%
80 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-04-06 01:24 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-04-06 01:24 +0000
1from collections.abc import Generator 1abcd
2from contextlib import contextmanager 1abcd
3from typing import Annotated, Any 1abcd
5import pytest 1abcd
6from fastapi import Depends, FastAPI 1abcd
7from fastapi.responses import StreamingResponse 1abcd
8from fastapi.testclient import TestClient 1abcd
11class Session: 1abcd
12 def __init__(self) -> None: 1abcd
13 self.data = ["foo", "bar", "baz"] 1hqeirnxsyjtfkuozlvgmwpA
14 self.open = True 1hqeirnxsyjtfkuozlvgmwpA
16 def __iter__(self) -> Generator[str, None, None]: 1abcd
17 for item in self.data: 1hqeirnBsjtfkuolvgmwp
18 if self.open: 1hqeirnBsjtfkuolvgmwp
19 yield item 1rnsuowp
20 else:
21 raise ValueError("Session closed") 1hqeiCBjtfklvgm
24@contextmanager 1abcd
25def acquire_session() -> Generator[Session, None, None]: 1abcd
26 session = Session() 1hqeirnxsyjtfkuozlvgmwpA
27 try: 1hqeirnxsyjtfkuozlvgmwpA
28 yield session 1hqeirnxsyjtfkuozlvgmwpA
29 finally:
30 session.open = False 1hqeirnxCBsyjtfkuozlvgmwpA
33def dep_session() -> Any: 1abcd
34 with acquire_session() as s: 1rnxsyuozwpA
35 yield s 1rnxsyuozwpA
38def broken_dep_session() -> Any: 1abcd
39 with acquire_session() as s: 1hqeiCBjtfklvgm
40 s.open = False 1hqeiCBjtfklvgm
41 yield s 1hqeiCBjtfklvgm
44SessionDep = Annotated[Session, Depends(dep_session)] 1abcd
45BrokenSessionDep = Annotated[Session, Depends(broken_dep_session)] 1abcd
47app = FastAPI() 1abcd
50@app.get("/data") 1abcd
51def get_data(session: SessionDep) -> Any: 1abcd
52 data = list(session) 1rsuw
53 return data 1rsuw
56@app.get("/stream-simple") 1abcd
57def get_stream_simple(session: SessionDep) -> Any: 1abcd
58 def iter_data(): 1xyzA
59 yield from ["x", "y", "z"] 1xyzA
61 return StreamingResponse(iter_data()) 1xyzA
64@app.get("/stream-session") 1abcd
65def get_stream_session(session: SessionDep) -> Any: 1abcd
66 def iter_data(): 1nEop
67 yield from session 1nEop
69 return StreamingResponse(iter_data()) 1nEop
72@app.get("/broken-session-data") 1abcd
73def get_broken_session_data(session: BrokenSessionDep) -> Any: 1abcd
74 return list(session) 1hqCBjtlv
77@app.get("/broken-session-stream") 1abcd
78def get_broken_session_stream(session: BrokenSessionDep) -> Any: 1abcd
79 def iter_data(): 1eiDFfkgm
80 yield from session 1eiDFfkgm
82 return StreamingResponse(iter_data()) 1eiDFfkgm
85client = TestClient(app) 1abcd
88def test_regular_no_stream(): 1abcd
89 response = client.get("/data") 1rsuw
90 assert response.json() == ["foo", "bar", "baz"] 1rsuw
93def test_stream_simple(): 1abcd
94 response = client.get("/stream-simple") 1xyzA
95 assert response.text == "xyz" 1xyzA
98def test_stream_session(): 1abcd
99 response = client.get("/stream-session") 1nEop
100 assert response.text == "foobarbaz" 1nEop
103def test_broken_session_data(): 1abcd
104 with pytest.raises(ValueError, match="Session closed"): 1qBtv
105 client.get("/broken-session-data") 1qBtv
108def test_broken_session_data_no_raise(): 1abcd
109 client = TestClient(app, raise_server_exceptions=False) 1hCjl
110 response = client.get("/broken-session-data") 1hCjl
111 assert response.status_code == 500 1hCjl
112 assert response.text == "Internal Server Error" 1hCjl
115def test_broken_session_stream_raise(): 1abcd
116 # Can raise ValueError on Pydantic v2 and ExceptionGroup on Pydantic v1
117 with pytest.raises((ValueError, Exception)): 1iFkm
118 client.get("/broken-session-stream") 1iFkm
121def test_broken_session_stream_no_raise(): 1abcd
122 """
123 When a dependency with yield raises after the streaming response already started
124 the 200 status code is already sent, but there's still an error in the server
125 afterwards, an exception is raised and captured or shown in the server logs.
126 """
127 with TestClient(app, raise_server_exceptions=False) as client: 1eDfg
128 response = client.get("/broken-session-stream") 1eDfg
129 assert response.status_code == 200 1eDfg
130 assert response.text == "" 1eDfg