为memory 和dream添加测试

This commit is contained in:
hc 2026-04-15 16:01:14 +08:00
parent 5bdc1bee18
commit 5b78f7cf80
2 changed files with 299 additions and 0 deletions

140
tests/test_dream.py Normal file
View File

@ -0,0 +1,140 @@
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 "已完成 dreammemory 已更新"
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 "已完成 dreammemory 已更新" 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 == "已完成 dreammemory 已更新"
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

159
tests/test_memory.py Normal file
View File

@ -0,0 +1,159 @@
from __future__ import annotations
import sys
from pathlib import Path
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from cc_slim.memory import MemoryStore
def make_store(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> tuple[MemoryStore, Path]:
home = tmp_path / "home"
home.mkdir()
monkeypatch.setattr("cc_slim.memory.Path.home", lambda: home)
workspace = tmp_path / "workspace"
workspace.mkdir()
return MemoryStore(workspace), workspace
def test_memory_initializes_structured_template(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
store, _ = make_store(monkeypatch, tmp_path)
content = store.read()
assert content.startswith("# Memory")
assert "## User Memory" in content
assert "## Project Memory" in content
assert "## Constraints" in content
assert "## Consolidated Facts" in content
assert "## Scratch Notes" in content
def test_memory_migrates_v1_text_into_scratch_notes(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
store, workspace = make_store(monkeypatch, tmp_path)
store.path().write_text("old note\nsecond line\n", encoding="utf-8")
migrated = MemoryStore(workspace)
sections = migrated.read_sections()
assert sections["Scratch Notes"] == "- old note\n second line"
def test_read_sections_parses_structured_memory(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
store, _ = make_store(monkeypatch, tmp_path)
store.path().write_text(
"""# Memory
## User Memory
- prefers concise output
## Project Memory
- use uv run
## Constraints
- windows first
## Consolidated Facts
- workspace is repo root
## Scratch Notes
- temporary note
""",
encoding="utf-8",
)
sections = store.read_sections()
assert sections["User Memory"] == "- prefers concise output"
assert sections["Project Memory"] == "- use uv run"
assert sections["Constraints"] == "- windows first"
assert sections["Consolidated Facts"] == "- workspace is repo root"
assert sections["Scratch Notes"] == "- temporary note"
def test_apply_dream_updates_structured_sections_only(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
store, _ = make_store(monkeypatch, tmp_path)
store.path().write_text(
"""# Memory
## User Memory
- old user
## Project Memory
- old project
## Constraints
- old constraint
## Consolidated Facts
- old fact
## Scratch Notes
- keep me
""",
encoding="utf-8",
)
store.apply_dream(
"""## User Memory
- new user
## Project Memory
- new project
## Constraints
- new constraint
## Consolidated Facts
- new fact
"""
)
sections = store.read_sections()
assert sections["User Memory"] == "- new user"
assert sections["Project Memory"] == "- new project"
assert sections["Constraints"] == "- new constraint"
assert sections["Consolidated Facts"] == "- new fact"
assert sections["Scratch Notes"] == "- keep me"
def test_apply_dream_keeps_unmentioned_sections(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
store, _ = make_store(monkeypatch, tmp_path)
store.path().write_text(
"""# Memory
## User Memory
- old user
## Project Memory
- old project
## Constraints
- old constraint
## Consolidated Facts
- old fact
## Scratch Notes
- keep me
""",
encoding="utf-8",
)
store.apply_dream(
"""## Project Memory
- updated project
## Consolidated Facts
- updated fact
"""
)
sections = store.read_sections()
assert sections["User Memory"] == "- old user"
assert sections["Project Memory"] == "- updated project"
assert sections["Constraints"] == "- old constraint"
assert sections["Consolidated Facts"] == "- updated fact"
assert sections["Scratch Notes"] == "- keep me"