test(backend): add comprehensive tests for OAuth and agent endpoints

- Added tests for OAuth provider admin and consent endpoints covering edge cases.
- Extended agent-related tests to handle incorrect project associations and lifecycle state transitions.
- Introduced tests for sprint status transitions and validation checks.
- Improved multiline formatting consistency across all test functions.
This commit is contained in:
2026-01-03 01:44:11 +01:00
parent acd18ff694
commit 664415111a
28 changed files with 1530 additions and 216 deletions

View File

@@ -159,7 +159,9 @@ async def test_agent_type_crud(async_test_db, agent_type_create_data):
@pytest_asyncio.fixture
async def test_agent_instance_crud(async_test_db, test_project_crud, test_agent_type_crud):
async def test_agent_instance_crud(
async_test_db, test_project_crud, test_agent_type_crud
):
"""Create a test agent instance in the database for CRUD tests."""
_test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:

View File

@@ -203,7 +203,7 @@ class TestAgentInstanceGetByProject:
self, db_session, test_project, test_agent_instance
):
"""Test getting agent instances with status filter."""
instances, total = await agent_instance.get_by_project(
instances, _total = await agent_instance.get_by_project(
db_session,
project_id=test_project.id,
status=AgentStatus.IDLE,

View File

@@ -17,7 +17,9 @@ class TestAgentInstanceCreate:
"""Tests for agent instance creation."""
@pytest.mark.asyncio
async def test_create_agent_instance_success(self, async_test_db, test_project_crud, test_agent_type_crud):
async def test_create_agent_instance_success(
self, async_test_db, test_project_crud, test_agent_type_crud
):
"""Test successfully creating an agent instance."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -41,7 +43,9 @@ class TestAgentInstanceCreate:
assert result.short_term_memory == {"context": "initial"}
@pytest.mark.asyncio
async def test_create_agent_instance_minimal(self, async_test_db, test_project_crud, test_agent_type_crud):
async def test_create_agent_instance_minimal(
self, async_test_db, test_project_crud, test_agent_type_crud
):
"""Test creating agent instance with minimal fields."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -62,12 +66,16 @@ class TestAgentInstanceRead:
"""Tests for agent instance read operations."""
@pytest.mark.asyncio
async def test_get_agent_instance_by_id(self, async_test_db, test_agent_instance_crud):
async def test_get_agent_instance_by_id(
self, async_test_db, test_agent_instance_crud
):
"""Test getting agent instance by ID."""
_test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
result = await agent_instance_crud.get(session, id=str(test_agent_instance_crud.id))
result = await agent_instance_crud.get(
session, id=str(test_agent_instance_crud.id)
)
assert result is not None
assert result.id == test_agent_instance_crud.id
@@ -102,33 +110,48 @@ class TestAgentInstanceUpdate:
"""Tests for agent instance update operations."""
@pytest.mark.asyncio
async def test_update_agent_instance_status(self, async_test_db, test_agent_instance_crud):
async def test_update_agent_instance_status(
self, async_test_db, test_agent_instance_crud
):
"""Test updating agent instance status."""
_test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
instance = await agent_instance_crud.get(session, id=str(test_agent_instance_crud.id))
instance = await agent_instance_crud.get(
session, id=str(test_agent_instance_crud.id)
)
update_data = AgentInstanceUpdate(
status=AgentStatus.WORKING,
current_task="Processing feature request",
)
result = await agent_instance_crud.update(session, db_obj=instance, obj_in=update_data)
result = await agent_instance_crud.update(
session, db_obj=instance, obj_in=update_data
)
assert result.status == AgentStatus.WORKING
assert result.current_task == "Processing feature request"
@pytest.mark.asyncio
async def test_update_agent_instance_memory(self, async_test_db, test_agent_instance_crud):
async def test_update_agent_instance_memory(
self, async_test_db, test_agent_instance_crud
):
"""Test updating agent instance short-term memory."""
_test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
instance = await agent_instance_crud.get(session, id=str(test_agent_instance_crud.id))
instance = await agent_instance_crud.get(
session, id=str(test_agent_instance_crud.id)
)
new_memory = {"conversation": ["msg1", "msg2"], "decisions": {"key": "value"}}
new_memory = {
"conversation": ["msg1", "msg2"],
"decisions": {"key": "value"},
}
update_data = AgentInstanceUpdate(short_term_memory=new_memory)
result = await agent_instance_crud.update(session, db_obj=instance, obj_in=update_data)
result = await agent_instance_crud.update(
session, db_obj=instance, obj_in=update_data
)
assert result.short_term_memory == new_memory
@@ -172,7 +195,9 @@ class TestAgentInstanceTerminate:
"""Tests for agent instance termination."""
@pytest.mark.asyncio
async def test_terminate_agent_instance(self, async_test_db, test_project_crud, test_agent_type_crud):
async def test_terminate_agent_instance(
self, async_test_db, test_project_crud, test_agent_type_crud
):
"""Test terminating an agent instance."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -189,7 +214,9 @@ class TestAgentInstanceTerminate:
# Terminate
async with AsyncTestingSessionLocal() as session:
result = await agent_instance_crud.terminate(session, instance_id=instance_id)
result = await agent_instance_crud.terminate(
session, instance_id=instance_id
)
assert result is not None
assert result.status == AgentStatus.TERMINATED
@@ -203,7 +230,9 @@ class TestAgentInstanceTerminate:
_test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
result = await agent_instance_crud.terminate(session, instance_id=uuid.uuid4())
result = await agent_instance_crud.terminate(
session, instance_id=uuid.uuid4()
)
assert result is None
@@ -211,7 +240,9 @@ class TestAgentInstanceMetrics:
"""Tests for agent instance metrics operations."""
@pytest.mark.asyncio
async def test_record_task_completion(self, async_test_db, test_agent_instance_crud):
async def test_record_task_completion(
self, async_test_db, test_agent_instance_crud
):
"""Test recording task completion with metrics."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -230,7 +261,9 @@ class TestAgentInstanceMetrics:
assert result.last_activity_at is not None
@pytest.mark.asyncio
async def test_record_multiple_task_completions(self, async_test_db, test_project_crud, test_agent_type_crud):
async def test_record_multiple_task_completions(
self, async_test_db, test_project_crud, test_agent_type_crud
):
"""Test recording multiple task completions accumulates metrics."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -267,7 +300,9 @@ class TestAgentInstanceMetrics:
assert result.cost_incurred == Decimal("0.0300")
@pytest.mark.asyncio
async def test_get_project_metrics(self, async_test_db, test_project_crud, test_agent_instance_crud):
async def test_get_project_metrics(
self, async_test_db, test_project_crud, test_agent_instance_crud
):
"""Test getting aggregated metrics for a project."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -290,7 +325,9 @@ class TestAgentInstanceByProject:
"""Tests for getting instances by project."""
@pytest.mark.asyncio
async def test_get_by_project(self, async_test_db, test_project_crud, test_agent_instance_crud):
async def test_get_by_project(
self, async_test_db, test_project_crud, test_agent_instance_crud
):
"""Test getting instances by project."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -304,7 +341,9 @@ class TestAgentInstanceByProject:
assert all(i.project_id == test_project_crud.id for i in instances)
@pytest.mark.asyncio
async def test_get_by_project_with_status(self, async_test_db, test_project_crud, test_agent_type_crud):
async def test_get_by_project_with_status(
self, async_test_db, test_project_crud, test_agent_type_crud
):
"""Test getting instances by project filtered by status."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -340,7 +379,9 @@ class TestAgentInstanceByAgentType:
"""Tests for getting instances by agent type."""
@pytest.mark.asyncio
async def test_get_by_agent_type(self, async_test_db, test_agent_type_crud, test_agent_instance_crud):
async def test_get_by_agent_type(
self, async_test_db, test_agent_type_crud, test_agent_instance_crud
):
"""Test getting instances by agent type."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -358,7 +399,9 @@ class TestBulkTerminate:
"""Tests for bulk termination of instances."""
@pytest.mark.asyncio
async def test_bulk_terminate_by_project(self, async_test_db, test_project_crud, test_agent_type_crud):
async def test_bulk_terminate_by_project(
self, async_test_db, test_project_crud, test_agent_type_crud
):
"""Test bulk terminating all instances in a project."""
_test_engine, AsyncTestingSessionLocal = async_test_db

View File

@@ -9,8 +9,7 @@ import pytest_asyncio
from sqlalchemy.exc import IntegrityError, OperationalError
from app.crud.syndarix.agent_type import agent_type
from app.models.syndarix import AgentInstance, AgentType, Project
from app.models.syndarix.enums import AgentStatus, ProjectStatus
from app.models.syndarix import AgentType
from app.schemas.syndarix import AgentTypeCreate
@@ -95,7 +94,9 @@ class TestAgentTypeCreate:
# Mock IntegrityError with slug in the message
mock_orig = MagicMock()
mock_orig.__str__ = lambda self: "duplicate key value violates unique constraint on slug"
mock_orig.__str__ = (
lambda self: "duplicate key value violates unique constraint on slug"
)
with patch.object(
db_session,
@@ -152,13 +153,13 @@ class TestAgentTypeGetMultiWithFilters:
@pytest.mark.asyncio
async def test_get_multi_with_filters_success(self, db_session, test_agent_type):
"""Test successfully getting agent types with filters."""
results, total = await agent_type.get_multi_with_filters(db_session)
_results, total = await agent_type.get_multi_with_filters(db_session)
assert total >= 1
@pytest.mark.asyncio
async def test_get_multi_with_filters_sort_asc(self, db_session, test_agent_type):
"""Test getting agent types with ascending sort order."""
results, total = await agent_type.get_multi_with_filters(
_results, total = await agent_type.get_multi_with_filters(
db_session,
sort_by="created_at",
sort_order="asc",
@@ -256,14 +257,18 @@ class TestAgentTypeGetByExpertise:
"""Tests for getting agent types by expertise."""
@pytest.mark.asyncio
@pytest.mark.skip(reason="Uses PostgreSQL JSONB contains operator, not available in SQLite")
@pytest.mark.skip(
reason="Uses PostgreSQL JSONB contains operator, not available in SQLite"
)
async def test_get_by_expertise_success(self, db_session, test_agent_type):
"""Test successfully getting agent types by expertise."""
results = await agent_type.get_by_expertise(db_session, expertise="python")
assert len(results) >= 1
@pytest.mark.asyncio
@pytest.mark.skip(reason="Uses PostgreSQL JSONB contains operator, not available in SQLite")
@pytest.mark.skip(
reason="Uses PostgreSQL JSONB contains operator, not available in SQLite"
)
async def test_get_by_expertise_db_error(self, db_session):
"""Test getting agent types by expertise when DB error occurs."""
with patch.object(

View File

@@ -42,7 +42,9 @@ class TestAgentTypeCreate:
assert result.is_active is True
@pytest.mark.asyncio
async def test_create_agent_type_duplicate_slug_fails(self, async_test_db, test_agent_type_crud):
async def test_create_agent_type_duplicate_slug_fails(
self, async_test_db, test_agent_type_crud
):
"""Test creating agent type with duplicate slug raises ValueError."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -109,7 +111,9 @@ class TestAgentTypeRead:
_test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
result = await agent_type_crud.get_by_slug(session, slug=test_agent_type_crud.slug)
result = await agent_type_crud.get_by_slug(
session, slug=test_agent_type_crud.slug
)
assert result is not None
assert result.slug == test_agent_type_crud.slug
@@ -120,7 +124,9 @@ class TestAgentTypeRead:
_test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
result = await agent_type_crud.get_by_slug(session, slug="non-existent-agent")
result = await agent_type_crud.get_by_slug(
session, slug="non-existent-agent"
)
assert result is None
@@ -128,48 +134,66 @@ class TestAgentTypeUpdate:
"""Tests for agent type update operations."""
@pytest.mark.asyncio
async def test_update_agent_type_basic_fields(self, async_test_db, test_agent_type_crud):
async def test_update_agent_type_basic_fields(
self, async_test_db, test_agent_type_crud
):
"""Test updating basic agent type fields."""
_test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
agent_type = await agent_type_crud.get(session, id=str(test_agent_type_crud.id))
agent_type = await agent_type_crud.get(
session, id=str(test_agent_type_crud.id)
)
update_data = AgentTypeUpdate(
name="Updated Agent Name",
description="Updated description",
)
result = await agent_type_crud.update(session, db_obj=agent_type, obj_in=update_data)
result = await agent_type_crud.update(
session, db_obj=agent_type, obj_in=update_data
)
assert result.name == "Updated Agent Name"
assert result.description == "Updated description"
@pytest.mark.asyncio
async def test_update_agent_type_expertise(self, async_test_db, test_agent_type_crud):
async def test_update_agent_type_expertise(
self, async_test_db, test_agent_type_crud
):
"""Test updating agent type expertise."""
_test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
agent_type = await agent_type_crud.get(session, id=str(test_agent_type_crud.id))
agent_type = await agent_type_crud.get(
session, id=str(test_agent_type_crud.id)
)
update_data = AgentTypeUpdate(
expertise=["new-skill", "another-skill"],
)
result = await agent_type_crud.update(session, db_obj=agent_type, obj_in=update_data)
result = await agent_type_crud.update(
session, db_obj=agent_type, obj_in=update_data
)
assert "new-skill" in result.expertise
@pytest.mark.asyncio
async def test_update_agent_type_model_params(self, async_test_db, test_agent_type_crud):
async def test_update_agent_type_model_params(
self, async_test_db, test_agent_type_crud
):
"""Test updating agent type model parameters."""
_test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
agent_type = await agent_type_crud.get(session, id=str(test_agent_type_crud.id))
agent_type = await agent_type_crud.get(
session, id=str(test_agent_type_crud.id)
)
new_params = {"temperature": 0.9, "max_tokens": 8192}
update_data = AgentTypeUpdate(model_params=new_params)
result = await agent_type_crud.update(session, db_obj=agent_type, obj_in=update_data)
result = await agent_type_crud.update(
session, db_obj=agent_type, obj_in=update_data
)
assert result.model_params == new_params
@@ -311,7 +335,9 @@ class TestAgentTypeSpecialMethods:
# Deactivate
async with AsyncTestingSessionLocal() as session:
result = await agent_type_crud.deactivate(session, agent_type_id=agent_type_id)
result = await agent_type_crud.deactivate(
session, agent_type_id=agent_type_id
)
assert result is not None
assert result.is_active is False
@@ -322,11 +348,15 @@ class TestAgentTypeSpecialMethods:
_test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
result = await agent_type_crud.deactivate(session, agent_type_id=uuid.uuid4())
result = await agent_type_crud.deactivate(
session, agent_type_id=uuid.uuid4()
)
assert result is None
@pytest.mark.asyncio
async def test_get_with_instance_count(self, async_test_db, test_agent_type_crud, test_agent_instance_crud):
async def test_get_with_instance_count(
self, async_test_db, test_agent_type_crud, test_agent_instance_crud
):
"""Test getting agent type with instance count."""
_test_engine, AsyncTestingSessionLocal = async_test_db

View File

@@ -3,13 +3,13 @@
import uuid
from datetime import UTC, datetime
from unittest.mock import AsyncMock, MagicMock, patch
from unittest.mock import MagicMock, patch
import pytest
import pytest_asyncio
from sqlalchemy.exc import IntegrityError, OperationalError
from app.crud.syndarix.issue import CRUDIssue, issue
from app.crud.syndarix.issue import issue
from app.models.syndarix import Issue, Project, Sprint
from app.models.syndarix.enums import (
IssuePriority,
@@ -18,7 +18,7 @@ from app.models.syndarix.enums import (
SprintStatus,
SyncStatus,
)
from app.schemas.syndarix import IssueCreate, IssueUpdate
from app.schemas.syndarix import IssueCreate
@pytest_asyncio.fixture
@@ -48,6 +48,7 @@ async def test_project(db_session):
async def test_sprint(db_session, test_project):
"""Create a test sprint."""
from datetime import date
sprint = Sprint(
id=uuid.uuid4(),
project_id=test_project.id,
@@ -203,7 +204,7 @@ class TestIssueGetByProject:
await db_session.commit()
# Test status filter
issues, total = await issue.get_by_project(
issues, _total = await issue.get_by_project(
db_session,
project_id=test_project.id,
status=IssueStatus.IN_PROGRESS,
@@ -212,7 +213,7 @@ class TestIssueGetByProject:
assert issues[0].status == IssueStatus.IN_PROGRESS
# Test priority filter
issues, total = await issue.get_by_project(
issues, _total = await issue.get_by_project(
db_session,
project_id=test_project.id,
priority=IssuePriority.HIGH,
@@ -221,12 +222,14 @@ class TestIssueGetByProject:
assert issues[0].priority == IssuePriority.HIGH
@pytest.mark.asyncio
@pytest.mark.skip(reason="Labels filter uses PostgreSQL @> operator, not available in SQLite")
@pytest.mark.skip(
reason="Labels filter uses PostgreSQL @> operator, not available in SQLite"
)
async def test_get_by_project_with_labels_filter(
self, db_session, test_project, test_issue
):
"""Test getting issues filtered by labels."""
issues, total = await issue.get_by_project(
issues, _total = await issue.get_by_project(
db_session,
project_id=test_project.id,
labels=["bug"],
@@ -249,7 +252,7 @@ class TestIssueGetByProject:
db_session.add(issue2)
await db_session.commit()
issues, total = await issue.get_by_project(
issues, _total = await issue.get_by_project(
db_session,
project_id=test_project.id,
sort_by="created_at",
@@ -257,8 +260,16 @@ class TestIssueGetByProject:
)
assert len(issues) == 2
# Compare without timezone info since DB may strip it
first_time = issues[0].created_at.replace(tzinfo=None) if issues[0].created_at.tzinfo else issues[0].created_at
second_time = issues[1].created_at.replace(tzinfo=None) if issues[1].created_at.tzinfo else issues[1].created_at
first_time = (
issues[0].created_at.replace(tzinfo=None)
if issues[0].created_at.tzinfo
else issues[0].created_at
)
second_time = (
issues[1].created_at.replace(tzinfo=None)
if issues[1].created_at.tzinfo
else issues[1].created_at
)
assert first_time <= second_time
@pytest.mark.asyncio
@@ -561,9 +572,7 @@ class TestIssueExternalTracker:
assert len(issues) >= 1
# Test with project filter
issues = await issue.get_pending_sync(
db_session, project_id=test_project.id
)
issues = await issue.get_pending_sync(db_session, project_id=test_project.id)
assert len(issues) >= 1
@pytest.mark.asyncio

View File

@@ -42,7 +42,9 @@ class TestIssueCreate:
assert result.story_points == 5
@pytest.mark.asyncio
async def test_create_issue_with_external_tracker(self, async_test_db, test_project_crud):
async def test_create_issue_with_external_tracker(
self, async_test_db, test_project_crud
):
"""Test creating issue with external tracker info."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -182,7 +184,9 @@ class TestIssueAssignment:
"""Tests for issue assignment operations."""
@pytest.mark.asyncio
async def test_assign_to_agent(self, async_test_db, test_issue_crud, test_agent_instance_crud):
async def test_assign_to_agent(
self, async_test_db, test_issue_crud, test_agent_instance_crud
):
"""Test assigning issue to an agent."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -198,7 +202,9 @@ class TestIssueAssignment:
assert result.human_assignee is None
@pytest.mark.asyncio
async def test_unassign_agent(self, async_test_db, test_issue_crud, test_agent_instance_crud):
async def test_unassign_agent(
self, async_test_db, test_issue_crud, test_agent_instance_crud
):
"""Test unassigning agent from issue."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -237,7 +243,9 @@ class TestIssueAssignment:
assert result.assigned_agent_id is None
@pytest.mark.asyncio
async def test_assign_to_human_clears_agent(self, async_test_db, test_issue_crud, test_agent_instance_crud):
async def test_assign_to_human_clears_agent(
self, async_test_db, test_issue_crud, test_agent_instance_crud
):
"""Test assigning to human clears agent assignment."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -304,7 +312,9 @@ class TestIssueByProject:
"""Tests for getting issues by project."""
@pytest.mark.asyncio
async def test_get_by_project(self, async_test_db, test_project_crud, test_issue_crud):
async def test_get_by_project(
self, async_test_db, test_project_crud, test_issue_crud
):
"""Test getting issues by project."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -397,7 +407,9 @@ class TestIssueBySprint:
"""Tests for getting issues by sprint."""
@pytest.mark.asyncio
async def test_get_by_sprint(self, async_test_db, test_project_crud, test_sprint_crud):
async def test_get_by_sprint(
self, async_test_db, test_project_crud, test_sprint_crud
):
"""Test getting issues by sprint."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -533,7 +545,11 @@ class TestIssueStats:
# Create issues with various statuses and priorities
async with AsyncTestingSessionLocal() as session:
for status in [IssueStatus.OPEN, IssueStatus.IN_PROGRESS, IssueStatus.CLOSED]:
for status in [
IssueStatus.OPEN,
IssueStatus.IN_PROGRESS,
IssueStatus.CLOSED,
]:
issue_data = IssueCreate(
project_id=test_project_crud.id,
title=f"Stats Issue {status.value}",

View File

@@ -10,7 +10,7 @@ from sqlalchemy.exc import IntegrityError, OperationalError
from app.crud.syndarix.project import project
from app.models.syndarix import Project
from app.models.syndarix.enums import AutonomyLevel, ProjectStatus
from app.models.syndarix.enums import ProjectStatus
from app.schemas.syndarix import ProjectCreate
@@ -88,7 +88,9 @@ class TestProjectCreate:
# Mock IntegrityError with slug in the message
mock_orig = MagicMock()
mock_orig.__str__ = lambda self: "duplicate key value violates unique constraint on slug"
mock_orig.__str__ = (
lambda self: "duplicate key value violates unique constraint on slug"
)
with patch.object(
db_session,
@@ -141,7 +143,7 @@ class TestProjectGetMultiWithFilters:
@pytest.mark.asyncio
async def test_get_multi_with_filters_success(self, db_session, test_project):
"""Test successfully getting projects with filters."""
results, total = await project.get_multi_with_filters(db_session)
_results, total = await project.get_multi_with_filters(db_session)
assert total >= 1
@pytest.mark.asyncio
@@ -162,17 +164,13 @@ class TestProjectGetWithCounts:
@pytest.mark.asyncio
async def test_get_with_counts_not_found(self, db_session):
"""Test getting non-existent project with counts."""
result = await project.get_with_counts(
db_session, project_id=uuid.uuid4()
)
result = await project.get_with_counts(db_session, project_id=uuid.uuid4())
assert result is None
@pytest.mark.asyncio
async def test_get_with_counts_success(self, db_session, test_project):
"""Test successfully getting project with counts."""
result = await project.get_with_counts(
db_session, project_id=test_project.id
)
result = await project.get_with_counts(db_session, project_id=test_project.id)
assert result is not None
assert result["project"].id == test_project.id
assert result["agent_count"] == 0
@@ -187,9 +185,7 @@ class TestProjectGetWithCounts:
side_effect=OperationalError("Connection lost", {}, Exception()),
):
with pytest.raises(OperationalError):
await project.get_with_counts(
db_session, project_id=test_project.id
)
await project.get_with_counts(db_session, project_id=test_project.id)
class TestProjectGetMultiWithCounts:
@@ -233,9 +229,7 @@ class TestProjectGetByOwner:
@pytest.mark.asyncio
async def test_get_projects_by_owner_empty(self, db_session):
"""Test getting projects by owner when none exist."""
results = await project.get_projects_by_owner(
db_session, owner_id=uuid.uuid4()
)
results = await project.get_projects_by_owner(db_session, owner_id=uuid.uuid4())
assert results == []
@pytest.mark.asyncio
@@ -247,9 +241,7 @@ class TestProjectGetByOwner:
side_effect=OperationalError("Connection lost", {}, Exception()),
):
with pytest.raises(OperationalError):
await project.get_projects_by_owner(
db_session, owner_id=uuid.uuid4()
)
await project.get_projects_by_owner(db_session, owner_id=uuid.uuid4())
class TestProjectArchive:
@@ -264,9 +256,7 @@ class TestProjectArchive:
@pytest.mark.asyncio
async def test_archive_project_success(self, db_session, test_project):
"""Test successfully archiving project."""
result = await project.archive_project(
db_session, project_id=test_project.id
)
result = await project.archive_project(db_session, project_id=test_project.id)
assert result is not None
assert result.status == ProjectStatus.ARCHIVED
@@ -279,6 +269,4 @@ class TestProjectArchive:
side_effect=OperationalError("Connection lost", {}, Exception()),
):
with pytest.raises(OperationalError):
await project.archive_project(
db_session, project_id=test_project.id
)
await project.archive_project(db_session, project_id=test_project.id)

View File

@@ -42,7 +42,9 @@ class TestProjectCreate:
assert result.owner_id == test_owner_crud.id
@pytest.mark.asyncio
async def test_create_project_duplicate_slug_fails(self, async_test_db, test_project_crud):
async def test_create_project_duplicate_slug_fails(
self, async_test_db, test_project_crud
):
"""Test creating project with duplicate slug raises ValueError."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -106,7 +108,9 @@ class TestProjectRead:
_test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
result = await project_crud.get_by_slug(session, slug=test_project_crud.slug)
result = await project_crud.get_by_slug(
session, slug=test_project_crud.slug
)
assert result is not None
assert result.slug == test_project_crud.slug
@@ -136,7 +140,9 @@ class TestProjectUpdate:
name="Updated Project Name",
description="Updated description",
)
result = await project_crud.update(session, db_obj=project, obj_in=update_data)
result = await project_crud.update(
session, db_obj=project, obj_in=update_data
)
assert result.name == "Updated Project Name"
assert result.description == "Updated description"
@@ -150,12 +156,16 @@ class TestProjectUpdate:
project = await project_crud.get(session, id=str(test_project_crud.id))
update_data = ProjectUpdate(status=ProjectStatus.PAUSED)
result = await project_crud.update(session, db_obj=project, obj_in=update_data)
result = await project_crud.update(
session, db_obj=project, obj_in=update_data
)
assert result.status == ProjectStatus.PAUSED
@pytest.mark.asyncio
async def test_update_project_autonomy_level(self, async_test_db, test_project_crud):
async def test_update_project_autonomy_level(
self, async_test_db, test_project_crud
):
"""Test updating project autonomy level."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -163,7 +173,9 @@ class TestProjectUpdate:
project = await project_crud.get(session, id=str(test_project_crud.id))
update_data = ProjectUpdate(autonomy_level=AutonomyLevel.AUTONOMOUS)
result = await project_crud.update(session, db_obj=project, obj_in=update_data)
result = await project_crud.update(
session, db_obj=project, obj_in=update_data
)
assert result.autonomy_level == AutonomyLevel.AUTONOMOUS
@@ -175,9 +187,14 @@ class TestProjectUpdate:
async with AsyncTestingSessionLocal() as session:
project = await project_crud.get(session, id=str(test_project_crud.id))
new_settings = {"mcp_servers": ["gitea", "slack"], "webhook_url": "https://example.com"}
new_settings = {
"mcp_servers": ["gitea", "slack"],
"webhook_url": "https://example.com",
}
update_data = ProjectUpdate(settings=new_settings)
result = await project_crud.update(session, db_obj=project, obj_in=update_data)
result = await project_crud.update(
session, db_obj=project, obj_in=update_data
)
assert result.settings == new_settings
@@ -273,7 +290,9 @@ class TestProjectFilters:
assert any(p.name == "Searchable Project" for p in projects)
@pytest.mark.asyncio
async def test_get_multi_with_filters_owner(self, async_test_db, test_owner_crud, test_project_crud):
async def test_get_multi_with_filters_owner(
self, async_test_db, test_owner_crud, test_project_crud
):
"""Test filtering projects by owner."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -287,7 +306,9 @@ class TestProjectFilters:
assert all(p.owner_id == test_owner_crud.id for p in projects)
@pytest.mark.asyncio
async def test_get_multi_with_filters_pagination(self, async_test_db, test_owner_crud):
async def test_get_multi_with_filters_pagination(
self, async_test_db, test_owner_crud
):
"""Test pagination of project results."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -348,7 +369,9 @@ class TestProjectSpecialMethods:
_test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
result = await project_crud.archive_project(session, project_id=test_project_crud.id)
result = await project_crud.archive_project(
session, project_id=test_project_crud.id
)
assert result is not None
assert result.status == ProjectStatus.ARCHIVED
@@ -359,11 +382,15 @@ class TestProjectSpecialMethods:
_test_engine, AsyncTestingSessionLocal = async_test_db
async with AsyncTestingSessionLocal() as session:
result = await project_crud.archive_project(session, project_id=uuid.uuid4())
result = await project_crud.archive_project(
session, project_id=uuid.uuid4()
)
assert result is None
@pytest.mark.asyncio
async def test_get_projects_by_owner(self, async_test_db, test_owner_crud, test_project_crud):
async def test_get_projects_by_owner(
self, async_test_db, test_owner_crud, test_project_crud
):
"""Test getting all projects by owner."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -377,7 +404,9 @@ class TestProjectSpecialMethods:
assert all(p.owner_id == test_owner_crud.id for p in projects)
@pytest.mark.asyncio
async def test_get_projects_by_owner_with_status(self, async_test_db, test_owner_crud):
async def test_get_projects_by_owner_with_status(
self, async_test_db, test_owner_crud
):
"""Test getting projects by owner filtered by status."""
_test_engine, AsyncTestingSessionLocal = async_test_db

View File

@@ -9,7 +9,7 @@ import pytest
import pytest_asyncio
from sqlalchemy.exc import IntegrityError, OperationalError
from app.crud.syndarix.sprint import CRUDSprint, sprint
from app.crud.syndarix.sprint import sprint
from app.models.syndarix import Issue, Project, Sprint
from app.models.syndarix.enums import (
IssueStatus,
@@ -174,7 +174,7 @@ class TestSprintGetByProject:
self, db_session, test_project, test_sprint
):
"""Test getting sprints with status filter."""
sprints, total = await sprint.get_by_project(
sprints, _total = await sprint.get_by_project(
db_session,
project_id=test_project.id,
status=SprintStatus.PLANNED,
@@ -478,7 +478,7 @@ class TestSprintWithIssueCounts:
db_session.add_all([issue1, issue2])
await db_session.commit()
results, total = await sprint.get_sprints_with_issue_counts(
results, _total = await sprint.get_sprints_with_issue_counts(
db_session, project_id=test_project.id
)
assert len(results) == 1

View File

@@ -121,7 +121,9 @@ class TestSprintUpdate:
name="Updated Sprint Name",
goal="Updated goal",
)
result = await sprint_crud.update(session, db_obj=sprint, obj_in=update_data)
result = await sprint_crud.update(
session, db_obj=sprint, obj_in=update_data
)
assert result.name == "Updated Sprint Name"
assert result.goal == "Updated goal"
@@ -139,7 +141,9 @@ class TestSprintUpdate:
start_date=today + timedelta(days=1),
end_date=today + timedelta(days=21),
)
result = await sprint_crud.update(session, db_obj=sprint, obj_in=update_data)
result = await sprint_crud.update(
session, db_obj=sprint, obj_in=update_data
)
assert result.start_date == today + timedelta(days=1)
assert result.end_date == today + timedelta(days=21)
@@ -163,7 +167,9 @@ class TestSprintLifecycle:
assert result.status == SprintStatus.ACTIVE
@pytest.mark.asyncio
async def test_start_sprint_with_custom_date(self, async_test_db, test_project_crud):
async def test_start_sprint_with_custom_date(
self, async_test_db, test_project_crud
):
"""Test starting sprint with custom start date."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -195,7 +201,9 @@ class TestSprintLifecycle:
assert result.start_date == new_start
@pytest.mark.asyncio
async def test_start_sprint_already_active_fails(self, async_test_db, test_project_crud):
async def test_start_sprint_already_active_fails(
self, async_test_db, test_project_crud
):
"""Test starting an already active sprint raises ValueError."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -250,7 +258,9 @@ class TestSprintLifecycle:
assert result.status == SprintStatus.COMPLETED
@pytest.mark.asyncio
async def test_complete_planned_sprint_fails(self, async_test_db, test_project_crud):
async def test_complete_planned_sprint_fails(
self, async_test_db, test_project_crud
):
"""Test completing a planned sprint raises ValueError."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -300,7 +310,9 @@ class TestSprintLifecycle:
assert result.status == SprintStatus.CANCELLED
@pytest.mark.asyncio
async def test_cancel_completed_sprint_fails(self, async_test_db, test_project_crud):
async def test_cancel_completed_sprint_fails(
self, async_test_db, test_project_crud
):
"""Test cancelling a completed sprint raises ValueError."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -329,7 +341,9 @@ class TestSprintByProject:
"""Tests for getting sprints by project."""
@pytest.mark.asyncio
async def test_get_by_project(self, async_test_db, test_project_crud, test_sprint_crud):
async def test_get_by_project(
self, async_test_db, test_project_crud, test_sprint_crud
):
"""Test getting sprints by project."""
_test_engine, AsyncTestingSessionLocal = async_test_db
@@ -506,7 +520,9 @@ class TestSprintWithIssueCounts:
"""Tests for getting sprints with issue counts."""
@pytest.mark.asyncio
async def test_get_sprints_with_issue_counts(self, async_test_db, test_project_crud, test_sprint_crud):
async def test_get_sprints_with_issue_counts(
self, async_test_db, test_project_crud, test_sprint_crud
):
"""Test getting sprints with issue counts."""
_test_engine, AsyncTestingSessionLocal = async_test_db

View File

@@ -12,7 +12,7 @@ from sqlalchemy.exc import DataError, IntegrityError, OperationalError
from sqlalchemy.orm import joinedload
from app.crud.user import user as user_crud
from app.schemas.users import UserCreate, UserUpdate
from app.schemas.users import UserCreate
class TestCRUDBaseGet: