"""감사 도구가 **바깥으로 못 나가는지** — 실제 유료 사고 뒤에 세운 문.

★2026-08-31: `assess_subjects` 가 **함수 안에서** `call_structured` 를
import 하는데 모듈 속성만 갈아 끼웠고, 가로채기가 빗나가 **gemini 15콜이
실제로 나갔다**(Opik `op:audit` 15건 · prompt 12,707 · completion 5,093).
그때 나는 「유료 0」이라고 보고했다.

★**가로채기는 빗나갈 수 있다**를 전제로 짓는다. `seal_outbound` 가 마지막
문이고, 이 파일이 그 문이 실제로 잠기는지 본다 — 「막힌 것을 봤다」는
positive evidence 이지 **회귀 보호가 아니다**(Codex).
"""
import subprocess
import sys
from pathlib import Path

import pytest

TOOL = Path("tools/grounding_audit/call_payload_table.py")


def _run(body: str):
    """★별도 프로세스에서 돌린다 — 이 시험이 제 프로세스의 socket 을
    영구히 세우면 뒤 시험이 통째로 죽는다."""
    return subprocess.run([sys.executable, "-c", body], capture_output=True,
                          text=True, timeout=60)


_PRELUDE = f"""
import sys
sys.path.insert(0, {str(Path('.').resolve())!r})
sys.path.insert(0, {str(Path('tools/grounding_audit').resolve())!r})
import importlib.util
spec = importlib.util.spec_from_file_location("cpt", {str(TOOL.resolve())!r})
m = importlib.util.module_from_spec(spec)
spec.loader.exec_module(m)
m.seal_outbound()
"""


class TestTheOutermostDoorIsLocked:
    def test_the_tool_has_the_guard(self):
        src = TOOL.read_text(encoding="utf-8")
        assert "def seal_outbound" in src
        assert "seal_outbound()" in src, "★정의만 하고 안 부르면 문이 아니다"

    def test_it_is_called_after_imports(self):
        """★import 때 litellm 가격표 GET 이 난다 — 그 전에 잠그면 도구가 죽는다."""
        src = TOOL.read_text(encoding="utf-8")
        i = src.index("    seal_outbound()")
        assert "from app.core.config import settings" in src[:i]

    @pytest.mark.parametrize("what,body", [
        ("http", "import http.client;"
                 "http.client.HTTPConnection('x').request('GET','/')"),
        ("https", "import http.client;"
                  "http.client.HTTPSConnection('x').request('GET','/')"),
        ("socket", "import socket;"
                   "socket.socket().connect(('127.0.0.1', 9))"),
    ])
    def test_each_boundary_refuses(self, what, body):
        """★세 경계를 **각각** 본다. 하나만 막고 「막았다」로 쓰면 그 하나가
        아닌 길로 나간다.

        ★★**이 시험이 못 잡는 것 하나** — `HTTPSConnection` 은 `request` 를
        `HTTPConnection` 에서 **상속**한다. 그래서 도구에서 HTTPS 줄만 빼도
        이 시험은 초록이다(되돌려 보고 확인했다: 0 failed). HTTP 줄과 socket
        줄은 각각 빼면 1건씩 빨개진다.

        그 줄을 남겨 두는 이유는 **의도를 코드에 적어 두려고**다. 실제로
        막는 것은 부모 쪽과 socket 이다.
        """
        r = _run(_PRELUDE + body)
        assert r.returncode != 0, f"★{what} 가 안 막혔다"
        assert "감사 도구는 바깥으로 못 나간다" in (r.stderr + r.stdout), \
            f"★{what} 가 다른 이유로 죽었다 — 문이 아니다\n{r.stderr[-400:]}"

    def test_without_the_guard_the_call_is_not_refused(self):
        """★★**대조** — 문을 안 걸면 같은 코드가 이 오류를 안 낸다.

        안 그러면 「원래 못 붙는 주소라 실패한 것」을 「문이 막았다」로
        읽는다.
        """
        body = _PRELUDE.replace("m.seal_outbound()", "") + \
            "import socket;\nsocket.socket().connect(('127.0.0.1', 9))"
        r = _run(body)
        assert "감사 도구는 바깥으로 못 나간다" not in (r.stderr + r.stdout)
