chore: standardize string quotes and fix import ordering across multiple modules
This commit is contained in:
+7
-17
@@ -1,8 +1,9 @@
|
||||
import pytest
|
||||
import os
|
||||
import tempfile
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def temp_dir():
|
||||
@@ -13,19 +14,8 @@ def temp_dir():
|
||||
@pytest.fixture
|
||||
def mock_api_response():
|
||||
return {
|
||||
'choices': [
|
||||
{
|
||||
'message': {
|
||||
'role': 'assistant',
|
||||
'content': 'Test response'
|
||||
}
|
||||
}
|
||||
],
|
||||
'usage': {
|
||||
'prompt_tokens': 10,
|
||||
'completion_tokens': 5,
|
||||
'total_tokens': 15
|
||||
}
|
||||
"choices": [{"message": {"role": "assistant", "content": "Test response"}}],
|
||||
"usage": {"prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15},
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +37,7 @@ def mock_args():
|
||||
|
||||
@pytest.fixture
|
||||
def sample_context_file(temp_dir):
|
||||
context_path = os.path.join(temp_dir, '.rcontext.txt')
|
||||
with open(context_path, 'w') as f:
|
||||
f.write('Sample context content\n')
|
||||
context_path = os.path.join(temp_dir, ".rcontext.txt")
|
||||
with open(context_path, "w") as f:
|
||||
f.write("Sample context content\n")
|
||||
return context_path
|
||||
|
||||
@@ -1,39 +1,53 @@
|
||||
import pytest
|
||||
from pr.core.advanced_context import AdvancedContextManager
|
||||
|
||||
|
||||
def test_adaptive_context_window_simple():
|
||||
mgr = AdvancedContextManager()
|
||||
messages = [{'content': 'short'}, {'content': 'this is a longer message with more words'}]
|
||||
window = mgr.adaptive_context_window(messages, 'simple')
|
||||
messages = [
|
||||
{"content": "short"},
|
||||
{"content": "this is a longer message with more words"},
|
||||
]
|
||||
window = mgr.adaptive_context_window(messages, "simple")
|
||||
assert isinstance(window, int)
|
||||
assert window >= 10
|
||||
|
||||
|
||||
def test_adaptive_context_window_medium():
|
||||
mgr = AdvancedContextManager()
|
||||
messages = [{'content': 'short'}, {'content': 'this is a longer message with more words'}]
|
||||
window = mgr.adaptive_context_window(messages, 'medium')
|
||||
messages = [
|
||||
{"content": "short"},
|
||||
{"content": "this is a longer message with more words"},
|
||||
]
|
||||
window = mgr.adaptive_context_window(messages, "medium")
|
||||
assert isinstance(window, int)
|
||||
assert window >= 20
|
||||
|
||||
|
||||
def test_adaptive_context_window_complex():
|
||||
mgr = AdvancedContextManager()
|
||||
messages = [{'content': 'short'}, {'content': 'this is a longer message with more words'}]
|
||||
window = mgr.adaptive_context_window(messages, 'complex')
|
||||
messages = [
|
||||
{"content": "short"},
|
||||
{"content": "this is a longer message with more words"},
|
||||
]
|
||||
window = mgr.adaptive_context_window(messages, "complex")
|
||||
assert isinstance(window, int)
|
||||
assert window >= 35
|
||||
|
||||
|
||||
def test_analyze_message_complexity():
|
||||
mgr = AdvancedContextManager()
|
||||
messages = [{'content': 'hello world'}, {'content': 'hello again'}]
|
||||
messages = [{"content": "hello world"}, {"content": "hello again"}]
|
||||
score = mgr._analyze_message_complexity(messages)
|
||||
assert 0 <= score <= 1
|
||||
|
||||
|
||||
def test_analyze_message_complexity_empty():
|
||||
mgr = AdvancedContextManager()
|
||||
messages = []
|
||||
score = mgr._analyze_message_complexity(messages)
|
||||
assert score == 0
|
||||
|
||||
|
||||
def test_extract_key_sentences():
|
||||
mgr = AdvancedContextManager()
|
||||
text = "This is the first sentence. This is the second sentence. This is a longer third sentence with more words."
|
||||
@@ -41,41 +55,47 @@ def test_extract_key_sentences():
|
||||
assert len(sentences) <= 2
|
||||
assert all(isinstance(s, str) for s in sentences)
|
||||
|
||||
|
||||
def test_extract_key_sentences_empty():
|
||||
mgr = AdvancedContextManager()
|
||||
text = ""
|
||||
sentences = mgr.extract_key_sentences(text, 5)
|
||||
assert sentences == []
|
||||
|
||||
|
||||
def test_advanced_summarize_messages():
|
||||
mgr = AdvancedContextManager()
|
||||
messages = [{'content': 'Hello'}, {'content': 'How are you?'}]
|
||||
messages = [{"content": "Hello"}, {"content": "How are you?"}]
|
||||
summary = mgr.advanced_summarize_messages(messages)
|
||||
assert isinstance(summary, str)
|
||||
|
||||
|
||||
def test_advanced_summarize_messages_empty():
|
||||
mgr = AdvancedContextManager()
|
||||
messages = []
|
||||
summary = mgr.advanced_summarize_messages(messages)
|
||||
assert summary == "No content to summarize."
|
||||
|
||||
|
||||
def test_score_message_relevance():
|
||||
mgr = AdvancedContextManager()
|
||||
message = {'content': 'hello world'}
|
||||
context = 'world hello'
|
||||
message = {"content": "hello world"}
|
||||
context = "world hello"
|
||||
score = mgr.score_message_relevance(message, context)
|
||||
assert 0 <= score <= 1
|
||||
|
||||
|
||||
def test_score_message_relevance_no_overlap():
|
||||
mgr = AdvancedContextManager()
|
||||
message = {'content': 'hello'}
|
||||
context = 'world'
|
||||
message = {"content": "hello"}
|
||||
context = "world"
|
||||
score = mgr.score_message_relevance(message, context)
|
||||
assert score == 0
|
||||
|
||||
|
||||
def test_score_message_relevance_empty():
|
||||
mgr = AdvancedContextManager()
|
||||
message = {'content': ''}
|
||||
context = ''
|
||||
message = {"content": ""}
|
||||
context = ""
|
||||
score = mgr.score_message_relevance(message, context)
|
||||
assert score == 0
|
||||
assert score == 0
|
||||
|
||||
+138
-52
@@ -1,127 +1,213 @@
|
||||
import pytest
|
||||
import time
|
||||
from pr.agents.agent_communication import (
|
||||
AgentCommunicationBus,
|
||||
AgentMessage,
|
||||
MessageType,
|
||||
)
|
||||
from pr.agents.agent_manager import AgentInstance, AgentManager
|
||||
from pr.agents.agent_roles import AgentRole, get_agent_role, list_agent_roles
|
||||
from pr.agents.agent_manager import AgentManager, AgentInstance
|
||||
from pr.agents.agent_communication import AgentCommunicationBus, AgentMessage, MessageType
|
||||
|
||||
|
||||
def test_get_agent_role():
|
||||
role = get_agent_role('coding')
|
||||
role = get_agent_role("coding")
|
||||
assert isinstance(role, AgentRole)
|
||||
assert role.name == 'coding'
|
||||
assert role.name == "coding"
|
||||
|
||||
|
||||
def test_list_agent_roles():
|
||||
roles = list_agent_roles()
|
||||
assert isinstance(roles, dict)
|
||||
assert len(roles) > 0
|
||||
assert 'coding' in roles
|
||||
assert "coding" in roles
|
||||
|
||||
|
||||
def test_agent_role():
|
||||
role = AgentRole(name='test', description='test', system_prompt='test', allowed_tools=set(), specialization_areas=[])
|
||||
assert role.name == 'test'
|
||||
role = AgentRole(
|
||||
name="test",
|
||||
description="test",
|
||||
system_prompt="test",
|
||||
allowed_tools=set(),
|
||||
specialization_areas=[],
|
||||
)
|
||||
assert role.name == "test"
|
||||
|
||||
|
||||
def test_agent_instance():
|
||||
role = get_agent_role('coding')
|
||||
instance = AgentInstance(agent_id='test', role=role)
|
||||
assert instance.agent_id == 'test'
|
||||
role = get_agent_role("coding")
|
||||
instance = AgentInstance(agent_id="test", role=role)
|
||||
assert instance.agent_id == "test"
|
||||
assert instance.role == role
|
||||
|
||||
|
||||
def test_agent_manager_init():
|
||||
mgr = AgentManager(':memory:', None)
|
||||
mgr = AgentManager(":memory:", None)
|
||||
assert mgr is not None
|
||||
|
||||
|
||||
def test_agent_manager_create_agent():
|
||||
mgr = AgentManager(':memory:', None)
|
||||
agent = mgr.create_agent('coding', 'test_agent')
|
||||
mgr = AgentManager(":memory:", None)
|
||||
agent = mgr.create_agent("coding", "test_agent")
|
||||
assert agent is not None
|
||||
|
||||
|
||||
def test_agent_manager_get_agent():
|
||||
mgr = AgentManager(':memory:', None)
|
||||
mgr.create_agent('coding', 'test_agent')
|
||||
agent = mgr.get_agent('test_agent')
|
||||
mgr = AgentManager(":memory:", None)
|
||||
mgr.create_agent("coding", "test_agent")
|
||||
agent = mgr.get_agent("test_agent")
|
||||
assert isinstance(agent, AgentInstance)
|
||||
|
||||
|
||||
def test_agent_manager_remove_agent():
|
||||
mgr = AgentManager(':memory:', None)
|
||||
mgr.create_agent('coding', 'test_agent')
|
||||
mgr.remove_agent('test_agent')
|
||||
agent = mgr.get_agent('test_agent')
|
||||
mgr = AgentManager(":memory:", None)
|
||||
mgr.create_agent("coding", "test_agent")
|
||||
mgr.remove_agent("test_agent")
|
||||
agent = mgr.get_agent("test_agent")
|
||||
assert agent is None
|
||||
|
||||
|
||||
def test_agent_manager_send_agent_message():
|
||||
mgr = AgentManager(':memory:', None)
|
||||
mgr.create_agent('coding', 'a')
|
||||
mgr.create_agent('coding', 'b')
|
||||
mgr.send_agent_message('a', 'b', 'test')
|
||||
mgr = AgentManager(":memory:", None)
|
||||
mgr.create_agent("coding", "a")
|
||||
mgr.create_agent("coding", "b")
|
||||
mgr.send_agent_message("a", "b", "test")
|
||||
assert True
|
||||
|
||||
|
||||
def test_agent_manager_get_agent_messages():
|
||||
mgr = AgentManager(':memory:', None)
|
||||
mgr.create_agent('coding', 'test')
|
||||
messages = mgr.get_agent_messages('test')
|
||||
mgr = AgentManager(":memory:", None)
|
||||
mgr.create_agent("coding", "test")
|
||||
messages = mgr.get_agent_messages("test")
|
||||
assert isinstance(messages, list)
|
||||
|
||||
|
||||
def test_agent_manager_get_session_summary():
|
||||
mgr = AgentManager(':memory:', None)
|
||||
mgr = AgentManager(":memory:", None)
|
||||
summary = mgr.get_session_summary()
|
||||
assert isinstance(summary, str)
|
||||
|
||||
|
||||
def test_agent_manager_collaborate_agents():
|
||||
mgr = AgentManager(':memory:', None)
|
||||
result = mgr.collaborate_agents('orchestrator', 'task', ['coding', 'research'])
|
||||
mgr = AgentManager(":memory:", None)
|
||||
result = mgr.collaborate_agents("orchestrator", "task", ["coding", "research"])
|
||||
assert result is not None
|
||||
|
||||
|
||||
def test_agent_manager_execute_agent_task():
|
||||
mgr = AgentManager(':memory:', None)
|
||||
mgr.create_agent('coding', 'test')
|
||||
result = mgr.execute_agent_task('test', 'task')
|
||||
mgr = AgentManager(":memory:", None)
|
||||
mgr.create_agent("coding", "test")
|
||||
result = mgr.execute_agent_task("test", "task")
|
||||
assert result is not None
|
||||
|
||||
|
||||
def test_agent_manager_clear_session():
|
||||
mgr = AgentManager(':memory:', None)
|
||||
mgr = AgentManager(":memory:", None)
|
||||
mgr.clear_session()
|
||||
assert True
|
||||
|
||||
|
||||
def test_agent_message():
|
||||
msg = AgentMessage(from_agent='a', to_agent='b', message_type=MessageType.REQUEST, content='test', metadata={}, timestamp=1.0, message_id='id')
|
||||
assert msg.from_agent == 'a'
|
||||
msg = AgentMessage(
|
||||
from_agent="a",
|
||||
to_agent="b",
|
||||
message_type=MessageType.REQUEST,
|
||||
content="test",
|
||||
metadata={},
|
||||
timestamp=1.0,
|
||||
message_id="id",
|
||||
)
|
||||
assert msg.from_agent == "a"
|
||||
|
||||
|
||||
def test_agent_message_to_dict():
|
||||
msg = AgentMessage(from_agent='a', to_agent='b', message_type=MessageType.REQUEST, content='test', metadata={}, timestamp=1.0, message_id='id')
|
||||
msg = AgentMessage(
|
||||
from_agent="a",
|
||||
to_agent="b",
|
||||
message_type=MessageType.REQUEST,
|
||||
content="test",
|
||||
metadata={},
|
||||
timestamp=1.0,
|
||||
message_id="id",
|
||||
)
|
||||
d = msg.to_dict()
|
||||
assert isinstance(d, dict)
|
||||
|
||||
|
||||
def test_agent_message_from_dict():
|
||||
d = {'from_agent': 'a', 'to_agent': 'b', 'message_type': 'request', 'content': 'test', 'metadata': {}, 'timestamp': 1.0, 'message_id': 'id'}
|
||||
d = {
|
||||
"from_agent": "a",
|
||||
"to_agent": "b",
|
||||
"message_type": "request",
|
||||
"content": "test",
|
||||
"metadata": {},
|
||||
"timestamp": 1.0,
|
||||
"message_id": "id",
|
||||
}
|
||||
msg = AgentMessage.from_dict(d)
|
||||
assert isinstance(msg, AgentMessage)
|
||||
|
||||
|
||||
def test_agent_communication_bus_init():
|
||||
bus = AgentCommunicationBus(':memory:')
|
||||
bus = AgentCommunicationBus(":memory:")
|
||||
assert bus is not None
|
||||
|
||||
|
||||
def test_agent_communication_bus_send_message():
|
||||
bus = AgentCommunicationBus(':memory:')
|
||||
msg = AgentMessage(from_agent='a', to_agent='b', message_type=MessageType.REQUEST, content='test', metadata={}, timestamp=1.0, message_id='id')
|
||||
bus = AgentCommunicationBus(":memory:")
|
||||
msg = AgentMessage(
|
||||
from_agent="a",
|
||||
to_agent="b",
|
||||
message_type=MessageType.REQUEST,
|
||||
content="test",
|
||||
metadata={},
|
||||
timestamp=1.0,
|
||||
message_id="id",
|
||||
)
|
||||
bus.send_message(msg)
|
||||
assert True
|
||||
|
||||
|
||||
def test_agent_communication_bus_receive_messages():
|
||||
bus = AgentCommunicationBus(':memory:')
|
||||
msg = AgentMessage(from_agent='a', to_agent='b', message_type=MessageType.REQUEST, content='test', metadata={}, timestamp=1.0, message_id='id')
|
||||
bus = AgentCommunicationBus(":memory:")
|
||||
msg = AgentMessage(
|
||||
from_agent="a",
|
||||
to_agent="b",
|
||||
message_type=MessageType.REQUEST,
|
||||
content="test",
|
||||
metadata={},
|
||||
timestamp=1.0,
|
||||
message_id="id",
|
||||
)
|
||||
bus.send_message(msg)
|
||||
messages = bus.receive_messages('b')
|
||||
messages = bus.receive_messages("b")
|
||||
assert len(messages) == 1
|
||||
|
||||
|
||||
def test_agent_communication_bus_get_conversation_history():
|
||||
bus = AgentCommunicationBus(':memory:')
|
||||
msg = AgentMessage(from_agent='a', to_agent='b', message_type=MessageType.REQUEST, content='test', metadata={}, timestamp=1.0, message_id='id')
|
||||
bus = AgentCommunicationBus(":memory:")
|
||||
msg = AgentMessage(
|
||||
from_agent="a",
|
||||
to_agent="b",
|
||||
message_type=MessageType.REQUEST,
|
||||
content="test",
|
||||
metadata={},
|
||||
timestamp=1.0,
|
||||
message_id="id",
|
||||
)
|
||||
bus.send_message(msg)
|
||||
history = bus.get_conversation_history('a', 'b')
|
||||
history = bus.get_conversation_history("a", "b")
|
||||
assert len(history) == 1
|
||||
|
||||
|
||||
def test_agent_communication_bus_mark_as_read():
|
||||
bus = AgentCommunicationBus(':memory:')
|
||||
msg = AgentMessage(from_agent='a', to_agent='b', message_type=MessageType.REQUEST, content='test', metadata={}, timestamp=1.0, message_id='id')
|
||||
bus = AgentCommunicationBus(":memory:")
|
||||
msg = AgentMessage(
|
||||
from_agent="a",
|
||||
to_agent="b",
|
||||
message_type=MessageType.REQUEST,
|
||||
content="test",
|
||||
metadata={},
|
||||
timestamp=1.0,
|
||||
message_id="id",
|
||||
)
|
||||
bus.send_message(msg)
|
||||
bus.mark_as_read(msg.message_id)
|
||||
assert True
|
||||
assert True
|
||||
|
||||
+29
-27
@@ -1,63 +1,65 @@
|
||||
import unittest
|
||||
from unittest.mock import patch, MagicMock
|
||||
import json
|
||||
import urllib.error
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from pr.core.api import call_api, list_models
|
||||
|
||||
|
||||
class TestApi(unittest.TestCase):
|
||||
|
||||
@patch('pr.core.api.urllib.request.urlopen')
|
||||
@patch('pr.core.api.auto_slim_messages')
|
||||
@patch("pr.core.api.urllib.request.urlopen")
|
||||
@patch("pr.core.api.auto_slim_messages")
|
||||
def test_call_api_success(self, mock_slim, mock_urlopen):
|
||||
mock_slim.return_value = [{'role': 'user', 'content': 'test'}]
|
||||
mock_slim.return_value = [{"role": "user", "content": "test"}]
|
||||
mock_response = MagicMock()
|
||||
mock_response.read.return_value = b'{"choices": [{"message": {"content": "response"}}], "usage": {"tokens": 10}}'
|
||||
mock_urlopen.return_value.__enter__.return_value = mock_response
|
||||
|
||||
result = call_api([], 'model', 'http://url', 'key', True, [{'name': 'tool'}])
|
||||
result = call_api([], "model", "http://url", "key", True, [{"name": "tool"}])
|
||||
|
||||
self.assertIn('choices', result)
|
||||
self.assertIn("choices", result)
|
||||
mock_urlopen.assert_called_once()
|
||||
|
||||
@patch('urllib.request.urlopen')
|
||||
@patch('pr.core.api.auto_slim_messages')
|
||||
@patch("urllib.request.urlopen")
|
||||
@patch("pr.core.api.auto_slim_messages")
|
||||
def test_call_api_http_error(self, mock_slim, mock_urlopen):
|
||||
mock_slim.return_value = [{'role': 'user', 'content': 'test'}]
|
||||
mock_urlopen.side_effect = urllib.error.HTTPError('http://url', 500, 'error', None, MagicMock())
|
||||
mock_slim.return_value = [{"role": "user", "content": "test"}]
|
||||
mock_urlopen.side_effect = urllib.error.HTTPError(
|
||||
"http://url", 500, "error", None, MagicMock()
|
||||
)
|
||||
|
||||
result = call_api([], 'model', 'http://url', 'key', False, [])
|
||||
result = call_api([], "model", "http://url", "key", False, [])
|
||||
|
||||
self.assertIn('error', result)
|
||||
self.assertIn("error", result)
|
||||
|
||||
@patch('urllib.request.urlopen')
|
||||
@patch('pr.core.api.auto_slim_messages')
|
||||
@patch("urllib.request.urlopen")
|
||||
@patch("pr.core.api.auto_slim_messages")
|
||||
def test_call_api_general_error(self, mock_slim, mock_urlopen):
|
||||
mock_slim.return_value = [{'role': 'user', 'content': 'test'}]
|
||||
mock_urlopen.side_effect = Exception('test error')
|
||||
mock_slim.return_value = [{"role": "user", "content": "test"}]
|
||||
mock_urlopen.side_effect = Exception("test error")
|
||||
|
||||
result = call_api([], 'model', 'http://url', 'key', False, [])
|
||||
result = call_api([], "model", "http://url", "key", False, [])
|
||||
|
||||
self.assertIn('error', result)
|
||||
self.assertIn("error", result)
|
||||
|
||||
@patch('urllib.request.urlopen')
|
||||
@patch("urllib.request.urlopen")
|
||||
def test_list_models_success(self, mock_urlopen):
|
||||
mock_response = MagicMock()
|
||||
mock_response.read.return_value = b'{"data": [{"id": "model1"}]}'
|
||||
mock_urlopen.return_value.__enter__.return_value = mock_response
|
||||
|
||||
result = list_models('http://url', 'key')
|
||||
result = list_models("http://url", "key")
|
||||
|
||||
self.assertEqual(result, [{'id': 'model1'}])
|
||||
self.assertEqual(result, [{"id": "model1"}])
|
||||
|
||||
@patch('urllib.request.urlopen')
|
||||
@patch("urllib.request.urlopen")
|
||||
def test_list_models_error(self, mock_urlopen):
|
||||
mock_urlopen.side_effect = Exception('error')
|
||||
mock_urlopen.side_effect = Exception("error")
|
||||
|
||||
result = list_models('http://url', 'key')
|
||||
result = list_models("http://url", "key")
|
||||
|
||||
self.assertIn('error', result)
|
||||
self.assertIn("error", result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
+63
-41
@@ -1,7 +1,6 @@
|
||||
import unittest
|
||||
from unittest.mock import patch, MagicMock
|
||||
import tempfile
|
||||
import os
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from pr.core.assistant import Assistant, process_message
|
||||
|
||||
|
||||
@@ -12,83 +11,106 @@ class TestAssistant(unittest.TestCase):
|
||||
self.args.verbose = False
|
||||
self.args.debug = False
|
||||
self.args.no_syntax = False
|
||||
self.args.model = 'test-model'
|
||||
self.args.api_url = 'test-url'
|
||||
self.args.model_list_url = 'test-list-url'
|
||||
self.args.model = "test-model"
|
||||
self.args.api_url = "test-url"
|
||||
self.args.model_list_url = "test-list-url"
|
||||
|
||||
@patch('sqlite3.connect')
|
||||
@patch('os.environ.get')
|
||||
@patch('pr.core.context.init_system_message')
|
||||
@patch('pr.core.enhanced_assistant.EnhancedAssistant')
|
||||
@patch("sqlite3.connect")
|
||||
@patch("os.environ.get")
|
||||
@patch("pr.core.context.init_system_message")
|
||||
@patch("pr.core.enhanced_assistant.EnhancedAssistant")
|
||||
def test_init(self, mock_enhanced, mock_init_sys, mock_env, mock_sqlite):
|
||||
mock_env.side_effect = lambda key, default: {'OPENROUTER_API_KEY': 'key', 'AI_MODEL': 'model', 'API_URL': 'url', 'MODEL_LIST_URL': 'list', 'USE_TOOLS': '1', 'STRICT_MODE': '0'}.get(key, default)
|
||||
mock_env.side_effect = lambda key, default: {
|
||||
"OPENROUTER_API_KEY": "key",
|
||||
"AI_MODEL": "model",
|
||||
"API_URL": "url",
|
||||
"MODEL_LIST_URL": "list",
|
||||
"USE_TOOLS": "1",
|
||||
"STRICT_MODE": "0",
|
||||
}.get(key, default)
|
||||
mock_conn = MagicMock()
|
||||
mock_sqlite.return_value = mock_conn
|
||||
mock_init_sys.return_value = {'role': 'system', 'content': 'sys'}
|
||||
mock_init_sys.return_value = {"role": "system", "content": "sys"}
|
||||
|
||||
assistant = Assistant(self.args)
|
||||
|
||||
self.assertEqual(assistant.api_key, 'key')
|
||||
self.assertEqual(assistant.model, 'test-model')
|
||||
self.assertEqual(assistant.api_key, "key")
|
||||
self.assertEqual(assistant.model, "test-model")
|
||||
mock_sqlite.assert_called_once()
|
||||
|
||||
@patch('pr.core.assistant.call_api')
|
||||
@patch('pr.core.assistant.render_markdown')
|
||||
@patch("pr.core.assistant.call_api")
|
||||
@patch("pr.core.assistant.render_markdown")
|
||||
def test_process_response_no_tools(self, mock_render, mock_call):
|
||||
assistant = MagicMock()
|
||||
assistant.messages = MagicMock()
|
||||
assistant.verbose = False
|
||||
assistant.syntax_highlighting = True
|
||||
mock_render.return_value = 'rendered'
|
||||
mock_render.return_value = "rendered"
|
||||
|
||||
response = {'choices': [{'message': {'content': 'content'}}]}
|
||||
response = {"choices": [{"message": {"content": "content"}}]}
|
||||
|
||||
result = Assistant.process_response(assistant, response)
|
||||
|
||||
self.assertEqual(result, 'rendered')
|
||||
assistant.messages.append.assert_called_with({'content': 'content'})
|
||||
self.assertEqual(result, "rendered")
|
||||
assistant.messages.append.assert_called_with({"content": "content"})
|
||||
|
||||
@patch('pr.core.assistant.call_api')
|
||||
@patch('pr.core.assistant.render_markdown')
|
||||
@patch('pr.core.assistant.get_tools_definition')
|
||||
@patch("pr.core.assistant.call_api")
|
||||
@patch("pr.core.assistant.render_markdown")
|
||||
@patch("pr.core.assistant.get_tools_definition")
|
||||
def test_process_response_with_tools(self, mock_tools_def, mock_render, mock_call):
|
||||
assistant = MagicMock()
|
||||
assistant.messages = MagicMock()
|
||||
assistant.verbose = False
|
||||
assistant.syntax_highlighting = True
|
||||
assistant.use_tools = True
|
||||
assistant.model = 'model'
|
||||
assistant.api_url = 'url'
|
||||
assistant.api_key = 'key'
|
||||
assistant.model = "model"
|
||||
assistant.api_url = "url"
|
||||
assistant.api_key = "key"
|
||||
mock_tools_def.return_value = []
|
||||
mock_call.return_value = {'choices': [{'message': {'content': 'follow'}}]}
|
||||
mock_call.return_value = {"choices": [{"message": {"content": "follow"}}]}
|
||||
|
||||
response = {'choices': [{'message': {'tool_calls': [{'id': '1', 'function': {'name': 'test', 'arguments': '{}'}}]}}]}
|
||||
response = {
|
||||
"choices": [
|
||||
{
|
||||
"message": {
|
||||
"tool_calls": [
|
||||
{"id": "1", "function": {"name": "test", "arguments": "{}"}}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
with patch.object(assistant, 'execute_tool_calls', return_value=[{'role': 'tool', 'content': 'result'}]):
|
||||
result = Assistant.process_response(assistant, response)
|
||||
with patch.object(
|
||||
assistant,
|
||||
"execute_tool_calls",
|
||||
return_value=[{"role": "tool", "content": "result"}],
|
||||
):
|
||||
Assistant.process_response(assistant, response)
|
||||
|
||||
mock_call.assert_called()
|
||||
|
||||
@patch('pr.core.assistant.call_api')
|
||||
@patch('pr.core.assistant.get_tools_definition')
|
||||
@patch("pr.core.assistant.call_api")
|
||||
@patch("pr.core.assistant.get_tools_definition")
|
||||
def test_process_message(self, mock_tools, mock_call):
|
||||
assistant = MagicMock()
|
||||
assistant.messages = MagicMock()
|
||||
assistant.verbose = False
|
||||
assistant.use_tools = True
|
||||
assistant.model = 'model'
|
||||
assistant.api_url = 'url'
|
||||
assistant.api_key = 'key'
|
||||
assistant.model = "model"
|
||||
assistant.api_url = "url"
|
||||
assistant.api_key = "key"
|
||||
mock_tools.return_value = []
|
||||
mock_call.return_value = {'choices': [{'message': {'content': 'response'}}]}
|
||||
mock_call.return_value = {"choices": [{"message": {"content": "response"}}]}
|
||||
|
||||
with patch('pr.core.assistant.render_markdown', return_value='rendered'):
|
||||
with patch('builtins.print'):
|
||||
process_message(assistant, 'test message')
|
||||
with patch("pr.core.assistant.render_markdown", return_value="rendered"):
|
||||
with patch("builtins.print"):
|
||||
process_message(assistant, "test message")
|
||||
|
||||
assistant.messages.append.assert_called_with({'role': 'user', 'content': 'test message'})
|
||||
assistant.messages.append.assert_called_with(
|
||||
{"role": "user", "content": "test message"}
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
+11
-12
@@ -1,31 +1,30 @@
|
||||
import pytest
|
||||
from pr import config
|
||||
|
||||
|
||||
class TestConfig:
|
||||
|
||||
def test_default_model_exists(self):
|
||||
assert hasattr(config, 'DEFAULT_MODEL')
|
||||
assert hasattr(config, "DEFAULT_MODEL")
|
||||
assert isinstance(config.DEFAULT_MODEL, str)
|
||||
assert len(config.DEFAULT_MODEL) > 0
|
||||
|
||||
def test_api_url_exists(self):
|
||||
assert hasattr(config, 'DEFAULT_API_URL')
|
||||
assert config.DEFAULT_API_URL.startswith('http')
|
||||
assert hasattr(config, "DEFAULT_API_URL")
|
||||
assert config.DEFAULT_API_URL.startswith("http")
|
||||
|
||||
def test_file_paths_exist(self):
|
||||
assert hasattr(config, 'DB_PATH')
|
||||
assert hasattr(config, 'LOG_FILE')
|
||||
assert hasattr(config, 'HISTORY_FILE')
|
||||
assert hasattr(config, "DB_PATH")
|
||||
assert hasattr(config, "LOG_FILE")
|
||||
assert hasattr(config, "HISTORY_FILE")
|
||||
|
||||
def test_autonomous_config(self):
|
||||
assert hasattr(config, 'MAX_AUTONOMOUS_ITERATIONS')
|
||||
assert hasattr(config, "MAX_AUTONOMOUS_ITERATIONS")
|
||||
assert config.MAX_AUTONOMOUS_ITERATIONS > 0
|
||||
|
||||
assert hasattr(config, 'CONTEXT_COMPRESSION_THRESHOLD')
|
||||
assert hasattr(config, "CONTEXT_COMPRESSION_THRESHOLD")
|
||||
assert config.CONTEXT_COMPRESSION_THRESHOLD > 0
|
||||
|
||||
def test_language_keywords(self):
|
||||
assert hasattr(config, 'LANGUAGE_KEYWORDS')
|
||||
assert 'python' in config.LANGUAGE_KEYWORDS
|
||||
assert isinstance(config.LANGUAGE_KEYWORDS['python'], list)
|
||||
assert hasattr(config, "LANGUAGE_KEYWORDS")
|
||||
assert "python" in config.LANGUAGE_KEYWORDS
|
||||
assert isinstance(config.LANGUAGE_KEYWORDS["python"], list)
|
||||
|
||||
+43
-28
@@ -1,56 +1,71 @@
|
||||
import pytest
|
||||
from unittest.mock import patch, mock_open
|
||||
import os
|
||||
from pr.core.config_loader import load_config, _load_config_file, _parse_value, create_default_config
|
||||
from unittest.mock import mock_open, patch
|
||||
|
||||
from pr.core.config_loader import (
|
||||
_load_config_file,
|
||||
_parse_value,
|
||||
create_default_config,
|
||||
load_config,
|
||||
)
|
||||
|
||||
|
||||
def test_parse_value_string():
|
||||
assert _parse_value('hello') == 'hello'
|
||||
assert _parse_value("hello") == "hello"
|
||||
|
||||
|
||||
def test_parse_value_int():
|
||||
assert _parse_value('123') == 123
|
||||
assert _parse_value("123") == 123
|
||||
|
||||
|
||||
def test_parse_value_float():
|
||||
assert _parse_value('1.23') == 1.23
|
||||
assert _parse_value("1.23") == 1.23
|
||||
|
||||
|
||||
def test_parse_value_bool_true():
|
||||
assert _parse_value('true') == True
|
||||
assert _parse_value("true") == True
|
||||
|
||||
|
||||
def test_parse_value_bool_false():
|
||||
assert _parse_value('false') == False
|
||||
assert _parse_value("false") == False
|
||||
|
||||
|
||||
def test_parse_value_bool_upper():
|
||||
assert _parse_value('TRUE') == True
|
||||
assert _parse_value("TRUE") == True
|
||||
|
||||
@patch('os.path.exists', return_value=False)
|
||||
|
||||
@patch("os.path.exists", return_value=False)
|
||||
def test_load_config_file_not_exists(mock_exists):
|
||||
config = _load_config_file('test.ini')
|
||||
config = _load_config_file("test.ini")
|
||||
assert config == {}
|
||||
|
||||
@patch('os.path.exists', return_value=True)
|
||||
@patch('configparser.ConfigParser')
|
||||
|
||||
@patch("os.path.exists", return_value=True)
|
||||
@patch("configparser.ConfigParser")
|
||||
def test_load_config_file_exists(mock_parser_class, mock_exists):
|
||||
mock_parser = mock_parser_class.return_value
|
||||
mock_parser.sections.return_value = ['api']
|
||||
mock_parser.items.return_value = [('key', 'value')]
|
||||
config = _load_config_file('test.ini')
|
||||
assert 'api' in config
|
||||
assert config['api']['key'] == 'value'
|
||||
mock_parser.sections.return_value = ["api"]
|
||||
mock_parser.items.return_value = [("key", "value")]
|
||||
config = _load_config_file("test.ini")
|
||||
assert "api" in config
|
||||
assert config["api"]["key"] == "value"
|
||||
|
||||
@patch('pr.core.config_loader._load_config_file')
|
||||
|
||||
@patch("pr.core.config_loader._load_config_file")
|
||||
def test_load_config(mock_load):
|
||||
mock_load.side_effect = [{'api': {'key': 'global'}}, {'api': {'key': 'local'}}]
|
||||
mock_load.side_effect = [{"api": {"key": "global"}}, {"api": {"key": "local"}}]
|
||||
config = load_config()
|
||||
assert config['api']['key'] == 'local'
|
||||
assert config["api"]["key"] == "local"
|
||||
|
||||
@patch('builtins.open', new_callable=mock_open)
|
||||
|
||||
@patch("builtins.open", new_callable=mock_open)
|
||||
def test_create_default_config(mock_file):
|
||||
result = create_default_config('test.ini')
|
||||
result = create_default_config("test.ini")
|
||||
assert result == True
|
||||
mock_file.assert_called_once_with('test.ini', 'w')
|
||||
mock_file.assert_called_once_with("test.ini", "w")
|
||||
handle = mock_file()
|
||||
handle.write.assert_called_once()
|
||||
|
||||
@patch('builtins.open', side_effect=Exception('error'))
|
||||
|
||||
@patch("builtins.open", side_effect=Exception("error"))
|
||||
def test_create_default_config_error(mock_file):
|
||||
result = create_default_config('test.ini')
|
||||
assert result == False
|
||||
result = create_default_config("test.ini")
|
||||
assert result == False
|
||||
|
||||
+10
-11
@@ -1,30 +1,29 @@
|
||||
import pytest
|
||||
from pr.core.context import should_compress_context, compress_context
|
||||
from pr.config import RECENT_MESSAGES_TO_KEEP
|
||||
from pr.core.context import compress_context, should_compress_context
|
||||
|
||||
|
||||
class TestContextManagement:
|
||||
|
||||
def test_should_compress_context_below_threshold(self):
|
||||
messages = [{'role': 'user', 'content': 'test'}] * 10
|
||||
messages = [{"role": "user", "content": "test"}] * 10
|
||||
assert should_compress_context(messages) is False
|
||||
|
||||
def test_should_compress_context_above_threshold(self):
|
||||
messages = [{'role': 'user', 'content': 'test'}] * 35
|
||||
messages = [{"role": "user", "content": "test"}] * 35
|
||||
assert should_compress_context(messages) is True
|
||||
|
||||
def test_compress_context_preserves_system_message(self):
|
||||
messages = [
|
||||
{'role': 'system', 'content': 'System prompt'},
|
||||
{'role': 'user', 'content': 'Hello'},
|
||||
{'role': 'assistant', 'content': 'Hi'},
|
||||
{"role": "system", "content": "System prompt"},
|
||||
{"role": "user", "content": "Hello"},
|
||||
{"role": "assistant", "content": "Hi"},
|
||||
] * 40 # Ensure compression
|
||||
compressed = compress_context(messages)
|
||||
assert compressed[0]['role'] == 'system'
|
||||
assert 'System prompt' in compressed[0]['content']
|
||||
assert compressed[0]["role"] == "system"
|
||||
assert "System prompt" in compressed[0]["content"]
|
||||
|
||||
def test_compress_context_keeps_recent_messages(self):
|
||||
messages = [{'role': 'user', 'content': f'msg{i}'} for i in range(40)]
|
||||
messages = [{"role": "user", "content": f"msg{i}"} for i in range(40)]
|
||||
compressed = compress_context(messages)
|
||||
# Should keep recent messages
|
||||
recent = compressed[-RECENT_MESSAGES_TO_KEEP:]
|
||||
@@ -32,4 +31,4 @@ class TestContextManagement:
|
||||
# Check that the messages are the most recent ones
|
||||
for i, msg in enumerate(recent):
|
||||
expected_index = 40 - RECENT_MESSAGES_TO_KEEP + i
|
||||
assert msg['content'] == f'msg{expected_index}'
|
||||
assert msg["content"] == f"msg{expected_index}"
|
||||
|
||||
@@ -1,89 +1,97 @@
|
||||
import pytest
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from pr.core.enhanced_assistant import EnhancedAssistant
|
||||
|
||||
|
||||
def test_enhanced_assistant_init():
|
||||
mock_base = MagicMock()
|
||||
assistant = EnhancedAssistant(mock_base)
|
||||
assert assistant.base == mock_base
|
||||
assert assistant.current_conversation_id is not None
|
||||
|
||||
|
||||
def test_enhanced_call_api_with_cache():
|
||||
mock_base = MagicMock()
|
||||
mock_base.model = 'test-model'
|
||||
mock_base.api_url = 'http://test'
|
||||
mock_base.api_key = 'key'
|
||||
mock_base.model = "test-model"
|
||||
mock_base.api_url = "http://test"
|
||||
mock_base.api_key = "key"
|
||||
mock_base.use_tools = False
|
||||
mock_base.verbose = False
|
||||
|
||||
|
||||
assistant = EnhancedAssistant(mock_base)
|
||||
assistant.api_cache = MagicMock()
|
||||
assistant.api_cache.get.return_value = {'cached': True}
|
||||
|
||||
result = assistant.enhanced_call_api([{'role': 'user', 'content': 'test'}])
|
||||
assert result == {'cached': True}
|
||||
assistant.api_cache.get.return_value = {"cached": True}
|
||||
|
||||
result = assistant.enhanced_call_api([{"role": "user", "content": "test"}])
|
||||
assert result == {"cached": True}
|
||||
assistant.api_cache.get.assert_called_once()
|
||||
|
||||
|
||||
def test_enhanced_call_api_without_cache():
|
||||
mock_base = MagicMock()
|
||||
mock_base.model = 'test-model'
|
||||
mock_base.api_url = 'http://test'
|
||||
mock_base.api_key = 'key'
|
||||
mock_base.model = "test-model"
|
||||
mock_base.api_url = "http://test"
|
||||
mock_base.api_key = "key"
|
||||
mock_base.use_tools = False
|
||||
mock_base.verbose = False
|
||||
|
||||
|
||||
assistant = EnhancedAssistant(mock_base)
|
||||
assistant.api_cache = None
|
||||
|
||||
|
||||
# It will try to call API and fail with network error, but that's expected
|
||||
result = assistant.enhanced_call_api([{'role': 'user', 'content': 'test'}])
|
||||
assert 'error' in result
|
||||
result = assistant.enhanced_call_api([{"role": "user", "content": "test"}])
|
||||
assert "error" in result
|
||||
|
||||
|
||||
def test_execute_workflow_not_found():
|
||||
mock_base = MagicMock()
|
||||
assistant = EnhancedAssistant(mock_base)
|
||||
assistant.workflow_storage = MagicMock()
|
||||
assistant.workflow_storage.load_workflow_by_name.return_value = None
|
||||
|
||||
result = assistant.execute_workflow('nonexistent')
|
||||
assert 'error' in result
|
||||
|
||||
result = assistant.execute_workflow("nonexistent")
|
||||
assert "error" in result
|
||||
|
||||
|
||||
def test_create_agent():
|
||||
mock_base = MagicMock()
|
||||
assistant = EnhancedAssistant(mock_base)
|
||||
assistant.agent_manager = MagicMock()
|
||||
assistant.agent_manager.create_agent.return_value = 'agent_id'
|
||||
|
||||
result = assistant.create_agent('role')
|
||||
assert result == 'agent_id'
|
||||
assistant.agent_manager.create_agent.return_value = "agent_id"
|
||||
|
||||
result = assistant.create_agent("role")
|
||||
assert result == "agent_id"
|
||||
|
||||
|
||||
def test_search_knowledge():
|
||||
mock_base = MagicMock()
|
||||
assistant = EnhancedAssistant(mock_base)
|
||||
assistant.knowledge_store = MagicMock()
|
||||
assistant.knowledge_store.search_entries.return_value = [{'result': True}]
|
||||
|
||||
result = assistant.search_knowledge('query')
|
||||
assert result == [{'result': True}]
|
||||
assistant.knowledge_store.search_entries.return_value = [{"result": True}]
|
||||
|
||||
result = assistant.search_knowledge("query")
|
||||
assert result == [{"result": True}]
|
||||
|
||||
|
||||
def test_get_cache_statistics():
|
||||
mock_base = MagicMock()
|
||||
assistant = EnhancedAssistant(mock_base)
|
||||
assistant.api_cache = MagicMock()
|
||||
assistant.api_cache.get_statistics.return_value = {'hits': 10}
|
||||
assistant.api_cache.get_statistics.return_value = {"hits": 10}
|
||||
assistant.tool_cache = MagicMock()
|
||||
assistant.tool_cache.get_statistics.return_value = {'misses': 5}
|
||||
|
||||
assistant.tool_cache.get_statistics.return_value = {"misses": 5}
|
||||
|
||||
stats = assistant.get_cache_statistics()
|
||||
assert 'api_cache' in stats
|
||||
assert 'tool_cache' in stats
|
||||
assert "api_cache" in stats
|
||||
assert "tool_cache" in stats
|
||||
|
||||
|
||||
def test_clear_caches():
|
||||
mock_base = MagicMock()
|
||||
assistant = EnhancedAssistant(mock_base)
|
||||
assistant.api_cache = MagicMock()
|
||||
assistant.tool_cache = MagicMock()
|
||||
|
||||
|
||||
assistant.clear_caches()
|
||||
assistant.api_cache.clear_all.assert_called_once()
|
||||
assistant.tool_cache.clear_all.assert_called_once()
|
||||
|
||||
+10
-9
@@ -1,24 +1,25 @@
|
||||
import pytest
|
||||
import tempfile
|
||||
import os
|
||||
from pr.core.logging import setup_logging, get_logger
|
||||
from pr.core.logging import get_logger, setup_logging
|
||||
|
||||
|
||||
def test_setup_logging_basic():
|
||||
logger = setup_logging(verbose=False)
|
||||
assert logger.name == 'pr'
|
||||
assert logger.name == "pr"
|
||||
assert logger.level == 20 # INFO
|
||||
|
||||
|
||||
def test_setup_logging_verbose():
|
||||
logger = setup_logging(verbose=True)
|
||||
assert logger.name == 'pr'
|
||||
assert logger.name == "pr"
|
||||
assert logger.level == 10 # DEBUG
|
||||
# Should have console handler
|
||||
assert len(logger.handlers) >= 2
|
||||
|
||||
|
||||
def test_get_logger_default():
|
||||
logger = get_logger()
|
||||
assert logger.name == 'pr'
|
||||
assert logger.name == "pr"
|
||||
|
||||
|
||||
def test_get_logger_named():
|
||||
logger = get_logger('test')
|
||||
assert logger.name == 'pr.test'
|
||||
logger = get_logger("test")
|
||||
assert logger.name == "pr.test"
|
||||
|
||||
+59
-43
@@ -1,118 +1,134 @@
|
||||
import pytest
|
||||
from unittest.mock import patch
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from pr.__main__ import main
|
||||
|
||||
|
||||
def test_main_version(capsys):
|
||||
with patch('sys.argv', ['pr', '--version']):
|
||||
with patch("sys.argv", ["pr", "--version"]):
|
||||
with pytest.raises(SystemExit):
|
||||
main()
|
||||
captured = capsys.readouterr()
|
||||
assert 'PR Assistant' in captured.out
|
||||
assert "PR Assistant" in captured.out
|
||||
|
||||
|
||||
def test_main_create_config_success(capsys):
|
||||
with patch('pr.core.config_loader.create_default_config', return_value=True):
|
||||
with patch('sys.argv', ['pr', '--create-config']):
|
||||
with patch("pr.core.config_loader.create_default_config", return_value=True):
|
||||
with patch("sys.argv", ["pr", "--create-config"]):
|
||||
main()
|
||||
captured = capsys.readouterr()
|
||||
assert 'Configuration file created' in captured.out
|
||||
assert "Configuration file created" in captured.out
|
||||
|
||||
|
||||
def test_main_create_config_fail(capsys):
|
||||
with patch('pr.core.config_loader.create_default_config', return_value=False):
|
||||
with patch('sys.argv', ['pr', '--create-config']):
|
||||
with patch("pr.core.config_loader.create_default_config", return_value=False):
|
||||
with patch("sys.argv", ["pr", "--create-config"]):
|
||||
main()
|
||||
captured = capsys.readouterr()
|
||||
assert 'Error creating configuration file' in captured.err
|
||||
assert "Error creating configuration file" in captured.err
|
||||
|
||||
|
||||
def test_main_list_sessions_no_sessions(capsys):
|
||||
with patch('pr.core.session.SessionManager') as mock_sm:
|
||||
with patch("pr.core.session.SessionManager") as mock_sm:
|
||||
mock_instance = mock_sm.return_value
|
||||
mock_instance.list_sessions.return_value = []
|
||||
with patch('sys.argv', ['pr', '--list-sessions']):
|
||||
with patch("sys.argv", ["pr", "--list-sessions"]):
|
||||
main()
|
||||
captured = capsys.readouterr()
|
||||
assert 'No saved sessions found' in captured.out
|
||||
assert "No saved sessions found" in captured.out
|
||||
|
||||
|
||||
def test_main_list_sessions_with_sessions(capsys):
|
||||
sessions = [{'name': 'test', 'created_at': '2023-01-01', 'message_count': 5}]
|
||||
with patch('pr.core.session.SessionManager') as mock_sm:
|
||||
sessions = [{"name": "test", "created_at": "2023-01-01", "message_count": 5}]
|
||||
with patch("pr.core.session.SessionManager") as mock_sm:
|
||||
mock_instance = mock_sm.return_value
|
||||
mock_instance.list_sessions.return_value = sessions
|
||||
with patch('sys.argv', ['pr', '--list-sessions']):
|
||||
with patch("sys.argv", ["pr", "--list-sessions"]):
|
||||
main()
|
||||
captured = capsys.readouterr()
|
||||
assert 'Found 1 saved sessions' in captured.out
|
||||
assert 'test' in captured.out
|
||||
assert "Found 1 saved sessions" in captured.out
|
||||
assert "test" in captured.out
|
||||
|
||||
|
||||
def test_main_delete_session_success(capsys):
|
||||
with patch('pr.core.session.SessionManager') as mock_sm:
|
||||
with patch("pr.core.session.SessionManager") as mock_sm:
|
||||
mock_instance = mock_sm.return_value
|
||||
mock_instance.delete_session.return_value = True
|
||||
with patch('sys.argv', ['pr', '--delete-session', 'test']):
|
||||
with patch("sys.argv", ["pr", "--delete-session", "test"]):
|
||||
main()
|
||||
captured = capsys.readouterr()
|
||||
assert "Session 'test' deleted" in captured.out
|
||||
|
||||
|
||||
def test_main_delete_session_fail(capsys):
|
||||
with patch('pr.core.session.SessionManager') as mock_sm:
|
||||
with patch("pr.core.session.SessionManager") as mock_sm:
|
||||
mock_instance = mock_sm.return_value
|
||||
mock_instance.delete_session.return_value = False
|
||||
with patch('sys.argv', ['pr', '--delete-session', 'test']):
|
||||
with patch("sys.argv", ["pr", "--delete-session", "test"]):
|
||||
main()
|
||||
captured = capsys.readouterr()
|
||||
assert "Error deleting session 'test'" in captured.err
|
||||
|
||||
|
||||
def test_main_export_session_json(capsys):
|
||||
with patch('pr.core.session.SessionManager') as mock_sm:
|
||||
with patch("pr.core.session.SessionManager") as mock_sm:
|
||||
mock_instance = mock_sm.return_value
|
||||
mock_instance.export_session.return_value = True
|
||||
with patch('sys.argv', ['pr', '--export-session', 'test', 'output.json']):
|
||||
with patch("sys.argv", ["pr", "--export-session", "test", "output.json"]):
|
||||
main()
|
||||
captured = capsys.readouterr()
|
||||
assert 'Session exported to output.json' in captured.out
|
||||
assert "Session exported to output.json" in captured.out
|
||||
|
||||
|
||||
def test_main_export_session_md(capsys):
|
||||
with patch('pr.core.session.SessionManager') as mock_sm:
|
||||
with patch("pr.core.session.SessionManager") as mock_sm:
|
||||
mock_instance = mock_sm.return_value
|
||||
mock_instance.export_session.return_value = True
|
||||
with patch('sys.argv', ['pr', '--export-session', 'test', 'output.md']):
|
||||
with patch("sys.argv", ["pr", "--export-session", "test", "output.md"]):
|
||||
main()
|
||||
captured = capsys.readouterr()
|
||||
assert 'Session exported to output.md' in captured.out
|
||||
assert "Session exported to output.md" in captured.out
|
||||
|
||||
|
||||
def test_main_usage(capsys):
|
||||
usage = {'total_requests': 10, 'total_tokens': 1000, 'total_cost': 0.01}
|
||||
with patch('pr.core.usage_tracker.UsageTracker.get_total_usage', return_value=usage):
|
||||
with patch('sys.argv', ['pr', '--usage']):
|
||||
usage = {"total_requests": 10, "total_tokens": 1000, "total_cost": 0.01}
|
||||
with patch(
|
||||
"pr.core.usage_tracker.UsageTracker.get_total_usage", return_value=usage
|
||||
):
|
||||
with patch("sys.argv", ["pr", "--usage"]):
|
||||
main()
|
||||
captured = capsys.readouterr()
|
||||
assert 'Total Usage Statistics' in captured.out
|
||||
assert 'Requests: 10' in captured.out
|
||||
assert "Total Usage Statistics" in captured.out
|
||||
assert "Requests: 10" in captured.out
|
||||
|
||||
|
||||
def test_main_plugins_no_plugins(capsys):
|
||||
with patch('pr.plugins.loader.PluginLoader') as mock_loader:
|
||||
with patch("pr.plugins.loader.PluginLoader") as mock_loader:
|
||||
mock_instance = mock_loader.return_value
|
||||
mock_instance.load_plugins.return_value = None
|
||||
mock_instance.list_loaded_plugins.return_value = []
|
||||
with patch('sys.argv', ['pr', '--plugins']):
|
||||
with patch("sys.argv", ["pr", "--plugins"]):
|
||||
main()
|
||||
captured = capsys.readouterr()
|
||||
assert 'No plugins loaded' in captured.out
|
||||
assert "No plugins loaded" in captured.out
|
||||
|
||||
|
||||
def test_main_plugins_with_plugins(capsys):
|
||||
with patch('pr.plugins.loader.PluginLoader') as mock_loader:
|
||||
with patch("pr.plugins.loader.PluginLoader") as mock_loader:
|
||||
mock_instance = mock_loader.return_value
|
||||
mock_instance.load_plugins.return_value = None
|
||||
mock_instance.list_loaded_plugins.return_value = ['plugin1', 'plugin2']
|
||||
with patch('sys.argv', ['pr', '--plugins']):
|
||||
mock_instance.list_loaded_plugins.return_value = ["plugin1", "plugin2"]
|
||||
with patch("sys.argv", ["pr", "--plugins"]):
|
||||
main()
|
||||
captured = capsys.readouterr()
|
||||
assert 'Loaded 2 plugins' in captured.out
|
||||
assert "Loaded 2 plugins" in captured.out
|
||||
|
||||
|
||||
def test_main_run_assistant():
|
||||
with patch('pr.__main__.Assistant') as mock_assistant:
|
||||
with patch("pr.__main__.Assistant") as mock_assistant:
|
||||
mock_instance = mock_assistant.return_value
|
||||
with patch('sys.argv', ['pr', 'test message']):
|
||||
with patch("sys.argv", ["pr", "test message"]):
|
||||
main()
|
||||
mock_assistant.assert_called_once()
|
||||
mock_instance.run.assert_called_once()
|
||||
mock_instance.run.assert_called_once()
|
||||
|
||||
+42
-27
@@ -1,116 +1,131 @@
|
||||
import pytest
|
||||
import tempfile
|
||||
import os
|
||||
import json
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from pr.core.session import SessionManager
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def temp_sessions_dir(tmp_path, monkeypatch):
|
||||
from pr.core import session
|
||||
|
||||
original_dir = session.SESSIONS_DIR
|
||||
monkeypatch.setattr(session, 'SESSIONS_DIR', str(tmp_path))
|
||||
monkeypatch.setattr(session, "SESSIONS_DIR", str(tmp_path))
|
||||
# Clean any existing files
|
||||
import shutil
|
||||
|
||||
if os.path.exists(str(tmp_path)):
|
||||
shutil.rmtree(str(tmp_path))
|
||||
os.makedirs(str(tmp_path), exist_ok=True)
|
||||
yield tmp_path
|
||||
monkeypatch.setattr(session, 'SESSIONS_DIR', original_dir)
|
||||
monkeypatch.setattr(session, "SESSIONS_DIR", original_dir)
|
||||
|
||||
|
||||
def test_session_manager_init(temp_sessions_dir):
|
||||
manager = SessionManager()
|
||||
SessionManager()
|
||||
assert os.path.exists(temp_sessions_dir)
|
||||
|
||||
|
||||
def test_save_and_load_session(temp_sessions_dir):
|
||||
manager = SessionManager()
|
||||
name = "test_session"
|
||||
messages = [{"role": "user", "content": "Hello"}]
|
||||
metadata = {"test": True}
|
||||
|
||||
|
||||
assert manager.save_session(name, messages, metadata)
|
||||
|
||||
|
||||
loaded = manager.load_session(name)
|
||||
assert loaded is not None
|
||||
assert loaded['name'] == name
|
||||
assert loaded['messages'] == messages
|
||||
assert loaded['metadata'] == metadata
|
||||
assert loaded["name"] == name
|
||||
assert loaded["messages"] == messages
|
||||
assert loaded["metadata"] == metadata
|
||||
|
||||
|
||||
def test_load_nonexistent_session(temp_sessions_dir):
|
||||
manager = SessionManager()
|
||||
loaded = manager.load_session("nonexistent")
|
||||
assert loaded is None
|
||||
|
||||
|
||||
def test_list_sessions(temp_sessions_dir):
|
||||
manager = SessionManager()
|
||||
# Save a session
|
||||
manager.save_session("session1", [{"role": "user", "content": "Hi"}])
|
||||
manager.save_session("session2", [{"role": "user", "content": "Hello"}])
|
||||
|
||||
|
||||
sessions = manager.list_sessions()
|
||||
assert len(sessions) == 2
|
||||
assert sessions[0]['name'] == "session2" # sorted by created_at desc
|
||||
assert sessions[0]["name"] == "session2" # sorted by created_at desc
|
||||
|
||||
|
||||
def test_delete_session(temp_sessions_dir):
|
||||
manager = SessionManager()
|
||||
name = "to_delete"
|
||||
manager.save_session(name, [{"role": "user", "content": "Test"}])
|
||||
|
||||
|
||||
assert manager.delete_session(name)
|
||||
assert manager.load_session(name) is None
|
||||
|
||||
|
||||
def test_delete_nonexistent_session(temp_sessions_dir):
|
||||
manager = SessionManager()
|
||||
assert not manager.delete_session("nonexistent")
|
||||
|
||||
|
||||
def test_export_session_json(temp_sessions_dir, tmp_path):
|
||||
manager = SessionManager()
|
||||
name = "export_test"
|
||||
messages = [{"role": "user", "content": "Export me"}]
|
||||
manager.save_session(name, messages)
|
||||
|
||||
|
||||
output_path = tmp_path / "exported.json"
|
||||
assert manager.export_session(name, str(output_path), 'json')
|
||||
assert manager.export_session(name, str(output_path), "json")
|
||||
assert output_path.exists()
|
||||
|
||||
|
||||
with open(output_path) as f:
|
||||
data = json.load(f)
|
||||
assert data['name'] == name
|
||||
assert data["name"] == name
|
||||
|
||||
|
||||
def test_export_session_markdown(temp_sessions_dir, tmp_path):
|
||||
manager = SessionManager()
|
||||
name = "export_md"
|
||||
messages = [{"role": "user", "content": "Markdown export"}]
|
||||
manager.save_session(name, messages)
|
||||
|
||||
|
||||
output_path = tmp_path / "exported.md"
|
||||
assert manager.export_session(name, str(output_path), 'markdown')
|
||||
assert manager.export_session(name, str(output_path), "markdown")
|
||||
assert output_path.exists()
|
||||
|
||||
|
||||
content = output_path.read_text()
|
||||
assert "# Session: export_md" in content
|
||||
|
||||
|
||||
def test_export_session_txt(temp_sessions_dir, tmp_path):
|
||||
manager = SessionManager()
|
||||
name = "export_txt"
|
||||
messages = [{"role": "user", "content": "Text export"}]
|
||||
manager.save_session(name, messages)
|
||||
|
||||
|
||||
output_path = tmp_path / "exported.txt"
|
||||
assert manager.export_session(name, str(output_path), 'txt')
|
||||
assert manager.export_session(name, str(output_path), "txt")
|
||||
assert output_path.exists()
|
||||
|
||||
|
||||
content = output_path.read_text()
|
||||
assert "Session: export_txt" in content
|
||||
|
||||
|
||||
def test_export_nonexistent_session(temp_sessions_dir, tmp_path):
|
||||
manager = SessionManager()
|
||||
output_path = tmp_path / "nonexistent.json"
|
||||
assert not manager.export_session("nonexistent", str(output_path), 'json')
|
||||
assert not manager.export_session("nonexistent", str(output_path), "json")
|
||||
|
||||
|
||||
def test_export_unsupported_format(temp_sessions_dir, tmp_path):
|
||||
manager = SessionManager()
|
||||
name = "test"
|
||||
manager.save_session(name, [{"role": "user", "content": "Test"}])
|
||||
|
||||
|
||||
output_path = tmp_path / "test.unsupported"
|
||||
assert not manager.export_session(name, str(output_path), 'unsupported')
|
||||
assert not manager.export_session(name, str(output_path), "unsupported")
|
||||
|
||||
+50
-51
@@ -1,69 +1,68 @@
|
||||
import pytest
|
||||
import os
|
||||
import tempfile
|
||||
from pr.tools.filesystem import read_file, write_file, list_directory, search_replace
|
||||
from pr.tools.patch import apply_patch, create_diff
|
||||
|
||||
from pr.tools.base import get_tools_definition
|
||||
from pr.tools.filesystem import list_directory, read_file, search_replace, write_file
|
||||
from pr.tools.patch import apply_patch, create_diff
|
||||
|
||||
|
||||
class TestFilesystemTools:
|
||||
|
||||
def test_write_and_read_file(self, temp_dir):
|
||||
filepath = os.path.join(temp_dir, 'test.txt')
|
||||
content = 'Hello, World!'
|
||||
filepath = os.path.join(temp_dir, "test.txt")
|
||||
content = "Hello, World!"
|
||||
|
||||
write_result = write_file(filepath, content)
|
||||
assert write_result['status'] == 'success'
|
||||
assert write_result["status"] == "success"
|
||||
|
||||
read_result = read_file(filepath)
|
||||
assert read_result['status'] == 'success'
|
||||
assert content in read_result['content']
|
||||
assert read_result["status"] == "success"
|
||||
assert content in read_result["content"]
|
||||
|
||||
def test_read_nonexistent_file(self):
|
||||
result = read_file('/nonexistent/path/file.txt')
|
||||
assert result['status'] == 'error'
|
||||
result = read_file("/nonexistent/path/file.txt")
|
||||
assert result["status"] == "error"
|
||||
|
||||
def test_list_directory(self, temp_dir):
|
||||
test_file = os.path.join(temp_dir, 'testfile.txt')
|
||||
with open(test_file, 'w') as f:
|
||||
f.write('test')
|
||||
test_file = os.path.join(temp_dir, "testfile.txt")
|
||||
with open(test_file, "w") as f:
|
||||
f.write("test")
|
||||
|
||||
result = list_directory(temp_dir)
|
||||
assert result['status'] == 'success'
|
||||
assert any(item['name'] == 'testfile.txt' for item in result['items'])
|
||||
assert result["status"] == "success"
|
||||
assert any(item["name"] == "testfile.txt" for item in result["items"])
|
||||
|
||||
def test_search_replace(self, temp_dir):
|
||||
filepath = os.path.join(temp_dir, 'test.txt')
|
||||
content = 'Hello, World!'
|
||||
with open(filepath, 'w') as f:
|
||||
filepath = os.path.join(temp_dir, "test.txt")
|
||||
content = "Hello, World!"
|
||||
with open(filepath, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
result = search_replace(filepath, 'World', 'Universe')
|
||||
assert result['status'] == 'success'
|
||||
result = search_replace(filepath, "World", "Universe")
|
||||
assert result["status"] == "success"
|
||||
|
||||
read_result = read_file(filepath)
|
||||
assert 'Hello, Universe!' in read_result['content']
|
||||
assert "Hello, Universe!" in read_result["content"]
|
||||
|
||||
|
||||
class TestPatchTools:
|
||||
|
||||
def test_create_diff(self, temp_dir):
|
||||
file1 = os.path.join(temp_dir, 'file1.txt')
|
||||
file2 = os.path.join(temp_dir, 'file2.txt')
|
||||
with open(file1, 'w') as f:
|
||||
f.write('line1\nline2\nline3\n')
|
||||
with open(file2, 'w') as f:
|
||||
f.write('line1\nline2 modified\nline3\n')
|
||||
file1 = os.path.join(temp_dir, "file1.txt")
|
||||
file2 = os.path.join(temp_dir, "file2.txt")
|
||||
with open(file1, "w") as f:
|
||||
f.write("line1\nline2\nline3\n")
|
||||
with open(file2, "w") as f:
|
||||
f.write("line1\nline2 modified\nline3\n")
|
||||
|
||||
result = create_diff(file1, file2)
|
||||
assert result['status'] == 'success'
|
||||
assert 'line2' in result['diff']
|
||||
assert 'line2 modified' in result['diff']
|
||||
assert result["status"] == "success"
|
||||
assert "line2" in result["diff"]
|
||||
assert "line2 modified" in result["diff"]
|
||||
|
||||
def test_apply_patch(self, temp_dir):
|
||||
filepath = os.path.join(temp_dir, 'file.txt')
|
||||
with open(filepath, 'w') as f:
|
||||
f.write('line1\nline2\nline3\n')
|
||||
filepath = os.path.join(temp_dir, "file.txt")
|
||||
with open(filepath, "w") as f:
|
||||
f.write("line1\nline2\nline3\n")
|
||||
|
||||
# Create a simple patch
|
||||
patch_content = """--- a/file.txt
|
||||
@@ -75,10 +74,10 @@ class TestPatchTools:
|
||||
line3
|
||||
"""
|
||||
result = apply_patch(filepath, patch_content)
|
||||
assert result['status'] == 'success'
|
||||
assert result["status"] == "success"
|
||||
|
||||
read_result = read_file(filepath)
|
||||
assert 'line2 modified' in read_result['content']
|
||||
assert "line2 modified" in read_result["content"]
|
||||
|
||||
|
||||
class TestToolDefinitions:
|
||||
@@ -92,27 +91,27 @@ class TestToolDefinitions:
|
||||
tools = get_tools_definition()
|
||||
|
||||
for tool in tools:
|
||||
assert 'type' in tool
|
||||
assert tool['type'] == 'function'
|
||||
assert 'function' in tool
|
||||
assert "type" in tool
|
||||
assert tool["type"] == "function"
|
||||
assert "function" in tool
|
||||
|
||||
func = tool['function']
|
||||
assert 'name' in func
|
||||
assert 'description' in func
|
||||
assert 'parameters' in func
|
||||
func = tool["function"]
|
||||
assert "name" in func
|
||||
assert "description" in func
|
||||
assert "parameters" in func
|
||||
|
||||
def test_filesystem_tools_present(self):
|
||||
tools = get_tools_definition()
|
||||
tool_names = [t['function']['name'] for t in tools]
|
||||
tool_names = [t["function"]["name"] for t in tools]
|
||||
|
||||
assert 'read_file' in tool_names
|
||||
assert 'write_file' in tool_names
|
||||
assert 'list_directory' in tool_names
|
||||
assert 'search_replace' in tool_names
|
||||
assert "read_file" in tool_names
|
||||
assert "write_file" in tool_names
|
||||
assert "list_directory" in tool_names
|
||||
assert "search_replace" in tool_names
|
||||
|
||||
def test_patch_tools_present(self):
|
||||
tools = get_tools_definition()
|
||||
tool_names = [t['function']['name'] for t in tools]
|
||||
tool_names = [t["function"]["name"] for t in tools]
|
||||
|
||||
assert 'apply_patch' in tool_names
|
||||
assert 'create_diff' in tool_names
|
||||
assert "apply_patch" in tool_names
|
||||
assert "create_diff" in tool_names
|
||||
|
||||
+64
-40
@@ -1,86 +1,110 @@
|
||||
import pytest
|
||||
import tempfile
|
||||
import os
|
||||
import json
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from pr.core.usage_tracker import UsageTracker
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def temp_usage_file(tmp_path, monkeypatch):
|
||||
from pr.core import usage_tracker
|
||||
|
||||
original_file = usage_tracker.USAGE_DB_FILE
|
||||
temp_file = str(tmp_path / "usage.json")
|
||||
monkeypatch.setattr(usage_tracker, 'USAGE_DB_FILE', temp_file)
|
||||
monkeypatch.setattr(usage_tracker, "USAGE_DB_FILE", temp_file)
|
||||
yield temp_file
|
||||
if os.path.exists(temp_file):
|
||||
os.remove(temp_file)
|
||||
monkeypatch.setattr(usage_tracker, 'USAGE_DB_FILE', original_file)
|
||||
monkeypatch.setattr(usage_tracker, "USAGE_DB_FILE", original_file)
|
||||
|
||||
|
||||
def test_usage_tracker_init():
|
||||
tracker = UsageTracker()
|
||||
summary = tracker.get_session_summary()
|
||||
assert summary['requests'] == 0
|
||||
assert summary['total_tokens'] == 0
|
||||
assert summary['estimated_cost'] == 0.0
|
||||
assert summary["requests"] == 0
|
||||
assert summary["total_tokens"] == 0
|
||||
assert summary["estimated_cost"] == 0.0
|
||||
|
||||
|
||||
def test_track_request_known_model():
|
||||
tracker = UsageTracker()
|
||||
tracker.track_request('gpt-3.5-turbo', 100, 50)
|
||||
|
||||
tracker.track_request("gpt-3.5-turbo", 100, 50)
|
||||
|
||||
summary = tracker.get_session_summary()
|
||||
assert summary['requests'] == 1
|
||||
assert summary['input_tokens'] == 100
|
||||
assert summary['output_tokens'] == 50
|
||||
assert summary['total_tokens'] == 150
|
||||
assert 'gpt-3.5-turbo' in summary['models_used']
|
||||
assert summary["requests"] == 1
|
||||
assert summary["input_tokens"] == 100
|
||||
assert summary["output_tokens"] == 50
|
||||
assert summary["total_tokens"] == 150
|
||||
assert "gpt-3.5-turbo" in summary["models_used"]
|
||||
# Cost: (100/1000)*0.0005 + (50/1000)*0.0015 = 0.00005 + 0.000075 = 0.000125
|
||||
assert abs(summary['estimated_cost'] - 0.000125) < 1e-6
|
||||
assert abs(summary["estimated_cost"] - 0.000125) < 1e-6
|
||||
|
||||
|
||||
def test_track_request_unknown_model():
|
||||
tracker = UsageTracker()
|
||||
tracker.track_request('unknown-model', 100, 50)
|
||||
|
||||
tracker.track_request("unknown-model", 100, 50)
|
||||
|
||||
summary = tracker.get_session_summary()
|
||||
assert summary['requests'] == 1
|
||||
assert summary['estimated_cost'] == 0.0 # Unknown model, cost 0
|
||||
assert summary["requests"] == 1
|
||||
assert summary["estimated_cost"] == 0.0 # Unknown model, cost 0
|
||||
|
||||
|
||||
def test_track_request_multiple():
|
||||
tracker = UsageTracker()
|
||||
tracker.track_request('gpt-3.5-turbo', 100, 50)
|
||||
tracker.track_request('gpt-4', 200, 100)
|
||||
|
||||
tracker.track_request("gpt-3.5-turbo", 100, 50)
|
||||
tracker.track_request("gpt-4", 200, 100)
|
||||
|
||||
summary = tracker.get_session_summary()
|
||||
assert summary['requests'] == 2
|
||||
assert summary['input_tokens'] == 300
|
||||
assert summary['output_tokens'] == 150
|
||||
assert summary['total_tokens'] == 450
|
||||
assert len(summary['models_used']) == 2
|
||||
assert summary["requests"] == 2
|
||||
assert summary["input_tokens"] == 300
|
||||
assert summary["output_tokens"] == 150
|
||||
assert summary["total_tokens"] == 450
|
||||
assert len(summary["models_used"]) == 2
|
||||
|
||||
|
||||
def test_get_formatted_summary():
|
||||
tracker = UsageTracker()
|
||||
tracker.track_request('gpt-3.5-turbo', 100, 50)
|
||||
|
||||
tracker.track_request("gpt-3.5-turbo", 100, 50)
|
||||
|
||||
formatted = tracker.get_formatted_summary()
|
||||
assert "Total Requests: 1" in formatted
|
||||
assert "Total Tokens: 150" in formatted
|
||||
assert "Estimated Cost: $0.0001" in formatted
|
||||
assert "gpt-3.5-turbo" in formatted
|
||||
|
||||
|
||||
def test_get_total_usage_no_file(temp_usage_file):
|
||||
total = UsageTracker.get_total_usage()
|
||||
assert total['total_requests'] == 0
|
||||
assert total['total_tokens'] == 0
|
||||
assert total['total_cost'] == 0.0
|
||||
assert total["total_requests"] == 0
|
||||
assert total["total_tokens"] == 0
|
||||
assert total["total_cost"] == 0.0
|
||||
|
||||
|
||||
def test_get_total_usage_with_data(temp_usage_file):
|
||||
# Manually create history file
|
||||
history = [
|
||||
{'timestamp': '2023-01-01', 'model': 'gpt-3.5-turbo', 'input_tokens': 100, 'output_tokens': 50, 'total_tokens': 150, 'cost': 0.000125},
|
||||
{'timestamp': '2023-01-02', 'model': 'gpt-4', 'input_tokens': 200, 'output_tokens': 100, 'total_tokens': 300, 'cost': 0.008}
|
||||
{
|
||||
"timestamp": "2023-01-01",
|
||||
"model": "gpt-3.5-turbo",
|
||||
"input_tokens": 100,
|
||||
"output_tokens": 50,
|
||||
"total_tokens": 150,
|
||||
"cost": 0.000125,
|
||||
},
|
||||
{
|
||||
"timestamp": "2023-01-02",
|
||||
"model": "gpt-4",
|
||||
"input_tokens": 200,
|
||||
"output_tokens": 100,
|
||||
"total_tokens": 300,
|
||||
"cost": 0.008,
|
||||
},
|
||||
]
|
||||
with open(temp_usage_file, 'w') as f:
|
||||
with open(temp_usage_file, "w") as f:
|
||||
json.dump(history, f)
|
||||
|
||||
|
||||
total = UsageTracker.get_total_usage()
|
||||
assert total['total_requests'] == 2
|
||||
assert total['total_tokens'] == 450
|
||||
assert abs(total['total_cost'] - 0.008125) < 1e-6
|
||||
assert total["total_requests"] == 2
|
||||
assert total["total_tokens"] == 450
|
||||
assert abs(total["total_cost"] - 0.008125) < 1e-6
|
||||
|
||||
@@ -1,49 +1,59 @@
|
||||
import pytest
|
||||
import tempfile
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
|
||||
from pr.core.exceptions import ValidationError
|
||||
from pr.core.validation import (
|
||||
validate_file_path,
|
||||
validate_directory_path,
|
||||
validate_model_name,
|
||||
validate_api_url,
|
||||
validate_directory_path,
|
||||
validate_file_path,
|
||||
validate_max_tokens,
|
||||
validate_model_name,
|
||||
validate_session_name,
|
||||
validate_temperature,
|
||||
validate_max_tokens,
|
||||
)
|
||||
from pr.core.exceptions import ValidationError
|
||||
|
||||
|
||||
def test_validate_file_path_empty():
|
||||
with pytest.raises(ValidationError, match="File path cannot be empty"):
|
||||
validate_file_path("")
|
||||
|
||||
|
||||
def test_validate_file_path_not_exist():
|
||||
with pytest.raises(ValidationError, match="File does not exist"):
|
||||
validate_file_path("/nonexistent/file.txt", must_exist=True)
|
||||
|
||||
|
||||
def test_validate_file_path_is_dir():
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
with pytest.raises(ValidationError, match="Path is a directory"):
|
||||
validate_file_path(tmpdir, must_exist=True)
|
||||
|
||||
|
||||
def test_validate_file_path_valid():
|
||||
with tempfile.NamedTemporaryFile() as tmpfile:
|
||||
result = validate_file_path(tmpfile.name, must_exist=True)
|
||||
assert os.path.isabs(result)
|
||||
assert result == os.path.abspath(tmpfile.name)
|
||||
|
||||
|
||||
def test_validate_directory_path_empty():
|
||||
with pytest.raises(ValidationError, match="Directory path cannot be empty"):
|
||||
validate_directory_path("")
|
||||
|
||||
|
||||
def test_validate_directory_path_not_exist():
|
||||
with pytest.raises(ValidationError, match="Directory does not exist"):
|
||||
validate_directory_path("/nonexistent/dir", must_exist=True)
|
||||
|
||||
|
||||
def test_validate_directory_path_not_dir():
|
||||
with tempfile.NamedTemporaryFile() as tmpfile:
|
||||
with pytest.raises(ValidationError, match="Path is not a directory"):
|
||||
validate_directory_path(tmpfile.name, must_exist=True)
|
||||
|
||||
|
||||
def test_validate_directory_path_create():
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
new_dir = os.path.join(tmpdir, "new_dir")
|
||||
@@ -51,72 +61,89 @@ def test_validate_directory_path_create():
|
||||
assert os.path.isdir(new_dir)
|
||||
assert result == os.path.abspath(new_dir)
|
||||
|
||||
|
||||
def test_validate_directory_path_valid():
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
result = validate_directory_path(tmpdir, must_exist=True)
|
||||
assert result == os.path.abspath(tmpdir)
|
||||
|
||||
|
||||
def test_validate_model_name_empty():
|
||||
with pytest.raises(ValidationError, match="Model name cannot be empty"):
|
||||
validate_model_name("")
|
||||
|
||||
|
||||
def test_validate_model_name_too_short():
|
||||
with pytest.raises(ValidationError, match="Model name too short"):
|
||||
validate_model_name("a")
|
||||
|
||||
|
||||
def test_validate_model_name_valid():
|
||||
result = validate_model_name("gpt-3.5-turbo")
|
||||
assert result == "gpt-3.5-turbo"
|
||||
|
||||
|
||||
def test_validate_api_url_empty():
|
||||
with pytest.raises(ValidationError, match="API URL cannot be empty"):
|
||||
validate_api_url("")
|
||||
|
||||
|
||||
def test_validate_api_url_invalid():
|
||||
with pytest.raises(ValidationError, match="API URL must start with"):
|
||||
validate_api_url("invalid-url")
|
||||
|
||||
|
||||
def test_validate_api_url_valid():
|
||||
result = validate_api_url("https://api.example.com")
|
||||
assert result == "https://api.example.com"
|
||||
|
||||
|
||||
def test_validate_session_name_empty():
|
||||
with pytest.raises(ValidationError, match="Session name cannot be empty"):
|
||||
validate_session_name("")
|
||||
|
||||
|
||||
def test_validate_session_name_invalid_char():
|
||||
with pytest.raises(ValidationError, match="contains invalid character"):
|
||||
validate_session_name("test/session")
|
||||
|
||||
|
||||
def test_validate_session_name_too_long():
|
||||
long_name = "a" * 256
|
||||
with pytest.raises(ValidationError, match="Session name too long"):
|
||||
validate_session_name(long_name)
|
||||
|
||||
|
||||
def test_validate_session_name_valid():
|
||||
result = validate_session_name("valid_session_123")
|
||||
assert result == "valid_session_123"
|
||||
|
||||
|
||||
def test_validate_temperature_too_low():
|
||||
with pytest.raises(ValidationError, match="Temperature must be between"):
|
||||
validate_temperature(-0.1)
|
||||
|
||||
|
||||
def test_validate_temperature_too_high():
|
||||
with pytest.raises(ValidationError, match="Temperature must be between"):
|
||||
validate_temperature(2.1)
|
||||
|
||||
|
||||
def test_validate_temperature_valid():
|
||||
result = validate_temperature(0.7)
|
||||
assert result == 0.7
|
||||
|
||||
|
||||
def test_validate_max_tokens_too_low():
|
||||
with pytest.raises(ValidationError, match="Max tokens must be at least 1"):
|
||||
validate_max_tokens(0)
|
||||
|
||||
|
||||
def test_validate_max_tokens_too_high():
|
||||
with pytest.raises(ValidationError, match="Max tokens too high"):
|
||||
validate_max_tokens(100001)
|
||||
|
||||
|
||||
def test_validate_max_tokens_valid():
|
||||
result = validate_max_tokens(1000)
|
||||
assert result == 1000
|
||||
|
||||
Reference in New Issue
Block a user