chore: import upstream snapshot with attribution
Tests / Import Check (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.14) (push) Has been cancelled
Tests / Python Tests (Python 3.11) (push) Has been cancelled
Tests / Python Tests (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.14) (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Tests / Lint and Format (push) Has been cancelled
Tests / Web Node Tests (push) Has been cancelled
Tests / Import Check (Python 3.11) (push) Has been cancelled
Tests / Import Check (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.14) (push) Has been cancelled
Tests / Python Tests (Python 3.11) (push) Has been cancelled
Tests / Python Tests (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.14) (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Tests / Lint and Format (push) Has been cancelled
Tests / Web Node Tests (push) Has been cancelled
Tests / Import Check (Python 3.11) (push) Has been cancelled
Tests / Import Check (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.13) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,318 @@
|
||||
"""Unit tests for the ``write_note`` tool (append + edit modes)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from deeptutor.tools.write_note import (
|
||||
ALL_TURNS_SENTINEL,
|
||||
DEFAULT_TURNS_TO_INCLUDE,
|
||||
MAX_NOTE_CHARS,
|
||||
MAX_TITLE_CHARS,
|
||||
write_note,
|
||||
)
|
||||
|
||||
|
||||
class _FakeManager:
|
||||
"""Notebook manager stand-in. Tracks last add / update for assertions."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
notebooks=(),
|
||||
records_by_nb=None,
|
||||
raise_on_add: bool = False,
|
||||
raise_on_update: bool = False,
|
||||
):
|
||||
self._notebooks = list(notebooks)
|
||||
self._records = dict(records_by_nb or {})
|
||||
self._raise_on_add = raise_on_add
|
||||
self._raise_on_update = raise_on_update
|
||||
self.last_add = None
|
||||
self.last_update = None
|
||||
|
||||
def list_notebooks(self):
|
||||
return list(self._notebooks)
|
||||
|
||||
def get_record(self, notebook_id, record_id):
|
||||
for rec in self._records.get(notebook_id, []):
|
||||
if str(rec.get("id")) == record_id:
|
||||
return rec
|
||||
return None
|
||||
|
||||
def add_record(self, **kwargs):
|
||||
if self._raise_on_add:
|
||||
raise RuntimeError("disk full")
|
||||
self.last_add = kwargs
|
||||
return {"record": {"id": "rec-123"}, "added_to_notebooks": kwargs["notebook_ids"]}
|
||||
|
||||
def update_record(self, notebook_id, record_id, **kwargs):
|
||||
if self._raise_on_update:
|
||||
raise RuntimeError("io error")
|
||||
target = self.get_record(notebook_id, record_id)
|
||||
if target is None:
|
||||
return None
|
||||
target.update(kwargs)
|
||||
self.last_update = {"notebook_id": notebook_id, "record_id": record_id, **kwargs}
|
||||
return target
|
||||
|
||||
|
||||
_SAMPLE_HISTORY = [
|
||||
{"role": "user", "content": "What is a vector?"},
|
||||
{"role": "assistant", "content": "A vector is..."},
|
||||
{"role": "user", "content": "And a tensor?"},
|
||||
{"role": "assistant", "content": "A tensor generalises..."},
|
||||
]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Mode validation + shared error paths
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_rejects_unknown_mode() -> None:
|
||||
outcome = write_note(
|
||||
mode="rewrite",
|
||||
notebook_id="nb-1",
|
||||
notebook_manager=_FakeManager(notebooks=[{"id": "nb-1", "name": "N"}]),
|
||||
)
|
||||
assert outcome.ok is False
|
||||
assert "Unknown mode" in outcome.error
|
||||
|
||||
|
||||
def test_rejects_unknown_notebook() -> None:
|
||||
outcome = write_note(
|
||||
mode="append",
|
||||
notebook_id="bogus",
|
||||
title="t",
|
||||
conversation_history=_SAMPLE_HISTORY,
|
||||
notebook_manager=_FakeManager(notebooks=[{"id": "nb-1", "name": "N"}]),
|
||||
)
|
||||
assert outcome.ok is False
|
||||
assert "Unknown notebook_id" in outcome.error
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Append mode
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_append_writes_real_transcript_by_default() -> None:
|
||||
manager = _FakeManager(notebooks=[{"id": "nb-1", "name": "Math"}])
|
||||
outcome = write_note(
|
||||
mode="append",
|
||||
notebook_id="nb-1",
|
||||
title="Vectors",
|
||||
turns_to_include=2,
|
||||
note="Bookmark for review.",
|
||||
conversation_history=_SAMPLE_HISTORY,
|
||||
current_user_message="",
|
||||
notebook_manager=manager,
|
||||
)
|
||||
assert outcome.ok is True
|
||||
assert outcome.mode == "append"
|
||||
body = manager.last_add["output"]
|
||||
assert "### User" in body and "### Assistant" in body
|
||||
assert "What is a vector?" in body
|
||||
assert "A tensor generalises..." in body
|
||||
# Note prefix appears above the transcript.
|
||||
assert "Bookmark for review." in body
|
||||
assert body.index("Bookmark") < body.index("### User")
|
||||
assert manager.last_add["metadata"]["mode"] == "append"
|
||||
assert manager.last_add["metadata"]["explicit_content"] is False
|
||||
|
||||
|
||||
def test_append_with_explicit_content_skips_auto_transcript() -> None:
|
||||
"""When the agent passes `content`, it's saved verbatim — supports
|
||||
the 'save an agent-authored summary' use case."""
|
||||
manager = _FakeManager(notebooks=[{"id": "nb-1", "name": "N"}])
|
||||
outcome = write_note(
|
||||
mode="append",
|
||||
notebook_id="nb-1",
|
||||
title="Summary",
|
||||
content="### My summary\n\nThings to remember.",
|
||||
conversation_history=_SAMPLE_HISTORY,
|
||||
notebook_manager=manager,
|
||||
)
|
||||
assert outcome.ok is True
|
||||
body = manager.last_add["output"]
|
||||
assert "Things to remember." in body
|
||||
# The default Q&A transcript headings should NOT appear when
|
||||
# explicit content was provided.
|
||||
assert "### User" not in body
|
||||
assert manager.last_add["metadata"]["explicit_content"] is True
|
||||
|
||||
|
||||
def test_append_accepts_all_sentinel_for_turns() -> None:
|
||||
manager = _FakeManager(notebooks=[{"id": "nb-1", "name": "N"}])
|
||||
outcome = write_note(
|
||||
mode="append",
|
||||
notebook_id="nb-1",
|
||||
title="Everything",
|
||||
turns_to_include=ALL_TURNS_SENTINEL,
|
||||
conversation_history=_SAMPLE_HISTORY,
|
||||
current_user_message="latest question",
|
||||
notebook_manager=manager,
|
||||
)
|
||||
assert outcome.ok is True
|
||||
body = manager.last_add["output"]
|
||||
# All four history pairs + the current question should be in the body.
|
||||
assert "What is a vector?" in body
|
||||
assert "And a tensor?" in body
|
||||
assert "latest question" in body
|
||||
|
||||
|
||||
def test_append_requires_title() -> None:
|
||||
manager = _FakeManager(notebooks=[{"id": "nb-1", "name": "N"}])
|
||||
outcome = write_note(
|
||||
mode="append",
|
||||
notebook_id="nb-1",
|
||||
title=" ",
|
||||
conversation_history=_SAMPLE_HISTORY,
|
||||
notebook_manager=manager,
|
||||
)
|
||||
assert outcome.ok is False
|
||||
assert "title" in outcome.error.lower()
|
||||
|
||||
|
||||
def test_append_refuses_empty_history_without_content() -> None:
|
||||
manager = _FakeManager(notebooks=[{"id": "nb-1", "name": "N"}])
|
||||
outcome = write_note(
|
||||
mode="append",
|
||||
notebook_id="nb-1",
|
||||
title="t",
|
||||
conversation_history=[],
|
||||
current_user_message="",
|
||||
notebook_manager=manager,
|
||||
)
|
||||
assert outcome.ok is False
|
||||
assert "nothing to save" in outcome.error.lower()
|
||||
|
||||
|
||||
def test_append_surfaces_manager_errors_as_outcome() -> None:
|
||||
manager = _FakeManager(notebooks=[{"id": "nb-1", "name": "N"}], raise_on_add=True)
|
||||
outcome = write_note(
|
||||
mode="append",
|
||||
notebook_id="nb-1",
|
||||
title="t",
|
||||
conversation_history=_SAMPLE_HISTORY,
|
||||
notebook_manager=manager,
|
||||
)
|
||||
assert outcome.ok is False
|
||||
assert "Save failed" in outcome.error
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Edit mode
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_edit_requires_record_id() -> None:
|
||||
manager = _FakeManager(
|
||||
notebooks=[{"id": "nb-1", "name": "N"}],
|
||||
records_by_nb={"nb-1": [{"id": "r1", "title": "old"}]},
|
||||
)
|
||||
outcome = write_note(
|
||||
mode="edit",
|
||||
notebook_id="nb-1",
|
||||
title="new",
|
||||
notebook_manager=manager,
|
||||
)
|
||||
assert outcome.ok is False
|
||||
assert "record_id" in outcome.error.lower()
|
||||
|
||||
|
||||
def test_edit_rejects_unknown_record() -> None:
|
||||
manager = _FakeManager(
|
||||
notebooks=[{"id": "nb-1", "name": "N"}],
|
||||
records_by_nb={"nb-1": [{"id": "r1", "title": "old"}]},
|
||||
)
|
||||
outcome = write_note(
|
||||
mode="edit",
|
||||
notebook_id="nb-1",
|
||||
record_id="r-missing",
|
||||
title="new",
|
||||
notebook_manager=manager,
|
||||
)
|
||||
assert outcome.ok is False
|
||||
assert "not found" in outcome.error.lower()
|
||||
assert "list_notebook" in outcome.error # tells the LLM where to discover ids
|
||||
|
||||
|
||||
def test_edit_requires_at_least_one_field_changed() -> None:
|
||||
manager = _FakeManager(
|
||||
notebooks=[{"id": "nb-1", "name": "N"}],
|
||||
records_by_nb={"nb-1": [{"id": "r1", "title": "old"}]},
|
||||
)
|
||||
outcome = write_note(
|
||||
mode="edit",
|
||||
notebook_id="nb-1",
|
||||
record_id="r1",
|
||||
notebook_manager=manager,
|
||||
)
|
||||
assert outcome.ok is False
|
||||
assert "at least one" in outcome.error.lower()
|
||||
|
||||
|
||||
def test_edit_patches_title_and_content() -> None:
|
||||
manager = _FakeManager(
|
||||
notebooks=[{"id": "nb-1", "name": "N"}],
|
||||
records_by_nb={"nb-1": [{"id": "r1", "title": "old", "output": "old body"}]},
|
||||
)
|
||||
outcome = write_note(
|
||||
mode="edit",
|
||||
notebook_id="nb-1",
|
||||
record_id="r1",
|
||||
title="new title",
|
||||
content="new body",
|
||||
notebook_manager=manager,
|
||||
)
|
||||
assert outcome.ok is True
|
||||
assert outcome.mode == "edit"
|
||||
assert outcome.record_id == "r1"
|
||||
assert manager.last_update == {
|
||||
"notebook_id": "nb-1",
|
||||
"record_id": "r1",
|
||||
"title": "new title",
|
||||
"output": "new body",
|
||||
}
|
||||
|
||||
|
||||
def test_edit_surfaces_manager_errors_as_outcome() -> None:
|
||||
manager = _FakeManager(
|
||||
notebooks=[{"id": "nb-1", "name": "N"}],
|
||||
records_by_nb={"nb-1": [{"id": "r1", "title": "old"}]},
|
||||
raise_on_update=True,
|
||||
)
|
||||
outcome = write_note(
|
||||
mode="edit",
|
||||
notebook_id="nb-1",
|
||||
record_id="r1",
|
||||
title="new",
|
||||
notebook_manager=manager,
|
||||
)
|
||||
assert outcome.ok is False
|
||||
assert "Edit failed" in outcome.error
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Constants / defaults
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_default_turns_to_include_is_three() -> None:
|
||||
assert DEFAULT_TURNS_TO_INCLUDE == 3
|
||||
|
||||
|
||||
def test_clips_oversized_title_and_note() -> None:
|
||||
manager = _FakeManager(notebooks=[{"id": "nb-1", "name": "N"}])
|
||||
outcome = write_note(
|
||||
mode="append",
|
||||
notebook_id="nb-1",
|
||||
title="x" * (MAX_TITLE_CHARS + 50),
|
||||
note="y" * (MAX_NOTE_CHARS + 100),
|
||||
conversation_history=_SAMPLE_HISTORY,
|
||||
notebook_manager=manager,
|
||||
)
|
||||
assert outcome.ok is True
|
||||
assert manager.last_add["title"].endswith("…")
|
||||
assert len(manager.last_add["title"]) <= MAX_TITLE_CHARS + 1
|
||||
# The clipped note appears in the body with an ellipsis.
|
||||
assert "…" in manager.last_add["output"]
|
||||
Reference in New Issue
Block a user