-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_actions.py
More file actions
253 lines (186 loc) · 9.01 KB
/
test_actions.py
File metadata and controls
253 lines (186 loc) · 9.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
import pytest
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock, patch
from agent_chat_cli.app import AgentChatCLIApp
from agent_chat_cli.components.chat_history import ChatHistory
from agent_chat_cli.components.messages import (
RoleType,
SystemMessage,
UserMessage,
AgentMessage,
)
from agent_chat_cli.components.tool_permission_prompt import ToolPermissionPrompt
from agent_chat_cli.utils.enums import ControlCommand
@pytest.fixture
def mock_agent_loop():
with patch("agent_chat_cli.app.AgentLoop") as mock:
instance = MagicMock()
instance.start = AsyncMock()
instance.query_queue = MagicMock()
instance.query_queue.put = AsyncMock()
instance.query_queue.empty = MagicMock(return_value=True)
instance.permission_response_queue = MagicMock()
instance.permission_response_queue.put = AsyncMock()
instance.client = MagicMock()
instance.client.interrupt = AsyncMock()
mock.return_value = instance
yield instance
@pytest.fixture
def mock_config():
with patch("agent_chat_cli.components.header.load_config") as mock:
mock.return_value = MagicMock(mcp_servers={}, agents={})
yield mock
class TestActionsAddMessageToChat:
async def test_adds_user_message(self, mock_agent_loop, mock_config):
app = AgentChatCLIApp()
async with app.run_test():
chat_history = app.query_one(ChatHistory)
await app.renderer.add_message(RoleType.USER, "Hello")
widgets = chat_history.query(UserMessage)
assert len(widgets) == 1
assert widgets.first().message == "Hello"
async def test_adds_system_message(self, mock_agent_loop, mock_config):
app = AgentChatCLIApp()
async with app.run_test():
chat_history = app.query_one(ChatHistory)
await app.renderer.add_message(RoleType.SYSTEM, "System alert")
widgets = chat_history.query(SystemMessage)
assert len(widgets) == 1
assert widgets.first().message == "System alert"
async def test_adds_agent_message(self, mock_agent_loop, mock_config):
app = AgentChatCLIApp()
async with app.run_test():
chat_history = app.query_one(ChatHistory)
await app.renderer.add_message(RoleType.AGENT, "I can help")
widgets = chat_history.query(AgentMessage)
assert len(widgets) == 1
assert widgets.first().message == "I can help"
async def test_raises_for_unsupported_type(self, mock_agent_loop, mock_config):
app = AgentChatCLIApp()
async with app.run_test():
with pytest.raises(ValueError, match="Unsupported message type"):
await app.renderer.add_message(RoleType.TOOL, "tool content")
class TestActionsPostSystemMessage:
async def test_adds_system_message_to_chat(self, mock_agent_loop, mock_config):
app = AgentChatCLIApp()
async with app.run_test():
chat_history = app.query_one(ChatHistory)
await app.actions.post_system_message("Connection established")
widgets = chat_history.query(SystemMessage)
assert len(widgets) == 1
assert widgets.first().message == "Connection established"
class TestActionsSubmitUserMessage:
async def test_adds_user_message_to_chat(self, mock_agent_loop, mock_config):
app = AgentChatCLIApp()
async with app.run_test():
chat_history = app.query_one(ChatHistory)
await app.actions.post_user_message("Hello agent")
widgets = chat_history.query(UserMessage)
assert len(widgets) == 1
assert widgets.first().message == "Hello agent"
async def test_starts_thinking_indicator(self, mock_agent_loop, mock_config):
from agent_chat_cli.components.thinking_indicator import ThinkingIndicator
app = AgentChatCLIApp()
async with app.run_test():
await app.actions.post_user_message("Hello agent")
thinking_indicator = app.query_one(ThinkingIndicator)
assert thinking_indicator.is_thinking is True
async def test_queues_message_to_agent_loop(self, mock_agent_loop, mock_config):
app = AgentChatCLIApp()
async with app.run_test():
await app.actions.post_user_message("Hello agent")
mock_agent_loop.query_queue.put.assert_called_with("Hello agent")
class TestActionsInterrupt:
async def test_sets_interrupting_flag(self, mock_agent_loop, mock_config):
app = AgentChatCLIApp()
async with app.run_test():
await app.actions.interrupt()
assert app.ui_state.interrupting is True
async def test_calls_client_interrupt(self, mock_agent_loop, mock_config):
app = AgentChatCLIApp()
async with app.run_test():
await app.actions.interrupt()
mock_agent_loop.client.interrupt.assert_called_once()
async def test_blocked_when_permission_prompt_visible(
self, mock_agent_loop, mock_config
):
app = AgentChatCLIApp()
async with app.run_test():
app.ui_state.show_permission_prompt(tool_name="test", tool_input={})
await app.actions.interrupt()
assert app.ui_state.interrupting is False
mock_agent_loop.client.interrupt.assert_not_called()
class TestActionsNew:
async def test_queues_new_conversation_command(self, mock_agent_loop, mock_config):
app = AgentChatCLIApp()
async with app.run_test():
await app.actions.new()
mock_agent_loop.query_queue.put.assert_called_with(
ControlCommand.NEW_CONVERSATION
)
async def test_clears_chat_history(self, mock_agent_loop, mock_config):
app = AgentChatCLIApp()
async with app.run_test():
chat_history = app.query_one(ChatHistory)
initial_children = len(chat_history.children)
await app.actions.new()
assert len(chat_history.children) <= initial_children
class TestActionsRespondToToolPermission:
async def test_queues_response(self, mock_agent_loop, mock_config):
app = AgentChatCLIApp()
async with app.run_test():
app.ui_state.show_permission_prompt(tool_name="test", tool_input={})
await app.actions.respond_to_tool_permission("yes")
mock_agent_loop.permission_response_queue.put.assert_called_with("yes")
async def test_hides_permission_prompt(self, mock_agent_loop, mock_config):
app = AgentChatCLIApp()
async with app.run_test():
app.ui_state.show_permission_prompt(tool_name="test", tool_input={})
await app.actions.respond_to_tool_permission("yes")
prompt = app.query_one(ToolPermissionPrompt)
assert prompt.is_visible is False
async def test_deny_response_queries_agent(self, mock_agent_loop, mock_config):
app = AgentChatCLIApp()
async with app.run_test():
app.ui_state.show_permission_prompt(tool_name="test", tool_input={})
await app.actions.respond_to_tool_permission("no")
calls = mock_agent_loop.query_queue.put.call_args_list
assert any("denied" in str(call).lower() for call in calls)
class TestActionsSave:
async def test_saves_conversation_to_file(
self, mock_agent_loop, mock_config, tmp_path, monkeypatch
):
monkeypatch.setattr(Path, "home", lambda: tmp_path)
app = AgentChatCLIApp()
async with app.run_test():
await app.actions.post_user_message("Hello")
await app.actions.save()
output_dir = tmp_path / ".claude" / "agent-chat-cli"
assert output_dir.exists()
files = list(output_dir.glob("convo-*.md"))
assert len(files) == 1
async def test_adds_system_message_with_file_path(
self, mock_agent_loop, mock_config, tmp_path, monkeypatch
):
monkeypatch.setattr(Path, "home", lambda: tmp_path)
app = AgentChatCLIApp()
async with app.run_test():
chat_history = app.query_one(ChatHistory)
initial_count = len(chat_history.children)
await app.actions.save()
system_messages = chat_history.query(SystemMessage)
assert len(system_messages) == initial_count + 1
last_message = system_messages.last()
assert "Conversation saved to" in last_message.message
assert ".claude/agent-chat-cli/convo-" in last_message.message
async def test_does_not_trigger_thinking(
self, mock_agent_loop, mock_config, tmp_path, monkeypatch
):
from agent_chat_cli.components.thinking_indicator import ThinkingIndicator
monkeypatch.setattr(Path, "home", lambda: tmp_path)
app = AgentChatCLIApp()
async with app.run_test():
app.ui_state.stop_thinking()
await app.actions.save()
thinking_indicator = app.query_one(ThinkingIndicator)
assert thinking_indicator.is_thinking is False