# -*- coding: utf-8 -*-
"""task-2729+18 GOAL_GATE_PLACEHOLDER_HARDENING (Option A REPLACEMENT) — regression (16).

대상: scripts/finish-task.sh GOAL-GATE 실행부 (회장 2026-06-08 승인, ACTIVE=false).
  - placeholder/템플릿 잔재 skip (토큰 한정 <[A-Za-z0-9_.-]+> + 미확장 $VAR/${VAR} + 리터럴 ...)
  - ★ 정상 shell redirection (command < in > out) 은 placeholder 아님 → 정상 실행
  - set +e/-e 격리 ($- errexit 저장·복원) → caller errexit 누수 0
  - timeout fail-closed
  - 4-state: PASS / SKIP(GOAL_ASSERTION_PLACEHOLDER_SKIPPED) / TIMEOUT / FAIL

절대 제약 (isolated):
  - 모든 동적 검증은 GOAL_GATE_LIB_MODE=1 로 finish-task.sh 의 goal-gate 함수만 source.
    (라이브러리 가드가 본문 실행 전 return → 실제 머지/spawn/systemd/canonical 변경 0)
  - 임시 파일은 pytest tmp_path 만. canonical(/home/jay/workspace/memory/**) 쓰기 0.
  - ANU key 완성 literal 0건 — 검증용으로만 분할 조합("c119085" + "addb0f8b7").

회장 verbatim 16 항목 1:1 매핑.
"""
from __future__ import annotations

import os
import subprocess
import time
from pathlib import Path

_ROOT = Path(__file__).resolve().parents[2]
_FINISH = _ROOT / "scripts" / "finish-task.sh"


def _run(snippet: str, env_extra: dict | None = None, timeout: float = 30.0):
    """GOAL_GATE_LIB_MODE=1 로 finish-task.sh 함수만 source 후 snippet 실행."""
    env = dict(os.environ)
    env["GOAL_GATE_LIB_MODE"] = "1"
    if env_extra:
        env.update(env_extra)
    full = f'source "{_FINISH}"\nset +e\n{snippet}\n'
    p = subprocess.run(
        ["bash", "-c", full],
        capture_output=True, text=True, env=env, timeout=timeout,
    )
    return p.stdout.strip(), p.stderr.strip(), p.returncode


def _evaluate(task_md: Path, env_extra: dict | None = None, timeout: float = 30.0):
    out, _err, _rc = _run(f'goal_gate_evaluate "{task_md}"', env_extra, timeout)
    return out


def _write_md(tmp_path: Path, name: str, body: str) -> Path:
    p = tmp_path / name
    p.write_text(body, encoding="utf-8")
    return p


def _hardening_region() -> str:
    """finish-task.sh 에서 task-2729+18 하드닝 추가 블록만 추출 (doctrine 정적 검사용)."""
    text = _FINISH.read_text(encoding="utf-8")
    start = text.find("task-2729+18: GOAL-GATE placeholder hardening")
    assert start != -1, "하드닝 배너 누락"
    # 블록 종료: 하드닝 배너 이후 첫 WORKSPACE= 대입(라이브러리 가드 직후)
    end = text.find('WORKSPACE="${FINISH_TASK_WORKSPACE_OVERRIDE', start)
    assert end != -1
    return text[start:end]


# ──────────────────────────────────────────────────────────────────────────
# #1 정상 redirection assertion 실행됨 (skip 아님 → PASS)
# ──────────────────────────────────────────────────────────────────────────
def test_01_normal_redirection_executes(tmp_path):
    (tmp_path / "bar.txt").write_text("foo\n", encoding="utf-8")
    out = tmp_path / "o.txt"
    md = _write_md(
        tmp_path, "pass.md",
        f"## goal_assertions\n- `grep -q foo {tmp_path}/bar.txt > {out}`\n",
    )
    assert _evaluate(md) == "PASS"
    # 입력 redirection(공백 포함) 도 placeholder 아님
    so, _e, _rc = _run('goal_assertion_is_placeholder "cat < in.txt > out.txt"; echo RC=$?')
    assert "RC=1" in so  # 1 = not placeholder


