107 lines
5.0 KiB
Python
107 lines
5.0 KiB
Python
import os
|
|
import tomllib
|
|
import unittest
|
|
|
|
from tests.fakes import ROOT, FakeJev, FakeModel
|
|
from tests.test_flow import run
|
|
from tests.test_pathjudge import APP, APP_TEST, all_gitea
|
|
from werkrevisor import risk
|
|
from werkrevisor.config import ConfigError, build_config
|
|
from werkrevisor.verdict import Verdict
|
|
|
|
TEMPLATE = "jvm-spring-backend"
|
|
OWN = {"name": "build_slots", "title": "Build slots", "question": "Does the change alter how build slots are taken?",
|
|
"yellow_min": 0.9, "action": "yellow"}
|
|
|
|
|
|
def host(**path_judge):
|
|
with open(os.path.join(ROOT, "config", "werkprobe.toml"), "rb") as f:
|
|
data = tomllib.load(f)
|
|
data["engine"] = {"mode": "combined", "model_role": "advises"}
|
|
data["gate"].update(yellow_gate="block", require_wip_transition=False, automerge=False)
|
|
data["hard_yellow"].update(paths=[], judged_paths=["server/*.gradle.kts"])
|
|
data["path_judge"] = {"judge": "jev", "scope": "all", **path_judge}
|
|
return data
|
|
|
|
|
|
def repo_config(categories=(), template=TEMPLATE, **host_path_judge):
|
|
repo = {"path_judge": {"risk_template": template}} if template else {}
|
|
if categories:
|
|
repo["risk"] = {"categories": list(categories)}
|
|
return build_config(host(**host_path_judge), repo)
|
|
|
|
|
|
def repo_run(jev, cfg):
|
|
return run(all_gitea(APP, APP_TEST), FakeModel(prompt_files=[APP, APP_TEST]), cfg=cfg, jev=jev)
|
|
|
|
|
|
def app_questions(jev):
|
|
return next(q for s, q in jev.requests if s.startswith(f"Changed file: {APP}"))
|
|
|
|
|
|
def reason(outcome, code):
|
|
return next(r for r in outcome.result.reasons if r.code == code)
|
|
|
|
|
|
class RiskInRepositoryConfigTest(unittest.TestCase):
|
|
def test_the_repository_chooses_the_template(self):
|
|
"""Scenario#63.01"""
|
|
jev = FakeJev()
|
|
repo_run(jev, repo_config())
|
|
self.assertLessEqual({c.name for c in risk.load(TEMPLATE)}, set(app_questions(jev)))
|
|
|
|
def test_the_repository_changes_the_threshold_or_the_consequence_of_a_category(self):
|
|
"""Scenario#63.02"""
|
|
cfg = repo_config([{"name": "concurrency", "action": "hint"}])
|
|
outcome, _ = repo_run(FakeJev(problems={(APP, "concurrency"): 0.97}), cfg)
|
|
self.assertIs(outcome.result.verdict, Verdict.GREEN)
|
|
self.assertIs(reason(outcome, "risk_concurrency").verdict, Verdict.GREEN)
|
|
cfg = repo_config([{"name": "authorization", "yellow_min": 0.5}])
|
|
outcome, _ = repo_run(FakeJev(problems={(APP, "authorization"): 0.6}), cfg)
|
|
self.assertIs(outcome.result.verdict, Verdict.YELLOW)
|
|
|
|
def test_the_repository_switches_a_category_off(self):
|
|
"""Scenario#63.03"""
|
|
jev = FakeJev()
|
|
repo_run(jev, repo_config([{"name": "sql_injection", "action": "off"}]))
|
|
self.assertNotIn("sql_injection", app_questions(jev))
|
|
self.assertIn("authorization", app_questions(jev))
|
|
|
|
def test_the_repository_adds_an_own_category(self):
|
|
"""Scenario#63.04"""
|
|
outcome, _ = repo_run(FakeJev(problems={(APP, "build_slots"): 0.95}), repo_config([OWN]))
|
|
self.assertIs(outcome.result.verdict, Verdict.YELLOW)
|
|
self.assertTrue(reason(outcome, "risk_build_slots").text.startswith(f"Build slots according to Jev: {APP} (0.95)"))
|
|
# Without a template, the own categories are all there is.
|
|
jev = FakeJev()
|
|
repo_run(jev, repo_config([OWN], template=None))
|
|
self.assertEqual(set(app_questions(jev)), {"weakens_checks", "security", "build_slots"})
|
|
|
|
def test_the_host_can_forbid_the_tuning(self):
|
|
"""Scenario#63.05"""
|
|
for categories, template in (((), TEMPLATE), ([OWN], None)):
|
|
with self.subTest(categories=categories, template=template):
|
|
with self.assertRaisesRegex(ConfigError, r"must not set \[(path_judge|risk)\] (risk_template|categories): "
|
|
r"the host config does not allow it"):
|
|
repo_config(categories, template, repository_may_tune_risks=False)
|
|
|
|
|
|
class RiskOverrideValidationTest(unittest.TestCase):
|
|
def test_invalid_entries_are_refused(self):
|
|
for entry, message in (({"name": "concurrency", "question": "Other?"}, r"concurrency: only yellow_min, orange_min "
|
|
r"and action can change a template category"),
|
|
({"name": "own", "action": "yellow"}, r"own: title must be a non-empty string"),
|
|
({"name": "concurrency", "action": "maybe"}, r"action must be one of yellow, hint, off"),
|
|
({"action": "hint"}, r"name must be an identifier")):
|
|
with self.subTest(entry=entry):
|
|
with self.assertRaisesRegex(ConfigError, message):
|
|
repo_config([entry])
|
|
|
|
def test_the_host_keeps_the_switch(self):
|
|
with self.assertRaisesRegex(ConfigError, r"must not set \[path_judge\] repository_may_tune_risks"):
|
|
build_config(host(), {"path_judge": {"repository_may_tune_risks": True}})
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|