mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
Service-level tests now exercise provider, permissions, project, journal, messaging, work_session, metrics, kanban, extraction, learning, notification, dashboard, llm_routing, a2a, task, repository_base, audit, db_seed, branch_name, indexed_document, query_helpers, agent. API route tests cover provider, journal, project, sessions, dashboard, work_session, tasks, a2a, groups, notifications, agents, channels, messages, kanban, api_resources. Pure-function helpers covered: handlers, deps_helpers, middleware, middleware_docs, transcription, pr templates, agents_config, errors, logging, journal/notification/channel/a2a access, task_lifecycle, streaming, converters, crypto, schemas (common + websocket), events, permissions extras. pyproject ruff per-file-ignores extended for tests so PLR2004 (status code magic values), PLC0415 (lazy imports), PLR0913 (fixture params), ARG001 (unused fixture deps), SIM105, and E501 don't fight test idioms.
128 lines
3.6 KiB
Python
128 lines
3.6 KiB
Python
"""api.utils.resources coverage."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
from uuid import uuid4
|
|
|
|
import pytest
|
|
from fastapi import HTTPException
|
|
from roboco.api.utils.resources import (
|
|
get_by_field_or_404,
|
|
get_or_404,
|
|
require_membership,
|
|
require_ownership,
|
|
require_recipient,
|
|
)
|
|
from roboco.db.tables import AgentTable
|
|
from roboco.models import AgentRole, AgentStatus, Team
|
|
|
|
if TYPE_CHECKING:
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_or_404_finds_existing(db_session: AsyncSession) -> None:
|
|
agent = AgentTable(
|
|
id=uuid4(),
|
|
name="Dev",
|
|
slug=f"d-{uuid4().hex[:8]}",
|
|
role=AgentRole.DEVELOPER,
|
|
team=Team.BACKEND,
|
|
status=AgentStatus.ACTIVE,
|
|
model_config={},
|
|
system_prompt="x",
|
|
capabilities=[],
|
|
permissions={},
|
|
metrics={},
|
|
)
|
|
db_session.add(agent)
|
|
await db_session.flush()
|
|
|
|
fetched = await get_or_404(db_session, AgentTable, agent.id)
|
|
assert fetched.id == agent.id
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_or_404_raises_when_missing(db_session: AsyncSession) -> None:
|
|
with pytest.raises(HTTPException) as exc:
|
|
await get_or_404(db_session, AgentTable, uuid4(), "Agent")
|
|
assert exc.value.status_code == 404
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_by_field_or_404_finds(db_session: AsyncSession) -> None:
|
|
agent = AgentTable(
|
|
id=uuid4(),
|
|
name="Dev2",
|
|
slug=f"d2-{uuid4().hex[:8]}",
|
|
role=AgentRole.DEVELOPER,
|
|
team=Team.BACKEND,
|
|
status=AgentStatus.ACTIVE,
|
|
model_config={},
|
|
system_prompt="x",
|
|
capabilities=[],
|
|
permissions={},
|
|
metrics={},
|
|
)
|
|
db_session.add(agent)
|
|
await db_session.flush()
|
|
|
|
fetched = await get_by_field_or_404(
|
|
db_session, AgentTable, "slug", agent.slug, "Agent"
|
|
)
|
|
assert fetched.id == agent.id
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_by_field_or_404_raises(db_session: AsyncSession) -> None:
|
|
with pytest.raises(HTTPException) as exc:
|
|
await get_by_field_or_404(db_session, AgentTable, "slug", "ghost-slug", "Agent")
|
|
assert exc.value.status_code == 404
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Ownership / Recipients / Membership
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_require_ownership_passes_for_owner() -> None:
|
|
aid = uuid4()
|
|
resource = type("R", (), {"owner": aid})()
|
|
require_ownership(resource, "owner", aid, "edit")
|
|
|
|
|
|
def test_require_ownership_raises_for_other_agent() -> None:
|
|
resource = type("R", (), {"owner": uuid4()})()
|
|
with pytest.raises(HTTPException) as exc:
|
|
require_ownership(resource, "owner", uuid4(), "edit")
|
|
assert exc.value.status_code == 403
|
|
|
|
|
|
def test_require_ownership_no_owner_passes() -> None:
|
|
resource = type("R", (), {"owner": None})()
|
|
# Should not raise — no owner means open access.
|
|
require_ownership(resource, "owner", uuid4(), "edit")
|
|
|
|
|
|
def test_require_recipient_passes() -> None:
|
|
aid = uuid4()
|
|
require_recipient([uuid4(), aid, uuid4()], aid)
|
|
|
|
|
|
def test_require_recipient_raises() -> None:
|
|
with pytest.raises(HTTPException) as exc:
|
|
require_recipient([uuid4(), uuid4()], uuid4())
|
|
assert exc.value.status_code == 403
|
|
|
|
|
|
def test_require_membership_passes() -> None:
|
|
aid = uuid4()
|
|
require_membership([uuid4(), aid], aid, "channel")
|
|
|
|
|
|
def test_require_membership_raises() -> None:
|
|
with pytest.raises(HTTPException) as exc:
|
|
require_membership([uuid4()], uuid4(), "channel")
|
|
assert exc.value.status_code == 403
|