"""Built-in help documents: imported from files, never editable in the UI.""" import pytest from httpx import AsyncClient from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.help_import import ( HelpImportError, import_help_documents, parse_help_document, ) from app.models import Document, Job, User pytestmark = pytest.mark.usefixtures("fake_embed") HELP_FILE = """--- key: test-hilfe title: "Testhilfe" --- # Testhilfe So funktioniert es. """ def test_parse_splits_frontmatter_from_body() -> None: key, title, body = parse_help_document(HELP_FILE) assert (key, title) == ("test-hilfe", "Testhilfe") assert body.startswith("# Testhilfe") def test_parse_rejects_a_file_without_frontmatter() -> None: with pytest.raises(HelpImportError): parse_help_document("# Just markdown") async def _import(db: AsyncSession, tmp_path, monkeypatch, text: str) -> int: (tmp_path / "hilfe.md").write_text(text) monkeypatch.setenv("PABLAN_HELP_DIR", str(tmp_path)) from app.config import get_settings get_settings.cache_clear() try: return await import_help_documents(db) finally: get_settings.cache_clear() async def test_import_is_idempotent_and_reindexes_only_on_change( db: AsyncSession, tmp_path, monkeypatch: pytest.MonkeyPatch ) -> None: assert await _import(db, tmp_path, monkeypatch, HELP_FILE) == 1 document = ( await db.execute(select(Document).where(Document.is_builtin.is_(True))) ).scalar_one() assert document.title == "Testhilfe" assert document.status.value == "published" assert document.visibility.value == "public" assert document.author_id is None # Unchanged: no second document, no new index job. assert await _import(db, tmp_path, monkeypatch, HELP_FILE) == 0 # Changed: content is refreshed in place and re-indexed. assert ( await _import( db, tmp_path, monkeypatch, HELP_FILE.replace("So funktioniert es.", "Neu.") ) == 1 ) documents = ( (await db.execute(select(Document).where(Document.is_builtin.is_(True)))) .scalars() .all() ) assert len(documents) == 1 assert "Neu." in documents[0].content_md jobs = (await db.execute(select(Job))).scalars().all() assert len(jobs) == 2 # one per actual change async def test_help_documents_cannot_be_edited_or_deleted( client: AsyncClient, db: AsyncSession, seeded_admin: User, tmp_path, monkeypatch: pytest.MonkeyPatch, ) -> None: """Not even an admin may edit them — the next deploy would overwrite it.""" await _import(db, tmp_path, monkeypatch, HELP_FILE) document = ( await db.execute(select(Document).where(Document.is_builtin.is_(True))) ).scalar_one() login = await client.post( "/api/auth/login", json={"email": "florian@test.dev", "password": "secret123"} ) assert login.status_code == 200 listed = (await client.get("/api/documents")).json()["items"] entry = next(row for row in listed if row["id"] == str(document.id)) assert entry["is_builtin"] is True assert entry["can_edit"] is False patched = await client.patch( f"/api/documents/{document.id}", json={"title": "Gekapert"} ) assert patched.status_code == 409 assert patched.json()["code"] == "builtin_readonly" deleted = await client.delete(f"/api/documents/{document.id}") assert deleted.status_code == 409