144 lines
4.3 KiB
Python
144 lines
4.3 KiB
Python
"""#11 Bootstrap 四段式拼装单元测试(v2.1)
|
|
|
|
覆盖:
|
|
- T1: build(role, task_context) 4 段结构
|
|
- T2: token 估算 + 预算告警
|
|
- T3: 缺失组件降级
|
|
- T4: build_for_task 便捷方法
|
|
|
|
"""
|
|
|
|
import pytest
|
|
|
|
pytestmark = pytest.mark.unit
|
|
from unittest.mock import patch
|
|
from pathlib import Path
|
|
|
|
from src.daemon.bootstrap import BootstrapBuilder, estimate_tokens
|
|
|
|
|
|
@pytest.fixture
|
|
def builder():
|
|
return BootstrapBuilder(max_tokens=4096)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# T1: build(role, task_context) 4 段结构
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestFourSectionBuild:
|
|
def test_basic_executor_build(self, builder):
|
|
b = builder.build(
|
|
role="executor",
|
|
task_context={"task_id": "t1", "title": "Write tests", "description": "Write unit tests",
|
|
"must_haves": "100% coverage", "status": "claimed"},
|
|
)
|
|
# 段: 任务上下文
|
|
assert "Write tests" in b
|
|
assert "t1" in b
|
|
assert "100% coverage" in b
|
|
|
|
def test_basic_reviewer_build(self, builder):
|
|
b = builder.build(
|
|
role="reviewer",
|
|
task_context={"task_id": "t2", "title": "Review PR"},
|
|
)
|
|
assert "Review PR" in b
|
|
|
|
def test_planner_build(self, builder):
|
|
b = builder.build(
|
|
role="planner",
|
|
task_context={"task_id": "t3", "title": "Plan sprint"},
|
|
)
|
|
assert "Plan sprint" in b
|
|
|
|
def test_depends_on_outputs_injected(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
|
|
|
|
def test_no_depends_on_omitted(self, builder):
|
|
b = builder.build(
|
|
role="executor",
|
|
task_context={"title": "T"},
|
|
)
|
|
assert "前序产出" not 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
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# T3: 缺失组件降级
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestGracefulDegradation:
|
|
def test_empty_task(self, builder):
|
|
b = builder.build(role="executor", task_context={})
|
|
assert b # 不为空
|
|
assert "# Role: executor" in b
|
|
|
|
def test_partial_task(self, builder):
|
|
b = builder.build(role="executor", task_context={"title": "Only title"})
|
|
assert "Only title" in b
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# T4: build_for_task 便捷方法
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestBuildForTask:
|
|
def test_build_for_task_object(self, builder):
|
|
"""用 mock task 对象测试 build_for_task"""
|
|
class MockTask:
|
|
id = "t1"
|
|
title = "Build Feature"
|
|
description = "Implement X with tests"
|
|
must_haves = "Unit tests, Documentation"
|
|
task_type = "coding"
|
|
risk_level = "low"
|
|
task = MockTask()
|
|
b = builder.build_for_task(task, role="executor")
|
|
assert "Build Feature" in b
|
|
assert "Implement X" in b
|
|
|
|
def test_build_for_task_ignores_kwargs(self, builder):
|
|
"""build_for_task accepts project_config and experiences"""
|
|
class MockTask:
|
|
id = "t1"
|
|
title = "T"
|
|
description = ""
|
|
must_haves = ""
|
|
task_type = "coding"
|
|
risk_level = "low"
|
|
task = MockTask()
|
|
# project_config/experiences are valid kwargs for build_for_task
|
|
b = builder.build_for_task(
|
|
task, role="executor",
|
|
project_config={"name": "Old"},
|
|
experiences=[{"x": 1}],
|
|
)
|
|
assert "T" in b
|
|
|
|
|
|
|