Files

128 lines
3.9 KiB
Python
Raw Permalink Normal View History

"""The task_cell_projects UNIQUE(task_id, team) constraint must hold on the real
Postgres schema — the 'one project per cell per task' invariant (#67).
The cell-map resolution tests stub the map against SimpleNamespace objects and
never touch a DB session, so the real constraint is unexercised. If it were
mis-declared or dropped, two same-team rows could coexist and
``_resolve_subtask_project`` would non-deterministically return one — cutting a
subtask's branch/PR against the WRONG repo. This integration test inserts two
rows with the same (task_id, team) and asserts the second raises IntegrityError
on ``uq_task_cell_projects_task_team``.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from uuid import uuid4
import pytest
import pytest_asyncio
from roboco.db.tables import (
AgentTable,
ProjectTable,
TaskCellProjectTable,
TaskTable,
)
from roboco.models import AgentRole, AgentStatus, Team
from roboco.models.base import Complexity, TaskNature, TaskStatus, TaskType
from sqlalchemy.exc import IntegrityError
if TYPE_CHECKING:
from collections.abc import AsyncIterator
from sqlalchemy.ext.asyncio import AsyncSession
@pytest_asyncio.fixture
async def setup_task_and_project(
db_session: AsyncSession,
) -> AsyncIterator[dict]:
system = AgentTable(
id=uuid4(),
name="System",
slug=f"system-{uuid4().hex[:8]}",
role=AgentRole.SYSTEM,
team=None,
status=AgentStatus.ACTIVE,
model_config={},
system_prompt="s",
capabilities=[],
permissions={},
metrics={},
)
db_session.add(system)
await db_session.flush()
project = ProjectTable(
id=uuid4(),
name="P",
slug=f"p-{uuid4().hex[:6]}",
git_url="https://example.com/r.git",
assigned_cell=Team.BACKEND,
created_by=system.id,
)
db_session.add(project)
await db_session.flush()
task = TaskTable(
id=uuid4(),
title="coordination root",
description="a real description over twenty chars long",
acceptance_criteria=["criterion one"],
team=Team.BACKEND,
status=TaskStatus.PENDING,
task_type=TaskType.CODE,
nature=TaskNature.TECHNICAL,
estimated_complexity=Complexity.MEDIUM,
created_by=system.id,
project_id=project.id,
)
db_session.add(task)
await db_session.flush()
yield {"task_id": task.id, "project_id": project.id}
@pytest.mark.asyncio
async def test_duplicate_task_team_raises_integrity_error(
db_session: AsyncSession, setup_task_and_project: dict
) -> None:
"""A second row with the same (task_id, team) violates the unique constraint."""
task_id = setup_task_and_project["task_id"]
project_id = setup_task_and_project["project_id"]
first = TaskCellProjectTable(
id=uuid4(), task_id=task_id, team=Team.BACKEND, project_id=project_id
)
db_session.add(first)
await db_session.flush()
second = TaskCellProjectTable(
id=uuid4(), task_id=task_id, team=Team.BACKEND, project_id=project_id
)
db_session.add(second)
with pytest.raises(IntegrityError):
await db_session.flush()
@pytest.mark.asyncio
async def test_same_task_different_teams_allowed(
db_session: AsyncSession, setup_task_and_project: dict
) -> None:
"""One project per cell per task — different cells coexist for one task."""
task_id = setup_task_and_project["task_id"]
project_id = setup_task_and_project["project_id"]
db_session.add_all(
[
TaskCellProjectTable(
id=uuid4(), task_id=task_id, team=Team.BACKEND, project_id=project_id
),
TaskCellProjectTable(
id=uuid4(), task_id=task_id, team=Team.FRONTEND, project_id=project_id
),
]
)
await db_session.flush() # no error — different teams