Skip to content

Commit 2a56df1

Browse files
committed
✨ Refactor: Ensure async engine disposal in setup_postgres_database with error handling and update test structure
1 parent aa6c7d8 commit 2a56df1

File tree

2 files changed

+60
-15
lines changed

2 files changed

+60
-15
lines changed

packages/service-library/src/servicelib/fastapi/postgres_lifespan.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ async def setup_postgres_database(_, state: State) -> AsyncIterator[State]:
3434
pg_settings
3535
)
3636

37-
yield {PostgresLifespanStateKeys.POSTGRES_ASYNC_ENGINE: async_engine}
38-
39-
with log_catch(_logger, reraise=False):
40-
await async_engine.dispose()
37+
try:
38+
yield {
39+
PostgresLifespanStateKeys.POSTGRES_ASYNC_ENGINE: async_engine,
40+
}
41+
42+
finally:
43+
with log_catch(_logger, reraise=False):
44+
await async_engine.dispose()

packages/service-library/tests/fastapi/test_postgres_lifespan.py

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ def app_environment(monkeypatch: pytest.MonkeyPatch) -> EnvVarsDict:
4040
)
4141

4242

43-
async def test_setup_postgres_database_in_an_app(
44-
is_pdb_enabled: bool,
43+
@pytest.fixture
44+
def app_lifespan(
4545
app_environment: EnvVarsDict,
4646
mock_create_async_engine_and_database_ready: MockType,
47-
):
47+
) -> LifespanManager:
4848
assert app_environment
4949

5050
class AppSettings(BaseApplicationSettings):
@@ -60,24 +60,28 @@ async def my_app_settings(app: FastAPI) -> AsyncIterator[State]:
6060
PostgresLifespanStateKeys.POSTGRES_SETTINGS: app.state.settings.CATALOG_POSTGRES
6161
}
6262

63-
async def my_db_setup(app: FastAPI, state: State) -> AsyncIterator[State]:
63+
async def my_database_setup(app: FastAPI, state: State) -> AsyncIterator[State]:
6464
app.state.my_db_engine = state[PostgresLifespanStateKeys.POSTGRES_ASYNC_ENGINE]
6565

66-
assert (
67-
app.state.my_db_engine
68-
== mock_create_async_engine_and_database_ready.return_value
69-
)
70-
7166
yield {}
7267

7368
# compose lifespans
7469
app_lifespan = LifespanManager()
7570
app_lifespan.add(my_app_settings)
7671

77-
postgres_lifespan.add(my_db_setup)
72+
postgres_lifespan.add(my_database_setup)
7873
app_lifespan.include(postgres_lifespan)
7974

80-
# define app
75+
return app_lifespan
76+
77+
78+
async def test_setup_postgres_database_in_an_app(
79+
is_pdb_enabled: bool,
80+
app_environment: EnvVarsDict,
81+
mock_create_async_engine_and_database_ready: MockType,
82+
app_lifespan: LifespanManager,
83+
):
84+
8185
app = FastAPI(lifespan=app_lifespan)
8286

8387
async with ASGILifespanManager(
@@ -103,6 +107,43 @@ async def my_db_setup(app: FastAPI, state: State) -> AsyncIterator[State]:
103107
]
104108
)
105109

110+
assert (
111+
app.state.my_db_engine
112+
== mock_create_async_engine_and_database_ready.return_value
113+
)
114+
106115
# Verify that the engine was disposed
107116
async_engine: Any = mock_create_async_engine_and_database_ready.return_value
108117
async_engine.dispose.assert_called_once()
118+
119+
120+
async def test_setup_postgres_database_dispose_engine_on_failure(
121+
is_pdb_enabled: bool,
122+
app_environment: EnvVarsDict,
123+
mock_create_async_engine_and_database_ready: MockType,
124+
app_lifespan: LifespanManager,
125+
):
126+
expected_msg = "my_faulty_setup error"
127+
128+
def raise_error():
129+
raise RuntimeError(expected_msg)
130+
131+
@app_lifespan.add
132+
async def my_faulty_setup(app: FastAPI, state: State) -> AsyncIterator[State]:
133+
assert PostgresLifespanStateKeys.POSTGRES_ASYNC_ENGINE in state
134+
raise_error()
135+
yield {}
136+
137+
app = FastAPI(lifespan=app_lifespan)
138+
139+
with pytest.raises(RuntimeError, match=expected_msg):
140+
async with ASGILifespanManager(
141+
app,
142+
startup_timeout=None if is_pdb_enabled else 10,
143+
shutdown_timeout=None if is_pdb_enabled else 10,
144+
):
145+
...
146+
147+
# Verify that the engine was disposed even if error happend
148+
async_engine: Any = mock_create_async_engine_and_database_ready.return_value
149+
async_engine.dispose.assert_called_once()

0 commit comments

Comments
 (0)