From 5a3eb674b580f5472bbe415262b832f3528b6f9a Mon Sep 17 00:00:00 2001 From: Armen Zambrano G <44410+armenzg@users.noreply.github.com> Date: Tue, 23 Apr 2024 10:39:34 -0400 Subject: [PATCH 1/5] feat(discover): Prevent calling Snuba with an empty list of projects --- src/sentry/search/events/builder/discover.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/sentry/search/events/builder/discover.py b/src/sentry/search/events/builder/discover.py index 9abcf9cfada42f..404800b8544c21 100644 --- a/src/sentry/search/events/builder/discover.py +++ b/src/sentry/search/events/builder/discover.py @@ -583,13 +583,16 @@ def resolve_params(self) -> list[WhereType]: if self.end: conditions.append(Condition(self.column("timestamp"), Op.LT, self.end)) - conditions.append( - Condition( - self.column("project_id"), - Op.IN, - self.params.project_ids, + # The if clause will prevent calling Snuba with an empty list of projects and complain with: + # sentry.utils.snuba.SnubaError: validation failed for entity events: missing required conditions for project_id + if self.params.project_ids: + conditions.append( + Condition( + self.column("project_id"), + Op.IN, + self.params.project_ids, + ) ) - ) if len(self.params.environments) > 0: term = event_search.SearchFilter( From e566d18078c726ecb97bde2401d53c5db73475e6 Mon Sep 17 00:00:00 2001 From: Armen Zambrano G <44410+armenzg@users.noreply.github.com> Date: Tue, 23 Apr 2024 11:49:44 -0400 Subject: [PATCH 2/5] Fix the tests --- src/sentry/utils/snuba.py | 2 ++ .../api/endpoints/test_organization_dashboard_widget_details.py | 2 ++ .../dynamic_sampling/tasks/test_custom_rule_notifications.py | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/sentry/utils/snuba.py b/src/sentry/utils/snuba.py index b8b5aa5f6e3d0b..f9eeee8cb25313 100644 --- a/src/sentry/utils/snuba.py +++ b/src/sentry/utils/snuba.py @@ -1033,6 +1033,8 @@ def _bulk_snuba_query( raise RateLimitExceeded(error["message"]) elif error["type"] == "schema": raise SchemaValidationError(error["message"]) + elif error["type"] == "invalid_query": + raise UnqualifiedQueryError(error["message"]) elif error["type"] == "clickhouse": raise clickhouse_error_codes_map.get(error["code"], QueryExecutionError)( error["message"] diff --git a/tests/sentry/api/endpoints/test_organization_dashboard_widget_details.py b/tests/sentry/api/endpoints/test_organization_dashboard_widget_details.py index 29d7f73971d086..94a8af5da8ec92 100644 --- a/tests/sentry/api/endpoints/test_organization_dashboard_widget_details.py +++ b/tests/sentry/api/endpoints/test_organization_dashboard_widget_details.py @@ -638,6 +638,8 @@ def test_save_with_invalid_orderby_not_from_columns_or_aggregates(self): assert "queries" in response.data, response.data def test_save_with_total_count(self): + # We cannot query the Discover entity without a project being defined for the org + self.create_project() data = { "title": "Test Query", "displayType": "table", diff --git a/tests/sentry/dynamic_sampling/tasks/test_custom_rule_notifications.py b/tests/sentry/dynamic_sampling/tasks/test_custom_rule_notifications.py index 63be9ab2e8b329..6079096425e404 100644 --- a/tests/sentry/dynamic_sampling/tasks/test_custom_rule_notifications.py +++ b/tests/sentry/dynamic_sampling/tasks/test_custom_rule_notifications.py @@ -49,6 +49,8 @@ def test_get_num_samples(self): """ Tests that the num_samples function returns the correct number of samples """ + # We cannot query the discover_transactions entity without a project being defined for the org + self.create_project() num_samples = get_num_samples(self.rule) assert num_samples == 0 self.create_transaction() From 61312d2723b0c8bf61610b9e047913bf677c4f86 Mon Sep 17 00:00:00 2001 From: Armen Zambrano G <44410+armenzg@users.noreply.github.com> Date: Tue, 23 Apr 2024 12:07:51 -0400 Subject: [PATCH 3/5] Add tests --- src/sentry/search/events/builder/discover.py | 5 ++-- .../search/events/builder/test_discover.py | 24 ++++++++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/sentry/search/events/builder/discover.py b/src/sentry/search/events/builder/discover.py index 404800b8544c21..8e8e497dce56cf 100644 --- a/src/sentry/search/events/builder/discover.py +++ b/src/sentry/search/events/builder/discover.py @@ -583,8 +583,9 @@ def resolve_params(self) -> list[WhereType]: if self.end: conditions.append(Condition(self.column("timestamp"), Op.LT, self.end)) - # The if clause will prevent calling Snuba with an empty list of projects and complain with: - # sentry.utils.snuba.SnubaError: validation failed for entity events: missing required conditions for project_id + # The clause will prevent calling Snuba with an empty list of projects, thus, returning + # no data. It will not instead complain with: + # sentry.utils.snuba.UnqualifiedQueryError: validation failed for entity events: missing required conditions for project_id if self.params.project_ids: conditions.append( Condition( diff --git a/tests/sentry/search/events/builder/test_discover.py b/tests/sentry/search/events/builder/test_discover.py index 9917f0cb49309c..8015d8485f81fa 100644 --- a/tests/sentry/search/events/builder/test_discover.py +++ b/tests/sentry/search/events/builder/test_discover.py @@ -16,8 +16,9 @@ from sentry.search.events.builder import QueryBuilder from sentry.search.events.types import ParamsType, QueryBuilderConfig from sentry.snuba.dataset import Dataset +from sentry.snuba.referrer import Referrer from sentry.testutils.cases import TestCase -from sentry.utils.snuba import QueryOutsideRetentionError +from sentry.utils.snuba import QueryOutsideRetentionError, UnqualifiedQueryError, bulk_snuba_queries from sentry.utils.validators import INVALID_ID_DETAILS pytestmark = pytest.mark.sentry_metrics @@ -67,6 +68,27 @@ def test_simple_query(self): ) query.get_snql_query().validate() + def test_query_without_project_ids(self): + params = { + "start": self.params["start"], + "end": self.params["end"], + "organization_id": self.organization.id, + } + with pytest.raises(UnqualifiedQueryError): + query = QueryBuilder(Dataset.Discover, params, query="foo", selected_columns=["id"]) + bulk_snuba_queries([query.get_snql_query()], referrer=Referrer.TESTING_TEST.value) + + def test_query_with_empty_project_ids(self): + params = { + "start": self.params["start"], + "end": self.params["end"], + "project_id": [], # We add an empty project_id list + "organization_id": self.organization.id, + } + with pytest.raises(UnqualifiedQueryError): + query = QueryBuilder(Dataset.Discover, params, query="foo", selected_columns=["id"]) + bulk_snuba_queries([query.get_snql_query()], referrer=Referrer.TESTING_TEST.value) + def test_simple_orderby(self): query = QueryBuilder( Dataset.Discover, From 7c7d49a154e47a5f6ca79a5000ffe8da03596e76 Mon Sep 17 00:00:00 2001 From: Armen Zambrano G <44410+armenzg@users.noreply.github.com> Date: Tue, 23 Apr 2024 12:08:22 -0400 Subject: [PATCH 4/5] Fix typing --- tests/sentry/search/events/builder/test_discover.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/sentry/search/events/builder/test_discover.py b/tests/sentry/search/events/builder/test_discover.py index 8015d8485f81fa..ea78bd41389cb4 100644 --- a/tests/sentry/search/events/builder/test_discover.py +++ b/tests/sentry/search/events/builder/test_discover.py @@ -69,7 +69,7 @@ def test_simple_query(self): query.get_snql_query().validate() def test_query_without_project_ids(self): - params = { + params: ParamsType = { "start": self.params["start"], "end": self.params["end"], "organization_id": self.organization.id, @@ -79,7 +79,7 @@ def test_query_without_project_ids(self): bulk_snuba_queries([query.get_snql_query()], referrer=Referrer.TESTING_TEST.value) def test_query_with_empty_project_ids(self): - params = { + params: ParamsType = { "start": self.params["start"], "end": self.params["end"], "project_id": [], # We add an empty project_id list From f34a3c3e3810e02d47fcd656b0bfb36729063cd9 Mon Sep 17 00:00:00 2001 From: Armen Zambrano G <44410+armenzg@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:14:43 -0400 Subject: [PATCH 5/5] Raise an error rather than relying on Snuba telling us --- src/sentry/search/events/builder/discover.py | 21 ++-- src/sentry/snuba/metrics/query_builder.py | 38 +++--- .../search/events/builder/test_discover.py | 4 +- .../snuba/metrics/test_query_builder.py | 109 +++++++++--------- 4 files changed, 92 insertions(+), 80 deletions(-) diff --git a/src/sentry/search/events/builder/discover.py b/src/sentry/search/events/builder/discover.py index 8e8e497dce56cf..59b554fc82c2d3 100644 --- a/src/sentry/search/events/builder/discover.py +++ b/src/sentry/search/events/builder/discover.py @@ -74,6 +74,7 @@ from sentry.utils.dates import outside_retention_with_modified_start from sentry.utils.snuba import ( QueryOutsideRetentionError, + UnqualifiedQueryError, is_duration_measurement, is_measurement, is_numeric_measurement, @@ -583,17 +584,17 @@ def resolve_params(self) -> list[WhereType]: if self.end: conditions.append(Condition(self.column("timestamp"), Op.LT, self.end)) - # The clause will prevent calling Snuba with an empty list of projects, thus, returning - # no data. It will not instead complain with: - # sentry.utils.snuba.UnqualifiedQueryError: validation failed for entity events: missing required conditions for project_id - if self.params.project_ids: - conditions.append( - Condition( - self.column("project_id"), - Op.IN, - self.params.project_ids, - ) + # This will prevent calling Snuba with an empty list of projects, thus, returning no data. + if not self.params.project_ids: + raise UnqualifiedQueryError("You need to specify at least one project.") + + conditions.append( + Condition( + self.column("project_id"), + Op.IN, + self.params.project_ids, ) + ) if len(self.params.environments) > 0: term = event_search.SearchFilter( diff --git a/src/sentry/snuba/metrics/query_builder.py b/src/sentry/snuba/metrics/query_builder.py index 463035b1d8461a..8637ccd71b86f0 100644 --- a/src/sentry/snuba/metrics/query_builder.py +++ b/src/sentry/snuba/metrics/query_builder.py @@ -716,9 +716,11 @@ def translate_meta_results( results.append( { "name": parent_alias, - "type": record["type"] - if defined_parent_meta_type is None - else defined_parent_meta_type, + "type": ( + record["type"] + if defined_parent_meta_type is None + else defined_parent_meta_type + ), } ) continue @@ -911,9 +913,11 @@ def _build_where(self) -> list[BooleanCondition | Condition]: alias=condition.lhs.alias, )[0], op=condition.op, - rhs=resolve_tag_value(self._use_case_id, self._org_id, condition.rhs) - if require_rhs_condition_resolution(condition.lhs.op) - else condition.rhs, + rhs=( + resolve_tag_value(self._use_case_id, self._org_id, condition.rhs) + if require_rhs_condition_resolution(condition.lhs.op) + else condition.rhs + ), ) ) except IndexError: @@ -1339,9 +1343,11 @@ def translate_result_groups(self): # to determine whether we need to reverse the tag value or not. groupby_alias_to_groupby_column = ( { - metric_groupby_obj.alias: metric_groupby_obj.field - if isinstance(metric_groupby_obj.field, str) - else metric_groupby_obj.field.op + metric_groupby_obj.alias: ( + metric_groupby_obj.field + if isinstance(metric_groupby_obj.field, str) + else metric_groupby_obj.field.op + ) for metric_groupby_obj in self._metrics_query.groupby } if self._metrics_query.groupby @@ -1352,13 +1358,15 @@ def translate_result_groups(self): dict( by=dict( ( - key, - reverse_resolve_tag_value( - self._use_case_id, self._organization_id, value, weak=True - ), + ( + key, + reverse_resolve_tag_value( + self._use_case_id, self._organization_id, value, weak=True + ), + ) + if groupby_alias_to_groupby_column.get(key) not in NON_RESOLVABLE_TAG_VALUES + else (key, value) ) - if groupby_alias_to_groupby_column.get(key) not in NON_RESOLVABLE_TAG_VALUES - else (key, value) for key, value in tags ), **data, diff --git a/tests/sentry/search/events/builder/test_discover.py b/tests/sentry/search/events/builder/test_discover.py index ea78bd41389cb4..efd2ffd3ad7732 100644 --- a/tests/sentry/search/events/builder/test_discover.py +++ b/tests/sentry/search/events/builder/test_discover.py @@ -68,7 +68,7 @@ def test_simple_query(self): ) query.get_snql_query().validate() - def test_query_without_project_ids(self): + def test_project_ids_missing(self): params: ParamsType = { "start": self.params["start"], "end": self.params["end"], @@ -78,7 +78,7 @@ def test_query_without_project_ids(self): query = QueryBuilder(Dataset.Discover, params, query="foo", selected_columns=["id"]) bulk_snuba_queries([query.get_snql_query()], referrer=Referrer.TESTING_TEST.value) - def test_query_with_empty_project_ids(self): + def test_project_ids_with_empty_list(self): params: ParamsType = { "start": self.params["start"], "end": self.params["end"], diff --git a/tests/sentry/snuba/metrics/test_query_builder.py b/tests/sentry/snuba/metrics/test_query_builder.py index 574a19cf0bdff0..bdce30b8c96350 100644 --- a/tests/sentry/snuba/metrics/test_query_builder.py +++ b/tests/sentry/snuba/metrics/test_query_builder.py @@ -5,6 +5,7 @@ from dataclasses import replace from datetime import datetime, timedelta, timezone from typing import Any +from unittest import TestCase as UnitTestCase from unittest import mock import pytest @@ -27,6 +28,8 @@ ) from sentry.exceptions import InvalidParams +from sentry.models.organization import Organization +from sentry.models.project import Project from sentry.sentry_metrics import indexer from sentry.sentry_metrics.use_case_id_registry import UseCaseID from sentry.sentry_metrics.utils import ( @@ -81,6 +84,13 @@ def PseudoProject(organization_id: int, id: int) -> Any: # TODO: use real proje return types.SimpleNamespace(organization_id=organization_id, id=id, slug="project-slug") +@pytest.fixture +def project(): + org = Organization.objects.create(id=1, slug="org-1") + project = Project.objects.create(id=1, slug="project-1", organization_id=org.id) + yield project + + MOCK_NOW = datetime(2021, 8, 25, 23, 59, tzinfo=timezone.utc) # the beginning of the 1h interval before MOCK_NOW BEG_1H_BEFORE_NOW = datetime(2021, 8, 25, 23, tzinfo=timezone.utc) @@ -247,7 +257,7 @@ def get_entity_of_metric_mocked(_, metric_name, use_case_id): ), ], ) -def test_parse_conditions(query_string, expected): +def test_parse_conditions(query_string, expected, project): org_id = ORG_ID use_case_id = UseCaseID.SESSIONS for s in ("myapp@2.0.0", "/bar/:orgId/"): @@ -256,7 +266,7 @@ def test_parse_conditions(query_string, expected): parsed = resolve_tags( use_case_id, org_id, - parse_conditions(query_string, [], []), + parse_conditions(query_string, [project], []), [], ) assert parsed == expected() @@ -347,9 +357,10 @@ def test_timestamps(): assert interval == 12 * 60 * 60 +@django_db_all @mock.patch("sentry.snuba.sessions_v2.get_now", return_value=MOCK_NOW) @mock.patch("sentry.api.utils.timezone.now", return_value=MOCK_NOW) -def test_build_snuba_query(mock_now, mock_now2): +def test_build_snuba_query(mock_now, mock_now2, project): # Your typical release health query querying everything having = [Condition(Column("sum"), Op.GT, 1000)] query_definition = MetricsQuery( @@ -368,7 +379,7 @@ def test_build_snuba_query(mock_now, mock_now2): having=having, ) snuba_queries, _ = SnubaQueryBuilder( - [PseudoProject(1, 1)], query_definition, use_case_id=UseCaseID.SESSIONS + [project], query_definition, use_case_id=UseCaseID.SESSIONS ).get_snuba_queries() org_id = 1 @@ -482,7 +493,7 @@ def expected_query(match, select, extra_groupby, metric_name): @mock.patch( "sentry.snuba.metrics.fields.base._get_entity_of_metric_mri", get_entity_of_metric_mocked ) -def test_build_snuba_query_mri(mock_now, mock_now2): +def test_build_snuba_query_mri(mock_now, mock_now2, project): org_id = 1 use_case_id = UseCaseID.SESSIONS # Your typical release health query querying everything @@ -501,10 +512,8 @@ def test_build_snuba_query_mri(mock_now, mock_now2): TOTALS_LIMIT = MAX_POINTS // NUM_INTERVALS SERIES_LIMIT = TOTALS_LIMIT * NUM_INTERVALS - query_definition = QueryDefinition([PseudoProject(1, 1)], query_params, allow_mri=True) - query_builder = SnubaQueryBuilder( - [PseudoProject(1, 1)], query_definition.to_metrics_query(), use_case_id - ) + query_definition = QueryDefinition([project], query_params, allow_mri=True) + query_builder = SnubaQueryBuilder([project], query_definition.to_metrics_query(), use_case_id) snuba_queries, fields_in_entities = query_builder.get_snuba_queries() assert fields_in_entities == { @@ -565,7 +574,7 @@ def test_build_snuba_query_mri(mock_now, mock_now2): @mock.patch( "sentry.snuba.metrics.fields.base._get_entity_of_metric_mri", get_entity_of_metric_mocked ) -def test_build_snuba_query_derived_metrics(mock_now, mock_now2): +def test_build_snuba_query_derived_metrics(mock_now, mock_now2, project): org_id = 1 use_case_id = UseCaseID.SESSIONS # Your typical release health query querying everything @@ -586,10 +595,8 @@ def test_build_snuba_query_derived_metrics(mock_now, mock_now2): TOTALS_LIMIT = MAX_POINTS // NUM_INTERVALS SERIES_LIMIT = TOTALS_LIMIT * NUM_INTERVALS - query_definition = QueryDefinition([PseudoProject(1, 1)], query_params) - query_builder = SnubaQueryBuilder( - [PseudoProject(1, 1)], query_definition.to_metrics_query(), use_case_id - ) + query_definition = QueryDefinition([project], query_params) + query_builder = SnubaQueryBuilder([project], query_definition.to_metrics_query(), use_case_id) snuba_queries, fields_in_entities = query_builder.get_snuba_queries() assert fields_in_entities == { "metrics_counters": [ @@ -730,7 +737,7 @@ def test_build_snuba_query_derived_metrics(mock_now, mock_now2): @django_db_all @mock.patch("sentry.snuba.sessions_v2.get_now", return_value=MOCK_NOW) @mock.patch("sentry.api.utils.timezone.now", return_value=MOCK_NOW) -def test_build_snuba_query_orderby(mock_now, mock_now2): +def test_build_snuba_query_orderby(mock_now, mock_now2, project): query_params = MultiValueDict( { "query": [ @@ -744,11 +751,9 @@ def test_build_snuba_query_orderby(mock_now, mock_now2): "per_page": ["2"], } ) - query_definition = QueryDefinition( - [PseudoProject(1, 1)], query_params, paginator_kwargs={"limit": 3} - ) + query_definition = QueryDefinition([project], query_params, paginator_kwargs={"limit": 3}) snuba_queries, _ = SnubaQueryBuilder( - [PseudoProject(1, 1)], query_definition.to_metrics_query(), UseCaseID.SESSIONS + [project], query_definition.to_metrics_query(), UseCaseID.SESSIONS ).get_snuba_queries() org_id = 1 @@ -833,7 +838,7 @@ def test_build_snuba_query_orderby(mock_now, mock_now2): @django_db_all @mock.patch("sentry.snuba.sessions_v2.get_now", return_value=MOCK_NOW) @mock.patch("sentry.api.utils.timezone.now", return_value=MOCK_NOW) -def test_build_snuba_query_with_derived_alias(mock_now, mock_now2): +def test_build_snuba_query_with_derived_alias(mock_now, mock_now2, project): query_params = MultiValueDict( { "query": ["release:staging"], @@ -844,11 +849,9 @@ def test_build_snuba_query_with_derived_alias(mock_now, mock_now2): "per_page": ["2"], } ) - query_definition = QueryDefinition( - [PseudoProject(1, 1)], query_params, paginator_kwargs={"limit": 3} - ) + query_definition = QueryDefinition([project], query_params, paginator_kwargs={"limit": 3}) snuba_queries, _ = SnubaQueryBuilder( - [PseudoProject(1, 1)], + [project], query_definition.to_metrics_query(), UseCaseID.SESSIONS, ).get_snuba_queries() @@ -953,7 +956,7 @@ def test_build_snuba_query_with_derived_alias(mock_now, mock_now2): @django_db_all @mock.patch("sentry.snuba.sessions_v2.get_now", return_value=MOCK_NOW) @mock.patch("sentry.api.utils.timezone.now", return_value=MOCK_NOW) -def test_translate_results_derived_metrics(_1, _2): +def test_translate_results_derived_metrics(_1, _2, project): query_params: MultiValueDict[str, str] = MultiValueDict( { "groupBy": [], @@ -966,7 +969,7 @@ def test_translate_results_derived_metrics(_1, _2): "statsPeriod": ["2d"], } ) - query_definition = QueryDefinition([PseudoProject(1, 1)], query_params) + query_definition = QueryDefinition([project], query_params) fields_in_entities = { "metrics_counters": [ ( @@ -1075,8 +1078,8 @@ def test_translate_results_derived_metrics(_1, _2): @django_db_all @mock.patch("sentry.snuba.sessions_v2.get_now", return_value=MOCK_NOW) @mock.patch("sentry.api.utils.timezone.now", return_value=MOCK_NOW) -def test_translate_results_missing_slots(_1, _2): - org_id = 1 +def test_translate_results_missing_slots(_1, _2, project): + org = project.organization use_case_id = UseCaseID.SESSIONS query_params = MultiValueDict( { @@ -1087,7 +1090,7 @@ def test_translate_results_missing_slots(_1, _2): "statsPeriod": ["3d"], } ) - query_definition = QueryDefinition([PseudoProject(1, 1)], query_params) + query_definition = QueryDefinition([project], query_params) fields_in_entities = { "metrics_counters": [ ("sum", SessionMRI.RAW_SESSION.value, "sum(sentry.sessions.session)"), @@ -1099,7 +1102,7 @@ def test_translate_results_missing_slots(_1, _2): "totals": { "data": [ { - "metric_id": resolve(use_case_id, org_id, SessionMRI.RAW_SESSION.value), + "metric_id": resolve(use_case_id, org.id, SessionMRI.RAW_SESSION.value), "sum(sentry.sessions.session)": 400, }, ], @@ -1107,13 +1110,13 @@ def test_translate_results_missing_slots(_1, _2): "series": { "data": [ { - "metric_id": resolve(use_case_id, org_id, SessionMRI.RAW_SESSION.value), + "metric_id": resolve(use_case_id, org.id, SessionMRI.RAW_SESSION.value), "bucketed_time": "2021-08-23T00:00Z", "sum(sentry.sessions.session)": 100, }, # no data for 2021-08-24 { - "metric_id": resolve(use_case_id, org_id, SessionMRI.RAW_SESSION.value), + "metric_id": resolve(use_case_id, org.id, SessionMRI.RAW_SESSION.value), "bucketed_time": "2021-08-25T00:00Z", "sum(sentry.sessions.session)": 300, }, @@ -1126,7 +1129,7 @@ def test_translate_results_missing_slots(_1, _2): get_intervals(query_definition.start, query_definition.end, query_definition.rollup) ) assert SnubaResultConverter( - org_id, + org.id, query_definition.to_metrics_query(), fields_in_entities, intervals, @@ -1303,6 +1306,7 @@ def test_translate_meta_result_type_composite_entity_derived_metric(_): ) +@django_db_all @pytest.mark.parametrize( "select,groupby,usecase,error_string", [ @@ -1376,11 +1380,11 @@ def test_translate_meta_result_type_composite_entity_derived_metric(_): ], ) def test_only_can_groupby_operations_can_be_added_to_groupby( - select, groupby, usecase, error_string + select, groupby, usecase, error_string, project ): query_definition = MetricsQuery( org_id=1, - project_ids=[1], + project_ids=[project.id], select=select, start=MOCK_NOW - timedelta(days=90), end=MOCK_NOW, @@ -1389,15 +1393,12 @@ def test_only_can_groupby_operations_can_be_added_to_groupby( ) if error_string: with pytest.raises(InvalidParams, match=error_string): - snuba_queries, _ = SnubaQueryBuilder( - [PseudoProject(1, 1)], query_definition, use_case_id=usecase - ).get_snuba_queries() + SnubaQueryBuilder([project], query_definition, use_case_id=usecase).get_snuba_queries() else: - snuba_queries, _ = SnubaQueryBuilder( - [PseudoProject(1, 1)], query_definition, use_case_id=usecase - ).get_snuba_queries() + SnubaQueryBuilder([project], query_definition, use_case_id=usecase).get_snuba_queries() +@django_db_all @pytest.mark.parametrize( "select,where,usecase,error_string", [ @@ -1474,7 +1475,9 @@ def test_only_can_groupby_operations_can_be_added_to_groupby( ), ], ) -def test_only_can_filter_operations_can_be_added_to_where(select, where, usecase, error_string): +def test_only_can_filter_operations_can_be_added_to_where( + select, where, usecase, error_string, project +): query_definition = MetricsQuery( org_id=1, project_ids=[1], @@ -1487,11 +1490,11 @@ def test_only_can_filter_operations_can_be_added_to_where(select, where, usecase if error_string: with pytest.raises(InvalidParams, match=error_string): snuba_queries, _ = SnubaQueryBuilder( - [PseudoProject(1, 1)], query_definition, use_case_id=usecase + [project], query_definition, use_case_id=usecase ).get_snuba_queries() else: snuba_queries, _ = SnubaQueryBuilder( - [PseudoProject(1, 1)], query_definition, use_case_id=usecase + [project], query_definition, use_case_id=usecase ).get_snuba_queries() @@ -1563,7 +1566,7 @@ def test_multiple_environments_are_passed_through_to_metrics_query(self): ] -class ResolveTagsTestCase(TestCase): +class ResolveTagsTestCase(UnitTestCase): def setUp(self): self.org_id = ORG_ID self.use_case_id = UseCaseID.TRANSACTIONS @@ -1950,6 +1953,7 @@ def test_resolve_tags_invalid_project_slugs(self, projects): ) +@django_db_all @pytest.mark.parametrize( "op, clickhouse_op", [ @@ -1957,14 +1961,14 @@ def test_resolve_tags_invalid_project_slugs(self, projects): ("max_timestamp", "maxIf"), ], ) -def test_timestamp_operators(op: MetricOperationType, clickhouse_op: str): +def test_timestamp_operators(op: MetricOperationType, clickhouse_op: str, project): """ Tests code generation for timestamp operators """ org_id = 1 query_definition = MetricsQuery( org_id=org_id, - project_ids=[1], + project_ids=[project.id], select=[ MetricField(op=op, metric_mri=SessionMRI.RAW_SESSION.value, alias="ts"), ], @@ -1973,11 +1977,9 @@ def test_timestamp_operators(op: MetricOperationType, clickhouse_op: str): granularity=Granularity(3600), ) - builder = SnubaQueryBuilder( - [PseudoProject(1, 1)], query_definition, use_case_id=UseCaseID.SESSIONS - ) + builder = SnubaQueryBuilder([project], query_definition, use_case_id=UseCaseID.SESSIONS) - snuba_queries, fields = builder.get_snuba_queries() + snuba_queries, _ = builder.get_snuba_queries() select = snuba_queries["metrics_counters"]["totals"].select assert len(select) == 1 field = select[0] @@ -2000,6 +2002,7 @@ def test_timestamp_operators(op: MetricOperationType, clickhouse_op: str): assert field == expected_field +@django_db_all @pytest.mark.parametrize( "include_totals, include_series", [ @@ -2008,7 +2011,7 @@ def test_timestamp_operators(op: MetricOperationType, clickhouse_op: str): [False, True], ], ) -def test_having_clause(include_totals, include_series): +def test_having_clause(include_totals, include_series, project): """ Tests that the having clause ends up in the snql queries in the expected form """ @@ -2029,7 +2032,7 @@ def test_having_clause(include_totals, include_series): include_series=include_series, ) snuba_queries, _ = SnubaQueryBuilder( - [PseudoProject(1, 1)], query_definition, use_case_id=UseCaseID.SESSIONS + [project], query_definition, use_case_id=UseCaseID.SESSIONS ).get_snuba_queries() queries = snuba_queries["metrics_counters"]