44# pylint: disable=unused-variable
55
66import uuid
7+ import warnings
78from collections .abc import AsyncIterator , Awaitable , Callable , Iterator
89from pathlib import Path
910
3738 user_to_groups ,
3839 users ,
3940)
41+ from sqlalchemy .ext .asyncio import AsyncEngine , create_async_engine
4042
4143pytest_plugins = [
4244 "pytest_simcore.pytest_global_environs" ,
@@ -81,6 +83,30 @@ def _make(is_async=True) -> Awaitable[Engine] | sa.engine.base.Engine:
8183 return _make
8284
8385
86+ @pytest .fixture
87+ def make_asyncpg_engine (postgres_service : str ) -> Callable [[bool ], AsyncEngine ]:
88+ # NOTE: users is responsible of `await engine.dispose()`
89+ dsn = postgres_service .replace ("postgresql://" , "postgresql+asyncpg://" )
90+ minsize = 1
91+ maxsize = 50
92+
93+ def _ (echo : bool ):
94+ engine : AsyncEngine = create_async_engine (
95+ dsn ,
96+ pool_size = minsize ,
97+ max_overflow = maxsize - minsize ,
98+ connect_args = {
99+ "server_settings" : {"application_name" : "postgres_database_tests" }
100+ },
101+ pool_pre_ping = True , # https://docs.sqlalchemy.org/en/14/core/pooling.html#dealing-with-disconnects
102+ future = True , # this uses sqlalchemy 2.0 API, shall be removed when sqlalchemy 2.0 is released
103+ echo = echo ,
104+ )
105+ return engine
106+
107+ return _
108+
109+
84110def is_postgres_responsive (dsn ) -> bool :
85111 """Check if something responds to ``url``"""
86112 try :
@@ -107,6 +133,11 @@ def pg_sa_engine(
107133) -> Iterator [sa .engine .Engine ]:
108134 """
109135 Runs migration to create tables and return a sqlalchemy engine
136+
137+ NOTE: use this fixture to ensure pg db:
138+ - up,
139+ - responsive,
140+ - init (w/ tables) and/or migrated
110141 """
111142 # NOTE: Using migration to upgrade/downgrade is not
112143 # such a great idea since these tests are used while developing
@@ -142,29 +173,56 @@ def pg_sa_engine(
142173
143174
144175@pytest .fixture
145- async def pg_engine (
176+ async def aiopg_engine (
146177 pg_sa_engine : sa .engine .Engine , make_engine : Callable
147178) -> AsyncIterator [Engine ]:
148179 """
149180 Return an aiopg.sa engine connected to a responsive and migrated pg database
150181 """
151- async_engine = await make_engine (is_async = True )
152182
153- yield async_engine
183+ aiopg_sa_engine = await make_engine (is_async = True )
184+
185+ warnings .warn (
186+ "The 'aiopg_engine' is deprecated since we are replacing `aiopg` library by `sqlalchemy.ext.asyncio`."
187+ "SEE https://github.com/ITISFoundation/osparc-simcore/issues/4529. "
188+ "Please use 'asyncpg_engine' instead." ,
189+ DeprecationWarning ,
190+ stacklevel = 2 ,
191+ )
192+
193+ yield aiopg_sa_engine
154194
155195 # closes async-engine connections and terminates
156- async_engine .close ()
157- await async_engine .wait_closed ()
158- async_engine .terminate ()
196+ aiopg_sa_engine .close ()
197+ await aiopg_sa_engine .wait_closed ()
198+ aiopg_sa_engine .terminate ()
159199
160200
161201@pytest .fixture
162- async def connection (pg_engine : Engine ) -> AsyncIterator [SAConnection ]:
202+ async def connection (aiopg_engine : Engine ) -> AsyncIterator [SAConnection ]:
163203 """Returns an aiopg.sa connection from an engine to a fully furnished and ready pg database"""
164- async with pg_engine .acquire () as _conn :
204+ async with aiopg_engine .acquire () as _conn :
165205 yield _conn
166206
167207
208+ @pytest .fixture
209+ async def asyncpg_engine (
210+ is_pdb_enabled : bool ,
211+ pg_sa_engine : sa .engine .Engine ,
212+ make_asyncpg_engine : Callable [[bool ], AsyncEngine ],
213+ ) -> AsyncIterator [AsyncEngine ]:
214+
215+ assert (
216+ pg_sa_engine
217+ ), "Ensures pg db up, responsive, init (w/ tables) and/or migrated"
218+
219+ _apg_engine = make_asyncpg_engine (is_pdb_enabled )
220+
221+ yield _apg_engine
222+
223+ await _apg_engine .dispose ()
224+
225+
168226#
169227# FACTORY FIXTURES
170228#
@@ -240,7 +298,7 @@ async def _creator(conn, group: RowProxy | None = None, **overrides) -> RowProxy
240298
241299@pytest .fixture
242300async def create_fake_cluster (
243- pg_engine : Engine , faker : Faker
301+ aiopg_engine : Engine , faker : Faker
244302) -> AsyncIterator [Callable [..., Awaitable [int ]]]:
245303 cluster_ids = []
246304 assert cluster_to_groups is not None
@@ -254,7 +312,7 @@ async def _creator(**overrides) -> int:
254312 "authentication" : faker .pydict (value_types = [str ]),
255313 }
256314 insert_values .update (overrides )
257- async with pg_engine .acquire () as conn :
315+ async with aiopg_engine .acquire () as conn :
258316 cluster_id = await conn .scalar (
259317 clusters .insert ().values (** insert_values ).returning (clusters .c .id )
260318 )
@@ -265,13 +323,13 @@ async def _creator(**overrides) -> int:
265323 yield _creator
266324
267325 # cleanup
268- async with pg_engine .acquire () as conn :
326+ async with aiopg_engine .acquire () as conn :
269327 await conn .execute (clusters .delete ().where (clusters .c .id .in_ (cluster_ids )))
270328
271329
272330@pytest .fixture
273331async def create_fake_project (
274- pg_engine : Engine ,
332+ aiopg_engine : Engine ,
275333) -> AsyncIterator [Callable [..., Awaitable [RowProxy ]]]:
276334 created_project_uuids = []
277335
@@ -288,7 +346,7 @@ async def _creator(conn, user: RowProxy, **overrides) -> RowProxy:
288346
289347 yield _creator
290348
291- async with pg_engine .acquire () as conn :
349+ async with aiopg_engine .acquire () as conn :
292350 await conn .execute (
293351 projects .delete ().where (projects .c .uuid .in_ (created_project_uuids ))
294352 )
0 commit comments