"""task-2510 회귀 테스트 — replacement_pr_runner 12 케이스.
QA 담당: 모리건(Morrigan)
대상: utils/replacement_pr_runner.py
"""
from __future__ import annotations

import json
import subprocess
import sys
from dataclasses import asdict
from pathlib import Path

import pytest

WORKSPACE = Path(__file__).resolve().parent.parent.parent
if str(WORKSPACE) in sys.path:
    sys.path.remove(str(WORKSPACE))
sys.path.insert(0, str(WORKSPACE))

from utils.replacement_pr_runner import (  # noqa: E402  # pyright: ignore[reportMissingImports]
    ReplacementPRRunner,
    assert_no_cherry_pick,
    detect_contamination,
    transplant_expected_files,
    assert_clean_working_tree,
    precheck_local_replacement_diff,
)
from utils.automation_contracts import (  # noqa: E402  # pyright: ignore[reportMissingImports]
    ReplacementResult,
    CriticalEscalationType,
)
from utils.merge_queue_executor import (  # noqa: E402  # pyright: ignore[reportMissingImports]
    TaskSpec,
    assert_no_forbidden_git_flags,
)


# ─── helpers ──────────────────────────────────────────────────────────────────

def cp(returncode=0, stdout="", stderr=""):
    return subprocess.CompletedProcess(args=[], returncode=returncode, stdout=stdout, stderr=stderr)


def make_runner(returns_by_args=None, *, default_returncode=0, default_stdout=""):
    """fake runner — args 패턴 매칭으로 응답 inject."""
    calls = []
    routes = list(returns_by_args.items()) if returns_by_args else []

    def runner(args, cwd=None, timeout=60):
        calls.append({"args": list(args), "cwd": cwd, "timeout": timeout})
        joined = " ".join(str(a) for a in args)
        for tokens, response in routes:
            if all(tok in joined for tok in tokens):
                return response
        return cp(returncode=default_returncode, stdout=default_stdout)

    setattr(runner, "calls", calls)
    return runner


def make_spec(task_id="task-2510", expected=None, dependency=None) -> TaskSpec:
    return TaskSpec(
        task_id=task_id,
        expected_files=expected or [
            "utils/replacement_pr_runner.py",
            "tests/regression/test_replacement_pr_runner_2510.py",
        ],
        risk_area="replacement_pr",
        dependency=dependency or [],
        parallel_policy="serial_only",
        merge_queue_position=7,
        stale_recheck_required=True,
        cherry_pick_allowed=False,
    )


def fake_pr_meta(head_ref="task/task-2507-dev5", head_sha="abcdef1", task_id="task-2507", files=None, **_extra):
    """fake pr metadata. **_extra: positional pr_number 등 호출자 ergonomics 흡수."""
    return {
        "head_ref": head_ref,
        "head_sha": head_sha,
        "base_ref": "main",
        "task_id": task_id,
        "files": files or [],
        "title": f"[{task_id}] test fixture",
        "number": _extra.get("pr_number", 60),
    }


# ─── T01 clean PR → no-op ─────────────────────────────────────────────────
def test_t01_clean_pr_no_op(monkeypatch):
    spec = make_spec()
    expected = list(spec.expected_files)
    runner = make_runner({})
    rpr = ReplacementPRRunner(runner=runner, dry_run=False)

    import utils.replacement_pr_runner as mod  # pyright: ignore[reportMissingImports]
    monkeypatch.setattr(mod, "fetch_pr_metadata", lambda pr, r: fake_pr_meta(files=expected))
    monkeypatch.setattr(mod, "compute_effective_diff", lambda meta, r: list(expected))

    result = rpr.execute(60, task_spec=spec)
    assert isinstance(result, ReplacementResult)
    assert result.success is True
    assert result.replacement_pr is None
    assert result.original_pr_preserved is True
    assert sorted(result.effective_diff_files) == sorted(expected)
    assert result.forbidden_paths == []
    assert result.failure_reason is None


# ─── T02 contaminated detection ───────────────────────────────────────────
def test_t02_contaminated_detection():
    expected = ["utils/replacement_pr_runner.py", "tests/regression/test_replacement_pr_runner_2510.py"]
    contaminated_extra = expected + ["utils/rogue_extra_module.py", "scripts/unrelated_script.sh"]
    result = detect_contamination(contaminated_extra, expected)
    assert result["contaminated"] is True
    assert "utils/rogue_extra_module.py" in result["extra"]
    assert "scripts/unrelated_script.sh" in result["extra"]


