Files
sanguo_moziplus_v2/tests/test_bootstrap.py
T
2026-05-17 06:06:12 +08:00

213 lines
6.9 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.
"""F11 Bootstrap 拼装单元测试
按 test-plan-v2.6.md §F11
- T1: 各 role 拼装(P0
- T2: token 估算(P0
- T3: 缺失组件降级(P1
- T4: 模板变量替换(P1
"""
import pytest
from pathlib import Path
from src.blackboard.models import Task
from src.daemon.bootstrap import BootstrapBuilder, estimate_tokens
@pytest.fixture
def builder():
return BootstrapBuilder(max_tokens=4096)
@pytest.fixture
def builder_with_templates(tmp_path):
template_dir = tmp_path / "templates"
template_dir.mkdir()
(template_dir / "executor.md").write_text("# Executor Role\nYou execute tasks.")
(template_dir / "reviewer.md").write_text("# Reviewer Role\nYou review code.")
(template_dir / "planner.md").write_text("# Planner Role\nYou plan tasks.")
return BootstrapBuilder(template_dir=template_dir, max_tokens=4096)
# ---------------------------------------------------------------------------
# T1: 各 role 拼装
# ---------------------------------------------------------------------------
class TestRoleBootstrap:
def test_executor_bootstrap(self, builder):
b = builder.build(
role="executor",
task_context={"task_id": "t1", "title": "Write tests"},
)
assert "Write tests" in b
assert "t1" in b
def test_reviewer_bootstrap(self, builder):
b = builder.build(
role="reviewer",
task_context={"task_id": "t2", "title": "Review PR"},
)
assert "Review PR" in b
def test_planner_bootstrap(self, builder):
b = builder.build(
role="planner",
task_context={"task_id": "t3", "title": "Plan sprint"},
)
assert "Plan sprint" in b
def test_executor_with_guardrail(self, builder):
b = builder.build(
role="executor",
guardrail_rules="## Guardrail\nNo dangerous ops",
)
assert "Guardrail" in b
def test_reviewer_no_guardrail(self, builder):
b = builder.build(
role="reviewer",
guardrail_rules="## Guardrail\nNo dangerous ops",
)
assert "Guardrail" not in b # reviewer 不注入 guardrail
def test_executor_with_review_protocol(self, builder):
b = builder.build(
role="executor",
review_protocols="## Review Protocol\nCheck tests",
)
assert "Review Protocol" in b
def test_reviewer_with_review_protocol(self, builder):
b = builder.build(
role="reviewer",
review_protocols="## Review Protocol\nCheck quality",
)
assert "Review Protocol" in b
def test_with_template(self, builder_with_templates):
b = builder_with_templates.build(role="executor")
assert "Executor Role" in b
assert "You execute tasks" in b
# ---------------------------------------------------------------------------
# T2: token 估算
# ---------------------------------------------------------------------------
class TestTokenEstimation:
def test_estimate_tokens_basic(self):
assert estimate_tokens("hello") > 0
def test_estimate_tokens_long_text(self):
text = "a" * 1000
tokens = estimate_tokens(text)
assert 200 < tokens < 400 # ~333
def test_bootstrap_under_limit(self, builder):
b = builder.build(
role="executor",
task_context={"title": "Short task"},
)
assert estimate_tokens(b) <= 4096
def test_bootstrap_over_limit_truncates(self):
builder = BootstrapBuilder(max_tokens=10)
b = builder.build(
role="executor",
task_context={"title": "A" * 10000},
)
assert estimate_tokens(b) <= 20 # 接近限制
assert "truncated" in b
# ---------------------------------------------------------------------------
# T3: 缺失组件降级
# ---------------------------------------------------------------------------
class TestGracefulDegradation:
def test_no_task_context(self, builder):
b = builder.build(role="executor")
assert b # 不为空
def test_no_project_context(self, builder):
b = builder.build(
role="executor",
task_context={"title": "Task"},
)
assert "Task" in b
def test_no_template(self, builder):
b = builder.build(role="executor")
assert b # 没有 template 也不崩溃
def test_empty_experiences(self, builder):
b = builder.build(role="executor", experiences=[])
assert b
def test_template_dir_not_exists(self, tmp_path):
builder = BootstrapBuilder(template_dir=tmp_path / "nonexistent")
b = builder.build(role="executor")
assert b # 不崩溃
# ---------------------------------------------------------------------------
# T4: 便捷方法 + 项目上下文
# ---------------------------------------------------------------------------
class TestBuildForTask:
def test_build_for_task_object(self, builder):
task = Task(
id="t1", title="Build Feature", status="pending",
assigned_by="daemon", task_type="coding",
description="Implement X with tests",
must_haves="Unit tests, Documentation",
risk_level="high",
)
b = builder.build_for_task(task, role="executor")
assert "Build Feature" in b
assert "Implement X" in b
assert "high" in b
def test_build_for_task_with_project(self, builder):
task = Task(id="t1", title="T", status="pending", assigned_by="d")
b = builder.build_for_task(
task, role="executor",
project_config={"name": "My Project", "agents": ["a1", "a2"]},
)
assert "My Project" in b
assert "a1" in b
def test_with_experiences(self, builder):
task = Task(id="t1", title="T", status="pending", assigned_by="d")
experiences = [
{"category": "pitfall", "summary": "Always test edge cases"},
{"category": "best_practice", "summary": "Use type hints"},
]
b = builder.build_for_task(task, role="executor", experiences=experiences)
assert "pitfall" in b
assert "Always test edge cases" in b
def test_with_skills(self, builder):
skills = [
{"name": "code-review", "description": "Review code quality"},
]
b = builder.build(
role="executor",
skill_descriptions=skills,
)
assert "code-review" in b
assert "Review code quality" in b
def test_with_depends_on_outputs(self, builder):
b = builder.build(
role="executor",
task_context={
"title": "T",
"depends_on_outputs": [
{"task_id": "t0", "summary": "Data downloaded"},
],
},
)
assert "前序产出" in b
assert "Data downloaded" in b