diff --git a/providers/fab/tests/unit/fab/auth_manager/conftest.py b/providers/fab/tests/unit/fab/auth_manager/conftest.py index 770999969c020..f8a13ec05889f 100644 --- a/providers/fab/tests/unit/fab/auth_manager/conftest.py +++ b/providers/fab/tests/unit/fab/auth_manager/conftest.py @@ -51,7 +51,15 @@ def factory(): _app.config["AUTH_ROLE_PUBLIC"] = None return _app - return factory() + flask_app = factory() + try: + yield flask_app + finally: + # Dispose the flask_sqlalchemy per-app engine so its pooled connections + # don't survive the session in long CI runs. + with flask_app.app_context(): + for fab_engine in flask_app.extensions["sqlalchemy"].engines.values(): + fab_engine.dispose() @pytest.fixture diff --git a/providers/fab/tests/unit/fab/auth_manager/test_security.py b/providers/fab/tests/unit/fab/auth_manager/test_security.py index 6252b08284a5a..389c054e11be7 100644 --- a/providers/fab/tests/unit/fab/auth_manager/test_security.py +++ b/providers/fab/tests/unit/fab/auth_manager/test_security.py @@ -235,8 +235,15 @@ def app(): ): _app = application.create_app(enable_plugins=False) _app.config["WTF_CSRF_ENABLED"] = False - with _app.app_context(): - yield _app + try: + with _app.app_context(): + yield _app + finally: + # Dispose the flask_sqlalchemy per-app engine so its pooled + # connections don't survive the module in long CI runs. + with _app.app_context(): + for fab_engine in _app.extensions["sqlalchemy"].engines.values(): + fab_engine.dispose() @pytest.fixture(scope="module") diff --git a/providers/fab/tests/unit/fab/www/test_auth.py b/providers/fab/tests/unit/fab/www/test_auth.py index 7400ff2bf14f6..51e002e0ca91d 100644 --- a/providers/fab/tests/unit/fab/www/test_auth.py +++ b/providers/fab/tests/unit/fab/www/test_auth.py @@ -41,7 +41,16 @@ def app(): ): "airflow.providers.fab.auth_manager.fab_auth_manager.FabAuthManager", } ): - yield application.create_app(enable_plugins=False) + flask_app = application.create_app(enable_plugins=False) + try: + yield flask_app + finally: + # flask_sqlalchemy creates a per-app engine in init_app(); a + # function-scoped app fixture without disposal would leak that + # engine's connection pool every test. + with flask_app.app_context(): + for fab_engine in flask_app.extensions["sqlalchemy"].engines.values(): + fab_engine.dispose() @pytest.mark.parametrize( diff --git a/providers/fab/tests/unit/fab/www/views/conftest.py b/providers/fab/tests/unit/fab/www/views/conftest.py index 94f7bbc776155..3c6e047deb994 100644 --- a/providers/fab/tests/unit/fab/www/views/conftest.py +++ b/providers/fab/tests/unit/fab/www/views/conftest.py @@ -34,7 +34,10 @@ @pytest.fixture(autouse=True, scope="module") def session(): - settings.configure_orm() + # reconfigure_orm() disposes any pre-existing engine before creating a new + # one. Plain configure_orm() leaks the previous engine's connection pool, + # which exhausts Postgres max_connections in long parallel CI runs. + settings.reconfigure_orm() return settings.Session @@ -102,6 +105,12 @@ def factory(): for user_dict in test_users: delete_user(app, user_dict["username"]) + # flask_sqlalchemy creates a per-app engine in init_app(); without disposal + # its pooled connections survive the fixture and accumulate across modules + # in long CI runs. + with app.app_context(): + for fab_engine in app.extensions["sqlalchemy"].engines.values(): + fab_engine.dispose() @pytest.fixture diff --git a/providers/fab/tests/unit/fab/www/views/test_views_custom_user_views.py b/providers/fab/tests/unit/fab/www/views/test_views_custom_user_views.py index 524633ea906c7..7e61cdff785ee 100644 --- a/providers/fab/tests/unit/fab/www/views/test_views_custom_user_views.py +++ b/providers/fab/tests/unit/fab/www/views/test_views_custom_user_views.py @@ -79,7 +79,15 @@ def app(): ): app = application.create_app(enable_plugins=False) app.config["WTF_CSRF_ENABLED"] = False - yield app + try: + yield app + finally: + # flask_sqlalchemy creates a per-app engine in init_app(); a + # function-scoped app fixture without disposal would leak that + # engine's connection pool every test. + with app.app_context(): + for fab_engine in app.extensions["sqlalchemy"].engines.values(): + fab_engine.dispose() @pytest.fixture