"""
test_critical_gap_false_positive_2506.py — task-2506 회귀 테스트

회장 명시 15건 (10 false-positive + 5 true-positive) 검증.
- False-positive: critical_gap fix 후 PASS여야 함
- True-positive: 기존 룰 유지 — FAIL이어야 함

수정 대상: teams/shared/verifiers/critical_gap.py
"""

import importlib.util
import sys
from pathlib import Path

# 같은 체크아웃(워크트리/PR 브랜치)의 verifier를 검증하도록 테스트 파일 기준 상대경로 사용
REPO_ROOT = Path(__file__).resolve().parents[2]
WORKSPACE = REPO_ROOT  # backward-compat alias


def _load_module(name: str, path: Path):
    spec = importlib.util.spec_from_file_location(name, path)
    if spec is None or spec.loader is None:
        raise ImportError(f"Failed to load module spec for {path}")
    module = importlib.util.module_from_spec(spec)
    sys.modules[name] = module
    spec.loader.exec_module(module)
    return module


critical_gap = _load_module(
    "critical_gap_t2506",
    WORKSPACE / "teams" / "shared" / "verifiers" / "critical_gap.py",
)


# ══════════════════════════════════════════════
# False-positive 케이스 — fix 후 PASS여야 함 (10건)
# ══════════════════════════════════════════════

def test_fp_priority_label_only(tmp_path):
    """FP1: '- 우선순위: critical' 단독 줄 → PASS (메타 정보, 이슈 아님)."""
    body = "- 우선순위: critical\n"
    report = tmp_path / "task-fp1.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-fp1", report_path=str(report))
    assert result["status"] == "PASS", (
        f"FP1 메타 우선순위 라벨이 이슈로 오인식: status={result['status']}, details={result.get('details')}"
    )


def test_fp_task_priority_bracket_header(tmp_path):
    """FP2: '## task: [CRITICAL] 20개 컨셉' 헤더 → PASS (태스크 제목 우선순위 표기, 이슈 아님)."""
    body = "## task: [CRITICAL] 20개 컨셉\n본문 내용 없음.\n"
    report = tmp_path / "task-fp2.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-fp2", report_path=str(report))
    assert result["status"] == "PASS", (
        f"FP2 태스크 헤더 우선순위 표기가 이슈로 오인식: status={result['status']}, details={result.get('details')}"
    )


def test_fp_critical_path_in_list(tmp_path):
    """FP3: '1. critical path 분석' 번호 목록 항목 → PASS (방법론 용어, 이슈 아님)."""
    body = "1. critical path 분석\n2. 일정 최적화\n"
    report = tmp_path / "task-fp3.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-fp3", report_path=str(report))
    assert result["status"] == "PASS", (
        f"FP3 critical path 방법론 용어가 이슈로 오인식: status={result['status']}, details={result.get('details')}"
    )


def test_fp_critical_chain_in_scqa(tmp_path):
    """FP4: SCQA 프레임 상황절 내 'critical chain 중복 투입 문제' → PASS (상황 묘사, 이슈 마커 아님)."""
    body = "### S 상황\n- critical chain 중복 투입 문제\n"
    report = tmp_path / "task-fp4.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-fp4", report_path=str(report))
    assert result["status"] == "PASS", (
        f"FP4 SCQA 상황절 critical chain이 이슈로 오인식: status={result['status']}, details={result.get('details')}"
    )


def test_fp_dispatch_level_critical_first_line(tmp_path):
    """FP5: 보고서 첫줄 '- 작업 레벨: critical' 단독 → PASS (디스패치 레벨 메타, 이슈 아님)."""
    body = "- 작업 레벨: critical\n작업이 완료되었습니다.\n"
    report = tmp_path / "task-fp5.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-fp5", report_path=str(report))
    assert result["status"] == "PASS", (
        f"FP5 작업 레벨 메타 라벨이 이슈로 오인식: status={result['status']}, details={result.get('details')}"
    )


