From f65e16ff4e7122aa15dc4efbeef616fc457877ef Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Mon, 24 Aug 2026 20:13:09 +0800 Subject: [PATCH] fix(testing): configure shared plugin test runtime (#6438) --- app/testing/bootstrap.py | 22 ++++++++- .../architecture/dependency-baseline.json | 8 +++- tests/test_testing_bootstrap_transactions.py | 45 +++++++++++++++++++ 3 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 tests/test_testing_bootstrap_transactions.py diff --git a/app/testing/bootstrap.py b/app/testing/bootstrap.py index df2c501b4..b23bbfd3e 100644 --- a/app/testing/bootstrap.py +++ b/app/testing/bootstrap.py @@ -181,7 +181,7 @@ def ensure_optional_stub(name: str, **attrs) -> None: def prepare_backend() -> None: - """隔离 CONFIG_DIR、补 sites 垫片、建表并加载配置快照。 + """隔离 CONFIG_DIR、补 sites 垫片、建表并装配测试数据库能力。 主程序中后端即当前包;插件仓由其 ``tests/_bootstrap.py`` shim 在 import 本模块前 先把后端目录注入 ``sys.path``。顺序固定:先隔离 CONFIG_DIR,再补 ``app.application.site.sites`` 垫片, @@ -192,9 +192,27 @@ def prepare_backend() -> None: ensure_sites_stub() from app.startup.initializers.database import init_db init_db() + from app.db.adapters.transaction import TransactionalWriteRunner + from app.db.session import SessionFactory, async_session_scope + from app.db.uow import configure_transaction_runners + + transaction_runner = TransactionalWriteRunner( + sync_session=SessionFactory, + async_session=async_session_scope, + ) + configure_transaction_runners( + sync=transaction_runner.sync, + async_=transaction_runner.async_, + ) + from app.application.service import configure_service_directory + + # 共享引导不启动真实服务模块;需要具体服务实例的测试应在自身边界显式覆盖该目录。 + configure_service_directory( + configs=lambda _config_key, _conf_type: [], + modules=lambda _module_type: [], + ) from app.db.oper.systemconfig import SystemConfigOper from app.db.oper.userconfig import UserConfigOper - from app.db.session import SessionFactory with SessionFactory() as session: SystemConfigOper().load_snapshot(session) diff --git a/tests/fixtures/architecture/dependency-baseline.json b/tests/fixtures/architecture/dependency-baseline.json index 1144ddcd3..6dc6d0fa8 100644 --- a/tests/fixtures/architecture/dependency-baseline.json +++ b/tests/fixtures/architecture/dependency-baseline.json @@ -13,8 +13,8 @@ "runtime_to_db": [], "workflow_to_db": [] }, - "edge_count": 6595, - "edge_sha256": "b0709f6e54bf046386d5df81c54a3e65889f18473b8cc91bbe7c97010bda02dc", + "edge_count": 6599, + "edge_sha256": "33b44ea7d1f05a4c76fd22c4c1b798689ac8e580099c50141fbd91345a625714", "edges": [ "app -> app.runtime", "app -> app.runtime.compat", @@ -6425,12 +6425,16 @@ "app.startup.lifecycle -> app.startup.lifecycle.components", "app.testing -> app.testing.stub", "app.testing.bootstrap -> app.application", + "app.testing.bootstrap -> app.application.service", "app.testing.bootstrap -> app.application.site", "app.testing.bootstrap -> app.db", + "app.testing.bootstrap -> app.db.adapters", + "app.testing.bootstrap -> app.db.adapters.transaction", "app.testing.bootstrap -> app.db.oper", "app.testing.bootstrap -> app.db.oper.systemconfig", "app.testing.bootstrap -> app.db.oper.userconfig", "app.testing.bootstrap -> app.db.session", + "app.testing.bootstrap -> app.db.uow", "app.testing.bootstrap -> app.startup", "app.testing.bootstrap -> app.startup.initializers", "app.testing.bootstrap -> app.startup.initializers.cache", diff --git a/tests/test_testing_bootstrap_transactions.py b/tests/test_testing_bootstrap_transactions.py new file mode 100644 index 000000000..3dd29fb8e --- /dev/null +++ b/tests/test_testing_bootstrap_transactions.py @@ -0,0 +1,45 @@ +"""共享测试引导的数据库事务能力回归测试。""" + +import pytest +from sqlalchemy import text + +from app.application import service +from app.db import uow +from app.schemas.types import SystemConfigKey +from app.testing.bootstrap import prepare_backend + + +def test_prepare_backend_configures_sync_transaction_runner(monkeypatch): + """插件仓只调用共享引导时,无 Session Oper 仍应获得隔离事务执行器。""" + monkeypatch.setattr(uow, "_sync_transaction_runner", None) + + prepare_backend() + + result = uow.run_sync_transaction( + lambda session: session.execute(text("SELECT 1")).scalar_one() + ) + assert result == 1 + + +@pytest.mark.asyncio +async def test_prepare_backend_configures_async_transaction_runner(monkeypatch): + """共享引导同时提供异步短事务,保持与生产组合根的装配契约一致。""" + monkeypatch.setattr(uow, "_async_transaction_runner", None) + + prepare_backend() + + async def select_one(session): + """在共享引导创建的隔离异步会话中执行最小查询。""" + return (await session.execute(text("SELECT 1"))).scalar_one() + + assert await uow.run_async_transaction(select_one) == 1 + + +def test_prepare_backend_configures_empty_service_directory(monkeypatch): + """未启动真实模块时,插件通知查询应得到确定性的空服务配置。""" + monkeypatch.setattr(service, "_config_loader", service._unconfigured_configs) + monkeypatch.setattr(service, "_module_loader", service._unconfigured_modules) + + prepare_backend() + + assert service.get_service_configs(SystemConfigKey.Notifications, object) == []