Download tests/test_patch_file.py from elyanlabs/trashclaw: direct link, hf CLI and curl.
- Browser
- Download file 6.55 kB
-
https://huggingface.co/elyanlabs/trashclaw/resolve/main/tests/test_patch_file.py
- Command line
-
hf download hf://elyanlabs/trashclaw/tests/test_patch_file.py
-
curl -L -o test_patch_file.py https://huggingface.co/elyanlabs/trashclaw/resolve/main/tests/test_patch_file.py
6.55 kB
| # SPDX-License-Identifier: MIT | |
| """Tests for tool_patch_file. | |
| Standard unified diffs carry context lines. The old implementation ignored | |
| them and removed lines counted from the hunk start, so a diff that changed | |
| line 2 deleted line 1 instead and still reported success. | |
| """ | |
| import os | |
| import sys | |
| import pytest | |
| sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) | |
| import trashclaw | |
| def isolate(tmp_path, monkeypatch): | |
| monkeypatch.setattr(trashclaw, "CWD", str(tmp_path)) | |
| monkeypatch.setattr(trashclaw, "UNDO_STACK", []) | |
| return tmp_path | |
| def _write(tmp_path, text, name="f.txt"): | |
| p = tmp_path / name | |
| p.write_text(text) | |
| return p | |
| def test_context_lines_are_honoured(tmp_path): | |
| p = _write(tmp_path, "a\nb\nc\n") | |
| patch = "--- a/f.txt\n+++ b/f.txt\n@@ -1,3 +1,3 @@\n a\n-b\n+B\n c\n" | |
| result = trashclaw.tool_patch_file(str(p), patch) | |
| assert result.startswith("Patched") | |
| assert p.read_text() == "a\nB\nc\n" | |
| def test_mismatched_hunk_is_rejected_and_file_untouched(tmp_path): | |
| p = _write(tmp_path, "a\nb\nc\n") | |
| result = trashclaw.tool_patch_file(str(p), "@@ -1,3 +1,3 @@\n a\n-x\n+X\n c\n") | |
| assert result.startswith("Error") | |
| assert "No changes written" in result | |
| assert p.read_text() == "a\nb\nc\n" | |
| assert trashclaw.UNDO_STACK == [] # nothing was changed, nothing to undo | |
| def test_partial_failure_writes_nothing(tmp_path): | |
| original = "".join(f"l{i}\n" for i in range(1, 11)) | |
| p = _write(tmp_path, original) | |
| patch = ("@@ -1,2 +1,2 @@\n l1\n-l2\n+L2\n" | |
| "@@ -8,2 +8,2 @@\n l8\n-nope\n+NOPE\n") | |
| result = trashclaw.tool_patch_file(str(p), patch) | |
| assert "hunk 2 of 2" in result | |
| assert p.read_text() == original | |
| def test_multiple_hunks_with_line_count_changes(tmp_path): | |
| p = _write(tmp_path, "".join(f"l{i}\n" for i in range(1, 21))) | |
| patch = ("@@ -1,3 +1,3 @@\n l1\n-l2\n+L2\n l3\n" | |
| "@@ -9,3 +9,4 @@\n l9\n-l10\n+L10\n+new\n l11\n") | |
| result = trashclaw.tool_patch_file(str(p), patch) | |
| assert "2 hunks, 3 additions, 2 removals" in result | |
| lines = p.read_text().splitlines() | |
| assert lines[:3] == ["l1", "L2", "l3"] | |
| assert lines[8:12] == ["l9", "L10", "new", "l11"] | |
| assert len(lines) == 21 | |
| def test_drifted_line_numbers_still_apply(tmp_path): | |
| p = _write(tmp_path, "x\ny\na\nb\nc\n") | |
| result = trashclaw.tool_patch_file(str(p), "@@ -1,3 +1,3 @@\n a\n-b\n+B\n c\n") | |
| assert result.startswith("Patched") | |
| assert p.read_text() == "x\ny\na\nB\nc\n" | |
| def test_blank_context_line_without_leading_space(tmp_path): | |
| p = _write(tmp_path, "def f():\n\n return 1\n") | |
| patch = "@@ -1,3 +1,3 @@\n def f():\n\n- return 1\n+ return 2\n" | |
| result = trashclaw.tool_patch_file(str(p), patch) | |
| assert result.startswith("Patched") | |
| assert p.read_text() == "def f():\n\n return 2\n" | |
| def test_pure_insertion_after_line(tmp_path): | |
| p = _write(tmp_path, "a\nb\n") | |
| result = trashclaw.tool_patch_file(str(p), "@@ -2,0 +3,1 @@\n+c\n") | |
| assert result.startswith("Patched") | |
| assert p.read_text() == "a\nb\nc\n" | |
| def test_create_new_file(tmp_path): | |
| p = tmp_path / "new.txt" | |
| result = trashclaw.tool_patch_file(str(p), "--- /dev/null\n+++ b/new.txt\n@@ -0,0 +1,2 @@\n+x\n+y\n") | |
| assert result.startswith("Patched") | |
| assert p.read_text() == "x\ny\n" | |
| def test_patch_without_hunks_is_an_error(tmp_path): | |
| p = _write(tmp_path, "a\n") | |
| result = trashclaw.tool_patch_file(str(p), "just some text") | |
| assert result.startswith("Error") | |
| assert p.read_text() == "a\n" | |
| def test_successful_patch_is_undoable(tmp_path): | |
| p = _write(tmp_path, "a\nb\n") | |
| trashclaw.tool_patch_file(str(p), "@@ -1,2 +1,2 @@\n a\n-b\n+B\n") | |
| assert trashclaw.UNDO_STACK[-1]["content"] == "a\nb\n" | |
| assert trashclaw.UNDO_STACK[-1]["action"] == "patch" | |
| # --- Review follow-ups: each case below was reported as "Patched" before. --- | |
| def _assert_refused(p, result, original): | |
| assert result.startswith("Error"), result | |
| assert "No changes written" in result | |
| assert p.read_text() == original | |
| assert trashclaw.UNDO_STACK == [] | |
| def test_removed_and_added_lines_that_look_like_file_headers(tmp_path): | |
| # "-- old" / "++ new" (Lua/SQL comments) show up in the diff as | |
| # "--- old" / "+++ new"; the hunk counts say they are body lines. | |
| p = _write(tmp_path, "a\nb\n-- x\nc\n") | |
| result = trashclaw.tool_patch_file(str(p), "@@ -2,2 +2,2 @@\n-b\n+B\n--- x\n+++ y\n") | |
| assert result.startswith("Patched"), result | |
| assert p.read_text() == "a\nB\n++ y\nc\n" | |
| def test_hunk_shorter_than_header_is_refused(tmp_path): | |
| p = _write(tmp_path, "a\nb\nc\n") | |
| result = trashclaw.tool_patch_file(str(p), "@@ -1,3 +1,3 @@\n a\n-b\n+B\n") | |
| _assert_refused(p, result, "a\nb\nc\n") | |
| assert "-3/+3" in result | |
| def test_hunk_longer_than_header_is_refused(tmp_path): | |
| p = _write(tmp_path, "a\nb\nc\n") | |
| result = trashclaw.tool_patch_file(str(p), "@@ -1,2 +1,2 @@\n a\n-b\n+B\n c\n") | |
| _assert_refused(p, result, "a\nb\nc\n") | |
| def test_trailing_blank_context_line_is_verified(tmp_path): | |
| p = _write(tmp_path, "a\nX\n") | |
| result = trashclaw.tool_patch_file(str(p), "@@ -1,2 +1,2 @@\n-a\n+A\n \n") | |
| _assert_refused(p, result, "a\nX\n") | |
| q = _write(tmp_path, "a\n\n", name="g.txt") | |
| assert trashclaw.tool_patch_file(str(q), "@@ -1,2 +1,2 @@\n-a\n+A\n \n").startswith("Patched") | |
| assert q.read_text() == "A\n\n" | |
| def test_drift_is_carried_into_later_insertion(tmp_path): | |
| p = _write(tmp_path, "x\na\nb\n") | |
| result = trashclaw.tool_patch_file(str(p), "@@ -1 +1 @@\n-a\n+A\n@@ -2,0 +3,1 @@\n+c\n") | |
| assert result.startswith("Patched"), result | |
| assert p.read_text() == "x\nA\nb\nc\n" | |
| def test_out_of_range_insertion_is_refused(tmp_path): | |
| p = _write(tmp_path, "a\nb\n") | |
| result = trashclaw.tool_patch_file(str(p), "@@ -999,0 +1000,1 @@\n+z\n") | |
| _assert_refused(p, result, "a\nb\n") | |
| def test_insertion_after_last_line_without_newline(tmp_path): | |
| p = _write(tmp_path, "a\nb") | |
| result = trashclaw.tool_patch_file(str(p), "@@ -2,0 +3,1 @@\n+c\n") | |
| assert result.startswith("Patched"), result | |
| assert p.read_text() == "a\nb\nc\n" | |
| def test_multi_file_diff_is_refused(tmp_path): | |
| p = _write(tmp_path, "a\n") | |
| patch = ("--- a/f.txt\n+++ b/f.txt\n@@ -1 +1 @@\n-a\n+A\n" | |
| "--- a/g.txt\n+++ b/g.txt\n@@ -1 +1 @@\n-A\n+Z\n") | |
| result = trashclaw.tool_patch_file(str(p), patch) | |
| _assert_refused(p, result, "a\n") | |
| assert "more than one file" in result | |