Spaces:
Paused
Paused
| 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() | |