Files
sanguo_llmwiki/tests/integration/test_mcp_tools.py
T
claude_dev 67157dff34 feat: 实现完整测试套件
- 单元测试: Database 和 Service 层
- 集成测试: MCP 工具端到端测试
- 测试配置: pytest.ini 和覆盖率设置
- 测试文档: tests/README.md

测试覆盖:
- 数据库 CRUD 操作
- FTS5 搜索和验证
- 缓存服务 (LRU + TTL)
- 解析服务 (frontmatter, wikilinks)
- 索引服务 (增量更新, 重建)
- MCP 工具集成测试

Co-Authored-By: Claude Dev <noreply@anthropic.com>
2026-06-26 12:21:10 +08:00

300 lines
8.2 KiB
Python

"""
Integration tests for MCP Tools
测试 MCP Server 工具的端到端功能。
需要设置 RUN_INTEGRATION=1 环境变量才能运行。
"""
import pytest
import os
import tempfile
import asyncio
# 仅在设置 RUN_INTEGRATION=1 时运行
pytestmark = pytest.mark.skipif(
not os.environ.get("RUN_INTEGRATION"),
reason="Integration tests require RUN_INTEGRATION=1"
)
from mcp_server.storage import Database
from mcp_server.services import QueryService, ParserService, IndexerService, GraphService, CacheService
from mcp_server.tools import (
WikiQueryTool,
WikiStatusTool,
WikiLintTool,
CrossLinkerTool,
TagTaxonomyTool,
WikiSynthesizeTool,
DailyUpdateTool,
MemoryBridgeTool
)
@pytest.fixture
async def setup_mcp():
"""设置 MCP 测试环境"""
with tempfile.TemporaryDirectory() as tmpdir:
# 创建测试 wiki vault
wiki_path = os.path.join(tmpdir, "wiki-vault")
os.makedirs(wiki_path)
# 创建测试页面
os.makedirs(os.path.join(wiki_path, "practices"), exist_ok=True)
with open(os.path.join(wiki_path, "practices", "test.md"), 'w') as f:
f.write("""---
name: test-practice
description: Test practice page
metadata:
type: practice
tags: [testing, best-practice]
---
# Test Practice
This is a test practice page.
## Key Points
- Point 1
- Point 2
## Related Links
- [[concepts/test-concept]]
- [[skills/test-skill]]
""")
# 创建概念页面
os.makedirs(os.path.join(wiki_path, "concepts"), exist_ok=True)
with open(os.path.join(wiki_path, "concepts", "test-concept.md"), 'w') as f:
f.write("""---
name: test-concept
description: Test concept page
metadata:
type: concept
tags: [concept]
---
# Test Concept
This is a test concept page.
## Related
- [[practices/test-practice]]
""")
# 创建数据库
db_path = os.path.join(tmpdir, "test.db")
db = Database(db_path)
await db.connect()
# 初始化索引
parser = ParserService()
indexer = IndexerService(db, parser, wiki_path)
await indexer.rebuild_index()
# 创建服务
cache = CacheService(max_size=100)
query_service = QueryService(db, cache)
graph_service = GraphService(db)
yield {
"db": db,
"query_service": query_service,
"parser": parser,
"indexer": indexer,
"graph_service": graph_service,
"wiki_path": wiki_path
}
# 清理
await db.close()
class TestWikiQueryTool:
"""wiki_query 工具测试"""
@pytest.mark.asyncio
async def test_search_by_query(self, setup_mcp):
"""测试搜索功能"""
query_service = setup_mcp["query_service"]
tool = WikiQueryTool(query_service)
result = await tool.handle(query="test")
assert result["success"] is True
assert "results" in result
@pytest.mark.asyncio
async def test_get_page(self, setup_mcp):
"""测试获取单个页面"""
query_service = setup_mcp["query_service"]
tool = WikiQueryTool(query_service)
result = await tool.handle(path="practices/test.md")
assert result["success"] is True
assert result["page"]["path"] == "practices/test.md"
assert result["page"]["title"] == "Test Practice"
class TestWikiStatusTool:
"""wiki_status 工具测试"""
@pytest.mark.asyncio
async def test_get_status(self, setup_mcp):
"""测试获取状态"""
query_service = setup_mcp["query_service"]
indexer = setup_mcp["indexer"]
graph_service = setup_mcp["graph_service"]
tool = WikiStatusTool(query_service, indexer, graph_service)
result = await tool.handle()
assert result["success"] is True
assert "stats" in result
assert "index_status" in result
class TestWikiLintTool:
"""wiki_lint 工具测试"""
@pytest.mark.asyncio
async def test_lint_all_pages(self, setup_mcp):
"""测试审计所有页面"""
parser = setup_mcp["parser"]
query_service = setup_mcp["query_service"]
graph_service = setup_mcp["graph_service"]
wiki_path = setup_mcp["wiki_path"]
tool = WikiLintTool(parser, query_service, graph_service, wiki_path)
result = await tool.handle(level="basic")
assert result["success"] is True
assert "summary" in result
@pytest.mark.asyncio
async def test_lint_single_page(self, setup_mcp):
"""测试审计单个页面"""
parser = setup_mcp["parser"]
query_service = setup_mcp["query_service"]
graph_service = setup_mcp["graph_service"]
wiki_path = setup_mcp["wiki_path"]
tool = WikiLintTool(parser, query_service, graph_service, wiki_path)
result = await tool.handle(path="practices/test.md")
assert result["success"] is True
assert "issues" in result
class TestCrossLinkerTool:
"""cross_linker 工具测试"""
@pytest.mark.asyncio
async def test_find_missing_links(self, setup_mcp):
"""测试查找缺失链接"""
graph_service = setup_mcp["graph_service"]
tool = CrossLinkerTool(graph_service)
result = await tool.handle(action="find_missing")
assert result["success"] is True
assert "missing_links" in result
class TestTagTaxonomyTool:
"""tag_taxonomy 工具测试"""
@pytest.mark.asyncio
async def test_get_taxonomy(self, setup_mcp):
"""测试获取标签分类"""
query_service = setup_mcp["query_service"]
tool = TagTaxonomyTool(query_service)
result = await tool.handle()
assert result["success"] is True
assert "taxonomy" in result
class TestWikiSynthesizeTool:
"""wiki_synthesize 工具测试"""
@pytest.mark.asyncio
async def test_synthesize_topic(self, setup_mcp):
"""测试主题综合"""
query_service = setup_mcp["query_service"]
graph_service = setup_mcp["graph_service"]
tool = WikiSynthesizeTool(query_service, graph_service)
result = await tool.handle(topic="test")
assert result["success"] is True
assert "synthesis" in result
class TestDailyUpdateTool:
"""daily_update 工具测试"""
@pytest.mark.asyncio
async def test_daily_update(self, setup_mcp):
"""测试日常维护"""
query_service = setup_mcp["query_service"]
indexer = setup_mcp["indexer"]
wiki_path = setup_mcp["wiki_path"]
tool = DailyUpdateTool(query_service, indexer, wiki_path)
result = await tool.handle()
assert result["success"] is True
assert "updated" in result
class TestMemoryBridgeTool:
"""memory_bridge 工具测试"""
@pytest.mark.asyncio
async def test_bridge_retrieval(self, setup_mcp):
"""测试记忆桥接"""
query_service = setup_mcp["query_service"]
tool = MemoryBridgeTool(query_service)
result = await tool.handle(query="test", source_tool="claude")
assert result["success"] is True
assert "results" in result
class TestEndToEnd:
"""端到端工作流测试"""
@pytest.mark.asyncio
async def test_full_workflow(self, setup_mcp):
"""测试完整工作流:索引 -> 查询 -> 链接"""
db = setup_mcp["db"]
parser = setup_mcp["parser"]
indexer = setup_mcp["indexer"]
query_service = setup_mcp["query_service"]
wiki_path = setup_mcp["wiki_path"]
# 1. 创建新页面
new_page_path = os.path.join(wiki_path, "practices", "new.md")
with open(new_page_path, 'w') as f:
f.write("""---
name: new-practice
description: New practice page
metadata:
type: practice
tags: [new, practice]
---
# New Practice
This is a new practice page.
## Links
- [[concepts/test-concept]]
""")
# 2. 重新索引
await indexer.incremental_update()
# 3. 查询新页面
result = await query_service.get_page("practices/new.md")
assert result is not None
assert result.title == "New Practice"
# 4. 验证链接
links = await db.get_links("practices/new.md")
assert "concepts/test-concept" in links