"""Shared test helpers — the plain functions tests call, as opposed to the fixtures in conftest.py. Each of these was copied into several test modules before #2825 consolidated them; a module imports what it needs with ``from tests.helpers import ...``. """ from __future__ import annotations from unittest.mock import AsyncMock, MagicMock def make_mock_session() -> AsyncMock: """A stand-in for ``async_session()`` — usable as ``async with``, with the commit/refresh/add surface a service touches. ``add`` is a MagicMock because the real ``Session.add`` is synchronous; an AsyncMock there would hand the service an un-awaited coroutine. """ s = AsyncMock() s.__aenter__ = AsyncMock(return_value=s) s.__aexit__ = AsyncMock(return_value=False) s.add = MagicMock() s.commit = AsyncMock() s.refresh = AsyncMock() return s async def ensure_user(session, username: str, role: str = "user"): """Get-or-create a User by username inside an open session (flushed, not committed). Integration tests share one database for the whole lane run, so a second test re-creating the same username dies on the unique constraint — every integration seed goes through this instead of ``User(...)`` + add. """ from sqlalchemy import select from scribe.models.user import User existing = ( await session.execute(select(User).where(User.username == username)) ).scalar_one_or_none() if existing is not None: return existing user = User(username=username, role=role) session.add(user) await session.flush() return user def fake_note(**attrs) -> MagicMock: """A MagicMock note with REAL values on every attribute the product reads to label, scope, or render a record. The hazard this exists for (note 2109): an auto-created MagicMock attribute is truthy and has a repr. The injected menu reads ``is_task`` / ``task_kind`` / ``note_type`` for its kind marker, ``user_id`` to decide whether a line needs a "shared by …" attribution, ``data`` for a snippet's language tag, and ``deleted_at`` to spot trash — on a bare MagicMock every record renders as another user's trashed task with a mock repr for a language. Defaults below are the ORDINARY state (own note, live, no structured data); override what the test is about. ``to_dict()`` returns the same values as a plain dict, so a tool that repackages ``note.to_dict()`` sees keys that agree with the attributes. """ values = { "id": 1, "title": "t", "body": "", "tags": [], "user_id": 7, "note_type": "note", "is_task": False, "task_kind": "work", "data": None, "deleted_at": None, } values.update(attrs) n = MagicMock() for key, value in values.items(): setattr(n, key, value) n.to_dict.return_value = dict(values) return n