PaperMate / tests /test_auth_admin.py
Phuc-HugigFace's picture
feat(auth+db): session-aware nav, ownership fix, admin hardening, schema cleanup
73aafbd
Raw
History Blame
3.78 kB
import unittest
from unittest.mock import AsyncMock, patch
from fastapi.testclient import TestClient
from backend.main import app
def _profile(role: str):
return {
"id": "profile-1",
"email": "user@example.com",
"role": role,
"is_active": True,
"monthly_review_limit": None,
"monthly_cost_limit_usd": None,
}
class AdminAuthTests(unittest.TestCase):
def setUp(self):
self.client = TestClient(app)
self.auth_user = {"id": "auth-1", "email": "user@example.com", "user_metadata": {}}
def test_admin_route_requires_authentication(self):
# No Authorization header → 401.
response = self.client.get("/api/admin/overview")
self.assertEqual(response.status_code, 401)
def test_admin_route_forbidden_for_regular_user(self):
with patch("backend.auth._verify_token", new=AsyncMock(return_value=self.auth_user)), \
patch("backend.auth.job_store.ensure_profile_for_auth_user",
new=AsyncMock(return_value=_profile("user"))):
response = self.client.get(
"/api/admin/overview", headers={"Authorization": "Bearer faketoken"}
)
self.assertEqual(response.status_code, 403)
self.assertIn("Admin access required", response.json()["detail"])
def test_admin_route_ok_for_admin(self):
with patch("backend.auth._verify_token", new=AsyncMock(return_value=self.auth_user)), \
patch("backend.auth.job_store.ensure_profile_for_auth_user",
new=AsyncMock(return_value=_profile("admin"))), \
patch("backend.main.job_store.get_admin_overview",
new=AsyncMock(return_value={"total_submissions": 5, "total_cost_usd": 1.23})), \
patch("backend.main.job_store.list_cost_by_day", new=AsyncMock(return_value=[])), \
patch("backend.main.job_store.list_cost_by_provider", new=AsyncMock(return_value=[])):
response = self.client.get(
"/api/admin/overview", headers={"Authorization": "Bearer faketoken"}
)
self.assertEqual(response.status_code, 200)
body = response.json()
self.assertEqual(body["overview"]["total_submissions"], 5)
def test_admin_cannot_demote_self(self):
with patch("backend.auth._verify_token", new=AsyncMock(return_value=self.auth_user)), \
patch("backend.auth.job_store.ensure_profile_for_auth_user",
new=AsyncMock(return_value=_profile("admin"))):
response = self.client.patch(
"/api/admin/users/profile-1",
headers={"Authorization": "Bearer faketoken"},
json={"role": "user"},
)
self.assertEqual(response.status_code, 400)
self.assertIn("your own admin role", response.json()["detail"])
def test_submit_still_anonymous_without_token(self):
# Optional auth: with no Authorization header the submit path never calls
# the verifier and proceeds anonymously.
fake_job = {"access_key": "anon-key", "status": "pending"}
with patch("backend.main.job_store.create_job", new=AsyncMock(return_value=fake_job)), \
patch("backend.main.send_access_key_email"), \
patch("backend.main._run_pipeline", new=AsyncMock()):
import io
response = self.client.post(
"/api/submit",
files={"file": ("paper.pdf", io.BytesIO(b"%PDF-1.4 x"), "application/pdf")},
data={"email": "anon@example.com"},
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()["access_key"], "anon-key")
if __name__ == "__main__":
unittest.main()