test_transport.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. import io
  2. from typing import Any
  3. from unittest.mock import patch
  4. from minimetrics.core import CounterMetric, DistributionMetric, GaugeMetric, SetMetric
  5. from minimetrics.transport import MetricEnvelopeTransport, RelayStatsdEncoder
  6. from minimetrics.types import BucketKey
  7. from sentry.testutils.helpers import override_options
  8. from sentry.testutils.pytest.fixtures import django_db_all
  9. def encode_metric(value):
  10. encoder = RelayStatsdEncoder()
  11. out = io.BytesIO()
  12. encoder._encode(value, out)
  13. return out.getvalue().decode("utf-8")
  14. def test_relay_encoder_with_counter():
  15. bucket_key: BucketKey = (
  16. "c",
  17. "button_click",
  18. "none",
  19. (
  20. ("browser", "Chrome"),
  21. ("browser.version", "1.0"),
  22. ),
  23. )
  24. metric = CounterMetric(first=2)
  25. flushed_metric = (1693994400, bucket_key, metric)
  26. result = encode_metric(flushed_metric)
  27. assert result == "button_click@none:2|c|#browser:Chrome,browser.version:1.0|T1693994400"
  28. def test_relay_encoder_with_distribution():
  29. bucket_key: BucketKey = (
  30. "d",
  31. "execution_time",
  32. "second",
  33. (
  34. ("browser", "Chrome"),
  35. ("browser.version", "1.0"),
  36. ),
  37. )
  38. metric = DistributionMetric(first=1.0)
  39. metric.add(0.5)
  40. metric.add(3.0)
  41. flushed_metric = (1693994400, bucket_key, metric)
  42. result = encode_metric(flushed_metric)
  43. assert (
  44. result
  45. == "execution_time@second:1.0:0.5:3.0|d|#browser:Chrome,browser.version:1.0|T1693994400"
  46. )
  47. def test_relay_encoder_with_set():
  48. bucket_key: BucketKey = (
  49. "s",
  50. "users",
  51. "none",
  52. (
  53. ("browser", "Chrome"),
  54. ("browser.version", "1.0"),
  55. ),
  56. )
  57. metric = SetMetric(first=123)
  58. metric.add(456)
  59. metric.add("riccardo")
  60. flushed_metric = (1693994400, bucket_key, metric)
  61. result = encode_metric(flushed_metric)
  62. pieces = result.split("|")
  63. m = pieces[0].split(":")
  64. assert m[0] == "users@none"
  65. assert sorted(m[1:]) == sorted(["123", "456", "3455635177"])
  66. assert pieces[1] == "s"
  67. assert pieces[2] == "#browser:Chrome,browser.version:1.0"
  68. assert pieces[3] == "T1693994400"
  69. def test_relay_encoder_with_gauge():
  70. bucket_key: BucketKey = (
  71. "g",
  72. "startup_time",
  73. "second",
  74. (
  75. ("browser", "Chrome"),
  76. ("browser.version", "1.0"),
  77. ),
  78. )
  79. metric = GaugeMetric(first=10.0)
  80. metric.add(5.0)
  81. metric.add(7.0)
  82. flushed_metric = (1693994400, bucket_key, metric)
  83. result = encode_metric(flushed_metric)
  84. assert (
  85. result
  86. == "startup_time@second:7.0:5.0:10.0:22.0:3|g|#browser:Chrome,browser.version:1.0|T1693994400"
  87. )
  88. def test_relay_encoder_with_invalid_chars():
  89. bucket_key: BucketKey = (
  90. "c",
  91. "büttòn_click",
  92. "second",
  93. (
  94. # Invalid tag key.
  95. ("browser\nname", "Chrome"),
  96. # Invalid tag value.
  97. ("browser.version", "\t1.\n0ô"),
  98. # Valid tag key and value.
  99. ("platform", "Android"),
  100. # Totally invalid tag key.
  101. ("\nöś", "Windows"),
  102. # Totally invalid tag value.
  103. ("version", "\n\t"),
  104. ),
  105. )
  106. metric = CounterMetric(first=1)
  107. flushed_metric = (1693994400, bucket_key, metric)
  108. result = encode_metric(flushed_metric)
  109. assert (
  110. result
  111. == "bttn_click@second:1|c|#browsername:Chrome,browser.version:1.0,platform:Android,version:|T1693994400"
  112. )
  113. bucket_key = (
  114. "c",
  115. "üòë",
  116. "second",
  117. (),
  118. )
  119. metric = CounterMetric(first=1)
  120. flushed_metric = (1693994400, bucket_key, metric)
  121. assert encode_metric(flushed_metric) == "invalid-metric-name@second:1|c|T1693994400"
  122. def test_relay_encoder_with_multiple_metrics():
  123. encoder = RelayStatsdEncoder()
  124. flushed_metric_1 = (
  125. 1693994400,
  126. (
  127. "g",
  128. "startup_time",
  129. "second",
  130. (
  131. ("browser", "Chrome"),
  132. ("browser.version", "1.0"),
  133. ),
  134. ),
  135. GaugeMetric(first=10.0),
  136. )
  137. flushed_metric_2 = (
  138. 1693994400,
  139. (
  140. "c",
  141. "button_click",
  142. "none",
  143. (
  144. ("browser", "Chrome"),
  145. ("browser.version", "1.0"),
  146. ),
  147. ),
  148. CounterMetric(first=1),
  149. )
  150. flushed_metric_3 = (
  151. 1693994400,
  152. (
  153. "c",
  154. # This name will be completely scraped, resulting in an invalid metric.
  155. "öüâ",
  156. "none",
  157. (
  158. ("browser", "Chrome"),
  159. ("browser.version", "1.0"),
  160. ),
  161. ),
  162. CounterMetric(first=1),
  163. )
  164. metrics: Any = [flushed_metric_1, flushed_metric_2, flushed_metric_3]
  165. result = encoder.encode_multiple(metrics).decode("utf-8")
  166. assert result == (
  167. "startup_time@second:10.0:10.0:10.0:10.0:1|g|#browser:Chrome,browser.version:1.0|T1693994400\n"
  168. "button_click@none:1|c|#browser:Chrome,browser.version:1.0|T1693994400\n"
  169. "invalid-metric-name@none:1|c|#browser:Chrome,browser.version:1.0|T1693994400\n"
  170. )
  171. @override_options(
  172. {
  173. "delightful_metrics.enable_envelope_serialization": True,
  174. "delightful_metrics.enable_capture_envelope": True,
  175. }
  176. )
  177. @patch("minimetrics.transport.sentry_sdk")
  178. @django_db_all
  179. def test_send(sentry_sdk):
  180. flushed_metric = (
  181. 1693994400,
  182. {
  183. (
  184. "c",
  185. "button_click",
  186. "none",
  187. (
  188. ("browser", "Chrome"),
  189. ("browser.version", "1.0"),
  190. ),
  191. ): CounterMetric(first=1),
  192. },
  193. )
  194. transport = MetricEnvelopeTransport(RelayStatsdEncoder())
  195. metrics: Any = [flushed_metric]
  196. transport.send(metrics)
  197. args = sentry_sdk.Hub.current.client.transport.capture_envelope.call_args.args
  198. assert len(args) == 1
  199. arg = args[0]
  200. assert arg.items[0].type == "statsd"
  201. assert arg.items[0].data_category == "statsd"