11"""Tests for core modules: config, tools, session, context."""
22
33import os
4- import tempfile
4+ import sys
55
66from anycoder import __version__ , Agent , LLMClient , Config
77from anycoder .config import MODEL_ALIASES
@@ -56,15 +56,13 @@ def test_tool_schemas():
5656 assert "description" in s ["function" ]
5757
5858
59- def test_read_file ():
59+ def test_read_file (tmp_path ):
6060 tool = TOOL_MAP ["read_file" ]
61- with tempfile .NamedTemporaryFile (mode = "w" , suffix = ".txt" , delete = False ) as f :
62- f .write ("line1\n line2\n line3\n " )
63- f .flush ()
64- result = tool .execute (file_path = f .name )
65- assert "line1" in result
66- assert "3 lines total" in result
67- os .unlink (f .name )
61+ path = tmp_path / "sample.txt"
62+ path .write_text ("line1\n line2\n line3\n " , encoding = "utf-8" )
63+ result = tool .execute (file_path = str (path ))
64+ assert "line1" in result
65+ assert "3 lines total" in result
6866
6967
7068def test_read_file_not_found ():
@@ -73,38 +71,35 @@ def test_read_file_not_found():
7371 assert "[error]" in result
7472
7573
76- def test_write_file ():
74+ def test_write_file (tmp_path ):
7775 tool = TOOL_MAP ["write_file" ]
78- path = tempfile . mktemp ( suffix = " .txt")
79- result = tool .execute (file_path = path , content = "hello\n world\n " )
76+ path = tmp_path / "write .txt"
77+ result = tool .execute (file_path = str ( path ) , content = "hello\n world\n " )
8078 assert "Wrote" in result
81- with open (path ) as f :
82- assert f .read () == "hello\n world\n "
83- os .unlink (path )
79+ assert path .read_text (encoding = "utf-8" ) == "hello\n world\n "
8480
8581
86- def test_edit_file ():
82+ def test_edit_file (tmp_path ):
8783 tool = TOOL_MAP ["edit_file" ]
88- with tempfile .NamedTemporaryFile (mode = "w" , suffix = ".py" , delete = False ) as f :
89- f .write ("foo = 1\n bar = 2\n " )
90- f .flush ()
91- result = tool .execute (file_path = f .name , old_string = "foo = 1" , new_string = "foo = 42" )
92- assert "Edited" in result
93- with open (f .name ) as rf :
94- assert "foo = 42" in rf .read ()
95- os .unlink (f .name )
84+ path = tmp_path / "sample.py"
85+ path .write_text ("foo = 1\n bar = 2\n " , encoding = "utf-8" )
86+ result = tool .execute (file_path = str (path ), old_string = "foo = 1" , new_string = "foo = 42" )
87+ assert "Edited" in result
88+ assert "foo = 42" in path .read_text (encoding = "utf-8" )
9689
9790
98- def test_edit_file_diff_output ():
91+ def test_edit_file_diff_output (tmp_path ):
9992 tool = TOOL_MAP ["edit_file" ]
100- with tempfile .NamedTemporaryFile (mode = "w" , suffix = ".py" , delete = False ) as f :
101- f .write ("hello = 'world'\n " )
102- f .flush ()
103- result = tool .execute (file_path = f .name , old_string = "hello = 'world'" , new_string = "hello = 'universe'" )
104- assert "---" in result and "+++" in result # unified diff markers
105- assert "-hello = 'world'" in result
106- assert "+hello = 'universe'" in result
107- os .unlink (f .name )
93+ path = tmp_path / "diff.py"
94+ path .write_text ("hello = 'world'\n " , encoding = "utf-8" )
95+ result = tool .execute (
96+ file_path = str (path ),
97+ old_string = "hello = 'world'" ,
98+ new_string = "hello = 'universe'" ,
99+ )
100+ assert "---" in result and "+++" in result # unified diff markers
101+ assert "-hello = 'world'" in result
102+ assert "+hello = 'universe'" in result
108103
109104
110105def test_edit_file_not_found ():
@@ -113,14 +108,12 @@ def test_edit_file_not_found():
113108 assert "[error]" in result
114109
115110
116- def test_edit_file_unique_check ():
111+ def test_edit_file_unique_check (tmp_path ):
117112 tool = TOOL_MAP ["edit_file" ]
118- with tempfile .NamedTemporaryFile (mode = "w" , suffix = ".py" , delete = False ) as f :
119- f .write ("x = 1\n x = 1\n " )
120- f .flush ()
121- result = tool .execute (file_path = f .name , old_string = "x = 1" , new_string = "x = 2" )
122- assert "appears 2 times" in result
123- os .unlink (f .name )
113+ path = tmp_path / "dupe.py"
114+ path .write_text ("x = 1\n x = 1\n " , encoding = "utf-8" )
115+ result = tool .execute (file_path = str (path ), old_string = "x = 1" , new_string = "x = 2" )
116+ assert "appears 2 times" in result
124117
125118
126119def test_glob_tool ():
@@ -143,33 +136,30 @@ def test_bash_tool():
143136
144137def test_bash_timeout ():
145138 tool = TOOL_MAP ["bash" ]
146- result = tool .execute (command = "sleep 10" , timeout = 1 )
139+ result = tool .execute (command = f'" { sys . executable } " -c "import time; time.sleep(10)"' , timeout = 1 )
147140 assert "timed out" in result .lower ()
148141
149142
150143# --- File change tracking ---
151144
152- def test_edit_tracks_changes ():
145+ def test_edit_tracks_changes (tmp_path ):
153146 from anycoder .tools .edit_file import _changed_files
154147 _changed_files .clear ()
155148 tool = TOOL_MAP ["edit_file" ]
156- with tempfile .NamedTemporaryFile (mode = "w" , suffix = ".py" , delete = False ) as f :
157- f .write ("aaa\n bbb\n " )
158- f .flush ()
159- tool .execute (file_path = f .name , old_string = "aaa" , new_string = "zzz" )
160- assert len (_changed_files ) > 0
161- os .unlink (f .name )
149+ path = tmp_path / "tracked.py"
150+ path .write_text ("aaa\n bbb\n " , encoding = "utf-8" )
151+ tool .execute (file_path = str (path ), old_string = "aaa" , new_string = "zzz" )
152+ assert len (_changed_files ) > 0
162153 _changed_files .clear ()
163154
164155
165- def test_write_tracks_changes ():
156+ def test_write_tracks_changes (tmp_path ):
166157 from anycoder .tools .edit_file import _changed_files
167158 _changed_files .clear ()
168159 tool = TOOL_MAP ["write_file" ]
169- path = tempfile . mktemp ( suffix = " .txt")
170- tool .execute (file_path = path , content = "tracked\n " )
160+ path = tmp_path / "tracked .txt"
161+ tool .execute (file_path = str ( path ) , content = "tracked\n " )
171162 assert len (_changed_files ) > 0
172- os .unlink (path )
173163 _changed_files .clear ()
174164
175165
@@ -217,40 +207,38 @@ def test_safe_command_not_blocked():
217207 assert "Blocked" not in result
218208
219209
220- def test_cd_tracking ():
210+ def test_cd_tracking (tmp_path ):
221211 import anycoder .tools .bash as bash_mod
222212 old_cwd = bash_mod ._cwd
223213 bash_mod ._cwd = None # reset
224214
225215 tool = TOOL_MAP ["bash" ]
226- # cd to /tmp should work
227- tool .execute (command = "cd /tmp && pwd" )
228- assert bash_mod ._cwd == "/tmp" or bash_mod ._cwd == "/private/tmp" # macOS /tmp -> /private/tmp
216+ command = f'cd "{ tmp_path } " && ' + ("cd" if os .name == "nt" else "pwd" )
217+ tool .execute (command = command )
218+ assert os .path .normcase (os .path .abspath (bash_mod ._cwd or "" )) == os .path .normcase (
219+ os .path .abspath (tmp_path )
220+ )
229221
230222 bash_mod ._cwd = old_cwd # restore
231223
232224
233225# --- Binary file detection ---
234226
235- def test_binary_file_rejected ():
227+ def test_binary_file_rejected (tmp_path ):
236228 tool = TOOL_MAP ["read_file" ]
237- with tempfile .NamedTemporaryFile (suffix = ".bin" , delete = False ) as f :
238- f .write (b"\x89 PNG\r \n \x1a \n \x00 \x00 \x00 \r IHDR" )
239- f .flush ()
240- result = tool .execute (file_path = f .name )
241- assert "Binary file" in result
242- os .unlink (f .name )
229+ path = tmp_path / "image.bin"
230+ path .write_bytes (b"\x89 PNG\r \n \x1a \n \x00 \x00 \x00 \r IHDR" )
231+ result = tool .execute (file_path = str (path ))
232+ assert "Binary file" in result
243233
244234
245- def test_text_file_not_rejected ():
235+ def test_text_file_not_rejected (tmp_path ):
246236 tool = TOOL_MAP ["read_file" ]
247- with tempfile .NamedTemporaryFile (mode = "w" , suffix = ".txt" , delete = False ) as f :
248- f .write ("just plain text\n " )
249- f .flush ()
250- result = tool .execute (file_path = f .name )
251- assert "Binary" not in result
252- assert "just plain text" in result
253- os .unlink (f .name )
237+ path = tmp_path / "plain.txt"
238+ path .write_text ("just plain text\n " , encoding = "utf-8" )
239+ result = tool .execute (file_path = str (path ))
240+ assert "Binary" not in result
241+ assert "just plain text" in result
254242
255243
256244# --- Context compression ---
@@ -323,17 +311,14 @@ def count_tokens(self, messages):
323311
324312# --- read_file offset/limit ---
325313
326- def test_read_file_with_offset_limit ():
314+ def test_read_file_with_offset_limit (tmp_path ):
327315 tool = TOOL_MAP ["read_file" ]
328- with tempfile .NamedTemporaryFile (mode = "w" , suffix = ".txt" , delete = False ) as f :
329- for i in range (100 ):
330- f .write (f"line { i } \n " )
331- f .flush ()
332- result = tool .execute (file_path = f .name , offset = 10 , limit = 5 )
333- assert "showing lines 11-15" in result
334- assert "line 10" in result # 0-indexed line 10 = "line 10"
335- assert "line 0" not in result
336- os .unlink (f .name )
316+ path = tmp_path / "long.txt"
317+ path .write_text ("" .join (f"line { i } \n " for i in range (100 )), encoding = "utf-8" )
318+ result = tool .execute (file_path = str (path ), offset = 10 , limit = 5 )
319+ assert "showing lines 11-15" in result
320+ assert "line 10" in result # 0-indexed line 10 = "line 10"
321+ assert "line 0" not in result
337322
338323
339324# --- grep skips junk dirs ---
0 commit comments