diff --git a/tests/tools/test_file_tools_live.py b/tests/tools/test_file_tools_live.py index 84ae37ed16673..81a11c061ad31 100644 --- a/tests/tools/test_file_tools_live.py +++ b/tests/tools/test_file_tools_live.py @@ -179,6 +179,19 @@ class TestPatchReplace: assert Path(path).read_text() == "hello earth\n" + def test_identical_replacement_explains_no_change(self, ops, tmp_path): + path = str(tmp_path / "unchanged.txt") + Path(path).write_text("hello world\n") + + result = ops.patch_replace(path, "world", "world") + + assert result.success is False + assert result.error is not None + assert "No edit was applied" in result.error + assert "existing text to replace in old_string" in result.error + assert "replacement text in new_string" in result.error + assert Path(path).read_text() == "hello world\n" + def test_multiline_patch(self, ops, tmp_path): path = str(tmp_path / "multi.txt") Path(path).write_text("line1\nline2\nline3\n") diff --git a/tests/tools/test_fuzzy_match.py b/tests/tools/test_fuzzy_match.py index 056ce93b1a440..e89afc936563b 100644 --- a/tests/tools/test_fuzzy_match.py +++ b/tests/tools/test_fuzzy_match.py @@ -25,6 +25,15 @@ class TestExactMatch: assert count == 0 assert err is not None + def test_identical_strings(self): + new, count, _, err = fuzzy_find_and_replace("abc", "abc", "abc") + assert count == 0 + assert new == "abc" + assert err == ( + "No edit was applied because old_string and new_string are identical. " + "Provide the existing text to replace in old_string and the changed " + "replacement text in new_string." + ) def test_multiline_exact(self): content = "line1\nline2\nline3" @@ -431,6 +440,28 @@ class TestFormatNoMatchHint: ) assert result == "" + def test_silent_on_identical_strings(self): + """old_string == new_string — hint irrelevant.""" + result = self.fmt( + "No edit was applied because old_string and new_string are identical. " + "Provide the existing text to replace in old_string and the changed " + "replacement text in new_string.", + 0, "foo", "foo bar\n", + ) + assert result == "" + + def test_silent_when_match_count_nonzero(self): + """If match succeeded, we shouldn't be in the error path — defense in depth.""" + result = self.fmt( + "Could not find a match for old_string in the file", + 1, "foo", "foo bar\n", + ) + assert result == "" + + def test_silent_on_none_error(self): + """No error at all — no hint.""" + result = self.fmt(None, 0, "foo", "bar\n") + assert result == "" def test_silent_when_no_similar_content(self): """Even for a valid no-match error, skip hint when nothing similar exists.""" diff --git a/tools/fuzzy_match.py b/tools/fuzzy_match.py index b2c17e7913a00..6c142e1253abc 100644 --- a/tools/fuzzy_match.py +++ b/tools/fuzzy_match.py @@ -143,7 +143,11 @@ def fuzzy_find_and_replace(content: str, old_string: str, new_string: str, return content, 0, None, "old_string is only whitespace — provide non-blank text to match" if old_string == new_string: - return content, 0, None, "old_string and new_string are identical" + return content, 0, None, ( + "No edit was applied because old_string and new_string are identical. " + "Provide the existing text to replace in old_string and the changed " + "replacement text in new_string." + ) # Try each matching strategy in order strategies: List[Tuple[str, Callable]] = [