Files
goose/tests/toolkit/test_developer.py
2024-09-27 18:25:26 +10:00

160 lines
5.7 KiB
Python

from pathlib import Path
from tempfile import TemporaryDirectory
from unittest.mock import MagicMock, Mock
import pytest
from goose.toolkit.base import Requirements
from goose.toolkit.developer import Developer
from contextlib import contextmanager
import os
@contextmanager
def change_dir(new_dir):
"""Context manager to temporarily change the current working directory."""
original_dir = os.getcwd()
os.chdir(new_dir)
try:
yield
finally:
os.chdir(original_dir)
@pytest.fixture
def temp_dir():
with TemporaryDirectory() as temp_dir:
yield Path(temp_dir)
@pytest.fixture
def developer_toolkit():
toolkit = Developer(notifier=MagicMock(), requires=Requirements(""))
# This mocking ensures that that the safety check is considered a pass in shell calls
toolkit.exchange_view = Mock()
toolkit.exchange_view.processor.replace.return_value = Mock()
toolkit.exchange_view.processor.replace.return_value.messages = []
toolkit.exchange_view.processor.replace.return_value.add = Mock()
toolkit.exchange_view.processor.replace.return_value.reply.return_value.text = "3"
toolkit.exchange_view.processor.replace.return_value.messages = [Mock()]
return toolkit
def test_system_prompt_with_goosehints(temp_dir, developer_toolkit):
readme_file = temp_dir / "README.md"
readme_file.write_text("This is from the README.md file.")
hints_file = temp_dir / ".goosehints"
jinja_template_content = "Hints:\n\n{% include 'README.md' %}\nEnd."
hints_file.write_text(jinja_template_content)
with change_dir(temp_dir):
system_prompt = developer_toolkit.system()
expected_end = "Hints:\n\nThis is from the README.md file.\nEnd."
assert system_prompt.endswith(expected_end)
def test_system_prompt_with_goosehints_only_from_home_dir(temp_dir, developer_toolkit):
readme_file_home = Path.home() / ".config/goose/README.md"
readme_file_home.parent.mkdir(parents=True, exist_ok=True)
readme_file_home.write_text("This is from the README.md file in home.")
home_hints_file = Path.home() / ".config/goose/.goosehints"
home_jinja_template_content = "Hints from home:\n\n{% include 'README.md' %}\nEnd."
home_hints_file.write_text(home_jinja_template_content)
try:
with change_dir(temp_dir):
system_prompt = developer_toolkit.system()
expected_content_home = "Hints from home:\n\nThis is from the README.md file in home.\nEnd."
expected_end = f"Hints:\n{expected_content_home}"
assert system_prompt.endswith(expected_end)
finally:
home_hints_file.unlink()
readme_file_home.unlink()
readme_file = temp_dir / "README.md"
readme_file.write_text("This is from the README.md file.")
hints_file = temp_dir / ".goosehints"
jinja_template_content = "Hints from local:\n\n{% include 'README.md' %}\nEnd."
hints_file.write_text(jinja_template_content)
home_hints_file = Path.home() / ".config/goose/.goosehints"
home_jinja_template_content = "Hints from home:\n\n{% include 'README.md' %}\nEnd."
home_hints_file.write_text(home_jinja_template_content)
home_readme_file = Path.home() / ".config/goose/README.md"
home_readme_file.write_text("This is from the README.md file in home.")
try:
with change_dir(temp_dir):
system_prompt = developer_toolkit.system()
expected_content_local = "Hints from local:\n\nThis is from the README.md file.\nEnd."
expected_content_home = "Hints from home:\n\nThis is from the README.md file in home.\nEnd."
expected_end = f"Hints:\n{expected_content_local}\n{expected_content_home}"
assert system_prompt.endswith(expected_end)
finally:
home_hints_file.unlink()
home_readme_file.unlink()
tasks = [
{"description": "Task 1", "status": "planned"},
{"description": "Task 2", "status": "complete"},
{"description": "Task 3", "status": "in-progress"},
]
updated_tasks = developer_toolkit.update_plan(tasks)
assert updated_tasks == tasks
def test_patch_file(temp_dir, developer_toolkit):
test_file = temp_dir / "test.txt"
before_content = "Hello World"
after_content = "Hello Goose"
test_file.write_text(before_content)
developer_toolkit.patch_file(test_file.as_posix(), before_content, after_content)
assert test_file.read_text() == after_content
def test_read_file(temp_dir, developer_toolkit):
test_file = temp_dir / "test.txt"
content = "Hello World"
test_file.write_text(content)
read_content = developer_toolkit.read_file(test_file.as_posix())
assert content in read_content
def test_shell(developer_toolkit):
command = "echo Hello World"
result = developer_toolkit.shell(command)
assert "Hello World" in result
def test_write_file(temp_dir, developer_toolkit):
test_file = temp_dir / "test.txt"
content = "Hello World"
developer_toolkit.write_file(test_file.as_posix(), content)
assert test_file.read_text() == content
def test_write_file_prevent_write_if_changed(temp_dir, developer_toolkit):
test_file = temp_dir / "test.txt"
content = "Hello World"
updated_content = "Hello Universe"
# Initial write to record the timestamp
developer_toolkit.write_file(test_file.as_posix(), content)
developer_toolkit.read_file(test_file.as_posix())
import time
# Modify file externally to simulate change
time.sleep(1)
test_file.write_text(updated_content)
# Try to write through toolkit and check for the raised exception
with pytest.raises(RuntimeError, match="has been modified"):
developer_toolkit.write_file(test_file.as_posix(), content)
assert test_file.read_text() == updated_content