hermes / tests /tools /test_patch_parser.py
lenson78's picture
initial upload: v2026.3.23 with HF Spaces deployment
9aa5185 verified
"""Tests for the V4A patch format parser."""
from types import SimpleNamespace
from tools.patch_parser import (
OperationType,
apply_v4a_operations,
parse_v4a_patch,
)
class TestParseUpdateFile:
def test_basic_update(self):
patch = """\
*** Begin Patch
*** Update File: src/main.py
@@ def greet @@
def greet():
- print("hello")
+ print("hi")
*** End Patch"""
ops, err = parse_v4a_patch(patch)
assert err is None
assert len(ops) == 1
op = ops[0]
assert op.operation == OperationType.UPDATE
assert op.file_path == "src/main.py"
assert len(op.hunks) == 1
hunk = op.hunks[0]
assert hunk.context_hint == "def greet"
prefixes = [l.prefix for l in hunk.lines]
assert " " in prefixes
assert "-" in prefixes
assert "+" in prefixes
def test_multiple_hunks(self):
patch = """\
*** Begin Patch
*** Update File: f.py
@@ first @@
a
-b
+c
@@ second @@
x
-y
+z
*** End Patch"""
ops, err = parse_v4a_patch(patch)
assert err is None
assert len(ops) == 1
assert len(ops[0].hunks) == 2
assert ops[0].hunks[0].context_hint == "first"
assert ops[0].hunks[1].context_hint == "second"
class TestParseAddFile:
def test_add_file(self):
patch = """\
*** Begin Patch
*** Add File: new/module.py
+import os
+
+print("hello")
*** End Patch"""
ops, err = parse_v4a_patch(patch)
assert err is None
assert len(ops) == 1
op = ops[0]
assert op.operation == OperationType.ADD
assert op.file_path == "new/module.py"
assert len(op.hunks) == 1
contents = [l.content for l in op.hunks[0].lines if l.prefix == "+"]
assert contents[0] == "import os"
assert contents[2] == 'print("hello")'
class TestParseDeleteFile:
def test_delete_file(self):
patch = """\
*** Begin Patch
*** Delete File: old/stuff.py
*** End Patch"""
ops, err = parse_v4a_patch(patch)
assert err is None
assert len(ops) == 1
assert ops[0].operation == OperationType.DELETE
assert ops[0].file_path == "old/stuff.py"
class TestParseMoveFile:
def test_move_file(self):
patch = """\
*** Begin Patch
*** Move File: old/path.py -> new/path.py
*** End Patch"""
ops, err = parse_v4a_patch(patch)
assert err is None
assert len(ops) == 1
assert ops[0].operation == OperationType.MOVE
assert ops[0].file_path == "old/path.py"
assert ops[0].new_path == "new/path.py"
class TestParseInvalidPatch:
def test_empty_patch_returns_empty_ops(self):
ops, err = parse_v4a_patch("")
assert err is None
assert ops == []
def test_no_begin_marker_still_parses(self):
patch = """\
*** Update File: f.py
line1
-old
+new
*** End Patch"""
ops, err = parse_v4a_patch(patch)
assert err is None
assert len(ops) == 1
def test_multiple_operations(self):
patch = """\
*** Begin Patch
*** Add File: a.py
+content_a
*** Delete File: b.py
*** Update File: c.py
keep
-remove
+add
*** End Patch"""
ops, err = parse_v4a_patch(patch)
assert err is None
assert len(ops) == 3
assert ops[0].operation == OperationType.ADD
assert ops[1].operation == OperationType.DELETE
assert ops[2].operation == OperationType.UPDATE
class TestApplyUpdate:
def test_preserves_non_prefix_pipe_characters_in_unmodified_lines(self):
patch = """\
*** Begin Patch
*** Update File: sample.py
@@ result @@
result = 1
- return result
+ return result + 1
*** End Patch"""
operations, err = parse_v4a_patch(patch)
assert err is None
class FakeFileOps:
def __init__(self):
self.written = None
def read_file(self, path, offset=1, limit=500):
return SimpleNamespace(
content=(
'def run():\n'
' cmd = "echo a | sed s/a/b/"\n'
' result = 1\n'
' return result'
),
error=None,
)
def write_file(self, path, content):
self.written = content
return SimpleNamespace(error=None)
file_ops = FakeFileOps()
result = apply_v4a_operations(operations, file_ops)
assert result.success is True
assert file_ops.written == (
'def run():\n'
' cmd = "echo a | sed s/a/b/"\n'
' result = 1\n'
' return result + 1'
)