def test_fp_bracket_critical_only_header(tmp_path):
    """FP6: '## [CRITICAL]' 대괄호 prefix만 있는 헤더 (이슈 본문 없음) → PASS (분류 헤더, 본문 이슈 없음)."""
    body = "## [CRITICAL]\n작업 완료.\n"
    report = tmp_path / "task-fp6.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-fp6", report_path=str(report))
    assert result["status"] == "PASS", (
        f"FP6 분류 헤더 [CRITICAL]이 미해결 이슈로 오인식: status={result['status']}, details={result.get('details')}"
    )


def test_fp_critical_thinking_phrase(tmp_path):
    """FP7: '이 작업은 critical thinking이 필요하다' 평문 문장 → PASS (일반 용어, 이슈 아님)."""
    body = "이 작업은 critical thinking이 필요하다.\n분석 완료.\n"
    report = tmp_path / "task-fp7.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-fp7", report_path=str(report))
    assert result["status"] == "PASS", (
        f"FP7 'critical thinking' 일반 문구가 이슈로 오인식: status={result['status']}, details={result.get('details')}"
    )


def test_fp_level_critical_meta(tmp_path):
    """FP8: '- level: critical' 메타 라벨 → PASS (severity와 다른 level 필드, 이슈 아님)."""
    body = "- level: critical\n작업 정상 완료.\n"
    report = tmp_path / "task-fp8.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-fp8", report_path=str(report))
    assert result["status"] == "PASS", (
        f"FP8 'level: critical' 메타 라벨이 이슈로 오인식: status={result['status']}, details={result.get('details')}"
    )


def test_fp_dispatch_level_critical_inline(tmp_path):
    """FP9: 'dispatch level critical' 명시 → PASS (디스패치 시스템 레벨 태그, 이슈 아님)."""
    body = "dispatch level critical\n모든 항목 처리 완료.\n"
    report = tmp_path / "task-fp9.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-fp9", report_path=str(report))
    assert result["status"] == "PASS", (
        f"FP9 'dispatch level critical' 태그가 이슈로 오인식: status={result['status']}, details={result.get('details')}"
    )


def test_fp_launch_priority_critical(tmp_path):
    """FP10: '발사 우선순위: critical' → PASS (발사/배포 우선순위 메타 정보, 이슈 아님)."""
    body = "발사 우선순위: critical\n배포 준비 완료.\n"
    report = tmp_path / "task-fp10.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-fp10", report_path=str(report))
    assert result["status"] == "PASS", (
        f"FP10 '발사 우선순위: critical' 메타가 이슈로 오인식: status={result['status']}, details={result.get('details')}"
    )


# ══════════════════════════════════════════════
# True-positive 케이스 — fix 후에도 FAIL이어야 함 (5건)
# ══════════════════════════════════════════════

def test_tp_critical_issue_section_with_body(tmp_path):
    """TP1: '## CRITICAL 이슈 1건 발견\\n- token 누락' → FAIL (실제 이슈 헤더 + 미해결)."""
    body = "## CRITICAL 이슈 1건 발견\n- token 누락\n"
    report = tmp_path / "task-tp1.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-tp1", report_path=str(report))
    assert result["status"] == "FAIL", (
        f"TP1 CRITICAL 이슈 헤더 + 미해결이 감지되지 않음: status={result['status']}, details={result.get('details')}"
    )


def test_tp_severity_critical_meta_in_issue_section(tmp_path):
    """TP2: '## 발견된 이슈\\n- severity: critical' → FAIL (severity는 이슈 자체를 나타냄)."""
    body = "## 발견된 이슈\n- severity: critical\n"
    report = tmp_path / "task-tp2.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-tp2", report_path=str(report))
    assert result["status"] == "FAIL", (
        f"TP2 'severity: critical' 이슈가 감지되지 않음: status={result['status']}, details={result.get('details')}"
    )


