"""Unit tests for background-note injection into the session (Agent._drain_task_notes)."""
from types import SimpleNamespace
import pytest
from navi.core import task_notes
from navi.core.agent import Agent
class FakeKv:
def __init__(self):
self.data = {}
async def get(self, user_id, session_id, scope, key):
return self.data.get((user_id, session_id, scope, key))
async def set(self, user_id, session_id, scope, key, value):
self.data[(user_id, session_id, scope, key)] = value
class FakeStore:
def __init__(self):
self.saved = []
async def save(self, session):
self.saved.append(session)
@pytest.fixture(autouse=True)
def kv(monkeypatch):
store = FakeKv()
task_notes.set_kv_store(store)
yield store
task_notes.set_kv_store(None)
def make_agent_with_store():
agent = object.__new__(Agent)
agent._sessions = FakeStore()
return agent
def make_session():
return SimpleNamespace(context=[], messages=[])
class TestDrainTaskNotes:
async def test_note_injected_as_system_message_and_saved(self):
agent = make_agent_with_store()
session = make_session()
job = SimpleNamespace(
task_id="bt-ab12", session_id="s1", tool="terminal", status="completed",
subagent_tokens=None, preview=lambda limit=800: "done 42",
)
await task_notes.add_note(job)
await agent._drain_task_notes("s1", session)
assert len(session.context) == 1
note = session.context[0]
assert note.role == "system"
assert "[Background task results]" in note.content
assert "bt-ab12" in note.content
assert note.metadata.get("source") == "task_note"
# note did NOT go into the display history
assert session.messages == []
# session was persisted
assert agent._sessions.saved == [session]
async def test_note_survives_context_build_system_filter(self):
"""Regression: build() used to drop ALL system-role history, so a
drained note was logged as delivered but never reached the LLM."""
from navi.core.context_builder import ContextBuilder
from tests.conftest_factory import make_profile, make_profile_registry
agent = make_agent_with_store()
session = make_session()
job = SimpleNamespace(
task_id="bt-ab12", session_id="s1", tool="terminal", status="completed",
subagent_tokens=None, preview=lambda limit=800: "BG-DONE-42",
)
await task_notes.add_note(job)
await agent._drain_task_notes("s1", session)
builder = ContextBuilder(profile_registry=make_profile_registry())
built = builder.build(session.context, make_profile("test"), None)
joined = "\n".join((m.content or "") for m in built)
assert "[Background task results]" in joined
assert "bt-ab12" in joined
async def test_no_notes_nothing_injected(self):
agent = make_agent_with_store()
session = make_session()
await agent._drain_task_notes("s1", session)
assert session.context == []
assert agent._sessions.saved == []
async def test_drain_failure_is_swallowed(self, monkeypatch):
agent = make_agent_with_store()
session = make_session()
async def broken_drain(session_id):
raise RuntimeError("kv down")
monkeypatch.setattr(task_notes, "drain", broken_drain)
await agent._drain_task_notes("s1", session) # must not raise
assert session.context == []