auto-sync: 2026-05-17 06:03:31

This commit is contained in:
cfdaily
2026-05-17 06:03:31 +08:00
parent bfe3cb7ae6
commit f74fae6d97
+212
View File
@@ -0,0 +1,212 @@
"""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) <= 15 # 接近限制
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