def test_tp_unresolved_in_issue_section(tmp_path):
    """TP3: '## 미해결 항목\\n- CRITICAL: 누락' → FAIL (CRITICAL 리스트 마커 + 미해결)."""
    body = "## 미해결 항목\n- CRITICAL: 누락\n"
    report = tmp_path / "task-tp3.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-tp3", report_path=str(report))
    assert result["status"] == "FAIL", (
        f"TP3 'CRITICAL: 누락' 마커가 감지되지 않음: status={result['status']}, details={result.get('details')}"
    )


def test_tp_security_critical_badge(tmp_path):
    """TP4: '## 발견된 이슈\\n- ![security-critical] 발견' → FAIL (보안 배지 마커 + 미해결)."""
    body = "## 발견된 이슈\n- ![security-critical] 발견\n"
    report = tmp_path / "task-tp4.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-tp4", report_path=str(report))
    assert result["status"] == "FAIL", (
        f"TP4 '![security-critical]' 배지가 감지되지 않음: status={result['status']}, details={result.get('details')}"
    )


def test_tp_critical_no_resolved_marker(tmp_path):
    """TP5: '## CRITICAL 이슈\\n- 보안 누락' (RESOLVED 마커 없음) → FAIL (이슈 있으나 해결 확인 없음)."""
    body = "## CRITICAL 이슈\n- 보안 누락\n"
    report = tmp_path / "task-tp5.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-tp5", report_path=str(report))
    assert result["status"] == "FAIL", (
        f"TP5 CRITICAL 이슈 + RESOLVED 없음이 감지되지 않음: status={result['status']}, details={result.get('details')}"
    )


# ══════════════════════════════════════════════
# task-2723: zero-count / negative-context 회귀 — fix 후 PASS여야 함
# ══════════════════════════════════════════════

def test_zc_high_critical_zero_count(tmp_path):
    """ZC1: '- HIGH/CRITICAL 0건' → PASS (0건 명시, 미해결 이슈 아님)."""
    body = "## 발견된 이슈\n- HIGH/CRITICAL 0건\n"
    report = tmp_path / "task-zc1.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-zc1", report_path=str(report))
    assert result["status"] == "PASS", (
        f"ZC1 '0건' 명시가 미해결 이슈로 오탐: status={result['status']}, details={result.get('details')}"
    )


def test_zc_high_critical_new_zero_count(tmp_path):
    """ZC2: '- HIGH/CRITICAL 신규 0건' → PASS (신규 0건 명시, 미해결 이슈 아님)."""
    body = "## 발견된 이슈\n- HIGH/CRITICAL 신규 0건\n"
    report = tmp_path / "task-zc2.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-zc2", report_path=str(report))
    assert result["status"] == "PASS", (
        f"ZC2 '신규 0건' 명시가 미해결 이슈로 오탐: status={result['status']}, details={result.get('details')}"
    )


def test_zc_new_high_critical_none(tmp_path):
    """ZC3: '- 신규 HIGH/CRITICAL 없음' → PASS (부재 명시, 미해결 이슈 아님)."""
    body = "## 발견된 이슈\n- 신규 HIGH/CRITICAL 없음\n"
    report = tmp_path / "task-zc3.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-zc3", report_path=str(report))
    assert result["status"] == "PASS", (
        f"ZC3 '없음' 명시가 미해결 이슈로 오탐: status={result['status']}, details={result.get('details')}"
    )


def test_zc_new_high_critical_none_formal(tmp_path):
    """ZC4: '- 신규 HIGH/CRITICAL 없습니다' → PASS (부재 명시 격식체, 미해결 이슈 아님)."""
    body = "## 발견된 이슈\n- 신규 HIGH/CRITICAL 없습니다\n"
    report = tmp_path / "task-zc4.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-zc4", report_path=str(report))
    assert result["status"] == "PASS", (
        f"ZC4 '없습니다' 명시가 미해결 이슈로 오탐: status={result['status']}, details={result.get('details')}"
    )


