- Add Project model with slug, description, autonomy level, and settings - Add AgentType model for agent templates with model config and failover - Add AgentInstance model for running agents with status and memory - Add Issue model with external tracker sync (Gitea/GitHub/GitLab) - Add Sprint model with velocity tracking and lifecycle management - Add comprehensive Pydantic schemas with validation - Add full CRUD operations for all models with filtering/sorting - Add 280+ tests for models, schemas, and CRUD operations Implements #23, #24, #25, #26, #27 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
245 lines
8.6 KiB
Python
245 lines
8.6 KiB
Python
# tests/schemas/syndarix/test_agent_instance_schemas.py
|
|
"""
|
|
Tests for AgentInstance schema validation.
|
|
"""
|
|
|
|
import uuid
|
|
from decimal import Decimal
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from app.schemas.syndarix import (
|
|
AgentInstanceCreate,
|
|
AgentInstanceUpdate,
|
|
AgentStatus,
|
|
)
|
|
|
|
|
|
class TestAgentInstanceCreateValidation:
|
|
"""Tests for AgentInstanceCreate schema validation."""
|
|
|
|
def test_valid_agent_instance_create(self, valid_agent_instance_data):
|
|
"""Test creating agent instance with valid data."""
|
|
instance = AgentInstanceCreate(**valid_agent_instance_data)
|
|
|
|
assert instance.agent_type_id is not None
|
|
assert instance.project_id is not None
|
|
|
|
def test_agent_instance_create_defaults(self, valid_agent_instance_data):
|
|
"""Test that defaults are applied correctly."""
|
|
instance = AgentInstanceCreate(**valid_agent_instance_data)
|
|
|
|
assert instance.status == AgentStatus.IDLE
|
|
assert instance.current_task is None
|
|
assert instance.short_term_memory == {}
|
|
assert instance.long_term_memory_ref is None
|
|
assert instance.session_id is None
|
|
|
|
def test_agent_instance_create_with_all_fields(self, valid_uuid):
|
|
"""Test creating agent instance with all optional fields."""
|
|
instance = AgentInstanceCreate(
|
|
agent_type_id=valid_uuid,
|
|
project_id=valid_uuid,
|
|
status=AgentStatus.WORKING,
|
|
current_task="Processing feature request",
|
|
short_term_memory={"context": "working"},
|
|
long_term_memory_ref="project-123/agent-456",
|
|
session_id="session-abc",
|
|
)
|
|
|
|
assert instance.status == AgentStatus.WORKING
|
|
assert instance.current_task == "Processing feature request"
|
|
assert instance.short_term_memory == {"context": "working"}
|
|
assert instance.long_term_memory_ref == "project-123/agent-456"
|
|
assert instance.session_id == "session-abc"
|
|
|
|
def test_agent_instance_create_agent_type_id_required(self, valid_uuid):
|
|
"""Test that agent_type_id is required."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
AgentInstanceCreate(
|
|
project_id=valid_uuid,
|
|
)
|
|
|
|
errors = exc_info.value.errors()
|
|
assert any("agent_type_id" in str(e).lower() for e in errors)
|
|
|
|
def test_agent_instance_create_project_id_required(self, valid_uuid):
|
|
"""Test that project_id is required."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
AgentInstanceCreate(
|
|
agent_type_id=valid_uuid,
|
|
)
|
|
|
|
errors = exc_info.value.errors()
|
|
assert any("project_id" in str(e).lower() for e in errors)
|
|
|
|
|
|
class TestAgentInstanceUpdateValidation:
|
|
"""Tests for AgentInstanceUpdate schema validation."""
|
|
|
|
def test_agent_instance_update_partial(self):
|
|
"""Test updating only some fields."""
|
|
update = AgentInstanceUpdate(
|
|
status=AgentStatus.WORKING,
|
|
)
|
|
|
|
assert update.status == AgentStatus.WORKING
|
|
assert update.current_task is None
|
|
assert update.short_term_memory is None
|
|
|
|
def test_agent_instance_update_all_fields(self):
|
|
"""Test updating all fields."""
|
|
from datetime import UTC, datetime
|
|
|
|
now = datetime.now(UTC)
|
|
update = AgentInstanceUpdate(
|
|
status=AgentStatus.WORKING,
|
|
current_task="New task",
|
|
short_term_memory={"new": "context"},
|
|
long_term_memory_ref="new-ref",
|
|
session_id="new-session",
|
|
last_activity_at=now,
|
|
tasks_completed=5,
|
|
tokens_used=10000,
|
|
cost_incurred=Decimal("1.5000"),
|
|
)
|
|
|
|
assert update.status == AgentStatus.WORKING
|
|
assert update.current_task == "New task"
|
|
assert update.tasks_completed == 5
|
|
assert update.tokens_used == 10000
|
|
assert update.cost_incurred == Decimal("1.5000")
|
|
|
|
def test_agent_instance_update_tasks_completed_negative_fails(self):
|
|
"""Test that negative tasks_completed raises ValidationError."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
AgentInstanceUpdate(tasks_completed=-1)
|
|
|
|
errors = exc_info.value.errors()
|
|
assert any("tasks_completed" in str(e).lower() for e in errors)
|
|
|
|
def test_agent_instance_update_tokens_used_negative_fails(self):
|
|
"""Test that negative tokens_used raises ValidationError."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
AgentInstanceUpdate(tokens_used=-1)
|
|
|
|
errors = exc_info.value.errors()
|
|
assert any("tokens_used" in str(e).lower() for e in errors)
|
|
|
|
def test_agent_instance_update_cost_incurred_negative_fails(self):
|
|
"""Test that negative cost_incurred raises ValidationError."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
AgentInstanceUpdate(cost_incurred=Decimal("-0.01"))
|
|
|
|
errors = exc_info.value.errors()
|
|
assert any("cost_incurred" in str(e).lower() for e in errors)
|
|
|
|
|
|
class TestAgentStatusEnum:
|
|
"""Tests for AgentStatus enum validation."""
|
|
|
|
def test_valid_agent_statuses(self, valid_uuid):
|
|
"""Test all valid agent statuses."""
|
|
for status in AgentStatus:
|
|
instance = AgentInstanceCreate(
|
|
agent_type_id=valid_uuid,
|
|
project_id=valid_uuid,
|
|
status=status,
|
|
)
|
|
assert instance.status == status
|
|
|
|
def test_invalid_agent_status(self, valid_uuid):
|
|
"""Test that invalid agent status raises ValidationError."""
|
|
with pytest.raises(ValidationError):
|
|
AgentInstanceCreate(
|
|
agent_type_id=valid_uuid,
|
|
project_id=valid_uuid,
|
|
status="invalid", # type: ignore
|
|
)
|
|
|
|
|
|
class TestAgentInstanceShortTermMemory:
|
|
"""Tests for AgentInstance short_term_memory validation."""
|
|
|
|
def test_short_term_memory_empty_dict(self, valid_uuid):
|
|
"""Test that empty short_term_memory is valid."""
|
|
instance = AgentInstanceCreate(
|
|
agent_type_id=valid_uuid,
|
|
project_id=valid_uuid,
|
|
short_term_memory={},
|
|
)
|
|
assert instance.short_term_memory == {}
|
|
|
|
def test_short_term_memory_complex(self, valid_uuid):
|
|
"""Test complex short_term_memory structure."""
|
|
memory = {
|
|
"conversation_history": [
|
|
{"role": "user", "content": "Hello"},
|
|
{"role": "assistant", "content": "Hi there"},
|
|
],
|
|
"recent_files": ["file1.py", "file2.py"],
|
|
"decisions": {"key": "value"},
|
|
"context_tokens": 1024,
|
|
}
|
|
instance = AgentInstanceCreate(
|
|
agent_type_id=valid_uuid,
|
|
project_id=valid_uuid,
|
|
short_term_memory=memory,
|
|
)
|
|
assert instance.short_term_memory == memory
|
|
|
|
|
|
class TestAgentInstanceStringFields:
|
|
"""Tests for AgentInstance string field validation."""
|
|
|
|
def test_long_term_memory_ref_max_length(self, valid_uuid):
|
|
"""Test long_term_memory_ref max length."""
|
|
long_ref = "a" * 500 # Max length is 500
|
|
|
|
instance = AgentInstanceCreate(
|
|
agent_type_id=valid_uuid,
|
|
project_id=valid_uuid,
|
|
long_term_memory_ref=long_ref,
|
|
)
|
|
assert instance.long_term_memory_ref == long_ref
|
|
|
|
def test_long_term_memory_ref_too_long(self, valid_uuid):
|
|
"""Test that too long long_term_memory_ref raises ValidationError."""
|
|
too_long = "a" * 501
|
|
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
AgentInstanceCreate(
|
|
agent_type_id=valid_uuid,
|
|
project_id=valid_uuid,
|
|
long_term_memory_ref=too_long,
|
|
)
|
|
|
|
errors = exc_info.value.errors()
|
|
assert any("long_term_memory_ref" in str(e).lower() for e in errors)
|
|
|
|
def test_session_id_max_length(self, valid_uuid):
|
|
"""Test session_id max length."""
|
|
long_session = "a" * 255 # Max length is 255
|
|
|
|
instance = AgentInstanceCreate(
|
|
agent_type_id=valid_uuid,
|
|
project_id=valid_uuid,
|
|
session_id=long_session,
|
|
)
|
|
assert instance.session_id == long_session
|
|
|
|
def test_session_id_too_long(self, valid_uuid):
|
|
"""Test that too long session_id raises ValidationError."""
|
|
too_long = "a" * 256
|
|
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
AgentInstanceCreate(
|
|
agent_type_id=valid_uuid,
|
|
project_id=valid_uuid,
|
|
session_id=too_long,
|
|
)
|
|
|
|
errors = exc_info.value.errors()
|
|
assert any("session_id" in str(e).lower() for e in errors)
|