# ─── T03 forbidden path → Critical FORBIDDEN_PATH_INTRUSION ──────────────
def test_t03_forbidden_path_intrusion(monkeypatch):
    spec = make_spec()
    expected = list(spec.expected_files)
    forbidden_file = ".github/workflows/ci.yml"

    runner = make_runner({})
    rpr = ReplacementPRRunner(runner=runner, dry_run=False)

    import utils.replacement_pr_runner as mod  # pyright: ignore[reportMissingImports]
    monkeypatch.setattr(mod, "fetch_pr_metadata", lambda pr, r: fake_pr_meta(files=expected + [forbidden_file]))
    monkeypatch.setattr(mod, "compute_effective_diff", lambda meta, r: expected + [forbidden_file])

    result = rpr.execute(60, task_spec=spec)
    assert result.success is False
    assert result.failure_reason is not None
    assert CriticalEscalationType.FORBIDDEN_PATH_INTRUSION.value in result.failure_reason
    assert forbidden_file in result.forbidden_paths


# ─── T04 transplant: expected_files만 git show로 이식 (★ tmp_path 격리) ──
def test_t04_transplant_expected_files_uses_git_show(tmp_path):
    """★ tmp_path 격리: 실제 source 손상 방지."""
    expected = ["utils/sample.py", "tests/regression/test_sample.py"]
    head_sha = "abcdef1234"
    runner = make_runner(
        {("git", "show"): cp(returncode=0, stdout="# fake transplanted content\n")},
        default_returncode=0,
    )

    transplant_expected_files(
        expected,
        head_sha,
        runner,
        repo_dir=str(tmp_path),  # ★ tmp_path 격리
    )

    # git show 호출 확인
    runner_calls = getattr(runner, "calls")
    show_calls = [c for c in runner_calls if "git" in c["args"] and "show" in c["args"]]
    assert len(show_calls) >= len(expected)

    # cherry-pick 호출 없음
    cherry_pick_calls = [c for c in runner_calls if "cherry-pick" in " ".join(str(a) for a in c["args"])]
    assert not cherry_pick_calls

    # tmp_path 안에만 파일이 작성됨 (실제 source 손상 X)
    for f in expected:
        assert (tmp_path / f).exists()
        assert "fake transplanted content" in (tmp_path / f).read_text()


# ─── T05 원 PR 보존 + [REPLACED] 코멘트 (close/delete 호출 X) ────────────
def test_t05_original_pr_preserved_comment_posted(monkeypatch, tmp_path):
    spec = make_spec()
    expected = list(spec.expected_files)
    contaminated = expected + ["utils/rogue_extra_module.py"]

    runner = make_runner({}, default_returncode=0, default_stdout="")
    rpr = ReplacementPRRunner(runner=runner, dry_run=False, repo_dir=str(tmp_path))

    import utils.replacement_pr_runner as mod  # pyright: ignore[reportMissingImports]
    monkeypatch.setattr(mod, "fetch_pr_metadata", lambda pr, r: fake_pr_meta(pr_number=60, files=contaminated))
    monkeypatch.setattr(mod, "compute_effective_diff", lambda meta, r: contaminated)
    monkeypatch.setattr(mod, "assert_clean_working_tree", lambda r, repo_dir=None: None)
    monkeypatch.setattr(mod, "create_clean_replacement_branch", lambda task_id, r, *, timestamp=None, repo_dir=None: "task/task-2510-replacement-20260508")
    monkeypatch.setattr(mod, "transplant_expected_files", lambda exp, src, r, *, repo_dir=None: list(exp))
    monkeypatch.setattr(mod, "commit_local", lambda task_id, r, *, repo_dir=None: cp(returncode=0))
    monkeypatch.setattr(mod, "precheck_local_replacement_diff", lambda branch, exp, r, *, repo_dir=None: (True, [], []))
    monkeypatch.setattr(mod, "push_branch", lambda branch, r, *, repo_dir=None: cp(returncode=0))
    monkeypatch.setattr(mod, "open_replacement_pr", lambda task_id, branch, source_pr, r, *, repo_dir=None: 61)
    monkeypatch.setattr(mod, "validate_replacement_diff", lambda replacement_pr, exp, r: (True, [], []))

    # post_replaced_comment는 stub하지 않고 실제 호출 → runner.calls에 기록됨
    result = rpr.execute(60, task_spec=spec)

    runner_calls = getattr(runner, "calls")
    all_calls_joined = [" ".join(str(a) for a in c["args"]) for c in runner_calls]

    # 금지 호출 없음
    for call_str in all_calls_joined:
        assert "gh pr close" not in call_str
        assert not ("gh pr edit" in call_str and "--state" in call_str and "closed" in call_str)
        assert not ("gh api" in call_str and "DELETE" in call_str)
        assert not ("git push" in call_str and "--delete" in call_str)

    # [REPLACED] 코멘트 호출 있음
    comment_calls = [c for c in all_calls_joined if "gh pr comment" in c and "[REPLACED]" in c]
    assert comment_calls, f"[REPLACED] comment must be posted, got calls: {all_calls_joined}"
    assert result.original_pr_preserved is True
    assert result.success is True
    assert result.replacement_pr == 61


