Browse Source

ref: fix typing in tests.snuba / tests.sentry.snuba (#57367)

<!-- Describe your PR here. -->
anthony sottile 1 year ago
parent
commit
f9c7b1ff63

+ 0 - 5
pyproject.toml

@@ -838,14 +838,9 @@ module = [
     "tests.sentry.sentry_metrics.limiters.test_writes_limiter",
     "tests.sentry.sentry_metrics.test_base_indexer",
     "tests.sentry.sentry_metrics.test_batch",
-    "tests.sentry.snuba.metrics.test_snql",
-    "tests.sentry.snuba.test_tasks",
     "tests.sentry.tagstore.test_types",
     "tests.sentry.tasks.test_post_process",
     "tests.sentry.web.test_client_config",
-    "tests.snuba.rules.conditions.test_event_frequency",
-    "tests.snuba.sessions.test_sessions",
-    "tests.snuba.tagstore.test_tagstore_backend",
 ]
 disable_error_code = [
     "arg-type",

+ 2 - 1
src/sentry/release_health/base.py

@@ -6,6 +6,7 @@ from enum import Enum
 from typing import (
     TYPE_CHECKING,
     Any,
+    Collection,
     Literal,
     Mapping,
     Optional,
@@ -360,7 +361,7 @@ class ReleaseHealthBackend(Service):
 
     def check_has_health_data(
         self,
-        projects_list: Sequence[ProjectOrRelease],
+        projects_list: Collection[ProjectOrRelease],
         now: Optional[datetime] = None,
     ) -> Set[ProjectOrRelease]:
         """

+ 2 - 1
src/sentry/release_health/metrics.py

@@ -4,6 +4,7 @@ from datetime import datetime, timedelta, timezone
 from typing import (
     Any,
     Callable,
+    Collection,
     Dict,
     List,
     Literal,
@@ -562,7 +563,7 @@ class MetricsReleaseHealthBackend(ReleaseHealthBackend):
 
     def check_has_health_data(
         self,
-        projects_list: Sequence[ProjectOrRelease],
+        projects_list: Collection[ProjectOrRelease],
         now: Optional[datetime] = None,
     ) -> Set[ProjectOrRelease]:
         if now is None:

+ 2 - 2
src/sentry/release_health/sessions.py

@@ -1,6 +1,6 @@
 from copy import deepcopy
 from datetime import datetime
-from typing import Mapping, Optional, Sequence, Set, Tuple, Union
+from typing import Collection, Mapping, Optional, Sequence, Set, Tuple, Union
 
 import sentry_sdk
 
@@ -130,7 +130,7 @@ class SessionsReleaseHealthBackend(ReleaseHealthBackend):
 
     def check_has_health_data(
         self,
-        projects_list: Sequence[ProjectOrRelease],
+        projects_list: Collection[ProjectOrRelease],
         now: Optional[datetime] = None,
     ) -> Set[ProjectOrRelease]:
         return _check_has_health_data(projects_list, now=now)

+ 8 - 6
tests/sentry/snuba/metrics/test_snql.py

@@ -50,17 +50,19 @@ pytestmark = pytest.mark.sentry_metrics
 class DerivedMetricSnQLTestCase(TestCase):
     def setUp(self):
         self.org_id = 666
-        self.metric_ids = []
+        self.metric_ids = set()
         for metric_name in [
             TransactionMRI.MEASUREMENTS_LCP.value,
             TransactionMRI.DURATION.value,
         ]:
-            self.metric_ids += [indexer.record(UseCaseID.TRANSACTIONS, self.org_id, metric_name)]
+            metric_id = indexer.record(UseCaseID.TRANSACTIONS, self.org_id, metric_name)
+            assert metric_id is not None
+            self.metric_ids.add(metric_id)
 
         indexer.bulk_record(
             {
                 UseCaseID.SESSIONS: {
-                    self.org_id: [
+                    self.org_id: {
                         "abnormal",
                         "crashed",
                         "errored_preaggr",
@@ -68,14 +70,14 @@ class DerivedMetricSnQLTestCase(TestCase):
                         "exited",
                         "init",
                         "session.status",
-                    ]
+                    }
                 }
             }
         )
         indexer.bulk_record(
             {
                 UseCaseID.TRANSACTIONS: {
-                    self.org_id: [
+                    self.org_id: {
                         TransactionSatisfactionTagValue.FRUSTRATED.value,
                         TransactionSatisfactionTagValue.SATISFIED.value,
                         TransactionSatisfactionTagValue.TOLERATED.value,
@@ -84,7 +86,7 @@ class DerivedMetricSnQLTestCase(TestCase):
                         TransactionStatusTagValue.UNKNOWN.value,
                         TransactionTagsKey.TRANSACTION_SATISFACTION.value,
                         TransactionTagsKey.TRANSACTION_STATUS.value,
-                    ]
+                    }
                 }
             }
         )

+ 7 - 4
tests/sentry/snuba/test_tasks.py

@@ -34,6 +34,7 @@ from sentry.snuba.tasks import (
     subscription_checker,
     update_subscription_in_snuba,
 )
+from sentry.testutils.abstract import Abstract
 from sentry.testutils.cases import TestCase
 from sentry.testutils.helpers import Feature
 from sentry.testutils.skips import requires_snuba
@@ -57,7 +58,9 @@ perf_indexer_record = partial(indexer_record, UseCaseID.TRANSACTIONS)
 rh_indexer_record = partial(indexer_record, UseCaseID.SESSIONS)
 
 
-class BaseSnubaTaskTest(metaclass=abc.ABCMeta):
+class BaseSnubaTaskTest(TestCase, metaclass=abc.ABCMeta):
+    __test__ = Abstract(__module__, __qualname__)  # type: ignore[name-defined]  # python/mypy#10570
+
     status_translations = {
         QuerySubscription.Status.CREATING: "create",
         QuerySubscription.Status.UPDATING: "update",
@@ -132,7 +135,7 @@ class BaseSnubaTaskTest(metaclass=abc.ABCMeta):
         )
 
 
-class CreateSubscriptionInSnubaTest(BaseSnubaTaskTest, TestCase):
+class CreateSubscriptionInSnubaTest(BaseSnubaTaskTest):
     expected_status = QuerySubscription.Status.CREATING
     task = create_subscription_in_snuba
 
@@ -215,7 +218,7 @@ class CreateSubscriptionInSnubaTest(BaseSnubaTaskTest, TestCase):
                     assert request_body["granularity"] == expected_granularity
 
 
-class UpdateSubscriptionInSnubaTest(BaseSnubaTaskTest, TestCase):
+class UpdateSubscriptionInSnubaTest(BaseSnubaTaskTest):
     expected_status = QuerySubscription.Status.UPDATING
     task = update_subscription_in_snuba
 
@@ -239,7 +242,7 @@ class UpdateSubscriptionInSnubaTest(BaseSnubaTaskTest, TestCase):
         assert sub.subscription_id is not None
 
 
-class DeleteSubscriptionFromSnubaTest(BaseSnubaTaskTest, TestCase):
+class DeleteSubscriptionFromSnubaTest(BaseSnubaTaskTest):
     expected_status = QuerySubscription.Status.DELETING
     task = delete_subscription_from_snuba
 

+ 63 - 60
tests/snuba/rules/conditions/test_event_frequency.py

@@ -14,6 +14,7 @@ from sentry.rules.conditions.event_frequency import (
     EventFrequencyPercentCondition,
     EventUniqueUserFrequencyCondition,
 )
+from sentry.testutils.abstract import Abstract
 from sentry.testutils.cases import PerformanceIssueTestCase, RuleTestCase, SnubaTestCase
 from sentry.testutils.helpers.datetime import before_now, freeze_time, iso_format
 from sentry.testutils.silo import region_silo_test
@@ -23,48 +24,7 @@ from sentry.utils.samples import load_data
 pytestmark = [requires_snuba]
 
 
-@pytest.mark.snuba_ci
-class FrequencyConditionMixin:
-    def increment(self, event, count, environment=None, timestamp=None):
-        raise NotImplementedError
-
-    def _run_test(self, minutes, data, passes, add_events=False):
-        if not self.environment:
-            self.environment = self.create_environment(name="prod")
-
-        rule = self.get_rule(data=data, rule=Rule(environment_id=None))
-        environment_rule = self.get_rule(data=data, rule=Rule(environment_id=self.environment.id))
-
-        event = self.add_event(
-            data={
-                "fingerprint": ["something_random"],
-                "user": {"id": uuid4().hex},
-            },
-            project_id=self.project.id,
-            timestamp=before_now(minutes=minutes),
-        )
-        if add_events:
-            self.increment(
-                event,
-                data["value"] + 1,
-                environment=self.environment.name,
-                timestamp=now() - timedelta(minutes=minutes),
-            )
-            self.increment(
-                event,
-                data["value"] + 1,
-                timestamp=now() - timedelta(minutes=minutes),
-            )
-
-        if passes:
-            self.assertPasses(rule, event)
-            self.assertPasses(environment_rule, event)
-        else:
-            self.assertDoesNotPass(rule, event)
-            self.assertDoesNotPass(environment_rule, event)
-
-
-class ErrorEventMixin:
+class ErrorEventMixin(SnubaTestCase):
     def add_event(self, data, project_id, timestamp):
         data["timestamp"] = iso_format(timestamp)
         # Store an error event
@@ -107,7 +67,51 @@ class PerfIssuePlatformEventMixin(PerformanceIssueTestCase):
         return event
 
 
-class StandardIntervalMixin:
+@pytest.mark.snuba_ci
+class StandardIntervalTestBase(SnubaTestCase, RuleTestCase):
+    __test__ = Abstract(__module__, __qualname__)  # type: ignore[name-defined]  # python/mypy#10570
+
+    def add_event(self, data, project_id, timestamp):
+        raise NotImplementedError
+
+    def increment(self, event, count, environment=None, timestamp=None):
+        raise NotImplementedError
+
+    def _run_test(self, minutes, data, passes, add_events=False):
+        if not self.environment:
+            self.environment = self.create_environment(name="prod")
+
+        rule = self.get_rule(data=data, rule=Rule(environment_id=None))
+        environment_rule = self.get_rule(data=data, rule=Rule(environment_id=self.environment.id))
+
+        event = self.add_event(
+            data={
+                "fingerprint": ["something_random"],
+                "user": {"id": uuid4().hex},
+            },
+            project_id=self.project.id,
+            timestamp=before_now(minutes=minutes),
+        )
+        if add_events:
+            self.increment(
+                event,
+                data["value"] + 1,
+                environment=self.environment.name,
+                timestamp=now() - timedelta(minutes=minutes),
+            )
+            self.increment(
+                event,
+                data["value"] + 1,
+                timestamp=now() - timedelta(minutes=minutes),
+            )
+
+        if passes:
+            self.assertPasses(rule, event)
+            self.assertPasses(environment_rule, event)
+        else:
+            self.assertDoesNotPass(rule, event)
+            self.assertDoesNotPass(environment_rule, event)
+
     def test_one_minute_with_events(self):
         data = {"interval": "1m", "value": 6}
         self._run_test(data=data, minutes=1, passes=True, add_events=True)
@@ -217,9 +221,9 @@ class StandardIntervalMixin:
         self.assertDoesNotPass(rule, event)
 
 
-class EventFrequencyConditionTestCase(
-    FrequencyConditionMixin, StandardIntervalMixin, SnubaTestCase
-):
+class EventFrequencyConditionTestCase(StandardIntervalTestBase):
+    __test__ = Abstract(__module__, __qualname__)  # type: ignore[name-defined]  # python/mypy#10570
+
     rule_cls = EventFrequencyCondition
 
     def increment(self, event, count, environment=None, timestamp=None):
@@ -236,11 +240,9 @@ class EventFrequencyConditionTestCase(
             )
 
 
-class EventUniqueUserFrequencyConditionTestCase(
-    FrequencyConditionMixin,
-    StandardIntervalMixin,
-    SnubaTestCase,
-):
+class EventUniqueUserFrequencyConditionTestCase(StandardIntervalTestBase):
+    __test__ = Abstract(__module__, __qualname__)  # type: ignore[name-defined]  # python/mypy#10570
+
     rule_cls = EventUniqueUserFrequencyCondition
 
     def increment(self, event, count, environment=None, timestamp=None):
@@ -259,9 +261,14 @@ class EventUniqueUserFrequencyConditionTestCase(
             )
 
 
-class EventFrequencyPercentConditionTestCase(SnubaTestCase):
+class EventFrequencyPercentConditionTestCase(SnubaTestCase, RuleTestCase):
+    __test__ = Abstract(__module__, __qualname__)  # type: ignore[name-defined]  # python/mypy#10570
+
     rule_cls = EventFrequencyPercentCondition
 
+    def add_event(self, data, project_id, timestamp):
+        raise NotImplementedError
+
     def _make_sessions(self, num):
         received = time.time()
 
@@ -432,9 +439,7 @@ class EventFrequencyPercentConditionTestCase(SnubaTestCase):
 
 @freeze_time((now() - timedelta(days=2)).replace(hour=12, minute=40, second=0, microsecond=0))
 @region_silo_test
-class ErrorIssueFrequencyConditionTestCase(
-    EventFrequencyConditionTestCase, RuleTestCase, ErrorEventMixin
-):
+class ErrorIssueFrequencyConditionTestCase(ErrorEventMixin, EventFrequencyConditionTestCase):
     pass
 
 
@@ -443,7 +448,6 @@ class ErrorIssueFrequencyConditionTestCase(
 class PerfIssuePlatformIssueFrequencyConditionTestCase(
     PerfIssuePlatformEventMixin,
     EventFrequencyConditionTestCase,
-    RuleTestCase,
 ):
     pass
 
@@ -451,7 +455,8 @@ class PerfIssuePlatformIssueFrequencyConditionTestCase(
 @freeze_time((now() - timedelta(days=2)).replace(hour=12, minute=40, second=0, microsecond=0))
 @region_silo_test
 class ErrorIssueUniqueUserFrequencyConditionTestCase(
-    EventUniqueUserFrequencyConditionTestCase, RuleTestCase, ErrorEventMixin
+    ErrorEventMixin,
+    EventUniqueUserFrequencyConditionTestCase,
 ):
     pass
 
@@ -461,7 +466,6 @@ class ErrorIssueUniqueUserFrequencyConditionTestCase(
 class PerfIssuePlatformIssueUniqueUserFrequencyConditionTestCase(
     PerfIssuePlatformEventMixin,
     EventUniqueUserFrequencyConditionTestCase,
-    RuleTestCase,
 ):
     pass
 
@@ -469,7 +473,7 @@ class PerfIssuePlatformIssueUniqueUserFrequencyConditionTestCase(
 @freeze_time((now() - timedelta(days=2)).replace(hour=12, minute=40, second=0, microsecond=0))
 @region_silo_test
 class ErrorIssueEventFrequencyPercentConditionTestCase(
-    EventFrequencyPercentConditionTestCase, RuleTestCase, ErrorEventMixin
+    ErrorEventMixin, EventFrequencyPercentConditionTestCase
 ):
     pass
 
@@ -479,6 +483,5 @@ class ErrorIssueEventFrequencyPercentConditionTestCase(
 class PerfIssuePlatformIssueEventFrequencyPercentConditionTestCase(
     PerfIssuePlatformEventMixin,
     EventFrequencyPercentConditionTestCase,
-    RuleTestCase,
 ):
     pass

+ 38 - 19
tests/snuba/sessions/test_sessions.py

@@ -1,5 +1,8 @@
+from __future__ import annotations
+
 import time
 from datetime import datetime, timedelta, timezone
+from unittest import mock
 
 import pytest
 from django.utils import timezone as django_timezone
@@ -22,17 +25,18 @@ def parametrize_backend(cls):
     hopefully we won't have more than one backend in the future.
     """
 
-    assert not hasattr(cls, "backend")
-    cls.backend = SessionsReleaseHealthBackend()
-
-    class MetricsLayerTest(BaseMetricsTestCase, cls):  # type: ignore[valid-type]
-        __doc__ = f"Repeat tests from {cls} with metrics layer"
-        backend = MetricsReleaseHealthBackend()
-        adjust_interval = True  # HACK interval adjustment for new MetricsLayer implementation
+    assert isinstance(cls.backend, SessionsReleaseHealthBackend)
 
-    MetricsLayerTest.__name__ = f"{cls.__name__}MetricsLayer"
-
-    globals()[MetricsLayerTest.__name__] = MetricsLayerTest
+    newcls = type(
+        f"{cls.__name__}MetricsLayer",
+        (BaseMetricsTestCase, cls),
+        {
+            "__doc__": f"Repeat tests from {cls} with metrics layer",
+            "backend": MetricsReleaseHealthBackend(),
+            "adjust_interval": True,  # HACK interval adjustment for new MetricsLayer implementation
+        },
+    )
+    globals()[newcls.__name__] = newcls
 
     return cls
 
@@ -56,6 +60,7 @@ def make_24h_stats(ts, adjust_start=False):
 
 @parametrize_backend
 class SnubaSessionsTest(TestCase, SnubaTestCase):
+    backend = SessionsReleaseHealthBackend()
     adjust_interval = False  # HACK interval adjustment for new MetricsLayer implementation
 
     def setUp(self):
@@ -505,7 +510,7 @@ class SnubaSessionsTest(TestCase, SnubaTestCase):
             )
 
             # Last returned date is generated within function, should be close to now:
-            last_date = data[-1].pop("date")
+            last_date = data[-1]["date"]
             assert django_timezone.now() - last_date < timedelta(seconds=1)
 
             assert data == [
@@ -528,6 +533,7 @@ class SnubaSessionsTest(TestCase, SnubaTestCase):
                     "crash_free_users": 100.0,
                     "total_sessions": 2,
                     "total_users": 1,
+                    "date": mock.ANY,  # tested above
                 },
             ]
 
@@ -537,7 +543,6 @@ class SnubaSessionsTest(TestCase, SnubaTestCase):
             start=start,
             environments=["prod"],
         )
