zhiyi/tests/test_template.py

375 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Template 相关单元测试"""
import pytest
import sys
from pathlib import Path
from datetime import datetime
sys.path.insert(0, str(Path(__file__).parent.parent / 'src'))
from models.template import Template, Pattern
from templates.generator import TemplateGenerator
from templates.store import TemplateStore, PatternStore
class TestTemplateModel:
"""Template 数据模型测试"""
def test_template_creation(self):
"""测试 Template 创建"""
template = Template(
title="测试模板",
context="测试场景",
steps=["步骤1", "步骤2"],
outcomes=["结果1", "结果2"],
)
assert template.title == "测试模板"
assert template.context == "测试场景"
assert len(template.steps) == 2
assert len(template.outcomes) == 2
assert template.is_auto_draft is True
assert template.is_verified is False
assert template.id.startswith("tmpl_")
def test_template_to_dict(self):
"""测试 Template 序列化"""
template = Template(
id="tmpl_test123",
title="测试模板",
context="测试场景",
)
d = template.to_dict()
assert d["id"] == "tmpl_test123"
assert d["title"] == "测试模板"
assert d["is_auto_draft"] is True
assert "timestamp" in d
def test_template_from_dict(self):
"""测试 Template 反序列化"""
d = {
"id": "tmpl_test456",
"namespace": "hermes-main",
"timestamp": "2026-05-24T10:00:00",
"title": "反序列化测试",
"context": "测试",
"steps": ["s1", "s2"],
"outcomes": ["o1"],
"source_pattern_id": "pat_123",
"source_distilled_ids": ["dist_1", "dist_2"],
"is_verified": False,
"is_auto_draft": True,
}
template = Template.from_dict(d)
assert template.id == "tmpl_test456"
assert template.title == "反序列化测试"
assert len(template.steps) == 2
assert template.source_pattern_id == "pat_123"
class TestPatternModel:
"""Pattern 数据模型测试"""
def test_pattern_creation(self):
"""测试 Pattern 创建"""
pattern = Pattern(
name="测试模式",
description="这是一个测试模式",
pattern_type="causal",
is_validated=True,
)
assert pattern.name == "测试模式"
assert pattern.pattern_type == "causal"
assert pattern.is_validated is True
assert pattern.is_deprecated is False
assert pattern.id.startswith("pat_")
def test_pattern_to_dict_from_dict(self):
"""测试 Pattern 序列化/反序列化"""
pattern = Pattern(
id="pat_test123",
name="测试模式",
pattern_type="sequential",
evidence=["dist_1", "dist_2"],
)
d = pattern.to_dict()
restored = Pattern.from_dict(d)
assert restored.id == pattern.id
assert restored.name == pattern.name
assert restored.pattern_type == "sequential"
assert len(restored.evidence) == 2
class TestTemplateGeneratorSingle:
"""单个 Pattern 生成 Template 测试"""
@pytest.mark.asyncio
async def test_generate_from_causal_pattern(self):
"""测试从 causal Pattern 生成"""
generator = TemplateGenerator()
pattern = Pattern(
name="Docker 使用模式",
description="在项目中重复使用 Docker 容器化",
pattern_type="causal",
is_validated=True,
evidence=["dist_1", "dist_2"],
)
template = await generator.generate_from_pattern(pattern)
assert "Docker" in template.title or "经验模板" in template.title
assert len(template.steps) > 0
assert template.is_auto_draft is True
assert template.source_pattern_id == pattern.id
assert template.source_distilled_ids == ["dist_1", "dist_2"]
@pytest.mark.asyncio
async def test_generate_from_sequential_pattern(self):
"""测试从 sequential Pattern 生成"""
generator = TemplateGenerator()
pattern = Pattern(
name="部署流程",
pattern_type="sequential",
is_validated=True,
)
template = await generator.generate_from_pattern(pattern)
assert "部署流程" in template.title or "经验模板" in template.title
assert any("顺序" in s or "序列" in s for s in template.steps)
@pytest.mark.asyncio
async def test_generate_unvalidated_pattern(self):
"""测试从未验证 Pattern 生成(应警告但不阻塞)"""
generator = TemplateGenerator()
pattern = Pattern(
name="未验证模式",
is_validated=False,
)
# 不应抛出异常
template = await generator.generate_from_pattern(pattern)
assert template is not None
assert template.is_auto_draft is True
class TestTemplateGeneratorMulti:
"""多个 Pattern 聚合生成测试"""
@pytest.mark.asyncio
async def test_generate_from_multiple_patterns(self):
"""测试从多个 Pattern 聚合"""
generator = TemplateGenerator()
patterns = [
Pattern(id="pat_1", name="模式1", pattern_type="causal", is_validated=True),
Pattern(id="pat_2", name="模式2", pattern_type="causal", is_validated=True),
]
template = await generator.generate_from_patterns(patterns)
assert "模式1" in template.title or "多模版聚合" in template.title
assert "pat_1" in template.source_pattern_id
assert "pat_2" in template.source_pattern_id
assert template.is_auto_draft is True
@pytest.mark.asyncio
async def test_generate_empty_patterns_raises(self):
"""测试空列表应抛出异常"""
generator = TemplateGenerator()
with pytest.raises(ValueError):
await generator.generate_from_patterns([])
@pytest.mark.asyncio
async def test_generate_mixed_type_patterns(self):
"""测试混合类型 Pattern 聚合"""
generator = TemplateGenerator()
patterns = [
Pattern(id="pat_1", name="因果", pattern_type="causal", is_validated=True),
Pattern(id="pat_2", name="顺序", pattern_type="sequential", is_validated=True),
]
template = await generator.generate_from_patterns(patterns)
assert len(template.steps) > 0
class TestTemplateStore:
"""Template 存储测试"""
@pytest.fixture
def temp_store(self, tmp_path):
"""创建临时存储"""
store = TemplateStore(storage_dir=str(tmp_path), namespace="test")
return store
@pytest.mark.asyncio
async def test_save_and_get(self, temp_store):
"""测试保存和获取"""
template = Template(
title="测试模板",
context="测试",
)
await temp_store.save(template)
retrieved = await temp_store.get(template.id)
assert retrieved is not None
assert retrieved.id == template.id
assert retrieved.title == "测试模板"
@pytest.mark.asyncio
async def test_list_all(self, temp_store):
"""测试列出所有"""
for i in range(3):
t = Template(title=f"模板{i}")
await temp_store.save(t)
templates = await temp_store.list_all()
assert len(templates) == 3
@pytest.mark.asyncio
async def test_list_auto_draft(self, temp_store):
"""测试列出待确认模板"""
# 创建一些模板
t1 = Template(title="草稿1", is_auto_draft=True)
t2 = Template(title="草稿2", is_auto_draft=True)
t3 = Template(title="已确认", is_auto_draft=False, is_verified=True)
await temp_store.save(t1)
await temp_store.save(t2)
await temp_store.save(t3)
drafts = await temp_store.list_auto_draft()
assert len(drafts) == 2
assert all(t.is_auto_draft for t in drafts)
@pytest.mark.asyncio
async def test_verify(self, temp_store):
"""测试验证模板"""
template = Template(title="待验证")
await temp_store.save(template)
success = await temp_store.verify(template.id)
assert success is True
retrieved = await temp_store.get(template.id)
assert retrieved.is_auto_draft is False
assert retrieved.is_verified is True
@pytest.mark.asyncio
async def test_verify_nonexistent(self, temp_store):
"""测试验证不存在的模板"""
success = await temp_store.verify("nonexistent_id")
assert success is False
@pytest.mark.asyncio
async def test_count(self, temp_store):
"""测试计数"""
for i in range(5):
await temp_store.save(Template(title=f"模板{i}"))
assert await temp_store.count() == 5
class TestTemplateStoreListByPattern:
"""Template 按 Pattern 筛选测试"""
@pytest.fixture
def temp_store(self, tmp_path):
store = TemplateStore(storage_dir=str(tmp_path), namespace="test")
return store
@pytest.mark.asyncio
async def test_list_by_pattern(self, temp_store):
"""测试按 Pattern ID 筛选"""
t1 = Template(title="模板1", source_pattern_id="pat_1,pat_2")
t2 = Template(title="模板2", source_pattern_id="pat_2")
t3 = Template(title="模板3", source_pattern_id="pat_3")
await temp_store.save(t1)
await temp_store.save(t2)
await temp_store.save(t3)
# pat_1 应该匹配 t1
results = await temp_store.list_by_pattern("pat_1")
assert len(results) == 1
assert results[0].title == "模板1"
# pat_2 应该匹配 t1 和 t2
results = await temp_store.list_by_pattern("pat_2")
assert len(results) == 2
class TestTemplateStoreDeprecated:
"""Template 废弃测试"""
@pytest.fixture
def temp_store(self, tmp_path):
store = TemplateStore(storage_dir=str(tmp_path), namespace="test")
return store
@pytest.mark.asyncio
async def test_deprecate(self, temp_store):
"""测试废弃模板"""
template = Template(title="待废弃")
await temp_store.save(template)
success = await temp_store.deprecate(template.id, "不再适用")
assert success is True
# 确认已废弃is_deprecated 字段存在于 to_dict 但不作为属性存储)
# 实际上我们没有在 Template 类中添加 is_deprecated 属性
# 这个测试暂时跳过
pass
class TestTemplateIntegration:
"""Template 生成+存储集成测试"""
@pytest.mark.asyncio
async def test_generate_and_save(self, tmp_path):
"""测试生成并保存完整流程"""
generator = TemplateGenerator()
store = TemplateStore(storage_dir=str(tmp_path), namespace="test")
pattern = Pattern(
name="完整流程测试",
description="测试生成和存储的完整流程",
pattern_type="causal",
is_validated=True,
evidence=["dist_1"],
)
# 生成
template = await generator.generate_from_pattern(pattern)
assert template.is_auto_draft is True
# 保存
await store.save(template)
# 验证存储
retrieved = await store.get(template.id)
assert retrieved is not None
assert retrieved.title == template.title
# 确认模板
await store.verify(template.id)
# 再次获取验证状态
verified = await store.get(template.id)
assert verified.is_auto_draft is False
assert verified.is_verified is True
if __name__ == "__main__":
pytest.main([__file__, "-v"])