"""Tests for ci_gate.main orchestration — coverage-visible entrypoints."""
from __future__ import annotations
import inspect
import logging
from datetime import date
from pathlib import Path
import pytest
import scripts.helpers.ci_gate.main as ci_gate_main
from scripts.helpers._config import Config
from scripts.helpers.ci_gate.diff import GitDiffResult
from scripts.helpers.ci_gate.main import _run_pytest, main
from scripts.helpers.ci_gate.models import Baseline, ChangeSet, TestExemption
from scripts.helpers.common.test_map_loader import TestMapFreshness
from tests.helpers.fake_subprocess import FakeCompleted
from tests.regression.scripts.helpers.conftest import default_ci_gate_policy
def _empty_diff() -> GitDiffResult:
return GitDiffResult(line_map={}, entries=())
@pytest.fixture(scope="module")
def gate_cfg() -> Config:
return Config(
test_map_path="/tmp/test_map.json",
base_branch="develop",
line_threshold=60.0,
branch_threshold=40.0,
benchmark_parallel=False,
feishu_webhook_url="",
msmodeling_cache=".msmodeling_cache",
weights_prune=True,
)
@pytest.fixture(scope="module")
def empty_baseline() -> Baseline:
return Baseline(test_map={}, policy=default_ci_gate_policy())
def test_run_pytest_empty_targets_returns_zero_without_subprocess() -> None:
assert _run_pytest([], marker="not npu") == 0
def test_run_pytest_invokes_subprocess_for_targets(
monkeypatch: pytest.MonkeyPatch,
) -> None:
captured: list[list[str]] = []
def _fake_run(cmd: list[str], **kwargs: object) -> FakeCompleted:
captured.append(cmd)
return FakeCompleted(0, "", "")
monkeypatch.setattr("scripts.helpers.ci_gate.main.subprocess.run", _fake_run)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.count_collected_tests",
lambda *_args, **_kwargs: 2,
)
code = _run_pytest(
["tests/regression/scripts/helpers/ci_gate/test_errors.py"],
marker="not npu and not nightly and not network",
)
assert code == 0
assert captured
run_cmd = captured[-1]
assert "-o" in run_cmd
assert "addopts=" in run_cmd
assert "-m" in run_cmd
assert "not npu and not nightly and not network" in run_cmd
assert "-n" in run_cmd
assert "pytest" in run_cmd
def test_run_pytest_node_targets_filter_via_collect_only(
monkeypatch: pytest.MonkeyPatch,
) -> None:
captured: dict[str, object] = {}
def _fake_collect(targets: list[str], *, marker: str) -> tuple[str, ...]:
captured["collect_targets"] = list(targets)
captured["collect_marker"] = marker
return ("tests/regression/cli/test_a.py::test_a",)
def _fake_build(
_python: str,
run_targets: list[str],
*,
marker: str,
collected_count: int,
**_kwargs: object,
) -> list[str]:
captured["run_targets"] = list(run_targets)
captured["collected_count"] = collected_count
captured["build_marker"] = marker
return ["pytest"]
monkeypatch.setattr("scripts.helpers.ci_gate.main.filter_collectable_node_ids", _fake_collect)
monkeypatch.setattr("scripts.helpers.ci_gate.main.build_pytest_cmd", _fake_build)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.subprocess.run",
lambda *_args, **_kwargs: FakeCompleted(0, "", ""),
)
code = _run_pytest(
[
"tests/regression/cli/test_a.py::test_a",
"tests/regression/cli/test_b.py::test_stale",
],
marker="not npu",
)
assert code == 0
assert captured["collect_targets"] == [
"tests/regression/cli/test_a.py::test_a",
"tests/regression/cli/test_b.py::test_stale",
]
assert captured["collect_marker"] == "not npu"
assert captured["run_targets"] == ["tests/regression/cli/test_a.py::test_a"]
assert captured["collected_count"] == 1
assert captured["build_marker"] == "not npu"
def test_run_pytest_stale_node_targets_skip_wave(
monkeypatch: pytest.MonkeyPatch,
) -> None:
subprocess_calls: list[list[str]] = []
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.filter_collectable_node_ids",
lambda *_args, **_kwargs: (),
)
def _fake_run(cmd: list[str], **_kwargs: object) -> FakeCompleted:
subprocess_calls.append(cmd)
return FakeCompleted(0, "", "")
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.subprocess.run",
_fake_run,
)
code = _run_pytest(
["tests/regression/cli/test_old.py::test_renamed"],
marker="not npu and not nightly and not network",
)
assert code == 0
assert subprocess_calls == []
def test_run_pytest_stale_node_targets_log_skipped(
monkeypatch: pytest.MonkeyPatch,
caplog: pytest.LogCaptureFixture,
) -> None:
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.filter_collectable_node_ids",
lambda *_args, **_kwargs: ("tests/regression/cli/test_a.py::test_a",),
)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.subprocess.run",
lambda *_args, **_kwargs: FakeCompleted(0, "", ""),
)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.build_pytest_cmd",
lambda *_args, **_kwargs: ["pytest"],
)
with caplog.at_level(logging.INFO, logger="ci_gate"):
_run_pytest(
[
"tests/regression/cli/test_a.py::test_a",
"tests/regression/cli/test_b.py::test_stale",
],
marker="not npu",
)
assert "Skipping non-collectable pytest node(s): tests/regression/cli/test_b.py::test_stale" in caplog.text
def test_run_pytest_adds_cov_args_when_requested(
monkeypatch: pytest.MonkeyPatch,
) -> None:
captured: dict[str, tuple[str, ...]] = {}
def _fake_build(
*_args: object,
extra_args: tuple[str, ...] = (),
**_kwargs: object,
) -> list[str]:
captured["extra_args"] = extra_args
return ["pytest"]
monkeypatch.setattr("scripts.helpers.ci_gate.main.build_pytest_cmd", _fake_build)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.subprocess.run",
lambda *_args, **_kwargs: FakeCompleted(0, "", ""),
)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main._collected_count_for_targets",
lambda *_args, **_kwargs: 1,
)
_run_pytest(["tests"], marker="not npu", use_cov=True)
extra_args = captured["extra_args"]
assert extra_args
assert any(str(arg).startswith("--cov=") for arg in extra_args)
assert "--cov-context=test" in extra_args
def test_run_pytest_cov_append_passes_flag(monkeypatch: pytest.MonkeyPatch) -> None:
captured: list[tuple[str, ...]] = []
def _fake_build(
*_args: object,
extra_args: tuple[str, ...] = (),
**_kwargs: object,
) -> list[str]:
captured.append(extra_args)
return ["pytest"]
monkeypatch.setattr("scripts.helpers.ci_gate.main.build_pytest_cmd", _fake_build)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.subprocess.run",
lambda *_args, **_kwargs: FakeCompleted(0, "", ""),
)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main._collected_count_for_targets",
lambda *_args, **_kwargs: 1,
)
_run_pytest(["tests"], marker="not npu", use_cov=True, cov_append=True)
assert "--cov-append" in captured[-1]
def test_run_pytest_full_suite_marker_uses_not_npu_only(
monkeypatch: pytest.MonkeyPatch,
) -> None:
captured: list[list[str]] = []
def _fake_run(cmd: list[str], **kwargs: object) -> FakeCompleted:
captured.append(cmd)
return FakeCompleted(0, "", "")
monkeypatch.setattr("scripts.helpers.ci_gate.main.subprocess.run", _fake_run)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.count_collected_tests",
lambda *_args, **_kwargs: 1,
)
code = _run_pytest(["tests"], marker="not npu")
assert code == 0
run_cmd = captured[-1]
assert run_cmd.count("-m") >= 1
assert "not npu" in run_cmd
assert "not nightly and not network" not in run_cmd
def test_main_passes_when_no_gate_work(
monkeypatch: pytest.MonkeyPatch,
gate_cfg: Config,
empty_baseline: Baseline,
) -> None:
monkeypatch.setattr("scripts.helpers.ci_gate.main.Config.from_env", lambda: gate_cfg)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.resolve_base_ref",
lambda _root, _branch: "abc" * 10,
)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.validate_gate_policy_if_changed",
lambda *_args: None,
)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.assess_test_map_freshness",
lambda *_args: TestMapFreshness(),
)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.load_baseline",
lambda *_args: (empty_baseline, "a" * 40),
)
monkeypatch.setattr("scripts.helpers.ci_gate.main.fetch_diff", lambda *_args: _empty_diff())
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.classify_changes",
lambda *_args: ChangeSet.build(),
)
assert main() == 0
def test_main_returns_one_on_unmapped_modified_source(
monkeypatch: pytest.MonkeyPatch,
gate_cfg: Config,
empty_baseline: Baseline,
tmp_path: Path,
) -> None:
monkeypatch.setattr("scripts.helpers.ci_gate.main.Config.from_env", lambda: gate_cfg)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.resolve_base_ref",
lambda _root, _branch: "abc" * 10,
)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.validate_gate_policy_if_changed",
lambda *_args: None,
)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.assess_test_map_freshness",
lambda *_args: TestMapFreshness(),
)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.load_baseline",
lambda *_args: (empty_baseline, "a" * 40),
)
monkeypatch.setattr("scripts.helpers.ci_gate.main.fetch_diff", lambda *_args: _empty_diff())
monkeypatch.setattr("scripts.helpers.ci_gate.main._COVERAGE_DATA_PATH", tmp_path / ".coverage.missing")
main_line = inspect.getsourcelines(ci_gate_main.main)[1] + 1
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.classify_changes",
lambda *_args: ChangeSet.build(
modified_source={"scripts/helpers/ci_gate/main.py": frozenset({main_line})},
),
)
assert main() == 1
def test_main_skips_all_exempt_changed_test_file(
monkeypatch: pytest.MonkeyPatch,
gate_cfg: Config,
) -> None:
monkeypatch.setattr(
"scripts.helpers.ci_gate.rules.collect_all_test_node_ids",
lambda targets, **_kwargs: tuple(f"{path}::test_case" for path in targets),
)
pytest_calls: list[list[str]] = []
policy = default_ci_gate_policy()
baseline = Baseline(
test_map={},
policy=policy.__class__(
sources=policy.sources,
tests=policy.tests,
configs=policy.configs,
source_exemptions=(),
test_exemptions=(
TestExemption(
test_id="tests/regression/cli/test_omitted.py::test_case",
reason="x",
applicant="a",
approver="fangkai",
deadline=date(2099, 12, 31),
),
),
approvers=policy.approvers,
),
)
monkeypatch.setattr("scripts.helpers.ci_gate.main.Config.from_env", lambda: gate_cfg)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.resolve_base_ref",
lambda _root, _branch: "abc" * 10,
)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.validate_gate_policy_if_changed",
lambda *_args: None,
)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.load_baseline",
lambda *_args: (baseline, "a" * 40),
)
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.assess_test_map_freshness",
lambda *_args: TestMapFreshness(),
)
monkeypatch.setattr("scripts.helpers.ci_gate.main.fetch_diff", lambda *_args: _empty_diff())
monkeypatch.setattr(
"scripts.helpers.ci_gate.main.classify_changes",
lambda *_args: ChangeSet.build(
new_test=("tests/regression/cli/test_omitted.py",),
),
)
def _fake_run_pytest(
targets: list[str],
*,
marker: str,
use_cov: bool = False,
cov_append: bool = False,
) -> int:
pytest_calls.append(targets)
return 0
monkeypatch.setattr("scripts.helpers.ci_gate.main._run_pytest", _fake_run_pytest)
assert main() == 0
assert pytest_calls == []