-        data[-1].pop("date")
         assert data == [
             {
                 "crash_free_sessions": None,
@@ -558,6 +563,7 @@ class SnubaSessionsTest(TestCase, SnubaTestCase):
                 "crash_free_users": 0.0,
                 "total_sessions": 1,
                 "total_users": 1,
+                "date": mock.ANY,
             },
         ]
         data = self.backend.get_crash_free_breakdown(
@@ -566,7 +572,6 @@ class SnubaSessionsTest(TestCase, SnubaTestCase):
             start=start,
             environments=["prod"],
         )
-        data[-1].pop("date")
         assert data == [
             {
                 "crash_free_sessions": None,
@@ -587,6 +592,7 @@ class SnubaSessionsTest(TestCase, SnubaTestCase):
                 "crash_free_users": None,
                 "total_sessions": 0,
                 "total_users": 0,
+                "date": mock.ANY,
             },
         ]
 
@@ -647,7 +653,7 @@ class SnubaSessionsTest(TestCase, SnubaTestCase):
         def ts(days: int) -> int:
             return day0 + days * one_day
 
-        return [[ts(i + 1), data] for i, data in enumerate(series)]
+        return [(ts(i + 1), data) for i, data in enumerate(series)]
 
     def _test_get_project_release_stats(
         self, stat: OverviewStat, release: str, expected_series, expected_totals
@@ -663,10 +669,10 @@ class SnubaSessionsTest(TestCase, SnubaTestCase):
             end=end,
         )
 
