Add JWT authentication with user/password login, role-based access control, and Bearer token support while maintaining legacy API key compatibility for transition; update README with comprehensive JWT security guidelines, installation instructions for Python/SSH, development startup scripts (.env, run_dev.sh/ps1), and migrate all API examples to JWT authentication; update Ansible inventory SSH key paths from Docker container paths to local user paths

This commit is contained in:
2025-12-14 17:33:34 -05:00
parent 5a512d39b5
commit 0030fcc101
177 changed files with 22810 additions and 289 deletions
+22
View File
@@ -2,12 +2,18 @@
from __future__ import annotations
import asyncio
import os
import sys
from pathlib import Path
# Add project root to path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
os.environ.setdefault(
"DATABASE_URL",
f"sqlite+aiosqlite:///{Path(__file__).resolve().parent / 'test_homelab.db'}",
)
import pytest
import pytest_asyncio
@@ -17,6 +23,7 @@ from app.crud.bootstrap_status import BootstrapStatusRepository
from app.crud.task import TaskRepository
from app.crud.schedule import ScheduleRepository
from app.crud.log import LogRepository
from app.crud.app_setting import AppSettingRepository
# Configure pytest-asyncio
pytestmark = pytest.mark.asyncio
@@ -202,5 +209,20 @@ async def test_create_schedule_run(setup_db):
assert run.status == "running"
@pytest.mark.asyncio
async def test_app_settings_set_get(setup_db):
async with async_session_maker() as session:
repo = AppSettingRepository(session)
row = await repo.set_value("metrics_collection_interval", "10m")
await session.commit()
assert row.key == "metrics_collection_interval"
assert row.value == "10m"
value = await repo.get_value("metrics_collection_interval")
assert value == "10m"
if __name__ == "__main__":
pytest.main([__file__, "-v"])
+49
View File
@@ -0,0 +1,49 @@
"""Tests for help documentation download endpoints helpers.
We keep this lightweight by validating that the help endpoints are registered
and that the PDF generator returns a non-empty PDF payload.
"""
from __future__ import annotations
import os
import sys
from pathlib import Path
import pytest
# Ensure project root on path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
@pytest.mark.asyncio
async def test_help_markdown_builder_exists_and_non_empty():
from app import app_optimized # type: ignore
assert hasattr(app_optimized, "_build_help_markdown")
md = app_optimized._build_help_markdown()
assert isinstance(md, str)
assert len(md) > 100
assert "Guide d'Utilisation" in md or "Démarrage Rapide" in md
@pytest.mark.asyncio
async def test_help_pdf_generator_returns_pdf_bytes():
pytest.importorskip("reportlab")
pytest.importorskip("PIL")
from app import app_optimized # type: ignore
md = app_optimized._build_help_markdown()
pdf_bytes = app_optimized._markdown_to_pdf_bytes(md)
assert isinstance(pdf_bytes, (bytes, bytearray))
assert len(pdf_bytes) > 1000
assert bytes(pdf_bytes[:4]) == b"%PDF"
def test_help_routes_registered():
from app.app_optimized import app # type: ignore
paths = {r.path for r in app.routes}
assert "/api/help/documentation.md" in paths
assert "/api/help/documentation.pdf" in paths
Binary file not shown.
Binary file not shown.
Binary file not shown.