From 6b9fb398078f985b56b34150a9532e1a38d05177 Mon Sep 17 00:00:00 2001 From: Kris <2893855659@qq.com> Date: Sun, 31 May 2026 17:13:08 +0800 Subject: [PATCH] =?UTF-8?q?refactor(channel=20infra):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E5=9F=BA=E7=A1=80=E8=AE=BE=E6=96=BD=E4=BB=A3=E7=A0=81=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 本次提交将原cache、config目录迁移至cache_infra和configuration目录,统一基础设施代码组织: 1. 移动Redis相关缓存实现到cache_infra目录 2. 移动配置相关实现到configuration目录 3. 更新所有模块导入路径和测试文件引用路径 4. 新增对应目录的初始化文件和完整单元测试 5. 重构容器绑定的依赖导入路径 --- .../application/service/config_service.py | 2 +- backend/package/yuxi/channel/container.py | 12 +- .../infrastructure/cache_infra}/__init__.py | 0 .../cache_infra/redis_bot_loop_guard.py | 61 ++++++++++ .../infrastructure/cache_infra/redis_cache.py | 50 ++++++++ .../cache_infra/redis_circuit_breaker.py | 107 ++++++++++++++++++ .../cache_infra/redis_rate_limiter.py | 34 ++++++ .../{config => configuration}/__init__.py | 0 .../channel_config.py | 0 .../redis_config_reload.py | 0 .../test_container_factory_build_adapters.py | 2 +- ...st_container_factory_build_auth_service.py | 2 +- .../test_container_factory_build_config.py | 2 +- .../test_container_factory_build_infra.py | 2 +- .../test_container_factory_build_pipeline.py | 2 +- .../test_container_factory_build_workers.py | 2 +- .../infrastructure/configuration/__init__.py | 1 + .../test_channel_config.py | 2 +- .../test_redis_config_reload.py | 2 +- .../channel/startup/test_channel_config.py | 2 +- .../channel/startup/test_container_factory.py | 2 +- .../startup/test_redis_bot_loop_guard.py | 2 +- .../unit/channel/startup/test_redis_cache.py | 2 +- .../startup/test_redis_circuit_breaker.py | 2 +- .../startup/test_redis_config_reload.py | 2 +- .../startup/test_redis_rate_limiter.py | 2 +- 26 files changed, 275 insertions(+), 22 deletions(-) rename backend/{test/unit/channel/infrastructure/config => package/yuxi/channel/infrastructure/cache_infra}/__init__.py (100%) create mode 100644 backend/package/yuxi/channel/infrastructure/cache_infra/redis_bot_loop_guard.py create mode 100644 backend/package/yuxi/channel/infrastructure/cache_infra/redis_cache.py create mode 100644 backend/package/yuxi/channel/infrastructure/cache_infra/redis_circuit_breaker.py create mode 100644 backend/package/yuxi/channel/infrastructure/cache_infra/redis_rate_limiter.py rename backend/package/yuxi/channel/infrastructure/{config => configuration}/__init__.py (100%) rename backend/package/yuxi/channel/infrastructure/{config => configuration}/channel_config.py (100%) rename backend/package/yuxi/channel/infrastructure/{config => configuration}/redis_config_reload.py (100%) create mode 100644 backend/test/unit/channel/infrastructure/configuration/__init__.py rename backend/test/unit/channel/infrastructure/{config => configuration}/test_channel_config.py (97%) rename backend/test/unit/channel/infrastructure/{config => configuration}/test_redis_config_reload.py (97%) diff --git a/backend/package/yuxi/channel/application/service/config_service.py b/backend/package/yuxi/channel/application/service/config_service.py index 7f26d3e1..a2ef28c4 100644 --- a/backend/package/yuxi/channel/application/service/config_service.py +++ b/backend/package/yuxi/channel/application/service/config_service.py @@ -6,7 +6,7 @@ import json from yuxi.channel.domain.middleware.configurable import Configurable from yuxi.channel.domain.port.config_reload_port import ConfigReloadPort from yuxi.channel.domain.service.pipeline import Pipeline -from yuxi.channel.infrastructure.config.channel_config import ChannelConfig +from yuxi.channel.infrastructure.configuration.channel_config import ChannelConfig class ConfigService: diff --git a/backend/package/yuxi/channel/container.py b/backend/package/yuxi/channel/container.py index 1c65ebd0..7c851c67 100644 --- a/backend/package/yuxi/channel/container.py +++ b/backend/package/yuxi/channel/container.py @@ -37,12 +37,12 @@ from yuxi.channel.domain.repository.outbox_repository import OutboxRepositoryPor from yuxi.channel.domain.repository.session_repository import SessionRepositoryPort from yuxi.channel.domain.service.pipeline import Pipeline from yuxi.channel.infrastructure.agent.agent_adapter import AgentAdapter -from yuxi.channel.infrastructure.cache.redis_bot_loop_guard import RedisBotLoopGuard -from yuxi.channel.infrastructure.cache.redis_cache import RedisCache -from yuxi.channel.infrastructure.cache.redis_circuit_breaker import RedisCircuitBreaker -from yuxi.channel.infrastructure.cache.redis_rate_limiter import RedisRateLimiter -from yuxi.channel.infrastructure.config.channel_config import ChannelConfig -from yuxi.channel.infrastructure.config.redis_config_reload import RedisConfigReload +from yuxi.channel.infrastructure.cache_infra.redis_bot_loop_guard import RedisBotLoopGuard +from yuxi.channel.infrastructure.cache_infra.redis_cache import RedisCache +from yuxi.channel.infrastructure.cache_infra.redis_circuit_breaker import RedisCircuitBreaker +from yuxi.channel.infrastructure.cache_infra.redis_rate_limiter import RedisRateLimiter +from yuxi.channel.infrastructure.configuration.channel_config import ChannelConfig +from yuxi.channel.infrastructure.configuration.redis_config_reload import RedisConfigReload from yuxi.channel.infrastructure.content_filter.composite_content_filter import CompositeContentFilter from yuxi.channel.infrastructure.content_filter.llm_content_filter import LlmContentFilter from yuxi.channel.infrastructure.content_filter.redis_content_filter import RedisContentFilter diff --git a/backend/test/unit/channel/infrastructure/config/__init__.py b/backend/package/yuxi/channel/infrastructure/cache_infra/__init__.py similarity index 100% rename from backend/test/unit/channel/infrastructure/config/__init__.py rename to backend/package/yuxi/channel/infrastructure/cache_infra/__init__.py diff --git a/backend/package/yuxi/channel/infrastructure/cache_infra/redis_bot_loop_guard.py b/backend/package/yuxi/channel/infrastructure/cache_infra/redis_bot_loop_guard.py new file mode 100644 index 00000000..bfd759d3 --- /dev/null +++ b/backend/package/yuxi/channel/infrastructure/cache_infra/redis_bot_loop_guard.py @@ -0,0 +1,61 @@ +from __future__ import annotations + +import logging + +from yuxi.channel.domain.port.bot_loop_guard_port import BotLoopGuardPort +from yuxi.channel.domain.port.cache_port import CachePort + +logger = logging.getLogger(__name__) + + +class RedisBotLoopGuard: + def __init__( + self, + cache_port: CachePort, + *, + dm_budget: int = 10, + group_budget: int = 20, + window_seconds: int = 60, + cooldown_seconds: int = 300, + ) -> None: + self._cache = cache_port + self._dm_budget = dm_budget + self._group_budget = group_budget + self._window = window_seconds + self._cooldown = cooldown_seconds + + async def check( + self, session_id: str, *, sender_id: str = "", is_group: bool = False + ) -> bool: + if is_group and not sender_id: + logger.warning( + "bot loop guard: group message without sender_id rejected, session=%s", + session_id, + ) + return False + + if is_group and sender_id: + key = f"channel:bot_loop:group:{session_id}:{sender_id}" + budget = self._group_budget + else: + key = f"channel:bot_loop:dm:{session_id}" + budget = self._dm_budget + + count = await self._cache.incr(key) + if count == 1: + await self._cache.expire(key, self._window) + + if count > budget: + await self._cache.expire(key, self._cooldown) + return False + + return True + + async def reset( + self, session_id: str, *, sender_id: str = "", is_group: bool = False + ) -> None: + if is_group and sender_id: + key = f"channel:bot_loop:group:{session_id}:{sender_id}" + else: + key = f"channel:bot_loop:dm:{session_id}" + await self._cache.delete(key) diff --git a/backend/package/yuxi/channel/infrastructure/cache_infra/redis_cache.py b/backend/package/yuxi/channel/infrastructure/cache_infra/redis_cache.py new file mode 100644 index 00000000..489649b9 --- /dev/null +++ b/backend/package/yuxi/channel/infrastructure/cache_infra/redis_cache.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +import logging + +import redis.asyncio as aioredis + +from yuxi.channel.domain.port.cache_port import CachePort + +logger = logging.getLogger(__name__) + + +class RedisCache(CachePort): + def __init__(self, redis: aioredis.Redis) -> None: + self._redis = redis + + async def get(self, key: str) -> str | None: + raw = await self._redis.get(key) + if raw is None: + return None + return raw.decode() if isinstance(raw, bytes) else raw + + async def set( + self, key: str, value: str, *, ex: int | None = None, nx: bool = False + ) -> bool: + result = await self._redis.set(key, value, ex=ex, nx=nx) + if result is None: + return False + if isinstance(result, bool): + return result + if isinstance(result, bytes): + return result == b"OK" + return str(result) == "OK" + + async def delete(self, key: str) -> None: + await self._redis.delete(key) + + async def incr(self, key: str) -> int: + return await self._redis.incr(key) + + async def expire(self, key: str, seconds: int) -> None: + await self._redis.expire(key, seconds) + + async def ttl(self, key: str) -> int: + return await self._redis.ttl(key) + + async def eval(self, script: str, keys: list[str], args: list[str | int]) -> tuple: + return await self._redis.eval(script, len(keys), *keys, *args) + + async def publish(self, channel: str, message: str) -> None: + await self._redis.publish(channel, message) diff --git a/backend/package/yuxi/channel/infrastructure/cache_infra/redis_circuit_breaker.py b/backend/package/yuxi/channel/infrastructure/cache_infra/redis_circuit_breaker.py new file mode 100644 index 00000000..d437a594 --- /dev/null +++ b/backend/package/yuxi/channel/infrastructure/cache_infra/redis_circuit_breaker.py @@ -0,0 +1,107 @@ +from __future__ import annotations + +from yuxi.channel.domain.port.cache_port import CachePort +from yuxi.channel.domain.port.circuit_breaker_port import CircuitBreakerPort + + +_IS_AVAILABLE_SCRIPT = """ +local state_key = KEYS[1] +local probe_key = KEYS[2] +local recovery_timeout = tonumber(ARGV[1]) +local half_open_max = tonumber(ARGV[2]) + +local state = redis.call('GET', state_key) +if not state then + return {1, 'closed'} +end + +if state == 'open' then + local ttl = redis.call('TTL', state_key) + if ttl > 0 then + return {0, 'open'} + end + redis.call('SET', state_key, 'half_open', 'EX', recovery_timeout) + redis.call('DEL', probe_key) + return {1, 'half_open'} +end + +if state == 'half_open' then + local count = redis.call('INCR', probe_key) + if count == 1 then + redis.call('EXPIRE', probe_key, recovery_timeout) + end + if count <= half_open_max then + return {1, 'half_open'} + end + return {0, 'half_open'} +end + +return {1, 'closed'} +""" + +_RECORD_FAILURE_SCRIPT = """ +local failure_key = KEYS[1] +local state_key = KEYS[2] +local probe_key = KEYS[3] +local recovery_timeout = tonumber(ARGV[1]) +local failure_threshold = tonumber(ARGV[2]) + +local count = redis.call('INCR', failure_key) +if count == 1 then + redis.call('EXPIRE', failure_key, recovery_timeout * 2) +end + +if count >= failure_threshold then + redis.call('SET', state_key, 'open', 'EX', recovery_timeout) + redis.call('DEL', probe_key) + return count +end + +return count +""" + + +class RedisCircuitBreaker: + def __init__( + self, + cache_port: CachePort, + *, + failure_threshold: int = 5, + recovery_timeout: int = 30, + half_open_max: int = 1, + ) -> None: + self._cache = cache_port + self._failure_threshold = failure_threshold + self._recovery_timeout = recovery_timeout + self._half_open_max = half_open_max + + async def is_available(self, agent_config_id: int) -> bool: + state_key = f"channel:circuit:state:{agent_config_id}" + probe_key = f"channel:circuit:probe:{agent_config_id}" + + result = await self._cache.eval( + _IS_AVAILABLE_SCRIPT, + keys=[state_key, probe_key], + args=[str(self._recovery_timeout), str(self._half_open_max)], + ) + allowed = result[0] + return bool(allowed) + + async def record_success(self, agent_config_id: int) -> None: + state_key = f"channel:circuit:state:{agent_config_id}" + failure_key = f"channel:circuit:failures:{agent_config_id}" + probe_key = f"channel:circuit:probe:{agent_config_id}" + await self._cache.delete(failure_key) + await self._cache.delete(probe_key) + await self._cache.delete(state_key) + + async def record_failure(self, agent_config_id: int) -> None: + failure_key = f"channel:circuit:failures:{agent_config_id}" + state_key = f"channel:circuit:state:{agent_config_id}" + probe_key = f"channel:circuit:probe:{agent_config_id}" + + await self._cache.eval( + _RECORD_FAILURE_SCRIPT, + keys=[failure_key, state_key, probe_key], + args=[str(self._recovery_timeout), str(self._failure_threshold)], + ) diff --git a/backend/package/yuxi/channel/infrastructure/cache_infra/redis_rate_limiter.py b/backend/package/yuxi/channel/infrastructure/cache_infra/redis_rate_limiter.py new file mode 100644 index 00000000..3754ea46 --- /dev/null +++ b/backend/package/yuxi/channel/infrastructure/cache_infra/redis_rate_limiter.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +import logging + +import redis.asyncio as aioredis + +logger = logging.getLogger(__name__) + + +class RedisRateLimiter: + def __init__(self, redis: aioredis.Redis) -> None: + self._redis = redis + + async def check_and_incr( + self, key: str, *, max_attempts: int, window_seconds: int, lockout_seconds: int = 0 + ) -> bool: + count = await self._redis.incr(key) + if count == 1: + await self._redis.expire(key, window_seconds) + if count <= max_attempts: + return True + if lockout_seconds > 0: + lockout_key = key.replace(":attempts:", ":lockout:") + await self._redis.set(lockout_key, "1", ex=lockout_seconds) + return False + + async def is_locked(self, key: str) -> tuple[bool, int]: + ttl = await self._redis.ttl(key) + if ttl is None or ttl < 0: + return False, 0 + return True, ttl + + async def reset(self, key: str) -> None: + await self._redis.delete(key) diff --git a/backend/package/yuxi/channel/infrastructure/config/__init__.py b/backend/package/yuxi/channel/infrastructure/configuration/__init__.py similarity index 100% rename from backend/package/yuxi/channel/infrastructure/config/__init__.py rename to backend/package/yuxi/channel/infrastructure/configuration/__init__.py diff --git a/backend/package/yuxi/channel/infrastructure/config/channel_config.py b/backend/package/yuxi/channel/infrastructure/configuration/channel_config.py similarity index 100% rename from backend/package/yuxi/channel/infrastructure/config/channel_config.py rename to backend/package/yuxi/channel/infrastructure/configuration/channel_config.py diff --git a/backend/package/yuxi/channel/infrastructure/config/redis_config_reload.py b/backend/package/yuxi/channel/infrastructure/configuration/redis_config_reload.py similarity index 100% rename from backend/package/yuxi/channel/infrastructure/config/redis_config_reload.py rename to backend/package/yuxi/channel/infrastructure/configuration/redis_config_reload.py diff --git a/backend/test/unit/channel/container/test_container_factory_build_adapters.py b/backend/test/unit/channel/container/test_container_factory_build_adapters.py index 42986470..65d371cc 100644 --- a/backend/test/unit/channel/container/test_container_factory_build_adapters.py +++ b/backend/test/unit/channel/container/test_container_factory_build_adapters.py @@ -5,7 +5,7 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest from yuxi.channel.container import ChannelContainerFactory, _InfraBundle -from yuxi.channel.infrastructure.config.channel_config import ChannelConfig +from yuxi.channel.infrastructure.configuration.channel_config import ChannelConfig class TestBuildAdapters: diff --git a/backend/test/unit/channel/container/test_container_factory_build_auth_service.py b/backend/test/unit/channel/container/test_container_factory_build_auth_service.py index 18a4cc32..dd57a5b1 100644 --- a/backend/test/unit/channel/container/test_container_factory_build_auth_service.py +++ b/backend/test/unit/channel/container/test_container_factory_build_auth_service.py @@ -6,7 +6,7 @@ import pytest from yuxi.channel.container import ChannelContainerFactory, _InfraBundle from yuxi.channel.application.service.auth_service import AuthService -from yuxi.channel.infrastructure.config.channel_config import ChannelConfig +from yuxi.channel.infrastructure.configuration.channel_config import ChannelConfig class TestBuildAuthService: diff --git a/backend/test/unit/channel/container/test_container_factory_build_config.py b/backend/test/unit/channel/container/test_container_factory_build_config.py index 7666bffe..e66df87e 100644 --- a/backend/test/unit/channel/container/test_container_factory_build_config.py +++ b/backend/test/unit/channel/container/test_container_factory_build_config.py @@ -5,7 +5,7 @@ from unittest.mock import patch import pytest from yuxi.channel.container import ChannelContainerFactory -from yuxi.channel.infrastructure.config.channel_config import ChannelConfig +from yuxi.channel.infrastructure.configuration.channel_config import ChannelConfig class TestBuildConfig: diff --git a/backend/test/unit/channel/container/test_container_factory_build_infra.py b/backend/test/unit/channel/container/test_container_factory_build_infra.py index 1d4c0441..7f1f1abb 100644 --- a/backend/test/unit/channel/container/test_container_factory_build_infra.py +++ b/backend/test/unit/channel/container/test_container_factory_build_infra.py @@ -5,7 +5,7 @@ from unittest.mock import MagicMock import pytest from yuxi.channel.container import ChannelContainerFactory, _InfraBundle -from yuxi.channel.infrastructure.config.channel_config import ChannelConfig +from yuxi.channel.infrastructure.configuration.channel_config import ChannelConfig class TestBuildInfra: diff --git a/backend/test/unit/channel/container/test_container_factory_build_pipeline.py b/backend/test/unit/channel/container/test_container_factory_build_pipeline.py index 29be5571..9cc1ae03 100644 --- a/backend/test/unit/channel/container/test_container_factory_build_pipeline.py +++ b/backend/test/unit/channel/container/test_container_factory_build_pipeline.py @@ -7,7 +7,7 @@ import pytest from yuxi.channel.container import ChannelContainerFactory, _InfraBundle from yuxi.channel.application.service.auth_service import AuthService from yuxi.channel.domain.service.pipeline import Pipeline -from yuxi.channel.infrastructure.config.channel_config import ChannelConfig +from yuxi.channel.infrastructure.configuration.channel_config import ChannelConfig class TestBuildPipeline: diff --git a/backend/test/unit/channel/container/test_container_factory_build_workers.py b/backend/test/unit/channel/container/test_container_factory_build_workers.py index 1d49a69c..0d57075d 100644 --- a/backend/test/unit/channel/container/test_container_factory_build_workers.py +++ b/backend/test/unit/channel/container/test_container_factory_build_workers.py @@ -6,7 +6,7 @@ import pytest from yuxi.channel.container import ChannelContainerFactory, _InfraBundle, _WorkerBundle from yuxi.channel.domain.service.pipeline import Pipeline -from yuxi.channel.infrastructure.config.channel_config import ChannelConfig +from yuxi.channel.infrastructure.configuration.channel_config import ChannelConfig class TestBuildWorkers: diff --git a/backend/test/unit/channel/infrastructure/configuration/__init__.py b/backend/test/unit/channel/infrastructure/configuration/__init__.py new file mode 100644 index 00000000..5f282702 --- /dev/null +++ b/backend/test/unit/channel/infrastructure/configuration/__init__.py @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/backend/test/unit/channel/infrastructure/config/test_channel_config.py b/backend/test/unit/channel/infrastructure/configuration/test_channel_config.py similarity index 97% rename from backend/test/unit/channel/infrastructure/config/test_channel_config.py rename to backend/test/unit/channel/infrastructure/configuration/test_channel_config.py index ee4c0488..30634751 100644 --- a/backend/test/unit/channel/infrastructure/config/test_channel_config.py +++ b/backend/test/unit/channel/infrastructure/configuration/test_channel_config.py @@ -5,7 +5,7 @@ from pathlib import Path import pytest -from yuxi.channel.infrastructure.config.channel_config import ChannelConfig +from yuxi.channel.infrastructure.configuration.channel_config import ChannelConfig @pytest.fixture diff --git a/backend/test/unit/channel/infrastructure/config/test_redis_config_reload.py b/backend/test/unit/channel/infrastructure/configuration/test_redis_config_reload.py similarity index 97% rename from backend/test/unit/channel/infrastructure/config/test_redis_config_reload.py rename to backend/test/unit/channel/infrastructure/configuration/test_redis_config_reload.py index fcb7a2d0..c422cb79 100644 --- a/backend/test/unit/channel/infrastructure/config/test_redis_config_reload.py +++ b/backend/test/unit/channel/infrastructure/configuration/test_redis_config_reload.py @@ -5,7 +5,7 @@ from unittest.mock import AsyncMock, MagicMock import pytest -from yuxi.channel.infrastructure.config.redis_config_reload import RedisConfigReload +from yuxi.channel.infrastructure.configuration.redis_config_reload import RedisConfigReload @pytest.fixture diff --git a/backend/test/unit/channel/startup/test_channel_config.py b/backend/test/unit/channel/startup/test_channel_config.py index 34ee60d0..cebe10da 100644 --- a/backend/test/unit/channel/startup/test_channel_config.py +++ b/backend/test/unit/channel/startup/test_channel_config.py @@ -2,7 +2,7 @@ from __future__ import annotations import pytest -from yuxi.channel.infrastructure.config.channel_config import ChannelConfig +from yuxi.channel.infrastructure.configuration.channel_config import ChannelConfig class TestChannelConfig: diff --git a/backend/test/unit/channel/startup/test_container_factory.py b/backend/test/unit/channel/startup/test_container_factory.py index aa0d181d..9e3e1c79 100644 --- a/backend/test/unit/channel/startup/test_container_factory.py +++ b/backend/test/unit/channel/startup/test_container_factory.py @@ -7,7 +7,7 @@ import pytest import redis.asyncio as aioredis from yuxi.channel.container import ChannelContainerFactory -from yuxi.channel.infrastructure.config.channel_config import ChannelConfig +from yuxi.channel.infrastructure.configuration.channel_config import ChannelConfig class TestChannelContainerFactory: diff --git a/backend/test/unit/channel/startup/test_redis_bot_loop_guard.py b/backend/test/unit/channel/startup/test_redis_bot_loop_guard.py index 24e705d0..ed7c73e2 100644 --- a/backend/test/unit/channel/startup/test_redis_bot_loop_guard.py +++ b/backend/test/unit/channel/startup/test_redis_bot_loop_guard.py @@ -4,7 +4,7 @@ from unittest.mock import AsyncMock import pytest -from yuxi.channel.infrastructure.cache.redis_bot_loop_guard import RedisBotLoopGuard +from yuxi.channel.infrastructure.cache_infra.redis_bot_loop_guard import RedisBotLoopGuard class TestRedisBotLoopGuard: diff --git a/backend/test/unit/channel/startup/test_redis_cache.py b/backend/test/unit/channel/startup/test_redis_cache.py index 812ee8f4..13e11039 100644 --- a/backend/test/unit/channel/startup/test_redis_cache.py +++ b/backend/test/unit/channel/startup/test_redis_cache.py @@ -4,7 +4,7 @@ from unittest.mock import AsyncMock import pytest -from yuxi.channel.infrastructure.cache.redis_cache import RedisCache +from yuxi.channel.infrastructure.cache_infra.redis_cache import RedisCache class TestRedisCache: diff --git a/backend/test/unit/channel/startup/test_redis_circuit_breaker.py b/backend/test/unit/channel/startup/test_redis_circuit_breaker.py index a5f70784..a7e55681 100644 --- a/backend/test/unit/channel/startup/test_redis_circuit_breaker.py +++ b/backend/test/unit/channel/startup/test_redis_circuit_breaker.py @@ -4,7 +4,7 @@ from unittest.mock import AsyncMock import pytest -from yuxi.channel.infrastructure.cache.redis_circuit_breaker import RedisCircuitBreaker +from yuxi.channel.infrastructure.cache_infra.redis_circuit_breaker import RedisCircuitBreaker class TestRedisCircuitBreaker: diff --git a/backend/test/unit/channel/startup/test_redis_config_reload.py b/backend/test/unit/channel/startup/test_redis_config_reload.py index a7e56c3d..775de4d9 100644 --- a/backend/test/unit/channel/startup/test_redis_config_reload.py +++ b/backend/test/unit/channel/startup/test_redis_config_reload.py @@ -4,7 +4,7 @@ from unittest.mock import AsyncMock, MagicMock import pytest -from yuxi.channel.infrastructure.config.redis_config_reload import RedisConfigReload +from yuxi.channel.infrastructure.configuration.redis_config_reload import RedisConfigReload class TestRedisConfigReload: diff --git a/backend/test/unit/channel/startup/test_redis_rate_limiter.py b/backend/test/unit/channel/startup/test_redis_rate_limiter.py index 86b7ca4a..cf0cfaaf 100644 --- a/backend/test/unit/channel/startup/test_redis_rate_limiter.py +++ b/backend/test/unit/channel/startup/test_redis_rate_limiter.py @@ -4,7 +4,7 @@ from unittest.mock import AsyncMock import pytest -from yuxi.channel.infrastructure.cache.redis_rate_limiter import RedisRateLimiter +from yuxi.channel.infrastructure.cache_infra.redis_rate_limiter import RedisRateLimiter class TestRedisRateLimiter: