Download api/tests/functional/test_generate_pdf_endpoint.py from Text-to-Document-Generation/Docgenie-API: direct link, hf CLI and curl.
- Browser
- Download file 7.97 kB
-
https://huggingface.co/spaces/Text-to-Document-Generation/Docgenie-API/resolve/main/api/tests/functional/test_generate_pdf_endpoint.py
- Command line
-
hf download hf://spaces/Text-to-Document-Generation/Docgenie-API/api/tests/functional/test_generate_pdf_endpoint.py
-
curl -L -o test_generate_pdf_endpoint.py https://huggingface.co/spaces/Text-to-Document-Generation/Docgenie-API/resolve/main/api/tests/functional/test_generate_pdf_endpoint.py
7.97 kB
| """ | |
| Functional tests β POST /generate/pdf endpoint | |
| ================================================ | |
| Tests for the synchronous PDF generation endpoint. | |
| Key behaviours exercised: | |
| β’ Schema validation (422) for missing / bad fields | |
| β’ 404 when request_id not in Supabase | |
| β’ Response headers contract (Content-Type, Content-Disposition) | |
| β’ num_solutions validation bounds (1β5) | |
| β’ Swagger-default token sanitisation ("string" β None) | |
| β’ seed_images validator (empty list β 422) | |
| """ | |
| import json | |
| import pytest | |
| import requests | |
| from tests.conftest import ( | |
| BASE_URL, TIMEOUT, SEED_IMAGE_URL, | |
| NONEXISTENT_REQUEST_ID, MINIMAL_GENERATE_PAYLOAD, | |
| ) | |
| ENDPOINT = f"{BASE_URL}/generate/pdf" | |
| # --------------------------------------------------------------------------- | |
| # Helpers | |
| # --------------------------------------------------------------------------- | |
| def make_payload(**overrides): | |
| import copy | |
| payload = copy.deepcopy(MINIMAL_GENERATE_PAYLOAD) | |
| payload.update(overrides) | |
| return payload | |
| def make_prompt_override(**kw): | |
| import copy | |
| pp = copy.deepcopy(MINIMAL_GENERATE_PAYLOAD["prompt_params"]) | |
| pp.update(kw) | |
| return pp | |
| # --------------------------------------------------------------------------- | |
| # 1. Schema / Input Validation (expects 422 from FastAPI) | |
| # --------------------------------------------------------------------------- | |
| class TestGeneratePdfInputValidation: | |
| """FastAPI must reject malformed requests with 422 Unprocessable Entity.""" | |
| def test_missing_request_id_returns_422(self, http): | |
| payload = { | |
| "seed_images": [SEED_IMAGE_URL], | |
| "prompt_params": MINIMAL_GENERATE_PAYLOAD["prompt_params"], | |
| } | |
| r = http.post(ENDPOINT, json=payload, timeout=TIMEOUT) | |
| assert r.status_code == 422, ( | |
| f"Expected 422 for missing request_id, got {r.status_code}" | |
| ) | |
| def test_empty_seed_images_returns_422(self, http): | |
| payload = make_payload(seed_images=[]) | |
| r = http.post(ENDPOINT, json=payload, timeout=TIMEOUT) | |
| assert r.status_code == 422, ( | |
| f"Expected 422 for empty seed_images, got {r.status_code}" | |
| ) | |
| def test_too_many_seed_images_returns_422(self, http): | |
| payload = make_payload(seed_images=[SEED_IMAGE_URL] * 11) | |
| r = http.post(ENDPOINT, json=payload, timeout=TIMEOUT) | |
| assert r.status_code == 422, ( | |
| f"Expected 422 for 11 seed_images (max 10), got {r.status_code}" | |
| ) | |
| def test_invalid_seed_image_url_returns_422(self, http): | |
| payload = make_payload(seed_images=["not-a-url"]) | |
| r = http.post(ENDPOINT, json=payload, timeout=TIMEOUT) | |
| assert r.status_code == 422, ( | |
| f"Expected 422 for non-URL seed image, got {r.status_code}" | |
| ) | |
| def test_num_solutions_below_min_returns_422(self, http): | |
| pp = make_prompt_override(num_solutions=0) | |
| payload = make_payload(prompt_params=pp) | |
| r = http.post(ENDPOINT, json=payload, timeout=TIMEOUT) | |
| assert r.status_code == 422, ( | |
| f"Expected 422 for num_solutions=0, got {r.status_code}" | |
| ) | |
| def test_num_solutions_above_max_returns_422(self, http): | |
| pp = make_prompt_override(num_solutions=6) | |
| payload = make_payload(prompt_params=pp) | |
| r = http.post(ENDPOINT, json=payload, timeout=TIMEOUT) | |
| assert r.status_code == 422, ( | |
| f"Expected 422 for num_solutions=6, got {r.status_code}" | |
| ) | |
| def test_handwriting_ratio_out_of_range_returns_422(self, http): | |
| pp = make_prompt_override(handwriting_ratio=1.5) | |
| payload = make_payload(prompt_params=pp) | |
| r = http.post(ENDPOINT, json=payload, timeout=TIMEOUT) | |
| assert r.status_code == 422, ( | |
| f"Expected 422 for handwriting_ratio=1.5, got {r.status_code}" | |
| ) | |
| def test_non_json_body_returns_422(self, http): | |
| r = http.post(ENDPOINT, data="this is not json", timeout=TIMEOUT, | |
| headers={"Content-Type": "application/json"}) | |
| assert r.status_code == 422, ( | |
| f"Expected 422 for non-JSON body, got {r.status_code}" | |
| ) | |
| def test_empty_body_returns_422(self, http): | |
| r = http.post(ENDPOINT, json={}, timeout=TIMEOUT) | |
| assert r.status_code == 422, ( | |
| f"Expected 422 for empty body, got {r.status_code}" | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # 2. Business-logic validation (valid JSON but non-existent request_id β 404) | |
| # --------------------------------------------------------------------------- | |
| class TestGeneratePdfBusinessLogic: | |
| """Valid schema, but request_id not in Supabase β 404.""" | |
| def test_nonexistent_request_id_returns_404(self, http): | |
| r = http.post(ENDPOINT, json=MINIMAL_GENERATE_PAYLOAD, timeout=TIMEOUT) | |
| assert r.status_code == 404, ( | |
| f"Expected 404 for unknown request_id, got {r.status_code}: {r.text}" | |
| ) | |
| def test_nonexistent_request_id_error_is_json(self, http): | |
| r = http.post(ENDPOINT, json=MINIMAL_GENERATE_PAYLOAD, timeout=TIMEOUT) | |
| assert "application/json" in r.headers.get("Content-Type", ""), ( | |
| "Error response must be JSON" | |
| ) | |
| def test_nonexistent_request_id_has_detail(self, http): | |
| r = http.post(ENDPOINT, json=MINIMAL_GENERATE_PAYLOAD, timeout=TIMEOUT) | |
| body = r.json() | |
| assert "detail" in body, f"Error body must have 'detail' field. Got: {body}" | |
| def test_swagger_string_token_is_sanitised(self, http): | |
| """ | |
| The frontend Swagger UI sends literal "string" as token defaults. | |
| The API should accept the request (not 422) and treat "string" as None. | |
| The result is still 404 because the request_id doesn't exist β but NOT 422. | |
| """ | |
| payload = make_payload( | |
| google_drive_token="string", | |
| google_drive_refresh_token="string", | |
| ) | |
| r = http.post(ENDPOINT, json=payload, timeout=TIMEOUT) | |
| assert r.status_code != 422, ( | |
| "API should NOT reject 'string' tokens with 422 β it should sanitise them." | |
| ) | |
| def test_none_google_tokens_are_accepted(self, http): | |
| """Explicitly null tokens are valid (Google Drive is optional).""" | |
| payload = make_payload( | |
| google_drive_token=None, | |
| google_drive_refresh_token=None, | |
| ) | |
| r = http.post(ENDPOINT, json=payload, timeout=TIMEOUT) | |
| # Still 404 (no real request_id) but schema accepted β not 422 | |
| assert r.status_code != 422 | |
| def test_valid_num_solutions_boundary_values_accepted(self, http): | |
| """num_solutions=1 and num_solutions=5 should pass schema validation.""" | |
| for n in [1, 5]: | |
| pp = make_prompt_override(num_solutions=n) | |
| payload = make_payload(prompt_params=pp) | |
| r = http.post(ENDPOINT, json=payload, timeout=TIMEOUT) | |
| assert r.status_code != 422, ( | |
| f"num_solutions={n} should be schema-valid, got {r.status_code}" | |
| ) | |
| def test_missing_prompt_params_uses_defaults(self, http): | |
| """prompt_params is optional (has defaults); omitting it must not raise 422.""" | |
| payload = {"request_id": NONEXISTENT_REQUEST_ID} | |
| r = http.post(ENDPOINT, json=payload, timeout=TIMEOUT) | |
| assert r.status_code != 422, ( | |
| f"Missing prompt_params should use defaults, got {r.status_code}" | |
| ) | |
| def test_request_id_with_user_prefix_is_accepted(self, http): | |
| """request_id supports 'user_id/request_id' format.""" | |
| payload = make_payload(request_id=f"user_42/{NONEXISTENT_REQUEST_ID}") | |
| r = http.post(ENDPOINT, json=payload, timeout=TIMEOUT) | |
| # The format is valid; server parses and looks up uuid β 404 | |
| assert r.status_code in (404, 500), ( | |
| f"Prefixed request_id should parse fine, got {r.status_code}" | |
| ) | |