141 lines
4.9 KiB
Python
141 lines
4.9 KiB
Python
from __future__ import annotations
|
||
|
||
import io
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
from rich.console import Console
|
||
|
||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||
|
||
from cc_slim.commands import handle_command
|
||
from cc_slim.engine import Agent, Config
|
||
from cc_slim.memory import MemoryStore
|
||
from cc_slim.mode import ModeState
|
||
from cc_slim.permissions import PermissionChecker
|
||
from cc_slim.session import SessionStore
|
||
from cc_slim.tools import build_default_tools
|
||
|
||
|
||
def make_workspace(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> tuple[Path, MemoryStore]:
|
||
home = tmp_path / "home"
|
||
home.mkdir()
|
||
monkeypatch.setattr("cc_slim.memory.Path.home", lambda: home)
|
||
monkeypatch.setattr("cc_slim.session.Path.home", lambda: home)
|
||
|
||
workspace = tmp_path / "workspace"
|
||
workspace.mkdir()
|
||
(workspace / "AGENTS.md").write_text("# Workspace Rules\nworkspace-agents\n", encoding="utf-8")
|
||
skills = workspace / "SKILLS"
|
||
skills.mkdir()
|
||
(skills / "a.md").write_text("skill-a\n", encoding="utf-8")
|
||
memory = MemoryStore(workspace)
|
||
return workspace, memory
|
||
|
||
|
||
def make_agent(monkeypatch: pytest.MonkeyPatch, workspace: Path) -> Agent:
|
||
monkeypatch.setattr(Agent, "_build_client", lambda self: object())
|
||
config = Config(provider="openai", model="test-model", api_key="key", base_url=None)
|
||
permissions = PermissionChecker(workspace)
|
||
return Agent(config=config, tools=build_default_tools(workspace), workspace=workspace, permission_checker=permissions)
|
||
|
||
|
||
def test_dream_command_is_routed(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
|
||
workspace, memory = make_workspace(monkeypatch, tmp_path)
|
||
output = io.StringIO()
|
||
console = Console(file=output, force_terminal=False, color_system=None)
|
||
|
||
class DummyAgent:
|
||
def __init__(self) -> None:
|
||
self.called = False
|
||
|
||
def dream(self, store: MemoryStore) -> str:
|
||
self.called = True
|
||
assert store is memory
|
||
return "已完成 dream,memory 已更新"
|
||
|
||
agent = DummyAgent()
|
||
result = handle_command(
|
||
"/dream",
|
||
console=console,
|
||
root=workspace,
|
||
config=type("ConfigObj", (), {"model": "test-model"})(),
|
||
store=SessionStore(workspace),
|
||
memory=memory,
|
||
mode=ModeState(),
|
||
permissions=PermissionChecker(workspace),
|
||
agent=agent, # type: ignore[arg-type]
|
||
build_agent=lambda *args, **kwargs: agent,
|
||
render_history=lambda store: None,
|
||
)
|
||
|
||
assert agent.called is True
|
||
assert result is agent
|
||
assert "已完成 dream,memory 已更新" in output.getvalue()
|
||
|
||
|
||
def test_agent_dream_updates_memory_and_refreshes_prompt(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
|
||
workspace, memory = make_workspace(monkeypatch, tmp_path)
|
||
agent = make_agent(monkeypatch, workspace)
|
||
agent.history = [{"role": "user", "content": "请记住这个项目运行在 Windows 上"}]
|
||
|
||
original_prompt = agent.system_prompt
|
||
monkeypatch.setattr(
|
||
agent,
|
||
"_run_dream_model",
|
||
lambda current_memory, session_text: """## Constraints
|
||
- 项目默认运行在 Windows 上
|
||
|
||
## Consolidated Facts
|
||
- 需要优先考虑 PowerShell 兼容性
|
||
""",
|
||
)
|
||
|
||
result = agent.dream(memory)
|
||
sections = memory.read_sections()
|
||
|
||
assert result == "已完成 dream,memory 已更新"
|
||
assert sections["Constraints"] == "- 项目默认运行在 Windows 上"
|
||
assert sections["Consolidated Facts"] == "- 需要优先考虑 PowerShell 兼容性"
|
||
assert agent.system_prompt != original_prompt
|
||
assert "项目默认运行在 Windows 上" in agent.system_prompt
|
||
|
||
|
||
def test_dream_does_not_modify_workspace_agents(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
|
||
workspace, memory = make_workspace(monkeypatch, tmp_path)
|
||
agent = make_agent(monkeypatch, workspace)
|
||
agent.history = [{"role": "user", "content": "记住这个项目使用 uv run"}]
|
||
agents_before = (workspace / "AGENTS.md").read_text(encoding="utf-8")
|
||
|
||
monkeypatch.setattr(
|
||
agent,
|
||
"_run_dream_model",
|
||
lambda current_memory, session_text: """## Project Memory
|
||
- 使用 uv run 执行命令
|
||
""",
|
||
)
|
||
|
||
agent.dream(memory)
|
||
|
||
assert (workspace / "AGENTS.md").read_text(encoding="utf-8") == agents_before
|
||
|
||
|
||
def test_system_prompt_layers_remain_in_order(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
|
||
workspace, memory = make_workspace(monkeypatch, tmp_path)
|
||
memory.apply_dream(
|
||
"""## Project Memory
|
||
- project-memory-marker
|
||
"""
|
||
)
|
||
agent = make_agent(monkeypatch, workspace)
|
||
prompt = agent.system_prompt
|
||
|
||
system_index = prompt.index("# cc-slim System")
|
||
runtime_index = prompt.index("## 运行环境")
|
||
agents_index = prompt.index("workspace-agents")
|
||
skills_index = prompt.index("skill-a")
|
||
memory_index = prompt.index("# Memory")
|
||
|
||
assert system_index < runtime_index < agents_index < skills_index < memory_index
|