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