mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-05 08:07:05 +00:00
153 lines
5.2 KiB
Python
153 lines
5.2 KiB
Python
import importlib.util
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
_MODULE_PATH = Path(__file__).resolve().parents[2] / "scripts" / "ruff_strict_gate.py"
|
|
_spec = importlib.util.spec_from_file_location("ruff_strict_gate", _MODULE_PATH)
|
|
gate = importlib.util.module_from_spec(_spec)
|
|
_spec.loader.exec_module(gate)
|
|
|
|
Violation = gate.Violation
|
|
|
|
|
|
def rule(name, limit):
|
|
return {name: {"limit": limit}}
|
|
|
|
|
|
def test_under_ceiling_passes():
|
|
assert gate.evaluate({"ANN001": 100}, {"ANN001": 100}, rule("ANN001", 110)) == []
|
|
|
|
|
|
def test_ceiling_is_the_limit_boundary():
|
|
budget = rule("ANN001", 110)
|
|
at = gate.evaluate({"ANN001": 110}, {"ANN001": 90}, budget)
|
|
over = gate.evaluate({"ANN001": 111}, {"ANN001": 90}, budget)
|
|
assert at == []
|
|
assert [b.rule for b in over] == ["ANN001"]
|
|
assert over[0].cap == 110
|
|
assert over[0].added == 21
|
|
|
|
|
|
def test_over_ceiling_and_change_added_fails():
|
|
breaches = gate.evaluate({"C901": 11}, {"C901": 9}, rule("C901", 10))
|
|
assert [b.rule for b in breaches] == ["C901"]
|
|
assert breaches[0].added == 2
|
|
|
|
|
|
def test_base_already_over_ceiling_change_added_nothing_is_not_blamed():
|
|
# drift safety: base is over limit, this change leaves the count where it is
|
|
assert gate.evaluate({"C901": 15}, {"C901": 15}, rule("C901", 10)) == []
|
|
|
|
|
|
def test_change_that_reduces_an_over_ceiling_rule_is_not_blamed():
|
|
# still over limit, but moving the right direction
|
|
assert gate.evaluate({"C901": 14}, {"C901": 16}, rule("C901", 10)) == []
|
|
|
|
|
|
def test_rules_are_independent():
|
|
budget = {**rule("ANN001", 150), **rule("C901", 10)}
|
|
breaches = gate.evaluate(
|
|
{"ANN001": 130, "C901": 11}, {"ANN001": 100, "C901": 10}, budget
|
|
)
|
|
assert [b.rule for b in breaches] == ["C901"] # ANN001 130 <= 150, C901 11 > 10
|
|
|
|
|
|
def test_missing_rule_counts_as_zero():
|
|
assert gate.evaluate({}, {}, rule("C901", 0)) == []
|
|
|
|
|
|
def test_update_ratchets_limit_down_by_what_the_branch_fixed_never_up():
|
|
budget = {**rule("ANN001", 150), **rule("C901", 10)}
|
|
# ANN001 fixed 20 (100 -> 80) so its limit falls 150 -> 130; C901 grew, so its
|
|
# limit holds flat at 10 (a fix must never loosen a ceiling).
|
|
current = {"ANN001": 80, "C901": 12}
|
|
base = {"ANN001": 100, "C901": 9}
|
|
assert gate.ratcheted_budget(budget, current, base) == {
|
|
"ANN001": {"limit": 130},
|
|
"C901": {"limit": 10},
|
|
}
|
|
|
|
|
|
def test_parse_changed_lines_maps_added_lines_per_file():
|
|
diff = (
|
|
"+++ b/litellm/a.py\n"
|
|
"@@ -10 +10,3 @@\n+x\n+y\n+z\n"
|
|
"+++ b/litellm/b.py\n"
|
|
"@@ -5,2 +7 @@\n+q\n"
|
|
)
|
|
changed = gate.parse_changed_lines(diff)
|
|
assert changed["litellm/a.py"] == {10, 11, 12}
|
|
assert changed["litellm/b.py"] == {7}
|
|
|
|
|
|
def test_introduced_keeps_only_violations_on_changed_lines():
|
|
violations = [
|
|
Violation("litellm/a.py", 10, "ANN001"),
|
|
Violation("litellm/a.py", 99, "C901"),
|
|
]
|
|
assert gate.introduced(violations, {"litellm/a.py": {10}}) == [
|
|
Violation("litellm/a.py", 10, "ANN001")
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize("hunk", ["@@ -1 +1 @@", "@@ -1,0 +1,2 @@"])
|
|
def test_parse_changed_lines_handles_single_and_ranged_hunks(hunk):
|
|
assert gate.parse_changed_lines(f"+++ b/litellm/a.py\n{hunk}\n")["litellm/a.py"]
|
|
|
|
|
|
def test_over_ceiling_flags_only_counts_above_the_limit():
|
|
budget = rule("C901", 10)
|
|
assert gate.over_ceiling({"C901": 10}, budget) == frozenset()
|
|
assert gate.over_ceiling({"C901": 11}, budget) == frozenset({"C901"})
|
|
assert gate.over_ceiling({}, budget) == frozenset()
|
|
|
|
|
|
def test_over_ceiling_ignores_rules_missing_from_the_budget():
|
|
assert gate.over_ceiling({"NEW99": 100}, rule("C901", 10)) == frozenset()
|
|
|
|
|
|
def test_over_ceiling_is_independent_across_rules():
|
|
budget = {**rule("ANN001", 150), **rule("C901", 10)}
|
|
assert gate.over_ceiling({"ANN001": 130, "C901": 11}, budget) == frozenset({"C901"})
|
|
|
|
|
|
def _git(cwd, *args):
|
|
proc = subprocess.run(["git", *args], cwd=cwd, capture_output=True, text=True)
|
|
assert proc.returncode == 0, proc.stderr
|
|
return proc.stdout.strip()
|
|
|
|
|
|
def _commit(cwd, name):
|
|
(cwd / name).write_text(name)
|
|
_git(cwd, "add", "-A")
|
|
_git(cwd, "commit", "-q", "-m", name)
|
|
return _git(cwd, "rev-parse", "HEAD")
|
|
|
|
|
|
def _branched_repo(tmp_path):
|
|
repo = tmp_path / "repo"
|
|
repo.mkdir()
|
|
_git(repo, "init", "-q", "-b", "main")
|
|
_git(repo, "config", "user.email", "gate@example.com")
|
|
_git(repo, "config", "user.name", "gate")
|
|
_git(repo, "config", "commit.gpgsign", "false")
|
|
branch_point = _commit(repo, "shared.txt")
|
|
_git(repo, "checkout", "-q", "-b", "feature")
|
|
_commit(repo, "feature.txt")
|
|
_git(repo, "checkout", "-q", "main")
|
|
base_tip = _commit(repo, "drift.txt")
|
|
_git(repo, "checkout", "-q", "feature")
|
|
return repo, branch_point, base_tip
|
|
|
|
|
|
def test_base_point_is_the_branch_point_when_no_merge_is_in_progress(tmp_path):
|
|
repo, branch_point, _ = _branched_repo(tmp_path)
|
|
assert gate.resolve_base_point("main", cwd=repo) == branch_point
|
|
|
|
|
|
def test_base_point_mid_merge_advances_to_the_merged_in_base_tip(tmp_path):
|
|
repo, _, base_tip = _branched_repo(tmp_path)
|
|
_git(repo, "merge", "--no-commit", "--no-ff", "main")
|
|
assert gate.resolve_base_point("main", cwd=repo) == base_tip
|