mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-09-29 22:34:33 +00:00
281 lines
9.9 KiB
Python
281 lines
9.9 KiB
Python
import copy
|
|
import time
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from sentry.sentry_metrics import indexer
|
|
from sentry.sentry_metrics.use_case_id_registry import UseCaseID
|
|
from sentry.snuba.metrics import SingularEntityDerivedMetric
|
|
from sentry.snuba.metrics.fields.snql import complement, division_float
|
|
from sentry.snuba.metrics.naming_layer.mapping import get_mri, get_public_name_from_mri
|
|
from sentry.snuba.metrics.naming_layer.mri import SessionMRI
|
|
from sentry.snuba.metrics.naming_layer.public import SessionMetricKey
|
|
from sentry.testutils.cases import MetricsAPIBaseTestCase, OrganizationMetricsIntegrationTestCase
|
|
from sentry.testutils.silo import region_silo_test
|
|
from tests.sentry.api.endpoints.test_organization_metrics import (
|
|
MOCKED_DERIVED_METRICS,
|
|
mocked_mri_resolver,
|
|
)
|
|
|
|
MOCKED_DERIVED_METRICS_2 = copy.deepcopy(MOCKED_DERIVED_METRICS)
|
|
MOCKED_DERIVED_METRICS_2.update(
|
|
{
|
|
"derived_metric.multiple_metrics": SingularEntityDerivedMetric(
|
|
metric_mri="derived_metric.multiple_metrics",
|
|
metrics=["metric_foo_doe", SessionMRI.ALL.value],
|
|
unit="percentage",
|
|
snql=lambda *args, metric_ids, alias=None: complement(
|
|
division_float(*args), alias=SessionMetricKey.CRASH_FREE_RATE.value
|
|
),
|
|
)
|
|
}
|
|
)
|
|
|
|
pytestmark = pytest.mark.sentry_metrics
|
|
|
|
|
|
def _indexer_record(org_id: int, string: str) -> None:
|
|
indexer.record(use_case_id=UseCaseID.SESSIONS, org_id=org_id, string=string)
|
|
|
|
|
|
@region_silo_test
|
|
class OrganizationMetricDetailsTest(OrganizationMetricsIntegrationTestCase):
|
|
|
|
endpoint = "sentry-api-0-organization-metric-details"
|
|
|
|
@property
|
|
def now(self):
|
|
return MetricsAPIBaseTestCase.MOCK_DATETIME
|
|
|
|
@patch(
|
|
"sentry.snuba.metrics.datasource.get_mri",
|
|
mocked_mri_resolver(["metric1", "metric2", "metric3"], get_mri),
|
|
)
|
|
@patch(
|
|
"sentry.snuba.metrics.get_public_name_from_mri",
|
|
mocked_mri_resolver(["metric1", "metric2", "metric3"], get_public_name_from_mri),
|
|
)
|
|
def test_metric_details(self):
|
|
# metric1:
|
|
response = self.get_success_response(
|
|
self.organization.slug,
|
|
"metric1",
|
|
)
|
|
assert response.data == {
|
|
"name": "metric1",
|
|
"type": "counter",
|
|
"operations": ["max_timestamp", "min_timestamp", "sum"],
|
|
"unit": None,
|
|
"tags": [
|
|
{"key": "tag1"},
|
|
{"key": "tag2"},
|
|
{"key": "tag3"},
|
|
],
|
|
}
|
|
|
|
# metric2:
|
|
response = self.get_success_response(
|
|
self.organization.slug,
|
|
"metric2",
|
|
)
|
|
assert response.data == {
|
|
"name": "metric2",
|
|
"type": "set",
|
|
"operations": ["count_unique", "max_timestamp", "min_timestamp"],
|
|
"unit": None,
|
|
"tags": [
|
|
{"key": "tag1"},
|
|
{"key": "tag2"},
|
|
{"key": "tag4"},
|
|
],
|
|
}
|
|
|
|
# metric3:
|
|
response = self.get_success_response(
|
|
self.organization.slug,
|
|
"metric3",
|
|
)
|
|
assert response.data == {
|
|
"name": "metric3",
|
|
"type": "set",
|
|
"operations": ["count_unique", "max_timestamp", "min_timestamp"],
|
|
"unit": None,
|
|
"tags": [],
|
|
}
|
|
|
|
@patch("sentry.snuba.metrics.datasource.get_mri", mocked_mri_resolver(["foo.bar"], get_mri))
|
|
def test_metric_details_metric_does_not_exist_in_indexer(self):
|
|
response = self.get_response(
|
|
self.organization.slug,
|
|
"foo.bar",
|
|
)
|
|
assert response.status_code == 404
|
|
assert (
|
|
response.data["detail"]
|
|
== "Some or all of the metric names in ['foo.bar'] do not exist in the indexer"
|
|
)
|
|
|
|
@patch("sentry.snuba.metrics.datasource.get_mri", mocked_mri_resolver(["foo.bar"], get_mri))
|
|
@patch(
|
|
"sentry.snuba.metrics.get_public_name_from_mri",
|
|
mocked_mri_resolver(["foo.bar"], get_public_name_from_mri),
|
|
)
|
|
def test_metric_details_metric_does_not_have_data(self):
|
|
_indexer_record(self.organization.id, "foo.bar")
|
|
response = self.get_response(
|
|
self.organization.slug,
|
|
"foo.bar",
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
_indexer_record(self.organization.id, SessionMRI.RAW_SESSION.value)
|
|
response = self.get_response(
|
|
self.organization.slug,
|
|
SessionMetricKey.CRASH_FREE_RATE.value,
|
|
)
|
|
assert response.status_code == 404
|
|
assert (
|
|
response.data["detail"]
|
|
== f"The following metrics ['{SessionMRI.CRASH_FREE_RATE.value}'] "
|
|
f"do not exist in the dataset"
|
|
)
|
|
|
|
def test_derived_metric_details(self):
|
|
# 3rd Test: Test for derived metrics when indexer and dataset have data
|
|
self.store_session(
|
|
self.build_session(
|
|
project_id=self.project.id,
|
|
started=(time.time() // 60) * 60,
|
|
status="ok",
|
|
release="[email protected]",
|
|
)
|
|
)
|
|
response = self.get_success_response(
|
|
self.organization.slug,
|
|
SessionMetricKey.CRASH_FREE_RATE.value,
|
|
)
|
|
assert response.data == {
|
|
"name": SessionMetricKey.CRASH_FREE_RATE.value,
|
|
"type": "numeric",
|
|
"operations": [],
|
|
"unit": "percentage",
|
|
"tags": [{"key": "environment"}, {"key": "release"}],
|
|
}
|
|
|
|
@patch("sentry.snuba.metrics.fields.base.DERIVED_METRICS", MOCKED_DERIVED_METRICS_2)
|
|
@patch("sentry.snuba.metrics.datasource.get_mri")
|
|
@patch("sentry.snuba.metrics.datasource.get_derived_metrics")
|
|
def test_incorrectly_setup_derived_metric(self, mocked_derived_metrics, mocked_get_mri):
|
|
mocked_get_mri.return_value = "crash_free_fake"
|
|
mocked_derived_metrics.return_value = MOCKED_DERIVED_METRICS_2
|
|
self.store_session(
|
|
self.build_session(
|
|
project_id=self.project.id,
|
|
started=(time.time() // 60) * 60,
|
|
status="ok",
|
|
release="[email protected]",
|
|
errors=2,
|
|
)
|
|
)
|
|
response = self.get_response(
|
|
self.organization.slug,
|
|
"crash_free_fake",
|
|
)
|
|
assert response.status_code == 400
|
|
assert response.json()["detail"] == (
|
|
"The following metrics {'crash_free_fake'} cannot be computed from single entities. "
|
|
"Please revise the definition of these singular entity derived metrics"
|
|
)
|
|
|
|
@patch("sentry.snuba.metrics.fields.base.DERIVED_METRICS", MOCKED_DERIVED_METRICS_2)
|
|
@patch(
|
|
"sentry.snuba.metrics.datasource.get_mri",
|
|
mocked_mri_resolver(["metric_foo_doe", "derived_metric.multiple_metrics"], get_mri),
|
|
)
|
|
@patch(
|
|
"sentry.snuba.metrics.get_public_name_from_mri",
|
|
mocked_mri_resolver(
|
|
["metric_foo_doe", "derived_metric.multiple_metrics"], get_public_name_from_mri
|
|
),
|
|
)
|
|
@patch("sentry.snuba.metrics.datasource.get_derived_metrics")
|
|
def test_same_entity_multiple_metric_ids_missing_data(self, mocked_derived_metrics):
|
|
"""
|
|
Test when not requested metrics have data in the dataset
|
|
"""
|
|
mocked_derived_metrics.return_value = MOCKED_DERIVED_METRICS_2
|
|
_indexer_record(self.organization.id, "metric_foo_doe")
|
|
self.store_session(
|
|
self.build_session(
|
|
project_id=self.project.id,
|
|
started=(time.time() // 60) * 60,
|
|
status="ok",
|
|
release="[email protected]",
|
|
errors=2,
|
|
)
|
|
)
|
|
response = self.get_response(
|
|
self.organization.slug,
|
|
"derived_metric.multiple_metrics",
|
|
)
|
|
assert response.status_code == 404
|
|
assert response.json()["detail"] == (
|
|
"Not all the requested metrics or the constituent metrics in "
|
|
"['derived_metric.multiple_metrics'] have data in the dataset"
|
|
)
|
|
|
|
@patch("sentry.snuba.metrics.fields.base.DERIVED_METRICS", MOCKED_DERIVED_METRICS_2)
|
|
@patch(
|
|
"sentry.snuba.metrics.datasource.get_mri",
|
|
mocked_mri_resolver(["metric_foo_doe", "derived_metric.multiple_metrics"], get_mri),
|
|
)
|
|
@patch(
|
|
"sentry.snuba.metrics.get_public_name_from_mri",
|
|
mocked_mri_resolver(
|
|
["metric_foo_doe", "derived_metric.multiple_metrics"], get_public_name_from_mri
|
|
),
|
|
)
|
|
@patch("sentry.snuba.metrics.datasource.get_derived_metrics")
|
|
def test_same_entity_multiple_metric_ids(self, mocked_derived_metrics):
|
|
"""
|
|
Test that ensures that if a derived metric is defined with constituent metrics that
|
|
belong to the same entity but have different ids, then we are able to correctly return
|
|
its detail info
|
|
"""
|
|
mocked_derived_metrics.return_value = MOCKED_DERIVED_METRICS_2
|
|
org_id = self.project.organization.id
|
|
|
|
self.store_session(
|
|
self.build_session(
|
|
project_id=self.project.id,
|
|
started=(time.time() // 60) * 60,
|
|
status="ok",
|
|
release="[email protected]",
|
|
errors=2,
|
|
)
|
|
)
|
|
self.store_metric(
|
|
org_id=org_id,
|
|
project_id=self.project.id,
|
|
type="counter",
|
|
name="metric_foo_doe",
|
|
timestamp=int(time.time() // 60 - 2) * 60,
|
|
tags={
|
|
"release": "foow",
|
|
},
|
|
value=5,
|
|
use_case_id=UseCaseID.SESSIONS,
|
|
)
|
|
response = self.get_success_response(
|
|
self.organization.slug,
|
|
"derived_metric.multiple_metrics",
|
|
)
|
|
assert response.data == {
|
|
"name": "derived_metric.multiple_metrics",
|
|
"type": "numeric",
|
|
"operations": [],
|
|
"unit": "percentage",
|
|
"tags": [{"key": "release"}],
|
|
}
|