Coverage for tests/test_router_events.py: 100%
176 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-12-04 08:29 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2025-12-04 08:29 +0000
1from contextlib import asynccontextmanager 1opqrstu
2from typing import AsyncGenerator, Dict, Union 1opqrstu
4import pytest 1opqrstu
5from fastapi import APIRouter, FastAPI, Request 1opqrstu
6from fastapi.testclient import TestClient 1opqrstu
7from pydantic import BaseModel 1opqrstu
10class State(BaseModel): 1opqrstu
11 app_startup: bool = False 1opqrstu
12 app_shutdown: bool = False 1opqrstu
13 router_startup: bool = False 1opqrstu
14 router_shutdown: bool = False 1opqrstu
15 sub_router_startup: bool = False 1opqrstu
16 sub_router_shutdown: bool = False 1opqrstu
19@pytest.fixture 1opqrstu
20def state() -> State: 1opqrstu
21 return State() 1opqrstu
24@pytest.mark.filterwarnings( 1opqrstu
25 r"ignore:\s*on_event is deprecated, use lifespan event handlers instead.*:DeprecationWarning"
26)
27def test_router_events(state: State) -> None: 1opqrstu
28 app = FastAPI() 1abcdefg
30 @app.get("/") 1abcdefg
31 def main() -> Dict[str, str]: 1abcdefg
32 return {"message": "Hello World"} 1abcdefg
34 @app.on_event("startup") 1abcdefg
35 def app_startup() -> None: 1abcdefg
36 state.app_startup = True 1abcdefg
38 @app.on_event("shutdown") 1abcdefg
39 def app_shutdown() -> None: 1abcdefg
40 state.app_shutdown = True 1abcdefg
42 router = APIRouter() 1abcdefg
44 @router.on_event("startup") 1abcdefg
45 def router_startup() -> None: 1abcdefg
46 state.router_startup = True 1abcdefg
48 @router.on_event("shutdown") 1abcdefg
49 def router_shutdown() -> None: 1abcdefg
50 state.router_shutdown = True 1abcdefg
52 sub_router = APIRouter() 1abcdefg
54 @sub_router.on_event("startup") 1abcdefg
55 def sub_router_startup() -> None: 1abcdefg
56 state.sub_router_startup = True 1abcdefg
58 @sub_router.on_event("shutdown") 1abcdefg
59 def sub_router_shutdown() -> None: 1abcdefg
60 state.sub_router_shutdown = True 1abcdefg
62 router.include_router(sub_router) 1abcdefg
63 app.include_router(router) 1abcdefg
65 assert state.app_startup is False 1abcdefg
66 assert state.router_startup is False 1abcdefg
67 assert state.sub_router_startup is False 1abcdefg
68 assert state.app_shutdown is False 1abcdefg
69 assert state.router_shutdown is False 1abcdefg
70 assert state.sub_router_shutdown is False 1abcdefg
71 with TestClient(app) as client: 1abcdefg
72 assert state.app_startup is True 1abcdefg
73 assert state.router_startup is True 1abcdefg
74 assert state.sub_router_startup is True 1abcdefg
75 assert state.app_shutdown is False 1abcdefg
76 assert state.router_shutdown is False 1abcdefg
77 assert state.sub_router_shutdown is False 1abcdefg
78 response = client.get("/") 1abcdefg
79 assert response.status_code == 200, response.text 1abcdefg
80 assert response.json() == {"message": "Hello World"} 1abcdefg
81 assert state.app_startup is True 1abcdefg
82 assert state.router_startup is True 1abcdefg
83 assert state.sub_router_startup is True 1abcdefg
84 assert state.app_shutdown is True 1abcdefg
85 assert state.router_shutdown is True 1abcdefg
86 assert state.sub_router_shutdown is True 1abcdefg
89def test_app_lifespan_state(state: State) -> None: 1opqrstu
90 @asynccontextmanager 1vwxyzAB
91 async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: 1vwxyzAB
92 state.app_startup = True 1vwxyzAB
93 yield 1vwxyzAB
94 state.app_shutdown = True 1vwxyzAB
96 app = FastAPI(lifespan=lifespan) 1vwxyzAB
98 @app.get("/") 1vwxyzAB
99 def main() -> Dict[str, str]: 1vwxyzAB
100 return {"message": "Hello World"} 1vwxyzAB
102 assert state.app_startup is False 1vwxyzAB
103 assert state.app_shutdown is False 1vwxyzAB
104 with TestClient(app) as client: 1vwxyzAB
105 assert state.app_startup is True 1vwxyzAB
106 assert state.app_shutdown is False 1vwxyzAB
107 response = client.get("/") 1vwxyzAB
108 assert response.status_code == 200, response.text 1vwxyzAB
109 assert response.json() == {"message": "Hello World"} 1vwxyzAB
110 assert state.app_startup is True 1vwxyzAB
111 assert state.app_shutdown is True 1vwxyzAB
114def test_router_nested_lifespan_state(state: State) -> None: 1opqrstu
115 @asynccontextmanager 1hijklmn
116 async def lifespan(app: FastAPI) -> AsyncGenerator[Dict[str, bool], None]: 1hijklmn
117 state.app_startup = True 1hijklmn
118 yield {"app": True} 1hijklmn
119 state.app_shutdown = True 1hijklmn
121 @asynccontextmanager 1hijklmn
122 async def router_lifespan(app: FastAPI) -> AsyncGenerator[Dict[str, bool], None]: 1hijklmn
123 state.router_startup = True 1hijklmn
124 yield {"router": True} 1hijklmn
125 state.router_shutdown = True 1hijklmn
127 @asynccontextmanager 1hijklmn
128 async def subrouter_lifespan(app: FastAPI) -> AsyncGenerator[Dict[str, bool], None]: 1hijklmn
129 state.sub_router_startup = True 1hijklmn
130 yield {"sub_router": True} 1hijklmn
131 state.sub_router_shutdown = True 1hijklmn
133 sub_router = APIRouter(lifespan=subrouter_lifespan) 1hijklmn
135 router = APIRouter(lifespan=router_lifespan) 1hijklmn
136 router.include_router(sub_router) 1hijklmn
138 app = FastAPI(lifespan=lifespan) 1hijklmn
139 app.include_router(router) 1hijklmn
141 @app.get("/") 1hijklmn
142 def main(request: Request) -> Dict[str, str]: 1hijklmn
143 assert request.state.app 1hijklmn
144 assert request.state.router 1hijklmn
145 assert request.state.sub_router 1hijklmn
146 return {"message": "Hello World"} 1hijklmn
148 assert state.app_startup is False 1hijklmn
149 assert state.router_startup is False 1hijklmn
150 assert state.sub_router_startup is False 1hijklmn
151 assert state.app_shutdown is False 1hijklmn
152 assert state.router_shutdown is False 1hijklmn
153 assert state.sub_router_shutdown is False 1hijklmn
155 with TestClient(app) as client: 1hijklmn
156 assert state.app_startup is True 1hijklmn
157 assert state.router_startup is True 1hijklmn
158 assert state.sub_router_startup is True 1hijklmn
159 assert state.app_shutdown is False 1hijklmn
160 assert state.router_shutdown is False 1hijklmn
161 assert state.sub_router_shutdown is False 1hijklmn
162 response = client.get("/") 1hijklmn
163 assert response.status_code == 200, response.text 1hijklmn
164 assert response.json() == {"message": "Hello World"} 1hijklmn
166 assert state.app_startup is True 1hijklmn
167 assert state.router_startup is True 1hijklmn
168 assert state.sub_router_startup is True 1hijklmn
169 assert state.app_shutdown is True 1hijklmn
170 assert state.router_shutdown is True 1hijklmn
171 assert state.sub_router_shutdown is True 1hijklmn
174def test_router_nested_lifespan_state_overriding_by_parent() -> None: 1opqrstu
175 @asynccontextmanager 1JKLMNOP
176 async def lifespan( 1JKLMNOP
177 app: FastAPI,
178 ) -> AsyncGenerator[Dict[str, Union[str, bool]], None]:
179 yield { 1JKLMNOP
180 "app_specific": True,
181 "overridden": "app",
182 }
184 @asynccontextmanager 1JKLMNOP
185 async def router_lifespan( 1JKLMNOP
186 app: FastAPI,
187 ) -> AsyncGenerator[Dict[str, Union[str, bool]], None]:
188 yield { 1JKLMNOP
189 "router_specific": True,
190 "overridden": "router", # should override parent
191 }
193 router = APIRouter(lifespan=router_lifespan) 1JKLMNOP
194 app = FastAPI(lifespan=lifespan) 1JKLMNOP
195 app.include_router(router) 1JKLMNOP
197 with TestClient(app) as client: 1JKLMNOP
198 assert client.app_state == { 1JKLMNOP
199 "app_specific": True,
200 "router_specific": True,
201 "overridden": "app",
202 }
205def test_merged_no_return_lifespans_return_none() -> None: 1opqrstu
206 @asynccontextmanager 1QRSTUVW
207 async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: 1QRSTUVW
208 yield 1QRSTUVW
210 @asynccontextmanager 1QRSTUVW
211 async def router_lifespan(app: FastAPI) -> AsyncGenerator[None, None]: 1QRSTUVW
212 yield 1QRSTUVW
214 router = APIRouter(lifespan=router_lifespan) 1QRSTUVW
215 app = FastAPI(lifespan=lifespan) 1QRSTUVW
216 app.include_router(router) 1QRSTUVW
218 with TestClient(app) as client: 1QRSTUVW
219 assert not client.app_state 1QRSTUVW
222def test_merged_mixed_state_lifespans() -> None: 1opqrstu
223 @asynccontextmanager 1CDEFGHI
224 async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: 1CDEFGHI
225 yield 1CDEFGHI
227 @asynccontextmanager 1CDEFGHI
228 async def router_lifespan(app: FastAPI) -> AsyncGenerator[Dict[str, bool], None]: 1CDEFGHI
229 yield {"router": True} 1CDEFGHI
231 @asynccontextmanager 1CDEFGHI
232 async def sub_router_lifespan(app: FastAPI) -> AsyncGenerator[None, None]: 1CDEFGHI
233 yield 1CDEFGHI
235 sub_router = APIRouter(lifespan=sub_router_lifespan) 1CDEFGHI
236 router = APIRouter(lifespan=router_lifespan) 1CDEFGHI
237 app = FastAPI(lifespan=lifespan) 1CDEFGHI
238 router.include_router(sub_router) 1CDEFGHI
239 app.include_router(router) 1CDEFGHI
241 with TestClient(app) as client: 1CDEFGHI
242 assert client.app_state == {"router": True} 1CDEFGHI