Phase 6.4 - Modular Architecture: - Add lewooogo-brain adapters for LLM providers - Add lewooogo-data dual memory (Redis + PostgreSQL) - Implement consensus engine for multi-agent decisions - Add incident memory service for historical context Phase 9 - Agent Teams (Claude Agent SDK): - Add base agent class with Claude Sonnet 4 integration - Implement action planner, blast radius, and security agents - Add agent API endpoints and proposal workflow - Integrate ADR-009 OpenClaw Agent Teams architecture DevOps & CI/CD: - Add GitHub Actions CI/CD workflows (ci.yaml, cd.yaml) - Add pre-commit hooks and secrets baseline - Add docker-compose for local development - Update Kubernetes network policies Frontend Improvements: - Add auto-healing error boundary component - Update i18n messages for agent features - Enhance dual-state incident card with execution feedback Documentation: - Add 7 ADRs covering MCP, design system, architecture decisions - Update ARCHITECTURE_MEMORY.md with modular design - Add GLOBAL_RULES.md and SOUL.md for project identity Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
173 lines
3.8 KiB
Python
173 lines
3.8 KiB
Python
"""
|
|
Pytest fixtures for lewooogo-data tests
|
|
========================================
|
|
|
|
提供測試所需的 Mock 與 Fixtures
|
|
"""
|
|
|
|
import pytest
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
from pydantic import BaseModel
|
|
|
|
|
|
# =============================================================================
|
|
# 測試用 Pydantic Model
|
|
# =============================================================================
|
|
|
|
class SampleModel(BaseModel):
|
|
"""測試用的 Pydantic Model"""
|
|
id: str
|
|
name: str
|
|
value: int = 0
|
|
|
|
|
|
# =============================================================================
|
|
# Redis Mock Fixtures
|
|
# =============================================================================
|
|
|
|
@pytest.fixture
|
|
def mock_redis():
|
|
"""
|
|
Mock Redis client
|
|
|
|
模擬 redis.asyncio 的行為
|
|
"""
|
|
mock = AsyncMock()
|
|
|
|
# 內部儲存
|
|
storage = {}
|
|
ttls = {}
|
|
|
|
async def mock_get(key):
|
|
return storage.get(key)
|
|
|
|
async def mock_setex(key, ttl, value):
|
|
storage[key] = value
|
|
ttls[key] = ttl
|
|
return True
|
|
|
|
async def mock_delete(key):
|
|
if key in storage:
|
|
del storage[key]
|
|
if key in ttls:
|
|
del ttls[key]
|
|
return 1
|
|
return 0
|
|
|
|
async def mock_exists(key):
|
|
return 1 if key in storage else 0
|
|
|
|
async def mock_ttl(key):
|
|
if key not in storage:
|
|
return -2
|
|
return ttls.get(key, -1)
|
|
|
|
async def mock_expire(key, ttl):
|
|
if key in storage:
|
|
ttls[key] = ttl
|
|
return True
|
|
return False
|
|
|
|
async def mock_ping():
|
|
return True
|
|
|
|
mock.get = mock_get
|
|
mock.setex = mock_setex
|
|
mock.delete = mock_delete
|
|
mock.exists = mock_exists
|
|
mock.ttl = mock_ttl
|
|
mock.expire = mock_expire
|
|
mock.ping = mock_ping
|
|
mock.close = AsyncMock()
|
|
|
|
# 暴露 storage 供測試驗證
|
|
mock._storage = storage
|
|
mock._ttls = ttls
|
|
|
|
return mock
|
|
|
|
|
|
@pytest.fixture
|
|
def patch_redis_pool(mock_redis):
|
|
"""
|
|
Patch Redis 連線池
|
|
|
|
將全域 _redis_pool 替換為 mock
|
|
"""
|
|
with patch(
|
|
"lewooogo_data.providers.redis_memory._redis_pool",
|
|
mock_redis
|
|
):
|
|
with patch(
|
|
"lewooogo_data.providers.redis_memory.get_redis_pool",
|
|
return_value=mock_redis
|
|
):
|
|
yield mock_redis
|
|
|
|
|
|
# =============================================================================
|
|
# PostgreSQL Mock Fixtures
|
|
# =============================================================================
|
|
|
|
@pytest.fixture
|
|
def mock_session():
|
|
"""
|
|
Mock SQLAlchemy AsyncSession
|
|
"""
|
|
session = AsyncMock()
|
|
|
|
# 內部儲存
|
|
storage = {}
|
|
|
|
async def mock_get(model_class, key):
|
|
return storage.get(key)
|
|
|
|
session.get = mock_get
|
|
session.add = MagicMock()
|
|
session.delete = AsyncMock()
|
|
session.commit = AsyncMock()
|
|
|
|
# 暴露 storage 供測試驗證
|
|
session._storage = storage
|
|
|
|
return session
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_session_factory(mock_session):
|
|
"""
|
|
Mock Session Factory
|
|
|
|
使用 context manager 模式
|
|
"""
|
|
class MockSessionFactory:
|
|
def __init__(self, session):
|
|
self._session = session
|
|
|
|
def __call__(self):
|
|
return self
|
|
|
|
async def __aenter__(self):
|
|
return self._session
|
|
|
|
async def __aexit__(self, *args):
|
|
pass
|
|
|
|
return MockSessionFactory(mock_session)
|
|
|
|
|
|
@pytest.fixture
|
|
def patch_pg_session(mock_session_factory):
|
|
"""
|
|
Patch PostgreSQL session factory
|
|
"""
|
|
with patch(
|
|
"lewooogo_data.providers.pg_memory._session_factory",
|
|
mock_session_factory
|
|
):
|
|
with patch(
|
|
"lewooogo_data.providers.pg_memory.get_session_factory",
|
|
return_value=mock_session_factory
|
|
):
|
|
yield mock_session_factory
|