# ─── T06 validate_replacement_diff: tuple return ──────────────────────────
def test_t06_validate_replacement_diff_exact_match():
    import utils.replacement_pr_runner as mod  # pyright: ignore[reportMissingImports]
    expected = ["utils/replacement_pr_runner.py", "tests/regression/test_replacement_pr_runner_2510.py"]

    files_payload_ok = json.dumps({"files": [{"path": p} for p in expected]})
    runner_ok = make_runner({("gh", "pr", "view"): cp(returncode=0, stdout=files_payload_ok)})
    valid, extra, missing = mod.validate_replacement_diff(61, expected, runner_ok)
    assert valid is True
    assert extra == []
    assert missing == []

    extra_files_list = expected + ["utils/rogue_extra.py"]
    files_payload_extra = json.dumps({"files": [{"path": p} for p in extra_files_list]})
    runner_extra = make_runner({("gh", "pr", "view"): cp(returncode=0, stdout=files_payload_extra)})
    valid_e, extra_e, _missing_e = mod.validate_replacement_diff(61, expected, runner_extra)
    assert valid_e is False
    assert "utils/rogue_extra.py" in extra_e


# ─── T07 replacement 실패 → Critical ──────────────────────────────────────
def test_t07_replacement_failure_critical(monkeypatch, tmp_path):
    spec = make_spec()
    expected = list(spec.expected_files)
    contaminated = expected + ["utils/rogue.py"]

    runner = make_runner({}, default_returncode=0)
    rpr = ReplacementPRRunner(runner=runner, dry_run=False, repo_dir=str(tmp_path))

    import utils.replacement_pr_runner as mod  # pyright: ignore[reportMissingImports]
    monkeypatch.setattr(mod, "fetch_pr_metadata", lambda pr, r: fake_pr_meta(files=contaminated))
    monkeypatch.setattr(mod, "compute_effective_diff", lambda meta, r: contaminated)
    # 실제 실행 경로(push_branch)에서 RuntimeError 시뮬레이션
    def boom(*a, **k):
        raise RuntimeError("PUSH_FAILED simulated")
    monkeypatch.setattr(mod, "assert_clean_working_tree", lambda *a, **k: None)
    monkeypatch.setattr(mod, "create_clean_replacement_branch", lambda *a, **k: "task/task-2510-replacement-zzz")
    monkeypatch.setattr(mod, "transplant_expected_files", lambda *a, **k: list(expected))
    monkeypatch.setattr(mod, "commit_local", lambda *a, **k: cp(returncode=0))
    monkeypatch.setattr(mod, "precheck_local_replacement_diff", lambda *a, **k: (True, [], []))
    monkeypatch.setattr(mod, "push_branch", boom)  # ★ 실제 사용 함수에 모킹

    result = rpr.execute(60, task_spec=spec)
    assert result.success is False
    assert result.failure_reason in {
        CriticalEscalationType.REPLACEMENT_PR_AUTO_CREATION_FAILED_FOR_CONTAMINATED_DIFF.value,
        CriticalEscalationType.REPLACEMENT_PR_FAILED.value,
    }


