122 lines
6.5 KiB
Python
122 lines
6.5 KiB
Python
import unittest
|
|
|
|
from tests.fakes import config, pr_ref
|
|
from werkrevisor.review import ModelRun, coverage_reasons, files_in_prompt, pr_agent_settings, unreadable_files
|
|
from werkrevisor.rules import ChangedFile
|
|
|
|
try:
|
|
from pr_agent.config_loader import get_settings
|
|
from pr_agent.algo.language_handler import is_valid_file
|
|
HAVE_PR_AGENT = True
|
|
except ImportError:
|
|
HAVE_PR_AGENT = False
|
|
|
|
|
|
@unittest.skipUnless(HAVE_PR_AGENT, "runs inside the PR-Agent image")
|
|
class PrAgentSettingsTest(unittest.TestCase):
|
|
def test_every_forced_setting_exists_in_pr_agent(self):
|
|
values = pr_agent_settings(config(), pr_ref(), "t" * 20, "k" * 20, "extra")
|
|
settings = get_settings()
|
|
# num_retries is only commented out in configuration.toml but read via get(); the keys are secrets.
|
|
not_in_defaults = {"config.num_retries", "openrouter.key", "gitea.personal_access_token"}
|
|
for key in values:
|
|
if key in not_in_defaults:
|
|
continue
|
|
with self.subTest(key=key):
|
|
section, name = key.split(".", 1)
|
|
self.assertIn(name, settings.get(section), f"{key} is not a PR-Agent 0.47.0 setting")
|
|
|
|
def test_settings_reach_pr_agent(self):
|
|
from werkrevisor.review import apply_settings
|
|
settings = get_settings()
|
|
apply_settings(settings, pr_agent_settings(config(), pr_ref(), "t" * 20, "k" * 20, "extra"))
|
|
self.assertEqual(settings.get("GITEA.URL"), "https://git.example.org")
|
|
self.assertEqual(settings.get("GITEA.PERSONAL_ACCESS_TOKEN"), "t" * 20)
|
|
self.assertEqual(settings.get("OPENROUTER.KEY"), "k" * 20)
|
|
self.assertEqual(settings.config.fallback_models, [])
|
|
self.assertFalse(settings.config.publish_output)
|
|
self.assertEqual(settings.pr_reviewer.extra_instructions, "extra")
|
|
self.assertEqual(settings.config.model, config().model.id)
|
|
|
|
def test_token_limits_reach_the_litellm_handler(self):
|
|
from pr_agent.algo.ai_handlers.litellm_ai_handler import LiteLLMAIHandler
|
|
from pr_agent.algo.token_budget import get_max_tokens
|
|
from werkrevisor.review import apply_settings
|
|
cfg = config()
|
|
apply_settings(get_settings(), pr_agent_settings(cfg, pr_ref(), "t" * 20, "k" * 20, ""))
|
|
handler = LiteLLMAIHandler()
|
|
self.assertEqual(handler.get_output_token_limit(cfg.model.id), cfg.model.max_output_tokens)
|
|
# Twice the input limit, so a too long prompt reaches werkrevisor's count unshortened.
|
|
self.assertEqual(get_max_tokens(cfg.model.id), 2 * 100000 + cfg.model.max_output_tokens)
|
|
cfg = config(model={"context_tokens": 120000})
|
|
apply_settings(get_settings(), pr_agent_settings(cfg, pr_ref(), "t" * 20, "k" * 20, ""))
|
|
self.assertEqual(get_max_tokens(cfg.model.id), 120000)
|
|
|
|
def test_reviewer_exposes_the_attributes_we_read(self):
|
|
from pr_agent.algo import pr_processing
|
|
from pr_agent.tools.pr_reviewer import PRReviewer
|
|
self.assertTrue(callable(pr_processing.pr_generate_compressed_diff))
|
|
self.assertTrue(callable(PRReviewer._load_review_yaml))
|
|
self.assertIsNone(PRReviewer.prediction_data)
|
|
|
|
def test_binary_files_are_not_reviewable(self):
|
|
self.assertFalse(is_valid_file("doc/img.png"))
|
|
self.assertTrue(is_valid_file("src/main/java/A.java"))
|
|
|
|
def test_gradle_files_are_reviewable_but_binary_files_are_not(self):
|
|
from werkrevisor.review import is_reviewable_by_pr_agent
|
|
for path, reviewable in (("settings.gradle", True), ("build.gradle", True), ("build.gradle.kts", True),
|
|
("gradle/wrapper/gradle-wrapper.jar", False), ("doc/img.png", False)):
|
|
with self.subTest(path=path):
|
|
self.assertEqual(is_reviewable_by_pr_agent(path), reviewable)
|
|
|
|
def test_reviewability_does_not_follow_changed_settings(self):
|
|
from werkrevisor.review import is_reviewable_by_pr_agent
|
|
settings = get_settings()
|
|
original = list(settings.bad_extensions.default)
|
|
try:
|
|
settings.set("bad_extensions.default", ["py"])
|
|
self.assertTrue(is_reviewable_by_pr_agent("src/a.py"))
|
|
self.assertFalse(is_reviewable_by_pr_agent("doc/img.png"))
|
|
finally:
|
|
settings.set("bad_extensions.default", original)
|
|
|
|
|
|
class PromptCoverageTest(unittest.TestCase):
|
|
DIFF = ("\n\n## File: 'src/a.py'\n\n__new hunk__\n1 +x\n"
|
|
"\n\n## File 'old.py' was deleted\n"
|
|
"\n\n## File: 'bad.py'\n\n> **This file could not be read.** PR-Agent failed\n")
|
|
|
|
def test_files_in_prompt(self):
|
|
self.assertEqual(files_in_prompt(self.DIFF), {"src/a.py", "old.py", "bad.py"})
|
|
self.assertEqual(unreadable_files(self.DIFF), ["bad.py"])
|
|
|
|
def test_notice_text_inside_diff_or_full_files_is_content(self):
|
|
from werkrevisor import fullfiles
|
|
source = 'UNREADABLE = "This file could not be read."\n> **This file could not be read.** PR-Agent failed\n'
|
|
diff = ("\n\n## File: 'werkrevisor/review.py'\n\n__new hunk__\n"
|
|
"15 UNREADABLE = \"This file could not be read.\"\n16 +x = 1\n"
|
|
"\n\n## File: 'docs/prs/2026-10-05-PR#9-x.md'\n\n__new hunk__\n1 +> **This file could not be read.**\n")
|
|
section = fullfiles.render_section([fullfiles.render_file("werkrevisor/review.py", source)], "a" * 40)
|
|
self.assertEqual(unreadable_files(diff + section), [])
|
|
self.assertEqual(files_in_prompt(diff + section), {"werkrevisor/review.py", "docs/prs/2026-10-05-PR#9-x.md"})
|
|
|
|
@unittest.skipUnless(HAVE_PR_AGENT, "runs inside the PR-Agent image")
|
|
def test_real_notice_of_pr_agent_is_detected(self):
|
|
from types import SimpleNamespace
|
|
from pr_agent.algo.pr_processing import _unreadable_file_notice
|
|
notice = _unreadable_file_notice(SimpleNamespace(filename="docs/prs/2026-10-05-PR#9-x.md"))
|
|
diff = "\n\n## File: 'src/a.py'\n\n__new hunk__\n1 +x\n" + notice
|
|
self.assertEqual(unreadable_files(diff), ["docs/prs/2026-10-05-PR#9-x.md"])
|
|
|
|
def test_coverage(self):
|
|
run = ModelRun(data={}, details={"completion_tokens": 10}, prompt_files={"a.py"})
|
|
files = [ChangedFile("a.py", "modified", 1), ChangedFile("b.py", "modified", 2),
|
|
ChangedFile("c.png", "modified", 0), ChangedFile("d.py", "renamed", 0)]
|
|
codes = [r.code for r in coverage_reasons(files, run, lambda p: not p.endswith(".png"), 4000)]
|
|
self.assertEqual(codes, ["skipped_files", "unreviewable_files", "no_text_diff"])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|