"""Bootstrap admin user on backend startup.""" from __future__ import annotations import pytest from sqlalchemy import delete, select from cyclone.auth import bootstrap, users from cyclone.auth.deps import AUTH_DISABLED from cyclone.auth.permissions import Role from cyclone.db import Session as DbSession from cyclone.db import SessionLocal, User @pytest.fixture(autouse=True) def _clear(monkeypatch): # Reset the bootstrap-side AUTH_DISABLED flag so tests don't leak state # into each other. The conftest fixture flips this back to True at the # start of every test, but bootstrap.run() mutates this module-level # value, so we restore it here too. monkeypatch.setattr("cyclone.auth.deps.AUTH_DISABLED", False) # conftest sets CYCLONE_AUTH_DISABLED=1 at module import. Drop it # by default so tests exercise the real bootstrap path; the # AUTH_DISABLED-specific test re-sets it explicitly. monkeypatch.delenv("CYCLONE_AUTH_DISABLED", raising=False) with SessionLocal()() as db: db.execute(delete(DbSession)) db.execute(delete(User)) db.commit() yield monkeypatch.setattr("cyclone.auth.deps.AUTH_DISABLED", False) with SessionLocal()() as db: db.execute(delete(DbSession)) db.execute(delete(User)) db.commit() def test_bootstrap_creates_admin_when_users_empty_and_env_set(monkeypatch): monkeypatch.setenv("CYCLONE_ADMIN_USERNAME", "firstadmin") monkeypatch.setenv("CYCLONE_ADMIN_PASSWORD", "firstadminpw1") bootstrap.run() with SessionLocal()() as db: u = db.execute(select(User).where(User.username == "firstadmin")).scalar_one() assert u.role == Role.ADMIN.value def test_bootstrap_noop_when_users_exist(monkeypatch): monkeypatch.setenv("CYCLONE_ADMIN_USERNAME", "ignored") monkeypatch.setenv("CYCLONE_ADMIN_PASSWORD", "ignoredignored1") with SessionLocal()() as db: users.create(db, username="existing", password="hunter2hunter2", role="admin") bootstrap.run() with SessionLocal()() as db: all_users = db.execute(select(User)).scalars().all() usernames = {u.username for u in all_users} assert usernames == {"existing"} def test_bootstrap_refuses_to_run_without_env(monkeypatch): monkeypatch.delenv("CYCLONE_ADMIN_USERNAME", raising=False) monkeypatch.delenv("CYCLONE_ADMIN_PASSWORD", raising=False) with pytest.raises(RuntimeError, match="CYCLONE_ADMIN_USERNAME"): bootstrap.run() def test_bootstrap_rejects_short_password(monkeypatch): monkeypatch.setenv("CYCLONE_ADMIN_USERNAME", "weak") monkeypatch.setenv("CYCLONE_ADMIN_PASSWORD", "short") with pytest.raises(RuntimeError, match="12 characters"): bootstrap.run() def test_bootstrap_skips_when_auth_disabled(monkeypatch): monkeypatch.setenv("CYCLONE_AUTH_DISABLED", "1") # Even without env vars, bootstrap should NOT raise when AUTH_DISABLED=1. bootstrap.run() # And it must flip the deps flag so the API skips auth checks. from cyclone.auth import deps as _deps assert _deps.AUTH_DISABLED is True