def test_zc_critical_equals_zero(tmp_path):
    """ZC5: '- CRITICAL=0' → PASS (0건 등식 표기, 미해결 이슈 아님)."""
    body = "## 발견된 이슈\n- CRITICAL=0\n"
    report = tmp_path / "task-zc5.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-zc5", report_path=str(report))
    assert result["status"] == "PASS", (
        f"ZC5 'CRITICAL=0' 등식이 미해결 이슈로 오탐: status={result['status']}, details={result.get('details')}"
    )


def test_zc_critical_eq_zero_spaced(tmp_path):
    """ZC6: '- CRITICAL = 0' → PASS (공백 포함 0건 등식, 미해결 이슈 아님)."""
    body = "## 발견된 이슈\n- CRITICAL = 0\n"
    report = tmp_path / "task-zc6.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-zc6", report_path=str(report))
    assert result["status"] == "PASS", (
        f"ZC6 'CRITICAL = 0' 등식이 미해결 이슈로 오탐: status={result['status']}, details={result.get('details')}"
    )


def test_zc_high_zero_critical_zero(tmp_path):
    """ZC7: '- HIGH: 0 / CRITICAL: 0' → PASS (양쪽 0건 명시, 미해결 이슈 아님).

    (round-2: narrowed ZERO_EXPR는 :/= 구분자 요구 → 콜론 표기로 보강)
    """
    body = "## 발견된 이슈\n- HIGH: 0 / CRITICAL: 0\n"
    report = tmp_path / "task-zc7.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-zc7", report_path=str(report))
    assert result["status"] == "PASS", (
        f"ZC7 'HIGH: 0 / CRITICAL: 0' 명시가 미해결 이슈로 오탐: status={result['status']}, details={result.get('details')}"
    )


def test_zc_fresh_unresolved_eq_zero(tmp_path):
    """ZC8: '- fresh unresolved HIGH/CRITICAL = 0' → PASS (0건 등식 영문, 미해결 이슈 아님)."""
    body = "## 발견된 이슈\n- fresh unresolved HIGH/CRITICAL = 0\n"
    report = tmp_path / "task-zc8.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-zc8", report_path=str(report))
    assert result["status"] == "PASS", (
        f"ZC8 'fresh unresolved ... = 0' 등식이 미해결 이슈로 오탐: status={result['status']}, details={result.get('details')}"
    )


def test_zc_no_new_high_critical_en(tmp_path):
    """ZC9: '- no new high/critical: 0건' → PASS (영문 부재 명시, 미해결 이슈 아님).

    (round-2: narrowed ZERO_EXPR는 명시적 zero 표현 요구 → '0건' 보강)
    """
    body = "## 발견된 이슈\n- no new high/critical: 0건\n"
    report = tmp_path / "task-zc9.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-zc9", report_path=str(report))
    assert result["status"] == "PASS", (
        f"ZC9 'no new high/critical: 0건' 부재 명시가 미해결 이슈로 오탐: status={result['status']}, details={result.get('details')}"
    )


def test_zc_preserve_unresolved_fix_absence(tmp_path):
    """ZC10: '- CRITICAL: SQL injection (수정 없음)' → FAIL.

    '수정 없음'은 fix 부재(=미해결 신호)이지 zero-count(이슈 0건)가 아니다.
    탐지력 보존 — 실제 미해결 critical이므로 FAIL이어야 함.
    """
    body = "## 발견된 이슈\n- CRITICAL: SQL injection (수정 없음)\n"
    report = tmp_path / "task-zc10.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-zc10", report_path=str(report))
    assert result["status"] == "FAIL", (
        f"ZC10 '수정 없음'(fix 부재=미해결)이 zero-count로 오인되어 통과: status={result['status']}, details={result.get('details')}"
    )


def test_zc_preserve_critical_badge_unresolved(tmp_path):
    """ZC11: '- ![critical] 인증 우회' → FAIL (실제 미해결 critical 배지, zero-count 아님)."""
    body = "## 발견된 이슈\n- ![critical] 인증 우회\n"
    report = tmp_path / "task-zc11.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-zc11", report_path=str(report))
    assert result["status"] == "FAIL", (
        f"ZC11 '![critical]' 미해결 배지가 zero-count로 오인되어 통과: status={result['status']}, details={result.get('details')}"
    )


