augment-sdk 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,472 @@
1
+ """
2
+ End-to-end tests for AuggieACPClient.
3
+
4
+ These tests verify the client works correctly with the actual Augment CLI agent.
5
+ Uses pytest for test framework.
6
+
7
+ By default, only quick sanity check tests run (test_start_and_stop, test_simple_math_query, test_context_manager).
8
+ To run all tests including slow ones, use: pytest -m ""
9
+ """
10
+
11
+ import pytest
12
+ import time
13
+ from typing import Optional, Any, List, Dict
14
+ from augment.acp import AuggieACPClient, AgentEventListener
15
+
16
+
17
+ class EventListenerForTesting(AgentEventListener):
18
+ """Event listener that captures all events for testing purposes."""
19
+
20
+ def __init__(self):
21
+ self.message_chunks: List[str] = []
22
+ self.complete_messages: List[str] = []
23
+ self.tool_calls: List[Dict[str, Any]] = []
24
+ self.thoughts: List[str] = []
25
+
26
+ def on_agent_message_chunk(self, text: str) -> None:
27
+ self.message_chunks.append(text)
28
+
29
+ def on_agent_message(self, message: str) -> None:
30
+ self.complete_messages.append(message)
31
+
32
+ def on_tool_call(
33
+ self,
34
+ tool_call_id: str,
35
+ title: str,
36
+ kind: Optional[str] = None,
37
+ status: Optional[str] = None,
38
+ ) -> None:
39
+ self.tool_calls.append(
40
+ {
41
+ "type": "call",
42
+ "id": tool_call_id,
43
+ "title": title,
44
+ "kind": kind,
45
+ "status": status,
46
+ }
47
+ )
48
+
49
+ def on_tool_response(
50
+ self,
51
+ tool_call_id: str,
52
+ status: Optional[str] = None,
53
+ content: Optional[Any] = None,
54
+ ) -> None:
55
+ self.tool_calls.append(
56
+ {
57
+ "type": "response",
58
+ "id": tool_call_id,
59
+ "status": status,
60
+ "content": content,
61
+ }
62
+ )
63
+
64
+ def on_agent_thought(self, text: str) -> None:
65
+ self.thoughts.append(text)
66
+
67
+ def reset(self):
68
+ """Reset all captured events."""
69
+ self.message_chunks.clear()
70
+ self.complete_messages.clear()
71
+ self.tool_calls.clear()
72
+ self.thoughts.clear()
73
+
74
+ def get_full_message(self) -> str:
75
+ """Get the complete message from all chunks."""
76
+ return "".join(self.message_chunks)
77
+
78
+
79
+ # ============================================================================
80
+ # Error Handling Tests
81
+ # ============================================================================
82
+
83
+
84
+ def test_invalid_cli_path_raises_file_not_found():
85
+ """Test that providing a non-existent CLI path raises FileNotFoundError."""
86
+ with pytest.raises(FileNotFoundError, match="CLI not found at"):
87
+ AuggieACPClient(cli_path="/path/to/nonexistent/cli")
88
+
89
+
90
+ def test_invalid_cli_path_fails_on_start():
91
+ """Test that starting with an invalid CLI path raises RuntimeError immediately."""
92
+ # Create a temporary file that exists but isn't a valid Node.js script
93
+ import tempfile
94
+ import time
95
+
96
+ with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".txt") as f:
97
+ f.write("not a real CLI")
98
+ temp_path = f.name
99
+
100
+ try:
101
+ client = AuggieACPClient(cli_path=temp_path)
102
+
103
+ # Should raise RuntimeError when the process exits immediately
104
+ # This should happen very quickly (< 1 second), not hang
105
+ start_time = time.time()
106
+ with pytest.raises(RuntimeError, match="Agent process exited"):
107
+ client.start()
108
+ elapsed = time.time() - start_time
109
+
110
+ # Verify it failed quickly (within 2 seconds)
111
+ assert elapsed < 2.0, f"Process exit detection took too long: {elapsed:.2f}s"
112
+ finally:
113
+ import os
114
+
115
+ os.unlink(temp_path)
116
+
117
+
118
+ # ============================================================================
119
+ # Basic Functionality Tests
120
+ # ============================================================================
121
+
122
+
123
+ def test_start_and_stop():
124
+ """Test basic start and stop functionality."""
125
+ client = AuggieACPClient()
126
+
127
+ # Should not be running initially
128
+ assert not client.is_running
129
+ assert client.session_id is None
130
+
131
+ # Start the client
132
+ client.start()
133
+ assert client.is_running
134
+ assert client.session_id is not None
135
+ session_id = client.session_id
136
+ assert len(session_id) == 36 # UUID format
137
+
138
+ # Stop the client
139
+ client.stop()
140
+ assert not client.is_running
141
+
142
+
143
+ def test_simple_math_query():
144
+ """Test sending a simple math query."""
145
+ with AuggieACPClient() as client:
146
+ response = client.send_message("What is 2 + 2? Answer with just the number.")
147
+
148
+ # Should contain the answer
149
+ assert "4" in response
150
+ assert len(response) > 0
151
+
152
+
153
+ @pytest.mark.slow
154
+ def test_multiple_messages_same_session():
155
+ """Test sending multiple messages in the same session."""
156
+ with AuggieACPClient() as client:
157
+ session_id = client.session_id
158
+
159
+ # First message
160
+ response1 = client.send_message("What is 5 + 3?")
161
+ assert "8" in response1
162
+
163
+ # Session should remain the same
164
+ assert client.session_id == session_id
165
+
166
+ # Second message - agent should remember context
167
+ response2 = client.send_message("What is that number times 2?")
168
+ assert "16" in response2
169
+
170
+ # Session should still be the same
171
+ assert client.session_id == session_id
172
+
173
+
174
+ def test_context_manager():
175
+ """Test context manager automatically starts and stops."""
176
+ client = AuggieACPClient()
177
+ assert not client.is_running
178
+
179
+ with client:
180
+ assert client.is_running
181
+ response = client.send_message("What is 10 * 5?")
182
+ assert "50" in response
183
+
184
+ # Should be stopped after exiting context
185
+ assert not client.is_running
186
+
187
+
188
+ @pytest.mark.slow
189
+ def test_clear_context():
190
+ """Test clearing context creates a new session."""
191
+ client = AuggieACPClient()
192
+ client.start()
193
+
194
+ try:
195
+ # Get initial session
196
+ session1 = client.session_id
197
+
198
+ # Send a message
199
+ client.send_message("Remember the number 42")
200
+
201
+ # Clear context
202
+ client.clear_context()
203
+
204
+ # Should have a new session
205
+ session2 = client.session_id
206
+ assert session1 != session2
207
+ assert client.is_running
208
+
209
+ # Agent should not remember the previous conversation
210
+ response = client.send_message("What number did I tell you to remember?")
211
+ # Response should indicate agent doesn't remember
212
+ assert any(
213
+ word in response.lower()
214
+ for word in ["don't", "no record", "haven't", "didn't"]
215
+ )
216
+ finally:
217
+ client.stop()
218
+
219
+
220
+ # ============================================================================
221
+ # Event Listener Tests
222
+ # ============================================================================
223
+
224
+
225
+ @pytest.mark.slow
226
+ @pytest.mark.skip(reason="CLI does not currently send agent_message_end events")
227
+ def test_event_listener_messages():
228
+ """Test event listener receives agent messages."""
229
+ listener = EventListenerForTesting()
230
+
231
+ with AuggieACPClient(listener=listener) as client:
232
+ response = client.send_message("What is 7 + 3?")
233
+
234
+ # Give a moment for the agent_message_end event to arrive
235
+ # (it may arrive slightly after send_message returns)
236
+ time.sleep(0.5)
237
+
238
+ # Listener should have received message chunks
239
+ assert len(listener.message_chunks) > 0
240
+
241
+ # Combined message should match response
242
+ full_message = listener.get_full_message()
243
+ assert full_message.strip() == response.strip()
244
+
245
+ # Should also have received the complete message
246
+ assert len(listener.complete_messages) > 0
247
+ assert listener.complete_messages[0].strip() == response.strip()
248
+
249
+
250
+ @pytest.mark.slow
251
+ @pytest.mark.timeout(30)
252
+ @pytest.mark.skip(
253
+ reason="Test times out - tool call events may not be working correctly"
254
+ )
255
+ def test_event_listener_tool_calls():
256
+ """Test event listener receives tool call events."""
257
+ listener = EventListenerForTesting()
258
+
259
+ with AuggieACPClient(listener=listener) as client:
260
+ # Send a message that will trigger a tool call
261
+ client.send_message(
262
+ "Read the file experimental/guy/auggie_sdk/QUICK_START.md", timeout=30.0
263
+ )
264
+
265
+ # Should have received tool call events
266
+ assert len(listener.tool_calls) > 0
267
+
268
+ # Should have at least one "call" event
269
+ call_events = [tc for tc in listener.tool_calls if tc["type"] == "call"]
270
+ assert len(call_events) > 0
271
+
272
+ # Should have at least one "response" event
273
+ response_events = [tc for tc in listener.tool_calls if tc["type"] == "response"]
274
+ assert len(response_events) > 0
275
+
276
+ # At least one tool call should be "view" (for reading the file)
277
+ tool_titles = [tc.get("title") for tc in listener.tool_calls]
278
+ assert "view" in tool_titles
279
+
280
+
281
+ # ============================================================================
282
+ # Timeout and Error Handling Tests
283
+ # ============================================================================
284
+
285
+
286
+ @pytest.mark.slow
287
+ def test_timeout_handling():
288
+ """Test that timeout parameter works."""
289
+ with AuggieACPClient() as client:
290
+ # Short timeout should still work for simple queries
291
+ response = client.send_message("What is 1 + 1?", timeout=5.0)
292
+ assert "2" in response
293
+
294
+
295
+ @pytest.mark.slow
296
+ def test_error_when_not_started():
297
+ """Test that sending message without starting raises error."""
298
+ client = AuggieACPClient()
299
+
300
+ with pytest.raises(RuntimeError) as exc_info:
301
+ client.send_message("Hello")
302
+
303
+ assert "not started" in str(exc_info.value).lower()
304
+
305
+
306
+ @pytest.mark.slow
307
+ def test_double_start_raises_error():
308
+ """Test that starting an already started client raises error."""
309
+ client = AuggieACPClient()
310
+ client.start()
311
+
312
+ try:
313
+ with pytest.raises(RuntimeError) as exc_info:
314
+ client.start()
315
+
316
+ assert "already started" in str(exc_info.value).lower()
317
+ finally:
318
+ client.stop()
319
+
320
+
321
+ # ============================================================================
322
+ # Session Management Tests
323
+ # ============================================================================
324
+
325
+
326
+ @pytest.mark.slow
327
+ def test_multiple_sequential_sessions():
328
+ """Test starting and stopping multiple times."""
329
+ client = AuggieACPClient()
330
+
331
+ # First session
332
+ client.start()
333
+ session1 = client.session_id
334
+ response1 = client.send_message("What is 2 + 2?")
335
+ assert "4" in response1
336
+ client.stop()
337
+
338
+ # Second session
339
+ client.start()
340
+ session2 = client.session_id
341
+ response2 = client.send_message("What is 3 + 3?")
342
+ assert "6" in response2
343
+ client.stop()
344
+
345
+ # Sessions should be different
346
+ assert session1 != session2
347
+
348
+
349
+ @pytest.mark.slow
350
+ @pytest.mark.timeout(30)
351
+ def test_long_response():
352
+ """Test handling longer responses."""
353
+ with AuggieACPClient() as client:
354
+ response = client.send_message(
355
+ "List three programming languages. Be brief.", timeout=30.0
356
+ )
357
+
358
+ # Should get a response with some content
359
+ assert len(response) > 10
360
+
361
+
362
+ @pytest.mark.slow
363
+ @pytest.mark.timeout(30)
364
+ @pytest.mark.skip(reason="Tool call events not being received from CLI")
365
+ def test_file_operation_tool_call():
366
+ """Test that file operations trigger appropriate tool calls."""
367
+ listener = EventListenerForTesting()
368
+
369
+ with AuggieACPClient(listener=listener) as client:
370
+ response = client.send_message(
371
+ "What is in the file experimental/guy/auggie_sdk/QUICK_START.md? Summarize in one sentence.",
372
+ timeout=30.0,
373
+ )
374
+
375
+ # Should have triggered a view tool call
376
+ tool_titles = [
377
+ tc.get("title") for tc in listener.tool_calls if tc["type"] == "start"
378
+ ]
379
+ assert "view" in tool_titles
380
+
381
+ # Response should mention something about the file
382
+ assert len(response) > 20
383
+
384
+
385
+ @pytest.mark.slow
386
+ def test_listener_can_be_none():
387
+ """Test that listener is optional."""
388
+ # Should work fine without a listener
389
+ with AuggieACPClient(listener=None) as client:
390
+ response = client.send_message("What is 5 * 5?")
391
+ assert "25" in response
392
+
393
+
394
+ @pytest.mark.slow
395
+ def test_session_persistence():
396
+ """Test that session persists across multiple messages."""
397
+ listener = EventListenerForTesting()
398
+
399
+ with AuggieACPClient(listener=listener) as client:
400
+ session_id = client.session_id
401
+
402
+ # Send multiple messages
403
+ for i in range(3):
404
+ listener.reset()
405
+ client.send_message(f"What is {i} + 1?")
406
+
407
+ # Session should remain the same
408
+ assert client.session_id == session_id
409
+
410
+ # Should get message chunks each time
411
+ assert len(listener.message_chunks) > 0
412
+
413
+
414
+ # ============================================================================
415
+ # Integration Tests
416
+ # ============================================================================
417
+
418
+
419
+ @pytest.mark.slow
420
+ def test_conversation_flow():
421
+ """Test a realistic conversation flow."""
422
+ listener = EventListenerForTesting()
423
+
424
+ with AuggieACPClient(listener=listener) as client:
425
+ # Start a conversation
426
+ response1 = client.send_message("What is 10 + 5?")
427
+ assert "15" in response1
428
+
429
+ # Continue the conversation
430
+ listener.reset()
431
+ response2 = client.send_message("What is that divided by 3?")
432
+ assert "5" in response2
433
+
434
+ # Clear context and start fresh
435
+ client.clear_context()
436
+ listener.reset()
437
+
438
+ # Agent should not remember the previous numbers
439
+ response3 = client.send_message("What was the last number we calculated?")
440
+ assert any(
441
+ word in response3.lower() for word in ["don't", "no record", "haven't"]
442
+ )
443
+
444
+
445
+ @pytest.mark.slow
446
+ @pytest.mark.timeout(30)
447
+ @pytest.mark.skip(reason="Tool call events not being received from CLI")
448
+ def test_tool_usage_workflow():
449
+ """Test a workflow that involves tool usage."""
450
+ listener = EventListenerForTesting()
451
+
452
+ with AuggieACPClient(listener=listener) as client:
453
+ # Ask agent to read a file
454
+ response = client.send_message(
455
+ "Read experimental/guy/auggie_sdk/QUICK_START.md and tell me what it's about in 5 words or less.",
456
+ timeout=30.0,
457
+ )
458
+
459
+ # Should have used the view tool
460
+ tool_titles = [
461
+ tc.get("title") for tc in listener.tool_calls if tc["type"] == "start"
462
+ ]
463
+ assert "view" in tool_titles
464
+
465
+ # Should have gotten a response
466
+ assert len(response) > 5
467
+
468
+ # Should have received completion status
469
+ statuses = [
470
+ tc.get("status") for tc in listener.tool_calls if tc["type"] == "update"
471
+ ]
472
+ assert "completed" in statuses