123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- import io
- from typing import Any
- from unittest.mock import patch
- from minimetrics.core import CounterMetric, DistributionMetric, GaugeMetric, SetMetric
- from minimetrics.transport import MetricEnvelopeTransport, RelayStatsdEncoder
- from minimetrics.types import BucketKey
- from sentry.testutils.helpers import override_options
- from sentry.testutils.pytest.fixtures import django_db_all
- def encode_metric(value):
- encoder = RelayStatsdEncoder()
- out = io.BytesIO()
- encoder._encode(value, out)
- return out.getvalue().decode("utf-8")
- def test_relay_encoder_with_counter():
- bucket_key: BucketKey = (
- "c",
- "button_click",
- "none",
- (
- ("browser", "Chrome"),
- ("browser.version", "1.0"),
- ),
- )
- metric = CounterMetric(first=2)
- flushed_metric = (1693994400, bucket_key, metric)
- result = encode_metric(flushed_metric)
- assert result == "button_click@none:2|c|#browser:Chrome,browser.version:1.0|T1693994400"
- def test_relay_encoder_with_distribution():
- bucket_key: BucketKey = (
- "d",
- "execution_time",
- "second",
- (
- ("browser", "Chrome"),
- ("browser.version", "1.0"),
- ),
- )
- metric = DistributionMetric(first=1.0)
- metric.add(0.5)
- metric.add(3.0)
- flushed_metric = (1693994400, bucket_key, metric)
- result = encode_metric(flushed_metric)
- assert (
- result
- == "execution_time@second:1.0:0.5:3.0|d|#browser:Chrome,browser.version:1.0|T1693994400"
- )
- def test_relay_encoder_with_set():
- bucket_key: BucketKey = (
- "s",
- "users",
- "none",
- (
- ("browser", "Chrome"),
- ("browser.version", "1.0"),
- ),
- )
- metric = SetMetric(first=123)
- metric.add(456)
- metric.add("riccardo")
- flushed_metric = (1693994400, bucket_key, metric)
- result = encode_metric(flushed_metric)
- pieces = result.split("|")
- m = pieces[0].split(":")
- assert m[0] == "users@none"
- assert sorted(m[1:]) == sorted(["123", "456", "3455635177"])
- assert pieces[1] == "s"
- assert pieces[2] == "#browser:Chrome,browser.version:1.0"
- assert pieces[3] == "T1693994400"
- def test_relay_encoder_with_gauge():
- bucket_key: BucketKey = (
- "g",
- "startup_time",
- "second",
- (
- ("browser", "Chrome"),
- ("browser.version", "1.0"),
- ),
- )
- metric = GaugeMetric(first=10.0)
- metric.add(5.0)
- metric.add(7.0)
- flushed_metric = (1693994400, bucket_key, metric)
- result = encode_metric(flushed_metric)
- assert (
- result
- == "startup_time@second:7.0:5.0:10.0:22.0:3|g|#browser:Chrome,browser.version:1.0|T1693994400"
- )
- def test_relay_encoder_with_invalid_chars():
- bucket_key: BucketKey = (
- "c",
- "büttòn_click",
- "second",
- (
- # Invalid tag key.
- ("browser\nname", "Chrome"),
- # Invalid tag value.
- ("browser.version", "\t1.\n0ô"),
- # Valid tag key and value.
- ("platform", "Android"),
- # Totally invalid tag key.
- ("\nöś", "Windows"),
- # Totally invalid tag value.
- ("version", "\n\t"),
- ),
- )
- metric = CounterMetric(first=1)
- flushed_metric = (1693994400, bucket_key, metric)
- result = encode_metric(flushed_metric)
- assert (
- result
- == "bttn_click@second:1|c|#browsername:Chrome,browser.version:1.0,platform:Android,version:|T1693994400"
- )
- bucket_key = (
- "c",
- "üòë",
- "second",
- (),
- )
- metric = CounterMetric(first=1)
- flushed_metric = (1693994400, bucket_key, metric)
- assert encode_metric(flushed_metric) == "invalid-metric-name@second:1|c|T1693994400"
- def test_relay_encoder_with_multiple_metrics():
- encoder = RelayStatsdEncoder()
- flushed_metric_1 = (
- 1693994400,
- (
- "g",
- "startup_time",
- "second",
- (
- ("browser", "Chrome"),
- ("browser.version", "1.0"),
- ),
- ),
- GaugeMetric(first=10.0),
- )
- flushed_metric_2 = (
- 1693994400,
- (
- "c",
- "button_click",
- "none",
- (
- ("browser", "Chrome"),
- ("browser.version", "1.0"),
- ),
- ),
- CounterMetric(first=1),
- )
- flushed_metric_3 = (
- 1693994400,
- (
- "c",
- # This name will be completely scraped, resulting in an invalid metric.
- "öüâ",
- "none",
- (
- ("browser", "Chrome"),
- ("browser.version", "1.0"),
- ),
- ),
- CounterMetric(first=1),
- )
- metrics: Any = [flushed_metric_1, flushed_metric_2, flushed_metric_3]
- result = encoder.encode_multiple(metrics).decode("utf-8")
- assert result == (
- "startup_time@second:10.0:10.0:10.0:10.0:1|g|#browser:Chrome,browser.version:1.0|T1693994400\n"
- "button_click@none:1|c|#browser:Chrome,browser.version:1.0|T1693994400\n"
- "invalid-metric-name@none:1|c|#browser:Chrome,browser.version:1.0|T1693994400\n"
- )
- @override_options(
- {
- "delightful_metrics.enable_envelope_serialization": True,
- "delightful_metrics.enable_capture_envelope": True,
- }
- )
- @patch("minimetrics.transport.sentry_sdk")
- @django_db_all
- def test_send(sentry_sdk):
- flushed_metric = (
- 1693994400,
- {
- (
- "c",
- "button_click",
- "none",
- (
- ("browser", "Chrome"),
- ("browser.version", "1.0"),
- ),
- ): CounterMetric(first=1),
- },
- )
- transport = MetricEnvelopeTransport(RelayStatsdEncoder())
- metrics: Any = [flushed_metric]
- transport.send(metrics)
- args = sentry_sdk.Hub.current.client.transport.capture_envelope.call_args.args
- assert len(args) == 1
- arg = args[0]
- assert arg.items[0].type == "statsd"
- assert arg.items[0].data_category == "statsd"
|