Initial commit: VoIdeaAI - voice-first AI idea assistant
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
"""Root conftest for VoIdea tests."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Ensure app is importable
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
# Set test environment
|
||||
os.environ.setdefault("PROJECT_ENV", "test")
|
||||
os.environ.setdefault("PROJECT_VERSION", "1.0.0")
|
||||
os.environ.setdefault("JWT_SECRET_KEY", "test-secret-key-not-for-production")
|
||||
@@ -0,0 +1 @@
|
||||
"""Integration tests for VoIdeaAI."""
|
||||
@@ -0,0 +1,80 @@
|
||||
"""Integration tests for auth API endpoints.
|
||||
|
||||
Uses FastAPI TestClient to test the full request-response cycle.
|
||||
Requires a running database or test DB configured via DATABASE_URL.
|
||||
"""
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.main import app
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
class TestAuthIntegration:
|
||||
def test_health(self):
|
||||
resp = client.get("/health")
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert data["status"] == "healthy"
|
||||
|
||||
def test_register_validation(self):
|
||||
resp = client.post(
|
||||
"/api/v1/auth/register",
|
||||
json={"email": "bad", "password": "12", "display_name": ""},
|
||||
)
|
||||
assert resp.status_code == 422
|
||||
|
||||
def test_login_invalid_credentials(self):
|
||||
resp = client.post(
|
||||
"/api/v1/auth/login",
|
||||
json={"email": "nonexistent@test.com", "password": "wrongpass"},
|
||||
)
|
||||
assert resp.status_code == 401
|
||||
assert resp.json()["detail"] == "Invalid credentials"
|
||||
|
||||
def test_register_and_login_flow(self):
|
||||
import uuid
|
||||
|
||||
suffix = uuid.uuid4().hex[:8]
|
||||
email = f"test{suffix}@example.com"
|
||||
|
||||
reg_resp = client.post(
|
||||
"/api/v1/auth/register",
|
||||
json={
|
||||
"email": email,
|
||||
"password": "StrongPass1",
|
||||
"display_name": "Test User",
|
||||
},
|
||||
)
|
||||
assert reg_resp.status_code == 201
|
||||
tokens = reg_resp.json()
|
||||
assert "access_token" in tokens
|
||||
assert "refresh_token" in tokens
|
||||
|
||||
login_resp = client.post(
|
||||
"/api/v1/auth/login",
|
||||
json={"email": email, "password": "StrongPass1"},
|
||||
)
|
||||
assert login_resp.status_code == 200
|
||||
assert "access_token" in login_resp.json()
|
||||
|
||||
def test_refresh_token(self):
|
||||
import uuid
|
||||
|
||||
suffix = uuid.uuid4().hex[:8]
|
||||
email = f"refresh{suffix}@example.com"
|
||||
|
||||
reg = client.post(
|
||||
"/api/v1/auth/register",
|
||||
json={"email": email, "password": "Pass1234", "display_name": "T"},
|
||||
)
|
||||
refresh_token = reg.json()["refresh_token"]
|
||||
|
||||
refresh_resp = client.post(
|
||||
"/api/v1/auth/refresh",
|
||||
json={"refresh_token": refresh_token},
|
||||
)
|
||||
assert refresh_resp.status_code == 200
|
||||
new_tokens = refresh_resp.json()
|
||||
assert new_tokens["refresh_token"] != refresh_token
|
||||
@@ -0,0 +1,105 @@
|
||||
"""Integration tests for voice API endpoints.
|
||||
|
||||
Uses FastAPI TestClient. Registers a test user and tests voice chat flow.
|
||||
"""
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.main import app
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def _register_user() -> tuple[str, str]:
|
||||
import uuid
|
||||
|
||||
suffix = uuid.uuid4().hex[:8]
|
||||
email = f"voice{suffix}@example.com"
|
||||
resp = client.post(
|
||||
"/api/v1/auth/register",
|
||||
json={"email": email, "password": "TestPass1", "display_name": "VoiceTester"},
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
data = resp.json()
|
||||
return data["access_token"], email
|
||||
|
||||
|
||||
class TestVoiceIntegration:
|
||||
def test_list_role_agents(self):
|
||||
resp = client.get("/api/v1/voice/agents")
|
||||
assert resp.status_code == 200
|
||||
agents = resp.json()
|
||||
assert len(agents) >= 13
|
||||
names = [a["name"] for a in agents]
|
||||
assert "Бизнес-аналитик" in names
|
||||
assert "Дирижёр" not in names
|
||||
|
||||
def test_chat_requires_auth(self):
|
||||
resp = client.post(
|
||||
"/api/v1/voice/chat",
|
||||
json={"text": "hello"},
|
||||
)
|
||||
assert resp.status_code in (401, 403)
|
||||
|
||||
def test_chat_with_auth(self):
|
||||
token, _ = _register_user()
|
||||
resp = client.post(
|
||||
"/api/v1/voice/chat",
|
||||
json={"text": "Придумай идею для стартапа"},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert "response" in data
|
||||
assert "agent_name" in data
|
||||
assert "session_id" in data
|
||||
assert data["session_id"] != ""
|
||||
|
||||
def test_chat_with_session(self):
|
||||
token, _ = _register_user()
|
||||
chat1 = client.post(
|
||||
"/api/v1/voice/chat",
|
||||
json={"text": "Экология"},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
)
|
||||
session_id = chat1.json()["session_id"]
|
||||
|
||||
chat2 = client.post(
|
||||
"/api/v1/voice/chat",
|
||||
json={"text": "А какие риски?", "session_id": session_id},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
)
|
||||
assert chat2.status_code == 200
|
||||
assert chat2.json()["session_id"] == session_id
|
||||
|
||||
def test_sessions_list(self):
|
||||
token, _ = _register_user()
|
||||
client.post(
|
||||
"/api/v1/voice/chat",
|
||||
json={"text": "Тестовая идея"},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
)
|
||||
resp = client.get(
|
||||
"/api/v1/voice/sessions",
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
sessions = resp.json()
|
||||
assert len(sessions) >= 1
|
||||
|
||||
def test_rate_interaction(self):
|
||||
token, _ = _register_user()
|
||||
chat = client.post(
|
||||
"/api/v1/voice/chat",
|
||||
json={"text": "Идея для оценки"},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
)
|
||||
interaction_id = chat.json().get("interaction_id")
|
||||
if not interaction_id:
|
||||
return
|
||||
resp = client.post(
|
||||
"/api/v1/voice/rate",
|
||||
json={"interaction_id": interaction_id, "rating": 5},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
@@ -0,0 +1,13 @@
|
||||
"""Smoke tests for VoIdea health endpoints."""
|
||||
|
||||
|
||||
def test_health_endpoint_exists():
|
||||
"""Health endpoint route exists and returns expected structure.
|
||||
|
||||
This test verifies the route config without hitting a real server.
|
||||
"""
|
||||
from app.main import app
|
||||
|
||||
routes = [route.path for route in app.routes]
|
||||
assert "/health" in routes
|
||||
assert "/api/v1/health" in routes
|
||||
@@ -0,0 +1,41 @@
|
||||
# Agent Tests - VoIdea
|
||||
|
||||
## Overview
|
||||
|
||||
Unit tests for system agents.
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
tests/unit/agents/
|
||||
├── test_base.py # Base agent tests
|
||||
├── test_doc_agent.py
|
||||
├── test_backlog_agent.py
|
||||
├── test_spec_agent.py
|
||||
├── test_audit_agent.py
|
||||
├── test_observer_agent.py
|
||||
├── test_evolution_agent.py
|
||||
└── test_registry.py
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
# Run all agent tests
|
||||
pytest tests/unit/agents/ -v
|
||||
|
||||
# Run specific agent tests
|
||||
pytest tests/unit/agents/test_doc_agent.py -v
|
||||
|
||||
# With coverage
|
||||
pytest tests/unit/agents/ --cov=app.agents --cov-report=html
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Tests use mocks where database is required
|
||||
- Integration tests require actual database connection
|
||||
|
||||
---
|
||||
|
||||
*Maintained by DocAgent*
|
||||
@@ -0,0 +1,78 @@
|
||||
"""Tests for AuditAgent."""
|
||||
|
||||
import pytest
|
||||
|
||||
from app.agents.audit_agent import AuditAgent
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def audit_agent():
|
||||
return AuditAgent()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_audit_agent_initialization(audit_agent):
|
||||
"""Test AuditAgent initializes correctly."""
|
||||
assert audit_agent.name == "audit_agent"
|
||||
assert audit_agent.version == "1.0.0"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_audit_agent_health_check(audit_agent):
|
||||
"""Test AuditAgent health check."""
|
||||
result = await audit_agent.health_check()
|
||||
assert isinstance(result, bool)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_audit_agent_run(audit_agent):
|
||||
"""Test AuditAgent run."""
|
||||
context = {"action": "quick", "paths": ["app"]}
|
||||
result = await audit_agent.run(context)
|
||||
assert hasattr(result, "success")
|
||||
assert hasattr(result, "message")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_audit_agent_quick_audit(audit_agent):
|
||||
"""Test AuditAgent quick audit."""
|
||||
result = await audit_agent._run_quick_audit(["app"])
|
||||
assert hasattr(result, "success")
|
||||
assert hasattr(result, "data")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_audit_agent_docs_check(audit_agent):
|
||||
"""Test AuditAgent docs check."""
|
||||
result = await audit_agent._check_docs()
|
||||
assert hasattr(result, "success")
|
||||
assert "missing_docs" in result.data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_audit_agent_status_transitions(audit_agent):
|
||||
"""Test AuditAgent status transitions."""
|
||||
assert audit_agent.status.value == "idle"
|
||||
|
||||
await audit_agent.set_running("audit")
|
||||
assert audit_agent.status.value == "running"
|
||||
|
||||
await audit_agent.set_idle()
|
||||
assert audit_agent.status.value == "idle"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_audit_agent_metrics(audit_agent):
|
||||
"""Test AuditAgent metrics."""
|
||||
metrics = await audit_agent.get_metrics()
|
||||
assert "agent_id" in metrics
|
||||
assert "status" in metrics
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_audit_agent_full_audit(audit_agent):
|
||||
"""Test AuditAgent full audit."""
|
||||
context = {"action": "full", "paths": ["app"]}
|
||||
result = await audit_agent.run(context)
|
||||
assert hasattr(result, "success")
|
||||
assert hasattr(result, "data")
|
||||
@@ -0,0 +1,73 @@
|
||||
"""Tests for BacklogAgent."""
|
||||
|
||||
import pytest
|
||||
from uuid import uuid4
|
||||
|
||||
from app.agents.backlog_agent import BacklogAgent
|
||||
from app.agents.base import AgentResult
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def backlog_agent():
|
||||
return BacklogAgent(session=None)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_backlog_agent_initialization(backlog_agent):
|
||||
"""Test BacklogAgent initializes correctly."""
|
||||
assert backlog_agent.name == "backlog_agent"
|
||||
assert backlog_agent.version == "1.0.0"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_backlog_agent_health_check(backlog_agent):
|
||||
"""Test BacklogAgent health check."""
|
||||
result = await backlog_agent.health_check()
|
||||
assert result is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_backlog_agent_run_without_session(backlog_agent):
|
||||
"""Test BacklogAgent run without database session."""
|
||||
result = await backlog_agent.run()
|
||||
assert result.success is False
|
||||
assert "session" in result.message.lower()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_backlog_agent_suggest_items(backlog_agent):
|
||||
"""Test BacklogAgent suggestions."""
|
||||
context = {"action": "suggest"}
|
||||
result = await backlog_agent.run(context)
|
||||
assert result.success
|
||||
assert "suggestions" in result.data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_backlog_agent_list_items(backlog_agent):
|
||||
"""Test BacklogAgent list items without session."""
|
||||
context = {"action": "list"}
|
||||
result = await backlog_agent.run(context)
|
||||
assert result.success is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_backlog_agent_status(backlog_agent):
|
||||
"""Test BacklogAgent status."""
|
||||
await backlog_agent.set_running("test")
|
||||
assert backlog_agent.status.value == "running"
|
||||
|
||||
await backlog_agent.set_idle()
|
||||
assert backlog_agent.status.value == "idle"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_backlog_agent_create_without_session(backlog_agent):
|
||||
"""Test BacklogAgent create without session."""
|
||||
context = {
|
||||
"action": "create",
|
||||
"title": "Test Task",
|
||||
"item_type": "task",
|
||||
}
|
||||
result = await backlog_agent.run(context)
|
||||
assert result.success is False
|
||||
@@ -0,0 +1,130 @@
|
||||
"""Tests for BaseAgent versioning methods."""
|
||||
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from app.agents.base import BaseAgent
|
||||
|
||||
|
||||
class SimpleTestAgent(BaseAgent):
|
||||
"""Minimal agent for testing base methods."""
|
||||
|
||||
name = "test_agent"
|
||||
version = "1.0.0"
|
||||
description = "Test agent"
|
||||
|
||||
async def run(self, context=None):
|
||||
return await super().run(context) # pragma: no cover
|
||||
|
||||
async def health_check(self):
|
||||
return True
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def agent():
|
||||
return SimpleTestAgent()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def cleanup_changelog():
|
||||
yield
|
||||
path = Path("CHANGELOG") / "agents" / "test_agent.md"
|
||||
if path.exists():
|
||||
path.unlink()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_agent_initialization(agent):
|
||||
"""Test agent initializes with default version."""
|
||||
assert agent.name == "test_agent"
|
||||
assert agent.version == "1.0.0"
|
||||
|
||||
|
||||
def test_compute_checksum(agent):
|
||||
"""Test compute_checksum returns valid SHA256."""
|
||||
checksum = agent.compute_checksum()
|
||||
assert len(checksum) == 64
|
||||
assert all(c in "0123456789abcdef" for c in checksum)
|
||||
|
||||
|
||||
def test_compute_checksum_is_deterministic(agent):
|
||||
"""Test checksum is deterministic for same file."""
|
||||
assert agent.compute_checksum() == agent.compute_checksum()
|
||||
|
||||
|
||||
def test_bump_version_patch(agent):
|
||||
"""Test bump_version patch increments patch."""
|
||||
new_version = agent.bump_version("patch")
|
||||
assert new_version == "1.0.1"
|
||||
assert agent.version == "1.0.1"
|
||||
|
||||
|
||||
def test_bump_version_minor(agent):
|
||||
"""Test bump_version minor increments minor, resets patch."""
|
||||
agent.version = "1.0.5"
|
||||
new_version = agent.bump_version("minor")
|
||||
assert new_version == "1.1.0"
|
||||
assert agent.version == "1.1.0"
|
||||
|
||||
|
||||
def test_bump_version_major(agent):
|
||||
"""Test bump_version major increments major, resets minor and patch."""
|
||||
agent.version = "1.2.3"
|
||||
new_version = agent.bump_version("major")
|
||||
assert new_version == "2.0.0"
|
||||
assert agent.version == "2.0.0"
|
||||
|
||||
|
||||
def test_bump_version_default_is_patch(agent):
|
||||
"""Test bump_version defaults to patch."""
|
||||
agent.version = "2.0.0"
|
||||
new_version = agent.bump_version()
|
||||
assert new_version == "2.0.1"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_write_changelog_entry_creates_file(agent, cleanup_changelog):
|
||||
"""Test _write_changelog_entry creates a new changelog file."""
|
||||
agent._write_changelog_entry("1.0.0", ["Initial version"])
|
||||
path = Path("CHANGELOG") / "agents" / "test_agent.md"
|
||||
assert path.exists()
|
||||
content = path.read_text(encoding="utf-8")
|
||||
assert "# test_agent Changelog" in content
|
||||
assert "## 1.0.0" in content
|
||||
assert "- Initial version" in content
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_write_changelog_entry_appends(agent, cleanup_changelog):
|
||||
"""Test _write_changelog_entry appends to existing file."""
|
||||
agent._write_changelog_entry("1.0.0", ["Initial version"])
|
||||
agent._write_changelog_entry("1.0.1", ["Bugfix"])
|
||||
path = Path("CHANGELOG") / "agents" / "test_agent.md"
|
||||
content = path.read_text(encoding="utf-8")
|
||||
assert "## 1.0.0" in content
|
||||
assert "## 1.0.1" in content
|
||||
assert "- Bugfix" in content
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_version_no_changelog(agent, cleanup_changelog):
|
||||
"""Test _check_version creates initial changelog on first run."""
|
||||
result = await agent._check_version(["Initial version"])
|
||||
assert result is not None
|
||||
path = Path("CHANGELOG") / "agents" / "test_agent.md"
|
||||
assert path.exists()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_version_unchanged(agent, cleanup_changelog):
|
||||
"""Test _check_version returns None when nothing changed."""
|
||||
agent._write_changelog_entry("1.0.0", ["Initial version"])
|
||||
result = await agent._check_version()
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_changelog_dir_default(agent):
|
||||
"""Test changelog_dir attribute."""
|
||||
assert str(agent.changelog_dir) == "CHANGELOG\\agents"
|
||||
@@ -0,0 +1,87 @@
|
||||
"""Tests for DocAgent."""
|
||||
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
|
||||
from app.agents.doc_agent import DocAgent
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def doc_agent():
|
||||
return DocAgent()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_doc_agent_initialization(doc_agent):
|
||||
"""Test DocAgent initializes correctly."""
|
||||
assert doc_agent.name == "doc_agent"
|
||||
assert doc_agent.version == "1.0.0"
|
||||
assert doc_agent.description
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_doc_agent_health_check(doc_agent):
|
||||
"""Test DocAgent health check."""
|
||||
result = await doc_agent.health_check()
|
||||
assert isinstance(result, bool)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_doc_agent_run_without_context(doc_agent):
|
||||
"""Test DocAgent run without context."""
|
||||
result = await doc_agent.run()
|
||||
assert hasattr(result, "success")
|
||||
assert hasattr(result, "message")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_doc_agent_run_with_context(doc_agent):
|
||||
"""Test DocAgent run with context."""
|
||||
context = {"action": "update_all"}
|
||||
result = await doc_agent.run(context)
|
||||
assert hasattr(result, "success")
|
||||
assert hasattr(result, "message")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_doc_agent_status_transitions(doc_agent):
|
||||
"""Test DocAgent status transitions."""
|
||||
assert doc_agent.status.value == "idle"
|
||||
|
||||
await doc_agent.set_running("test_task")
|
||||
assert doc_agent.status.value == "running"
|
||||
|
||||
await doc_agent.set_idle()
|
||||
assert doc_agent.status.value == "idle"
|
||||
|
||||
await doc_agent.set_error("test error")
|
||||
assert doc_agent.status.value == "error"
|
||||
|
||||
await doc_agent.set_idle()
|
||||
assert doc_agent.status.value == "idle"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_doc_agent_metrics(doc_agent):
|
||||
"""Test DocAgent metrics retrieval."""
|
||||
metrics = await doc_agent.get_metrics()
|
||||
assert "agent_id" in metrics
|
||||
assert "status" in metrics
|
||||
assert metrics["agent_id"] == "doc_agent"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_file_purpose(doc_agent):
|
||||
"""Test file purpose detection."""
|
||||
assert "entry point" in doc_agent._get_file_purpose("main.py").lower()
|
||||
assert "configuration" in doc_agent._get_file_purpose("config.py").lower()
|
||||
assert "data models" in doc_agent._get_file_purpose("models.py").lower()
|
||||
assert "Module file" in doc_agent._get_file_purpose("unknown.py")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_doc_agent_invalid_action(doc_agent):
|
||||
"""Test DocAgent with invalid action."""
|
||||
context = {"action": "invalid_action"}
|
||||
result = await doc_agent.run(context)
|
||||
assert hasattr(result, "success")
|
||||
@@ -0,0 +1,180 @@
|
||||
"""Tests for EvolutionAgent."""
|
||||
|
||||
import pytest
|
||||
|
||||
from app.agents.evolution_agent import ALL_AGENTS, EvolutionAgent
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def evolution_agent():
|
||||
return EvolutionAgent()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_evolution_agent_initialization(evolution_agent):
|
||||
"""Test EvolutionAgent initializes correctly."""
|
||||
assert evolution_agent.name == "evolution_agent"
|
||||
assert evolution_agent.version == "1.0.0"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_evolution_agent_health_check(evolution_agent):
|
||||
"""Test EvolutionAgent health check."""
|
||||
result = await evolution_agent.health_check()
|
||||
assert isinstance(result, bool)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_evolution_agent_get_status(evolution_agent):
|
||||
"""Test EvolutionAgent get status."""
|
||||
result = await evolution_agent.run()
|
||||
assert result.success
|
||||
assert "total_agents" in result.data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_evolution_agent_analyze(evolution_agent):
|
||||
"""Test EvolutionAgent analyze."""
|
||||
context = {"action": "analyze"}
|
||||
result = await evolution_agent.run(context)
|
||||
assert result.success
|
||||
assert "analyses" in result.data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_evolution_agent_suggest(evolution_agent):
|
||||
"""Test EvolutionAgent suggest improvements."""
|
||||
context = {"action": "suggest"}
|
||||
result = await evolution_agent.run(context)
|
||||
assert result.success
|
||||
assert "suggestions" in result.data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_evolution_agent_evolve_without_id(evolution_agent):
|
||||
"""Test EvolutionAgent evolve without agent_id."""
|
||||
context = {"action": "evolve"}
|
||||
result = await evolution_agent.run(context)
|
||||
assert result.success is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_evolution_agent_evolve(evolution_agent):
|
||||
"""Test EvolutionAgent evolve with capabilities bump."""
|
||||
context = {
|
||||
"action": "evolve",
|
||||
"agent_id": "doc_agent",
|
||||
"capabilities": ["new_capability"],
|
||||
}
|
||||
result = await evolution_agent.run(context)
|
||||
assert result.success
|
||||
assert result.data.get("agent_id") == "doc_agent"
|
||||
assert "new_version" in result.data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_evolution_agent_version_check(evolution_agent):
|
||||
"""Test EvolutionAgent version_check action."""
|
||||
context = {"action": "version_check"}
|
||||
result = await evolution_agent.run(context)
|
||||
assert result.success
|
||||
assert "versions" in result.data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_evolution_agent_version_bump(evolution_agent):
|
||||
"""Test EvolutionAgent version_bump action."""
|
||||
context = {
|
||||
"action": "version_bump",
|
||||
"agent_id": "doc_agent",
|
||||
"version_type": "minor",
|
||||
"entries": ["Added: new capability"],
|
||||
}
|
||||
result = await evolution_agent.run(context)
|
||||
assert result.success
|
||||
assert result.data.get("agent_id") == "doc_agent"
|
||||
assert result.data.get("new_version") == "1.1.0"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_evolution_agent_version_bump_invalid_type(evolution_agent):
|
||||
"""Test EvolutionAgent version_bump with invalid type."""
|
||||
context = {
|
||||
"action": "version_bump",
|
||||
"agent_id": "doc_agent",
|
||||
"version_type": "patch",
|
||||
"entries": [],
|
||||
}
|
||||
result = await evolution_agent.run(context)
|
||||
assert result.success is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_evolution_agent_version_bump_major(evolution_agent):
|
||||
"""Test EvolutionAgent version_bump major."""
|
||||
context = {
|
||||
"action": "version_bump",
|
||||
"agent_id": "doc_agent",
|
||||
"version_type": "major",
|
||||
"entries": ["Breaking: changed API"],
|
||||
}
|
||||
result = await evolution_agent.run(context)
|
||||
assert result.success
|
||||
assert result.data.get("new_version") == "2.0.0"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_evolution_agent_version_check_specific(evolution_agent):
|
||||
"""Test EvolutionAgent version_check for specific agent."""
|
||||
context = {
|
||||
"action": "version_check",
|
||||
"agent_id": "doc_agent",
|
||||
}
|
||||
result = await evolution_agent.run(context)
|
||||
assert result.success
|
||||
assert "doc_agent" in result.data["versions"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_all_agents_list_completeness(evolution_agent):
|
||||
"""Test ALL_AGENTS contains all 11 agents."""
|
||||
assert len(ALL_AGENTS) == 11
|
||||
agent_ids = {a["id"] for a in ALL_AGENTS}
|
||||
expected = {
|
||||
"doc_agent", "backlog_agent", "spec_agent", "audit_agent",
|
||||
"observer_agent", "security_agent", "qa_tester_agent",
|
||||
"fix_agent", "ui_test_agent", "rollout_agent", "evolution_agent",
|
||||
}
|
||||
assert agent_ids == expected
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_evolution_agent_status_transitions(evolution_agent):
|
||||
"""Test EvolutionAgent status transitions."""
|
||||
assert evolution_agent.status.value == "idle"
|
||||
|
||||
await evolution_agent.set_running("evolution")
|
||||
assert evolution_agent.status.value == "running"
|
||||
|
||||
await evolution_agent.set_idle()
|
||||
assert evolution_agent.status.value == "idle"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_evolution_agent_metrics(evolution_agent):
|
||||
"""Test EvolutionAgent metrics."""
|
||||
metrics = await evolution_agent.get_metrics()
|
||||
assert "agent_id" in metrics
|
||||
assert "status" in metrics
|
||||
assert metrics["capabilities_tracked"] == 11
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_agent_capabilities(evolution_agent):
|
||||
"""Test capability retrieval."""
|
||||
caps = evolution_agent._get_agent_capabilities("doc_agent")
|
||||
assert len(caps) > 0
|
||||
assert "documentation" in caps
|
||||
|
||||
caps = evolution_agent._get_agent_capabilities("unknown")
|
||||
assert len(caps) == 0
|
||||
@@ -0,0 +1,68 @@
|
||||
"""Tests for FixAgent."""
|
||||
|
||||
import pytest
|
||||
|
||||
from app.agents.fix_agent import FixAgent
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fix_agent():
|
||||
return FixAgent()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fix_agent_initialization(fix_agent):
|
||||
"""Test FixAgent initializes correctly."""
|
||||
assert fix_agent.name == "fix_agent"
|
||||
assert fix_agent.version == "1.0.0"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fix_agent_health_check(fix_agent):
|
||||
"""Test FixAgent health check."""
|
||||
result = await fix_agent.health_check()
|
||||
assert isinstance(result, bool)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fix_agent_run(fix_agent):
|
||||
"""Test FixAgent run."""
|
||||
result = await fix_agent.run({"action": "suggest"})
|
||||
assert hasattr(result, "success")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fix_agent_analyze(fix_agent):
|
||||
"""Test FixAgent analyze."""
|
||||
context = {
|
||||
"bug_data": {
|
||||
"error_message": "AttributeError: 'NoneType' object has no attribute 'id'",
|
||||
}
|
||||
}
|
||||
result = await fix_agent._analyze_bug(context)
|
||||
assert result.success
|
||||
assert "analysis" in result.data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fix_agent_identify_error_type(fix_agent):
|
||||
"""Test FixAgent error type identification."""
|
||||
assert fix_agent._identify_error_type("AttributeError: 'NoneType'") == "AttributeError"
|
||||
assert fix_agent._identify_error_type("ValueError: invalid value") == "ValueError"
|
||||
assert fix_agent._identify_error_type("KeyError: 'id'") == "KeyError"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fix_agent_suggest_fixes(fix_agent):
|
||||
"""Test FixAgent suggest fixes."""
|
||||
result = await fix_agent._suggest_fixes({})
|
||||
assert result.success
|
||||
assert "suggestions" in result.data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fix_agent_severity_assessment(fix_agent):
|
||||
"""Test FixAgent severity assessment."""
|
||||
assert fix_agent._assess_severity("security vulnerability") == "critical"
|
||||
assert fix_agent._assess_severity("crash on startup") == "high"
|
||||
assert fix_agent._assess_severity("minor bug") == "medium"
|
||||
@@ -0,0 +1,71 @@
|
||||
"""Tests for ObserverAgent."""
|
||||
|
||||
import pytest
|
||||
|
||||
from app.agents.observer_agent import ObserverAgent
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def observer_agent():
|
||||
return ObserverAgent(session=None)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_observer_agent_initialization(observer_agent):
|
||||
"""Test ObserverAgent initializes correctly."""
|
||||
assert observer_agent.name == "observer_agent"
|
||||
assert observer_agent.version == "1.0.0"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_observer_agent_health_check(observer_agent):
|
||||
"""Test ObserverAgent health check."""
|
||||
result = await observer_agent.health_check()
|
||||
assert result is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_observer_agent_run_without_session(observer_agent):
|
||||
"""Test ObserverAgent run without session."""
|
||||
result = await observer_agent.run()
|
||||
assert result.success is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_observer_agent_collect_disabled(observer_agent):
|
||||
"""Test ObserverAgent collect when disabled."""
|
||||
context = {
|
||||
"action": "collect",
|
||||
"metric_name": "test_metric",
|
||||
"metric_value": 1.0,
|
||||
}
|
||||
result = await observer_agent.run(context)
|
||||
assert result.success
|
||||
assert result.data.get("skipped") or result.data.get("enabled") is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_observer_agent_status(observer_agent):
|
||||
"""Test ObserverAgent status."""
|
||||
await observer_agent.set_running("observation")
|
||||
assert observer_agent.status.value == "running"
|
||||
|
||||
await observer_agent.set_idle()
|
||||
assert observer_agent.status.value == "idle"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_observer_agent_metrics(observer_agent):
|
||||
"""Test ObserverAgent metrics."""
|
||||
metrics = await observer_agent.get_metrics()
|
||||
assert "agent_id" in metrics
|
||||
assert "enabled" in metrics
|
||||
assert "status" in metrics
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_observer_agent_report_without_session(observer_agent):
|
||||
"""Test ObserverAgent report without session."""
|
||||
context = {"action": "report", "period": "daily"}
|
||||
result = await observer_agent.run(context)
|
||||
assert result.success is False
|
||||
@@ -0,0 +1,64 @@
|
||||
"""Tests for QATesterAgent."""
|
||||
|
||||
import pytest
|
||||
|
||||
from app.agents.qa_tester_agent import QATesterAgent
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def qa_tester_agent():
|
||||
return QATesterAgent(session=None)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_qa_tester_initialization(qa_tester_agent):
|
||||
"""Test QATesterAgent initializes correctly."""
|
||||
assert qa_tester_agent.name == "qa_tester_agent"
|
||||
assert qa_tester_agent.version == "1.0.0"
|
||||
assert qa_tester_agent.max_temp_users == 10
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_qa_tester_health_check(qa_tester_agent):
|
||||
"""Test QATesterAgent health check."""
|
||||
result = await qa_tester_agent.health_check()
|
||||
assert result is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_qa_tester_run_without_session(qa_tester_agent):
|
||||
"""Test QATesterAgent run without session."""
|
||||
result = await qa_tester_agent.run()
|
||||
assert hasattr(result, "success")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_qa_tester_create_users_simulation(qa_tester_agent):
|
||||
"""Test QATesterAgent create users simulation."""
|
||||
result = await qa_tester_agent._create_temp_users(3)
|
||||
assert result.success
|
||||
assert result.data.get("simulated") is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_qa_tester_status(qa_tester_agent):
|
||||
"""Test QATesterAgent status."""
|
||||
result = await qa_tester_agent._get_status()
|
||||
assert result.success
|
||||
assert "temp_users_count" in result.data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_qa_tester_run_tests(qa_tester_agent):
|
||||
"""Test QATesterAgent run tests."""
|
||||
result = await qa_tester_agent._run_tests()
|
||||
assert result.success
|
||||
assert "total" in result.data
|
||||
assert "passed" in result.data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_qa_tester_cleanup_simulation(qa_tester_agent):
|
||||
"""Test QATesterAgent cleanup simulation."""
|
||||
result = await qa_tester_agent._cleanup()
|
||||
assert result.success
|
||||
@@ -0,0 +1,81 @@
|
||||
"""Tests for AgentRegistry."""
|
||||
|
||||
import pytest
|
||||
|
||||
from app.agents.registry import AgentRegistry, get_agent, get_all_agents
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def registry():
|
||||
return AgentRegistry()
|
||||
|
||||
|
||||
def test_registry_initialization(registry):
|
||||
"""Test registry initializes with all agents."""
|
||||
agents = registry.list_agents()
|
||||
assert len(agents) >= 5
|
||||
agent_names = [a["name"] for a in agents]
|
||||
assert "doc_agent" in agent_names
|
||||
assert "backlog_agent" in agent_names
|
||||
assert "spec_agent" in agent_names
|
||||
assert "audit_agent" in agent_names
|
||||
assert "observer_agent" in agent_names
|
||||
assert "evolution_agent" in agent_names
|
||||
|
||||
|
||||
def test_get_agent(registry):
|
||||
"""Test getting specific agent."""
|
||||
agent = registry.get("doc_agent")
|
||||
assert agent is not None
|
||||
assert agent.name == "doc_agent"
|
||||
|
||||
|
||||
def test_get_nonexistent_agent(registry):
|
||||
"""Test getting non-existent agent."""
|
||||
agent = registry.get("nonexistent_agent")
|
||||
assert agent is None
|
||||
|
||||
|
||||
def test_list_agents(registry):
|
||||
"""Test listing all agents."""
|
||||
agents = registry.list_agents()
|
||||
assert isinstance(agents, list)
|
||||
assert all("name" in a for a in agents)
|
||||
assert all("status" in a for a in agents)
|
||||
|
||||
|
||||
def test_get_all_agents_function():
|
||||
"""Test get_all_agents function."""
|
||||
agents = get_all_agents()
|
||||
assert isinstance(agents, list)
|
||||
assert len(agents) > 0
|
||||
|
||||
|
||||
def test_get_agent_function():
|
||||
"""Test get_agent function."""
|
||||
agent = get_agent("spec_agent")
|
||||
assert agent is not None
|
||||
assert agent.name == "spec_agent"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_nonexistent_agent(registry):
|
||||
"""Test running non-existent agent."""
|
||||
result = await registry.run_agent("nonexistent")
|
||||
assert result.success is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_health_check_all(registry):
|
||||
"""Test health check for all agents."""
|
||||
results = await registry.health_check_all()
|
||||
assert isinstance(results, dict)
|
||||
assert all(isinstance(v, bool) for v in results.values())
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_metrics_all(registry):
|
||||
"""Test getting metrics for all agents."""
|
||||
metrics = registry.get_metrics_all()
|
||||
assert isinstance(metrics, dict)
|
||||
assert all("status" in m for m in metrics.values())
|
||||
@@ -0,0 +1,78 @@
|
||||
"""Tests for RolloutAgent."""
|
||||
|
||||
import pytest
|
||||
|
||||
from app.agents.rollout_agent import RolloutAgent
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def rollout_agent():
|
||||
return RolloutAgent()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rollout_agent_initialization(rollout_agent):
|
||||
"""Test RolloutAgent initializes correctly."""
|
||||
assert rollout_agent.name == "rollout_agent"
|
||||
assert rollout_agent.version == "1.0.0"
|
||||
assert rollout_agent._current_stage == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rollout_agent_health_check(rollout_agent):
|
||||
"""Test RolloutAgent health check."""
|
||||
result = await rollout_agent.health_check()
|
||||
assert result is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rollout_agent_get_status(rollout_agent):
|
||||
"""Test RolloutAgent get status."""
|
||||
result = await rollout_agent._get_status()
|
||||
assert result.success
|
||||
assert "current_stage" in result.data
|
||||
assert result.data["stage_name"] == "development"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rollout_agent_promote(rollout_agent):
|
||||
"""Test RolloutAgent promote."""
|
||||
result = await rollout_agent._promote_to_next_stage()
|
||||
assert result.success
|
||||
assert result.data["stage_name"] == "3_users"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rollout_agent_rollback(rollout_agent):
|
||||
"""Test RolloutAgent rollback."""
|
||||
await rollout_agent._promote_to_next_stage()
|
||||
result = await rollout_agent._rollback(0)
|
||||
assert result.success
|
||||
assert result.data.get("rollback_history")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rollout_agent_pause_resume(rollout_agent):
|
||||
"""Test RolloutAgent pause and resume."""
|
||||
pause_result = await rollout_agent._pause_rollout()
|
||||
assert pause_result.success
|
||||
|
||||
resume_result = await rollout_agent._resume_rollout()
|
||||
assert resume_result.success
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rollout_agent_health_check_metrics(rollout_agent):
|
||||
"""Test RolloutAgent health check."""
|
||||
result = await rollout_agent._check_stage_health()
|
||||
assert hasattr(result, "success")
|
||||
assert "metrics" in result.data
|
||||
assert "issues" in result.data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rollout_agent_final_stage_cannot_promote(rollout_agent):
|
||||
"""Test RolloutAgent cannot promote past final stage."""
|
||||
rollout_agent._current_stage = 5
|
||||
result = await rollout_agent._promote_to_next_stage()
|
||||
assert result.success is False
|
||||
@@ -0,0 +1,64 @@
|
||||
"""Tests for SecurityAgent."""
|
||||
|
||||
import pytest
|
||||
|
||||
from app.agents.security_agent import SecurityAgent
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def security_agent():
|
||||
return SecurityAgent()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_security_agent_initialization(security_agent):
|
||||
"""Test SecurityAgent initializes correctly."""
|
||||
assert security_agent.name == "security_agent"
|
||||
assert security_agent.version == "1.0.0"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_security_agent_health_check(security_agent):
|
||||
"""Test SecurityAgent health check."""
|
||||
result = await security_agent.health_check()
|
||||
assert isinstance(result, bool)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_security_agent_run(security_agent):
|
||||
"""Test SecurityAgent run."""
|
||||
context = {"action": "scan", "paths": ["app"]}
|
||||
result = await security_agent.run(context)
|
||||
assert hasattr(result, "success")
|
||||
assert hasattr(result, "message")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_security_agent_full_check(security_agent):
|
||||
"""Test SecurityAgent full security check."""
|
||||
result = await security_agent.run({"action": "full", "paths": ["app"]})
|
||||
assert hasattr(result, "success")
|
||||
assert hasattr(result, "data")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_security_agent_dependencies(security_agent):
|
||||
"""Test SecurityAgent dependency check."""
|
||||
result = await security_agent._check_dependencies()
|
||||
assert hasattr(result, "success")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_security_agent_compliance(security_agent):
|
||||
"""Test SecurityAgent 152-FZ compliance check."""
|
||||
result = await security_agent._check_152_fz_compliance()
|
||||
assert hasattr(result, "success")
|
||||
assert "warnings" in result.data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_security_agent_metrics(security_agent):
|
||||
"""Test SecurityAgent metrics."""
|
||||
metrics = await security_agent.get_metrics()
|
||||
assert "agent_id" in metrics
|
||||
assert "status" in metrics
|
||||
@@ -0,0 +1,76 @@
|
||||
"""Tests for SpecAgent."""
|
||||
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
|
||||
from app.agents.spec_agent import SpecAgent
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def spec_agent():
|
||||
return SpecAgent()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_spec_agent_initialization(spec_agent):
|
||||
"""Test SpecAgent initializes correctly."""
|
||||
assert spec_agent.name == "spec_agent"
|
||||
assert spec_agent.version == "1.0.0"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_spec_agent_health_check(spec_agent):
|
||||
"""Test SpecAgent health check."""
|
||||
result = await spec_agent.health_check()
|
||||
assert isinstance(result, bool)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_spec_agent_check_version(spec_agent):
|
||||
"""Test SpecAgent version check."""
|
||||
result = await spec_agent.run({"action": "check"})
|
||||
assert result.success
|
||||
assert "current_version" in result.data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_spec_agent_bump_version(spec_agent):
|
||||
"""Test SpecAgent version bump."""
|
||||
context = {
|
||||
"action": "version_bump",
|
||||
"version_type": "patch",
|
||||
}
|
||||
result = await spec_agent.run(context)
|
||||
assert result.success
|
||||
assert "new_version" in result.data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_spec_agent_parse_commit_type(spec_agent):
|
||||
"""Test commit type parsing."""
|
||||
assert spec_agent._parse_commit_type("feat: add feature") == "feat"
|
||||
assert spec_agent._parse_commit_type("fix: bug fix") == "fix"
|
||||
assert spec_agent._parse_commit_type("docs: update docs") == "docs"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_spec_agent_parse_commit_message(spec_agent):
|
||||
"""Test commit message parsing."""
|
||||
assert "add feature" in spec_agent._parse_commit_message("feat: add feature")
|
||||
assert "bug fix" in spec_agent._parse_commit_message("fix: bug fix")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_spec_agent_metrics(spec_agent):
|
||||
"""Test SpecAgent metrics."""
|
||||
metrics = await spec_agent.get_metrics()
|
||||
assert "agent_id" in metrics
|
||||
assert "version" in metrics
|
||||
assert "status" in metrics
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_spec_agent_unknown_action(spec_agent):
|
||||
"""Test SpecAgent with unknown action."""
|
||||
result = await spec_agent.run({"action": "unknown"})
|
||||
assert result.success
|
||||
@@ -0,0 +1,62 @@
|
||||
"""Tests for UITestAgent."""
|
||||
|
||||
import pytest
|
||||
|
||||
from app.agents.ui_test_agent import UITestAgent
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ui_test_agent():
|
||||
return UITestAgent()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_test_agent_initialization(ui_test_agent):
|
||||
"""Test UITestAgent initializes correctly."""
|
||||
assert ui_test_agent.name == "ui_test_agent"
|
||||
assert ui_test_agent.version == "1.0.0"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_test_agent_health_check(ui_test_agent):
|
||||
"""Test UITestAgent health check."""
|
||||
result = await ui_test_agent.health_check()
|
||||
assert isinstance(result, bool)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_test_agent_run(ui_test_agent):
|
||||
"""Test UITestAgent run."""
|
||||
result = await ui_test_agent.run({"action": "layout"})
|
||||
assert hasattr(result, "success")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_test_agent_visual_regression(ui_test_agent):
|
||||
"""Test UITestAgent visual regression."""
|
||||
result = await ui_test_agent._visual_regression_test("desktop")
|
||||
assert hasattr(result, "success")
|
||||
assert "passed" in result.data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_test_agent_accessibility(ui_test_agent):
|
||||
"""Test UITestAgent accessibility check."""
|
||||
result = await ui_test_agent._accessibility_check()
|
||||
assert hasattr(result, "success")
|
||||
assert "issues" in result.data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_test_agent_layout(ui_test_agent):
|
||||
"""Test UITestAgent layout validation."""
|
||||
result = await ui_test_agent._layout_validation()
|
||||
assert hasattr(result, "success")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_test_agent_responsive(ui_test_agent):
|
||||
"""Test UITestAgent responsive test."""
|
||||
result = await ui_test_agent._responsive_test()
|
||||
assert hasattr(result, "success")
|
||||
assert "viewports" in result.data
|
||||
@@ -0,0 +1,53 @@
|
||||
"""Tests for API route registration and root endpoints."""
|
||||
|
||||
from app.main import app
|
||||
|
||||
|
||||
def test_root_endpoint():
|
||||
"""Test root endpoint returns correct info."""
|
||||
routes = {r.path for r in app.routes if hasattr(r, "methods")}
|
||||
assert "/" in routes
|
||||
assert "/health" in routes
|
||||
assert "/api/v1/health" in routes
|
||||
|
||||
|
||||
def test_all_api_routes_registered():
|
||||
"""Test all expected API v1 routes are registered."""
|
||||
routes = {r.path for r in app.routes if hasattr(r, "methods")}
|
||||
|
||||
expected = {
|
||||
"/api/v1/auth/register",
|
||||
"/api/v1/auth/login",
|
||||
"/api/v1/auth/refresh",
|
||||
"/api/v1/auth/oauth/{provider}",
|
||||
"/api/v1/auth/oauth/{provider}/callback",
|
||||
"/api/v1/users/me",
|
||||
"/api/v1/ideas/",
|
||||
"/api/v1/ideas/{idea_id}",
|
||||
"/api/v1/ideas/{idea_id}/analyze",
|
||||
"/api/v1/agents/",
|
||||
"/api/v1/agents/{agent_name}",
|
||||
"/api/v1/agents/{agent_name}/run",
|
||||
"/api/v1/sync/pull",
|
||||
"/api/v1/sync/push",
|
||||
"/api/v1/admin/users",
|
||||
"/api/v1/admin/users/{user_id}/role",
|
||||
"/api/v1/admin/health",
|
||||
"/api/v1/admin/logs",
|
||||
}
|
||||
|
||||
for path in expected:
|
||||
assert path in routes, f"Missing route: {path}"
|
||||
|
||||
|
||||
def test_openapi_schema():
|
||||
"""Test OpenAPI schema is generated."""
|
||||
schema = app.openapi()
|
||||
assert "VoIdea" in schema["info"]["title"]
|
||||
assert "paths" in schema
|
||||
assert len(schema["paths"]) >= 20
|
||||
|
||||
|
||||
def test_app_title():
|
||||
"""Test app has correct title."""
|
||||
assert "VoIdea" in app.title
|
||||
@@ -0,0 +1,78 @@
|
||||
"""Tests for API schema validation."""
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from app.schemas.auth import LoginRequest, RegisterRequest
|
||||
from app.schemas.idea import IdeaCreate, IdeaUpdate
|
||||
from app.schemas.user import UserCreate
|
||||
|
||||
|
||||
class TestAuthSchemas:
|
||||
def test_register_valid(self):
|
||||
data = RegisterRequest(
|
||||
email="test@example.com",
|
||||
password="password123",
|
||||
display_name="Test User",
|
||||
)
|
||||
assert data.email == "test@example.com"
|
||||
|
||||
def test_register_short_password(self):
|
||||
with pytest.raises(ValidationError):
|
||||
RegisterRequest(
|
||||
email="test@example.com",
|
||||
password="123",
|
||||
display_name="Test",
|
||||
)
|
||||
|
||||
def test_register_invalid_email(self):
|
||||
with pytest.raises(ValidationError):
|
||||
RegisterRequest(
|
||||
email="not-an-email",
|
||||
password="password123",
|
||||
display_name="Test",
|
||||
)
|
||||
|
||||
def test_login_valid(self):
|
||||
data = LoginRequest(email="test@example.com", password="pass")
|
||||
assert data.email == "test@example.com"
|
||||
|
||||
|
||||
class TestIdeaSchemas:
|
||||
def test_idea_create_valid(self):
|
||||
data = IdeaCreate(title="My Idea", content="Some content")
|
||||
assert data.title == "My Idea"
|
||||
assert data.is_public is False
|
||||
|
||||
def test_idea_create_with_tags(self):
|
||||
data = IdeaCreate(
|
||||
title="My Idea",
|
||||
content="Content",
|
||||
tags=["tech", "AI"],
|
||||
is_public=True,
|
||||
)
|
||||
assert data.tags == ["tech", "AI"]
|
||||
assert data.is_public is True
|
||||
|
||||
def test_idea_create_empty_title(self):
|
||||
with pytest.raises(ValidationError):
|
||||
IdeaCreate(title="", content="Content")
|
||||
|
||||
def test_idea_update_partial(self):
|
||||
data = IdeaUpdate(title="New Title")
|
||||
assert data.title == "New Title"
|
||||
assert data.content is None
|
||||
|
||||
def test_idea_update_empty(self):
|
||||
data = IdeaUpdate()
|
||||
assert data.title is None
|
||||
|
||||
|
||||
class TestUserSchemas:
|
||||
def test_user_create_valid(self):
|
||||
data = UserCreate(
|
||||
email="test@example.com",
|
||||
password="password123",
|
||||
display_name="Test",
|
||||
)
|
||||
assert data.display_name == "Test"
|
||||
Reference in New Issue
Block a user