from __future__ import annotations import importlib from datetime import UTC, datetime, timedelta import pytest PASSWORD = "correct horse battery staple" @pytest.mark.asyncio async def test_remote_setup_requires_bootstrap_secret(tmp_path) -> None: conftest = importlib.import_module("conftest") app_module = importlib.import_module("backup_tool.api.app") settings = conftest.make_settings(tmp_path).model_copy( update={"public_base_url": "https://backup.example.test", "bootstrap_secret": "bootstrap"} ) cli = importlib.import_module("backup_tool.cli") from alembic import command from httpx import ASGITransport, AsyncClient command.upgrade(cli.build_alembic_config(settings), "head") app = app_module.create_app(settings) async with AsyncClient(transport=ASGITransport(app=app), base_url="https://backup.example.test") as client: denied = await client.post("/api/v2/setup", json={"username": "admin", "password": PASSWORD}) assert denied.status_code == 403 accepted = await client.post("/api/v2/setup", json={"username": "admin", "password": PASSWORD, "bootstrap_secret": "bootstrap"}) assert accepted.status_code == 201 await app.state.engine.dispose() def test_expired_session_is_rejected(tmp_path) -> None: auth = importlib.import_module("backup_tool.security.auth") key = tmp_path / "master.key" key.write_bytes(b"x" * 32) expired = auth.sign_session("user", "csrf", key, expires_at=datetime.now(UTC) - timedelta(seconds=1), session_id="session") assert auth.verify_session(expired, key) is None