"""resolve_process precedence (id → exact title → substring) in services/notes.py. Mocks async_session — no real DB, matching the other notes-service tests. """ from unittest.mock import AsyncMock, MagicMock, patch import pytest def _make_mock_session(): s = AsyncMock() s.__aenter__ = AsyncMock(return_value=s) s.__aexit__ = AsyncMock(return_value=False) return s def _result(first=None, all_=None): """A SQLAlchemy-result mock exposing .scalars().first()/.all().""" r = MagicMock() r.scalars.return_value.first.return_value = first r.scalars.return_value.all.return_value = all_ or [] return r def _note(id, title): n = MagicMock() n.id = id n.title = title return n @pytest.mark.asyncio async def test_resolve_process_by_numeric_id(): note = _note(5, "Drift Audit") session = _make_mock_session() # numeric id → first execute (id lookup) hits session.execute = AsyncMock(side_effect=[_result(first=note)]) with patch("scribe.services.notes.async_session") as cls: cls.return_value = session from scribe.services.notes import resolve_process found, candidates = await resolve_process(1, "5") assert found is note assert candidates == [] assert session.execute.await_count == 1 @pytest.mark.asyncio async def test_resolve_process_exact_title_beats_substring(): note = _note(7, "Drift Audit") session = _make_mock_session() # non-digit → exact-title query (first execute) hits; substring never runs session.execute = AsyncMock(side_effect=[_result(first=note)]) with patch("scribe.services.notes.async_session") as cls: cls.return_value = session from scribe.services.notes import resolve_process found, candidates = await resolve_process(1, "Drift Audit") assert found is note assert candidates == [] assert session.execute.await_count == 1 @pytest.mark.asyncio async def test_resolve_process_substring_returns_candidates(): n1 = _note(7, "Drift Audit Remediation") n2 = _note(9, "Drift Audit Notes") session = _make_mock_session() # exact miss, then substring returns two (most-recent first) session.execute = AsyncMock(side_effect=[_result(first=None), _result(all_=[n1, n2])]) with patch("scribe.services.notes.async_session") as cls: cls.return_value = session from scribe.services.notes import resolve_process found, candidates = await resolve_process(1, "drift") assert found is n1 assert candidates == [{"id": 9, "title": "Drift Audit Notes"}] assert session.execute.await_count == 2 @pytest.mark.asyncio async def test_resolve_process_no_match(): session = _make_mock_session() session.execute = AsyncMock(side_effect=[_result(first=None), _result(all_=[])]) with patch("scribe.services.notes.async_session") as cls: cls.return_value = session from scribe.services.notes import resolve_process found, candidates = await resolve_process(1, "nope") assert found is None assert candidates == []