mirror of
https://github.com/open-jarvis/OpenJarvis.git
synced 2026-07-27 21:05:34 +00:00
* fix(channels): wire channel→agent handler and fix Telegram send pipeline * format code * add supported tests
197 lines
6.7 KiB
Python
197 lines
6.7 KiB
Python
"""Tests for the apply_patch tool."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from openjarvis.tools.apply_patch import ApplyPatchTool
|
|
|
|
|
|
class TestApplyPatchTool:
|
|
def test_spec(self):
|
|
tool = ApplyPatchTool()
|
|
assert tool.spec.name == "apply_patch"
|
|
assert tool.spec.category == "filesystem"
|
|
assert "file:write" in tool.spec.required_capabilities
|
|
|
|
def test_no_patch_provided(self):
|
|
tool = ApplyPatchTool()
|
|
result = tool.execute(patch="")
|
|
assert result.success is False
|
|
assert "No patch provided" in result.content
|
|
|
|
def test_simple_one_hunk_patch(self, tmp_path):
|
|
f = tmp_path / "hello.txt"
|
|
f.write_text("line1\nline2\nline3\n", encoding="utf-8")
|
|
patch = (
|
|
"--- a/hello.txt\n"
|
|
"+++ b/hello.txt\n"
|
|
"@@ -1,3 +1,3 @@\n"
|
|
" line1\n"
|
|
"-line2\n"
|
|
"+line2_modified\n"
|
|
" line3\n"
|
|
)
|
|
tool = ApplyPatchTool()
|
|
result = tool.execute(patch=patch, path=str(f), backup=False)
|
|
assert result.success is True
|
|
assert result.metadata["hunks_applied"] == 1
|
|
content = f.read_text(encoding="utf-8")
|
|
assert "line2_modified" in content
|
|
assert "line2\n" not in content
|
|
|
|
def test_multi_hunk_patch(self, tmp_path):
|
|
f = tmp_path / "multi.txt"
|
|
f.write_text(
|
|
"alpha\nbeta\ngamma\ndelta\nepsilon\nzeta\neta\ntheta\n",
|
|
encoding="utf-8",
|
|
)
|
|
patch = (
|
|
"--- a/multi.txt\n"
|
|
"+++ b/multi.txt\n"
|
|
"@@ -1,3 +1,3 @@\n"
|
|
" alpha\n"
|
|
"-beta\n"
|
|
"+BETA\n"
|
|
" gamma\n"
|
|
"@@ -6,3 +6,3 @@\n"
|
|
" zeta\n"
|
|
"-eta\n"
|
|
"+ETA\n"
|
|
" theta\n"
|
|
)
|
|
tool = ApplyPatchTool()
|
|
result = tool.execute(patch=patch, path=str(f), backup=False)
|
|
assert result.success is True
|
|
assert result.metadata["hunks_applied"] == 2
|
|
content = f.read_text(encoding="utf-8")
|
|
assert "BETA" in content
|
|
assert "ETA" in content
|
|
assert "beta" not in content
|
|
lines = content.splitlines()
|
|
assert "eta" not in lines
|
|
|
|
def test_context_mismatch_error(self, tmp_path):
|
|
f = tmp_path / "mismatch.txt"
|
|
f.write_text("aaa\nbbb\nccc\n", encoding="utf-8")
|
|
patch = (
|
|
"--- a/mismatch.txt\n"
|
|
"+++ b/mismatch.txt\n"
|
|
"@@ -1,3 +1,3 @@\n"
|
|
" aaa\n"
|
|
"-WRONG_CONTENT\n"
|
|
"+replaced\n"
|
|
" ccc\n"
|
|
)
|
|
tool = ApplyPatchTool()
|
|
result = tool.execute(patch=patch, path=str(f), backup=False)
|
|
assert result.success is False
|
|
assert "mismatch" in result.content.lower()
|
|
|
|
def test_backup_creation(self, tmp_path):
|
|
f = tmp_path / "backup_me.txt"
|
|
f.write_text("original\ncontent\n", encoding="utf-8")
|
|
patch = (
|
|
"--- a/backup_me.txt\n"
|
|
"+++ b/backup_me.txt\n"
|
|
"@@ -1,2 +1,2 @@\n"
|
|
" original\n"
|
|
"-content\n"
|
|
"+new_content\n"
|
|
)
|
|
tool = ApplyPatchTool()
|
|
result = tool.execute(patch=patch, path=str(f), backup=True)
|
|
assert result.success is True
|
|
assert "backup_path" in result.metadata
|
|
bak = tmp_path / "backup_me.txt.bak"
|
|
assert bak.exists()
|
|
assert bak.read_text(encoding="utf-8") == "original\ncontent\n"
|
|
assert "new_content" in f.read_text(encoding="utf-8")
|
|
|
|
def test_backup_disabled(self, tmp_path):
|
|
f = tmp_path / "no_bak.txt"
|
|
f.write_text("hello\nworld\n", encoding="utf-8")
|
|
patch = (
|
|
"--- a/no_bak.txt\n"
|
|
"+++ b/no_bak.txt\n"
|
|
"@@ -1,2 +1,2 @@\n"
|
|
"-hello\n"
|
|
"+goodbye\n"
|
|
" world\n"
|
|
)
|
|
tool = ApplyPatchTool()
|
|
result = tool.execute(patch=patch, path=str(f), backup=False)
|
|
assert result.success is True
|
|
assert "backup_path" not in result.metadata
|
|
bak = tmp_path / "no_bak.txt.bak"
|
|
assert not bak.exists()
|
|
|
|
def test_blocks_sensitive_files(self, tmp_path):
|
|
f = tmp_path / ".env"
|
|
f.write_text("SECRET=foo\n", encoding="utf-8")
|
|
patch = "--- a/.env\n+++ b/.env\n@@ -1 +1 @@\n-SECRET=foo\n+SECRET=bar\n"
|
|
tool = ApplyPatchTool()
|
|
result = tool.execute(patch=patch, path=str(f))
|
|
assert result.success is False
|
|
assert "sensitive" in result.content.lower()
|
|
|
|
def test_auto_detect_path_from_patch_header(self, tmp_path):
|
|
f = tmp_path / "auto.txt"
|
|
f.write_text("one\ntwo\nthree\n", encoding="utf-8")
|
|
patch = (
|
|
f"--- a/auto.txt\n+++ b/{f}\n@@ -1,3 +1,3 @@\n one\n-two\n+TWO\n three\n"
|
|
)
|
|
tool = ApplyPatchTool()
|
|
# No explicit path — should auto-detect from +++ header
|
|
result = tool.execute(patch=patch, backup=False)
|
|
assert result.success is True
|
|
assert "TWO" in f.read_text(encoding="utf-8")
|
|
|
|
def test_malformed_patch(self):
|
|
tool = ApplyPatchTool()
|
|
result = tool.execute(patch="this is not a patch at all")
|
|
assert result.success is False
|
|
lower = result.content.lower()
|
|
assert "malformed" in lower or "no hunks" in lower
|
|
|
|
def test_file_not_found(self):
|
|
tool = ApplyPatchTool()
|
|
patch = (
|
|
"--- a/nonexistent.txt\n+++ b/nonexistent.txt\n@@ -1 +1 @@\n-old\n+new\n"
|
|
)
|
|
result = tool.execute(patch=patch, path="/nonexistent/path/file.txt")
|
|
assert result.success is False
|
|
assert "not found" in result.content.lower()
|
|
|
|
def test_addition_only_hunk(self, tmp_path):
|
|
f = tmp_path / "add_only.txt"
|
|
f.write_text("first\nsecond\n", encoding="utf-8")
|
|
patch = (
|
|
"--- a/add_only.txt\n"
|
|
"+++ b/add_only.txt\n"
|
|
"@@ -1,2 +1,3 @@\n"
|
|
" first\n"
|
|
"+inserted\n"
|
|
" second\n"
|
|
)
|
|
tool = ApplyPatchTool()
|
|
result = tool.execute(patch=patch, path=str(f), backup=False)
|
|
assert result.success is True
|
|
content = f.read_text(encoding="utf-8")
|
|
assert content == "first\ninserted\nsecond\n"
|
|
|
|
def test_removal_only_hunk(self, tmp_path):
|
|
f = tmp_path / "del_only.txt"
|
|
f.write_text("keep\nremove_me\nkeep_too\n", encoding="utf-8")
|
|
patch = (
|
|
"--- a/del_only.txt\n"
|
|
"+++ b/del_only.txt\n"
|
|
"@@ -1,3 +1,2 @@\n"
|
|
" keep\n"
|
|
"-remove_me\n"
|
|
" keep_too\n"
|
|
)
|
|
tool = ApplyPatchTool()
|
|
result = tool.execute(patch=patch, path=str(f), backup=False)
|
|
assert result.success is True
|
|
content = f.read_text(encoding="utf-8")
|
|
assert content == "keep\nkeep_too\n"
|