fix(tools): enforce edit_file line hints
This commit is contained in:
@@ -70,7 +70,7 @@ def test_edit_file_expected_replacements_allows_replace_all_when_count_matches(t
|
||||
assert target.read_text() == "changed\nchanged\n"
|
||||
|
||||
|
||||
def test_edit_file_can_select_nearest_line_hint(tmp_path):
|
||||
def test_edit_file_line_hint_selects_matching_occurrence(tmp_path):
|
||||
target = tmp_path / "duplicate.txt"
|
||||
target.write_text("one\nsame\ntwo\nsame\n")
|
||||
tool = EditFileTool(workspace=tmp_path)
|
||||
@@ -86,7 +86,7 @@ def test_edit_file_can_select_nearest_line_hint(tmp_path):
|
||||
assert target.read_text() == "one\nsame\ntwo\nchanged\n"
|
||||
|
||||
|
||||
def test_edit_file_rejects_unique_match_far_from_line_hint(tmp_path):
|
||||
def test_edit_file_rejects_unique_match_outside_line_hint(tmp_path):
|
||||
target = tmp_path / "wrong-line.txt"
|
||||
target.write_text("one\nsame\ntwo\nother\n")
|
||||
tool = EditFileTool(workspace=tmp_path)
|
||||
@@ -95,48 +95,15 @@ def test_edit_file_rejects_unique_match_far_from_line_hint(tmp_path):
|
||||
path=str(target),
|
||||
old_text="same",
|
||||
new_text="changed",
|
||||
line_hint=20,
|
||||
line_hint=4,
|
||||
))
|
||||
|
||||
assert "line_hint 20 does not match the old_text location" in result
|
||||
assert "line_hint 4 does not match the old_text location" in result
|
||||
assert "old_text appears at line 2" in result
|
||||
assert target.read_text() == "one\nsame\ntwo\nother\n"
|
||||
|
||||
|
||||
def test_edit_file_target_line_selects_matching_occurrence(tmp_path):
|
||||
target = tmp_path / "duplicate.txt"
|
||||
target.write_text("one\nsame\ntwo\nsame\n")
|
||||
tool = EditFileTool(workspace=tmp_path)
|
||||
|
||||
result = asyncio.run(tool.execute(
|
||||
path=str(target),
|
||||
old_text="same",
|
||||
new_text="changed",
|
||||
target_line=4,
|
||||
))
|
||||
|
||||
assert "Successfully edited" in result
|
||||
assert target.read_text() == "one\nsame\ntwo\nchanged\n"
|
||||
|
||||
|
||||
def test_edit_file_target_line_rejects_unique_match_on_wrong_line(tmp_path):
|
||||
target = tmp_path / "wrong-line.txt"
|
||||
target.write_text("one\nsame\ntwo\nother\n")
|
||||
tool = EditFileTool(workspace=tmp_path)
|
||||
|
||||
result = asyncio.run(tool.execute(
|
||||
path=str(target),
|
||||
old_text="same",
|
||||
new_text="changed",
|
||||
target_line=4,
|
||||
))
|
||||
|
||||
assert "target_line 4 does not match the old_text location" in result
|
||||
assert "old_text appears at line 2" in result
|
||||
assert target.read_text() == "one\nsame\ntwo\nother\n"
|
||||
|
||||
|
||||
def test_edit_file_target_line_can_cover_multiline_match(tmp_path):
|
||||
def test_edit_file_line_hint_can_cover_multiline_match(tmp_path):
|
||||
target = tmp_path / "block.txt"
|
||||
target.write_text("before\nstart\nmiddle\nend\nafter\n")
|
||||
tool = EditFileTool(workspace=tmp_path)
|
||||
@@ -145,64 +112,13 @@ def test_edit_file_target_line_can_cover_multiline_match(tmp_path):
|
||||
path=str(target),
|
||||
old_text="start\nmiddle\nend",
|
||||
new_text="start\nchanged\nend",
|
||||
target_line=3,
|
||||
line_hint=3,
|
||||
))
|
||||
|
||||
assert "Successfully edited" in result
|
||||
assert target.read_text() == "before\nstart\nchanged\nend\nafter\n"
|
||||
|
||||
|
||||
def test_edit_file_target_start_line_rejects_context_that_starts_too_early(tmp_path):
|
||||
target = tmp_path / "block.txt"
|
||||
target.write_text("before\nstart\nmiddle\nend\nafter\n")
|
||||
tool = EditFileTool(workspace=tmp_path)
|
||||
|
||||
result = asyncio.run(tool.execute(
|
||||
path=str(target),
|
||||
old_text="before\nstart\nmiddle",
|
||||
new_text="before\nchanged\nmiddle",
|
||||
target_line=2,
|
||||
target_start_line=2,
|
||||
))
|
||||
|
||||
assert "target_start_line 2 does not match the old_text start" in result
|
||||
assert target.read_text() == "before\nstart\nmiddle\nend\nafter\n"
|
||||
|
||||
|
||||
def test_edit_file_target_start_line_allows_exact_block_start(tmp_path):
|
||||
target = tmp_path / "block.txt"
|
||||
target.write_text("before\nstart\nmiddle\nend\nafter\n")
|
||||
tool = EditFileTool(workspace=tmp_path)
|
||||
|
||||
result = asyncio.run(tool.execute(
|
||||
path=str(target),
|
||||
old_text="start\nmiddle\nend",
|
||||
new_text="start\nchanged\nend",
|
||||
target_line=3,
|
||||
target_start_line=2,
|
||||
))
|
||||
|
||||
assert "Successfully edited" in result
|
||||
assert target.read_text() == "before\nstart\nchanged\nend\nafter\n"
|
||||
|
||||
|
||||
def test_edit_file_target_line_error_wins_when_start_line_matches(tmp_path):
|
||||
target = tmp_path / "block.txt"
|
||||
target.write_text("before\nstart\nmiddle\nend\nafter\n")
|
||||
tool = EditFileTool(workspace=tmp_path)
|
||||
|
||||
result = asyncio.run(tool.execute(
|
||||
path=str(target),
|
||||
old_text="start\nmiddle",
|
||||
new_text="changed\nmiddle",
|
||||
target_line=4,
|
||||
target_start_line=2,
|
||||
))
|
||||
|
||||
assert "target_line 4 does not match the old_text location" in result
|
||||
assert target.read_text() == "before\nstart\nmiddle\nend\nafter\n"
|
||||
|
||||
|
||||
def test_edit_file_can_edit_ipynb_as_json(tmp_path):
|
||||
target = tmp_path / "analysis.ipynb"
|
||||
target.write_text('{"cells": []}')
|
||||
@@ -236,18 +152,18 @@ def test_edit_file_multiple_match_hint_mentions_occurrence(tmp_path):
|
||||
|
||||
def test_edit_file_rejects_ambiguous_line_hint(tmp_path):
|
||||
target = tmp_path / "duplicate.txt"
|
||||
target.write_text("same\nmiddle\nsame\n")
|
||||
target.write_text("same same\n")
|
||||
tool = EditFileTool(workspace=tmp_path)
|
||||
|
||||
result = asyncio.run(tool.execute(
|
||||
path=str(target),
|
||||
old_text="same",
|
||||
new_text="changed",
|
||||
line_hint=2,
|
||||
line_hint=1,
|
||||
))
|
||||
|
||||
assert "line_hint 2 is ambiguous" in result
|
||||
assert target.read_text() == "same\nmiddle\nsame\n"
|
||||
assert "line_hint 1 is ambiguous" in result
|
||||
assert target.read_text() == "same same\n"
|
||||
|
||||
|
||||
def test_edit_file_rejects_occurrence_with_replace_all(tmp_path):
|
||||
@@ -301,40 +217,6 @@ def test_edit_file_rejects_line_hint_with_occurrence(tmp_path):
|
||||
assert target.read_text() == "same\nsame\n"
|
||||
|
||||
|
||||
def test_edit_file_rejects_target_line_with_occurrence(tmp_path):
|
||||
target = tmp_path / "duplicate.txt"
|
||||
target.write_text("same\nsame\n")
|
||||
tool = EditFileTool(workspace=tmp_path)
|
||||
|
||||
result = asyncio.run(tool.execute(
|
||||
path=str(target),
|
||||
old_text="same",
|
||||
new_text="changed",
|
||||
occurrence=1,
|
||||
target_line=1,
|
||||
))
|
||||
|
||||
assert "target_line cannot be used with occurrence" in result
|
||||
assert target.read_text() == "same\nsame\n"
|
||||
|
||||
|
||||
def test_edit_file_rejects_target_start_line_with_occurrence(tmp_path):
|
||||
target = tmp_path / "duplicate.txt"
|
||||
target.write_text("same\nsame\n")
|
||||
tool = EditFileTool(workspace=tmp_path)
|
||||
|
||||
result = asyncio.run(tool.execute(
|
||||
path=str(target),
|
||||
old_text="same",
|
||||
new_text="changed",
|
||||
occurrence=1,
|
||||
target_start_line=1,
|
||||
))
|
||||
|
||||
assert "target_start_line cannot be used with occurrence" in result
|
||||
assert target.read_text() == "same\nsame\n"
|
||||
|
||||
|
||||
def test_edit_file_rejects_zero_occurrence(tmp_path):
|
||||
target = tmp_path / "duplicate.txt"
|
||||
target.write_text("same\n")
|
||||
@@ -365,35 +247,3 @@ def test_edit_file_rejects_zero_line_hint(tmp_path):
|
||||
|
||||
assert "line_hint must be >= 1" in result
|
||||
assert target.read_text() == "same\n"
|
||||
|
||||
|
||||
def test_edit_file_rejects_zero_target_line(tmp_path):
|
||||
target = tmp_path / "duplicate.txt"
|
||||
target.write_text("same\n")
|
||||
tool = EditFileTool(workspace=tmp_path)
|
||||
|
||||
result = asyncio.run(tool.execute(
|
||||
path=str(target),
|
||||
old_text="same",
|
||||
new_text="changed",
|
||||
target_line=0,
|
||||
))
|
||||
|
||||
assert "target_line must be >= 1" in result
|
||||
assert target.read_text() == "same\n"
|
||||
|
||||
|
||||
def test_edit_file_rejects_zero_target_start_line(tmp_path):
|
||||
target = tmp_path / "duplicate.txt"
|
||||
target.write_text("same\n")
|
||||
tool = EditFileTool(workspace=tmp_path)
|
||||
|
||||
result = asyncio.run(tool.execute(
|
||||
path=str(target),
|
||||
old_text="same",
|
||||
new_text="changed",
|
||||
target_start_line=0,
|
||||
))
|
||||
|
||||
assert "target_start_line must be >= 1" in result
|
||||
assert target.read_text() == "same\n"
|
||||
|
||||
Reference in New Issue
Block a user