Skip to content

Commit 6f374be

Browse files
committed
Add workspace edit flow acceptance test
New acceptance test covers hash-anchored read/edit, git status, test command execution, git diff inspection, and final audit summary Update acceptance docs to mark workspace edit flow as completed
1 parent 1b5edf5 commit 6f374be

2 files changed

Lines changed: 105 additions & 1 deletion

File tree

docs/acceptance.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ TeaAgent acceptance tests live under `tests/acceptance/` and verify user-facing
1212
- A2A federation flow: remote discovery, partial endpoint failure, capability routing, delegation, context forwarding, and agent trace metadata.
1313
- Managed runtime flow: tool metadata context construction, workspace/request context forwarding, persisted managed task audit events, and result trace metadata.
1414
- Long-running worker flow: background worker start, list, show, log tail, and stop lifecycle.
15+
- Workspace edit flow: hash-anchored read/edit, git status, test command execution, git diff inspection, and final diff summary.
1516

1617
## Next User Stories
1718

1819
- Live provider conformance gated by environment variables.
19-
- Workspace edit flow covering hash reads, patch application, git status, test execution, and final diff summary.
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
from __future__ import annotations
2+
3+
import io
4+
import json
5+
import subprocess
6+
import sys
7+
import tempfile
8+
import unittest
9+
from contextlib import redirect_stdout
10+
from pathlib import Path
11+
12+
from conftest import FakeAdapter
13+
14+
from teaagent.cli import main
15+
from teaagent.workspace_tools._helpers import compute_line_hash
16+
17+
18+
class WorkspaceEditFlowAcceptanceTests(unittest.TestCase):
19+
def test_hash_read_edit_git_test_and_diff_summary(self) -> None:
20+
with tempfile.TemporaryDirectory() as tmp:
21+
root = Path(tmp)
22+
target = root / 'notes.txt'
23+
target.write_text('Hello\n', encoding='utf-8')
24+
subprocess.run(['git', 'init'], cwd=tmp, check=True, capture_output=True)
25+
26+
line_hash = compute_line_hash(1, 'Hello\n')
27+
test_command = (
28+
f'{sys.executable} -c '
29+
'"from pathlib import Path; '
30+
"assert Path('notes.txt').read_text(encoding='utf-8') == "
31+
"'Hello from TeaAgent\\n'\""
32+
)
33+
adapter = FakeAdapter(
34+
[
35+
'{"type":"tool","tool_name":"workspace_read_file_hashed","arguments":{"path":"notes.txt"},"call_id":"read-hashed"}',
36+
json.dumps(
37+
{
38+
'type': 'tool',
39+
'tool_name': 'workspace_edit_at_hash',
40+
'arguments': {
41+
'path': 'notes.txt',
42+
'line': 1,
43+
'hash': line_hash,
44+
'old': 'Hello',
45+
'new': 'Hello from TeaAgent',
46+
},
47+
'call_id': 'edit-line-1',
48+
}
49+
),
50+
'{"type":"tool","tool_name":"workspace_git_status","arguments":{},"call_id":"git-status"}',
51+
json.dumps(
52+
{
53+
'type': 'tool',
54+
'tool_name': 'workspace_run_shell_mutate',
55+
'arguments': {
56+
'command': test_command,
57+
'timeout_seconds': 5,
58+
},
59+
'call_id': 'run-test',
60+
}
61+
),
62+
'{"type":"tool","tool_name":"workspace_run_shell_inspect","arguments":{"command":"git diff -- notes.txt","timeout_seconds":5},"call_id":"git-diff"}',
63+
'{"type":"final","content":"Diff summary: notes.txt line 1 changed from Hello to Hello from TeaAgent; test command passed."}',
64+
]
65+
)
66+
67+
output = io.StringIO()
68+
with redirect_stdout(output):
69+
exit_code = main(
70+
[
71+
'agent',
72+
'run',
73+
'gpt',
74+
'Safely edit notes.txt and verify the change',
75+
'--root',
76+
tmp,
77+
'--allow-destructive',
78+
],
79+
_adapter_factory=lambda _provider, model=None: adapter,
80+
)
81+
payload = json.loads(output.getvalue())
82+
83+
self.assertEqual(exit_code, 0)
84+
self.assertEqual(
85+
target.read_text(encoding='utf-8'), 'Hello from TeaAgent\n'
86+
)
87+
self.assertEqual(payload['status'], 'completed')
88+
self.assertIn('Diff summary:', payload['final_answer'])
89+
self.assertEqual(payload['audit_summary']['destructive_tool_calls'], 2)
90+
self.assertEqual(
91+
payload['audit_summary']['tool_names'],
92+
[
93+
'workspace_read_file_hashed',
94+
'workspace_edit_at_hash',
95+
'workspace_git_status',
96+
'workspace_run_shell_mutate',
97+
'workspace_run_shell_inspect',
98+
],
99+
)
100+
self.assertEqual(adapter.outputs, [])
101+
102+
103+
if __name__ == '__main__':
104+
unittest.main()

0 commit comments

Comments
 (0)