|
4 | 4 | import pytest |
5 | 5 | from pydantic import ValidationError |
6 | 6 |
|
7 | | -from feast.arrow_error_handler import arrow_client_error_handling_decorator |
8 | | -from feast.errors import PermissionNotFoundException |
| 7 | +from feast.arrow_error_handler import ( |
| 8 | + _get_exception_data, |
| 9 | + arrow_client_error_handling_decorator, |
| 10 | +) |
| 11 | +from feast.errors import FeatureViewNotFoundException, PermissionNotFoundException |
9 | 12 | from feast.infra.offline_stores.remote import RemoteOfflineStoreConfig |
10 | 13 |
|
11 | 14 | permissionError = PermissionNotFoundException("dummy_name", "dummy_project") |
@@ -179,3 +182,50 @@ def method_on_client(self_arg): |
179 | 182 | def test_config_rejects_negative_connection_retries(self): |
180 | 183 | with pytest.raises(ValidationError): |
181 | 184 | RemoteOfflineStoreConfig(host="localhost", connection_retries=-1) |
| 185 | + |
| 186 | + |
| 187 | +class TestGetExceptionData: |
| 188 | + def test_non_string_input_returns_empty(self): |
| 189 | + assert _get_exception_data(12345) == "" |
| 190 | + assert _get_exception_data(None) == "" |
| 191 | + assert _get_exception_data(b"bytes") == "" |
| 192 | + |
| 193 | + def test_no_flight_error_prefix_returns_empty(self): |
| 194 | + assert _get_exception_data("some random error") == "" |
| 195 | + |
| 196 | + def test_flight_error_prefix_without_json_returns_empty(self): |
| 197 | + assert _get_exception_data("Flight error: no json here") == "" |
| 198 | + |
| 199 | + def test_extracts_json_from_flight_error(self): |
| 200 | + fv_error = FeatureViewNotFoundException("my_view", "my_project") |
| 201 | + error_str = f"Flight error: {fv_error.to_error_detail()}" |
| 202 | + result = _get_exception_data(error_str) |
| 203 | + assert '"class": "FeatureViewNotFoundException"' in result |
| 204 | + assert '"module": "feast.errors"' in result |
| 205 | + |
| 206 | + def test_extracts_json_with_trailing_text(self): |
| 207 | + fv_error = FeatureViewNotFoundException("my_view", "my_project") |
| 208 | + error_str = ( |
| 209 | + f"Flight error: {fv_error.to_error_detail()}. " |
| 210 | + "gRPC client debug context: some extra info" |
| 211 | + ) |
| 212 | + result = _get_exception_data(error_str) |
| 213 | + assert '"class": "FeatureViewNotFoundException"' in result |
| 214 | + assert '"module": "feast.errors"' in result |
| 215 | + |
| 216 | + def test_extracts_json_with_grpc_debug_context_containing_braces(self): |
| 217 | + fv_error = FeatureViewNotFoundException("my_view", "my_project") |
| 218 | + error_str = ( |
| 219 | + f"Flight error: {fv_error.to_error_detail()}. " |
| 220 | + "gRPC client debug context: UNKNOWN:Error received from peer " |
| 221 | + 'ipv4:127.0.0.1:59930 {grpc_message:"Flight error: ...", ' |
| 222 | + 'grpc_status:2, created_time:"2026-03-17T17:32:07"}' |
| 223 | + ) |
| 224 | + result = _get_exception_data(error_str) |
| 225 | + assert '"class": "FeatureViewNotFoundException"' in result |
| 226 | + assert '"module": "feast.errors"' in result |
| 227 | + from feast.errors import FeastError |
| 228 | + |
| 229 | + reconstructed = FeastError.from_error_detail(result) |
| 230 | + assert reconstructed is not None |
| 231 | + assert "my_view" in str(reconstructed) |
0 commit comments