|
| 1 | +import os |
| 2 | +import unittest |
| 3 | +from pathlib import Path |
| 4 | +from unittest.mock import MagicMock, patch |
| 5 | +from aider.coders import Coder |
| 6 | +from aider.io import InputOutput |
| 7 | +from aider.models import Model |
| 8 | +from aider.utils import GitTemporaryDirectory |
| 9 | + |
| 10 | + |
| 11 | +class TestIterateCoder(unittest.TestCase): |
| 12 | + def setUp(self): |
| 13 | + self.GPT35 = Model("gpt-3.5-turbo") |
| 14 | + self.io = InputOutput(yes=True) |
| 15 | + # self.webbrowser_patcher = patch("aider.io.webbrowser.open") |
| 16 | + # self.mock_webbrowser = self.webbrowser_patcher.start() |
| 17 | + |
| 18 | + # Get all Python files in aider/coders directory |
| 19 | + coders_dir = Path(__file__).parent.parent.parent / "aider" / "coders" |
| 20 | + self.files = [str(f) for f in coders_dir.glob("*.py") if f.is_file()] |
| 21 | + |
| 22 | + # Create coder with all files |
| 23 | + self.coder = Coder.create( |
| 24 | + main_model=self.GPT35, |
| 25 | + io=self.io, |
| 26 | + fnames=self.files, |
| 27 | + edit_format='iterate' |
| 28 | + ) |
| 29 | + |
| 30 | + def tearDown(self): |
| 31 | + # self.webbrowser_patcher.stop() |
| 32 | + return |
| 33 | + """Tests that: |
| 34 | + - Every request retains the chat history until the /iterate command but not the history of other iterations. |
| 35 | + - Added files and history until the /iterate is unmodified. |
| 36 | + - Every file is processed(even if a single file that'll be sent with the request exceeds the limits.) and no duplicate processing |
| 37 | + """ |
| 38 | + def test_iterate_resets_history_and_processes_all_files(self): |
| 39 | + processed_files :list[str]= [] |
| 40 | + original_context:list[dict[str,str]] |
| 41 | + prev_file_names : list[str] = None |
| 42 | + # Track messages sent to LLM and files processed |
| 43 | + def mock_send(self,messages, model=None, functions=None): |
| 44 | + nonlocal original_context |
| 45 | + nonlocal processed_files |
| 46 | + nonlocal prev_file_names |
| 47 | + for original_message in original_context: |
| 48 | + assert original_message in messages, f"Chat history before start of the command is not retained." |
| 49 | + # Simulate response mentioning filename |
| 50 | + a : str="" |
| 51 | + files_message = [msg['content'] for msg in messages if "*added these files to the chat*" in msg['content']][0] |
| 52 | + from re import findall |
| 53 | + file_names = findall(r'.*\n(\S+\.py)\n```.*',files_message) |
| 54 | + for f_name in file_names: |
| 55 | + assert prev_file_names == None or f_name not in prev_file_names, "files from previous iterations hasn't been cleaned up." |
| 56 | + prev_file_names = file_names |
| 57 | + processed_files.extend(file_names) |
| 58 | + # Return minimal response |
| 59 | + self.partial_response_content = "Done." |
| 60 | + self.partial_response_function_call=dict() |
| 61 | + |
| 62 | + with GitTemporaryDirectory(): |
| 63 | + # Mock the send method |
| 64 | + with patch.object(Coder, 'send',new_callable=lambda: mock_send): |
| 65 | + self.coder.coder = Coder.create(main_model=self.coder.main_model, edit_format=self.coder.main_model.edit_format,from_coder=self.coder,**self.coder.original_kwargs) |
| 66 | + |
| 67 | + # Add initial conversation history |
| 68 | + original_context = self.coder.done_messages = [ |
| 69 | + {"role": "user", "content": "Initial conversation"}, |
| 70 | + {"role": "assistant", "content": "OK"} |
| 71 | + ] |
| 72 | + |
| 73 | + # Run iterate command |
| 74 | + self.coder.run(with_message="Process all files") |
| 75 | + # Verify all files were processed |
| 76 | + input_basenames = {Path(f).name for f in self.files} |
| 77 | + processed_basenames = {Path(f).name for f in processed_files} |
| 78 | + missing = input_basenames - processed_basenames |
| 79 | + assert not missing, f"Files not processed: {missing}" |
| 80 | + |
| 81 | + # Verify history preservation and structure |
| 82 | + assert len(self.coder.done_messages) == 2, "Original chat history was modified" |
| 83 | + # Verify final file state |
| 84 | + assert len(self.coder.abs_fnames) == len(self.files), "Not all files remained in chat" |
| 85 | + |
| 86 | +if __name__ == "__main__": |
| 87 | + unittest.main() |
0 commit comments