Python · 4006 bytes Raw Blame History
1 """Tests for Sprint 06 tool-surface expansion."""
2
3 from __future__ import annotations
4
5 import json
6 import subprocess
7 from pathlib import Path
8
9 import pytest
10
11 from loader.tools.base import create_default_registry
12 from loader.tools.file_tools import PatchTool
13 from loader.tools.git_tools import GitTool
14 from loader.tools.workflow_tools import AskUserQuestionTool
15
16
17 @pytest.mark.asyncio
18 async def test_patch_tool_applies_structured_hunks(temp_dir: Path) -> None:
19 target = temp_dir / "sample.txt"
20 target.write_text("alpha\nbeta\ngamma\n")
21 tool = PatchTool(workspace_root=temp_dir)
22
23 result = await tool.execute(
24 file_path=str(target),
25 hunks=[
26 {
27 "old_start": 2,
28 "old_lines": 1,
29 "new_start": 2,
30 "new_lines": 1,
31 "lines": ["-beta", "+beta updated"],
32 }
33 ],
34 )
35
36 assert result.is_error is False
37 assert target.read_text() == "alpha\nbeta updated\ngamma\n"
38 assert result.metadata["structured_patch"]
39
40
41 @pytest.mark.asyncio
42 async def test_patch_tool_rejects_context_mismatch(temp_dir: Path) -> None:
43 target = temp_dir / "sample.txt"
44 target.write_text("alpha\nbeta\ngamma\n")
45 tool = PatchTool(workspace_root=temp_dir)
46
47 result = await tool.execute(
48 file_path=str(target),
49 hunks=[
50 {
51 "old_start": 2,
52 "old_lines": 1,
53 "new_start": 2,
54 "new_lines": 1,
55 "lines": ["-wrong line", "+beta updated"],
56 }
57 ],
58 )
59
60 assert result.is_error is True
61 assert "context mismatch" in result.output
62
63
64 @pytest.mark.asyncio
65 async def test_git_tool_inspects_read_only_repo_state(temp_dir: Path) -> None:
66 subprocess.run(["git", "init", "--quiet"], cwd=temp_dir, check=True)
67 (temp_dir / "README.md").write_text("loader\n")
68
69 tool = GitTool(workspace_root=temp_dir)
70 status = await tool.execute(action="status", args=["--short"], cwd=".")
71 branch = await tool.execute(action="branch", args=["--show-current"], cwd=".")
72
73 assert status.is_error is False
74 assert "README.md" in status.output
75 assert branch.is_error is False
76
77
78 @pytest.mark.asyncio
79 async def test_notepad_append_alias_updates_selected_section(temp_dir: Path) -> None:
80 registry = create_default_registry(temp_dir)
81
82 working = await registry.execute(
83 "notepad_append",
84 content="Investigate explore runtime.",
85 section="working",
86 )
87 manual = await registry.execute(
88 "notepad_append",
89 content="Keep read-only mode strict.",
90 section="manual",
91 )
92 notepad = await registry.execute("notepad_read", section="all")
93
94 assert working.is_error is False
95 assert manual.is_error is False
96 assert "Investigate explore runtime." in notepad.output
97 assert "Keep read-only mode strict." in notepad.output
98
99
100 @pytest.mark.asyncio
101 async def test_richer_ask_user_question_formats_context_and_options() -> None:
102 tool = AskUserQuestionTool()
103
104 async def answer(question: str, options: list[str] | None) -> str:
105 assert "Fix Strategy" in question
106 assert "Pick the safer repair path." in question
107 assert "Which fix should Loader apply?" in question
108 assert options == [
109 "Minimal patch - touch the parser only",
110 "Broader refactor - clean up parser and tests",
111 ]
112 return "1"
113
114 result = await tool.execute(
115 title="Fix Strategy",
116 context="Pick the safer repair path.",
117 question="Which fix should Loader apply?",
118 options=[
119 {"label": "Minimal patch", "description": "touch the parser only"},
120 {"label": "Broader refactor", "description": "clean up parser and tests"},
121 ],
122 user_response_handler=answer,
123 )
124
125 payload = json.loads(result.output)
126 assert result.is_error is False
127 assert payload["answer"] == "Minimal patch - touch the parser only"