为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