# ─── T08 PR #54 fixture 78건 → contaminated ────────────────────────────────
def test_t08_pr54_fixture_78_files_contaminated():
    expected = ["utils/replacement_pr_runner.py", "tests/regression/test_replacement_pr_runner_2510.py"]
    contaminated_files = list(expected)
    for prefix in ["task-2487+1", "task-2503", "task-2485+1", "task-2488", "task-2489", "task-2493"]:
        for i in range(10):
            contaminated_files.append(f"utils/{prefix}_module_{i}.py")
            contaminated_files.append(f"tests/regression/test_{prefix}_{i}.py")
    for i in range(3):
        contaminated_files.append(f"scripts/poc_script_{i}.sh")
        contaminated_files.append(f"tests/POC/poc_test_{i}.py")
    contaminated_files = list(dict.fromkeys(contaminated_files))
    assert len(contaminated_files) >= 78  # 회장 명시 78건 이상 확보

    result = detect_contamination(contaminated_files, expected)
    assert result["contaminated"] is True
    assert len(result["extra"]) >= 1


# ─── T09 task-2506 (117건) / task-2507 fixture ───────────────────────────────
def test_t09_task2506_117_files_contaminated():
    expected_2506 = ["utils/auto_gemini_triage.py", "tests/regression/test_auto_gemini_triage_2506.py"]
    contaminated_2506 = list(expected_2506)
    for i in range(57):
        contaminated_2506.append(f"utils/task-2479-dev1_accumulated_{i}.py")
        contaminated_2506.append(f"tests/regression/test_task2479_{i}.py")
    contaminated_2506.append("scripts/dev1_bootstrap.sh")
    contaminated_2506.append("tests/POC/poc_gemini.py")
    contaminated_2506 = list(dict.fromkeys(contaminated_2506))
    r2506 = detect_contamination(contaminated_2506, expected_2506)
    assert r2506["contaminated"] is True

    expected_2507 = ["utils/replacement_pr_runner.py", "tests/regression/test_replacement_pr_runner_2510.py"]
    contaminated_2507 = list(expected_2507)
    for i in range(39):
        contaminated_2507.append(f"utils/task-2507_base_acc_{i}.py")
        contaminated_2507.append(f"tests/regression/test_task2507_acc_{i}.py")
    contaminated_2507 = list(dict.fromkeys(contaminated_2507))
    r2507 = detect_contamination(contaminated_2507, expected_2507)
    assert r2507["contaminated"] is True


# ─── T10 자동 cherry-pick 금지 ────────────────────────────────────────────
def test_t10_assert_no_cherry_pick_raises():
    with pytest.raises(RuntimeError) as excinfo:
        assert_no_cherry_pick(["git", "cherry-pick", "abc"])
    assert "CHERRY_PICK_FORBIDDEN" in str(excinfo.value)


def test_t10_assert_no_cherry_pick_safe_merge():
    # merge는 차단되지 않음
    assert_no_cherry_pick(["git", "merge", "abc"])  # no exception
    assert_no_cherry_pick(["git", "show", "abc:file.py"])  # no exception


# ─── T11 force/rebase/admin flag 정적 차단 ────────────────────────────────
def test_t11_force_flag_raises():
    with pytest.raises(RuntimeError):
        assert_no_forbidden_git_flags(["git", "push", "--force"])


def test_t11_admin_flag_raises():
    with pytest.raises(RuntimeError):
        assert_no_forbidden_git_flags(["gh", "pr", "merge", "--admin"])


def test_t11_rebase_raises():
    with pytest.raises(RuntimeError):
        assert_no_forbidden_git_flags(["git", "rebase", "main"])


# ─── T12 ReplacementResult JSON 직렬화 ────────────────────────────────────
def test_t12_replacement_result_json_roundtrip():
    # 성공
    ok = ReplacementResult(
        source_pr=54, replacement_pr=61, original_pr_preserved=True,
        expected_files=["utils/replacement_pr_runner.py", "tests/regression/test_replacement_pr_runner_2510.py"],
        effective_diff_files=["utils/replacement_pr_runner.py", "tests/regression/test_replacement_pr_runner_2510.py"],
        forbidden_paths=[], success=True, failure_reason=None,
    )
    payload_ok = json.dumps(asdict(ok))
    parsed_ok = json.loads(payload_ok)
    rebuilt_ok = ReplacementResult(**parsed_ok)
    assert rebuilt_ok.success is True
    assert rebuilt_ok.replacement_pr == 61

    # 실패
    fail = ReplacementResult(
        source_pr=54, replacement_pr=None, original_pr_preserved=True,
        expected_files=["utils/replacement_pr_runner.py"], effective_diff_files=[],
        forbidden_paths=[], success=False,
        failure_reason=CriticalEscalationType.REPLACEMENT_PR_FAILED.value,
    )
    payload_fail = json.dumps(asdict(fail))
    parsed_fail = json.loads(payload_fail)
    rebuilt_fail = ReplacementResult(**parsed_fail)
    assert rebuilt_fail.success is False
    assert rebuilt_fail.failure_reason == CriticalEscalationType.REPLACEMENT_PR_FAILED.value


