ForcePilot/backend/test/unit/channels/infrastructure/test_factory.py

679 lines
27 KiB
Python
Raw Normal View History

"""yuxi.channels.infrastructure.factory 单元测试。
覆盖以下纯函数 / 装配辅助函数
- ``_register_default_match_tiers``注册 8 个默认层级 + 6 个内置 matcher
- ``_construct_health_dependencies``构造健康检查子依赖五元组
- ``_register_builtin_event_subscribers``注册内置事件订阅者
- ``_register_di_singletons``注册全量 DI 单例到容器
``create_host_bootstrap`` / ``create_host_shutdown`` / ``load_channel_plugins``
依赖 Redis / ARQ / DB 等外部资源不属于单元测试范畴本文件不覆盖
不依赖运行中的 Docker 服务纯单元测试
"""
from __future__ import annotations
from unittest.mock import MagicMock
import pytest
from yuxi.channels.application.circuit_breaker.channel_circuit_breaker import (
ChannelCircuitBreaker,
)
from yuxi.channels.application.extension.event_bus import EventBus
from yuxi.channels.application.health.channel_probe import ChannelProbe
from yuxi.channels.application.health.diagnostics_exporter import (
DiagnosticsExporter,
)
from yuxi.channels.application.health.health_aggregator import HealthAggregator
from yuxi.channels.application.lifecycle.plugin_lifecycle_manager import (
PluginLifecycleManager,
)
from yuxi.channels.application.lifecycle.plugin_loader import PluginLoader
from yuxi.channels.application.pipeline.stage_slot_injector import StageSlotInjector
from yuxi.channels.application.transport import TransportManager
from yuxi.channels.adapters.content_review_repository_adapter import (
ContentReviewRepositoryAdapter,
)
from yuxi.channels.adapters.default_content_moderation_adapter import (
DefaultContentModerationAdapter,
)
from yuxi.channels.adapters.inmemory_tracer_adapter import (
InMemoryTracerAdapter,
)
from yuxi.channels.adapters.redis_config_adapter import RedisConfigAdapter
from yuxi.channels.application.extension.channel_event_broadcaster import (
ChannelEventBroadcaster,
)
from yuxi.channels.application.extension.config_source_registry import (
ConfigSourceRegistry,
)
from yuxi.channels.application.extension.event_subscription_registry import (
EventSubscriptionRegistry,
)
from yuxi.channels.application.extension.stage_slot_registry import StageSlotRegistry
from yuxi.channels.application.lifecycle.config_scope_registry import (
ConfigScopeRegistry,
)
from yuxi.channels.application.lifecycle.sensitive_field_registry import (
SensitiveFieldRegistry,
)
from yuxi.channels.application.lifecycle.plugin_dependency_resolver import (
PluginDependencyResolver,
)
from yuxi.channels.contract.dtos.outbox import OutboxConfig
from yuxi.channels.contract.ports.driven.cache_port import CachePort
from yuxi.channels.contract.ports.driven.config_port import ConfigPort
from yuxi.channels.contract.ports.driven.event_publisher_port import (
EventPublisherPort,
)
from yuxi.channels.contract.ports.driven.logger_port import LoggerPort
from yuxi.channels.contract.ports.driven.persistence_port import PersistencePort
from yuxi.channels.contract.ports.driven.queue_port import QueuePort
from yuxi.channels.contract.ports.driven.content_review_repository_port import (
ContentReviewRepositoryPort,
)
from yuxi.channels.contract.ports.driven.agent_run_execution_port import (
AgentRunExecutionPort,
)
from yuxi.channels.core.registry.capability_registry import CapabilityRegistry
from yuxi.channels.core.registry.command_registry import CommandRegistryTable
from yuxi.channels.core.registry.identity_resolver_registry import (
IdentityResolverRegistry,
)
from yuxi.channels.core.registry.plugin_registry import PluginRegistry
from yuxi.channels.core.registry.route_match_registry import RouteMatchRegistry
from yuxi.channels.core.registry.whitelist_registry import WhitelistRegistry
from yuxi.channels.core.service.degradation_manager import DegradationManager
from yuxi.channels.infrastructure.dependency_injection import (
DependencyInjectionContainer,
)
from yuxi.channels.infrastructure.factory import (
_PluginLoadingCore,
_construct_health_dependencies,
_register_builtin_event_subscribers,
_register_default_match_tiers,
_register_di_singletons,
)
pytestmark = pytest.mark.unit
# ---------------------------------------------------------------------------
# _register_default_match_tiers
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestRegisterDefaultMatchTiers:
"""``_register_default_match_tiers`` 注册 8 个默认层级 + 6 个内置 matcher。"""
def test_registers_eight_default_tiers(self, fake_logger):
# Arrange
registry = RouteMatchRegistry()
# Act
_register_default_match_tiers(registry)
# Assert: 8 个默认层级
tiers = registry.listTiers()
tier_names = [t.name for t in tiers]
expected_names = [
"session_key",
"identity_id",
"peer_id",
"chat_type",
"channel_session",
"channel_type",
"account",
"default",
]
assert sorted(tier_names) == sorted(expected_names)
assert len(tiers) == 8
def test_default_tier_priorities_descending_for_route_resolver(self, fake_logger):
"""优先级数值由高到低session_key(800) → default(100)。"""
# Arrange
registry = RouteMatchRegistry()
# Act
_register_default_match_tiers(registry)
# Assert
tiers_by_name = {t.name: t.priority for t in registry.listTiers()}
assert tiers_by_name["session_key"] == 800
assert tiers_by_name["identity_id"] == 700
assert tiers_by_name["peer_id"] == 600
assert tiers_by_name["chat_type"] == 500
assert tiers_by_name["channel_session"] == 400
assert tiers_by_name["channel_type"] == 300
assert tiers_by_name["account"] == 200
assert tiers_by_name["default"] == 100
def test_registers_six_builtin_matchers(self, fake_logger):
"""注册 6 个内置 matchersession_key/identity_id/peer_id/chat_type/account/default
channel_session / channel_type 两层无内置 matcher由插件可注入
"""
# Arrange
registry = RouteMatchRegistry()
# Act
_register_default_match_tiers(registry)
# Assert: 通过尝试注册同名的 matcher 不抛错来间接验证已注册
# registerMatcher 内部允许覆盖)。这里仅断言 6 个 matcher 名称
# 可以重新注册(覆盖),不会因为缺少对应层级而失败。
builtin_matcher_names = [
"session_key",
"identity_id",
"peer_id",
"chat_type",
"account",
"default",
]
for name in builtin_matcher_names:
# registerMatcher 内部按名查找 tier未注册时会抛 ValidationError
# 这里重新注册同名 matcher 不抛错即证明对应 tier 与原 matcher 已存在
registry.registerMatcher(name, lambda ctx: None)
# ---------------------------------------------------------------------------
# _construct_health_dependencies
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestConstructHealthDependencies:
"""``_construct_health_dependencies`` 构造健康检查子依赖五元组。"""
def test_returns_five_tuple_with_expected_types(self, fake_logger):
# Arrange
plugin_registry = MagicMock(spec=PluginRegistry)
persistence_port = MagicMock(spec=PersistencePort)
cache_port = MagicMock(spec=CachePort)
queue_port = MagicMock(spec=QueuePort)
capability_registry = MagicMock(spec=CapabilityRegistry)
event_publisher = MagicMock(spec=EventBus)
redis_client = MagicMock()
# Act
(
config_port,
channel_circuit_breaker,
channel_probe,
tracer_port,
diagnostics_exporter,
) = _construct_health_dependencies(
plugin_registry=plugin_registry,
persistence_port=persistence_port,
cache_port=cache_port,
queue_port=queue_port,
capability_registry=capability_registry,
event_publisher=event_publisher,
redis_client=redis_client,
logger=fake_logger,
)
# Assert
assert isinstance(config_port, RedisConfigAdapter)
assert isinstance(channel_circuit_breaker, ChannelCircuitBreaker)
assert isinstance(channel_probe, ChannelProbe)
assert isinstance(tracer_port, InMemoryTracerAdapter)
assert isinstance(diagnostics_exporter, DiagnosticsExporter)
def test_key_to_scope_map_propagated_to_config_port(self, fake_logger):
"""key_to_scope_map 透传到 RedisConfigAdapterF-02"""
# Arrange
from yuxi.channels.contract.dtos.config import ConfigScope
key_to_scope_map = {"channel.test_key": ConfigScope.CHANNEL}
# Act
(
config_port,
*_rest,
) = _construct_health_dependencies(
plugin_registry=MagicMock(spec=PluginRegistry),
persistence_port=MagicMock(spec=PersistencePort),
cache_port=MagicMock(spec=CachePort),
queue_port=MagicMock(spec=QueuePort),
capability_registry=MagicMock(spec=CapabilityRegistry),
event_publisher=MagicMock(spec=EventBus),
redis_client=MagicMock(),
logger=fake_logger,
key_to_scope_map=key_to_scope_map,
)
# Assert: RedisConfigAdapter 持有 key_to_scope_map 视图
assert config_port._key_to_scope_map is key_to_scope_map
def test_key_to_scope_map_none_does_not_raise(self, fake_logger):
"""key_to_scope_map=None 时正常构造(向后兼容)。"""
# Act
result = _construct_health_dependencies(
plugin_registry=MagicMock(spec=PluginRegistry),
persistence_port=MagicMock(spec=PersistencePort),
cache_port=MagicMock(spec=CachePort),
queue_port=MagicMock(spec=QueuePort),
capability_registry=MagicMock(spec=CapabilityRegistry),
event_publisher=MagicMock(spec=EventBus),
redis_client=MagicMock(),
logger=fake_logger,
key_to_scope_map=None,
)
# Assert
assert len(result) == 5
assert isinstance(result[0], RedisConfigAdapter)
# ---------------------------------------------------------------------------
# _register_builtin_event_subscribers
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestRegisterBuiltinEventSubscribers:
"""``_register_builtin_event_subscribers`` 注册内置事件订阅。"""
def _make_event_bus(self, fake_logger) -> EventBus:
"""构造真实 EventBus 实例(避免 mock 丢失内部状态)。"""
degradation_manager = MagicMock(spec=DegradationManager)
return EventBus(logger=fake_logger, degradation_manager=degradation_manager)
def test_registers_outbox_state_changed_subscriber(self, fake_logger):
# Arrange
event_bus = self._make_event_bus(fake_logger)
# Act
_register_builtin_event_subscribers(
event_bus=event_bus,
persistence_port=MagicMock(spec=PersistencePort),
queue_port=MagicMock(spec=QueuePort),
whitelist_registry=MagicMock(spec=WhitelistRegistry),
cache_port=MagicMock(spec=CachePort),
plugin_registry=MagicMock(spec=PluginRegistry),
logger=fake_logger,
)
# Assert
assert "OutboxStateChanged" in event_bus._subscriptions
assert len(event_bus._subscriptions["OutboxStateChanged"]) == 1
def test_registers_eight_plugin_lifecycle_event_subscribers(self, fake_logger):
"""8 类插件生命周期事件共用同一 PluginLifecycleAuditHandler 实例。"""
# Arrange
event_bus = self._make_event_bus(fake_logger)
plugin_event_types = (
"PluginDiscovered",
"PluginLoaded",
"PluginStarted",
"PluginPaused",
"PluginResumed",
"PluginStopped",
"PluginUnloaded",
"PluginFailed",
)
# Act
_register_builtin_event_subscribers(
event_bus=event_bus,
persistence_port=MagicMock(spec=PersistencePort),
queue_port=MagicMock(spec=QueuePort),
whitelist_registry=MagicMock(spec=WhitelistRegistry),
cache_port=MagicMock(spec=CachePort),
plugin_registry=MagicMock(spec=PluginRegistry),
logger=fake_logger,
)
# Assert
for event_type in plugin_event_types:
assert event_type in event_bus._subscriptions
assert len(event_bus._subscriptions[event_type]) == 1
def test_registers_config_event_pair_for_whitelist_and_route_match(self, fake_logger):
"""ConfigChanged/ConfigRollback 各注册 WhitelistConfigHandler +
RouteMatchCacheHandler 2 个订阅"""
# Arrange
event_bus = self._make_event_bus(fake_logger)
# Act
_register_builtin_event_subscribers(
event_bus=event_bus,
persistence_port=MagicMock(spec=PersistencePort),
queue_port=MagicMock(spec=QueuePort),
whitelist_registry=MagicMock(spec=WhitelistRegistry),
cache_port=MagicMock(spec=CachePort),
plugin_registry=MagicMock(spec=PluginRegistry),
logger=fake_logger,
)
# Assert
for config_event in ("ConfigChanged", "ConfigRollback"):
assert config_event in event_bus._subscriptions
assert len(event_bus._subscriptions[config_event]) == 2
def test_registers_pairing_approved_subscriber(self, fake_logger):
# Arrange
event_bus = self._make_event_bus(fake_logger)
# Act
_register_builtin_event_subscribers(
event_bus=event_bus,
persistence_port=MagicMock(spec=PersistencePort),
queue_port=MagicMock(spec=QueuePort),
whitelist_registry=MagicMock(spec=WhitelistRegistry),
cache_port=MagicMock(spec=CachePort),
plugin_registry=MagicMock(spec=PluginRegistry),
logger=fake_logger,
)
# Assert
assert "PairingApproved" in event_bus._subscriptions
assert len(event_bus._subscriptions["PairingApproved"]) == 1
def test_registers_channel_degraded_and_recovered_subscribers(self, fake_logger):
# Arrange
event_bus = self._make_event_bus(fake_logger)
# Act
_register_builtin_event_subscribers(
event_bus=event_bus,
persistence_port=MagicMock(spec=PersistencePort),
queue_port=MagicMock(spec=QueuePort),
whitelist_registry=MagicMock(spec=WhitelistRegistry),
cache_port=MagicMock(spec=CachePort),
plugin_registry=MagicMock(spec=PluginRegistry),
logger=fake_logger,
)
# Assert
assert len(event_bus._subscriptions["ChannelDegraded"]) == 1
assert len(event_bus._subscriptions["ChannelRecovered"]) == 1
def test_registers_three_channel_event_broadcaster_subscribers(self, fake_logger):
"""ChannelSessionUpdated/ChannelMessageReceived/ChannelMessageSent → 广播器。"""
# Arrange
event_bus = self._make_event_bus(fake_logger)
# Act
_register_builtin_event_subscribers(
event_bus=event_bus,
persistence_port=MagicMock(spec=PersistencePort),
queue_port=MagicMock(spec=QueuePort),
whitelist_registry=MagicMock(spec=WhitelistRegistry),
cache_port=MagicMock(spec=CachePort),
plugin_registry=MagicMock(spec=PluginRegistry),
logger=fake_logger,
)
# Assert
for channel_event in (
"ChannelSessionUpdated",
"ChannelMessageReceived",
"ChannelMessageSent",
):
assert channel_event in event_bus._subscriptions
assert len(event_bus._subscriptions[channel_event]) == 1
def test_di_container_register_broadcaster_when_provided(self, fake_logger):
"""di_container 提供时注册 ChannelEventBroadcaster 单例。"""
# Arrange
event_bus = self._make_event_bus(fake_logger)
di_container = DependencyInjectionContainer()
# Act
_register_builtin_event_subscribers(
event_bus=event_bus,
persistence_port=MagicMock(spec=PersistencePort),
queue_port=MagicMock(spec=QueuePort),
whitelist_registry=MagicMock(spec=WhitelistRegistry),
cache_port=MagicMock(spec=CachePort),
plugin_registry=MagicMock(spec=PluginRegistry),
logger=fake_logger,
di_container=di_container,
)
# Assert: ChannelEventBroadcaster 单例可解析
broadcaster = di_container.resolve(ChannelEventBroadcaster)
assert isinstance(broadcaster, ChannelEventBroadcaster)
def test_di_container_none_does_not_raise(self, fake_logger):
"""di_container=None 时不抛错(仅不注册 broadcaster 单例)。"""
# Arrange
event_bus = self._make_event_bus(fake_logger)
# Act / Assert: 不抛异常
_register_builtin_event_subscribers(
event_bus=event_bus,
persistence_port=MagicMock(spec=PersistencePort),
queue_port=MagicMock(spec=QueuePort),
whitelist_registry=MagicMock(spec=WhitelistRegistry),
cache_port=MagicMock(spec=CachePort),
plugin_registry=MagicMock(spec=PluginRegistry),
logger=fake_logger,
di_container=None,
)
# ---------------------------------------------------------------------------
# _register_di_singletons
# ---------------------------------------------------------------------------
def _make_plugin_loading_core() -> _PluginLoadingCore:
"""构造 _PluginLoadingCore 桩,所有字段使用 MagicMock。"""
return _PluginLoadingCore(
plugin_registry=MagicMock(spec=PluginRegistry),
stage_slot_registry=MagicMock(spec=StageSlotRegistry),
event_subscription_registry=MagicMock(spec=EventSubscriptionRegistry),
config_source_registry=MagicMock(spec=ConfigSourceRegistry),
route_match_registry=MagicMock(spec=RouteMatchRegistry),
whitelist_registry=MagicMock(spec=WhitelistRegistry),
capability_registry=MagicMock(spec=CapabilityRegistry),
event_bus=MagicMock(spec=EventBus),
cache_port=MagicMock(spec=CachePort),
degradation_manager=MagicMock(spec=DegradationManager),
plugin_lifecycle_manager=MagicMock(spec=PluginLifecycleManager),
plugin_dependency_resolver=MagicMock(spec=PluginDependencyResolver),
plugin_loader=MagicMock(spec=PluginLoader),
outbox_config=OutboxConfig(
ttl_seconds=3600,
retry_backoff_schedule=(60,),
max_retry=3,
),
redis_client=MagicMock(),
arq_pool=MagicMock(),
execution_port_impl=MagicMock(),
sensitive_registry=MagicMock(spec=SensitiveFieldRegistry),
config_scope_registry=MagicMock(spec=ConfigScopeRegistry),
)
@pytest.mark.unit
class TestRegisterDiSingletons:
"""``_register_di_singletons`` 注册全量 DI 单例到容器。"""
def test_registers_core_registries_and_shared_dependencies(self, fake_logger):
# Arrange
di = DependencyInjectionContainer()
core = _make_plugin_loading_core()
# Act
_register_di_singletons(
di_container=di,
core=core,
persistence_port=MagicMock(spec=PersistencePort),
session_factory=MagicMock(),
queue_port=MagicMock(spec=QueuePort),
config_port=MagicMock(spec=ConfigPort),
stage_slot_injector=MagicMock(spec=StageSlotInjector),
health_aggregator=MagicMock(spec=HealthAggregator),
channel_circuit_breaker=MagicMock(spec=ChannelCircuitBreaker),
transport_manager=MagicMock(spec=TransportManager),
logger=fake_logger,
)
# Assert: 注册中心与共享依赖
assert di.resolve(PluginRegistry) is core.plugin_registry
assert di.resolve(CapabilityRegistry) is core.capability_registry
assert di.resolve(RouteMatchRegistry) is core.route_match_registry
assert di.resolve(WhitelistRegistry) is core.whitelist_registry
assert di.resolve(EventBus) is core.event_bus
assert di.resolve(EventPublisherPort) is core.event_bus
assert di.resolve(StageSlotRegistry) is core.stage_slot_registry
assert di.resolve(EventSubscriptionRegistry) is core.event_subscription_registry
assert di.resolve(ConfigSourceRegistry) is core.config_source_registry
assert di.resolve(PluginLifecycleManager) is core.plugin_lifecycle_manager
assert di.resolve(PluginDependencyResolver) is core.plugin_dependency_resolver
assert di.resolve(PluginLoader) is core.plugin_loader
assert di.resolve(SensitiveFieldRegistry) is core.sensitive_registry
assert di.resolve(ConfigScopeRegistry) is core.config_scope_registry
def test_registers_command_and_identity_resolver_registries(self, fake_logger):
"""命令注册表与身份解析器注册表由本函数构造并注册。"""
# Arrange
di = DependencyInjectionContainer()
core = _make_plugin_loading_core()
# Act
_register_di_singletons(
di_container=di,
core=core,
persistence_port=MagicMock(spec=PersistencePort),
session_factory=MagicMock(),
queue_port=MagicMock(spec=QueuePort),
config_port=MagicMock(spec=ConfigPort),
stage_slot_injector=MagicMock(spec=StageSlotInjector),
health_aggregator=MagicMock(spec=HealthAggregator),
channel_circuit_breaker=MagicMock(spec=ChannelCircuitBreaker),
transport_manager=MagicMock(spec=TransportManager),
logger=fake_logger,
)
# Assert
cmd_registry = di.resolve(CommandRegistryTable)
assert isinstance(cmd_registry, CommandRegistryTable)
identity_registry = di.resolve(IdentityResolverRegistry)
assert isinstance(identity_registry, IdentityResolverRegistry)
def test_registers_app_level_ports_and_orchestration_components(self, fake_logger):
# Arrange
di = DependencyInjectionContainer()
core = _make_plugin_loading_core()
persistence_port = MagicMock(spec=PersistencePort)
queue_port = MagicMock(spec=QueuePort)
config_port = MagicMock(spec=ConfigPort)
stage_slot_injector = MagicMock(spec=StageSlotInjector)
health_aggregator = MagicMock(spec=HealthAggregator)
channel_circuit_breaker = MagicMock(spec=ChannelCircuitBreaker)
transport_manager = MagicMock(spec=TransportManager)
# Act
_register_di_singletons(
di_container=di,
core=core,
persistence_port=persistence_port,
session_factory=MagicMock(),
queue_port=queue_port,
config_port=config_port,
stage_slot_injector=stage_slot_injector,
health_aggregator=health_aggregator,
channel_circuit_breaker=channel_circuit_breaker,
transport_manager=transport_manager,
logger=fake_logger,
)
# Assert
assert di.resolve(StageSlotInjector) is stage_slot_injector
assert di.resolve(HealthAggregator) is health_aggregator
assert di.resolve(ChannelCircuitBreaker) is channel_circuit_breaker
assert di.resolve(LoggerPort) is fake_logger
assert di.resolve(CachePort) is core.cache_port
assert di.resolve(PersistencePort) is persistence_port
assert di.resolve(QueuePort) is queue_port
assert di.resolve(ConfigPort) is config_port
assert di.resolve(TransportManager) is transport_manager
def test_registers_content_review_domain_singletons(self, fake_logger):
"""内容审核域 3 个单例CR-01~CR-03"""
# Arrange
di = DependencyInjectionContainer()
core = _make_plugin_loading_core()
session_factory = MagicMock()
# Act
_register_di_singletons(
di_container=di,
core=core,
persistence_port=MagicMock(spec=PersistencePort),
session_factory=session_factory,
queue_port=MagicMock(spec=QueuePort),
config_port=MagicMock(spec=ConfigPort),
stage_slot_injector=MagicMock(spec=StageSlotInjector),
health_aggregator=MagicMock(spec=HealthAggregator),
channel_circuit_breaker=MagicMock(spec=ChannelCircuitBreaker),
transport_manager=MagicMock(spec=TransportManager),
logger=fake_logger,
)
# Assert
moderation = di.resolve(DefaultContentModerationAdapter)
assert isinstance(moderation, DefaultContentModerationAdapter)
review_adapter = di.resolve(ContentReviewRepositoryAdapter)
assert isinstance(review_adapter, ContentReviewRepositoryAdapter)
review_port = di.resolve(ContentReviewRepositoryPort)
# ContentReviewRepositoryPort 与 ContentReviewRepositoryAdapter 绑定到同一实例
assert review_port is review_adapter
def test_registers_redis_arq_and_execution_port_singletons(self, fake_logger):
# Arrange
di = DependencyInjectionContainer()
core = _make_plugin_loading_core()
# Act
_register_di_singletons(
di_container=di,
core=core,
persistence_port=MagicMock(spec=PersistencePort),
session_factory=MagicMock(),
queue_port=MagicMock(spec=QueuePort),
config_port=MagicMock(spec=ConfigPort),
stage_slot_injector=MagicMock(spec=StageSlotInjector),
health_aggregator=MagicMock(spec=HealthAggregator),
channel_circuit_breaker=MagicMock(spec=ChannelCircuitBreaker),
transport_manager=MagicMock(spec=TransportManager),
logger=fake_logger,
)
# Assert: 共享 Redis / ARQ / AgentRunExecutionPort 单例
# (由 core 直接注册)
assert di.resolve(AgentRunExecutionPort) is core.execution_port_impl
def test_async_session_not_registered_as_singleton(self, fake_logger):
"""AsyncSession 是请求级资源不应注册为单例session_factory 注册为单例供按需创建。"""
# Arrange
di = DependencyInjectionContainer()
core = _make_plugin_loading_core()
from sqlalchemy.ext.asyncio import AsyncSession
# Act
_register_di_singletons(
di_container=di,
core=core,
persistence_port=MagicMock(spec=PersistencePort),
session_factory=MagicMock(),
queue_port=MagicMock(spec=QueuePort),
config_port=MagicMock(spec=ConfigPort),
stage_slot_injector=MagicMock(spec=StageSlotInjector),
health_aggregator=MagicMock(spec=HealthAggregator),
channel_circuit_breaker=MagicMock(spec=ChannelCircuitBreaker),
transport_manager=MagicMock(spec=TransportManager),
logger=fake_logger,
)
# Assert: AsyncSession 未注册为单例(避免并发污染)
with pytest.raises(Exception):
di.resolve(AsyncSession)