"""Testes do contrato de saída do rail OOS.

O `label` ("IN_SCOPE"/"OUT_OF_SCOPE") saiu do prompt em 2026-08-05: nenhum
consumidor de produção o lia (a decisão usa `allowed` + `code`, fixado no
pipeline) e ele era gerado a cada turno. O `reason` passou a vir VAZIO quando
`allowed=true`, como no FRASEOLOGIA — descrever por que algo está DENTRO do
escopo não é lido por ninguém.
"""
from __future__ import annotations

import os

import pytest

os.environ.setdefault("USE_MOCK_LLM", "true")

from agent_framework.guardrails.calibrated.llm_rails import out_of_scope
from agent_framework.guardrails.calibrated.prompts.out_of_scope import build_oos_prompt


@pytest.fixture(autouse=True)
def _force_mock(monkeypatch):
    monkeypatch.setenv("USE_MOCK_LLM", "true")


def test_prompt_oos_nao_pede_mais_label():
    prompt = build_oos_prompt("Posso ajudar com sua fatura?")
    assert '"label"' not in prompt
    assert "IN_SCOPE" not in prompt.split("Responda APENAS JSON valido:")[-1]


def test_prompt_oos_exige_reason_vazio_quando_allowed():
    prompt = build_oos_prompt("Posso ajudar com sua fatura?")
    contrato = prompt.split("Responda APENAS JSON valido:")[-1]
    assert "se allowed=true: string vazia" in contrato
    # e os exemplos de IN_SCOPE seguem a mesma regra
    assert '{"allowed": true, "reason": ""}' in prompt


def test_oos_rail_devolve_shape_esperado():
    r = out_of_scope("Posso te ajudar com mais alguma dúvida sobre a fatura?")
    assert r.code == "OOS"
    assert r.mechanism == "llm_supervisor"
    assert isinstance(r.allowed, bool)
