Files
fast-next-template/backend/tests/schemas/syndarix/test_project_schemas.py
Felipe Cardoso 520a4d60fb feat(backend): Add Syndarix domain models with CRUD operations
- 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>
2025-12-30 02:07:27 +01:00

301 lines
9.5 KiB
Python

# tests/schemas/syndarix/test_project_schemas.py
"""
Tests for Project schema validation.
"""
import uuid
import pytest
from pydantic import ValidationError
from app.schemas.syndarix import (
AutonomyLevel,
ProjectCreate,
ProjectStatus,
ProjectUpdate,
)
class TestProjectCreateValidation:
"""Tests for ProjectCreate schema validation."""
def test_valid_project_create(self, valid_project_data):
"""Test creating project with valid data."""
project = ProjectCreate(**valid_project_data)
assert project.name == "Test Project"
assert project.slug == "test-project"
assert project.description == "A test project"
def test_project_create_defaults(self):
"""Test that defaults are applied correctly."""
project = ProjectCreate(
name="Minimal Project",
slug="minimal-project",
)
assert project.autonomy_level == AutonomyLevel.MILESTONE
assert project.status == ProjectStatus.ACTIVE
assert project.settings == {}
assert project.owner_id is None
def test_project_create_with_owner(self, valid_project_data):
"""Test creating project with owner ID."""
owner_id = uuid.uuid4()
project = ProjectCreate(
**valid_project_data,
owner_id=owner_id,
)
assert project.owner_id == owner_id
def test_project_create_name_empty_fails(self):
"""Test that empty name raises ValidationError."""
with pytest.raises(ValidationError) as exc_info:
ProjectCreate(
name="",
slug="valid-slug",
)
errors = exc_info.value.errors()
assert any("name" in str(e) for e in errors)
def test_project_create_name_whitespace_only_fails(self):
"""Test that whitespace-only name raises ValidationError."""
with pytest.raises(ValidationError) as exc_info:
ProjectCreate(
name=" ",
slug="valid-slug",
)
errors = exc_info.value.errors()
assert any("name" in str(e) for e in errors)
def test_project_create_name_stripped(self):
"""Test that name is stripped of leading/trailing whitespace."""
project = ProjectCreate(
name=" Padded Name ",
slug="padded-slug",
)
assert project.name == "Padded Name"
def test_project_create_slug_required(self):
"""Test that slug is required for create."""
with pytest.raises(ValidationError) as exc_info:
ProjectCreate(name="No Slug Project")
errors = exc_info.value.errors()
assert any("slug" in str(e).lower() for e in errors)
class TestProjectSlugValidation:
"""Tests for Project slug validation."""
def test_valid_slugs(self):
"""Test various valid slug formats."""
valid_slugs = [
"simple",
"with-hyphens",
"has123numbers",
"mix3d-with-hyphen5",
"a", # Single character
]
for slug in valid_slugs:
project = ProjectCreate(
name="Test Project",
slug=slug,
)
assert project.slug == slug
def test_invalid_slug_uppercase(self):
"""Test that uppercase letters in slug raise ValidationError."""
with pytest.raises(ValidationError) as exc_info:
ProjectCreate(
name="Test Project",
slug="Invalid-Uppercase",
)
errors = exc_info.value.errors()
assert any("slug" in str(e).lower() for e in errors)
def test_invalid_slug_special_chars(self):
"""Test that special characters in slug raise ValidationError."""
invalid_slugs = [
"has_underscore",
"has.dot",
"has@symbol",
"has space",
"has/slash",
]
for slug in invalid_slugs:
with pytest.raises(ValidationError):
ProjectCreate(
name="Test Project",
slug=slug,
)
def test_invalid_slug_starts_with_hyphen(self):
"""Test that slug starting with hyphen raises ValidationError."""
with pytest.raises(ValidationError) as exc_info:
ProjectCreate(
name="Test Project",
slug="-invalid-start",
)
errors = exc_info.value.errors()
assert any("hyphen" in str(e).lower() for e in errors)
def test_invalid_slug_ends_with_hyphen(self):
"""Test that slug ending with hyphen raises ValidationError."""
with pytest.raises(ValidationError) as exc_info:
ProjectCreate(
name="Test Project",
slug="invalid-end-",
)
errors = exc_info.value.errors()
assert any("hyphen" in str(e).lower() for e in errors)
def test_invalid_slug_consecutive_hyphens(self):
"""Test that consecutive hyphens in slug raise ValidationError."""
with pytest.raises(ValidationError) as exc_info:
ProjectCreate(
name="Test Project",
slug="invalid--consecutive",
)
errors = exc_info.value.errors()
assert any("consecutive" in str(e).lower() for e in errors)
class TestProjectUpdateValidation:
"""Tests for ProjectUpdate schema validation."""
def test_project_update_partial(self):
"""Test updating only some fields."""
update = ProjectUpdate(
name="Updated Name",
)
assert update.name == "Updated Name"
assert update.slug is None
assert update.description is None
assert update.autonomy_level is None
assert update.status is None
def test_project_update_all_fields(self):
"""Test updating all fields."""
owner_id = uuid.uuid4()
update = ProjectUpdate(
name="Updated Name",
slug="updated-slug",
description="Updated description",
autonomy_level=AutonomyLevel.AUTONOMOUS,
status=ProjectStatus.PAUSED,
settings={"key": "value"},
owner_id=owner_id,
)
assert update.name == "Updated Name"
assert update.slug == "updated-slug"
assert update.autonomy_level == AutonomyLevel.AUTONOMOUS
assert update.status == ProjectStatus.PAUSED
def test_project_update_empty_name_fails(self):
"""Test that empty name in update raises ValidationError."""
with pytest.raises(ValidationError) as exc_info:
ProjectUpdate(name="")
errors = exc_info.value.errors()
assert any("name" in str(e) for e in errors)
def test_project_update_slug_validation(self):
"""Test that slug validation applies to updates too."""
with pytest.raises(ValidationError):
ProjectUpdate(slug="Invalid-Slug")
class TestProjectEnums:
"""Tests for Project enum validation."""
def test_valid_autonomy_levels(self):
"""Test all valid autonomy levels."""
for level in AutonomyLevel:
# Replace underscores with hyphens for valid slug
slug_suffix = level.value.replace("_", "-")
project = ProjectCreate(
name="Test Project",
slug=f"project-{slug_suffix}",
autonomy_level=level,
)
assert project.autonomy_level == level
def test_invalid_autonomy_level(self):
"""Test that invalid autonomy level raises ValidationError."""
with pytest.raises(ValidationError):
ProjectCreate(
name="Test Project",
slug="invalid-autonomy",
autonomy_level="invalid", # type: ignore
)
def test_valid_project_statuses(self):
"""Test all valid project statuses."""
for status in ProjectStatus:
project = ProjectCreate(
name="Test Project",
slug=f"project-status-{status.value}",
status=status,
)
assert project.status == status
def test_invalid_project_status(self):
"""Test that invalid project status raises ValidationError."""
with pytest.raises(ValidationError):
ProjectCreate(
name="Test Project",
slug="invalid-status",
status="invalid", # type: ignore
)
class TestProjectSettings:
"""Tests for Project settings validation."""
def test_settings_empty_dict(self):
"""Test that empty settings dict is valid."""
project = ProjectCreate(
name="Test Project",
slug="empty-settings",
settings={},
)
assert project.settings == {}
def test_settings_complex_structure(self):
"""Test that complex settings structure is valid."""
complex_settings = {
"mcp_servers": ["gitea", "slack"],
"webhooks": {
"on_issue_created": "https://example.com",
},
"flags": True,
"count": 42,
}
project = ProjectCreate(
name="Test Project",
slug="complex-settings",
settings=complex_settings,
)
assert project.settings == complex_settings
def test_settings_default_to_empty_dict(self):
"""Test that settings default to empty dict when not provided."""
project = ProjectCreate(
name="Test Project",
slug="default-settings",
)
assert project.settings == {}