Files
Frank Schwenk 88bdf6f34b feat: overhaul with modular API, agent endpoints, migrations, backup and tests
- Split monolithic app.py into server/api/ routers
- Add Pydantic schemas for input validation
- Add /agent/* API with separate AGENT_TOKEN auth
- Replace static schema.sql with versioned migrations (datenerhaltend)
- Add tools/backup_db.py for SQLite backups
- Add Dockerfile HEALTHCHECK and compose.yml AGENT_TOKEN env
- Add pytest tests for user and agent API
- Update README with API docs and deployment safety notes
- Bump version to 1.2.0
2026-09-21 12:46:44 +02:00

86 lines
2.4 KiB
Python

from __future__ import annotations
import os
from pathlib import Path
# Set environment variables BEFORE importing the app or settings module.
# Use a fixed test workspace so every module that imports settings sees the
# same paths. The client fixture wipes the DB before each test.
TEST_DIR = Path(__file__).resolve().parent
TEST_DB = TEST_DIR / "test.sqlite"
TEST_MEDS = TEST_DIR / "fixtures" / "meds.yaml"
TEST_MESSAGES = TEST_DIR / "fixtures" / "messages.yaml"
os.environ["APP_PIN"] = "1234"
os.environ["JWT_SECRET"] = "test-secret-must-be-at-least-32-bytes-long-ok"
os.environ["AGENT_TOKEN"] = "agent-test-token-must-be-long-enough-for-secrets"
os.environ["VAPID_PRIVATE_KEY"] = ""
os.environ["VAPID_PUBLIC_KEY"] = ""
os.environ["OPENROUTER_API_KEY"] = ""
os.environ["DB_PATH"] = str(TEST_DB)
os.environ["MEDS_PATH"] = str(TEST_MEDS)
os.environ["MESSAGES_PATH"] = str(TEST_MESSAGES)
# Neutralize the background scheduler for tests — it binds to the asyncio loop
# and survives poorly across multiple TestClient lifecycles.
import server.scheduler
server.scheduler.start_scheduler = lambda: None # type: ignore[assignment]
import pytest
from fastapi.testclient import TestClient
from server.app import app
from server.auth import create_token
from server.config_loader import reload_meds_config
def _write_fixtures() -> None:
TEST_MEDS.parent.mkdir(parents=True, exist_ok=True)
TEST_MEDS.write_text(
"timezone: Europe/Berlin\n"
"slots:\n"
" - id: morning\n"
" time: \"08:00\"\n"
" label: Morgens\n"
" meds:\n"
" - name: Elvanse\n"
" dose: 30mg\n"
" reminder_window_minutes: 90\n"
)
TEST_MESSAGES.write_text(
"reminder: [\"Zeit für Medis.\"]\n"
"success: [\"Genommen.\"]\n"
"missed: [\"Verpasst.\"]\n"
"snooze: [\"Snooze.\"]\n"
"motivation_fallback: [\"Motivation.\"]\n"
"roast_fallback: [\"Roast.\"]\n"
)
_write_fixtures()
reload_meds_config()
@pytest.fixture(scope="function")
def client():
if TEST_DB.exists():
TEST_DB.unlink()
with TestClient(app) as c:
yield c
@pytest.fixture
def user_token() -> str:
return create_token()
@pytest.fixture
def auth_headers(user_token: str):
return {"Authorization": f"Bearer {user_token}"}
@pytest.fixture
def agent_headers():
return {"Authorization": "Bearer agent-test-token-must-be-long-enough-for-secrets"}