Download api/tests/functional/test_generate_async_endpoint.py from Text-to-Document-Generation/Docgenie-API: direct link, hf CLI and curl.
- Browser
- Download file 5.49 kB
-
https://huggingface.co/spaces/Text-to-Document-Generation/Docgenie-API/resolve/main/api/tests/functional/test_generate_async_endpoint.py
- Command line
-
hf download hf://spaces/Text-to-Document-Generation/Docgenie-API/api/tests/functional/test_generate_async_endpoint.py
-
curl -L -o test_generate_async_endpoint.py https://huggingface.co/spaces/Text-to-Document-Generation/Docgenie-API/resolve/main/api/tests/functional/test_generate_async_endpoint.py
5.49 kB
| """ | |
| Functional tests — POST /generate/async endpoint | |
| ================================================== | |
| Tests for the async (RQ-queue) document generation endpoint. | |
| Key behaviours exercised: | |
| • Schema validation (422) for missing / bad fields | |
| • 404 when request_id not in Supabase | |
| • 503 behaviour when Redis queue is unavailable (if applicable) | |
| • Response contract when request is queued successfully | |
| • Same prompt_params validation as /generate/pdf (shared schema) | |
| """ | |
| import copy | |
| 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/async" | |
| def make_payload(**overrides): | |
| payload = copy.deepcopy(MINIMAL_GENERATE_PAYLOAD) | |
| payload.update(overrides) | |
| return payload | |
| def make_prompt_override(**kw): | |
| pp = copy.deepcopy(MINIMAL_GENERATE_PAYLOAD["prompt_params"]) | |
| pp.update(kw) | |
| return pp | |
| # --------------------------------------------------------------------------- | |
| # 1. Schema / Input Validation | |
| # --------------------------------------------------------------------------- | |
| class TestGenerateAsyncInputValidation: | |
| """FastAPI must reject malformed requests before any business logic.""" | |
| 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 | |
| def test_empty_seed_images_returns_422(self, http): | |
| r = http.post(ENDPOINT, json=make_payload(seed_images=[]), timeout=TIMEOUT) | |
| assert r.status_code == 422 | |
| def test_too_many_seed_images_returns_422(self, http): | |
| r = http.post(ENDPOINT, | |
| json=make_payload(seed_images=[SEED_IMAGE_URL] * 11), | |
| timeout=TIMEOUT) | |
| assert r.status_code == 422 | |
| def test_invalid_seed_image_url_returns_422(self, http): | |
| r = http.post(ENDPOINT, | |
| json=make_payload(seed_images=["not-a-url"]), | |
| timeout=TIMEOUT) | |
| assert r.status_code == 422 | |
| def test_num_solutions_below_min_returns_422(self, http): | |
| pp = make_prompt_override(num_solutions=0) | |
| r = http.post(ENDPOINT, json=make_payload(prompt_params=pp), timeout=TIMEOUT) | |
| assert r.status_code == 422 | |
| def test_num_solutions_above_max_returns_422(self, http): | |
| pp = make_prompt_override(num_solutions=6) | |
| r = http.post(ENDPOINT, json=make_payload(prompt_params=pp), timeout=TIMEOUT) | |
| assert r.status_code == 422 | |
| def test_empty_body_returns_422(self, http): | |
| r = http.post(ENDPOINT, json={}, timeout=TIMEOUT) | |
| assert r.status_code == 422 | |
| # --------------------------------------------------------------------------- | |
| # 2. Business-logic (valid schema, unknown request_id → 404 or 503) | |
| # --------------------------------------------------------------------------- | |
| class TestGenerateAsyncBusinessLogic: | |
| """ | |
| With a valid schema but nonexistent request_id the API should: | |
| • Return 404 if Redis is available (request_id lookup fails first), OR | |
| • Return 503 if Redis is unavailable (queue not initialised) | |
| Both are acceptable non-422 responses. | |
| """ | |
| def test_nonexistent_request_id_is_not_422(self, http): | |
| r = http.post(ENDPOINT, json=MINIMAL_GENERATE_PAYLOAD, timeout=TIMEOUT) | |
| assert r.status_code != 422, ( | |
| f"Valid schema must not produce 422, got {r.status_code}" | |
| ) | |
| def test_nonexistent_request_id_returns_404_or_503(self, http): | |
| r = http.post(ENDPOINT, json=MINIMAL_GENERATE_PAYLOAD, timeout=TIMEOUT) | |
| assert r.status_code in (404, 503), ( | |
| f"Expected 404 (no request) or 503 (no Redis), got {r.status_code}: {r.text}" | |
| ) | |
| def test_error_response_is_json(self, http): | |
| r = http.post(ENDPOINT, json=MINIMAL_GENERATE_PAYLOAD, timeout=TIMEOUT) | |
| assert "application/json" in r.headers.get("Content-Type", "") | |
| def test_error_response_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'. Got: {body}" | |
| def test_swagger_string_tokens_not_422(self, http): | |
| 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 | |
| def test_none_google_tokens_accepted(self, http): | |
| payload = make_payload(google_drive_token=None, google_drive_refresh_token=None) | |
| r = http.post(ENDPOINT, json=payload, timeout=TIMEOUT) | |
| assert r.status_code != 422 | |
| def test_num_solutions_boundary_values_schema_valid(self, http): | |
| for n in [1, 5]: | |
| pp = make_prompt_override(num_solutions=n) | |
| r = http.post(ENDPOINT, json=make_payload(prompt_params=pp), timeout=TIMEOUT) | |
| assert r.status_code != 422, ( | |
| f"num_solutions={n} should be schema-valid" | |
| ) | |
| def test_missing_prompt_params_uses_defaults(self, http): | |
| payload = {"request_id": NONEXISTENT_REQUEST_ID} | |
| r = http.post(ENDPOINT, json=payload, timeout=TIMEOUT) | |
| assert r.status_code != 422 | |