import io import pytest from main import create_app from config import Config class TestConfig(Config): API_KEY = "test-key-123" DEBUG = True @pytest.fixture def app(): app = create_app(TestConfig) app.config["TESTING"] = True return app @pytest.fixture def client(app): return app.test_client() class TestHealthCheck: def test_health_no_auth_required(self, client): resp = client.get("/health") assert resp.status_code in (200, 503) data = resp.get_json() assert "status" in data assert "uptime_seconds" in data def test_health_returns_checks(self, client): resp = client.get("/health") data = resp.get_json() assert "checks" in data assert "upload_directory" in data["checks"] class TestAuth: def test_no_api_key_returns_401(self, client): resp = client.post("/api/ocr") assert resp.status_code == 401 def test_wrong_api_key_returns_401(self, client): resp = client.post("/api/ocr", headers={"X-API-Key": "wrong-key"}) assert resp.status_code == 401 def test_correct_api_key_passes_auth(self, client): # Will get 400 (no doc_type) but not 401 resp = client.post("/api/ocr", headers={"X-API-Key": "test-key-123"}) assert resp.status_code == 400 class TestOCREndpoint: def test_missing_doc_type_returns_400(self, client): resp = client.post( "/api/ocr", headers={"X-API-Key": "test-key-123"}, ) assert resp.status_code == 400 assert "doc_type is required" in resp.get_json()["error"] def test_invalid_doc_type_returns_400(self, client): resp = client.post( "/api/ocr", headers={"X-API-Key": "test-key-123"}, data={"doc_type": "invalid_type"}, ) assert resp.status_code == 400 assert "Invalid doc_type" in resp.get_json()["error"] def test_no_file_returns_400(self, client): resp = client.post( "/api/ocr", headers={"X-API-Key": "test-key-123"}, data={"doc_type": "sa_id_card"}, ) assert resp.status_code == 400 assert "No front image" in resp.get_json()["error"] def test_invalid_file_type_returns_400(self, client): data = { "front": (io.BytesIO(b"not an image"), "test.txt"), "doc_type": "sa_id_card", } resp = client.post( "/api/ocr", headers={"X-API-Key": "test-key-123"}, data=data, content_type="multipart/form-data", ) assert resp.status_code == 400 assert "Invalid file type" in resp.get_json()["error"] def test_back_rejected_for_non_id_card(self, client): data = { "front": (io.BytesIO(b"\x89PNG\r\n"), "front.png"), "back": (io.BytesIO(b"\x89PNG\r\n"), "back.png"), "doc_type": "sa_id_book", } resp = client.post( "/api/ocr", headers={"X-API-Key": "test-key-123"}, data=data, content_type="multipart/form-data", ) assert resp.status_code == 400 assert "only accepted for doc_type=sa_id_card" in resp.get_json()["error"] def test_legacy_file_key_accepted(self, client): """Legacy 'file' key should work as front image.""" data = { "file": (io.BytesIO(b"not an image"), "test.txt"), "doc_type": "sa_id_card", } resp = client.post( "/api/ocr", headers={"X-API-Key": "test-key-123"}, data=data, content_type="multipart/form-data", ) # 400 for invalid file type (not 400 for missing file) assert resp.status_code == 400 assert "Invalid file type" in resp.get_json()["error"] def test_request_id_in_response(self, client): resp = client.post( "/api/ocr", headers={"X-API-Key": "test-key-123", "X-Request-ID": "test-req-123"}, data={"doc_type": "sa_id_card"}, ) assert resp.headers.get("X-Request-ID") == "test-req-123" def test_request_id_generated_if_missing(self, client): resp = client.post( "/api/ocr", headers={"X-API-Key": "test-key-123"}, data={"doc_type": "sa_id_card"}, ) assert resp.headers.get("X-Request-ID") is not None class TestWarmEndpoint: def test_warm_no_auth_required(self, client): resp = client.get("/warm") assert resp.status_code == 200 data = resp.get_json() assert "status" in data assert "engines" in data class TestNotFound: def test_unknown_route_returns_404(self, client): resp = client.get("/api/unknown", headers={"X-API-Key": "test-key-123"}) assert resp.status_code == 404 data = resp.get_json() assert "error" in data