2
2
import unittest
3
3
from unittest .mock import MagicMock , patch
4
4
5
+ import psycopg2
5
6
import pytest
7
+ from django .db import OperationalError
6
8
from django .utils import timezone
7
- from rest_framework .exceptions import APIException
9
+ from rest_framework .exceptions import APIException , Throttled
8
10
from sentry_sdk import Scope
9
11
10
12
from sentry .api .utils import (
17
19
from sentry .exceptions import IncompatibleMetricsQuery , InvalidParams , InvalidSearchQuery
18
20
from sentry .testutils .cases import APITestCase
19
21
from sentry .testutils .helpers .datetime import freeze_time
22
+ from sentry .testutils .helpers .options import override_options
20
23
from sentry .utils .snuba import (
21
24
DatasetSelectionError ,
22
25
QueryConnectionFailed ,
@@ -168,13 +171,12 @@ class FooBarError(Exception):
168
171
pass
169
172
170
173
171
- class HandleQueryErrorsTest :
174
+ class HandleQueryErrorsTest ( APITestCase ) :
172
175
@patch ("sentry.api.utils.ParseError" )
173
176
def test_handle_query_errors (self , mock_parse_error ):
174
177
exceptions = [
175
178
DatasetSelectionError ,
176
179
IncompatibleMetricsQuery ,
177
- InvalidParams ,
178
180
InvalidSearchQuery ,
179
181
QueryConnectionFailed ,
180
182
QueryExecutionError ,
@@ -198,6 +200,42 @@ def test_handle_query_errors(self, mock_parse_error):
198
200
except Exception as e :
199
201
assert isinstance (e , (FooBarError , APIException ))
200
202
203
+ def test_handle_postgres_timeout (self ):
204
+ class TimeoutError (OperationalError ):
205
+ pgcode = psycopg2 .errorcodes .QUERY_CANCELED
206
+
207
+ # Test when option is disabled (default)
208
+ try :
209
+ with handle_query_errors ():
210
+ raise TimeoutError ()
211
+ except Exception as e :
212
+ assert isinstance (e , TimeoutError ) # Should propagate original error
213
+
214
+ # Test when option is enabled
215
+ with override_options ({"api.postgres-query-timeout-error-handling.enabled" : True }):
216
+ try :
217
+ with handle_query_errors ():
218
+ raise TimeoutError ()
219
+ except Exception as e :
220
+ assert isinstance (e , Throttled )
221
+ assert (
222
+ str (e )
223
+ == "Query timeout. Please try with a smaller date range or fewer conditions."
224
+ )
225
+
226
+ @patch ("sentry.api.utils.ParseError" )
227
+ def test_handle_other_operational_error (self , mock_parse_error ):
228
+ class OtherError (OperationalError ):
229
+ # No pgcode attribute
230
+ pass
231
+
232
+ try :
233
+ with handle_query_errors ():
234
+ raise OtherError ()
235
+ except Exception as e :
236
+ assert isinstance (e , OtherError ) # Should propagate original error
237
+ mock_parse_error .assert_not_called ()
238
+
201
239
202
240
class ClampDateRangeTest (unittest .TestCase ):
203
241
def test_no_clamp_if_range_under_max (self ):
0 commit comments