-        # Let's not care about lists vs. tuples:
-        stats = [[ts, data] for ts, data in stats]
+        # one system returns lists instead of tuples
+        normed = [(ts, data) for ts, data in stats]
 
-        assert stats == self._add_timestamps_to_series(expected_series, start)
+        assert normed == self._add_timestamps_to_series(expected_series, start)
         assert totals == expected_totals
 
     def test_get_project_release_stats_users(self):
@@ -1000,6 +1006,8 @@ class GetCrashFreeRateTestCase(TestCase, SnubaTestCase):
         In the previous 24h (>24h & <48h) -> 4 Exited + 1 Crashed / 5 Total Sessions -> 80%
     """
 
+    backend = SessionsReleaseHealthBackend()
+
     def setUp(self):
         super().setUp()
         self.session_started = time.time() // 60 * 60
@@ -1128,6 +1136,8 @@ class GetCrashFreeRateTestCase(TestCase, SnubaTestCase):
 @region_silo_test
 @parametrize_backend
 class GetProjectReleasesCountTest(TestCase, SnubaTestCase):
+    backend = SessionsReleaseHealthBackend()
+
     def test_empty(self):
         # Test no errors when no session data
         org = self.create_organization()
@@ -1142,6 +1152,7 @@ class GetProjectReleasesCountTest(TestCase, SnubaTestCase):
     def test_with_other_metrics(self):
         if not self.backend.is_metrics_based():
             return
+        assert isinstance(self, BaseMetricsTestCase)
 
         # Test no errors when no session data
         org = self.create_organization()
@@ -1222,6 +1233,8 @@ class GetProjectReleasesCountTest(TestCase, SnubaTestCase):
 
 @parametrize_backend
 class CheckReleasesHaveHealthDataTest(TestCase, SnubaTestCase):
+    backend = SessionsReleaseHealthBackend()
+
     def run_test(self, expected, projects, releases, start=None, end=None):
         if not start:
             start = datetime.now() - timedelta(days=1)
@@ -1262,6 +1275,8 @@ class CheckReleasesHaveHealthDataTest(TestCase, SnubaTestCase):
 
 @parametrize_backend
 class CheckNumberOfSessions(TestCase, SnubaTestCase):
+    backend = SessionsReleaseHealthBackend()
+
     def setUp(self):
         super().setUp()
         self.dev_env = self.create_environment(name="development", project=self.project)
@@ -1528,7 +1543,8 @@ class CheckNumberOfSessions(TestCase, SnubaTestCase):
 
         assert set(actual) == {(p1.id, 3), (p2.id, 1)}
 
-        for eids in ([], None):
+        eids_tests: tuple[list[int] | None, ...] = ([], None)
+        for eids in eids_tests:
             actual = self.backend.get_num_sessions_per_project(
                 project_ids=[self.project.id, self.another_project.id],
                 environment_ids=eids,
@@ -1543,6 +1559,8 @@ class CheckNumberOfSessions(TestCase, SnubaTestCase):
 @region_silo_test(stable=True)
 @parametrize_backend
 class InitWithoutUserTestCase(TestCase, SnubaTestCase):
+    backend = SessionsReleaseHealthBackend()
+
     def setUp(self):
         super().setUp()
         self.received = time.time()
@@ -1635,7 +1653,7 @@ class InitWithoutUserTestCase(TestCase, SnubaTestCase):
         )
 
         # Last returned date is generated within function, should be close to now:
-        last_date = data[-1].pop("date")
+        last_date = data[-1]["date"]
 
         assert django_timezone.now() - last_date < timedelta(seconds=1)
 
@@ -1659,6 +1677,7 @@ class InitWithoutUserTestCase(TestCase, SnubaTestCase):
                 "crash_free_users": 66.66666666666667,
                 "total_sessions": 3,
                 "total_users": 3,
+                "date": mock.ANY,  # tested above
             },
         ]
 

+ 14 - 7
tests/snuba/tagstore/test_tagstore_backend.py

@@ -20,6 +20,7 @@ from sentry.tagstore.exceptions import (
 )
 from sentry.tagstore.snuba.backend import SnubaTagStorage
 from sentry.tagstore.types import GroupTagValue, TagValue
+from sentry.testutils.abstract import Abstract
 from sentry.testutils.cases import PerformanceIssueTestCase, SnubaTestCase, TestCase
 from sentry.testutils.helpers.datetime import before_now, iso_format
 from sentry.utils.samples import load_data
@@ -173,6 +174,7 @@ class TagStorageTest(TestCase, SnubaTestCase, SearchIssueTestMixin, PerformanceI
             [("foo", "bar"), ("biz", "baz")],
             "releaseme",
         )
+        assert group_info is not None
         return group_info.group, env
 
     def test_get_group_tag_keys_and_top_values(self):
@@ -1271,7 +1273,8 @@ class ProfilingTagStorageTest(TestCase, SnubaTestCase, SearchIssueTestMixin):
             None,
             first_group_timestamp_start + timedelta(minutes=4),
         )
-        first_group = group_info.group if group_info else None
+        assert group_info is not None
+        first_group = group_info.group
 
         second_group_fingerprint = f"{ProfileFileIOGroupType.type_id}-group2"
         second_group_timestamp_start = timezone.now() - timedelta(hours=5)
@@ -1283,7 +1286,8 @@ class ProfilingTagStorageTest(TestCase, SnubaTestCase, SearchIssueTestMixin):
                 self.environment.name if incr != 4 else None,
                 second_group_timestamp_start + timedelta(minutes=incr),
             )
-            second_group = group_info.group if group_info else None
+            assert group_info is not None
+            second_group = group_info.group
 
         assert self.ts.get_generic_groups_user_counts(
             [self.project.id],
@@ -1317,7 +1321,8 @@ class ProfilingTagStorageTest(TestCase, SnubaTestCase, SearchIssueTestMixin):
             self.environment.name,
             last_event_ts,
         )
-        group = group_info.group if group_info else None
+        assert group_info is not None
+        group = group_info.group
 
         group_seen_stats = self.ts.get_generic_group_list_tag_value(
             [group.project_id],
@@ -1340,7 +1345,9 @@ class ProfilingTagStorageTest(TestCase, SnubaTestCase, SearchIssueTestMixin):
         }
 
 
-class BaseSemverTest:
+class BaseSemverTest(TestCase, SnubaTestCase):
+    __test__ = Abstract(__module__, __qualname__)  # type: ignore[name-defined]  # python/mypy#10570
+
     KEY: str
 
     def setUp(self):
@@ -1369,7 +1376,7 @@ class BaseSemverTest:
         ]
 
 
-class GetTagValuePaginatorForProjectsSemverTest(BaseSemverTest, TestCase, SnubaTestCase):
+class GetTagValuePaginatorForProjectsSemverTest(BaseSemverTest):
     KEY = SEMVER_ALIAS
 
     def test_semver(self):
@@ -1467,7 +1474,7 @@ class GetTagValuePaginatorForProjectsSemverTest(BaseSemverTest, TestCase, SnubaT
         )
 
 
-class GetTagValuePaginatorForProjectsSemverPackageTest(BaseSemverTest, TestCase, SnubaTestCase):
+class GetTagValuePaginatorForProjectsSemverPackageTest(BaseSemverTest):
     KEY = SEMVER_PACKAGE_ALIAS
 
     def test_semver_package(self):
@@ -1547,7 +1554,7 @@ class GetTagValuePaginatorForProjectsReleaseStageTest(TestCase, SnubaTestCase):
         self.run_test(ReleaseStages.ADOPTED, [], project=project_2, environment=self.environment)
 
 
-class GetTagValuePaginatorForProjectsSemverBuildTest(BaseSemverTest, TestCase, SnubaTestCase):
+class GetTagValuePaginatorForProjectsSemverBuildTest(BaseSemverTest):
     KEY = SEMVER_BUILD_ALIAS
 
     def test_semver_package(self):