# ══════════════════════════════════════════════
# task-2723+1: round-2 narrowed zero-count 가드 회귀 (12건, 회장 명시)
#  - PASS(=zero-count 인정): _is_zero_count_context True
#  - FAIL(=탐지력 유지): _is_zero_count_context False
# ══════════════════════════════════════════════

# ── PASS 케이스 (zero-count 인정 — suppress True) ──

def test_r2_pass_high_critical_zero_count():
    """R2-1: 'HIGH/CRITICAL 0건' → zero-count 인정(True)."""
    assert critical_gap._is_zero_count_context("- HIGH/CRITICAL 0건") is True


def test_r2_pass_critical_equals_zero():
    """R2-2: 'CRITICAL=0' → zero-count 인정(True)."""
    assert critical_gap._is_zero_count_context("- CRITICAL=0") is True


def test_r2_pass_fresh_unresolved_eq_zero():
    """R2-3: 'fresh unresolved HIGH/CRITICAL = 0' → zero-count 인정(True)."""
    assert critical_gap._is_zero_count_context(
        "- fresh unresolved HIGH/CRITICAL = 0"
    ) is True


def test_r2_pass_new_high_none_new_critical_none():
    """R2-4: '신규 HIGH 없음 / 신규 CRITICAL 없음' → zero-count 인정(True)."""
    assert critical_gap._is_zero_count_context(
        "- 신규 HIGH 없음 / 신규 CRITICAL 없음"
    ) is True


# ── FAIL 케이스 (탐지력 유지 — suppress 안 함, False) ──

def test_r2_fail_critical_auth_missing():
    """R2-5: '[CRITICAL] 인증 없음' → 미억제(False) — strong marker + vuln deny."""
    assert critical_gap._is_zero_count_context("[CRITICAL] 인증 없음") is False


def test_r2_fail_critical_authz_missing():
    """R2-6: '[CRITICAL] 권한 검증 없음' → 미억제(False)."""
    assert critical_gap._is_zero_count_context("[CRITICAL] 권한 검증 없음") is False


def test_r2_fail_critical_encryption_missing():
    """R2-7: '[CRITICAL] 암호화 없음' → 미억제(False)."""
    assert critical_gap._is_zero_count_context("[CRITICAL] 암호화 없음") is False


def test_r2_fail_high_secure_mode_zero():
    """R2-8: '[HIGH] secure_mode=0' → 미억제(False) — strong marker + secure_mode=0 deny."""
    assert critical_gap._is_zero_count_context("[HIGH] secure_mode=0") is False


def test_r2_fail_high_timeout_zero():
    """R2-9: '[HIGH] timeout=0' → 미억제(False) — timeout=0 deny."""
    assert critical_gap._is_zero_count_context("[HIGH] timeout=0") is False


def test_r2_fail_critical_port_zero():
    """R2-10: '[CRITICAL] port=0' → 미억제(False) — port=0 deny."""
    assert critical_gap._is_zero_count_context("[CRITICAL] port=0") is False


def test_r2_fail_critical_new_bug_no_patch():
    """R2-11: '[CRITICAL] 신규 버그, 패치 없음' → 미억제(False) — strong marker + 패치 없음 deny."""
    assert critical_gap._is_zero_count_context("[CRITICAL] 신규 버그, 패치 없음") is False


# ── #12: 탐지력 보존 — end-to-end verify() 회귀 (실제 critical 후보는 FAIL 유지) ──

