"""`domains/service.py` emits log events for the allowlist rejection and every mutation (ADR-0011). `ensure_domain_allowed` is the one that matters most: it runs before any `ingestion_jobs` row exists, so without its own log a rejected upload leaves no operational trace at all. """ from collections.abc import MutableMapping from typing import Any import pytest import structlog from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker from src.application.domains import ( DomainAlreadyExistsError, UnknownDomainError, create_domain, ensure_domain_allowed, set_domain_status, update_domain, ) from tests.support.factories import create_tenant, create_tenant_domain pytestmark = [ pytest.mark.integration, pytest.mark.postgres, pytest.mark.asyncio(loop_scope="session"), ] def _events_by_name( logs: list[MutableMapping[str, Any]], name: str ) -> list[MutableMapping[str, Any]]: return [entry for entry in logs if entry.get("event") == name] async def test_ensure_domain_allowed_logs_nothing_when_the_domain_is_active( db_session: AsyncSession, ) -> None: tenant = await create_tenant(db_session) await create_tenant_domain(db_session, tenant=tenant, domain="fire") with structlog.testing.capture_logs() as logs: await ensure_domain_allowed(db_session, tenant_id=tenant.id, domain="fire") assert _events_by_name(logs, "domain.rejected") == [] async def test_ensure_domain_allowed_logs_rejection_for_an_unregistered_domain( db_session: AsyncSession, ) -> None: tenant = await create_tenant(db_session) await create_tenant_domain(db_session, tenant=tenant, domain="fire") with structlog.testing.capture_logs() as logs, pytest.raises(UnknownDomainError): await ensure_domain_allowed(db_session, tenant_id=tenant.id, domain="fier") rejected = _events_by_name(logs, "domain.rejected") assert len(rejected) == 1 assert rejected[0]["reason"] == "unregistered" assert rejected[0]["domain"] == "fier" assert rejected[0]["tenant_id"] == str(tenant.id) async def test_ensure_domain_allowed_logs_rejection_for_a_disabled_domain( db_session: AsyncSession, ) -> None: tenant = await create_tenant(db_session) await create_tenant_domain(db_session, tenant=tenant, domain="fire", status="disabled") with structlog.testing.capture_logs() as logs, pytest.raises(UnknownDomainError): await ensure_domain_allowed(db_session, tenant_id=tenant.id, domain="fire") rejected = _events_by_name(logs, "domain.rejected") assert len(rejected) == 1 assert rejected[0]["reason"] == "disabled" async def test_create_domain_emits_domain_created( db_session: AsyncSession, db_sessionmaker: async_sessionmaker[AsyncSession] ) -> None: tenant = await create_tenant(db_session) await db_session.commit() with structlog.testing.capture_logs() as logs: await create_domain( db_sessionmaker, tenant_id=tenant.id, domain="car", display_name="Car insurance" ) created = _events_by_name(logs, "domain.created") assert len(created) == 1 assert created[0]["domain"] == "car" async def test_create_domain_duplicate_does_not_emit_domain_created( db_session: AsyncSession, db_sessionmaker: async_sessionmaker[AsyncSession] ) -> None: tenant = await create_tenant(db_session) await create_tenant_domain(db_session, tenant=tenant, domain="fire") await db_session.commit() with structlog.testing.capture_logs() as logs, pytest.raises(DomainAlreadyExistsError): await create_domain( db_sessionmaker, tenant_id=tenant.id, domain="fire", display_name="Fire again" ) assert _events_by_name(logs, "domain.created") == [] async def test_update_domain_emits_domain_updated( db_session: AsyncSession, db_sessionmaker: async_sessionmaker[AsyncSession] ) -> None: tenant = await create_tenant(db_session) await create_tenant_domain(db_session, tenant=tenant, domain="fire") await db_session.commit() with structlog.testing.capture_logs() as logs: await update_domain( db_sessionmaker, tenant_id=tenant.id, domain="fire", display_name="Fire & perils" ) updated = _events_by_name(logs, "domain.updated") assert len(updated) == 1 assert updated[0]["domain"] == "fire" async def test_set_domain_status_emits_domain_status_changed( db_session: AsyncSession, db_sessionmaker: async_sessionmaker[AsyncSession] ) -> None: tenant = await create_tenant(db_session) await create_tenant_domain(db_session, tenant=tenant, domain="fire") await db_session.commit() with structlog.testing.capture_logs() as logs: await set_domain_status( db_sessionmaker, tenant_id=tenant.id, domain="fire", status="disabled" ) changed = _events_by_name(logs, "domain.status_changed") assert len(changed) == 1 assert changed[0]["domain"] == "fire" assert changed[0]["status"] == "disabled"