feat: Add intelligent auto-router and enhanced integrations
- Add intelligent-router.sh hook for automatic agent routing - Add AUTO-TRIGGER-SUMMARY.md documentation - Add FINAL-INTEGRATION-SUMMARY.md documentation - Complete Prometheus integration (6 commands + 4 tools) - Complete Dexto integration (12 commands + 5 tools) - Enhanced Ralph with access to all agents - Fix /clawd command (removed disable-model-invocation) - Update hooks.json to v5 with intelligent routing - 291 total skills now available - All 21 commands with automatic routing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
391
prometheus/tests/utils/test_memory_utils.py
Normal file
391
prometheus/tests/utils/test_memory_utils.py
Normal file
@@ -0,0 +1,391 @@
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
|
||||
from prometheus.models.context import Context
|
||||
from prometheus.models.query import Query
|
||||
from prometheus.utils.memory_utils import AthenaMemoryClient
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def athena_client():
|
||||
"""Create an AthenaMemoryClient instance for testing."""
|
||||
return AthenaMemoryClient(base_url="http://test-athena-service.com")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_contexts():
|
||||
"""Create sample Context objects for testing."""
|
||||
return [
|
||||
Context(
|
||||
relative_path="src/main.py",
|
||||
content="def main():\n print('Hello')",
|
||||
start_line_number=1,
|
||||
end_line_number=2,
|
||||
),
|
||||
Context(
|
||||
relative_path="src/utils.py",
|
||||
content="def helper():\n return True",
|
||||
start_line_number=10,
|
||||
end_line_number=11,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_query():
|
||||
"""Create a sample Query object for testing."""
|
||||
return Query(
|
||||
essential_query="How to implement authentication?",
|
||||
extra_requirements="Using JWT tokens",
|
||||
purpose="Feature implementation",
|
||||
)
|
||||
|
||||
|
||||
class TestAthenaMemoryClient:
|
||||
"""Test suite for AthenaMemoryClient class."""
|
||||
|
||||
def test_init(self):
|
||||
"""Test AthenaMemoryClient initialization."""
|
||||
client = AthenaMemoryClient(base_url="http://example.com/")
|
||||
assert client.base_url == "http://example.com"
|
||||
assert client.timeout == 30
|
||||
|
||||
def test_init_strips_trailing_slash(self):
|
||||
"""Test that trailing slashes are removed from base_url."""
|
||||
client = AthenaMemoryClient(base_url="http://example.com///")
|
||||
assert client.base_url == "http://example.com"
|
||||
|
||||
@patch("prometheus.utils.memory_utils.requests.post")
|
||||
def test_store_memory_success(self, mock_post, athena_client, sample_contexts):
|
||||
"""Test successful memory storage."""
|
||||
# Setup mock response
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {"status": "success", "memory_id": "123"}
|
||||
mock_post.return_value = mock_response
|
||||
|
||||
# Call the method
|
||||
result = athena_client.store_memory(
|
||||
repository_id=42,
|
||||
essential_query="How to use async?",
|
||||
extra_requirements="Python 3.11",
|
||||
purpose="Learning",
|
||||
contexts=sample_contexts,
|
||||
)
|
||||
|
||||
# Assertions
|
||||
assert result == {"status": "success", "memory_id": "123"}
|
||||
mock_post.assert_called_once()
|
||||
|
||||
# Verify the call arguments
|
||||
call_args = mock_post.call_args
|
||||
assert call_args[0][0] == "http://test-athena-service.com/semantic-memory/store/"
|
||||
assert call_args[1]["timeout"] == 30
|
||||
|
||||
# Verify payload structure
|
||||
payload = call_args[1]["json"]
|
||||
assert payload["repository_id"] == 42
|
||||
assert payload["query"]["essential_query"] == "How to use async?"
|
||||
assert payload["query"]["extra_requirements"] == "Python 3.11"
|
||||
assert payload["query"]["purpose"] == "Learning"
|
||||
assert len(payload["contexts"]) == 2
|
||||
assert payload["contexts"][0]["relative_path"] == "src/main.py"
|
||||
|
||||
@patch("prometheus.utils.memory_utils.requests.post")
|
||||
def test_store_memory_request_exception(self, mock_post, athena_client, sample_contexts):
|
||||
"""Test store_memory raises RequestException on failure."""
|
||||
# Setup mock to raise an exception
|
||||
mock_post.side_effect = requests.RequestException("Connection error")
|
||||
|
||||
# Verify exception is raised
|
||||
with pytest.raises(requests.RequestException) as exc_info:
|
||||
athena_client.store_memory(
|
||||
repository_id=42,
|
||||
essential_query="test query",
|
||||
extra_requirements="",
|
||||
purpose="",
|
||||
contexts=sample_contexts,
|
||||
)
|
||||
|
||||
assert "Connection error" in str(exc_info.value)
|
||||
|
||||
@patch("prometheus.utils.memory_utils.requests.post")
|
||||
def test_store_memory_http_error(self, mock_post, athena_client, sample_contexts):
|
||||
"""Test store_memory handles HTTP errors."""
|
||||
# Setup mock response with error status
|
||||
mock_response = MagicMock()
|
||||
mock_response.raise_for_status.side_effect = requests.HTTPError("404 Not Found")
|
||||
mock_post.return_value = mock_response
|
||||
|
||||
# Verify exception is raised
|
||||
with pytest.raises(requests.HTTPError):
|
||||
athena_client.store_memory(
|
||||
repository_id=42,
|
||||
essential_query="test query",
|
||||
extra_requirements="",
|
||||
purpose="",
|
||||
contexts=sample_contexts,
|
||||
)
|
||||
|
||||
@patch("prometheus.utils.memory_utils.requests.get")
|
||||
def test_retrieve_memory_success(self, mock_get, athena_client, sample_query):
|
||||
"""Test successful memory retrieval."""
|
||||
# Setup mock response
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {
|
||||
"data": [
|
||||
{"content": "Authentication code snippet 1"},
|
||||
{"content": "Authentication code snippet 2"},
|
||||
]
|
||||
}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
# Call the method
|
||||
result = athena_client.retrieve_memory(repository_id=42, query=sample_query)
|
||||
|
||||
# Assertions
|
||||
assert len(result) == 2
|
||||
assert result[0]["content"] == "Authentication code snippet 1"
|
||||
mock_get.assert_called_once()
|
||||
|
||||
# Verify the call arguments
|
||||
call_args = mock_get.call_args
|
||||
assert call_args[0][0] == "http://test-athena-service.com/semantic-memory/retrieve/42/"
|
||||
assert call_args[1]["timeout"] == 30
|
||||
|
||||
# Verify query parameters
|
||||
params = call_args[1]["params"]
|
||||
assert params["essential_query"] == "How to implement authentication?"
|
||||
assert params["extra_requirements"] == "Using JWT tokens"
|
||||
assert params["purpose"] == "Feature implementation"
|
||||
|
||||
@patch("prometheus.utils.memory_utils.requests.get")
|
||||
def test_retrieve_memory_with_optional_fields(self, mock_get, athena_client):
|
||||
"""Test memory retrieval with empty optional query fields."""
|
||||
# Setup mock response
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {"data": []}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
# Create query with empty optional fields
|
||||
query = Query(essential_query="test query", extra_requirements="", purpose="")
|
||||
|
||||
# Call the method
|
||||
result = athena_client.retrieve_memory(repository_id=42, query=query)
|
||||
|
||||
# Verify empty strings are passed correctly
|
||||
call_args = mock_get.call_args
|
||||
params = call_args[1]["params"]
|
||||
assert params["extra_requirements"] == ""
|
||||
assert params["purpose"] == ""
|
||||
assert result == []
|
||||
|
||||
@patch("prometheus.utils.memory_utils.requests.get")
|
||||
def test_retrieve_memory_request_exception(self, mock_get, athena_client, sample_query):
|
||||
"""Test retrieve_memory raises RequestException on failure."""
|
||||
# Setup mock to raise an exception
|
||||
mock_get.side_effect = requests.RequestException("Timeout error")
|
||||
|
||||
# Verify exception is raised
|
||||
with pytest.raises(requests.RequestException) as exc_info:
|
||||
athena_client.retrieve_memory(repository_id=42, query=sample_query)
|
||||
|
||||
assert "Timeout error" in str(exc_info.value)
|
||||
|
||||
@patch("prometheus.utils.memory_utils.requests.get")
|
||||
def test_retrieve_memory_http_error(self, mock_get, athena_client, sample_query):
|
||||
"""Test retrieve_memory handles HTTP errors."""
|
||||
# Setup mock response with error status
|
||||
mock_response = MagicMock()
|
||||
mock_response.raise_for_status.side_effect = requests.HTTPError("500 Server Error")
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
# Verify exception is raised
|
||||
with pytest.raises(requests.HTTPError):
|
||||
athena_client.retrieve_memory(repository_id=42, query=sample_query)
|
||||
|
||||
@patch("prometheus.utils.memory_utils.requests.delete")
|
||||
def test_delete_repository_memory_success(self, mock_delete, athena_client):
|
||||
"""Test successful repository memory deletion."""
|
||||
# Setup mock response
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {"status": "success", "deleted_count": 15}
|
||||
mock_delete.return_value = mock_response
|
||||
|
||||
# Call the method
|
||||
result = athena_client.delete_repository_memory(repository_id=42)
|
||||
|
||||
# Assertions
|
||||
assert result == {"status": "success", "deleted_count": 15}
|
||||
mock_delete.assert_called_once()
|
||||
|
||||
# Verify the call arguments
|
||||
call_args = mock_delete.call_args
|
||||
assert call_args[0][0] == "http://test-athena-service.com/semantic-memory/42/"
|
||||
assert call_args[1]["timeout"] == 30
|
||||
|
||||
@patch("prometheus.utils.memory_utils.requests.delete")
|
||||
def test_delete_repository_memory_request_exception(self, mock_delete, athena_client):
|
||||
"""Test delete_repository_memory raises RequestException on failure."""
|
||||
# Setup mock to raise an exception
|
||||
mock_delete.side_effect = requests.RequestException("Network error")
|
||||
|
||||
# Verify exception is raised
|
||||
with pytest.raises(requests.RequestException) as exc_info:
|
||||
athena_client.delete_repository_memory(repository_id=42)
|
||||
|
||||
assert "Network error" in str(exc_info.value)
|
||||
|
||||
@patch("prometheus.utils.memory_utils.requests.delete")
|
||||
def test_delete_repository_memory_http_error(self, mock_delete, athena_client):
|
||||
"""Test delete_repository_memory handles HTTP errors."""
|
||||
# Setup mock response with error status
|
||||
mock_response = MagicMock()
|
||||
mock_response.raise_for_status.side_effect = requests.HTTPError("403 Forbidden")
|
||||
mock_delete.return_value = mock_response
|
||||
|
||||
# Verify exception is raised
|
||||
with pytest.raises(requests.HTTPError):
|
||||
athena_client.delete_repository_memory(repository_id=42)
|
||||
|
||||
|
||||
class TestModuleLevelFunctions:
|
||||
"""Test suite for module-level convenience functions."""
|
||||
|
||||
@patch("prometheus.utils.memory_utils.athena_client.store_memory")
|
||||
def test_store_memory_function(self, mock_store, sample_contexts):
|
||||
"""Test module-level store_memory function."""
|
||||
from prometheus.utils.memory_utils import store_memory
|
||||
|
||||
# Setup mock
|
||||
mock_store.return_value = {"status": "success"}
|
||||
|
||||
# Call the function
|
||||
result = store_memory(
|
||||
repository_id=123,
|
||||
essential_query="test query",
|
||||
extra_requirements="requirements",
|
||||
purpose="testing",
|
||||
contexts=sample_contexts,
|
||||
)
|
||||
|
||||
# Verify it delegates to the client
|
||||
assert result == {"status": "success"}
|
||||
mock_store.assert_called_once_with(
|
||||
repository_id=123,
|
||||
essential_query="test query",
|
||||
extra_requirements="requirements",
|
||||
purpose="testing",
|
||||
contexts=sample_contexts,
|
||||
)
|
||||
|
||||
@patch("prometheus.utils.memory_utils.athena_client.retrieve_memory")
|
||||
def test_retrieve_memory_function(self, mock_retrieve, sample_query):
|
||||
"""Test module-level retrieve_memory function."""
|
||||
from prometheus.utils.memory_utils import retrieve_memory
|
||||
|
||||
# Setup mock
|
||||
mock_retrieve.return_value = [{"content": "test"}]
|
||||
|
||||
# Call the function
|
||||
result = retrieve_memory(repository_id=123, query=sample_query)
|
||||
|
||||
# Verify it delegates to the client
|
||||
assert result == [{"content": "test"}]
|
||||
mock_retrieve.assert_called_once_with(repository_id=123, query=sample_query)
|
||||
|
||||
@patch("prometheus.utils.memory_utils.athena_client.delete_repository_memory")
|
||||
def test_delete_repository_memory_function(self, mock_delete):
|
||||
"""Test module-level delete_repository_memory function."""
|
||||
from prometheus.utils.memory_utils import delete_repository_memory
|
||||
|
||||
# Setup mock
|
||||
mock_delete.return_value = {"status": "deleted"}
|
||||
|
||||
# Call the function
|
||||
result = delete_repository_memory(repository_id=123)
|
||||
|
||||
# Verify it delegates to the client
|
||||
assert result == {"status": "deleted"}
|
||||
mock_delete.assert_called_once_with(repository_id=123)
|
||||
|
||||
|
||||
class TestEdgeCases:
|
||||
"""Test edge cases and boundary conditions."""
|
||||
|
||||
@patch("prometheus.utils.memory_utils.requests.post")
|
||||
def test_store_memory_with_empty_contexts_list(self, mock_post, athena_client):
|
||||
"""Test storing memory with empty contexts list."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {"status": "success"}
|
||||
mock_post.return_value = mock_response
|
||||
|
||||
result = athena_client.store_memory(
|
||||
repository_id=1,
|
||||
essential_query="test",
|
||||
extra_requirements="",
|
||||
purpose="",
|
||||
contexts=[],
|
||||
)
|
||||
|
||||
# Verify empty list is handled
|
||||
payload = mock_post.call_args[1]["json"]
|
||||
assert payload["contexts"] == []
|
||||
assert result == {"status": "success"}
|
||||
|
||||
@patch("prometheus.utils.memory_utils.requests.get")
|
||||
def test_retrieve_memory_empty_results(self, mock_get, athena_client):
|
||||
"""Test retrieving memory when no results are found."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {"data": []}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
query = Query(
|
||||
essential_query="nonexistent query",
|
||||
extra_requirements="",
|
||||
purpose="",
|
||||
)
|
||||
|
||||
result = athena_client.retrieve_memory(repository_id=999, query=query)
|
||||
|
||||
assert result == []
|
||||
|
||||
def test_context_serialization(self, sample_contexts):
|
||||
"""Test that Context objects are properly serialized."""
|
||||
context = sample_contexts[0]
|
||||
serialized = context.model_dump()
|
||||
|
||||
assert serialized["relative_path"] == "src/main.py"
|
||||
assert serialized["content"] == "def main():\n print('Hello')"
|
||||
assert serialized["start_line_number"] == 1
|
||||
assert serialized["end_line_number"] == 2
|
||||
|
||||
@patch("prometheus.utils.memory_utils.requests.post")
|
||||
def test_store_memory_with_special_characters(self, mock_post, athena_client):
|
||||
"""Test storing memory with special characters in content."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {"status": "success"}
|
||||
mock_post.return_value = mock_response
|
||||
|
||||
special_context = Context(
|
||||
relative_path="test/file.py",
|
||||
content="def test():\n # Special chars: @#$%^&*(){}[]|\\<>?~`",
|
||||
start_line_number=1,
|
||||
end_line_number=2,
|
||||
)
|
||||
|
||||
result = athena_client.store_memory(
|
||||
repository_id=1,
|
||||
essential_query="test with special chars: @#$%",
|
||||
extra_requirements="requirements with 中文",
|
||||
purpose="purpose with émojis 🚀",
|
||||
contexts=[special_context],
|
||||
)
|
||||
|
||||
assert result == {"status": "success"}
|
||||
# Verify special characters are preserved in the payload
|
||||
payload = mock_post.call_args[1]["json"]
|
||||
assert "@#$%" in payload["query"]["essential_query"]
|
||||
assert "中文" in payload["query"]["extra_requirements"]
|
||||
assert "🚀" in payload["query"]["purpose"]
|
||||
Reference in New Issue
Block a user