def test_r2_preserve_e2e_critical_auth_missing(tmp_path):
    """R2-12a: 이슈 섹션 내 '- [CRITICAL] 인증 없음' → verify FAIL (실제 미해결 critical)."""
    body = "## 발견된 이슈\n- [CRITICAL] 인증 없음\n"
    report = tmp_path / "task-r2-12a.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-r2-12a", report_path=str(report))
    assert result["status"] == "FAIL", (
        f"R2-12a '[CRITICAL] 인증 없음'이 억제되어 통과: status={result['status']}, details={result.get('details')}"
    )


def test_r2_preserve_e2e_critical_no_patch(tmp_path):
    """R2-12b: 이슈 섹션 내 '- [CRITICAL] 신규 버그, 패치 없음' → verify FAIL."""
    body = "## 발견된 이슈\n- [CRITICAL] 신규 버그, 패치 없음\n"
    report = tmp_path / "task-r2-12b.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-r2-12b", report_path=str(report))
    assert result["status"] == "FAIL", (
        f"R2-12b '[CRITICAL] 패치 없음'이 억제되어 통과: status={result['status']}, details={result.get('details')}"
    )


def test_r2_preserve_e2e_zero_count_pass(tmp_path):
    """R2-12c: 이슈 섹션 내 '- HIGH/CRITICAL 0건' → verify PASS (zero-count 인정 보존)."""
    body = "## 발견된 이슈\n- HIGH/CRITICAL 0건\n"
    report = tmp_path / "task-r2-12c.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-r2-12c", report_path=str(report))
    assert result["status"] == "PASS", (
        f"R2-12c '0건'이 오탐: status={result['status']}, details={result.get('details')}"
    )


# ══════════════════════════════════════════════
# task-2723+2: round-3 한글 무공백 zero-count 회귀 (3건, 회장 명시)
#  - 한글은 re에서 \w라 "총0건"·"이슈없음"의 한글↔숫자/한글↔한글 경계에 \b 없음.
#  - 선행 \b → (?<!\d) 교체 + 한글어 word-boundary 제거 후 PASS 되어야 함.
#  - anchor 동반 문맥으로 구성("이슈"가 anchor): zero-expr 단독 suppress 아님.
# ══════════════════════════════════════════════

def test_r3_zc_total_zero_count_nospace(tmp_path):
    """R3-1: '- CRITICAL 총0건' → PASS (한글 무공백 '총0건' zero-count 인정)."""
    body = "## 발견된 이슈\n- CRITICAL 총0건\n"
    report = tmp_path / "task-r3-1.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-r3-1", report_path=str(report))
    assert result["status"] == "PASS", (
        f"R3-1 '총0건' 한글 무공백 zero-count가 미해결 이슈로 오탐: "
        f"status={result['status']}, details={result.get('details')}"
    )


def test_r3_zc_issue_none_nospace(tmp_path):
    """R3-2: '- 이슈없음' → PASS (한글 무공백 '이슈없음' zero-count 인정, '이슈'가 anchor)."""
    body = "## 발견된 이슈\n- 이슈없음\n"
    report = tmp_path / "task-r3-2.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-r3-2", report_path=str(report))
    assert result["status"] == "PASS", (
        f"R3-2 '이슈없음' 한글 무공백 zero-count가 미해결 이슈로 오탐: "
        f"status={result['status']}, details={result.get('details')}"
    )


def test_r3_zc_issue_none_plain_nospace(tmp_path):
    """R3-3: '- 이슈없다' → PASS (한글 무공백 '이슈없다' zero-count 인정, '이슈'가 anchor)."""
    body = "## 발견된 이슈\n- 이슈없다\n"
    report = tmp_path / "task-r3-3.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-r3-3", report_path=str(report))
    assert result["status"] == "PASS", (
        f"R3-3 '이슈없다' 한글 무공백 zero-count가 미해결 이슈로 오탐: "
        f"status={result['status']}, details={result.get('details')}"
    )


# ══════════════════════════════════════════════
# task-2723+3: severity-count 구조 파싱 redesign — mixed-count 회귀 (회장 명시)
#  - round-1~3 은 줄 전체 suppress 라 "CRITICAL: 0, HIGH: 1" 에서 HIGH 1 까지 억제했다.
#  - redesign 은 (severity, count) 쌍을 파싱하여 하나라도 >0 이면 탐지 유지(FAIL).
# ══════════════════════════════════════════════