# ──────────────────────────────────────────────────────────────────────────
# #2 <...> 템플릿 placeholder skip
# ──────────────────────────────────────────────────────────────────────────
def test_02_angle_template_token_skipped(tmp_path):
    for cmd in ("grep <foo> x", "curl <url>", "grep <TASK_ID> y"):
        so, _e, _rc = _run(f'goal_assertion_is_placeholder "{cmd}"; echo RC=$?')
        assert "RC=0" in so, f"{cmd} should be placeholder"
    md = _write_md(
        tmp_path, "tpl.md",
        "## goal_assertions\n- `grep <foo> x`\n- `curl <url>`\n",
    )
    assert _evaluate(md) == "SKIP"


# ──────────────────────────────────────────────────────────────────────────
# #3 미확장 $VAR / ${VAR} skip
# ──────────────────────────────────────────────────────────────────────────
def test_03_unexpanded_var_skipped(tmp_path):
    for cmd in ("python3 $QC_SCRIPT --gate", "grep ${TASK_ID} x"):
        so, _e, _rc = _run(f"goal_assertion_is_placeholder '{cmd}'; echo RC=$?")
        assert "RC=0" in so, f"{cmd} should be placeholder"
    md = _write_md(
        tmp_path, "var.md",
        "## goal_assertions\n- `python3 $QC_SCRIPT --gate --task-id foo`\n",
    )
    assert _evaluate(md) == "SKIP"


# ──────────────────────────────────────────────────────────────────────────
# #4 리터럴 ... 포함 skip
# ──────────────────────────────────────────────────────────────────────────
def test_04_literal_ellipsis_skipped(tmp_path):
    so, _e, _rc = _run("goal_assertion_is_placeholder 'python3 x --task-id ...'; echo RC=$?")
    assert "RC=0" in so
    md = _write_md(
        tmp_path, "ell.md",
        "## goal_assertions\n- `python3 run.py --task-id ...`\n",
    )
    assert _evaluate(md) == "SKIP"


# ──────────────────────────────────────────────────────────────────────────
# #5 정상 pass 실행 (allowed 명령 성공 → PASS)
# ──────────────────────────────────────────────────────────────────────────
def test_05_normal_pass(tmp_path):
    md = _write_md(
        tmp_path, "ok.md",
        "## goal_assertions\n- `python3 -c \"import sys; sys.exit(0)\"`\n",
    )
    assert _evaluate(md) == "PASS"


# ──────────────────────────────────────────────────────────────────────────
# #6 실패 assertion fail-closed (FAIL)
# ──────────────────────────────────────────────────────────────────────────
def test_06_fail_closed(tmp_path):
    (tmp_path / "bar.txt").write_text("foo\n", encoding="utf-8")
    md = _write_md(
        tmp_path, "fail.md",
        f"## goal_assertions\n- `grep -q NOPE {tmp_path}/bar.txt`\n",
    )
    assert _evaluate(md) == "FAIL"


# ──────────────────────────────────────────────────────────────────────────
# #7 timeout 명령 timeout 후 fail-closed (TIMEOUT)
# ──────────────────────────────────────────────────────────────────────────
def test_07_timeout_fail_closed(tmp_path):
    md = _write_md(
        tmp_path, "to.md",
        "## goal_assertions\n- `python3 -c \"import time; time.sleep(10)\"`\n",
    )
    t0 = time.time()
    state = _evaluate(md, env_extra={"GOAL_CMD_TIMEOUT": "1"}, timeout=15.0)
    elapsed = time.time() - t0
    assert state == "TIMEOUT"
    assert elapsed < 8.0, f"timeout guard 미작동(elapsed={elapsed:.1f}s)"


# ──────────────────────────────────────────────────────────────────────────
# #8 placeholder skip 이 callback/finalize hang 안 시킴 (빠르게 SKIP, exit 0)
# ──────────────────────────────────────────────────────────────────────────
def test_08_skip_does_not_hang(tmp_path):
    md = _write_md(
        tmp_path, "skip.md",
        "## goal_assertions\n"
        "- `python3 $QC_SCRIPT --gate --task-id ...`\n"
        "- `grep <foo> x`\n",
    )
    t0 = time.time()
    out, _e, rc = _run(f'goal_gate_evaluate "{md}"; echo "RC=$?"', timeout=15.0)
    elapsed = time.time() - t0
    assert "SKIP" in out
    assert "RC=0" in out  # 함수는 BLOCK/exit 안 함 (호출부 책임)
    assert elapsed < 8.0


