为memory 和dream添加测试
This commit is contained in:
parent
5bdc1bee18
commit
5b78f7cf80
140
tests/test_dream.py
Normal file
140
tests/test_dream.py
Normal 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 "已完成 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
|
||||
159
tests/test_memory.py
Normal file
159
tests/test_memory.py
Normal 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"
|
||||
Loading…
Reference in New Issue
Block a user