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 Contribute Delete
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()