import unittest from tests.fakes import BOT, HEAD, FakeGitea, FakeModel, config, diff_for, gitea_file, review_data from tests.test_flow import run from werkrevisor.verdict import Verdict AUTOMERGE = {"yellow_gate": "approval", "human_reviewers": ["alice"], "automerge": True, "require_wip_transition": True} DOC = "docs/prs/1.md" EARLIER = "e" * 40 LONG_LINE = "A sentence that runs on and on " * 6 OLD_TEXT = f"# Title\n\nFirst sentence.\n{LONG_LINE}\n" NEW_TEXT = f"# Title\n\nFirst sentence.\n{LONG_LINE}\nA new sentence.\n" def earlier_round(): return {"id": 1, "user": {"login": BOT}, "created_at": "2026-10-12T08:00:00Z", "body": f"\n\n## werkrevisor"} def doc_run(hint_line, earlier=True, review=None, old=OLD_TEXT, new=NEW_TEXT): gitea = FakeGitea(files=[gitea_file(DOC, status="added", additions=5)], diff=diff_for(DOC, added=new.rstrip("\n").split("\n"), new=True), comments=[earlier_round()] if earlier else ()) gitea.raw_files[DOC] = new.encode() gitea.raw_files_at[(DOC, EARLIER)] = old.encode() gitea.raw_files_at[(DOC, HEAD)] = new.encode() data = review_data(issues=[DOC] if hint_line else [], header="[low] Wording") for issue in data["review"]["key_issues_to_review"]: issue.update(start_line=hint_line, end_line=hint_line) cfg = config(gate=AUTOMERGE, review=review or {}) outcome, _ = run(gitea, FakeModel(data=data, prompt_files=[DOC]), cfg=cfg) return outcome, gitea def commented_lines(gitea): return sorted(c["new_position"] for comments in gitea.review_comments for c in comments) class ChangedLinesTest(unittest.TestCase): def test_new_hint_on_old_text(self): outcome, gitea = doc_run(hint_line=3) self.assertIs(outcome.result.verdict, Verdict.GREEN) self.assertEqual(commented_lines(gitea), []) self.assertIn("Wording", gitea.created_comments[0]) self.assertEqual(len(gitea.scheduled_merges), 1) def test_hint_on_changed_line(self): outcome, gitea = doc_run(hint_line=5) self.assertEqual(commented_lines(gitea), [5]) self.assertEqual(gitea.scheduled_merges, []) self.assertIn("merge waits for 1 open hint(s)", outcome.result.gate_note) def test_first_round(self): outcome, gitea = doc_run(hint_line=3, earlier=False) self.assertEqual(commented_lines(gitea), [3, 4]) self.assertEqual(gitea.scheduled_merges, []) def test_writing_rule_on_old_text(self): outcome, gitea = doc_run(hint_line=None) self.assertEqual(commented_lines(gitea), []) self.assertIn("line longer than 128 characters", gitea.created_comments[0]) self.assertEqual(len(gitea.scheduled_merges), 1) def test_unreadable_earlier_file(self): gitea = FakeGitea(files=[gitea_file(DOC, status="added", additions=5)], diff=diff_for(DOC, added=NEW_TEXT.rstrip("\n").split("\n"), new=True), comments=[earlier_round()]) gitea.raw_files[DOC] = NEW_TEXT.encode() gitea.raw_files_at[(DOC, EARLIER)] = None data = review_data(issues=[DOC], header="[low] Wording") for issue in data["review"]["key_issues_to_review"]: issue.update(start_line=3, end_line=3) outcome, _ = run(gitea, FakeModel(data=data, prompt_files=[DOC]), cfg=config(gate=AUTOMERGE)) self.assertEqual(commented_lines(gitea), [3, 4]) def test_doc_hint_line_comments_keeps_the_hint(self): outcome, gitea = doc_run(hint_line=3, review={"doc_hint_line_comments": True}) self.assertEqual(commented_lines(gitea), [3]) self.assertEqual(gitea.scheduled_merges, []) def test_a_finding_on_a_heading_covers_its_section(self): old = "# Title\n\n## Solution\n\nOld text.\n" new = "# Title\n\n## Solution\n\nNew text.\n" outcome, gitea = doc_run(hint_line=3, old=old, new=new) self.assertEqual(commented_lines(gitea), [3]) self.assertEqual(gitea.scheduled_merges, []) def test_a_paragraph_rule_covers_its_paragraph(self): old = "# Title\n\nOne.\nTwo.\nThree.\nFour.\nFive.\n" new = "# Title\n\nOne.\nTwo.\nThree.\nFour.\nFive.\nSix.\n" outcome, gitea = doc_run(hint_line=None, old=old, new=new) self.assertEqual(commented_lines(gitea), [3]) self.assertEqual(gitea.scheduled_merges, [])