Initial commit.

This commit is contained in:
2025-08-21 00:34:47 +02:00
commit d6b45d662d
40 changed files with 4396 additions and 0 deletions
View File
+69
View File
@@ -0,0 +1,69 @@
import asyncio
import pytest
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock
import tempfile
from pyr.core.config import PyrConfig
from pyr.core.app import PyrApp
from pyr.ai.client import BaseAIClient
from pyr.storage.database import DatabaseManager
@pytest.fixture
def temp_dir():
with tempfile.TemporaryDirectory() as td:
yield Path(td)
@pytest.fixture
def test_config(temp_dir):
return PyrConfig(
provider="openai",
model="gpt-3.5-turbo",
api_key="test-key",
base_url="https://api.openai.com",
db_path=str(temp_dir / "test.db"),
cache_dir=str(temp_dir / "cache"),
verbose=True,
syntax_highlight=True,
use_tools=True,
)
@pytest.fixture
def mock_ai_client():
client = AsyncMock(spec=BaseAIClient)
client.chat = AsyncMock(return_value="Test response")
client.chat_with_tools = AsyncMock(return_value=MagicMock(content="Tool response"))
client.close = AsyncMock()
client.add_system_message = AsyncMock()
client.add_user_message = AsyncMock()
client.add_assistant_message = AsyncMock()
return client
@pytest.fixture
async def test_app(test_config, mock_ai_client):
app = PyrApp(test_config)
app.ai_client = mock_ai_client
await app.startup()
yield app
await app.shutdown()
@pytest.fixture
async def test_db(temp_dir):
db_path = temp_dir / "test.db"
db = DatabaseManager(str(db_path))
await db.initialize()
yield db
await db.close()
@pytest.fixture(scope="session")
def event_loop():
loop = asyncio.new_event_loop()
yield loop
loop.close()
View File
+50
View File
@@ -0,0 +1,50 @@
import pytest
import os
from pyr.core.config import PyrConfig, AIProvider
def test_config_creation():
config = PyrConfig()
assert config.provider == AIProvider.OPENAI
assert config.model == "gpt-4o-mini"
assert config.verbose is True
def test_config_with_overrides():
config = PyrConfig(
provider=AIProvider.ANTHROPIC,
model="claude-3-5-haiku-20241022",
temperature=0.5
)
assert config.provider == AIProvider.ANTHROPIC
assert config.model == "claude-3-5-haiku-20241022"
assert config.temperature == 0.5
def test_config_urls():
config = PyrConfig(provider=AIProvider.OPENAI)
assert "openai.com" in config.get_completions_url()
assert "openai.com" in config.get_models_url()
def test_config_headers():
config = PyrConfig(api_key="test-key", provider=AIProvider.OPENAI)
headers = config.get_auth_headers()
assert "Authorization" in headers
assert headers["Authorization"] == "Bearer test-key"
def test_anthropic_headers():
config = PyrConfig(api_key="test-key", provider=AIProvider.ANTHROPIC)
headers = config.get_auth_headers()
assert "x-api-key" in headers
assert headers["x-api-key"] == "test-key"
def test_env_var_override(monkeypatch):
monkeypatch.setenv("R_MODEL", "test-model")
monkeypatch.setenv("R_PROVIDER", "anthropic")
config = PyrConfig()
assert config.model == "test-model"
assert config.provider == AIProvider.ANTHROPIC
View File
+77
View File
@@ -0,0 +1,77 @@
import pytest
from pathlib import Path
from pyr.tools.file_ops import ReadFileTool, WriteFileTool, DirectoryGlobTool, MkdirTool
@pytest.mark.asyncio
async def test_write_and_read_file(temp_dir):
write_tool = WriteFileTool()
read_tool = ReadFileTool()
test_file = temp_dir / "test.txt"
test_content = "Hello, PYR!"
result = await write_tool.execute(str(test_file), test_content)
assert "successfully" in result.lower()
result = await read_tool.execute(str(test_file))
assert test_content in result
@pytest.mark.asyncio
async def test_write_file_append(temp_dir):
write_tool = WriteFileTool()
read_tool = ReadFileTool()
test_file = temp_dir / "append_test.txt"
await write_tool.execute(str(test_file), "Line 1\n")
await write_tool.execute(str(test_file), "Line 2\n", append=True)
result = await read_tool.execute(str(test_file))
assert "Line 1" in result
assert "Line 2" in result
@pytest.mark.asyncio
async def test_directory_glob(temp_dir):
glob_tool = DirectoryGlobTool()
(temp_dir / "test1.txt").write_text("content1")
(temp_dir / "test2.txt").write_text("content2")
(temp_dir / "other.log").write_text("log content")
result = await glob_tool.execute(f"{temp_dir}/*.txt")
assert "test1.txt" in result
assert "test2.txt" in result
assert "other.log" not in result
@pytest.mark.asyncio
async def test_mkdir_tool(temp_dir):
mkdir_tool = MkdirTool()
new_dir = temp_dir / "new_directory" / "nested"
result = await mkdir_tool.execute(str(new_dir))
assert "created" in result.lower()
assert new_dir.exists()
assert new_dir.is_dir()
@pytest.mark.asyncio
async def test_read_nonexistent_file():
read_tool = ReadFileTool()
result = await read_tool.execute("/nonexistent/file.txt")
assert "error" in result.lower()
def test_tool_definitions():
read_tool = ReadFileTool()
definition = read_tool.get_definition()
assert definition.function["name"] == "read_file"
assert "path" in definition.function["parameters"]["properties"]
assert "path" in definition.function["parameters"]["required"]