# ── parse_severity_counts 단위 검증 ──

def test_r4_parse_mixed_colon():
    """R4-parse: 'CRITICAL: 0, HIGH: 1' → [0, 1] (각 severity 별 count)."""
    assert critical_gap.parse_severity_counts("CRITICAL: 0, HIGH: 1") == [0, 1]


def test_r4_parse_mixed_counter_unit():
    """R4-parse: 'CRITICAL 0개, HIGH 2개' → [0, 2]."""
    assert critical_gap.parse_severity_counts("CRITICAL 0개, HIGH 2개") == [0, 2]


def test_r4_parse_group_single_zero():
    """R4-parse: 'HIGH/CRITICAL 0건' → [0] (슬래시 결합 그룹은 단일 count)."""
    assert critical_gap.parse_severity_counts("HIGH/CRITICAL 0건") == [0]


# ── mixed-count: _is_zero_count_context 미억제(False) ──

def test_r4_mixed_zero_one_not_suppressed():
    """R4-1: 'CRITICAL: 0, HIGH: 1' → False (HIGH 1 탐지, suppress 금지)."""
    assert critical_gap._is_zero_count_context("CRITICAL: 0, HIGH: 1") is False


def test_r4_mixed_zero_two_not_suppressed():
    """R4-2: 'CRITICAL 0개, HIGH 2개' → False (HIGH 2 탐지, suppress 금지)."""
    assert critical_gap._is_zero_count_context("CRITICAL 0개, HIGH 2개") is False


# ── all-zero: _is_zero_count_context 억제(True) 보존 ──

def test_r4_all_zero_suppressed():
    """R4-3: 'HIGH: 0 / CRITICAL: 0' → True (모든 count 0 → 억제 보존)."""
    assert critical_gap._is_zero_count_context("HIGH: 0 / CRITICAL: 0") is True


# ── mixed-count: end-to-end verify() FAIL (이슈 섹션 내) ──

def test_r4_e2e_mixed_zero_one_fail(tmp_path):
    """R4-4: 이슈 섹션 내 '- CRITICAL: 0, HIGH: 1' → verify FAIL (HIGH 1 미해결 탐지)."""
    body = "## 발견된 이슈\n- CRITICAL: 0, HIGH: 1\n"
    report = tmp_path / "task-r4-4.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-r4-4", report_path=str(report))
    assert result["status"] == "FAIL", (
        f"R4-4 mixed-count 'HIGH: 1'이 억제되어 통과: status={result['status']}, details={result.get('details')}"
    )


def test_r4_e2e_mixed_zero_two_fail(tmp_path):
    """R4-5: 이슈 섹션 내 '- CRITICAL 0개, HIGH 2개' → verify FAIL (HIGH 2 미해결 탐지)."""
    body = "## 발견된 이슈\n- CRITICAL 0개, HIGH 2개\n"
    report = tmp_path / "task-r4-5.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-r4-5", report_path=str(report))
    assert result["status"] == "FAIL", (
        f"R4-5 mixed-count 'HIGH: 2개'가 억제되어 통과: status={result['status']}, details={result.get('details')}"
    )


def test_r4_e2e_all_zero_pass(tmp_path):
    """R4-6: 이슈 섹션 내 '- HIGH/CRITICAL 0건' → verify PASS (all-zero 억제 보존)."""
    body = "## 발견된 이슈\n- HIGH/CRITICAL 0건\n"
    report = tmp_path / "task-r4-6.md"
    report.write_text(body, encoding="utf-8")
    result = critical_gap.verify("task-r4-6", report_path=str(report))
    assert result["status"] == "PASS", (
        f"R4-6 'HIGH/CRITICAL 0건' all-zero가 오탐: status={result['status']}, details={result.get('details')}"
    )
