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()