Files
odysseus/tests/test_document_close_clears_active_route.py
Yeoh Ing Ji 3e49658204 refactor(tools): extract document tools to handle registry (#3666)
* feat(tools): add document management tool handlers to the agent_tools module

* feat(tools): extraced document tools for create, update, edit, suggest, and manage from tool_implementations.py

* feat(tests): refactor document tool tests to use TOOL_HANDLERS and document_tools

* refactor(tools): add document tool dispatcher and updated tool calling path

* refactor(tools): remove duplicated document management functions

* refactor(tools): removing unused functions and adding new import paths

* refactor(tools): update document tool execute methods to use context dictionary

* refactor(tests): update import paths for document tools in test files

* refactor(tests): update owner parameter format in document management tests

* refactor(tests): update import path for _owned_document_query

* feat(tools): add document management tool handlers to the agent_tools module

* feat(tools): extraced document tools for create, update, edit, suggest, and manage from tool_implementations.py

* feat(tests): refactor document tool tests to use TOOL_HANDLERS and document_tools

* refactor(tools): add document tool dispatcher and updated tool calling path

* refactor(tools): remove duplicated document management functions

* refactor(tools): removing unused functions and adding new import paths

* refactor(tools): update document tool execute methods to use context dictionary

* refactor(tests): update import paths for document tools in test files

* refactor(tests): update owner parameter format in document management tests

* refactor(tests): update import path for _owned_document_query

* refactor: update import paths for document tools

* fix(tests): correct source path for document ID test
2026-06-10 10:41:52 +02:00

98 lines
3.4 KiB
Python

"""Issue #1160 — route-level regression for clearing the active-document pointer.
Exercises the REAL ``PATCH /api/document/{id}`` (session_id="") and
``DELETE /api/document/{id}`` handlers, proving that closing a document's tab
(detach or delete) clears the in-memory active-document pointer under the actual
owner/session routing — not just the helper in isolation.
Calls the route handler callables DIRECTLY (extracted from the router) instead of
through Starlette's TestClient. The TestClient path spun up a middleware app +
threadpool that could hang in some environments; calling the async handler with a
minimal fake request keeps the same real coverage (handler + DB + owner routing)
while completing reliably everywhere.
"""
import tempfile
import uuid
from types import SimpleNamespace
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import NullPool
from unittest.mock import MagicMock
from tests.helpers.import_state import clear_fake_database_modules
clear_fake_database_modules()
import core.database as cdb
import routes.document_routes as droutes
from core.database import Document
from core.database import Session as DbSession
from routes.document_helpers import DocumentPatch
from src.agent_tools.document_tools import set_active_document, get_active_document
_TMPDB = tempfile.NamedTemporaryFile(suffix=".db", delete=False)
_ENGINE = create_engine(
f"sqlite:///{_TMPDB.name}",
connect_args={"check_same_thread": False},
poolclass=NullPool,
)
cdb.Base.metadata.create_all(_ENGINE)
_TS = sessionmaker(bind=_ENGINE, autoflush=False, autocommit=False)
droutes.SessionLocal = _TS # route handlers resolve SessionLocal at call time
def _req():
return SimpleNamespace(state=SimpleNamespace(current_user="tester"))
def _endpoint(method, path):
router = droutes.setup_document_routes(MagicMock(), None)
for r in router.routes:
if getattr(r, "path", None) == path and method in getattr(r, "methods", set()):
return r.endpoint
raise RuntimeError(f"{method} {path} not found")
def _make_doc():
sid = "s-" + uuid.uuid4().hex[:8]
db = _TS()
try:
db.add(DbSession(id=sid, owner="tester", name="s", model="m", endpoint_url="http://x"))
doc = Document(
id=str(uuid.uuid4()), session_id=sid, title="t",
language="markdown", current_content="hi", version_count=1,
is_active=True, owner="tester",
)
db.add(doc)
db.commit()
return doc.id
finally:
db.close()
async def test_patch_unlink_clears_active_document():
patch_document = _endpoint("PATCH", "/api/document/{doc_id}")
doc_id = _make_doc()
set_active_document(doc_id)
await patch_document(_req(), doc_id, DocumentPatch(session_id=""))
assert get_active_document() is None
async def test_delete_clears_active_document():
delete_document = _endpoint("DELETE", "/api/document/{doc_id}")
doc_id = _make_doc()
set_active_document(doc_id)
await delete_document(_req(), doc_id)
assert get_active_document() is None
async def test_unlinking_a_different_doc_leaves_pointer():
patch_document = _endpoint("PATCH", "/api/document/{doc_id}")
active_id = _make_doc()
other_id = _make_doc()
set_active_document(active_id)
await patch_document(_req(), other_id, DocumentPatch(session_id=""))
assert get_active_document() == active_id