test: trim #90322 regression to one invariant, drop the hint-splitting workaround

The credentials test no longer has to strip trailing text before json.loads;
the new file keeps the single behaviour contract (truncated output round-trips
through json.loads and carries the next offset) — the existing TestSearchHints
cases already cover offset arithmetic.
This commit is contained in:
kshitijk4poor
2026-09-06 22:59:37 +05:30
committed by kshitij
parent 12ff3da3e4
commit 8ed990e70f
2 changed files with 13 additions and 50 deletions
+1 -1
View File
@@ -150,7 +150,7 @@ def test_search_tool_filters_credential_results(fake_home, tmp_path, monkeypatch
path=str(fake_home),
task_id="search-filter-credentials",
)
out = json.loads(search_response.split("\n\n[Hint:", 1)[0])
out = json.loads(search_response)
raw = json.dumps(out)
returned_paths = {
match["path"] for match in out.get("matches", [])
+12 -49
View File
@@ -1,64 +1,27 @@
"""Regression tests for search_files truncation output staying pure JSON (#90322).
"""search_files output is one JSON document even when truncated (#90322).
The truncated-results hint used to be appended as plain text after the
serialized JSON payload (``{...}\\n\\n[Hint: ...]``), so the tool result was
no longer parseable JSON — downstream tool-message handling on providers
strict about tool-content formatting could reject or mishandle it. The hint
now rides inside the payload as a structured ``_hint`` field, matching the
existing ``_omitted``/``_warning`` side-channel convention in the same
function.
The pagination hint used to be appended as text after the serialized payload,
so json.loads on the tool result failed (execute_code RPC, strict tool-message
providers). It now rides inside the payload as ``_hint``.
"""
import json
from unittest.mock import MagicMock, patch
import pytest
class _FakeSearchResult:
"""Minimal stand-in for FileOperations.search return value."""
def __init__(self, truncated=False):
self.matches = []
self._truncated = truncated
class _TruncatedSearch:
matches = []
def to_dict(self, densify=False):
payload = {"matches": [{"file": "test.py", "line": 1, "text": "match"}]}
if self._truncated:
payload["truncated"] = True
payload["total_count"] = 20
return payload
return {"matches": [{"file": "t.py", "line": 1, "text": "m"}], "truncated": True, "total_count": 20}
def _make_fake_file_ops(truncated):
def test_truncated_search_result_round_trips_json():
fake = MagicMock()
fake.search = lambda **kw: _FakeSearchResult(truncated=truncated)
return fake
class TestSearchTruncationStaysJson:
@patch("tools.file_tools._get_file_ops", return_value=_make_fake_file_ops(True))
def test_truncated_result_is_parseable_json(self, _mock_ops):
"""The whole tool output must round-trip through json.loads — no
text appended after the serialized payload."""
from tools.file_tools import search_tool
raw = search_tool("def main", offset=0, limit=20, task_id="t1")
parsed = json.loads(raw) # raises on main: trailing "[Hint: ...]" text
assert parsed["truncated"] is True
@patch("tools.file_tools._get_file_ops", return_value=_make_fake_file_ops(True))
def test_truncation_hint_is_a_structured_field(self, _mock_ops):
fake.search = lambda **kw: _TruncatedSearch()
with patch("tools.file_tools._get_file_ops", return_value=fake):
from tools.file_tools import search_tool
parsed = json.loads(search_tool("def main", offset=0, limit=20, task_id="t1"))
assert "_hint" in parsed
assert "offset=20" in parsed["_hint"]
@patch("tools.file_tools._get_file_ops", return_value=_make_fake_file_ops(False))
def test_untruncated_result_has_no_hint(self, _mock_ops):
from tools.file_tools import search_tool
parsed = json.loads(search_tool("def main", task_id="t1"))
assert "truncated" not in parsed
assert "_hint" not in parsed
assert parsed["truncated"] is True
assert "offset=20" in parsed["_hint"]