ForcePilot/backend/test/unit/channels/conftest.py

153 lines
6.1 KiB
Python
Raw Normal View History

"""channels 限界上下文单元测试跨层通用 fixture。
本文件提供横跨 core / application / adapters / plugins 各层复用的被驱动
端口桩LoggerPort / ConfigPort / CachePort / EventPublisherPort /
TracerPort桩使用 ``unittest.mock`` ``MagicMock`` / ``AsyncMock``
不实现真实逻辑端口方法签名以 ``yuxi.channels.contract.ports.driven``
下的 Protocol 为准
注意LoggerPort / TracerPort 的方法在端口协议中均为 ``async def``因此
此处使用 ``MagicMock(spec=...)`` MagicMock 会自动为 async 方法生成
AsyncMock ``await``与端口协议保持一致单文件独有的 helper 应保
留在对应测试文件内 testing-guidelines.md
"""
from __future__ import annotations
from unittest.mock import AsyncMock, MagicMock
import pytest
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.tracer_port import TracerPort
from yuxi.channels.core.service.config_manager import ConfigManager
@pytest.fixture
def fake_logger() -> LoggerPort:
"""LoggerPort 桩。
``MagicMock(spec=LoggerPort)`` 自动为 ``log`` / ``debug`` / ``info`` /
``warn`` / ``error`` / ``exception`` 生成 AsyncMock ``await``
默认返回 None需要断言调用参数时直接使用对应方法的 ``assert_*``
"""
return MagicMock(spec=LoggerPort)
@pytest.fixture
def fake_config() -> ConfigPort:
"""ConfigPort 桩,``get`` 默认返回 None``update`` 默认返回 None。"""
mock = AsyncMock(spec=ConfigPort)
mock.get.return_value = None
mock.update.return_value = None
return mock
@pytest.fixture
def fake_config_manager() -> ConfigManager:
"""ConfigManager 桩。
``AsyncMock(spec=ConfigManager)`` 自动为 ``update`` / ``updateInternal``
/ ``rollback`` async 方法生成 AsyncMock``update`` /
``updateInternal`` 默认返回 None调用方按需覆盖返回值
"""
mock = AsyncMock(spec=ConfigManager)
mock.update.return_value = None
mock.updateInternal.return_value = None
return mock
@pytest.fixture
def fake_cache() -> CachePort:
"""CachePort 桩,预设常用方法返回值。
``get`` 返回 None``set`` / ``delete`` / ``expire`` /
``acquireAdvisoryLock`` / ``releaseAdvisoryLock`` / ``ping`` 返回 True
``incr`` 返回 1
"""
mock = AsyncMock(spec=CachePort)
mock.get.return_value = None
mock.set.return_value = True
mock.delete.return_value = True
mock.incr.return_value = 1
mock.expire.return_value = True
mock.acquireAdvisoryLock.return_value = True
mock.releaseAdvisoryLock.return_value = True
mock.ping.return_value = True
return mock
@pytest.fixture
def fake_event_publisher() -> EventPublisherPort:
"""EventPublisherPort 桩,``publish`` 默认返回 None。"""
mock = AsyncMock(spec=EventPublisherPort)
mock.publish.return_value = None
return mock
@pytest.fixture
def fake_tracer() -> TracerPort:
"""TracerPort 桩。
``MagicMock(spec=TracerPort)`` 自动为 ``startSpan`` / ``endSpan`` /
``getCurrentTrace`` 生成 AsyncMock ``await````startSpan`` 默认
返回 Span-like MagicMock``endSpan`` 返回 None``getCurrentTrace``
返回 Option-like MagicMock调用方可按需覆盖
"""
mock = MagicMock(spec=TracerPort)
mock.startSpan.return_value = MagicMock()
mock.endSpan.return_value = None
mock.getCurrentTrace.return_value = MagicMock()
return mock
@pytest.fixture
def fake_plugin_host() -> MagicMock:
"""PluginHost 桩(跨层共享)。
``MagicMock()`` 模拟 ``PluginHost`` 协议
``yuxi.channels.contract.plugin.entry.PluginHost``
- 所有 ``getXxxPort()`` 返回 ``MagicMock`` 调用方可覆盖为带
``spec`` 的端口桩 ``host.getLoggerPort.return_value = fake_logger``
- ``registerAdapter()`` / ``registerStageSlot()`` / ``registerEventSubscription()``
/ ``registerConfigSource()`` / ``registerMatchTier()`` / ``registerMatcher()``
返回 ``None``
- ``publishEvent`` ``AsyncMock`` ``await``返回 ``None``
fixture 提升至 channels 限界上下文根 conftest application/lifecycle
plugins 各层测试复用避免在子目录 conftest 重复定义
"""
host = MagicMock()
# 端口获取:返回 MagicMock 桩,调用方可覆盖为带 spec 的端口桩
host.getConfigPort.return_value = MagicMock()
host.getLoggerPort.return_value = MagicMock()
host.getPersistencePort.return_value = MagicMock()
host.getCachePort.return_value = MagicMock()
host.getTracerPort.return_value = MagicMock()
host.getQueuePort.return_value = MagicMock()
host.getConversationPort.return_value = MagicMock()
host.getAgentRunPort.return_value = MagicMock()
host.getIdentityResolverPort.return_value = MagicMock()
host.getChannelAccountRepositoryPort.return_value = MagicMock()
host.getChannelSessionRepositoryPort.return_value = MagicMock()
host.getPairingRepositoryPort.return_value = MagicMock()
host.getAuditLogRepositoryPort.return_value = MagicMock()
host.getOutboxRepositoryPort.return_value = MagicMock()
host.getUserIdentityRepositoryPort.return_value = MagicMock()
host.getIdempotencyRepositoryPort.return_value = MagicMock()
host.getPersistenceHealthPort.return_value = MagicMock()
# 扩展点注册:返回 None
host.registerAdapter.return_value = None
host.registerStageSlot.return_value = None
host.registerEventSubscription.return_value = None
host.registerConfigSource.return_value = None
host.registerMatchTier.return_value = None
host.registerMatcher.return_value = None
# 事件发布async def使用 AsyncMock
host.publishEvent = AsyncMock(return_value=None)
return host