# ─── T13 pre-flight dirty tree → REPLACEMENT_PR_AUTO_CREATION_FAILED ──────
def test_t13_dirty_working_tree_fails(monkeypatch, tmp_path):
    """dirty working tree 감지 시 replacement 흐름 진입 전에 실패 반환."""
    spec = make_spec()
    expected = list(spec.expected_files)
    contaminated = expected + ["utils/rogue_dirty.py"]

    # git status --porcelain 이 비어있지 않은 응답 반환 → dirty
    dirty_runner = make_runner(
        {("git", "status", "--porcelain"): cp(returncode=0, stdout=" M utils/dirty_file.py\n")},
        default_returncode=0,
        default_stdout="",
    )
    rpr = ReplacementPRRunner(runner=dirty_runner, dry_run=False, repo_dir=str(tmp_path))

    import utils.replacement_pr_runner as mod  # pyright: ignore[reportMissingImports]
    monkeypatch.setattr(mod, "fetch_pr_metadata", lambda pr, r: fake_pr_meta(pr_number=60, files=contaminated))
    monkeypatch.setattr(mod, "compute_effective_diff", lambda meta, r: contaminated)

    result = rpr.execute(60, task_spec=spec)

    assert result.success is False
    assert result.failure_reason == CriticalEscalationType.REPLACEMENT_PR_AUTO_CREATION_FAILED_FOR_CONTAMINATED_DIFF.value
    # original PR은 건드리지 않음
    assert result.original_pr_preserved is True
    # escalation packet 채워짐
    assert rpr.last_escalation_packet is not None
    assert rpr.last_escalation_packet.escalation_type == CriticalEscalationType.REPLACEMENT_PR_AUTO_CREATION_FAILED_FOR_CONTAMINATED_DIFF


# ─── T14 precheck_local_replacement_diff mismatch → PR open 호출 X ──────
def test_t14_precheck_mismatch_no_pr_open(monkeypatch, tmp_path):
    """로컬 diff 사전 검증 실패 시 gh pr create 호출 없이 REPLACEMENT_PR_FAILED 반환."""
    spec = make_spec()
    expected = list(spec.expected_files)
    contaminated = expected + ["utils/rogue_precheck.py"]

    # status --porcelain은 clean (empty)
    # precheck_local_replacement_diff 에서 mismatch 반환을 monkeypatch
    runner = make_runner({}, default_returncode=0, default_stdout="")
    rpr = ReplacementPRRunner(runner=runner, dry_run=False, repo_dir=str(tmp_path))

    import utils.replacement_pr_runner as mod  # pyright: ignore[reportMissingImports]
    monkeypatch.setattr(mod, "fetch_pr_metadata", lambda pr, r: fake_pr_meta(pr_number=60, files=contaminated))
    monkeypatch.setattr(mod, "compute_effective_diff", lambda meta, r: contaminated)
    monkeypatch.setattr(mod, "assert_clean_working_tree", lambda r, repo_dir=None: None)
    monkeypatch.setattr(mod, "create_clean_replacement_branch", lambda task_id, r, *, timestamp=None, repo_dir=None: "task/task-2510-replacement-precheck")
    monkeypatch.setattr(mod, "transplant_expected_files", lambda exp, src, r, *, repo_dir=None: list(exp))
    monkeypatch.setattr(mod, "commit_local", lambda task_id, r, *, repo_dir=None: cp(returncode=0))
    # precheck 실패 (extra 파일이 있음)
    monkeypatch.setattr(mod, "precheck_local_replacement_diff", lambda branch, exp, r, *, repo_dir=None: (False, ["utils/unexpected_extra.py"], []))

    # open_replacement_pr가 호출되면 테스트 실패하도록
    pr_open_called = []
    def fail_if_called(*a, **k):
        pr_open_called.append(True)
        return 99
    monkeypatch.setattr(mod, "open_replacement_pr", fail_if_called)

    result = rpr.execute(60, task_spec=spec)

    assert result.success is False
    assert result.failure_reason == CriticalEscalationType.REPLACEMENT_PR_FAILED.value
    assert not pr_open_called, "gh pr create must NOT be called when precheck fails"
    # escalation packet 채워짐
    assert rpr.last_escalation_packet is not None
    assert rpr.last_escalation_packet.escalation_type == CriticalEscalationType.REPLACEMENT_PR_FAILED


