"""The `memory_search` tool — argument handling and output shape."""
from datetime import datetime, timezone
from navi.memory.search import MemoryHit
from navi.tools._internal.base import ToolContext
from navi.tools.memory_search import MemorySearchTool
NOW = datetime(2026, 11, 9, 12, 0, tzinfo=timezone.utc)
class FakeSearch:
def __init__(self, hits=None):
self.hits = hits or []
self.calls = []
async def search(self, query, **kwargs):
self.calls.append((query, kwargs))
return self.hits
async def latest_summary_at(self, session_id):
return NOW if session_id == "s1" else None
class TestArguments:
async def test_a_missing_query_is_an_error(self):
result = await MemorySearchTool(FakeSearch()).execute({})
assert result.success is False
assert result.error == "missing query"
async def test_defaults_are_scope_all_and_k_five(self):
search = FakeSearch()
await MemorySearchTool(search).execute({"query": "port"})
query, kwargs = search.calls[0]
assert query == "port"
assert kwargs["scope"] == "all"
assert kwargs["k"] == 5
async def test_an_invalid_scope_falls_back_to_all(self):
search = FakeSearch()
await MemorySearchTool(search).execute({"query": "port", "scope": "everything"})
assert search.calls[0][1]["scope"] == "all"
async def test_k_is_clamped(self):
search = FakeSearch()
tool = MemorySearchTool(search)
await tool.execute({"query": "x", "k": 500})
await tool.execute({"query": "x", "k": "nope"})
assert search.calls[0][1]["k"] == 20
assert search.calls[1][1]["k"] == 5
async def test_user_and_session_come_from_the_tool_context(self):
search = FakeSearch()
ctx = ToolContext(session_id="s1", user_id="u1")
await MemorySearchTool(search).execute({"query": "x"}, ctx)
kwargs = search.calls[0][1]
assert kwargs["user_id"] == "u1"
assert kwargs["current_session_id"] == "s1"
assert kwargs["summary_at"] == NOW
class TestOutput:
async def test_hits_are_rendered_with_provenance(self):
hits = [
MemoryHit(source_type="fact", text="server_port: 8099", key="server_port",
category="technical", timestamp=NOW),
MemoryHit(source_type="raw", text="we set the port to 8099",
session_id="s1", start_seq=3, end_seq=4, timestamp=NOW),
]
result = await MemorySearchTool(FakeSearch(hits)).execute({"query": "port"})
assert result.success is True
assert "fact [technical]" in result.output
assert "raw · session s1 · turns 3–4" in result.output