Files
incidentops/tests/repositories/test_service.py

202 lines
8.2 KiB
Python

"""Tests for ServiceRepository."""
from uuid import uuid4
import asyncpg
import pytest
from app.repositories.org import OrgRepository
from app.repositories.service import ServiceRepository
class TestServiceRepository:
"""Tests for ServiceRepository conforming to SPECS.md."""
async def _create_org(self, conn: asyncpg.Connection, slug: str) -> uuid4:
"""Helper to create an org."""
org_repo = OrgRepository(conn)
org_id = uuid4()
await org_repo.create(org_id, f"Org {slug}", slug)
return org_id
async def test_create_service_returns_service_data(self, db_conn: asyncpg.Connection) -> None:
"""Creating a service returns the service data."""
org_id = await self._create_org(db_conn, "service-org")
repo = ServiceRepository(db_conn)
service_id = uuid4()
result = await repo.create(service_id, org_id, "API Gateway", "api-gateway")
assert result["id"] == service_id
assert result["org_id"] == org_id
assert result["name"] == "API Gateway"
assert result["slug"] == "api-gateway"
assert result["created_at"] is not None
async def test_create_service_slug_unique_per_org(self, db_conn: asyncpg.Connection) -> None:
"""Service slug must be unique within an org per SPECS.md."""
org_id = await self._create_org(db_conn, "unique-slug-org")
repo = ServiceRepository(db_conn)
await repo.create(uuid4(), org_id, "Service One", "my-service")
with pytest.raises(asyncpg.UniqueViolationError):
await repo.create(uuid4(), org_id, "Service Two", "my-service")
async def test_same_slug_allowed_in_different_orgs(self, db_conn: asyncpg.Connection) -> None:
"""Same slug can exist in different orgs."""
org1 = await self._create_org(db_conn, "org-one")
org2 = await self._create_org(db_conn, "org-two")
repo = ServiceRepository(db_conn)
slug = "shared-slug"
# Both should succeed
result1 = await repo.create(uuid4(), org1, "Service Org1", slug)
result2 = await repo.create(uuid4(), org2, "Service Org2", slug)
assert result1["slug"] == slug
assert result2["slug"] == slug
assert result1["org_id"] != result2["org_id"]
async def test_get_by_id_returns_service(self, db_conn: asyncpg.Connection) -> None:
"""get_by_id returns the correct service."""
org_id = await self._create_org(db_conn, "get-service-org")
repo = ServiceRepository(db_conn)
service_id = uuid4()
await repo.create(service_id, org_id, "My Service", "my-service")
result = await repo.get_by_id(service_id)
assert result is not None
assert result["id"] == service_id
assert result["name"] == "My Service"
async def test_get_by_id_returns_none_for_nonexistent(self, db_conn: asyncpg.Connection) -> None:
"""get_by_id returns None for non-existent service."""
repo = ServiceRepository(db_conn)
result = await repo.get_by_id(uuid4())
assert result is None
async def test_get_by_org_returns_all_org_services(self, db_conn: asyncpg.Connection) -> None:
"""get_by_org returns all services for an organization."""
org_id = await self._create_org(db_conn, "multi-service-org")
repo = ServiceRepository(db_conn)
await repo.create(uuid4(), org_id, "Service A", "service-a")
await repo.create(uuid4(), org_id, "Service B", "service-b")
await repo.create(uuid4(), org_id, "Service C", "service-c")
result = await repo.get_by_org(org_id)
assert len(result) == 3
names = {s["name"] for s in result}
assert names == {"Service A", "Service B", "Service C"}
async def test_get_by_org_returns_empty_for_no_services(self, db_conn: asyncpg.Connection) -> None:
"""get_by_org returns empty list for org with no services."""
org_id = await self._create_org(db_conn, "empty-service-org")
repo = ServiceRepository(db_conn)
result = await repo.get_by_org(org_id)
assert result == []
async def test_get_by_org_only_returns_own_services(self, db_conn: asyncpg.Connection) -> None:
"""get_by_org doesn't return services from other orgs (tenant isolation)."""
org1 = await self._create_org(db_conn, "isolated-org-1")
org2 = await self._create_org(db_conn, "isolated-org-2")
repo = ServiceRepository(db_conn)
await repo.create(uuid4(), org1, "Org1 Service", "org1-service")
await repo.create(uuid4(), org2, "Org2 Service", "org2-service")
result = await repo.get_by_org(org1)
assert len(result) == 1
assert result[0]["name"] == "Org1 Service"
async def test_get_by_slug_returns_service(self, db_conn: asyncpg.Connection) -> None:
"""get_by_slug returns service by org and slug."""
org_id = await self._create_org(db_conn, "slug-lookup-org")
repo = ServiceRepository(db_conn)
service_id = uuid4()
await repo.create(service_id, org_id, "Slug Service", "slug-service")
result = await repo.get_by_slug(org_id, "slug-service")
assert result is not None
assert result["id"] == service_id
async def test_get_by_slug_returns_none_for_wrong_org(self, db_conn: asyncpg.Connection) -> None:
"""get_by_slug returns None if slug exists but in different org."""
org1 = await self._create_org(db_conn, "slug-org-1")
org2 = await self._create_org(db_conn, "slug-org-2")
repo = ServiceRepository(db_conn)
await repo.create(uuid4(), org1, "Service", "the-slug")
result = await repo.get_by_slug(org2, "the-slug")
assert result is None
async def test_get_by_slug_returns_none_for_nonexistent(self, db_conn: asyncpg.Connection) -> None:
"""get_by_slug returns None for non-existent slug."""
org_id = await self._create_org(db_conn, "no-slug-org")
repo = ServiceRepository(db_conn)
result = await repo.get_by_slug(org_id, "nonexistent")
assert result is None
async def test_slug_exists_returns_true_when_exists(self, db_conn: asyncpg.Connection) -> None:
"""slug_exists returns True when slug exists in org."""
org_id = await self._create_org(db_conn, "exists-org")
repo = ServiceRepository(db_conn)
await repo.create(uuid4(), org_id, "Exists Service", "exists-slug")
result = await repo.slug_exists(org_id, "exists-slug")
assert result is True
async def test_slug_exists_returns_false_when_not_exists(self, db_conn: asyncpg.Connection) -> None:
"""slug_exists returns False when slug doesn't exist in org."""
org_id = await self._create_org(db_conn, "not-exists-org")
repo = ServiceRepository(db_conn)
result = await repo.slug_exists(org_id, "no-such-slug")
assert result is False
async def test_slug_exists_returns_false_for_other_org(self, db_conn: asyncpg.Connection) -> None:
"""slug_exists returns False for slug in different org."""
org1 = await self._create_org(db_conn, "other-org-1")
org2 = await self._create_org(db_conn, "other-org-2")
repo = ServiceRepository(db_conn)
await repo.create(uuid4(), org1, "Service", "cross-org-slug")
result = await repo.slug_exists(org2, "cross-org-slug")
assert result is False
async def test_service_requires_valid_org_foreign_key(self, db_conn: asyncpg.Connection) -> None:
"""services.org_id must reference existing org."""
repo = ServiceRepository(db_conn)
with pytest.raises(asyncpg.ForeignKeyViolationError):
await repo.create(uuid4(), uuid4(), "Orphan Service", "orphan")
async def test_get_by_org_orders_by_name(self, db_conn: asyncpg.Connection) -> None:
"""get_by_org returns services ordered by name."""
org_id = await self._create_org(db_conn, "ordered-org")
repo = ServiceRepository(db_conn)
await repo.create(uuid4(), org_id, "Zebra", "zebra")
await repo.create(uuid4(), org_id, "Alpha", "alpha")
await repo.create(uuid4(), org_id, "Middle", "middle")
result = await repo.get_by_org(org_id)
names = [s["name"] for s in result]
assert names == ["Alpha", "Middle", "Zebra"]