test_utils.py 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. from unittest import mock
  2. import pytest
  3. from django.conf import settings
  4. from urllib3.connectionpool import ConnectionPool
  5. from urllib3.exceptions import ConnectTimeoutError, MaxRetryError, ReadTimeoutError
  6. from sentry.profiles.utils import RetrySkipTimeout
  7. DUMMY_POOL = ConnectionPool("dummy")
  8. def get_url(path: str) -> str:
  9. return f"{settings.SENTRY_VROOM}{path}"
  10. def test_retry_on_post_profile() -> None:
  11. """
  12. When it's a POST to /profile, we should retry on ReadTimeoutErrors up to the maximum allowed.
  13. """
  14. path = "/profile"
  15. error = ReadTimeoutError(DUMMY_POOL, path, "read timed out")
  16. retry = RetrySkipTimeout(total=3, allowed_methods={"POST"})
  17. retry = retry.increment(method="POST", url=get_url(path), error=error)
  18. retry = retry.increment(method="POST", url=get_url(path), error=error)
  19. retry = retry.increment(method="POST", url=get_url(path), error=error)
  20. with pytest.raises(MaxRetryError):
  21. retry.increment(method="POST", url=get_url(path), error=error)
  22. def test_retry_on_other_route() -> None:
  23. """
  24. When it's NOT a POST to /profile, we should NOT retry on ReadTimeoutErrors.
  25. """
  26. path = f"/organizations/1/projects/1/profile/{'a' * 32}"
  27. error = ReadTimeoutError(DUMMY_POOL, path, "read timed out")
  28. retry = RetrySkipTimeout(total=3, allowed_methods={"GET"})
  29. with pytest.raises(ReadTimeoutError):
  30. retry.increment(method="GET", url=get_url(path), error=error)
  31. @pytest.mark.parametrize(
  32. "path,normalized",
  33. [
  34. ("/organizations/1/filters", "/organizations/:orgId/filters"),
  35. ("/organizations/1/profiles", "/organizations/:orgId/profiles"),
  36. (
  37. "/organizations/1/projects/1/functions",
  38. "/organizations/:orgId/projects/:projId/functions",
  39. ),
  40. (
  41. f"/organizations/1/projects/1/profiles/{'a' * 32}",
  42. "/organizations/:orgId/projects/:projId/profiles/:uuid",
  43. ),
  44. (
  45. f"/organizations/1/projects/1/raw_profiles/{'a' * 32}",
  46. "/organizations/:orgId/projects/:projId/raw_profiles/:uuid",
  47. ),
  48. (
  49. f"/organizations/1/projects/1/transactions/{'a' * 32}",
  50. "/organizations/:orgId/projects/:projId/transactions/:uuid",
  51. ),
  52. ("/organizations/1/stats", "/organizations/:orgId/stats"),
  53. ("/organizations/1/transactions", "/organizations/:orgId/transactions"),
  54. ("/call_tree", "/call_tree"),
  55. ("/profile", "/profile"),
  56. ],
  57. )
  58. def test_retry_metric_normalizes_path(path: str, normalized: str) -> None:
  59. error = ConnectTimeoutError()
  60. retry = RetrySkipTimeout(total=3, allowed_methods={"GET"})
  61. with mock.patch("sentry.profiles.utils.metrics.incr") as incr:
  62. retry = retry.increment(method="GET", url=get_url(path), error=error)
  63. incr.assert_called_with(
  64. "profiling.client.retry", tags={"method": "GET", "path": normalized}
  65. )