Spaces:
Running
Running
| import io | |
| import unittest | |
| from unittest.mock import AsyncMock, patch | |
| from fastapi.testclient import TestClient | |
| from backend.main import app | |
| class ApiContractTests(unittest.TestCase): | |
| def setUp(self): | |
| self.client = TestClient(app) | |
| def test_submit_rejects_non_pdf(self): | |
| response = self.client.post( | |
| "/api/submit", | |
| files={"file": ("note.txt", b"hello", "text/plain")}, | |
| data={"email": "user@example.com"}, | |
| ) | |
| self.assertEqual(response.status_code, 400) | |
| self.assertIn("Only PDF files", response.json()["detail"]) | |
| def test_review_lookup_404_for_unknown_key(self): | |
| with patch("backend.main.job_store.get_job", new=AsyncMock(return_value=None)): | |
| response = self.client.get("/api/review/missing-key") | |
| self.assertEqual(response.status_code, 404) | |
| self.assertIn("Review not found", response.json()["detail"]) | |
| def test_review_lookup_returns_existing_job_shape(self): | |
| job = { | |
| "access_key": "known-key", | |
| "email": "user@example.com", | |
| "status": "completed", | |
| "created_at": "2026-01-01T00:00:00+00:00", | |
| "completed_at": "2026-01-01T00:05:00+00:00", | |
| "paper_filename": "paper.pdf", | |
| "review": {"overall_assessment": 3.5, "paper_summary": "Solid."}, | |
| "has_parsed_markdown": True, | |
| "error": None, | |
| } | |
| with patch("backend.main.job_store.get_job", new=AsyncMock(return_value=job)): | |
| response = self.client.get("/api/review/known-key") | |
| self.assertEqual(response.status_code, 200) | |
| body = response.json() | |
| self.assertEqual(body["access_key"], "known-key") | |
| self.assertEqual(body["review"]["overall_assessment"], 3.5) | |
| # Retrieval links are attached for the original PDF and parsed markdown. | |
| self.assertEqual(body["pdf_url"], "/api/review/known-key/pdf") | |
| self.assertEqual(body["markdown_url"], "/api/review/known-key/markdown") | |
| def test_submit_returns_access_key_with_valid_pdf(self): | |
| fake_job = { | |
| "access_key": "new-secret", | |
| "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()): | |
| response = self.client.post( | |
| "/api/submit", | |
| files={"file": ("paper.pdf", io.BytesIO(b"%PDF-1.4 sample"), "application/pdf")}, | |
| data={"email": "user@example.com"}, | |
| ) | |
| self.assertEqual(response.status_code, 200) | |
| body = response.json() | |
| self.assertEqual(body["access_key"], "new-secret") | |
| self.assertNotIn("submission_key", body) | |
| self.assertIn("submitted", body["message"].lower()) | |
| def test_feedback_is_saved_in_feedback_table_contract(self): | |
| job = {"access_key": "known-key", "status": "completed"} | |
| save_feedback = AsyncMock(return_value={"id": 1}) | |
| with patch("backend.main.job_store.get_job", new=AsyncMock(return_value=job)), \ | |
| patch("backend.main.job_store.save_feedback", new=save_feedback): | |
| response = self.client.post( | |
| "/api/feedback/known-key", | |
| json={ | |
| "helpfulness": "high", | |
| "has_critical_error": False, | |
| "has_suggestions": True, | |
| "comments": "Good signal", | |
| }, | |
| ) | |
| self.assertEqual(response.status_code, 200) | |
| save_feedback.assert_awaited_once() | |