"""RA-01: `tenant_session` runtime-checks the transaction-state
contract.

`SET LOCAL` only takes effect inside a transaction. SQLAlchemy 2.x
AsyncSession auto-begins one on the first execute(), so the
SET LOCAL statement itself starts the transaction. The runtime
check below catches the case where that assumption breaks — a
future SQLAlchemy major that disables autobegin by default, or a
consumer's session config that turns it off explicitly.

Without the check, a tenant_session call on a session-not-in-
transaction silently has no effect AND tenant scoping is bypassed
— a security-relevant failure mode. The runtime check turns the
silent failure into a loud RuntimeError.

(RA-01 in mgf-sqlalchemy's RELIABILITY_AND_ACCURACY tracker.)
"""

from __future__ import annotations

from unittest.mock import AsyncMock, MagicMock

import pytest

from mgf.sqlalchemy import tenant_session

_TENANT_ID = "11111111-2222-3333-4444-555555555555"


class TestTransactionStateContract:
    """RA-01: tenant_session raises RuntimeError if the session is
    not in a transaction after SET LOCAL — the autobegin contract."""

    @pytest.mark.asyncio
    async def test_session_in_transaction_passes(self) -> None:
        """Happy path: session.in_transaction() returns True (SQLA's
        autobegin worked). The yield runs normally."""
        session = MagicMock()
        session.execute = AsyncMock(return_value=None)
        session.in_transaction = MagicMock(return_value=True)

        async with tenant_session(session, _TENANT_ID):
            pass

        session.execute.assert_called_once()
        session.in_transaction.assert_called_once()

    @pytest.mark.asyncio
    async def test_session_not_in_transaction_raises(self) -> None:
        """Defensive path: session.in_transaction() returns False
        (autobegin disabled, or a future SQLA change). Raise
        RuntimeError loudly so the consumer sees the bypass,
        rather than silently proceeding with no tenant scoping."""
        session = MagicMock()
        session.execute = AsyncMock(return_value=None)
        session.in_transaction = MagicMock(return_value=False)

        with pytest.raises(RuntimeError, match="not in a transaction"):
            async with tenant_session(session, _TENANT_ID):
                pass

        # SET LOCAL did execute — the check fires AFTER, on the
        # "verify autobegin worked" pass.
        session.execute.assert_called_once()

    @pytest.mark.asyncio
    async def test_runtime_error_message_names_ra_01(self) -> None:
        """The error message cross-references the tracker entry so
        a future maintainer hitting this in production can find the
        context."""
        session = MagicMock()
        session.execute = AsyncMock(return_value=None)
        session.in_transaction = MagicMock(return_value=False)

        with pytest.raises(RuntimeError) as exc_info:
            async with tenant_session(session, _TENANT_ID):
                pass

        msg = str(exc_info.value)
        assert "RA-01" in msg
        assert "SET LOCAL" in msg
        # And points at the actionable workaround.
        assert "session.begin" in msg