# ──────────────────────────────────────────────────────────────────────────
# #9 goal_assertions 없으면 기존 동작 유지 (PASS, 비차단)
# ──────────────────────────────────────────────────────────────────────────
def test_09_no_goal_assertions_backward_compat(tmp_path):
    md = _write_md(tmp_path, "none.md", "## other\nnothing\n")
    assert _evaluate(md) == "PASS"


# ──────────────────────────────────────────────────────────────────────────
# #10 set 격리 (함수 후 caller errexit 무변경)
# ──────────────────────────────────────────────────────────────────────────
def test_10_set_isolation_errexit_preserved(tmp_path):
    # caller errexit ON → 성공/실패 모두 보존
    snippet = (
        "set -e\n"
        'goal_assertion_exec_isolated "true"  || true\n'
        'case "$-" in *e*) echo OK1;; *) echo LEAK1;; esac\n'
        'goal_assertion_exec_isolated "false" || true\n'
        'case "$-" in *e*) echo OK2;; *) echo LEAK2;; esac\n'
    )
    out, _e, _rc = _run(snippet)
    assert "OK1" in out and "OK2" in out
    assert "LEAK" not in out
    # caller errexit OFF → spurious enable 금지
    snippet2 = (
        "set +e\n"
        'goal_assertion_exec_isolated "false"\n'
        'case "$-" in *e*) echo SPURIOUS;; *) echo CLEAN;; esac\n'
    )
    out2, _e2, _rc2 = _run(snippet2)
    assert "CLEAN" in out2 and "SPURIOUS" not in out2


# ──────────────────────────────────────────────────────────────────────────
# #11 bash -n PASS
# ──────────────────────────────────────────────────────────────────────────
def test_11_bash_n_syntax():
    p = subprocess.run(["bash", "-n", str(_FINISH)], capture_output=True, text=True)
    assert p.returncode == 0, p.stderr


# ──────────────────────────────────────────────────────────────────────────
# #12 raw key 0 (ANU key 완성 literal 부재)
# ──────────────────────────────────────────────────────────────────────────
def test_12_no_raw_key_literal():
    anu_key = "c119085" + "addb0f8b7"  # 분할 조합 — 완성 literal 미작성
    finish_text = _FINISH.read_text(encoding="utf-8")
    test_text = Path(__file__).read_text(encoding="utf-8")
    assert anu_key not in finish_text
    assert anu_key not in test_text  # 본 테스트 파일도 완성 literal 0


# ──────────────────────────────────────────────────────────────────────────
# #13 ACTIVE=false (하드닝 블록이 production ACTIVE 선언 안 함)
# ──────────────────────────────────────────────────────────────────────────
def test_13_active_false():
    region = _hardening_region()
    assert "ACTIVE=false" in region
    assert "ACTIVE=true" not in region


# ──────────────────────────────────────────────────────────────────────────
# #14 systemctl enable 0
# ──────────────────────────────────────────────────────────────────────────
def test_14_no_systemctl_enable():
    region = _hardening_region()
    assert "systemctl" not in region
    assert "systemctl enable" not in _FINISH.read_text(encoding="utf-8")


# ──────────────────────────────────────────────────────────────────────────
# #15 activation_epoch absent / real spawn 0
# ──────────────────────────────────────────────────────────────────────────
def test_15_no_activation_epoch_no_spawn():
    region = _hardening_region()
    assert "activation_epoch" not in region
    for forbidden in ("anu wake", "real spawn", "spawn_real", "subprocess.Popen"):
        assert forbidden not in region


# ──────────────────────────────────────────────────────────────────────────
# #16 canonical task-2716 branch·live memory artifacts 무손상
#     (하드닝 블록이 canonical-mutation/destructive verb 미포함 + TASK_FILE 외 memory write 0)
# ──────────────────────────────────────────────────────────────────────────
def test_16_no_canonical_mutation():
    region = _hardening_region()
    for verb in ("reset --hard", "checkout -f", "stash -u", "git clean", "rm -rf"):
        assert verb not in region, f"destructive verb leaked: {verb}"
    # 하드닝 블록은 TASK_FILE(읽기) 만 다룸 — canonical memory 경로 직접 쓰기 0
    assert "/memory/events" not in region
    assert "git reset" not in region
