Download tests/test_request_limits.py from Kiriten892/wanderlust-chatbot: direct link, hf CLI and curl.
- Browser
- Download file 7.12 kB
-
https://huggingface.co/spaces/Kiriten892/wanderlust-chatbot/resolve/main/tests/test_request_limits.py
- Command line
-
hf download hf://spaces/Kiriten892/wanderlust-chatbot/tests/test_request_limits.py
-
curl -L -o test_request_limits.py https://huggingface.co/spaces/Kiriten892/wanderlust-chatbot/resolve/main/tests/test_request_limits.py
7.12 kB
| """ | |
| Input size caps on the ML service request models (app.api.schemas). | |
| Every request field is bounded so a single call cannot make the service process — | |
| or forward to the LLM — arbitrarily large payloads. Over-limit input is rejected | |
| with a 422 by FastAPI; these tests check the models directly plus one real-route | |
| round trip for the error shape. | |
| """ | |
| import pytest | |
| from pydantic import ValidationError | |
| from app.api import schemas | |
| from app.api.schemas import ( | |
| PredictRequest, RecommendRequest, PlanRequest, MultiDestPlanRequest, | |
| CompareRequest, BudgetOptimizeRequest, | |
| ) | |
| def _turn(content='hi', role='user'): | |
| return {'role': role, 'content': content} | |
| class TestPredictRequestLimits: | |
| def test_message_at_limit_ok(self): | |
| PredictRequest(text='a' * schemas.MAX_MESSAGE_CHARS) | |
| def test_message_over_limit_rejected(self): | |
| with pytest.raises(ValidationError): | |
| PredictRequest(text='a' * (schemas.MAX_MESSAGE_CHARS + 1)) | |
| def test_history_at_limit_ok(self): | |
| PredictRequest(text='hi', conversation_history=[_turn()] * schemas.MAX_HISTORY_TURNS) | |
| def test_too_many_history_turns_rejected(self): | |
| with pytest.raises(ValidationError): | |
| PredictRequest(text='hi', conversation_history=[_turn()] * (schemas.MAX_HISTORY_TURNS + 1)) | |
| def test_long_history_turn_rejected(self): | |
| with pytest.raises(ValidationError, match='too long'): | |
| PredictRequest(text='hi', conversation_history=[ | |
| _turn('x' * (schemas.MAX_HISTORY_TURN_CHARS + 1), role='assistant')]) | |
| def test_history_role_must_be_user_or_assistant(self, turn): | |
| with pytest.raises(ValidationError, match='role'): | |
| PredictRequest(text='hi', conversation_history=[turn]) | |
| def test_history_content_must_be_string(self): | |
| with pytest.raises(ValidationError, match='string'): | |
| PredictRequest(text='hi', conversation_history=[{'role': 'user', 'content': {'nested': 'x' * 10}}]) | |
| def test_history_turn_with_huge_extra_field_rejected(self): | |
| with pytest.raises(ValidationError): | |
| PredictRequest(text='hi', conversation_history=[{**_turn(), 'meta': 'x' * 10_000}]) | |
| def test_history_turn_with_small_extra_field_ok(self): | |
| PredictRequest(text='hi', conversation_history=[{**_turn(), 'timestamp': '2026-10-01T10:00:00Z'}]) | |
| def test_realistic_context_ok(self): | |
| PredictRequest(text='hi', context={ | |
| 'destination_id': 'da-nang', 'destination_name': 'Đà Nẵng', | |
| 'duration': {'days': 5, 'nights': 4}, 'budget': {'amount': 10_000_000, 'currency': 'VND'}, | |
| 'num_people': {'adults': 2, 'children': 1}, 'dietary_restrictions': ['no_seafood'], | |
| 'awaiting': 'dietary', | |
| }) | |
| def test_context_with_too_many_keys_rejected(self): | |
| with pytest.raises(ValidationError, match='too many keys'): | |
| PredictRequest(text='hi', context={f'k{i}': i for i in range(schemas.MAX_CONTEXT_KEYS + 1)}) | |
| def test_oversized_context_rejected(self): | |
| with pytest.raises(ValidationError, match='too large'): | |
| PredictRequest(text='hi', context={'blob': 'x' * (schemas.MAX_CONTEXT_JSON_CHARS + 1)}) | |
| def test_oversized_ids_rejected(self): | |
| with pytest.raises(ValidationError): | |
| PredictRequest(text='hi', user_id='u' * 101) | |
| with pytest.raises(ValidationError): | |
| PredictRequest(text='hi', language='v' * 11) | |
| class TestOtherRequestLimits: | |
| def test_recommend_lists_and_query_capped(self): | |
| RecommendRequest(tags=['beach'] * schemas.MAX_LIST_ITEMS, query='q' * 500, top_k=50, month=12) | |
| for kwargs in ( | |
| {'tags': ['beach'] * (schemas.MAX_LIST_ITEMS + 1)}, | |
| {'interests': ['x' * 101]}, | |
| {'query': 'q' * 501}, | |
| {'top_k': 51}, {'top_k': 0}, | |
| {'month': 13}, | |
| ): | |
| with pytest.raises(ValidationError): | |
| RecommendRequest(**kwargs) | |
| def test_plan_duration_days_capped(self): | |
| PlanRequest(destination_id='hanoi', duration={'days': schemas.MAX_TRIP_DAYS, 'nights': 59}) | |
| for duration in ({'days': schemas.MAX_TRIP_DAYS + 1}, {'days': 0}, {'days': '3'}, {'days': True}): | |
| with pytest.raises(ValidationError, match='days'): | |
| PlanRequest(destination_id='hanoi', duration=duration) | |
| def test_plan_other_fields_capped(self): | |
| for kwargs in ( | |
| {'destination_id': 'x' * 101}, | |
| {'preferences': ['a'] * (schemas.MAX_LIST_ITEMS + 1)}, | |
| {'num_people': {'adults': 10_000}}, | |
| {'budget': {'note': 'x' * 2000}}, | |
| ): | |
| with pytest.raises(ValidationError): | |
| PlanRequest(**{'destination_id': 'hanoi', 'duration': {'days': 3}, **kwargs}) | |
| def test_multi_destination_capped(self): | |
| MultiDestPlanRequest(destination_ids=['a', 'b'], total_days=schemas.MAX_TRIP_DAYS) | |
| with pytest.raises(ValidationError): | |
| MultiDestPlanRequest(destination_ids=['a'] * (schemas.MAX_DESTINATIONS + 1), total_days=5) | |
| with pytest.raises(ValidationError): | |
| MultiDestPlanRequest(destination_ids=['a', 'b'], total_days=schemas.MAX_TRIP_DAYS + 1) | |
| with pytest.raises(ValidationError): | |
| MultiDestPlanRequest(destination_ids=['a', 'b'], total_days=5, num_people={'children': -1}) | |
| def test_compare_capped(self): | |
| CompareRequest(destination_ids=['a', 'b'], days=3) | |
| for kwargs in ({'days': schemas.MAX_TRIP_DAYS + 1}, {'num_adults': schemas.MAX_PEOPLE + 1}, | |
| {'destination_ids': ['a'] * (schemas.MAX_DESTINATIONS + 1)}): | |
| with pytest.raises(ValidationError): | |
| CompareRequest(**{'destination_ids': ['a', 'b'], **kwargs}) | |
| def test_budget_optimize_capped(self): | |
| BudgetOptimizeRequest(destination_id='hanoi', total_budget=5e6, days=3, | |
| priorities={'accommodation': 'high', 'food': 'low'}) | |
| for kwargs in ({'days': schemas.MAX_TRIP_DAYS + 1}, {'month': 0}, | |
| {'priorities': {f'k{i}': 'high' for i in range(11)}}, | |
| {'currency': 'X' * 11}): | |
| with pytest.raises(ValidationError): | |
| BudgetOptimizeRequest(**{'destination_id': 'hanoi', 'total_budget': 5e6, 'days': 3, **kwargs}) | |
| class TestOverLimitIs422: | |
| def test_oversized_history_returns_422(self, monkeypatch): | |
| from fastapi.testclient import TestClient | |
| import app.main as main | |
| monkeypatch.setattr(main.settings, 'INTERNAL_API_KEY', 'test-key') | |
| main._rate_limit_requests.clear() | |
| client = TestClient(main.app) | |
| r = client.post('/api/predict/stream', headers={'X-ML-Service-Key': 'test-key'}, | |
| json={'text': 'hi', 'conversation_history': [_turn()] * 21}) | |
| assert r.status_code == 422 | |
| assert 'conversation_history' in r.text | |