test: add pytest suite - 97 tests, search + indexer + auth

Create comprehensive test suite with 97 passing tests:
- tests/conftest.py: fixtures (TestClient, temp vault dirs, index setup)
- tests/test_search.py (27 tests): tokenizer, snippets, highlight,
  tag filter, search API, advanced search, suggest, tags API
- tests/test_indexer.py (32 tests): frontmatter parsing, inline tags,
  title extraction, scan_vault, find_file_in_index, backlinks
- tests/test_auth.py (38 tests): password hashing, JWT create/decode,
  token revocation, user CRUD, login lockout, rate limiting, middleware

Also fix: lazy WeasyPrint import (graceful fallback when GTK missing),
add data/ to .gitignore (runtime files from test runs).
This commit is contained in:
2026-05-27 22:06:27 -04:00
parent a5afbb1dc1
commit edb9e98f81
6 changed files with 971 additions and 1 deletions
+12 -1
View File
@@ -582,7 +582,14 @@ app.add_middleware(SecurityHeadersMiddleware)
from backend.auth.router import router as auth_router
from backend.auth.middleware import require_auth, require_admin, check_vault_access
from backend.secret_redactor import redact_file_content
from backend.pdf_export import generate_pdf, build_pdf_html
# Lazy import: WeasyPrint PDF export (requires GTK, may not be available everywhere)
try:
from backend.pdf_export import generate_pdf, build_pdf_html
except OSError:
generate_pdf = None
build_pdf_html = None
import logging
logging.getLogger("obsigate").warning("PDF export unavailable (WeasyPrint/GTK not found)")
from backend.share import create_share, get_share_by_token, record_access, revoke_share, list_shares
from backend.webhooks import get_webhooks, create_webhook, update_webhook, delete_webhook, dispatch_webhooks
from backend.saved_searches import get_saved, save_search, delete_saved
@@ -1232,6 +1239,8 @@ async def api_file_download(vault_name: str, path: str = Query(..., description=
@app.get("/api/file/{vault_name}/pdf")
async def api_file_pdf(vault_name: str, path: str = Query(..., description="Relative path to file"), current_user=Depends(require_auth)):
"""Download a markdown file as PDF."""
if generate_pdf is None:
raise HTTPException(501, "PDF export unavailable (WeasyPrint/GTK not available)")
if not check_vault_access(vault_name, current_user):
raise HTTPException(403, f"Accès refusé à la vault '{vault_name}'")
vault_data = get_vault_data(vault_name)
@@ -2870,6 +2879,8 @@ async def api_share_revoke(share_id: str, current_user=Depends(require_auth)):
@app.get("/s/{token}/pdf")
async def public_share_pdf_download(token: str):
"""Download shared document as real PDF via WeasyPrint."""
if generate_pdf is None:
raise HTTPException(501, "PDF export unavailable (WeasyPrint/GTK not available)")
share = get_share_by_token(token)
if not share:
raise HTTPException(404, "Share not found or expired")