# ─── T15 build_escalation_packet 흐름 — 실패 시 last_escalation_packet ──
def test_t15_escalation_packet_populated_on_failure(monkeypatch, tmp_path):
    """실패 경로에서 runner.last_escalation_packet이 채워지는지 검증."""
    spec = make_spec()
    expected = list(spec.expected_files)
    contaminated = expected + ["utils/rogue_escalation.py"]

    runner = make_runner({}, default_returncode=0, default_stdout="")
    rpr = ReplacementPRRunner(runner=runner, dry_run=False, repo_dir=str(tmp_path))

    import utils.replacement_pr_runner as mod  # pyright: ignore[reportMissingImports]
    monkeypatch.setattr(mod, "fetch_pr_metadata", lambda pr, r: fake_pr_meta(pr_number=60, files=contaminated))
    monkeypatch.setattr(mod, "compute_effective_diff", lambda meta, r: contaminated)
    monkeypatch.setattr(mod, "assert_clean_working_tree", lambda r, repo_dir=None: None)
    monkeypatch.setattr(mod, "create_clean_replacement_branch", lambda task_id, r, *, timestamp=None, repo_dir=None: "task/task-2510-replacement-esc")
    monkeypatch.setattr(mod, "transplant_expected_files", lambda exp, src, r, *, repo_dir=None: list(exp))
    # commit_local에서 RuntimeError 발생 → REPLACEMENT_PR_AUTO_CREATION_FAILED
    def boom_commit(*a, **k):
        raise RuntimeError("COMMIT_FAILED simulated for T15")
    monkeypatch.setattr(mod, "commit_local", boom_commit)

    # 초기에는 None
    assert rpr.last_escalation_packet is None

    result = rpr.execute(60, task_spec=spec)

    assert result.success is False
    # last_escalation_packet이 채워졌는지
    assert rpr.last_escalation_packet is not None
    pkt = rpr.last_escalation_packet
    assert pkt.pr_number == 60
    assert pkt.task_id == spec.task_id
    assert pkt.escalation_type in (
        CriticalEscalationType.REPLACEMENT_PR_AUTO_CREATION_FAILED_FOR_CONTAMINATED_DIFF,
        CriticalEscalationType.REPLACEMENT_PR_FAILED,
    )
    assert pkt.reason != ""
    assert isinstance(pkt.evidence, dict)


# ─── T16 wiring 활성화 회귀 (task-2516) ──────────────────────────────────────
def test_wiring_activated_default_runtime_path_2516():
    """task-2516: replacement_pr_runner 모듈의 top-level import에서 merge_queue_executor와의
    circular import가 제거되어 default wiring path(_WIRING_AVAILABLE=True)가 활성화되는지 회귀 검증.
    """
    import importlib
    # Reset state to trigger fresh import (test isolation).
    for mod_name in ("utils.merge_queue_executor", "utils.replacement_pr_runner"):
        if mod_name in sys.modules:
            del sys.modules[mod_name]
    mqe = importlib.import_module("utils.merge_queue_executor")
    assert mqe._WIRING_AVAILABLE is True, (
        "W1 wiring 비활성 — replacement_pr_runner.py에 circular import가 남아있음"
    )
    assert mqe.ReplacementPRRunner is not None, "ReplacementPRRunner가 None — wiring 누락"
    # task-2510에서 정의된 wrapper 함수들도 lazy import 경로로 정상 호출 가능해야 함
    rpr = importlib.import_module("utils.replacement_pr_runner")
    equal, extra, missing = rpr.compare_effective_diff(["a.py"], ["a.py"])
    assert equal is True
    assert extra == [] and missing == []
