werkator build succeeded after 01:14
werkrevisor 0.11.0: exact token count before the model call, and a focused style review. - werkrevisor counts the exact prompt PR-Agent sends, with o200k_base and `[model] token_count_factor` (default 1.15), instead of estimating chars/3.5. - The model no longer reports line length or sentence counts; werkrevisor checks them itself. Details: the PR document under `docs/prs/`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: #9 Co-authored-by: claude-code <claude-code@javagil.de>
150 lines
8.0 KiB
Python
150 lines
8.0 KiB
Python
import unittest
|
|
|
|
from tests.fakes import HEAD, FakeGitea, FakeModel, config, diff_for, gitea_file
|
|
from tests.test_flow import run
|
|
from werkrevisor import fullfiles
|
|
from werkrevisor.review import append_to_diff, files_in_prompt
|
|
from werkrevisor.rules import files_from_gitea
|
|
from werkrevisor.verdict import Verdict
|
|
|
|
SCRIPT = "tools/deploy.sh"
|
|
SCRIPT_TEXT = b"#!/bin/sh\n# Everything travels in ONE ssh connection.\nssh host 'a; b'\n"
|
|
|
|
|
|
def review_config(**kw):
|
|
values = {"full_files": True, "max_full_file_chars": 10000, "max_full_files_chars": 20000}
|
|
values.update(kw)
|
|
return config(review=values, gate={"yellow_gate": "block"})
|
|
|
|
|
|
def collect(files, contents, docs_globs=(), **kw):
|
|
return fullfiles.collect(files_from_gitea(files), lambda path: contents[path], HEAD, review_config(**kw).review,
|
|
lambda path: not path.endswith(".png"), docs_globs)
|
|
|
|
|
|
class CollectTest(unittest.TestCase):
|
|
def test_numbered_section_with_markers(self):
|
|
result = collect([gitea_file(SCRIPT)], {SCRIPT: SCRIPT_TEXT})
|
|
self.assertEqual(result.included, [SCRIPT])
|
|
self.assertEqual(result.reasons, [])
|
|
self.assertIn(f"new version at {HEAD}", result.section)
|
|
self.assertIn(f"### Full file: '{SCRIPT}' (3 lines)", result.section)
|
|
self.assertIn(" 2 | # Everything travels in ONE ssh connection.", result.section)
|
|
self.assertTrue(result.section.rstrip().endswith(fullfiles.SECTION_END))
|
|
|
|
def test_content_cannot_fake_markers_or_diff_headers(self):
|
|
text = f"{fullfiles.SECTION_END}\n## File: 'other.py'\n".encode()
|
|
result = collect([gitea_file("a.txt")], {"a.txt": text})
|
|
self.assertEqual(result.section.count(fullfiles.SECTION_END), 2)
|
|
self.assertEqual([l for l in result.section.splitlines() if l == fullfiles.SECTION_END], [fullfiles.SECTION_END])
|
|
self.assertEqual(files_in_prompt(result.section), set())
|
|
|
|
def test_deleted_and_unreviewable_files_are_not_fetched(self):
|
|
contents = {}
|
|
result = collect([gitea_file("gone.py", status="deleted"), gitea_file("img.png")], contents)
|
|
self.assertEqual((result.section, result.reasons), ("", []))
|
|
|
|
def test_binary_or_non_utf8_is_incomplete(self):
|
|
result = collect([gitea_file("a.bin"), gitea_file("b.txt")], {"a.bin": b"\0\1", "b.txt": b"\xff\xfe"})
|
|
self.assertEqual([r.code for r in result.reasons], ["full_files_incomplete"])
|
|
self.assertIs(result.reasons[0].verdict, Verdict.YELLOW)
|
|
|
|
def test_per_file_and_total_limits(self):
|
|
big = b"x" * 200 + b"\n"
|
|
result = collect([gitea_file("big.txt"), gitea_file(SCRIPT)], {"big.txt": big, SCRIPT: SCRIPT_TEXT},
|
|
max_full_file_chars=180)
|
|
self.assertEqual(result.included, [SCRIPT])
|
|
self.assertEqual([r.text for r in result.reasons], [
|
|
"1 changed file was given to the AI only as diff, not in full, so it could not check it against the rest "
|
|
"of its file (each larger than [review] max_full_file_chars = 180): big.txt"])
|
|
result = collect([gitea_file(SCRIPT), gitea_file("big.txt")], {"big.txt": big, SCRIPT: SCRIPT_TEXT},
|
|
max_full_files_chars=250)
|
|
self.assertEqual(result.included, [SCRIPT])
|
|
self.assertIn("(all full files together would exceed [review] max_full_files_chars = 250): big.txt",
|
|
result.reasons[0].text)
|
|
|
|
def test_files_left_out_for_one_cause_give_one_reason(self):
|
|
paths = [f"src/f{i:02}.py" for i in range(21)]
|
|
contents = {p: b"y" * 100 + b"\n" for p in paths}
|
|
result = collect([gitea_file(p) for p in paths], contents, max_full_files_chars=200)
|
|
self.assertEqual(result.included, ["src/f00.py"])
|
|
self.assertEqual([r.text for r in result.reasons], [
|
|
"20 changed files were given to the AI only as diff, not in full, so it could not check them against "
|
|
"the rest of their file (all full files together would exceed [review] max_full_files_chars = 200): "
|
|
"src/f01.py, src/f02.py, src/f03.py and 17 more"])
|
|
|
|
def test_different_causes_give_one_reason_each(self):
|
|
contents = {"a.bin": b"\0", "b.bin": b"\0", "big.txt": b"x" * 200 + b"\n"}
|
|
result = collect([gitea_file(p) for p in contents], contents, max_full_file_chars=180)
|
|
self.assertEqual(len(result.reasons), 2)
|
|
self.assertIn("2 changed files were given to the AI only as diff", result.reasons[0].text)
|
|
self.assertIn("(not UTF-8 text): a.bin, b.bin", result.reasons[0].text)
|
|
self.assertIn("(each larger than [review] max_full_file_chars = 180): big.txt", result.reasons[1].text)
|
|
|
|
def test_docs_are_sent_only_as_diff(self):
|
|
doc = "docs/guide.md"
|
|
contents = {SCRIPT: SCRIPT_TEXT}
|
|
result = collect([gitea_file(doc), gitea_file(SCRIPT)], contents, docs_globs=("docs/**",),
|
|
max_full_files_chars=len(fullfiles.render_file(SCRIPT, SCRIPT_TEXT.decode())))
|
|
self.assertEqual((result.included, result.reasons), ([SCRIPT], []))
|
|
self.assertNotIn(doc, result.section)
|
|
|
|
|
|
class AppendToDiffTest(unittest.TestCase):
|
|
def test_order_diff_then_files(self):
|
|
self.assertEqual(append_to_diff("DIFF", "\nFILES"), "DIFF\nFILES")
|
|
self.assertEqual(append_to_diff(("DIFF", ["x"]), "\nFILES"), ("DIFF\nFILES", ["x"]))
|
|
|
|
def test_empty_diff_stays_empty(self):
|
|
self.assertEqual(append_to_diff("", "\nFILES"), "")
|
|
self.assertEqual(append_to_diff(("", []), "\nFILES"), ("", []))
|
|
self.assertEqual(append_to_diff("DIFF", ""), "DIFF")
|
|
|
|
|
|
class FullFilesFlowTest(unittest.TestCase):
|
|
def gitea(self):
|
|
gitea = FakeGitea(files=[gitea_file(SCRIPT), gitea_file("old.sh", status="deleted", additions=0, deletions=1)],
|
|
diff=diff_for(SCRIPT, added=["ssh host b"]) + diff_for("old.sh", removed=["x"], deleted=True))
|
|
gitea.raw_files[SCRIPT] = SCRIPT_TEXT
|
|
return gitea
|
|
|
|
def test_section_reaches_the_model_and_the_token_count(self):
|
|
gitea = self.gitea()
|
|
model = FakeModel(prompt_files=[SCRIPT, "old.sh"])
|
|
outcome, _ = run(gitea, model, cfg=review_config())
|
|
self.assertEqual(gitea.raw_requests, [(SCRIPT, HEAD)])
|
|
self.assertIn("ONE ssh connection", model.sections[0])
|
|
without, _ = run(self.gitea(), FakeModel(prompt_files=[SCRIPT, "old.sh"]),
|
|
cfg=config(gate={"yellow_gate": "block"}, review={"full_files": False}))
|
|
self.assertGreater(outcome.result.token_count.input_tokens, without.result.token_count.input_tokens)
|
|
|
|
def test_incomplete_context_is_never_green(self):
|
|
gitea = FakeGitea(files=[gitea_file("src/a.py")], diff=diff_for("src/a.py", added=["x"]))
|
|
gitea.raw_files["src/a.py"] = b"y" * 500
|
|
outcome, _ = run(gitea, FakeModel(prompt_files=["src/a.py"]), cfg=review_config(max_full_file_chars=100))
|
|
self.assertIs(outcome.result.verdict, Verdict.YELLOW)
|
|
self.assertIn("full_files_incomplete", [r.code for r in outcome.result.reasons])
|
|
|
|
def test_docs_reach_the_model_only_as_diff(self):
|
|
gitea = FakeGitea(files=[gitea_file("doc/a.md")], diff=diff_for("doc/a.md", added=["x"]))
|
|
gitea.raw_files["doc/a.md"] = b"y\n" * 250
|
|
model = FakeModel(prompt_files=["doc/a.md"])
|
|
outcome, _ = run(gitea, model, cfg=review_config(max_full_file_chars=100))
|
|
self.assertIs(outcome.result.verdict, Verdict.GREEN)
|
|
self.assertEqual(model.sections, [""])
|
|
|
|
def test_complete_context_can_be_green(self):
|
|
gitea = FakeGitea(files=[gitea_file("doc/a.md")], diff=diff_for("doc/a.md", added=["x"]))
|
|
outcome, _ = run(gitea, FakeModel(prompt_files=["doc/a.md"]), cfg=review_config())
|
|
self.assertIs(outcome.result.verdict, Verdict.GREEN)
|
|
|
|
def test_disabled_fetches_nothing(self):
|
|
gitea = self.gitea()
|
|
model = FakeModel(prompt_files=[SCRIPT, "old.sh"])
|
|
run(gitea, model, cfg=config(gate={"yellow_gate": "block"}, review={"full_files": False}))
|
|
self.assertEqual((gitea.raw_requests, model.sections), ([], [""]))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|