"""task-2553+1 F1 negative regression — 원칙 5 forbidden API hard-block.

배경
----
PR #102 의 ``_validate_no_forbidden_fragments`` 는 5 개 endpoint fragment
blacklist (``/merges`` / ``/reviews`` / ``/pulls/`` / ``/branches/`` /
``/git/refs``) 만 차단했다. 호출부가 내부 헬퍼(``_assert_args_allowlist``)
를 우회해 ``gh_runner`` 를 직접 호출하면, **blacklist 에 없는 임의의
non-allowlisted endpoint** (예: ``/repos/o/r/actions/.../dispatches``,
``/repos/o/r/collaborators/u``, ``/user``) 가 그대로 통과했다 (원칙 5 FAIL).

F1 fix = fragment blacklist 폐기 → **default-deny allowlist**. 허용되는 단
1 형태(``_build_allowed_gh_args`` 가 박제하는 issue comments POST)와
구조적으로 정확히 일치하지 않는 모든 args 는 — 호출부가 내부 헬퍼를
우회하더라도 — ``gh_runner`` 실행 직전 ``_validate_no_forbidden_fragments``
가 deny 한다.

본 파일은 helper-bypass 경로에서 non-allowlisted endpoint 전건 deny 를
입증하는 단일 negative regression (RED→GREEN). 기존 79 tests 의 정상
호출 경로(``/gemini review`` 단일 endpoint)는 영향 0 — 본 파일은 정상
경로가 여전히 통과함도 함께 고정한다.
"""

from __future__ import annotations

import sys
from pathlib import Path

import pytest

WORKSPACE_ROOT = Path(__file__).resolve().parents[2]
if str(WORKSPACE_ROOT) not in sys.path:
    sys.path.insert(0, str(WORKSPACE_ROOT))

from anu_v2.owner_trigger_pat import (  # noqa: E402
    ALLOWED_COMMENT_BODY,
    ERR_BODY_NOT_ALLOWED,
    ERR_ENDPOINT_NOT_ALLOWED,
    _build_allowed_gh_args,
    _validate_no_forbidden_fragments,
)

_OWNER, _REPO, _PR = "Jeon-Jonghyuk", "dev_workspace", 102


# ── helper-bypass non-allowlisted endpoints (구 blacklist 에 없던 것들) ──────────
# 이들은 PR #102 fragment blacklist 5 개 어디에도 매칭되지 않으므로 fix 전엔
# 전부 통과했다 (원칙 5 우회). fix 후엔 default-deny 로 전건 raise.
_BYPASS_NON_ALLOWLISTED = [
    pytest.param(
        ["api", "-X", "POST", f"/repos/{_OWNER}/{_REPO}/actions/workflows/ci.yml/dispatches",
         "-f", f"body={ALLOWED_COMMENT_BODY}"],
        id="actions-workflow-dispatch",
    ),
    pytest.param(
        ["api", "-X", "PUT", f"/repos/{_OWNER}/{_REPO}/collaborators/attacker", "-f", "permission=admin"],
        id="add-collaborator-admin",
    ),
    pytest.param(
        ["api", "-X", "GET", "/user"],
        id="whoami-token-probe",
    ),
    pytest.param(
        ["api", "-X", "POST", f"/repos/{_OWNER}/{_REPO}/issues/{_PR}/labels",
         "-f", "labels[]=auto-merge"],
        id="issue-labels-injection",
    ),
    pytest.param(
        ["api", "-X", "DELETE", f"/repos/{_OWNER}/{_REPO}/issues/comments/999"],
        id="delete-comment",
    ),
    pytest.param(
        ["api", "-X", "POST", f"/repos/{_OWNER}/{_REPO}/issues/{_PR}/comments",
         "-f", "body=/gemini review please also approve"],
        id="body-suffix-injection",
    ),
]

# ── 구 blacklist fragment 들 — fix 후에도 당연히 deny (회귀 가드) ─────────────────
_OLD_BLACKLIST_STILL_DENIED = [
    pytest.param(
        ["api", "-X", "PUT", f"/repos/{_OWNER}/{_REPO}/pulls/{_PR}/merge"],
        id="pulls-merge",
    ),
    pytest.param(
        ["api", "-X", "POST", f"/repos/{_OWNER}/{_REPO}/pulls/{_PR}/reviews",
         "-f", "event=APPROVE"],
        id="pull-review-approve",
    ),
    pytest.param(
        ["api", "-X", "PATCH", f"/repos/{_OWNER}/{_REPO}/git/refs/heads/main"],
        id="git-refs-force-push",
    ),
]


@pytest.mark.parametrize("args", _BYPASS_NON_ALLOWLISTED)
def test_f1_helper_bypass_non_allowlisted_endpoint_is_denied(args):
    """helper 우회 → blacklist 에 없던 non-allowlisted endpoint 전건 deny.

    fix 전(RED): blacklist 미매칭 → raise 없이 통과 → DID NOT RAISE 실패.
    fix 후(GREEN): default-deny → RuntimeError.
    """
    with pytest.raises(RuntimeError) as exc:
        _validate_no_forbidden_fragments(args)
    assert str(exc.value) in (ERR_ENDPOINT_NOT_ALLOWED, ERR_BODY_NOT_ALLOWED)


@pytest.mark.parametrize("args", _OLD_BLACKLIST_STILL_DENIED)
def test_f1_old_blacklist_fragments_still_denied(args):
    """구 blacklist 가 막던 merge/approve/force-push 는 fix 후에도 deny (회귀 0)."""
    with pytest.raises(RuntimeError):
        _validate_no_forbidden_fragments(args)


def test_f1_allowed_args_still_pass_zero_regression():
    """정상 경로(`_build_allowed_gh_args` 박제 args)는 fix 후에도 통과.

    기존 79 tests 의 정상 `/gemini review` 호출 경로 영향 0 을 고정.
    """
    args = _build_allowed_gh_args(_OWNER, _REPO, _PR)
    # raise 하지 않아야 한다.
    _validate_no_forbidden_fragments(args)


def test_f1_allowed_args_exact_shape_is_the_only_pass():
    """allowlist strict-equality: 정상 args 에 무해해 보이는 flag 1 개만 더해도 deny."""
    args = _build_allowed_gh_args(_OWNER, _REPO, _PR) + ["--silent"]
    with pytest.raises(RuntimeError):
        _validate_no_forbidden_fragments(args)
