"""Admin-only user management endpoints.""" from __future__ import annotations import pytest from fastapi.testclient import TestClient from sqlalchemy import delete from cyclone.api import app from cyclone.auth import users from cyclone.db import Session as DbSession from cyclone.db import SessionLocal, User @pytest.fixture(autouse=True) def _clear(monkeypatch): # conftest sets AUTH_DISABLED=True so pre-auth tests keep passing # without a login. The auth-admin tests need the real auth path # exercised, so flip it back off here. monkeypatch.setattr("cyclone.auth.deps.AUTH_DISABLED", False) with SessionLocal()() as db: db.execute(delete(DbSession)) db.execute(delete(User)) db.commit() yield with SessionLocal()() as db: db.execute(delete(DbSession)) db.execute(delete(User)) db.commit() @pytest.fixture def admin_client(): client = TestClient(app) with SessionLocal()() as db: users.create(db, username="root", password="rootpassword1", role="admin") login = client.post( "/api/auth/login", json={"username": "root", "password": "rootpassword1"}, ) assert login.status_code == 200 return client @pytest.fixture def user_client(): client = TestClient(app) with SessionLocal()() as db: users.create(db, username="plain", password="plainpassword1", role="user") login = client.post( "/api/auth/login", json={"username": "plain", "password": "plainpassword1"}, ) return client def test_list_users_as_admin(admin_client): with SessionLocal()() as db: users.create(db, username="alice", password="hunter2hunter2", role="user") resp = admin_client.get("/api/admin/users") assert resp.status_code == 200 body = resp.json() usernames = {u["username"] for u in body} assert {"root", "alice"} <= usernames def test_list_users_as_nonadmin_returns_403(user_client): resp = user_client.get("/api/admin/users") assert resp.status_code == 403 def test_create_user_as_admin(admin_client): resp = admin_client.post( "/api/admin/users", json={"username": "newbie", "password": "newbiepassword1", "role": "viewer"}, ) assert resp.status_code == 201 assert resp.json()["username"] == "newbie" assert resp.json()["role"] == "viewer" def test_create_user_rejects_invalid_role(admin_client): resp = admin_client.post( "/api/admin/users", json={"username": "badrole", "password": "hunter2hunter2", "role": "owner"}, ) assert resp.status_code == 422 def test_patch_user_role_and_password(admin_client): with SessionLocal()() as db: u = users.create(db, username="subject", password="hunter2hunter2", role="viewer") resp = admin_client.patch( f"/api/admin/users/{u.id}", json={"role": "user", "password": "newpassword1"}, ) assert resp.status_code == 200 assert resp.json()["role"] == "user" def test_admin_cannot_demote_self(admin_client): me = admin_client.get("/api/auth/me").json() resp = admin_client.patch( f"/api/admin/users/{me['id']}", json={"role": "viewer"}, ) assert resp.status_code == 409