chore: initial commit
Co-authored-by: Lifei Zhou <lifei@squareup.com> Co-authored-by: Mic Neale <micn@tbd.email> Co-authored-by: Lily Delalande <ldelalande@squareup.com> Co-authored-by: Bradley Axen <baxen@squareup.com> Co-authored-by: Andy Lane <alane@squareup.com> Co-authored-by: Elena Zherdeva <ezherdeva@squareup.com> Co-authored-by: Zaki Ali <zaki@squareup.com> Co-authored-by: Salman Mohammed <smohammed@squareup.com>
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
lint.select = ["E", "W", "F", "N"]
|
||||
line-length = 120
|
||||
@@ -0,0 +1,47 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from goose.cli.prompt.goose_prompt_session import GoosePromptSession
|
||||
from goose.cli.prompt.user_input import PromptAction, UserInput
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_prompt_session():
|
||||
with patch("prompt_toolkit.PromptSession") as mock_prompt_session:
|
||||
yield mock_prompt_session
|
||||
|
||||
|
||||
def test_get_save_session_name(mock_prompt_session):
|
||||
mock_prompt_session.prompt.return_value = "my_session"
|
||||
goose_prompt_session = GoosePromptSession(mock_prompt_session)
|
||||
|
||||
assert goose_prompt_session.get_save_session_name() == "my_session"
|
||||
|
||||
|
||||
def test_get_user_input_to_continue(mock_prompt_session):
|
||||
mock_prompt_session.prompt.return_value = "input_value"
|
||||
goose_prompt_session = GoosePromptSession(mock_prompt_session)
|
||||
|
||||
user_input = goose_prompt_session.get_user_input()
|
||||
|
||||
assert user_input == UserInput(PromptAction.CONTINUE, "input_value")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("exit_input", ["exit", ":q"])
|
||||
def test_get_user_input_to_exit(exit_input, mock_prompt_session):
|
||||
mock_prompt_session.prompt.return_value = exit_input
|
||||
goose_prompt_session = GoosePromptSession(mock_prompt_session)
|
||||
|
||||
user_input = goose_prompt_session.get_user_input()
|
||||
|
||||
assert user_input == UserInput(PromptAction.EXIT)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("error", [EOFError, KeyboardInterrupt])
|
||||
def test_get_user_input_to_exit_when_error_occurs(error, mock_prompt_session):
|
||||
mock_prompt_session.prompt.side_effect = error
|
||||
goose_prompt_session = GoosePromptSession(mock_prompt_session)
|
||||
|
||||
user_input = goose_prompt_session.get_user_input()
|
||||
|
||||
assert user_input == UserInput(PromptAction.EXIT)
|
||||
@@ -0,0 +1,253 @@
|
||||
from goose.cli.prompt.lexer import (
|
||||
PromptLexer,
|
||||
command_itself,
|
||||
completion_for_command,
|
||||
value_for_command,
|
||||
)
|
||||
from prompt_toolkit.document import Document
|
||||
|
||||
|
||||
# Helper function to create a Document and lexer instance
|
||||
def create_lexer_and_document(commands, text):
|
||||
lexer = PromptLexer(commands)
|
||||
document = Document(text)
|
||||
return lexer, document
|
||||
|
||||
|
||||
# Test cases
|
||||
def test_lex_document_command():
|
||||
lexer, document = create_lexer_and_document(["file"], "/file:example.txt")
|
||||
tokens = lexer.lex_document(document)
|
||||
expected_tokens = [("class:command", "/file:"), ("class:parameter", "example.txt")]
|
||||
assert tokens(0) == expected_tokens
|
||||
|
||||
|
||||
def test_lex_document_partial_command():
|
||||
lexer, document = create_lexer_and_document(["file"], "/fi")
|
||||
tokens = lexer.lex_document(document)
|
||||
expected_tokens = [("class:command", "/fi")]
|
||||
assert tokens(0) == expected_tokens
|
||||
|
||||
|
||||
def test_lex_document_with_text():
|
||||
lexer, document = create_lexer_and_document(["file"], "Some text /file:example.txt")
|
||||
tokens = lexer.lex_document(document)
|
||||
expected_tokens = [
|
||||
("class:text", "S"),
|
||||
("class:text", "o"),
|
||||
("class:text", "m"),
|
||||
("class:text", "e"),
|
||||
("class:text", " "),
|
||||
("class:text", "t"),
|
||||
("class:text", "e"),
|
||||
("class:text", "x"),
|
||||
("class:text", "t"),
|
||||
("class:text", " "),
|
||||
("class:command", "/file:"),
|
||||
("class:parameter", "example.txt"),
|
||||
]
|
||||
assert tokens(0) == expected_tokens
|
||||
|
||||
|
||||
def test_lex_document_with_command_in_middle():
|
||||
lexer, document = create_lexer_and_document(["file"], "Some text /file:example.txt more text")
|
||||
tokens = lexer.lex_document(document)
|
||||
expected_tokens = [
|
||||
("class:text", "S"),
|
||||
("class:text", "o"),
|
||||
("class:text", "m"),
|
||||
("class:text", "e"),
|
||||
("class:text", " "),
|
||||
("class:text", "t"),
|
||||
("class:text", "e"),
|
||||
("class:text", "x"),
|
||||
("class:text", "t"),
|
||||
("class:text", " "),
|
||||
("class:command", "/file:"),
|
||||
("class:parameter", "example.txt"),
|
||||
("class:text", " "),
|
||||
("class:text", "m"),
|
||||
("class:text", "o"),
|
||||
("class:text", "r"),
|
||||
("class:text", "e"),
|
||||
("class:text", " "),
|
||||
("class:text", "t"),
|
||||
("class:text", "e"),
|
||||
("class:text", "x"),
|
||||
("class:text", "t"),
|
||||
]
|
||||
actual_tokens = list(tokens(0))
|
||||
assert actual_tokens == expected_tokens
|
||||
|
||||
|
||||
def test_lex_document_multiple_commands():
|
||||
lexer, document = create_lexer_and_document(
|
||||
["command", "anothercommand"],
|
||||
"/command:example1.txt more text /anothercommand:example2.txt",
|
||||
)
|
||||
tokens = lexer.lex_document(document)
|
||||
expected_tokens = [
|
||||
("class:command", "/command:"),
|
||||
("class:parameter", "example1.txt"),
|
||||
("class:text", " "),
|
||||
("class:text", "m"),
|
||||
("class:text", "o"),
|
||||
("class:text", "r"),
|
||||
("class:text", "e"),
|
||||
("class:text", " "),
|
||||
("class:text", "t"),
|
||||
("class:text", "e"),
|
||||
("class:text", "x"),
|
||||
("class:text", "t"),
|
||||
("class:text", " "),
|
||||
("class:command", "/anothercommand:"),
|
||||
("class:parameter", "example2.txt"),
|
||||
]
|
||||
actual_tokens = list(tokens(0))
|
||||
assert actual_tokens == expected_tokens
|
||||
|
||||
|
||||
def test_lex_document_multiple_same_commands():
|
||||
lexer, document = create_lexer_and_document(
|
||||
["command"],
|
||||
"/command:example1.txt more text /command:example2.txt",
|
||||
)
|
||||
tokens = lexer.lex_document(document)
|
||||
expected_tokens = [
|
||||
("class:command", "/command:"),
|
||||
("class:parameter", "example1.txt"),
|
||||
("class:text", " "),
|
||||
("class:text", "m"),
|
||||
("class:text", "o"),
|
||||
("class:text", "r"),
|
||||
("class:text", "e"),
|
||||
("class:text", " "),
|
||||
("class:text", "t"),
|
||||
("class:text", "e"),
|
||||
("class:text", "x"),
|
||||
("class:text", "t"),
|
||||
("class:text", " "),
|
||||
("class:command", "/command:"),
|
||||
("class:parameter", "example2.txt"),
|
||||
]
|
||||
actual_tokens = list(tokens(0))
|
||||
assert actual_tokens == expected_tokens
|
||||
|
||||
|
||||
def test_lex_document_two_half_commands():
|
||||
lexer, document = create_lexer_and_document(
|
||||
["command"],
|
||||
"/comma /com",
|
||||
)
|
||||
tokens = lexer.lex_document(document)
|
||||
expected_tokens = [
|
||||
("class:text", "/"),
|
||||
("class:text", "c"),
|
||||
("class:text", "o"),
|
||||
("class:text", "m"),
|
||||
("class:text", "m"),
|
||||
("class:text", "a"),
|
||||
("class:text", " "),
|
||||
("class:command", "/com"),
|
||||
]
|
||||
actual_tokens = list(tokens(0))
|
||||
assert actual_tokens == expected_tokens
|
||||
|
||||
|
||||
def test_lex_document_command_attached_to_pre_string():
|
||||
lexer, document = create_lexer_and_document(
|
||||
["command"],
|
||||
"some/command:example.txt",
|
||||
)
|
||||
expected_tokens = [
|
||||
("class:text", "s"),
|
||||
("class:text", "o"),
|
||||
("class:text", "m"),
|
||||
("class:text", "e"),
|
||||
("class:text", "/"),
|
||||
("class:text", "c"),
|
||||
("class:text", "o"),
|
||||
("class:text", "m"),
|
||||
("class:text", "m"),
|
||||
("class:text", "a"),
|
||||
("class:text", "n"),
|
||||
("class:text", "d"),
|
||||
("class:text", ":"),
|
||||
("class:text", "e"),
|
||||
("class:text", "x"),
|
||||
("class:text", "a"),
|
||||
("class:text", "m"),
|
||||
("class:text", "p"),
|
||||
("class:text", "l"),
|
||||
("class:text", "e"),
|
||||
("class:text", "."),
|
||||
("class:text", "t"),
|
||||
("class:text", "x"),
|
||||
("class:text", "t"),
|
||||
]
|
||||
tokens = lexer.lex_document(document)
|
||||
actual_tokens = list(tokens(0))
|
||||
assert actual_tokens == expected_tokens
|
||||
|
||||
|
||||
def test_lex_document_partial_command_attached_to_pre_string():
|
||||
lexer, document = create_lexer_and_document(
|
||||
["command"],
|
||||
"some/com",
|
||||
)
|
||||
tokens = lexer.lex_document(document)
|
||||
expected_tokens = [
|
||||
("class:text", "s"),
|
||||
("class:text", "o"),
|
||||
("class:text", "m"),
|
||||
("class:text", "e"),
|
||||
("class:text", "/"),
|
||||
("class:text", "c"),
|
||||
("class:text", "o"),
|
||||
("class:text", "m"),
|
||||
]
|
||||
actual_tokens = list(tokens(0))
|
||||
assert actual_tokens == expected_tokens
|
||||
|
||||
|
||||
def test_lex_document_no_command():
|
||||
lexer, document = create_lexer_and_document([], "Some random text")
|
||||
tokens = lexer.lex_document(document)
|
||||
expected_tokens = [("class:text", character) for character in "Some random text"]
|
||||
actual_tokens = list(tokens(0))
|
||||
assert actual_tokens == expected_tokens
|
||||
|
||||
|
||||
def test_lex_document_ending_char_of_parameter_is_symbol():
|
||||
lexer, document = create_lexer_and_document(
|
||||
["command"],
|
||||
"/command:example.txt/",
|
||||
)
|
||||
expected_tokens = [
|
||||
("class:command", "/command:"),
|
||||
("class:parameter", "example.txt/"),
|
||||
]
|
||||
tokens = lexer.lex_document(document)
|
||||
actual_tokens = list(tokens(0))
|
||||
assert actual_tokens == expected_tokens
|
||||
|
||||
|
||||
def test_command_itself():
|
||||
pattern = command_itself("file:")
|
||||
matches = pattern.match("/file:example.txt")
|
||||
assert matches is not None
|
||||
assert matches.group(1) == "/file:"
|
||||
|
||||
|
||||
def test_value_for_command():
|
||||
pattern = value_for_command("file:")
|
||||
matches = pattern.search("/file:example.txt")
|
||||
assert matches is not None
|
||||
assert matches.group(1) == "example.txt"
|
||||
|
||||
|
||||
def test_completion_for_command():
|
||||
pattern = completion_for_command("file:")
|
||||
matches = pattern.search("/file:")
|
||||
assert matches is not None
|
||||
assert matches.group(1) == "file:"
|
||||
@@ -0,0 +1,37 @@
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from goose.cli.prompt.prompt_validator import PromptValidator
|
||||
from prompt_toolkit.validation import ValidationError
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def validator():
|
||||
return PromptValidator()
|
||||
|
||||
|
||||
@patch("prompt_toolkit.document.Document.text")
|
||||
def test_validate_should_not_raise_error_when_input_is_none(document, validator):
|
||||
try:
|
||||
validator.validate(create_mock_document(None))
|
||||
except Exception as e:
|
||||
pytest.fail(f"An error was raised: {e}")
|
||||
|
||||
|
||||
@patch("prompt_toolkit.document.Document.text", return_value="user typed something")
|
||||
def test_validate_should_not_raise_error_when_user_has_input(document, validator):
|
||||
try:
|
||||
validator.validate(create_mock_document("user typed something"))
|
||||
except Exception as e:
|
||||
pytest.fail(f"An error was raised: {e}")
|
||||
|
||||
|
||||
def test_validate_should_raise_validation_error_when_user_has_empty_input(validator):
|
||||
with pytest.raises(ValidationError):
|
||||
validator.validate(create_mock_document(""))
|
||||
|
||||
|
||||
def create_mock_document(text: str) -> MagicMock:
|
||||
document = MagicMock()
|
||||
document.text = text
|
||||
return document
|
||||
@@ -0,0 +1,15 @@
|
||||
from goose.cli.prompt.user_input import PromptAction, UserInput
|
||||
|
||||
|
||||
def test_user_input_with_action_continue():
|
||||
input = UserInput(action=PromptAction.CONTINUE, text="Hello")
|
||||
assert input.to_continue() is True
|
||||
assert input.to_exit() is False
|
||||
assert input.text == "Hello"
|
||||
|
||||
|
||||
def test_user_input_with_action_exit():
|
||||
input = UserInput(action=PromptAction.EXIT)
|
||||
assert input.to_continue() is False
|
||||
assert input.to_exit() is True
|
||||
assert input.text is None
|
||||
@@ -0,0 +1,81 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from goose.cli.config import ensure_config, read_config, session_path, write_config
|
||||
from goose.profile import default_profile
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_profile_config_path(tmp_path):
|
||||
with patch("goose.cli.config.PROFILES_CONFIG_PATH", tmp_path / "profiles.yaml") as mock_path:
|
||||
yield mock_path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_default_model_configuration():
|
||||
with patch(
|
||||
"goose.cli.config.default_model_configuration", return_value=("provider", "processor", "accelerator")
|
||||
) as mock_default_model_configuration:
|
||||
yield mock_default_model_configuration
|
||||
|
||||
|
||||
def test_read_write_config(mock_profile_config_path, profile_factory):
|
||||
profiles = {
|
||||
"profile1": profile_factory({"provider": "providerA"}),
|
||||
}
|
||||
write_config(profiles)
|
||||
|
||||
assert read_config() == profiles
|
||||
|
||||
|
||||
def test_ensure_config_create_profiles_file_with_default_profile(
|
||||
mock_profile_config_path, mock_default_model_configuration
|
||||
):
|
||||
assert not mock_profile_config_path.exists()
|
||||
|
||||
ensure_config(name="default")
|
||||
assert mock_profile_config_path.exists()
|
||||
|
||||
assert read_config() == {"default": default_profile(*mock_default_model_configuration())}
|
||||
|
||||
|
||||
def test_ensure_config_add_default_profile(mock_profile_config_path, profile_factory, mock_default_model_configuration):
|
||||
existing_profile = profile_factory({"provider": "providerA"})
|
||||
write_config({"profile1": existing_profile})
|
||||
|
||||
ensure_config(name="default")
|
||||
|
||||
assert read_config() == {
|
||||
"profile1": existing_profile,
|
||||
"default": default_profile(*mock_default_model_configuration()),
|
||||
}
|
||||
|
||||
|
||||
@patch("goose.cli.config.Confirm.ask", return_value=True)
|
||||
def test_ensure_config_overwrite_default_profile(
|
||||
mock_confirm, mock_profile_config_path, profile_factory, mock_default_model_configuration
|
||||
):
|
||||
existing_profile = profile_factory({"provider": "providerA"})
|
||||
profile_name = "default"
|
||||
write_config({profile_name: existing_profile})
|
||||
|
||||
expected_default_profile = default_profile(*mock_default_model_configuration())
|
||||
assert ensure_config(name="default") == expected_default_profile
|
||||
assert read_config() == {"default": expected_default_profile}
|
||||
|
||||
|
||||
@patch("goose.cli.config.Confirm.ask", return_value=False)
|
||||
def test_ensure_config_keep_original_default_profile(
|
||||
mock_confirm, mock_profile_config_path, profile_factory, mock_default_model_configuration
|
||||
):
|
||||
existing_profile = profile_factory({"provider": "providerA"})
|
||||
profile_name = "default"
|
||||
write_config({profile_name: existing_profile})
|
||||
|
||||
assert ensure_config(name="default") == existing_profile
|
||||
|
||||
assert read_config() == {"default": existing_profile}
|
||||
|
||||
|
||||
def test_session_path(mock_sessions_path):
|
||||
assert session_path("session1") == mock_sessions_path / "session1.jsonl"
|
||||
@@ -0,0 +1,80 @@
|
||||
from datetime import datetime
|
||||
from time import time
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
from exchange import Message
|
||||
from goose.cli.main import goose_cli
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_print():
|
||||
with patch("goose.cli.main.print") as mock_print:
|
||||
yield mock_print
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_session_files_path(tmp_path):
|
||||
with patch("goose.cli.main.SESSIONS_PATH", tmp_path) as session_files_path:
|
||||
yield session_files_path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_session():
|
||||
with patch("goose.cli.main.Session") as mock_session_class:
|
||||
mock_session_instance = MagicMock()
|
||||
mock_session_class.return_value = mock_session_instance
|
||||
yield mock_session_class, mock_session_instance
|
||||
|
||||
|
||||
def test_session_resume_command_with_session_name(mock_session):
|
||||
mock_session_class, mock_session_instance = mock_session
|
||||
runner = CliRunner()
|
||||
runner.invoke(goose_cli, ["session", "resume", "session1", "--profile", "default"])
|
||||
mock_session_class.assert_called_once_with(name="session1", profile="default")
|
||||
mock_session_instance.run.assert_called_once()
|
||||
|
||||
|
||||
def test_session_resume_command_without_session_name_without_session_files(
|
||||
mock_print, mock_session_files_path, mock_session
|
||||
):
|
||||
_, mock_session_instance = mock_session
|
||||
runner = CliRunner()
|
||||
runner.invoke(goose_cli, ["session", "resume"])
|
||||
mock_print.assert_called_with("No sessions found.")
|
||||
mock_session_instance.run.assert_not_called()
|
||||
|
||||
|
||||
def test_session_resume_command_without_session_name_use_latest_session(
|
||||
mock_print, mock_session_files_path, mock_session, create_session_file
|
||||
):
|
||||
mock_session_class, mock_session_instance = mock_session
|
||||
for index, session_name in enumerate(["first", "second"]):
|
||||
create_session_file([Message.user("Hello1")], mock_session_files_path / f"{session_name}.jsonl", time() + index)
|
||||
runner = CliRunner()
|
||||
runner.invoke(goose_cli, ["session", "resume", "--profile", "default"])
|
||||
|
||||
second_file_path = mock_session_files_path / "second.jsonl"
|
||||
mock_print.assert_called_once_with(f"Resuming most recent session: second from {second_file_path}")
|
||||
mock_session_class.assert_called_once_with(name="second", profile="default")
|
||||
mock_session_instance.run.assert_called_once()
|
||||
|
||||
|
||||
def test_session_list_command(mock_print, mock_session_files_path, create_session_file):
|
||||
create_session_file([Message.user("Hello")], mock_session_files_path / "abc.jsonl")
|
||||
runner = CliRunner()
|
||||
runner.invoke(goose_cli, ["session", "list"])
|
||||
file_time = datetime.fromtimestamp(mock_session_files_path.stat().st_mtime).strftime("%Y-%m-%d %H:%M:%S")
|
||||
mock_print.assert_called_with(f"{file_time} abc")
|
||||
|
||||
|
||||
def test_session_clear_command(mock_session_files_path, create_session_file):
|
||||
for index, session_name in enumerate(["first", "second"]):
|
||||
create_session_file([Message.user("Hello1")], mock_session_files_path / f"{session_name}.jsonl", time() + index)
|
||||
runner = CliRunner()
|
||||
runner.invoke(goose_cli, ["session", "clear", "--keep", "1"])
|
||||
|
||||
session_files = list(mock_session_files_path.glob("*.jsonl"))
|
||||
assert len(session_files) == 1
|
||||
assert session_files[0].stem == "second"
|
||||
@@ -0,0 +1,134 @@
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from exchange import Message
|
||||
from goose.cli.prompt.goose_prompt_session import GoosePromptSession
|
||||
from goose.cli.prompt.user_input import PromptAction, UserInput
|
||||
from goose.cli.session import Session
|
||||
from prompt_toolkit import PromptSession
|
||||
|
||||
SPECIFIED_SESSION_NAME = "mySession"
|
||||
SESSION_NAME = "test"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_specified_session_name():
|
||||
with patch.object(PromptSession, "prompt", return_value=SPECIFIED_SESSION_NAME) as specified_session_name:
|
||||
yield specified_session_name
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def create_session_with_mock_configs(mock_sessions_path, exchange_factory, profile_factory):
|
||||
with patch("goose.cli.session.build_exchange", return_value=exchange_factory()), patch(
|
||||
"goose.cli.session.load_profile", return_value=profile_factory()
|
||||
), patch("goose.cli.session.SessionNotifier") as mock_session_notifier, patch(
|
||||
"goose.cli.session.load_provider", return_value="provider"
|
||||
):
|
||||
mock_session_notifier.return_value = MagicMock()
|
||||
|
||||
def create_session(session_attributes: dict = {}):
|
||||
return Session(**session_attributes)
|
||||
|
||||
yield create_session
|
||||
|
||||
|
||||
def test_session_does_not_extend_last_user_message_on_init(
|
||||
create_session_with_mock_configs, mock_sessions_path, create_session_file
|
||||
):
|
||||
messages = [Message.user("Hello"), Message.assistant("Hi"), Message.user("Last should be removed")]
|
||||
create_session_file(messages, mock_sessions_path / f"{SESSION_NAME}.jsonl")
|
||||
|
||||
session = create_session_with_mock_configs({"name": SESSION_NAME})
|
||||
print("Messages after session init:", session.exchange.messages) # Debugging line
|
||||
assert len(session.exchange.messages) == 2
|
||||
assert [message.text for message in session.exchange.messages] == ["Hello", "Hi"]
|
||||
|
||||
|
||||
def test_save_session_create_session(mock_sessions_path, create_session_with_mock_configs, mock_specified_session_name):
|
||||
session = create_session_with_mock_configs()
|
||||
session.exchange.messages.append(Message.assistant("Hello"))
|
||||
|
||||
session.save_session()
|
||||
session_file = mock_sessions_path / f"{SPECIFIED_SESSION_NAME}.jsonl"
|
||||
assert session_file.exists()
|
||||
|
||||
saved_messages = session.load_session()
|
||||
assert len(saved_messages) == 1
|
||||
assert saved_messages[0].text == "Hello"
|
||||
|
||||
|
||||
def test_save_session_resume_session_new_file(
|
||||
mock_sessions_path, create_session_with_mock_configs, mock_specified_session_name, create_session_file
|
||||
):
|
||||
with patch("goose.cli.session.confirm", return_value=False):
|
||||
existing_messages = [Message.assistant("existing_message")]
|
||||
existing_session_file = mock_sessions_path / f"{SESSION_NAME}.jsonl"
|
||||
create_session_file(existing_messages, existing_session_file)
|
||||
|
||||
new_session_file = mock_sessions_path / f"{SPECIFIED_SESSION_NAME}.jsonl"
|
||||
assert not new_session_file.exists()
|
||||
|
||||
session = create_session_with_mock_configs({"name": SESSION_NAME})
|
||||
session.exchange.messages.append(Message.assistant("new_message"))
|
||||
|
||||
session.save_session()
|
||||
|
||||
assert new_session_file.exists()
|
||||
assert existing_session_file.exists()
|
||||
|
||||
saved_messages = session.load_session()
|
||||
assert [message.text for message in saved_messages] == ["existing_message", "new_message"]
|
||||
|
||||
|
||||
def test_save_session_resume_session_existing_session_file(
|
||||
mock_sessions_path, create_session_with_mock_configs, create_session_file
|
||||
):
|
||||
with patch("goose.cli.session.confirm", return_value=True):
|
||||
existing_messages = [Message.assistant("existing_message")]
|
||||
existing_session_file = mock_sessions_path / f"{SESSION_NAME}.jsonl"
|
||||
create_session_file(existing_messages, existing_session_file)
|
||||
|
||||
session = create_session_with_mock_configs({"name": SESSION_NAME})
|
||||
session.exchange.messages.append(Message.assistant("new_message"))
|
||||
|
||||
session.save_session()
|
||||
|
||||
saved_messages = session.load_session()
|
||||
assert [message.text for message in saved_messages] == ["existing_message", "new_message"]
|
||||
|
||||
|
||||
def test_process_first_message_return_message(create_session_with_mock_configs):
|
||||
session = create_session_with_mock_configs()
|
||||
with patch.object(
|
||||
GoosePromptSession, "get_user_input", return_value=UserInput(action=PromptAction.CONTINUE, text="Hello")
|
||||
):
|
||||
message = session.process_first_message()
|
||||
|
||||
assert message.text == "Hello"
|
||||
assert len(session.exchange.messages) == 0
|
||||
|
||||
|
||||
def test_process_first_message_to_exit(create_session_with_mock_configs):
|
||||
session = create_session_with_mock_configs()
|
||||
with patch.object(GoosePromptSession, "get_user_input", return_value=UserInput(action=PromptAction.EXIT)):
|
||||
message = session.process_first_message()
|
||||
|
||||
assert message is None
|
||||
|
||||
|
||||
def test_process_first_message_return_last_exchange_message(create_session_with_mock_configs):
|
||||
session = create_session_with_mock_configs()
|
||||
session.exchange.messages.append(Message.user("Hi"))
|
||||
|
||||
message = session.process_first_message()
|
||||
|
||||
assert message.text == "Hi"
|
||||
assert len(session.exchange.messages) == 0
|
||||
|
||||
|
||||
def test_generate_session_name(create_session_with_mock_configs):
|
||||
session = create_session_with_mock_configs()
|
||||
with patch.object(GoosePromptSession, "get_save_session_name", return_value=SPECIFIED_SESSION_NAME):
|
||||
session.generate_session_name()
|
||||
|
||||
assert session.name == SPECIFIED_SESSION_NAME
|
||||
@@ -0,0 +1,59 @@
|
||||
import json
|
||||
import os
|
||||
from time import time
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
from exchange import Exchange
|
||||
from goose.profile import Profile
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def profile_factory():
|
||||
def _create_profile(attributes={}):
|
||||
profile_attrs = {
|
||||
"provider": "mock_provider",
|
||||
"processor": "mock_processor",
|
||||
"accelerator": "mock_accelerator",
|
||||
"moderator": "mock_moderator",
|
||||
"toolkits": [],
|
||||
}
|
||||
profile_attrs.update(attributes)
|
||||
return Profile(**profile_attrs)
|
||||
|
||||
return _create_profile
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def exchange_factory():
|
||||
def _create_exchange(attributes={}):
|
||||
exchange_attrs = {
|
||||
"provider": "mock_provider",
|
||||
"system": "mock_system",
|
||||
"tools": [],
|
||||
"moderator": Mock(),
|
||||
"model": "mock_model",
|
||||
}
|
||||
exchange_attrs.update(attributes)
|
||||
return Exchange(**exchange_attrs)
|
||||
|
||||
return _create_exchange
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_sessions_path(tmp_path):
|
||||
with patch("goose.cli.config.SESSIONS_PATH", tmp_path) as mock_path:
|
||||
yield mock_path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def create_session_file():
|
||||
def _create_session_file(messages, session_file_path, mtime=time()):
|
||||
with open(session_file_path, "w") as session_file:
|
||||
for m in messages:
|
||||
json.dump(m.to_dict(), session_file)
|
||||
session_file.write("\n")
|
||||
session_file.close()
|
||||
os.utime(session_file_path, (mtime, mtime))
|
||||
|
||||
return _create_session_file
|
||||
@@ -0,0 +1,50 @@
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from goose.cli.prompt.completer import GoosePromptCompleter
|
||||
from goose.command.base import Command
|
||||
from prompt_toolkit.completion import Completion
|
||||
from prompt_toolkit.document import Document
|
||||
|
||||
# Mock Command class
|
||||
dummy_command = Mock(spec=Command)
|
||||
|
||||
dummy_command.get_completions = Mock(
|
||||
return_value=[
|
||||
Completion(text="completion1"),
|
||||
Completion(text="completion2"),
|
||||
]
|
||||
)
|
||||
|
||||
commands_list = {"test_command1": dummy_command, "test_command2": dummy_command}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def completer():
|
||||
return GoosePromptCompleter(commands=commands_list)
|
||||
|
||||
|
||||
def test_get_command_completions(completer):
|
||||
document = Document(text="/test_command1:input")
|
||||
completions = list(completer.get_command_completions(document))
|
||||
assert len(completions) == 2
|
||||
assert completions[0].text == "completion1"
|
||||
assert completions[1].text == "completion2"
|
||||
|
||||
|
||||
def test_get_command_name_completions(completer):
|
||||
document = Document(text="/test")
|
||||
completions = list(completer.get_command_name_completions(document))
|
||||
print(completions)
|
||||
assert len(completions) == 2
|
||||
assert completions[0].text == "test_command1"
|
||||
assert completions[1].text == "test_command2"
|
||||
|
||||
|
||||
def test_get_completions(completer):
|
||||
document = Document(text="/test_command1:input")
|
||||
completions = list(completer.get_completions(document, None))
|
||||
print(completions)
|
||||
assert len(completions) == 2
|
||||
assert completions[0].text == "completion1"
|
||||
assert completions[1].text == "completion2"
|
||||
@@ -0,0 +1,68 @@
|
||||
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
|
||||
|
||||
|
||||
@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_update_plan(developer_toolkit):
|
||||
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
|
||||
@@ -0,0 +1,136 @@
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from exchange import Exchange
|
||||
from goose.utils.ask import ask_an_ai, clear_exchange, replace_prompt
|
||||
|
||||
|
||||
# tests for `ask_an_ai`
|
||||
def test_ask_an_ai_empty_input():
|
||||
"""Test that function raises TypeError if input is empty."""
|
||||
exchange = MagicMock(spec=Exchange)
|
||||
with pytest.raises(TypeError):
|
||||
ask_an_ai("", exchange)
|
||||
|
||||
|
||||
def test_ask_an_ai_no_history():
|
||||
"""Test the no_history functionality."""
|
||||
exchange = MagicMock(spec=Exchange)
|
||||
with patch("goose.utils.ask.clear_exchange") as mock_clear:
|
||||
ask_an_ai("Test input", exchange, no_history=True)
|
||||
mock_clear.assert_called_once_with(exchange)
|
||||
|
||||
|
||||
def test_ask_an_ai_prompt_replacement():
|
||||
"""Test that the prompt is replaced if provided."""
|
||||
exchange = MagicMock(spec=Exchange)
|
||||
prompt = "New prompt"
|
||||
|
||||
with patch("goose.utils.ask.replace_prompt") as mock_replace_prompt:
|
||||
# Configure the mock to return a new mock object with the same spec
|
||||
modified_exchange = MagicMock(spec=Exchange)
|
||||
mock_replace_prompt.return_value = modified_exchange
|
||||
|
||||
ask_an_ai("Test input", exchange, prompt=prompt, no_history=False)
|
||||
|
||||
# Check if replace_prompt was called correctly
|
||||
mock_replace_prompt.assert_called_once_with(exchange, prompt)
|
||||
|
||||
# Assert that the modified exchange was returned correctly
|
||||
assert mock_replace_prompt.return_value is modified_exchange, "Should return the modified exchange mock"
|
||||
|
||||
|
||||
def test_ask_an_ai_exchange_usage():
|
||||
"""Test that the exchange adds and processes the message correctly."""
|
||||
exchange = MagicMock(spec=Exchange)
|
||||
input_text = "Test input"
|
||||
message_mock = MagicMock(return_value="Mocked Message")
|
||||
|
||||
with patch("goose.utils.ask.Message.user", new=message_mock):
|
||||
ask_an_ai(input_text, exchange, no_history=False)
|
||||
|
||||
# Assert that Message.user was called with the correct input
|
||||
message_mock.assert_called_once_with(input_text)
|
||||
|
||||
# Assert that exchange.add was called with the mocked message
|
||||
exchange.add.assert_called_once_with("Mocked Message")
|
||||
exchange.reply.assert_called_once()
|
||||
|
||||
|
||||
def test_ask_an_ai_return_value():
|
||||
"""Test that the function returns the correct reply."""
|
||||
exchange = MagicMock(spec=Exchange)
|
||||
expected_reply = "AI response"
|
||||
exchange.reply.return_value = expected_reply
|
||||
result = ask_an_ai("Test input", exchange, no_history=False)
|
||||
assert result == expected_reply, "Function should return the reply from the exchange."
|
||||
|
||||
|
||||
# tests for `clear_exchange`
|
||||
def test_clear_exchange_without_tools():
|
||||
"""Test clearing messages and checkpoints but not tools."""
|
||||
# Arrange
|
||||
exchange = MagicMock(spec=Exchange)
|
||||
|
||||
# Act
|
||||
new_exchange = clear_exchange(exchange, clear_tools=False)
|
||||
|
||||
# Assert
|
||||
exchange.replace.assert_called_once_with(messages=[], checkpoints=[])
|
||||
assert new_exchange == exchange.replace.return_value, "Should return the modified exchange"
|
||||
|
||||
|
||||
def test_clear_exchange_with_tools():
|
||||
"""Test clearing messages, checkpoints, and tools."""
|
||||
# Arrange
|
||||
exchange = MagicMock(spec=Exchange)
|
||||
|
||||
# Act
|
||||
new_exchange = clear_exchange(exchange, clear_tools=True)
|
||||
|
||||
# Assert
|
||||
exchange.replace.assert_called_once_with(messages=[], checkpoints=[], tools=())
|
||||
assert new_exchange == exchange.replace.return_value, "Should return the modified exchange with tools cleared"
|
||||
|
||||
|
||||
def test_clear_exchange_return_value():
|
||||
"""Test that the returned value is a new exchange object."""
|
||||
# Arrange
|
||||
exchange = MagicMock(spec=Exchange)
|
||||
new_exchange_mock = MagicMock(spec=Exchange)
|
||||
exchange.replace.return_value = new_exchange_mock
|
||||
|
||||
# Act
|
||||
new_exchange = clear_exchange(exchange, clear_tools=False)
|
||||
|
||||
# Assert
|
||||
assert new_exchange == new_exchange_mock, "Returned exchange should be the new exchange instance"
|
||||
|
||||
|
||||
# tests for `replace_prompt`
|
||||
def test_replace_prompt():
|
||||
"""Test that the system prompt is correctly replaced."""
|
||||
# Arrange
|
||||
exchange = MagicMock(spec=Exchange)
|
||||
prompt = "New system prompt"
|
||||
|
||||
# Act
|
||||
new_exchange = replace_prompt(exchange, prompt)
|
||||
|
||||
# Assert
|
||||
exchange.replace.assert_called_once_with(system=prompt)
|
||||
assert new_exchange == exchange.replace.return_value, "Should return the modified exchange with the new prompt"
|
||||
|
||||
|
||||
def test_replace_prompt_return_value():
|
||||
"""Test that the returned value is a new exchange object."""
|
||||
# Arrange
|
||||
exchange = MagicMock(spec=Exchange)
|
||||
expected_new_exchange = MagicMock(spec=Exchange)
|
||||
exchange.replace.return_value = expected_new_exchange
|
||||
|
||||
# Act
|
||||
new_exchange = replace_prompt(exchange, "Another prompt")
|
||||
|
||||
# Assert
|
||||
assert new_exchange == expected_new_exchange, "Returned exchange should be the new exchange instance"
|
||||
@@ -0,0 +1,192 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from goose.utils.file_utils import (
|
||||
create_extensions_list,
|
||||
create_language_weighting,
|
||||
) # Adjust the import path as necessary
|
||||
|
||||
|
||||
# tests for `create_extensions_list`
|
||||
def test_create_extensions_list_valid_input():
|
||||
"""Test with valid input and multiple file extensions."""
|
||||
project_root = "/fake/project/root"
|
||||
max_n = 3
|
||||
files = [
|
||||
"/fake/project/root/file1.py",
|
||||
"/fake/project/root/file2.py",
|
||||
"/fake/project/root/file3.md",
|
||||
"/fake/project/root/file4.md",
|
||||
"/fake/project/root/file5.txt",
|
||||
"/fake/project/root/file6.py",
|
||||
"/fake/project/root/file7.md",
|
||||
]
|
||||
|
||||
with patch("goose.utils.file_utils.create_file_list", return_value=files):
|
||||
extensions = create_extensions_list(project_root, max_n)
|
||||
assert extensions == [".py", ".md", ".txt"], "Should return the top 3 extensions in the correct order"
|
||||
|
||||
|
||||
def test_create_extensions_list_zero_max_n():
|
||||
"""Test that a ValueError is raised when max_n is 0."""
|
||||
project_root = "/fake/project/root"
|
||||
max_n = 0
|
||||
|
||||
with pytest.raises(ValueError, match="Number of file extensions must be greater than 0"):
|
||||
create_extensions_list(project_root, max_n)
|
||||
|
||||
|
||||
def test_create_extensions_list_no_files():
|
||||
"""Test with a project root that contains no files."""
|
||||
project_root = "/fake/project/root"
|
||||
max_n = 3
|
||||
|
||||
with patch("goose.utils.file_utils.create_file_list", return_value=[]):
|
||||
extensions = create_extensions_list(project_root, max_n)
|
||||
assert extensions == [], "Should return an empty list when no files are present"
|
||||
|
||||
|
||||
def test_create_extensions_list_fewer_extensions_than_max_n():
|
||||
"""Test when there are fewer unique extensions than max_n."""
|
||||
project_root = "/fake/project/root"
|
||||
max_n = 5
|
||||
files = [
|
||||
"/fake/project/root/file1.py",
|
||||
"/fake/project/root/file2.py",
|
||||
"/fake/project/root/file3.md",
|
||||
]
|
||||
|
||||
with patch("goose.utils.file_utils.create_file_list", return_value=files):
|
||||
extensions = create_extensions_list(project_root, max_n)
|
||||
assert extensions == [".py", ".md"], "Should return all available extensions when fewer than max_n"
|
||||
|
||||
|
||||
def test_create_extensions_list_files_without_extensions():
|
||||
"""Test that files without extensions are ignored."""
|
||||
project_root = "/fake/project/root"
|
||||
max_n = 3
|
||||
files = [
|
||||
"/fake/project/root/file1",
|
||||
"/fake/project/root/file2.py",
|
||||
"/fake/project/root/file3",
|
||||
"/fake/project/root/file4.md",
|
||||
]
|
||||
|
||||
with patch("goose.utils.file_utils.create_file_list", return_value=files):
|
||||
extensions = create_extensions_list(project_root, max_n)
|
||||
assert extensions == [".py", ".md"], "Should ignore files without extensions"
|
||||
|
||||
|
||||
# tests for `create_language_weighting`
|
||||
def test_create_language_weighting_normal_case():
|
||||
"""Test the function with multiple files and different sizes."""
|
||||
files = [
|
||||
"/fake/project/file1.py",
|
||||
"/fake/project/file2.py",
|
||||
"/fake/project/file3.md",
|
||||
"/fake/project/file4.txt",
|
||||
]
|
||||
|
||||
sizes = {
|
||||
"/fake/project/file1.py": 100,
|
||||
"/fake/project/file2.py": 200,
|
||||
"/fake/project/file3.md": 50,
|
||||
"/fake/project/file4.txt": 150,
|
||||
}
|
||||
|
||||
# Mocking os.path.getsize to return different sizes for different files
|
||||
with patch("os.path.getsize") as mock_getsize:
|
||||
mock_getsize.side_effect = lambda file: sizes[file]
|
||||
|
||||
result = create_language_weighting(files)
|
||||
|
||||
total = sum(sizes.values())
|
||||
|
||||
expected_result = {
|
||||
".py": 300 / total * 100, # 300 out of 600 total
|
||||
".txt": 150 / total * 100, # 150 out of 600 total
|
||||
".md": 50 / total * 100, # 50 out of 600 total
|
||||
}
|
||||
|
||||
# Check if the result matches the expected output
|
||||
assert result[".py"] == pytest.approx(expected_result.get(".py"), 0.01)
|
||||
assert result[".txt"] == pytest.approx(expected_result.get(".txt"), 0.01)
|
||||
assert result[".md"] == pytest.approx(expected_result.get(".md"), 0.01)
|
||||
|
||||
|
||||
def test_create_language_weighting_no_files():
|
||||
"""Test the function when no files are provided."""
|
||||
files = []
|
||||
|
||||
result = create_language_weighting(files)
|
||||
assert result == {}, "Should return an empty dictionary when no files are provided"
|
||||
|
||||
|
||||
def test_create_language_weighting_files_without_extensions():
|
||||
"""Test the function when files have no extensions."""
|
||||
files = [
|
||||
"/fake/project/file1",
|
||||
"/fake/project/file2",
|
||||
]
|
||||
|
||||
with patch("os.path.getsize", return_value=100):
|
||||
result = create_language_weighting(files)
|
||||
|
||||
assert result == {}, "Should return an empty dictionary when files have no extensions"
|
||||
|
||||
|
||||
def test_create_language_weighting_zero_total_size():
|
||||
"""Test the function when all files have a size of 0."""
|
||||
files = [
|
||||
"/fake/project/file1.py",
|
||||
"/fake/project/file2.py",
|
||||
]
|
||||
|
||||
with patch("os.path.getsize", return_value=0):
|
||||
result = create_language_weighting(files)
|
||||
|
||||
assert result == {".py": 0}
|
||||
|
||||
|
||||
def test_create_language_weighting_single_file():
|
||||
"""Test the function with a single file."""
|
||||
files = [
|
||||
"/fake/project/file1.py",
|
||||
]
|
||||
|
||||
with patch("os.path.getsize", return_value=100):
|
||||
result = create_language_weighting(files)
|
||||
|
||||
assert result == {".py": 100.0}, "Should return 100% for the single file's extension"
|
||||
|
||||
|
||||
def test_create_language_weighting_mixed_extensions():
|
||||
"""Test the function with files of mixed extensions and sizes."""
|
||||
files = [
|
||||
"/fake/project/file1.py",
|
||||
"/fake/project/file2.py",
|
||||
"/fake/project/file3.md",
|
||||
"/fake/project/file4.txt",
|
||||
"/fake/project/file5.md",
|
||||
]
|
||||
|
||||
with patch("os.path.getsize") as mock_getsize:
|
||||
mock_getsize.side_effect = lambda file: {
|
||||
"/fake/project/file1.py": 100,
|
||||
"/fake/project/file2.py": 100,
|
||||
"/fake/project/file3.md": 200,
|
||||
"/fake/project/file4.txt": 300,
|
||||
"/fake/project/file5.md": 100,
|
||||
}[file]
|
||||
|
||||
result = create_language_weighting(files)
|
||||
|
||||
expected_result = {
|
||||
".txt": 37.5, # 300 out of 800 total
|
||||
".md": 37.5, # 300 out of 800 total
|
||||
".py": 25.0, # 200 out of 800 total
|
||||
}
|
||||
|
||||
assert result[".txt"] == pytest.approx(expected_result.get(".txt"), 0.01)
|
||||
assert result[".md"] == pytest.approx(expected_result.get(".md"), 0.01)
|
||||
assert result[".py"] == pytest.approx(expected_result.get(".py"), 0.01)
|
||||
@@ -0,0 +1,77 @@
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from exchange import Message
|
||||
from goose.utils.session_file import list_sorted_session_files, read_from_file, session_file_exists, write_to_file
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def file_path(tmp_path):
|
||||
return tmp_path / "test_file.jsonl"
|
||||
|
||||
|
||||
def test_read_write_to_file(file_path):
|
||||
messages = [
|
||||
Message.user("prompt1"),
|
||||
Message.user("prompt2"),
|
||||
]
|
||||
write_to_file(file_path, messages)
|
||||
assert file_path.exists()
|
||||
|
||||
assert read_from_file(file_path) == messages
|
||||
|
||||
|
||||
def test_read_from_file_non_existing_file(tmp_path):
|
||||
with pytest.raises(FileNotFoundError):
|
||||
read_from_file(tmp_path / "no_existing.json")
|
||||
|
||||
|
||||
def test_read_from_file_non_jsonl_file(file_path):
|
||||
file_path.write_text("Hello World")
|
||||
with pytest.raises(RuntimeError):
|
||||
read_from_file(file_path)
|
||||
|
||||
|
||||
def test_list_sorted_session_files(tmp_path):
|
||||
session_files_directory = tmp_path / "session_files_dir"
|
||||
session_files_directory.mkdir()
|
||||
file_names = ["file1", "file2", "file3"]
|
||||
created_session_files = [create_session_file(session_files_directory, file_name) for file_name in file_names]
|
||||
|
||||
sorted_files = list_sorted_session_files(session_files_directory)
|
||||
assert sorted_files == {
|
||||
"file3": created_session_files[2],
|
||||
"file2": created_session_files[1],
|
||||
"file1": created_session_files[0],
|
||||
}
|
||||
|
||||
|
||||
def test_list_sorted_session_without_session_files(tmp_path):
|
||||
session_files_directory = tmp_path / "session_files_dir"
|
||||
|
||||
sorted_files = list_sorted_session_files(session_files_directory)
|
||||
assert sorted_files == {}
|
||||
|
||||
|
||||
def test_session_file_exists_return_false_when_directory_does_not_exist(tmp_path):
|
||||
session_files_directory = tmp_path / "session_files_dir"
|
||||
assert not session_file_exists(session_files_directory)
|
||||
|
||||
|
||||
def test_session_file_exists_return_false_when_no_session_file_exists(tmp_path):
|
||||
session_files_directory = tmp_path / "session_files_dir"
|
||||
session_files_directory.mkdir()
|
||||
assert not session_file_exists(session_files_directory)
|
||||
|
||||
|
||||
def test_session_file_exists_return_true_when_session_file_exists(tmp_path):
|
||||
session_files_directory = tmp_path / "session_files_dir"
|
||||
session_files_directory.mkdir()
|
||||
create_session_file(session_files_directory, "session1")
|
||||
assert session_file_exists(session_files_directory)
|
||||
|
||||
|
||||
def create_session_file(file_path, file_name) -> Path:
|
||||
file = file_path / f"{file_name}.jsonl"
|
||||
file.touch()
|
||||
return file
|
||||
@@ -0,0 +1,63 @@
|
||||
import string
|
||||
|
||||
import pytest
|
||||
from goose.utils import droid, ensure, ensure_list, load_plugins
|
||||
|
||||
|
||||
class MockClass:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.name == other.name
|
||||
|
||||
|
||||
def test_load_plugins():
|
||||
plugins = load_plugins("exchange.provider")
|
||||
assert isinstance(plugins, dict)
|
||||
assert len(plugins) > 0
|
||||
|
||||
|
||||
def test_ensure_with_class():
|
||||
mock_class = MockClass("foo")
|
||||
assert ensure(MockClass)(mock_class) == mock_class
|
||||
|
||||
|
||||
def test_ensure_with_dictionary():
|
||||
mock_class = ensure(MockClass)({"name": "foo"})
|
||||
assert mock_class == MockClass("foo")
|
||||
|
||||
|
||||
def test_ensure_with_invalid_dictionary():
|
||||
with pytest.raises(TypeError):
|
||||
ensure(MockClass)({"age": "foo"})
|
||||
|
||||
|
||||
def test_ensure_with_list():
|
||||
mock_class = ensure(MockClass)(["foo"])
|
||||
assert mock_class == MockClass("foo")
|
||||
|
||||
|
||||
def test_ensure_with_invalid_list():
|
||||
with pytest.raises(TypeError):
|
||||
ensure(MockClass)(["foo", "bar"])
|
||||
|
||||
|
||||
def test_ensure_with_value():
|
||||
mock_class = ensure(MockClass)("foo")
|
||||
assert mock_class == MockClass("foo")
|
||||
|
||||
|
||||
def test_ensure_list():
|
||||
obj_list = ensure_list(MockClass)(["foo", "bar"])
|
||||
assert obj_list == [MockClass("foo"), MockClass("bar")]
|
||||
|
||||
|
||||
def test_droid():
|
||||
result = droid()
|
||||
assert isinstance(result, str)
|
||||
assert len(result) == 4
|
||||
for character in [result[i] for i in [0, 2]]:
|
||||
assert character in string.ascii_lowercase, "should be in lower case"
|
||||
for character in [result[i] for i in [1, 3]]:
|
||||
assert character in string.digits, "should be a digit"
|
||||
Reference in New Issue
Block a user