# allow: no-sut-import — black-box HTTP test; drives real routes through the Flask test client """Unit tests for Chat route input validation. Tests verify request validation in chat API routes: - Title length validation - Status value validation - Message content validation - Pagination parameter validation """ import json class TestSessionRouteValidation: """Tests for session route input validation.""" def test_create_session_accepts_valid_request(self, authenticated_client): """Test that valid session creation request succeeds.""" response = authenticated_client.post( "/api/chat/sessions", json={"initial_query": "Test query", "title": "Test Title"}, content_type="application/json", ) assert response.status_code == 200 data = json.loads(response.data) assert data["success"] is True def test_create_session_without_body(self, authenticated_client): """Test that session can be created with empty body.""" response = authenticated_client.post( "/api/chat/sessions", json={}, # Empty body content_type="application/json", ) # Should succeed - body fields are optional assert response.status_code == 200 data = json.loads(response.data) assert data["success"] is True def test_update_session_invalid_title_length(self, authenticated_client): """Test that titles over 500 chars are rejected.""" # First create a session create_response = authenticated_client.post( "/api/chat/sessions", json={"initial_query": "Test"}, content_type="application/json", ) session_id = json.loads(create_response.data)["session_id"] # Try to update with a too-long title long_title = "A" * 501 response = authenticated_client.patch( f"/api/chat/sessions/{session_id}", json={"title": long_title}, content_type="application/json", ) assert response.status_code == 400 data = json.loads(response.data) assert data["success"] is False assert "too long" in data["error"].lower() def test_update_session_invalid_status_value(self, authenticated_client): """Test that invalid status values are rejected.""" # First create a session create_response = authenticated_client.post( "/api/chat/sessions", json={"initial_query": "Test"}, content_type="application/json", ) session_id = json.loads(create_response.data)["session_id"] # Try to update with an invalid status response = authenticated_client.patch( f"/api/chat/sessions/{session_id}", json={"status": "invalid_status"}, content_type="application/json", ) assert response.status_code == 400 data = json.loads(response.data) assert data["success"] is False assert "invalid" in data["error"].lower() def test_get_session_nonexistent_uuid(self, authenticated_client): """Test that nonexistent session returns 404.""" response = authenticated_client.get( "/api/chat/sessions/00000000-0000-0000-0000-000000000000" ) assert response.status_code == 404 data = json.loads(response.data) assert data["success"] is False class TestMessageRouteValidation: """Tests for message route input validation.""" def test_send_message_empty_content_rejected(self, authenticated_client): """Test that empty message content is rejected.""" # Create a session first create_response = authenticated_client.post( "/api/chat/sessions", json={"initial_query": "Test"}, content_type="application/json", ) session_id = json.loads(create_response.data)["session_id"] # Try to send empty message response = authenticated_client.post( f"/api/chat/sessions/{session_id}/messages", json={"content": ""}, content_type="application/json", ) assert response.status_code == 400 data = json.loads(response.data) assert data["success"] is False assert "required" in data["error"].lower() def test_send_message_missing_content_rejected(self, authenticated_client): """Test that missing content field is rejected.""" # Create a session first create_response = authenticated_client.post( "/api/chat/sessions", json={"initial_query": "Test"}, content_type="application/json", ) session_id = json.loads(create_response.data)["session_id"] # Try to send message without content response = authenticated_client.post( f"/api/chat/sessions/{session_id}/messages", json={}, content_type="application/json", ) assert response.status_code == 400 data = json.loads(response.data) assert data["success"] is False def test_send_message_content_length_limit(self, authenticated_client): """Test that message content is limited to 10,000 characters.""" # Create a session first create_response = authenticated_client.post( "/api/chat/sessions", json={"initial_query": "Test"}, content_type="application/json", ) session_id = json.loads(create_response.data)["session_id"] # Try to send message with content > 10000 chars long_content = "A" * 10001 response = authenticated_client.post( f"/api/chat/sessions/{session_id}/messages", json={"content": long_content, "trigger_research": False}, content_type="application/json", ) assert response.status_code == 400 data = json.loads(response.data) assert data["success"] is False assert "too long" in data["error"].lower() or "10000" in data["error"] def test_send_message_to_nonexistent_session(self, authenticated_client): """Test that sending to nonexistent session returns 404.""" response = authenticated_client.post( "/api/chat/sessions/00000000-0000-0000-0000-000000000000/messages", json={"content": "Test message", "trigger_research": False}, content_type="application/json", ) assert response.status_code == 404 data = json.loads(response.data) assert data["success"] is False class TestPaginationValidation: """Tests for pagination parameter validation.""" def test_get_messages_pagination_limits(self, authenticated_client): """Test that pagination parameters are bounded correctly.""" # Create a session create_response = authenticated_client.post( "/api/chat/sessions", json={"initial_query": "Test"}, content_type="application/json", ) session_id = json.loads(create_response.data)["session_id"] # Test with limit > 100 (should be capped) response = authenticated_client.get( f"/api/chat/sessions/{session_id}/messages?limit=200&offset=0" ) assert response.status_code == 200 # Response should succeed (limit is capped internally) data = json.loads(response.data) assert data["success"] is True def test_get_messages_invalid_offset(self, authenticated_client): """Test that invalid offset falls back to default.""" # Create a session create_response = authenticated_client.post( "/api/chat/sessions", json={"initial_query": "Test"}, content_type="application/json", ) session_id = json.loads(create_response.data)["session_id"] # Test with invalid offset (should fall back to 0) response = authenticated_client.get( f"/api/chat/sessions/{session_id}/messages?offset=invalid" ) assert response.status_code == 200 data = json.loads(response.data) assert data["success"] is True def test_list_sessions_invalid_status_falls_back( self, authenticated_client ): """Test that invalid status falls back to 'active'.""" response = authenticated_client.get( "/api/chat/sessions?status=invalid_status" ) # Should succeed and return active sessions assert response.status_code == 200 data = json.loads(response.data) assert data["success"] is True def test_list_sessions_valid_status_values(self, authenticated_client): """Test that all valid status values are accepted.""" valid_statuses = ["active", "archived", "deleted", "all"] for status in valid_statuses: response = authenticated_client.get( f"/api/chat/sessions?status={status}" ) assert response.status_code == 200, ( f"Status '{status}' should be valid" ) data = json.loads(response.data) assert data["success"] is True class TestStatusTransitionValidation: """Tests for session status transition validation.""" def test_session_can_be_archived(self, authenticated_client): """Test that active session can be archived.""" # Create session create_response = authenticated_client.post( "/api/chat/sessions", json={"initial_query": "Test"}, content_type="application/json", ) session_id = json.loads(create_response.data)["session_id"] # Archive it response = authenticated_client.patch( f"/api/chat/sessions/{session_id}", json={"status": "archived"}, content_type="application/json", ) assert response.status_code == 200 data = json.loads(response.data) assert data["success"] is True assert data["session"]["status"] == "archived" def test_session_can_be_deleted(self, authenticated_client): """Test that session can be permanently deleted.""" # Create session create_response = authenticated_client.post( "/api/chat/sessions", json={"initial_query": "Test"}, content_type="application/json", ) session_id = json.loads(create_response.data)["session_id"] # Delete it (hard delete) response = authenticated_client.delete( f"/api/chat/sessions/{session_id}" ) assert response.status_code == 200 data = json.loads(response.data) assert data["success"] is True # Verify it's gone get_response = authenticated_client.get( f"/api/chat/sessions/{session_id}" ) assert get_response.status_code == 404