diff --git a/src/sentry/dashboards/endpoints/organization_dashboard_generate.py b/src/sentry/dashboards/endpoints/organization_dashboard_generate.py
index b5d148a4867e..b6a71e7b32a3 100644
--- a/src/sentry/dashboards/endpoints/organization_dashboard_generate.py
+++ b/src/sentry/dashboards/endpoints/organization_dashboard_generate.py
@@ -158,7 +158,7 @@ class OrganizationDashboardGenerateEndpoint(OrganizationEndpoint):
permission_classes = (OrganizationDashboardGeneratePermission,)
def post(self, request: Request, organization: Organization) -> Response:
- has_access, error = has_seer_access_with_detail(organization, request.user)
+ has_access, error = has_seer_access_with_detail(organization)
if not has_access:
raise PermissionDenied(error)
diff --git a/src/sentry/features/temporary.py b/src/sentry/features/temporary.py
index f2fa8f7524b3..5e9bc5152f91 100644
--- a/src/sentry/features/temporary.py
+++ b/src/sentry/features/temporary.py
@@ -113,8 +113,6 @@ def register_temporary_features(manager: FeatureManager) -> None:
manager.add("organizations:explore-errors", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
# Enable returning the migrated discover queries in explore saved queries
manager.add("organizations:expose-migrated-discover-queries", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
- # Enable GenAI features such as Autofix and Issue Summary
- manager.add("organizations:gen-ai-features", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
# Enable organization investigation notebooks.
manager.add("organizations:investigations", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
# Enable the 'translate' functionality for GenAI on the explore > traces page
diff --git a/src/sentry/feedback/endpoints/organization_feedback_categories.py b/src/sentry/feedback/endpoints/organization_feedback_categories.py
index 1bb71c8270ea..13cab166f826 100644
--- a/src/sentry/feedback/endpoints/organization_feedback_categories.py
+++ b/src/sentry/feedback/endpoints/organization_feedback_categories.py
@@ -103,7 +103,7 @@ def get(self, request: Request, organization: Organization) -> Response:
:auth: required
"""
- if not has_seer_access(organization, actor=request.user):
+ if not has_seer_access(organization):
return Response(
{"detail": "AI categorization is not available for this organization."}, status=403
)
diff --git a/src/sentry/feedback/endpoints/organization_feedback_summary.py b/src/sentry/feedback/endpoints/organization_feedback_summary.py
index 4f10d724582c..ad5c8096ee69 100644
--- a/src/sentry/feedback/endpoints/organization_feedback_summary.py
+++ b/src/sentry/feedback/endpoints/organization_feedback_summary.py
@@ -90,7 +90,7 @@ def get(self, request: Request, organization: Organization) -> Response:
:auth: required
"""
- if not has_seer_access(organization, actor=request.user):
+ if not has_seer_access(organization):
return Response(
{"detail": "AI summaries are not available for this organization."}, status=403
)
diff --git a/src/sentry/integrations/utils/external_issues.py b/src/sentry/integrations/utils/external_issues.py
index 1f0c5b40df57..a32569d252b6 100644
--- a/src/sentry/integrations/utils/external_issues.py
+++ b/src/sentry/integrations/utils/external_issues.py
@@ -124,7 +124,7 @@ def maybe_generate_external_issue_details(
) -> GeneratedExternalIssueDetails:
organization = group.organization
empty_result = GeneratedExternalIssueDetails(title=None, description=None)
- if not has_seer_access(organization, actor=user):
+ if not has_seer_access(organization):
return empty_result
if not features.has("organizations:external-issues-ai-generate", organization, actor=user):
return empty_result
diff --git a/src/sentry/replays/endpoints/project_replay_summary.py b/src/sentry/replays/endpoints/project_replay_summary.py
index 10feec9b7856..512f65827835 100644
--- a/src/sentry/replays/endpoints/project_replay_summary.py
+++ b/src/sentry/replays/endpoints/project_replay_summary.py
@@ -152,7 +152,7 @@ def has_replay_summary_access(self, project: Project, request: Request) -> bool:
project.organization,
actor=request.user,
)
- and has_seer_access(project.organization, actor=request.user)
+ and has_seer_access(project.organization)
)
def get(self, request: Request, project: Project, replay_id: str) -> Response:
diff --git a/src/sentry/seer/agent/client.py b/src/sentry/seer/agent/client.py
index c0eba83a3747..be65e745393b 100644
--- a/src/sentry/seer/agent/client.py
+++ b/src/sentry/seer/agent/client.py
@@ -375,7 +375,7 @@ def __init__(
raise ValueError("category_key and category_value must be provided together")
# Validate base Seer access on init (agent-specific flag checks are done at the endpoint level)
- has_access, error = has_seer_access_with_detail(organization, user)
+ has_access, error = has_seer_access_with_detail(organization)
if not has_access:
raise SeerPermissionError(error or "Access denied")
diff --git a/src/sentry/seer/agent/client_utils.py b/src/sentry/seer/agent/client_utils.py
index f3b013c7498c..800d444d0938 100644
--- a/src/sentry/seer/agent/client_utils.py
+++ b/src/sentry/seer/agent/client_utils.py
@@ -389,8 +389,8 @@ def has_seer_agent_access_with_detail(
Returns:
tuple[bool, str | None]: (has_access, error_message)
"""
- # Check base Seer access (gen-ai-features, hide_ai_features, acknowledgement)
- has_access, error = has_seer_access_with_detail(organization, actor)
+ # Check base Seer access (self-hosted, hide_ai_features, acknowledgement)
+ has_access, error = has_seer_access_with_detail(organization)
if not has_access:
return False, error
diff --git a/src/sentry/seer/autofix/issue_summary.py b/src/sentry/seer/autofix/issue_summary.py
index a502f9158851..ba5ad795d836 100644
--- a/src/sentry/seer/autofix/issue_summary.py
+++ b/src/sentry/seer/autofix/issue_summary.py
@@ -41,7 +41,7 @@
from sentry.seer.entrypoints.operator import SeerAutofixOperator
from sentry.seer.models import SummarizeIssueResponse
from sentry.seer.models.run import SeerRun, SeerRunMirrorStatus
-from sentry.seer.seer_setup import has_seer_access
+from sentry.seer.seer_setup import has_seer_access, is_seer_available
from sentry.seer.signed_seer_api import (
SeerViewerContext,
SummarizeIssueRequest,
@@ -585,8 +585,8 @@ def get_issue_summary(
"""
if user is None:
user = AnonymousUser()
- if not features.has("organizations:gen-ai-features", group.organization, actor=user):
- return {"detail": "Feature flag not enabled"}, 400
+ if not is_seer_available():
+ return {"detail": "Seer is not available on this installation."}, 400
if group.organization.get_option("sentry:hide_ai_features"):
return {"detail": "AI features are disabled for this organization."}, 403
diff --git a/src/sentry/seer/autofix/trigger.py b/src/sentry/seer/autofix/trigger.py
index 7b73d1ec0d99..ea19890ee90a 100644
--- a/src/sentry/seer/autofix/trigger.py
+++ b/src/sentry/seer/autofix/trigger.py
@@ -28,14 +28,15 @@ def get_seer_automation_ineligibility_reason(
group: Group,
) -> SeerAutomationIneligibilityReason | None:
"""Return the reason an issue is ineligible for Seer automation, or None if eligible."""
- from sentry import features, quotas
+ from sentry import quotas
from sentry.constants import DataCategory
from sentry.seer.autofix.utils import is_issue_category_eligible
+ from sentry.seer.seer_setup import is_seer_available
if not is_issue_category_eligible(group):
return "not_eligible.issue_category_ineligible"
- if not features.has("organizations:gen-ai-features", group.organization):
+ if not is_seer_available():
return "not_eligible.gen_ai_feature_disabled"
gen_ai_allowed = not group.organization.get_option("sentry:hide_ai_features")
diff --git a/src/sentry/seer/endpoints/organization_seer_agent_chat.py b/src/sentry/seer/endpoints/organization_seer_agent_chat.py
index 5517171f226d..9211597fb165 100644
--- a/src/sentry/seer/endpoints/organization_seer_agent_chat.py
+++ b/src/sentry/seer/endpoints/organization_seer_agent_chat.py
@@ -213,7 +213,7 @@ def get(
"""
has_access, error = has_seer_agent_access_with_detail(organization, request.user)
- has_seer_access, _ = has_seer_access_with_detail(organization, request.user)
+ has_seer_access, _ = has_seer_access_with_detail(organization)
if not has_access and not has_seer_access:
raise PermissionDenied(error)
@@ -263,7 +263,7 @@ def post(
"""
has_access, error = has_seer_agent_access_with_detail(organization, request.user)
- has_seer_access, _ = has_seer_access_with_detail(organization, request.user)
+ has_seer_access, _ = has_seer_access_with_detail(organization)
# Orgs with Seer access can continue existing dashboard generate runs, but cannot start new runs from this endpoint.
can_continue_dashboards_generate_run = has_seer_access and run_id is not None
diff --git a/src/sentry/seer/endpoints/search_agent_start.py b/src/sentry/seer/endpoints/search_agent_start.py
index 359bff24649f..9e89af26e98b 100644
--- a/src/sentry/seer/endpoints/search_agent_start.py
+++ b/src/sentry/seer/endpoints/search_agent_start.py
@@ -161,7 +161,7 @@ def post(self, request: Request, organization: Organization) -> Response:
status=status.HTTP_403_FORBIDDEN,
)
- has_seer_access, detail = has_seer_access_with_detail(organization, actor=request.user)
+ has_seer_access, detail = has_seer_access_with_detail(organization)
if not has_seer_access:
return Response(
{"detail": detail},
diff --git a/src/sentry/seer/endpoints/search_agent_state.py b/src/sentry/seer/endpoints/search_agent_state.py
index 6fd186069eea..553b32caf1ea 100644
--- a/src/sentry/seer/endpoints/search_agent_state.py
+++ b/src/sentry/seer/endpoints/search_agent_state.py
@@ -91,7 +91,7 @@ def get(self, request: Request, organization: Organization, run_id: str) -> Resp
status=status.HTTP_403_FORBIDDEN,
)
- has_seer_access, detail = has_seer_access_with_detail(organization, actor=request.user)
+ has_seer_access, detail = has_seer_access_with_detail(organization)
if not has_seer_access:
return Response(
{"detail": detail},
diff --git a/src/sentry/seer/endpoints/trace_explorer_ai_query.py b/src/sentry/seer/endpoints/trace_explorer_ai_query.py
index 12146d67bf06..27d49e4c7599 100644
--- a/src/sentry/seer/endpoints/trace_explorer_ai_query.py
+++ b/src/sentry/seer/endpoints/trace_explorer_ai_query.py
@@ -8,7 +8,6 @@
from rest_framework.request import Request
from rest_framework.response import Response
-from sentry import features
from sentry.api.api_owners import ApiOwner
from sentry.api.api_publish_status import ApiPublishStatus
from sentry.api.base import cell_silo_endpoint
@@ -16,6 +15,7 @@
from sentry.models.organization import Organization
from sentry.seer.endpoints.trace_explorer_ai_setup import OrganizationTraceExplorerAIPermission
from sentry.seer.models import SeerApiError
+from sentry.seer.seer_setup import is_seer_available
from sentry.seer.signed_seer_api import (
SeerViewerContext,
TranslateQueryRequest,
@@ -97,9 +97,7 @@ def post(self, request: Request, organization: Organization) -> Response:
status=status.HTTP_403_FORBIDDEN,
)
- if not features.has(
- "organizations:gen-ai-features", organization=organization, actor=request.user
- ):
+ if not is_seer_available():
return Response(
{"detail": "Organization does not have access to this feature"},
status=status.HTTP_403_FORBIDDEN,
diff --git a/src/sentry/seer/endpoints/trace_explorer_ai_setup.py b/src/sentry/seer/endpoints/trace_explorer_ai_setup.py
index 6c34880a50f8..c5095052005c 100644
--- a/src/sentry/seer/endpoints/trace_explorer_ai_setup.py
+++ b/src/sentry/seer/endpoints/trace_explorer_ai_setup.py
@@ -7,7 +7,6 @@
from rest_framework.exceptions import ParseError
from rest_framework.response import Response
-from sentry import features
from sentry.api.api_owners import ApiOwner
from sentry.api.api_publish_status import ApiPublishStatus
from sentry.api.base import cell_silo_endpoint
@@ -15,6 +14,7 @@
from sentry.api.bases.organization import OrganizationPermission
from sentry.models.organization import Organization
from sentry.seer.models import SeerApiError
+from sentry.seer.seer_setup import is_seer_available
from sentry.seer.signed_seer_api import (
CreateCacheRequest,
SeerViewerContext,
@@ -84,9 +84,7 @@ def post(self, request: Request, organization: Organization) -> Response:
status=status.HTTP_403_FORBIDDEN,
)
- if not features.has(
- "organizations:gen-ai-features", organization=organization, actor=request.user
- ):
+ if not is_seer_available():
return Response(
{"detail": "Organization does not have access to this feature"},
status=status.HTTP_403_FORBIDDEN,
diff --git a/src/sentry/seer/endpoints/trace_explorer_ai_translate_agentic.py b/src/sentry/seer/endpoints/trace_explorer_ai_translate_agentic.py
index f9102b40100a..58620f024fd5 100644
--- a/src/sentry/seer/endpoints/trace_explorer_ai_translate_agentic.py
+++ b/src/sentry/seer/endpoints/trace_explorer_ai_translate_agentic.py
@@ -129,7 +129,7 @@ def post(self, request: Request, organization: Organization) -> Response:
status=status.HTTP_403_FORBIDDEN,
)
- has_seer_access, detail = has_seer_access_with_detail(organization, actor=request.user)
+ has_seer_access, detail = has_seer_access_with_detail(organization)
if not has_seer_access:
return Response(
{"detail": detail},
diff --git a/src/sentry/seer/seer_setup.py b/src/sentry/seer/seer_setup.py
index b2184c91b624..cec829053451 100644
--- a/src/sentry/seer/seer_setup.py
+++ b/src/sentry/seer/seer_setup.py
@@ -1,11 +1,7 @@
-from django.contrib.auth.models import AnonymousUser
-
from sentry import features
from sentry.models.organization import Organization
from sentry.organizations.services.organization.model import RpcOrganization
from sentry.seer.constants import SEER_GITLAB_SCM_PROVIDERS, SEER_SUPPORTED_SCM_PROVIDERS
-from sentry.users.models.user import User
-from sentry.users.services.user.model import RpcUser
from sentry.utils.settings import is_self_hosted
@@ -20,27 +16,16 @@ def is_seer_available() -> bool:
return not is_self_hosted()
-def has_seer_access(
- organization: Organization | RpcOrganization,
- actor: User | AnonymousUser | RpcUser | None = None,
-) -> bool:
- return (
- is_seer_available()
- and features.has("organizations:gen-ai-features", organization, actor=actor)
- and not bool(organization.get_option("sentry:hide_ai_features"))
- )
+def has_seer_access(organization: Organization | RpcOrganization) -> bool:
+ return is_seer_available() and not bool(organization.get_option("sentry:hide_ai_features"))
def has_seer_access_with_detail(
organization: Organization | RpcOrganization,
- actor: User | AnonymousUser | RpcUser | None = None,
) -> tuple[bool, str | None]:
if not is_seer_available():
return False, "Seer is not available on this installation."
- if not features.has("organizations:gen-ai-features", organization, actor=actor):
- return False, "Feature flag not enabled"
-
if organization.get_option("sentry:hide_ai_features"):
return False, "AI features are disabled for this organization."
diff --git a/src/sentry/tasks/seer/explorer_index.py b/src/sentry/tasks/seer/explorer_index.py
index 467a2e523429..970e81ce14c4 100644
--- a/src/sentry/tasks/seer/explorer_index.py
+++ b/src/sentry/tasks/seer/explorer_index.py
@@ -35,7 +35,6 @@
EXPLORER_INDEX_DISPATCH_STEP = timedelta(seconds=37)
FEATURE_NAMES = [
- "organizations:gen-ai-features",
"organizations:seer-explorer-index",
"organizations:seat-based-seer-enabled",
"organizations:seer-added",
@@ -73,43 +72,18 @@ def get_seer_explorer_enabled_projects() -> Generator[tuple[int, int]]:
if bool(project.organization.get_option("sentry:hide_ai_features")):
continue
- is_eligible = False
with start_span(
op="seer_explorer_index.has_feature", name="seer_explorer_index.has_feature"
):
batch_result = features.batch_has(FEATURE_NAMES, organization=project.organization)
-
if batch_result:
- org_key = f"organization:{project.organization.id}"
- org_features = batch_result.get(org_key, {})
- has_gen_ai = org_features.get("organizations:gen-ai-features", False)
- has_explorer_index = org_features.get("organizations:seer-explorer-index", False)
-
- if has_explorer_index and has_gen_ai:
- is_eligible = True
-
- has_seer_plan = org_features.get(
- "organizations:seat-based-seer-enabled", False
- ) or org_features.get("organizations:seer-added", False)
-
- if has_seer_plan and has_gen_ai:
- is_eligible = True
-
+ org_features = batch_result.get(f"organization:{project.organization.id}", {})
else:
- has_gen_ai = features.has("organizations:gen-ai-features", project.organization)
- has_explorer_index = features.has(
- "organizations:seer-explorer-index", project.organization
- )
-
- if has_explorer_index and has_gen_ai:
- is_eligible = True
-
- has_seer_plan = features.has(
- "organizations:seat-based-seer-enabled", project.organization
- ) or features.has("organizations:seer-added", project.organization)
+ org_features = {
+ name: features.has(name, project.organization) for name in FEATURE_NAMES
+ }
- if has_seer_plan and has_gen_ai:
- is_eligible = True
+ is_eligible = any(org_features.get(name, False) for name in FEATURE_NAMES)
if not is_eligible:
continue
diff --git a/src/sentry/tasks/seer/night_shift/cron.py b/src/sentry/tasks/seer/night_shift/cron.py
index 76ba01b010c6..39bbd7c5330c 100644
--- a/src/sentry/tasks/seer/night_shift/cron.py
+++ b/src/sentry/tasks/seer/night_shift/cron.py
@@ -79,7 +79,6 @@
BATCH_FEATURE_NAMES = [
"organizations:seer-night-shift",
- "organizations:gen-ai-features",
]
PER_ORG_FEATURE_NAMES = [
# INTERNAL handlers aren't routed through batch_has_for_organizations,
@@ -610,7 +609,7 @@ def _get_eligible_orgs_from_batch(
for org in eligible:
if all(features.has(f, org) for f in PER_ORG_FEATURE_NAMES):
paid_eligible.append(org)
- elif features.has("organizations:gen-ai-features", org) and is_free_cohort_org(org):
+ elif is_free_cohort_org(org):
free_cohort_eligible.append(org)
return paid_eligible + free_cohort_eligible
diff --git a/src/sentry/testutils/helpers/github.py b/src/sentry/testutils/helpers/github.py
index 4ad4c8046938..b6bc753fe04e 100644
--- a/src/sentry/testutils/helpers/github.py
+++ b/src/sentry/testutils/helpers/github.py
@@ -144,7 +144,7 @@ def send_github_webhook_event(
@override_settings(SENTRY_SELF_HOSTED=False)
class GitHubWebhookCodeReviewTestCase(GitHubWebhookTestCase):
# Code review features are org features as set in options automator
- CODE_REVIEW_FEATURES = {"organizations:gen-ai-features", "organizations:code-review-beta"}
+ CODE_REVIEW_FEATURES = {"organizations:code-review-beta"}
# Options to set are regional options as set in options automator
OPTIONS_TO_SET: dict[str, Any] = {}
# Org options are org options as set via OrganizationOption.objects.set_value
diff --git a/src/sentry/uptime/endpoints/organization_uptime_assertion_suggestions.py b/src/sentry/uptime/endpoints/organization_uptime_assertion_suggestions.py
index 855aa428ac54..4f4a5d91945b 100644
--- a/src/sentry/uptime/endpoints/organization_uptime_assertion_suggestions.py
+++ b/src/sentry/uptime/endpoints/organization_uptime_assertion_suggestions.py
@@ -84,8 +84,7 @@ def post(
request: Request,
organization: Organization,
) -> Response:
- # Check if AI features are enabled (includes gen-ai-features flag + hide_ai_features opt-out)
- if not has_seer_access(organization, actor=request.user):
+ if not has_seer_access(organization):
return self.respond(
{"detail": "AI features are not enabled for this organization"},
status=403,
diff --git a/static/app/components/feedback/feedbackItem/feedbackItemUsername.spec.tsx b/static/app/components/feedback/feedbackItem/feedbackItemUsername.spec.tsx
index 704fd2f0da43..7f5c1353f571 100644
--- a/static/app/components/feedback/feedbackItem/feedbackItemUsername.spec.tsx
+++ b/static/app/components/feedback/feedbackItem/feedbackItemUsername.spec.tsx
@@ -3,6 +3,7 @@ import {FeedbackIssueFixture} from 'sentry-fixture/feedbackIssue';
import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
import {FeedbackItemUsername} from 'sentry/components/feedback/feedbackItem/feedbackItemUsername';
+import {ConfigStore} from 'sentry/stores/configStore';
describe('FeedbackItemUsername', () => {
let seerSetupMock: any;
@@ -135,6 +136,10 @@ describe('FeedbackItemUsername', () => {
});
describe('AI summary functionality', () => {
+ beforeEach(() => {
+ ConfigStore.set('isSelfHosted', false);
+ });
+
it('should display summary and include it in email subject when AI summary is enabled', async () => {
seerSetupMock = mockSeerSetup();
@@ -146,11 +151,7 @@ describe('FeedbackItemUsername', () => {
},
});
- render(, {
- organization: {
- features: ['gen-ai-features'],
- },
- });
+ render();
await waitFor(() => {
expect(seerSetupMock).toHaveBeenCalled();
@@ -168,17 +169,18 @@ describe('FeedbackItemUsername', () => {
it.each([
{
description: 'AI features are disabled',
- features: [] as string[],
+ isSelfHosted: true,
summary: 'Login issue with payment flow',
},
{
description: 'AI features enabled but summary is null',
- features: ['gen-ai-features'],
+ isSelfHosted: false,
summary: null,
},
])(
'should not display summary or include it in email subject when $description',
- async ({features, summary}) => {
+ async ({isSelfHosted, summary}) => {
+ ConfigStore.set('isSelfHosted', isSelfHosted);
seerSetupMock = mockSeerSetup();
const issue = FeedbackIssueFixture({
@@ -189,11 +191,7 @@ describe('FeedbackItemUsername', () => {
},
});
- render(, {
- organization: {
- features,
- },
- });
+ render();
await waitFor(() => {
expect(seerSetupMock).toHaveBeenCalled();
diff --git a/static/app/components/feedback/summaryCategories/feedbackCategories.tsx b/static/app/components/feedback/summaryCategories/feedbackCategories.tsx
index 68459967f6ac..a78e4f7fe980 100644
--- a/static/app/components/feedback/summaryCategories/feedbackCategories.tsx
+++ b/static/app/components/feedback/summaryCategories/feedbackCategories.tsx
@@ -39,8 +39,7 @@ function getSearchTermForLabelList(labels: string[]) {
export function FeedbackCategories() {
const {isError, isPending, categories, tooFewFeedbacks} = useFeedbackCategories();
- // if we are showing this component, gen-ai-features must be true
- // and org.hideAiFeatures must be false,
+ // if we are showing this component, AI features are allowed for the org,
// but we still need to check that their seer acknowledgement exists
const {isPending: isOrgSeerSetupPending} = useOrganizationSeerSetup();
diff --git a/static/app/components/feedback/summaryCategories/feedbackSummary.tsx b/static/app/components/feedback/summaryCategories/feedbackSummary.tsx
index ac59749a977e..fe7dffbaa742 100644
--- a/static/app/components/feedback/summaryCategories/feedbackSummary.tsx
+++ b/static/app/components/feedback/summaryCategories/feedbackSummary.tsx
@@ -11,8 +11,7 @@ import {useOrganization} from 'sentry/utils/useOrganization';
export function FeedbackSummary() {
const {isError, isPending, summary, tooFewFeedbacks} = useFeedbackSummary();
- // if we are showing this component, gen-ai-features must be true
- // and org.hideAiFeatures must be false,
+ // if we are showing this component, AI features are allowed for the org,
// but we still need to check that their seer acknowledgement exists
const {isPending: isOrgSeerSetupPending} = useOrganizationSeerSetup();
const organization = useOrganization();
diff --git a/static/app/components/searchQueryBuilder/askSeerCombobox/askSeerComboBox.spec.tsx b/static/app/components/searchQueryBuilder/askSeerCombobox/askSeerComboBox.spec.tsx
index c5892126fa39..cf394b3542c9 100644
--- a/static/app/components/searchQueryBuilder/askSeerCombobox/askSeerComboBox.spec.tsx
+++ b/static/app/components/searchQueryBuilder/askSeerCombobox/askSeerComboBox.spec.tsx
@@ -13,6 +13,7 @@ import {
SearchQueryBuilderProvider,
useSearchQueryBuilderAI,
} from 'sentry/components/searchQueryBuilder/context';
+import {ConfigStore} from 'sentry/stores/configStore';
import * as analytics from 'sentry/utils/analytics';
import {getApiUrl} from 'sentry/utils/api/getApiUrl';
import {fetchMutation} from 'sentry/utils/queryClient';
@@ -47,7 +48,7 @@ const askSeerMutationOptions = mutationOptions({
});
const {organization} = initializeOrg({
- organization: {features: ['gen-ai-features'], hideAiFeatures: false},
+ organization: {hideAiFeatures: false},
});
const feedbackIntegration = {
@@ -78,6 +79,8 @@ describe('AskSeerComboBox', () => {
// Combobox announcements will pollute the test output if we don't clear them
destroyAnnouncer();
+ ConfigStore.set('isSelfHosted', false);
+
MockApiClient.clearMockResponses();
MockApiClient.addMockResponse({
@@ -586,7 +589,8 @@ describe('AskSeerComboBox', () => {
await waitFor(() => expect(queryRequest).toHaveBeenCalledTimes(2));
});
- it('does not render if the organization does not have the gen-ai-features feature', () => {
+ it('does not render when self-hosted', () => {
+ ConfigStore.set('isSelfHosted', true);
const {container} = render(
{
});
const {organization} = initializeOrg({
organization: {
- features: ['gen-ai-features'],
hideAiFeatures: false,
},
});
diff --git a/static/app/components/searchQueryBuilder/index.spec.tsx b/static/app/components/searchQueryBuilder/index.spec.tsx
index 8953517fe9f5..b64b282f4d18 100644
--- a/static/app/components/searchQueryBuilder/index.spec.tsx
+++ b/static/app/components/searchQueryBuilder/index.spec.tsx
@@ -7258,11 +7258,7 @@ describe('SearchQueryBuilder', () => {
describe('ask seer', () => {
it('renders ask seer in the footer', async () => {
- render(, {
- organization: {
- features: ['gen-ai-features'],
- },
- });
+ render();
await userEvent.click(getLastInput());
@@ -7284,12 +7280,7 @@ describe('SearchQueryBuilder', () => {
onCaseInsensitiveClick={jest.fn()}
/>
- ,
- {
- organization: {
- features: ['gen-ai-features'],
- },
- }
+
);
await userEvent.click(getLastInput());
@@ -7326,12 +7317,7 @@ describe('SearchQueryBuilder', () => {
{...defaultProps}
enableAISearch
initialQuery="browser.name:Firefox"
- />,
- {
- organization: {
- features: ['gen-ai-features'],
- },
- }
+ />
);
await userEvent.click(
@@ -7345,11 +7331,7 @@ describe('SearchQueryBuilder', () => {
});
it('does not render ask seer in the footer when AI search is disabled', async () => {
- render(, {
- organization: {
- features: ['gen-ai-features'],
- },
- });
+ render();
await userEvent.click(getLastInput());
@@ -7456,12 +7438,7 @@ describe('SearchQueryBuilder', () => {
render(
- ,
- {
- organization: {
- features: ['gen-ai-features'],
- },
- }
+
);
await userEvent.click(getLastInput());
@@ -7542,11 +7519,7 @@ describe('SearchQueryBuilder', () => {
}
it('keeps ask seer in the footer when searching free text', async () => {
- render(, {
- organization: {
- features: ['gen-ai-features'],
- },
- });
+ render();
await userEvent.click(getLastInput());
await userEvent.type(screen.getByRole('combobox'), 'some free text');
@@ -7572,12 +7545,7 @@ describe('SearchQueryBuilder', () => {
- ,
- {
- organization: {
- features: ['gen-ai-features'],
- },
- }
+
);
await userEvent.click(getLastInput());
@@ -7614,12 +7582,7 @@ describe('SearchQueryBuilder', () => {
- ,
- {
- organization: {
- features: ['gen-ai-features'],
- },
- }
+
);
await userEvent.click(getLastInput());
@@ -7656,12 +7619,7 @@ describe('SearchQueryBuilder', () => {
- ,
- {
- organization: {
- features: ['gen-ai-features'],
- },
- }
+
);
await userEvent.click(getLastInput());
@@ -7689,12 +7647,7 @@ describe('SearchQueryBuilder', () => {
- ,
- {
- organization: {
- features: ['gen-ai-features'],
- },
- }
+
);
await userEvent.click(screen.getByRole('row', {name: 'find slow'}));
@@ -7731,12 +7684,7 @@ describe('SearchQueryBuilder', () => {
- ,
- {
- organization: {
- features: ['gen-ai-features'],
- },
- }
+
);
await userEvent.click(getLastInput());
@@ -7767,12 +7715,7 @@ describe('SearchQueryBuilder', () => {
- ,
- {
- organization: {
- features: ['gen-ai-features'],
- },
- }
+
);
await userEvent.click(getLastInput());
@@ -7804,12 +7747,7 @@ describe('SearchQueryBuilder', () => {
- ,
- {
- organization: {
- features: ['gen-ai-features'],
- },
- }
+
);
await userEvent.click(getLastInput());
diff --git a/static/app/components/seer/autofixChatContext.spec.tsx b/static/app/components/seer/autofixChatContext.spec.tsx
index 9ccd73bfbcff..950bdcf34d2b 100644
--- a/static/app/components/seer/autofixChatContext.spec.tsx
+++ b/static/app/components/seer/autofixChatContext.spec.tsx
@@ -48,7 +48,7 @@ describe('AutofixChatProvider', () => {
const organization = OrganizationFixture({
openMembership: true,
hideAiFeatures: false,
- features: ['seer-explorer', 'gen-ai-features'],
+ features: ['seer-explorer'],
});
const chatUrl = `/organizations/${organization.slug}/seer/explorer-chat/`;
diff --git a/static/app/utils/replays/hooks/useActiveReplayTab.spec.tsx b/static/app/utils/replays/hooks/useActiveReplayTab.spec.tsx
index e38eb9ad541a..0794a657799d 100644
--- a/static/app/utils/replays/hooks/useActiveReplayTab.spec.tsx
+++ b/static/app/utils/replays/hooks/useActiveReplayTab.spec.tsx
@@ -22,7 +22,7 @@ describe('useActiveReplayTab', () => {
const {result} = renderHookWithProviders(useActiveReplayTab, {
initialProps: {},
organization: OrganizationFixture({
- features: ['gen-ai-features', 'replay-ai-summaries'],
+ features: ['replay-ai-summaries'],
}),
});
@@ -35,7 +35,7 @@ describe('useActiveReplayTab', () => {
const {result} = renderHookWithProviders(useActiveReplayTab, {
initialProps: {},
organization: OrganizationFixture({
- features: ['gen-ai-features', 'replay-ai-summaries'],
+ features: ['replay-ai-summaries'],
}),
});
@@ -49,7 +49,7 @@ describe('useActiveReplayTab', () => {
location: {pathname: '/mock-pathname/', query: {query: 'click.tag:button'}},
},
organization: OrganizationFixture({
- features: ['gen-ai-features', 'replay-ai-summaries'],
+ features: ['replay-ai-summaries'],
}),
});
expect(result.current.getActiveTab()).toBe(TabKey.AI);
@@ -67,11 +67,7 @@ describe('useActiveReplayTab', () => {
const {result} = renderHookWithProviders(useActiveReplayTab, {
initialProps: {isVideoReplay: true},
organization: OrganizationFixture({
- features: [
- 'gen-ai-features',
- 'replay-ai-summaries',
- 'replay-ai-summaries-mobile',
- ],
+ features: ['replay-ai-summaries', 'replay-ai-summaries-mobile'],
}),
});
@@ -82,7 +78,7 @@ describe('useActiveReplayTab', () => {
const {result} = renderHookWithProviders(useActiveReplayTab, {
initialProps: {isVideoReplay: true},
organization: OrganizationFixture({
- features: ['gen-ai-features', 'replay-ai-summaries'],
+ features: ['replay-ai-summaries'],
}),
});
diff --git a/static/app/utils/seer/areAiFeaturesAllowed.spec.ts b/static/app/utils/seer/areAiFeaturesAllowed.spec.ts
index 3f11286e12b6..8c0d4709909a 100644
--- a/static/app/utils/seer/areAiFeaturesAllowed.spec.ts
+++ b/static/app/utils/seer/areAiFeaturesAllowed.spec.ts
@@ -8,33 +8,19 @@ describe('areAiFeaturesAllowed', () => {
ConfigStore.set('isSelfHosted', false);
});
- it('allows when flagged, not hidden, and not self-hosted', () => {
- const organization = OrganizationFixture({
- features: ['gen-ai-features'],
- hideAiFeatures: false,
- });
+ it('allows when not hidden and not self-hosted', () => {
+ const organization = OrganizationFixture({hideAiFeatures: false});
expect(areAiFeaturesAllowed(organization)).toBe(true);
});
- it('denies without the flag', () => {
- const organization = OrganizationFixture({features: [], hideAiFeatures: false});
- expect(areAiFeaturesAllowed(organization)).toBe(false);
- });
-
it('denies when the org hides AI features', () => {
- const organization = OrganizationFixture({
- features: ['gen-ai-features'],
- hideAiFeatures: true,
- });
+ const organization = OrganizationFixture({hideAiFeatures: true});
expect(areAiFeaturesAllowed(organization)).toBe(false);
});
it('denies on self-hosted', () => {
ConfigStore.set('isSelfHosted', true);
- const organization = OrganizationFixture({
- features: ['gen-ai-features'],
- hideAiFeatures: false,
- });
+ const organization = OrganizationFixture({hideAiFeatures: false});
expect(areAiFeaturesAllowed(organization)).toBe(false);
});
});
diff --git a/static/app/utils/seer/areAiFeaturesAllowed.ts b/static/app/utils/seer/areAiFeaturesAllowed.ts
index 66084530b2c8..81d818f2d365 100644
--- a/static/app/utils/seer/areAiFeaturesAllowed.ts
+++ b/static/app/utils/seer/areAiFeaturesAllowed.ts
@@ -2,11 +2,7 @@ import {ConfigStore} from 'sentry/stores/configStore';
import type {Organization} from 'sentry/types/organization';
export function areAiFeaturesAllowed(
- organization: Pick
+ organization: Pick
): boolean {
- return (
- !ConfigStore.get('isSelfHosted') &&
- !organization.hideAiFeatures &&
- organization.features.includes('gen-ai-features')
- );
+ return !ConfigStore.get('isSelfHosted') && !organization.hideAiFeatures;
}
diff --git a/static/app/views/dashboards/manage/index.spec.tsx b/static/app/views/dashboards/manage/index.spec.tsx
index 03e4989774b0..9e22cacd789a 100644
--- a/static/app/views/dashboards/manage/index.spec.tsx
+++ b/static/app/views/dashboards/manage/index.spec.tsx
@@ -119,7 +119,7 @@ describe('Dashboards > Detail', () => {
});
it('creates new dashboard', async () => {
- const org = OrganizationFixture({features: FEATURES});
+ const org = OrganizationFixture({features: FEATURES, hideAiFeatures: true});
const {router} = render(, {
organization: org,
diff --git a/static/app/views/dashboards/widgetBuilder/buildSteps/filterResultsStep/eventsSearchBar.spec.tsx b/static/app/views/dashboards/widgetBuilder/buildSteps/filterResultsStep/eventsSearchBar.spec.tsx
index 95bfa2be2c83..b1e94c691efa 100644
--- a/static/app/views/dashboards/widgetBuilder/buildSteps/filterResultsStep/eventsSearchBar.spec.tsx
+++ b/static/app/views/dashboards/widgetBuilder/buildSteps/filterResultsStep/eventsSearchBar.spec.tsx
@@ -43,7 +43,7 @@ describe('EventsSearchBar', () => {
it('hides Ask Seer for errors widgets', async () => {
organization = OrganizationFixture({
- features: ['gen-ai-features', 'gen-ai-search-agent-translate'],
+ features: ['gen-ai-search-agent-translate'],
});
render(
diff --git a/static/app/views/explore/spans/content.spec.tsx b/static/app/views/explore/spans/content.spec.tsx
index ddfbdb000878..33f5b36fdb85 100644
--- a/static/app/views/explore/spans/content.spec.tsx
+++ b/static/app/views/explore/spans/content.spec.tsx
@@ -26,15 +26,11 @@ function TopBarWrapper({children}: {children: ReactNode}) {
}
describe('ExploreContent', () => {
- const {organization, project} = initializeOrg({
- organization: {
- features: ['gen-ai-features'],
- },
- });
+ const {organization, project} = initializeOrg();
const {organization: highRangeOrganization, project: highRangeProject} = initializeOrg({
organization: {
slug: 'high-range-org',
- features: ['gen-ai-features', 'visibility-explore-range-high'],
+ features: ['visibility-explore-range-high'],
},
});
@@ -213,7 +209,7 @@ describe('ExploreContent', () => {
const highRangeOrganizationWithoutFeature = {
...highRangeOrganization,
- features: ['gen-ai-features'],
+ features: [],
};
const {rerender} = render(
@@ -291,7 +287,7 @@ describe('ExploreContent', () => {
const highRangeOrganizationWithOverride = {
...highRangeOrganization,
- features: ['gen-ai-features'],
+ features: [],
};
OrganizationStore.onUpdate(highRangeOrganizationWithOverride, {replace: true});
diff --git a/static/app/views/explore/spans/spansTab.spec.tsx b/static/app/views/explore/spans/spansTab.spec.tsx
index 81bf33d57432..099d3e39ca50 100644
--- a/static/app/views/explore/spans/spansTab.spec.tsx
+++ b/static/app/views/explore/spans/spansTab.spec.tsx
@@ -14,6 +14,7 @@ import {
import {ALL_ACCESS_PROJECTS} from 'sentry/components/pageFilters/constants';
import type {DatePageFilterProps} from 'sentry/components/pageFilters/date/datePageFilter';
import {PageFiltersStore} from 'sentry/components/pageFilters/store';
+import {ConfigStore} from 'sentry/stores/configStore';
import {ProjectsStore} from 'sentry/stores/projectsStore';
import type {Project} from 'sentry/types/project';
import {trackAnalytics} from 'sentry/utils/analytics';
@@ -64,11 +65,7 @@ const invalidAttributeValidationBody: EventValidationData = {
};
describe('SpansTabContent', () => {
- const {organization, project} = initializeOrg({
- organization: {
- features: ['gen-ai-features'],
- },
- });
+ const {organization, project} = initializeOrg();
function setProjects(projects: Project[], selectedProjectIds?: number[]) {
ProjectsStore.loadInitialData(projects);
@@ -536,9 +533,14 @@ describe('SpansTabContent', () => {
describe('Ask Seer', () => {
describe('when the AI features are disabled', () => {
+ afterEach(() => {
+ ConfigStore.set('isSelfHosted', false);
+ });
+
it('does not display the Ask Seer combobox', async () => {
+ ConfigStore.set('isSelfHosted', true);
render(, {
- organization: {...organization, features: []},
+ organization,
additionalWrapper: Wrapper,
});
diff --git a/static/app/views/issueDetails/actions/index.spec.tsx b/static/app/views/issueDetails/actions/index.spec.tsx
index beef441cfaa2..c71dc5e9f3c8 100644
--- a/static/app/views/issueDetails/actions/index.spec.tsx
+++ b/static/app/views/issueDetails/actions/index.spec.tsx
@@ -113,6 +113,14 @@ describe('GroupActions', () => {
seerReposLinked: false,
},
});
+ MockApiClient.addMockResponse({
+ url: `/organizations/${organization.slug}/issues/${group.id}/autofix/`,
+ body: {autofix: null},
+ });
+ MockApiClient.addMockResponse({
+ url: `/organizations/${organization.slug}/seer/onboarding-check/`,
+ body: {isSeerConfigured: false},
+ });
MockApiClient.addMockResponse({
url: `/organizations/${organization.slug}/integrations/coding-agents/`,
body: {integrations: []},
diff --git a/static/app/views/issueDetails/groupDetails.spec.tsx b/static/app/views/issueDetails/groupDetails.spec.tsx
index 49fdec450f4b..7bb46f0b5607 100644
--- a/static/app/views/issueDetails/groupDetails.spec.tsx
+++ b/static/app/views/issueDetails/groupDetails.spec.tsx
@@ -147,6 +147,14 @@ describe('groupDetails', () => {
url: `/projects/${defaultInit.organization.slug}/${project.slug}/`,
body: project,
});
+ MockApiClient.addMockResponse({
+ url: `/organizations/${defaultInit.organization.slug}/issues/${group.id}/autofix/`,
+ body: {autofix: null},
+ });
+ MockApiClient.addMockResponse({
+ url: `/organizations/${defaultInit.organization.slug}/seer/onboarding-check/`,
+ body: {isSeerConfigured: false},
+ });
MockApiClient.addMockResponse({
url: `/organizations/${defaultInit.organization.slug}/issues/${group.id}/autofix/setup/`,
body: AutofixSetupFixture({}),
@@ -236,7 +244,6 @@ describe('groupDetails', () => {
const organization = {
...defaultInit.organization,
hideAiFeatures: false,
- features: ['gen-ai-features'],
};
const query = {
project: group.project.id,
diff --git a/static/app/views/issueDetails/groupDetailsLayout.spec.tsx b/static/app/views/issueDetails/groupDetailsLayout.spec.tsx
index 28ebcbd4f35e..e9c8f79a5a42 100644
--- a/static/app/views/issueDetails/groupDetailsLayout.spec.tsx
+++ b/static/app/views/issueDetails/groupDetailsLayout.spec.tsx
@@ -100,6 +100,14 @@ describe('GroupDetailsLayout', () => {
integration: {ok: true, reason: null},
}),
});
+ MockApiClient.addMockResponse({
+ url: `/organizations/${organization.slug}/issues/${group.id}/autofix/`,
+ body: {autofix: null},
+ });
+ MockApiClient.addMockResponse({
+ url: `/organizations/${organization.slug}/seer/onboarding-check/`,
+ body: {isSeerConfigured: false},
+ });
MockApiClient.addMockResponse({
url: '/projects/org-slug/project-slug/',
body: [project],
diff --git a/static/app/views/issueDetails/sidebar/autofixSection.spec.tsx b/static/app/views/issueDetails/sidebar/autofixSection.spec.tsx
index a5d7fa3ff558..b869d1a661c0 100644
--- a/static/app/views/issueDetails/sidebar/autofixSection.spec.tsx
+++ b/static/app/views/issueDetails/sidebar/autofixSection.spec.tsx
@@ -22,7 +22,6 @@ describe('AutofixSection', () => {
const mockProject = DetailedProjectFixture();
const organization = OrganizationFixture({
hideAiFeatures: false,
- features: ['gen-ai-features'],
});
let mockGroup: ReturnType;
@@ -71,7 +70,6 @@ describe('AutofixSection', () => {
it('renders Resources section when AI features are disabled', () => {
const customOrganization = OrganizationFixture({
hideAiFeatures: true,
- features: ['gen-ai-features'],
});
const performanceGroup: Group = {
@@ -124,7 +122,6 @@ describe('AutofixSection', () => {
it('returns null when AI features are disabled and no resources exist', () => {
const customOrganization = OrganizationFixture({
hideAiFeatures: true,
- features: ['gen-ai-features'],
});
const {container} = render(
@@ -446,7 +443,7 @@ describe('AutofixSection', () => {
it('shows org setup UI when SCM integration is missing', async () => {
const seatBasedOrg = OrganizationFixture({
hideAiFeatures: false,
- features: ['gen-ai-features', 'seat-based-seer-enabled'],
+ features: ['seat-based-seer-enabled'],
});
MockApiClient.addMockResponse({
@@ -479,7 +476,7 @@ describe('AutofixSection', () => {
it('shows project setup UI when repos are not linked', async () => {
const seatBasedOrg = OrganizationFixture({
hideAiFeatures: false,
- features: ['gen-ai-features', 'seat-based-seer-enabled'],
+ features: ['seat-based-seer-enabled'],
});
MockApiClient.addMockResponse({
@@ -512,7 +509,7 @@ describe('AutofixSection', () => {
it('skips setup UI for legacy seer plan orgs without SCM integration', async () => {
const legacyOrg = OrganizationFixture({
hideAiFeatures: false,
- features: ['gen-ai-features', 'seer-added'],
+ features: ['seer-added'],
});
MockApiClient.addMockResponse({
diff --git a/static/app/views/issueDetails/sidebar/seerDrawer.spec.tsx b/static/app/views/issueDetails/sidebar/seerDrawer.spec.tsx
index 5404147fec2c..40ebe8dd40ff 100644
--- a/static/app/views/issueDetails/sidebar/seerDrawer.spec.tsx
+++ b/static/app/views/issueDetails/sidebar/seerDrawer.spec.tsx
@@ -59,7 +59,6 @@ function makeExplorerAutofixData({
describe('SeerDrawer', () => {
const organization = OrganizationFixture({
hideAiFeatures: false,
- features: ['gen-ai-features'],
});
const mockGroup = GroupFixture();
@@ -402,7 +401,7 @@ describe('SeerDrawer', () => {
render(, {
organization: OrganizationFixture({
hideAiFeatures: false,
- features: ['gen-ai-features', feature],
+ features: [feature],
}),
});
diff --git a/static/app/views/issueDetails/sidebar/sidebar.spec.tsx b/static/app/views/issueDetails/sidebar/sidebar.spec.tsx
index a83b3cccde02..019dcd52449e 100644
--- a/static/app/views/issueDetails/sidebar/sidebar.spec.tsx
+++ b/static/app/views/issueDetails/sidebar/sidebar.spec.tsx
@@ -25,7 +25,7 @@ describe('IssueDetailsSidebar', () => {
const activityContent = 'test-note';
const issueTrackingKey = 'issue-key';
- const organization = OrganizationFixture({features: ['gen-ai-features']});
+ const organization = OrganizationFixture();
const project = ProjectFixture();
const group = GroupFixture({
activity: [
diff --git a/static/app/views/issueList/pages/inbox/index.spec.tsx b/static/app/views/issueList/pages/inbox/index.spec.tsx
index 6306796f9874..23b039c8c1b2 100644
--- a/static/app/views/issueList/pages/inbox/index.spec.tsx
+++ b/static/app/views/issueList/pages/inbox/index.spec.tsx
@@ -34,7 +34,7 @@ jest.mock('sentry/utils/useMedia');
describe('InboxPage', () => {
const organization = OrganizationFixture({
- features: ['issue-inbox', 'gen-ai-features', 'seat-based-seer-enabled'],
+ features: ['issue-inbox', 'seat-based-seer-enabled'],
});
const seerOrganization = organization;
const project = ProjectFixture({
diff --git a/static/app/views/issueList/pages/inbox/issuePreview/issuePreview.spec.tsx b/static/app/views/issueList/pages/inbox/issuePreview/issuePreview.spec.tsx
index 8e7df6413f6f..7ba66ec612f3 100644
--- a/static/app/views/issueList/pages/inbox/issuePreview/issuePreview.spec.tsx
+++ b/static/app/views/issueList/pages/inbox/issuePreview/issuePreview.spec.tsx
@@ -17,7 +17,7 @@ import {GroupStatus, ProgressState, type Group} from 'sentry/types/group';
import {IssuePreview} from './issuePreview';
describe('IssuePreview', () => {
- const organization = OrganizationFixture({features: ['gen-ai-features']});
+ const organization = OrganizationFixture();
const project = ProjectFixture({id: '1'});
const group = GroupFixture({id: '101', project, hasSeen: true});
const fixAppliedGroup = GroupFixture({
diff --git a/static/app/views/navigation/secondary/sections/issues/issuesSecondaryNavigation.spec.tsx b/static/app/views/navigation/secondary/sections/issues/issuesSecondaryNavigation.spec.tsx
index 737afd76efd6..3bcd183e4218 100644
--- a/static/app/views/navigation/secondary/sections/issues/issuesSecondaryNavigation.spec.tsx
+++ b/static/app/views/navigation/secondary/sections/issues/issuesSecondaryNavigation.spec.tsx
@@ -12,7 +12,7 @@ import type {LLMContextNodeSnapshot} from 'sentry/views/seerExplorer/contexts/ll
describe('IssuesSecondaryNavigation', () => {
const inboxCountQuery = `is:unresolved issue.progress:[fix_proposed,diagnosed,assigned,identified] assigned_or_suggested:[me,my_teams]${INBOX_AUTOFIX_CATEGORY_FILTER}`;
const organization = OrganizationFixture({
- features: ['issue-inbox', 'gen-ai-features', 'seat-based-seer-enabled'],
+ features: ['issue-inbox', 'seat-based-seer-enabled'],
});
beforeEach(() => {
@@ -73,7 +73,7 @@ describe('IssuesSecondaryNavigation', () => {
it('does not render Inbox or request its count without the inbox feature', async () => {
const request = mockInboxCount({});
const organizationWithoutAutofix = OrganizationFixture({
- features: ['gen-ai-features', 'seat-based-seer-enabled'],
+ features: ['seat-based-seer-enabled'],
});
renderNavigation(organizationWithoutAutofix);
@@ -86,12 +86,7 @@ describe('IssuesSecondaryNavigation', () => {
it('renders the Autofix Overview link when the org has seer-night-shift-ui', async () => {
mockInboxCount({});
const organizationWithOverview = OrganizationFixture({
- features: [
- 'issue-inbox',
- 'gen-ai-features',
- 'seat-based-seer-enabled',
- 'seer-night-shift-ui',
- ],
+ features: ['issue-inbox', 'seat-based-seer-enabled', 'seer-night-shift-ui'],
});
renderNavigation(organizationWithOverview);
diff --git a/static/app/views/navigation/topBar.spec.tsx b/static/app/views/navigation/topBar.spec.tsx
index 1ac5ce3676ac..488f804739a3 100644
--- a/static/app/views/navigation/topBar.spec.tsx
+++ b/static/app/views/navigation/topBar.spec.tsx
@@ -33,7 +33,7 @@ function renderTopBar(width?: number) {
render({topBar}, {
organization: OrganizationFixture({
- features: ['gen-ai-features', 'seer-explorer'],
+ features: ['seer-explorer'],
}),
});
}
diff --git a/static/app/views/seerExplorer/components/drawer/useSeerExplorerDrawer.spec.tsx b/static/app/views/seerExplorer/components/drawer/useSeerExplorerDrawer.spec.tsx
index 4b8e25785175..bc6bc1ed87a6 100644
--- a/static/app/views/seerExplorer/components/drawer/useSeerExplorerDrawer.spec.tsx
+++ b/static/app/views/seerExplorer/components/drawer/useSeerExplorerDrawer.spec.tsx
@@ -35,7 +35,7 @@ const DRAWER_LABEL = 'Seer Explorer Drawer';
const enabledOrg = OrganizationFixture({
openMembership: true,
- features: ['seer-explorer', 'gen-ai-features'],
+ features: ['seer-explorer'],
hideAiFeatures: false,
});
diff --git a/static/app/views/seerExplorer/components/seerExplorerContent.spec.tsx b/static/app/views/seerExplorer/components/seerExplorerContent.spec.tsx
index 4d365da8c574..e71e6c91ddbe 100644
--- a/static/app/views/seerExplorer/components/seerExplorerContent.spec.tsx
+++ b/static/app/views/seerExplorer/components/seerExplorerContent.spec.tsx
@@ -42,7 +42,7 @@ const defaultHookReturn: ReturnType {
const organization = OrganizationFixture({
openMembership: true,
- features: ['seer-explorer', 'gen-ai-features'],
+ features: ['seer-explorer'],
hideAiFeatures: false,
});
@@ -84,7 +84,7 @@ describe('SeerExplorerContent', () => {
it('renders thinking traces when code mode tools is enabled', async () => {
const codeModeOrganization = OrganizationFixture({
openMembership: true,
- features: ['seer-explorer', 'gen-ai-features', 'seer-explorer-code-mode-tools'],
+ features: ['seer-explorer', 'seer-explorer-code-mode-tools'],
hideAiFeatures: false,
});
@@ -972,11 +972,7 @@ describe('SeerExplorerContent', () => {
const orgWithFlag = OrganizationFixture({
openMembership: true,
hideAiFeatures: false,
- features: [
- 'seer-explorer',
- 'gen-ai-features',
- 'seer-explorer-context-engine-fe-override-ui-flag',
- ],
+ features: ['seer-explorer', 'seer-explorer-context-engine-fe-override-ui-flag'],
});
it('does not show the debug menu without any debug feature flag', async () => {
@@ -1163,7 +1159,7 @@ describe('SeerExplorerContent', () => {
it('hides the reinstall nudge when the user cannot manage integrations', async () => {
const memberOrg = OrganizationFixture({
openMembership: true,
- features: ['seer-explorer', 'gen-ai-features'],
+ features: ['seer-explorer'],
hideAiFeatures: false,
access: ['org:read', 'project:read', 'team:read', 'alerts:read'],
});
diff --git a/static/app/views/seerExplorer/components/seerExplorerHeader.spec.tsx b/static/app/views/seerExplorer/components/seerExplorerHeader.spec.tsx
index dbb233a9c932..7873a18a5725 100644
--- a/static/app/views/seerExplorer/components/seerExplorerHeader.spec.tsx
+++ b/static/app/views/seerExplorer/components/seerExplorerHeader.spec.tsx
@@ -5,7 +5,7 @@ import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
import {SeerExplorerHeader} from 'sentry/views/seerExplorer/components/seerExplorerHeader';
import {SeerExplorerSessionsProvider} from 'sentry/views/seerExplorer/seerExplorerSessionContext';
-const BASE_FEATURES = ['seer-explorer', 'gen-ai-features'];
+const BASE_FEATURES = ['seer-explorer'];
function orgWith(...extraFeatures: string[]) {
return OrganizationFixture({
diff --git a/static/app/views/seerExplorer/components/sidebar/seerExplorerSidebarLayout.spec.tsx b/static/app/views/seerExplorer/components/sidebar/seerExplorerSidebarLayout.spec.tsx
index d55f95953aaf..c9ffb646b424 100644
--- a/static/app/views/seerExplorer/components/sidebar/seerExplorerSidebarLayout.spec.tsx
+++ b/static/app/views/seerExplorer/components/sidebar/seerExplorerSidebarLayout.spec.tsx
@@ -21,7 +21,7 @@ import {
const POSITION_KEY = 'seer-explorer-sidebar-position';
-const seerFeatures = ['seer-explorer', 'gen-ai-features'];
+const seerFeatures = ['seer-explorer'];
const defaultHookReturn: ReturnType = {
sessionData: null,
diff --git a/static/app/views/seerExplorer/hooks/useSeerExplorer.spec.tsx b/static/app/views/seerExplorer/hooks/useSeerExplorer.spec.tsx
index c52556410233..8a193f72f0e3 100644
--- a/static/app/views/seerExplorer/hooks/useSeerExplorer.spec.tsx
+++ b/static/app/views/seerExplorer/hooks/useSeerExplorer.spec.tsx
@@ -31,7 +31,7 @@ describe('useSeerExplorer', () => {
});
const organization = OrganizationFixture({
- features: ['seer-explorer', 'gen-ai-features'],
+ features: ['seer-explorer'],
hideAiFeatures: false,
openMembership: true,
});
diff --git a/static/app/views/seerExplorer/hooks/useSeerExplorerPolling.spec.tsx b/static/app/views/seerExplorer/hooks/useSeerExplorerPolling.spec.tsx
index 94ddac2cdd76..4f76907864b1 100644
--- a/static/app/views/seerExplorer/hooks/useSeerExplorerPolling.spec.tsx
+++ b/static/app/views/seerExplorer/hooks/useSeerExplorerPolling.spec.tsx
@@ -6,7 +6,7 @@ import {useSeerExplorerPolling} from './useSeerExplorerPolling';
describe('useSeerExplorerPolling', () => {
const organization = OrganizationFixture({
- features: ['seer-explorer', 'gen-ai-features'],
+ features: ['seer-explorer'],
hideAiFeatures: false,
openMembership: true,
});
diff --git a/static/app/views/seerWorkflows/overview/index.spec.tsx b/static/app/views/seerWorkflows/overview/index.spec.tsx
index ab96ca3a0f83..b32f4d182898 100644
--- a/static/app/views/seerWorkflows/overview/index.spec.tsx
+++ b/static/app/views/seerWorkflows/overview/index.spec.tsx
@@ -23,6 +23,7 @@ import {
setPageFiltersStorage,
} from 'sentry/components/pageFilters/persistence';
import {PageFiltersStore} from 'sentry/components/pageFilters/store';
+import {ConfigStore} from 'sentry/stores/configStore';
import {OrganizationStore} from 'sentry/stores/organizationStore';
import {ProjectsStore} from 'sentry/stores/projectsStore';
import {TeamStore} from 'sentry/stores/teamStore';
@@ -39,7 +40,7 @@ import {useOverviewSeerDrawer} from 'sentry/views/seerWorkflows/overview/useOver
describe('AutofixOverview', () => {
const organization = OrganizationFixture({
- features: ['seer-night-shift-ui', 'gen-ai-features'],
+ features: ['seer-night-shift-ui'],
});
const basePath = `/organizations/${organization.slug}/issues/autofix/`;
@@ -374,6 +375,10 @@ describe('AutofixOverview', () => {
});
describe('Seer drawer', () => {
+ beforeEach(() => {
+ ConfigStore.set('isSelfHosted', false);
+ });
+
// Holds setup open so the drawer sits in its loading state and fires no
// downstream content requests.
function mockDrawerFor(groupId: string) {
@@ -429,7 +434,8 @@ describe('AutofixOverview', () => {
expect(groupRequest).toHaveBeenCalled();
});
- it('stays closed and clears the param when the org lacks gen-ai access', async () => {
+ it('stays closed and clears the param when self-hosted', async () => {
+ ConfigStore.set('isSelfHosted', true);
mockOverview({base: {autofix_root_cause: [rootCauseRun]}});
mockDrawerFor('2');
diff --git a/static/app/views/settings/organizationGeneralSettings/organizationSettingsForm.spec.tsx b/static/app/views/settings/organizationGeneralSettings/organizationSettingsForm.spec.tsx
index 32e68000ebc2..2257f7dfec84 100644
--- a/static/app/views/settings/organizationGeneralSettings/organizationSettingsForm.spec.tsx
+++ b/static/app/views/settings/organizationGeneralSettings/organizationSettingsForm.spec.tsx
@@ -9,6 +9,7 @@ import {
waitFor,
} from 'sentry-test/reactTestingLibrary';
+import {ConfigStore} from 'sentry/stores/configStore';
import {OrganizationStore} from 'sentry/stores/organizationStore';
import * as RegionUtils from 'sentry/utils/cells';
import {OrganizationSettingsForm} from 'sentry/views/settings/organizationGeneralSettings/organizationSettingsForm';
@@ -43,6 +44,10 @@ describe('OrganizationSettingsForm', () => {
onSave.mockReset();
});
+ afterEach(() => {
+ ConfigStore.set('isSelfHosted', false);
+ });
+
it('can change a form field', async () => {
putMock = MockApiClient.addMockResponse({
url: `/organizations/${organization.slug}/`,
@@ -183,7 +188,7 @@ describe('OrganizationSettingsForm', () => {
// initialData.hideAiFeatures = false (default) → switch starts OFF
render(
,
- {organization: {...organization, features: ['gen-ai-features']}}
+ {organization}
);
const mock = MockApiClient.addMockResponse({
url: `/organizations/${organization.slug}/`,
@@ -216,7 +221,7 @@ describe('OrganizationSettingsForm', () => {
initialData={OrganizationFixture({hideAiFeatures: true})}
onSave={onSave}
/>,
- {organization: {...organization, features: ['gen-ai-features']}}
+ {organization}
);
const mock = MockApiClient.addMockResponse({
url: `/organizations/${organization.slug}/`,
@@ -254,7 +259,7 @@ describe('OrganizationSettingsForm', () => {
{
organization: {
...organization,
- features: ['autofix', 'gen-ai-features'],
+ features: ['autofix'],
},
}
);
@@ -263,15 +268,11 @@ describe('OrganizationSettingsForm', () => {
expect(toggle).toBeEnabled();
});
- it('disables "Show Generative AI Features" toggle when feature flag is off', () => {
+ it('disables "Show Generative AI Features" toggle when self-hosted', () => {
+ ConfigStore.set('isSelfHosted', true);
render(
,
- {
- organization: {
- ...organization,
- features: [], // No gen-ai-features flag
- },
- }
+ {organization}
);
const checkbox = screen.getByRole('checkbox', {
diff --git a/static/app/views/settings/organizationGeneralSettings/organizationSettingsForm.tsx b/static/app/views/settings/organizationGeneralSettings/organizationSettingsForm.tsx
index 227e8108ce76..4a36197bcb39 100644
--- a/static/app/views/settings/organizationGeneralSettings/organizationSettingsForm.tsx
+++ b/static/app/views/settings/organizationGeneralSettings/organizationSettingsForm.tsx
@@ -437,12 +437,12 @@ export function OrganizationSettingsForm({initialData, onSave}: Props) {
});
const access = useMemo(() => new Set(organization.access), [organization]);
const hasWriteAccess = access.has('org:write');
- const hasGenAiFeatureFlag = organization.features.includes('gen-ai-features');
+ const isSeerAvailable = !ConfigStore.get('isSelfHosted');
const localityData = shouldDisplayLocalities()
? getLocalityDataFromOrganization(organization)
: null;
- const aiEnabled = hasGenAiFeatureFlag ? (initialData.hideAiFeatures ?? false) : false;
+ const aiEnabled = isSeerAvailable ? (initialData.hideAiFeatures ?? false) : false;
// Shared mutation options for most general fields
const orgMutationOptions = mutationOptions({
@@ -653,7 +653,7 @@ export function OrganizationSettingsForm({initialData, onSave}: Props) {
)}
diff --git a/static/app/views/settings/projectPerformance/projectPerformance.spec.tsx b/static/app/views/settings/projectPerformance/projectPerformance.spec.tsx
index 086b32da432b..aa0e524cbe1c 100644
--- a/static/app/views/settings/projectPerformance/projectPerformance.spec.tsx
+++ b/static/app/views/settings/projectPerformance/projectPerformance.spec.tsx
@@ -85,11 +85,7 @@ function getDetectorSlider({label, index}: {index: number; label: string}) {
describe('projectPerformance', () => {
const org = OrganizationFixture({
- features: [
- 'performance-view',
- 'performance-web-vitals-seer-suggestions',
- 'gen-ai-features',
- ],
+ features: ['performance-view', 'performance-web-vitals-seer-suggestions'],
});
const project = ProjectFixture();
const configUrl = '/projects/org-slug/project-slug/transaction-threshold/configure/';
@@ -810,7 +806,6 @@ describe('projectPerformance', () => {
features: [
'performance-view',
'performance-web-vitals-seer-suggestions',
- 'gen-ai-features',
'ai-issue-detection',
],
}),
diff --git a/static/app/views/settings/projectUserFeedback/index.spec.tsx b/static/app/views/settings/projectUserFeedback/index.spec.tsx
index 79ee7d30f606..c69ff0aed4bd 100644
--- a/static/app/views/settings/projectUserFeedback/index.spec.tsx
+++ b/static/app/views/settings/projectUserFeedback/index.spec.tsx
@@ -105,7 +105,6 @@ describe('ProjectUserFeedback', () => {
});
it('cannot toggle spam detection when the user does not have the spam feature flag', () => {
- organization.features.push('gen-ai-features');
seerSetupMock = mockSeerSetup();
render(, {
@@ -120,7 +119,6 @@ describe('ProjectUserFeedback', () => {
it('can toggle spam detection', async () => {
organization.features.push('user-feedback-spam-ingest');
- organization.features.push('gen-ai-features');
seerSetupMock = mockSeerSetup();
const detailedProject = DetailedProjectFixture(project);
diff --git a/tests/sentry/dashboards/endpoints/test_organization_dashboard_generate.py b/tests/sentry/dashboards/endpoints/test_organization_dashboard_generate.py
index bdca7ac3e484..cd6e418742a4 100644
--- a/tests/sentry/dashboards/endpoints/test_organization_dashboard_generate.py
+++ b/tests/sentry/dashboards/endpoints/test_organization_dashboard_generate.py
@@ -9,11 +9,9 @@
from sentry.dashboards.on_completion_hook import DashboardOnCompletionHook
from sentry.seer.models import SeerPermissionError
from sentry.testutils.cases import APITestCase
-from sentry.testutils.helpers.features import with_feature
@override_settings(SENTRY_SELF_HOSTED=False)
-@with_feature("organizations:gen-ai-features")
class OrganizationDashboardGenerateEndpointTest(APITestCase):
endpoint = "sentry-api-0-organization-dashboards-generate"
diff --git a/tests/sentry/integrations/slack/test_message_builder.py b/tests/sentry/integrations/slack/test_message_builder.py
index db77174a8167..558e6b067c53 100644
--- a/tests/sentry/integrations/slack/test_message_builder.py
+++ b/tests/sentry/integrations/slack/test_message_builder.py
@@ -42,7 +42,6 @@
from sentry.silo.base import SiloMode
from sentry.testutils.cases import PerformanceIssueTestCase, TestCase
from sentry.testutils.factories import EventType
-from sentry.testutils.helpers import with_feature
from sentry.testutils.helpers.datetime import before_now, freeze_time
from sentry.testutils.outbox import outbox_runner
from sentry.testutils.silo import assume_test_silo_mode
@@ -1000,7 +999,7 @@ def _has_autofix_button(self, blocks: dict[str, Any]) -> bool:
@override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.quotas.backend.check_seer_quota", return_value=True)
- @with_feature({"organizations:gen-ai-features": True})
+ @override_settings(SENTRY_SELF_HOSTED=False)
def test_autofix_button_shown_when_all_conditions_met(self, mock_quota: MagicMock) -> None:
group = self.create_group(project=self.project)
blocks = SlackIssuesMessageBuilder(group).build()
@@ -1008,7 +1007,7 @@ def test_autofix_button_shown_when_all_conditions_met(self, mock_quota: MagicMoc
@override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.quotas.backend.check_seer_quota", return_value=True)
- @with_feature({"organizations:gen-ai-features": True})
+ @override_settings(SENTRY_SELF_HOSTED=False)
def test_autofix_button_hidden_on_unfurl(self, mock_quota: MagicMock) -> None:
group = self.create_group(project=self.project)
blocks = SlackIssuesMessageBuilder(group, is_unfurl=True).build()
@@ -1016,7 +1015,7 @@ def test_autofix_button_hidden_on_unfurl(self, mock_quota: MagicMock) -> None:
@override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.quotas.backend.check_seer_quota", return_value=True)
- @with_feature({"organizations:gen-ai-features": True})
+ @override_settings(SENTRY_SELF_HOSTED=False)
def test_autofix_button_hidden_when_no_other_actions(self, mock_quota: MagicMock) -> None:
group = self.create_group(project=self.project)
blocks = SlackIssuesMessageBuilder(group, issue_details=True).build()
diff --git a/tests/sentry/integrations/slack/webhooks/events/__init__.py b/tests/sentry/integrations/slack/webhooks/events/__init__.py
index 7baa2039fc9f..acf49f3e5962 100644
--- a/tests/sentry/integrations/slack/webhooks/events/__init__.py
+++ b/tests/sentry/integrations/slack/webhooks/events/__init__.py
@@ -13,7 +13,6 @@
UNSET = object()
SEER_EXPLORER_FEATURES = {
- "organizations:gen-ai-features": True,
"organizations:seer-explorer": True,
}
diff --git a/tests/sentry/integrations/test_issues.py b/tests/sentry/integrations/test_issues.py
index 83e842e38b2c..c1f9a01bdc76 100644
--- a/tests/sentry/integrations/test_issues.py
+++ b/tests/sentry/integrations/test_issues.py
@@ -2,6 +2,7 @@
from unittest.mock import MagicMock, patch
import pytest
+from django.test import override_settings
from django.utils import timezone
from sentry.analytics.events.issue_resolved import IssueResolvedEvent
@@ -635,6 +636,7 @@ def test_status_sync_inbound_unresolve_webhook_and_sends_to_sentry_app(
assert data["installation"]["uuid"] == str(self.sentry_app_installation.uuid)
+@override_settings(SENTRY_SELF_HOSTED=False)
class IssueDefaultTest(TestCase):
def setUp(self) -> None:
event = self.store_event(
@@ -823,9 +825,7 @@ def test_feature_flag_disabled_skips_ai(self, mock_request: MagicMock) -> None:
def test_hide_ai_features_skips_ai(self, mock_request: MagicMock) -> None:
self.group.organization.update_option("sentry:hide_ai_features", True)
- with self.feature(
- ["organizations:gen-ai-features", "organizations:external-issues-ai-generate"]
- ):
+ with self.feature(["organizations:external-issues-ai-generate"]):
config = self.installation.get_create_issue_config(self.group, self.user)
title_field = next(f for f in config if f["name"] == "title")
@@ -836,9 +836,7 @@ def test_hide_ai_features_skips_ai(self, mock_request: MagicMock) -> None:
def test_ai_exception_falls_back(self, mock_request: MagicMock) -> None:
mock_request.side_effect = Exception("Connection error")
- with self.feature(
- ["organizations:gen-ai-features", "organizations:external-issues-ai-generate"]
- ):
+ with self.feature(["organizations:external-issues-ai-generate"]):
config = self.installation.get_create_issue_config(self.group, self.user)
title_field = next(f for f in config if f["name"] == "title")
diff --git a/tests/sentry/integrations/utils/test_external_issues.py b/tests/sentry/integrations/utils/test_external_issues.py
index 357adac5eedd..6922d16f8630 100644
--- a/tests/sentry/integrations/utils/test_external_issues.py
+++ b/tests/sentry/integrations/utils/test_external_issues.py
@@ -117,16 +117,15 @@ def test_feature_flag_disabled_returns_empty(self, mock_request: MagicMock) -> N
def test_hide_ai_features_returns_empty(self, mock_request: MagicMock) -> None:
self.group.organization.update_option("sentry:hide_ai_features", True)
- with self.feature(
- ["organizations:gen-ai-features", "organizations:external-issues-ai-generate"]
- ):
+ with self.feature(["organizations:external-issues-ai-generate"]):
result = maybe_generate_external_issue_details(group=self.group, user=self.user)
assert result == GeneratedExternalIssueDetails(title=None, description=None)
mock_request.assert_not_called()
+ @override_settings(SENTRY_SELF_HOSTED=True)
@patch("sentry.integrations.utils.external_issues.make_llm_generate_request")
- def test_gen_ai_features_disabled_returns_empty(self, mock_request: MagicMock) -> None:
+ def test_self_hosted_returns_empty(self, mock_request: MagicMock) -> None:
with self.feature("organizations:external-issues-ai-generate"):
result = maybe_generate_external_issue_details(group=self.group, user=self.user)
@@ -137,9 +136,7 @@ def test_gen_ai_features_disabled_returns_empty(self, mock_request: MagicMock) -
def test_exception_returns_empty(self, mock_request: MagicMock) -> None:
mock_request.side_effect = Exception("Connection error")
- with self.feature(
- ["organizations:gen-ai-features", "organizations:external-issues-ai-generate"]
- ):
+ with self.feature(["organizations:external-issues-ai-generate"]):
result = maybe_generate_external_issue_details(group=self.group, user=self.user)
assert result == GeneratedExternalIssueDetails(title=None, description=None)
@@ -152,9 +149,7 @@ def test_successful_returns_details(self, mock_request: MagicMock) -> None:
}
mock_request.return_value = mock_response
- with self.feature(
- ["organizations:gen-ai-features", "organizations:external-issues-ai-generate"]
- ):
+ with self.feature(["organizations:external-issues-ai-generate"]):
result = maybe_generate_external_issue_details(group=self.group, user=self.user)
assert result == GeneratedExternalIssueDetails(
diff --git a/tests/sentry/pr_metrics/test_emit.py b/tests/sentry/pr_metrics/test_emit.py
index 9f3cb3cf2fdb..75007a60b782 100644
--- a/tests/sentry/pr_metrics/test_emit.py
+++ b/tests/sentry/pr_metrics/test_emit.py
@@ -3,6 +3,7 @@
from unittest.mock import patch
import pytest
+from django.test import override_settings
from sentry.analytics.events.pr_metrics_events import PrCloseMetricsEvent
from sentry.models.grouplink import GroupLink
@@ -175,9 +176,9 @@ def _doc_suite(
@with_feature(
[
"organizations:pr-metrics",
- "organizations:gen-ai-features",
]
)
+@override_settings(SENTRY_SELF_HOSTED=False)
class PrMetricsEmissionTest(TestCase):
def setUp(self) -> None:
self.repo = self.create_repo(
@@ -1697,9 +1698,9 @@ def test_untracked_pr_does_not_enqueue_cleanup(
@with_feature(
[
"organizations:pr-metrics",
- "organizations:gen-ai-features",
]
)
+@override_settings(SENTRY_SELF_HOSTED=False)
class MultiOrgEmissionDedupeTest(TestCase):
"""A provider PR shared across orgs fans out to one tracked row per org; only
the canonical (run's-org) row should emit."""
@@ -1826,9 +1827,9 @@ def test_tracked_sibling_emits_when_lower_id_row_untracked(self, mock_record: An
@with_feature(
[
"organizations:pr-metrics",
- "organizations:gen-ai-features",
]
)
+@override_settings(SENTRY_SELF_HOSTED=False)
class DeduplicationKeyTest(TestCase):
"""The same provider PR, fanned out to one row per org, must build the same
opaque deduplication_key so a consumer can collapse them."""
diff --git a/tests/sentry/pr_metrics/test_webhooks.py b/tests/sentry/pr_metrics/test_webhooks.py
index 10885137b1ac..7db79315cdc5 100644
--- a/tests/sentry/pr_metrics/test_webhooks.py
+++ b/tests/sentry/pr_metrics/test_webhooks.py
@@ -392,8 +392,9 @@ def test_missing_pr_logs_unresolved_and_does_not_raise(self) -> None:
CLOSED_AT = datetime(2020, 6, 4, 10, 0, 0, tzinfo=timezone.utc)
-@with_feature(["organizations:pr-metrics", "organizations:gen-ai-features"])
+@with_feature(["organizations:pr-metrics"])
@cell_silo_test
+@override_settings(SENTRY_SELF_HOSTED=False)
class HandleWebhookForPrMetricsEmissionTest(TestCase):
def setUp(self) -> None:
self.project = self.create_project(organization=self.organization)
@@ -565,12 +566,12 @@ def test_does_nothing_when_flag_off(self, mock_record: MagicMock) -> None:
).exists()
@patch("sentry.analytics.record")
+ @override_settings(SENTRY_SELF_HOSTED=True)
def test_emits_without_seer_access(self, mock_record: MagicMock) -> None:
# Seer access is no longer required for activity tracking, so the
# commits-after-open signal is present regardless — a clean merge can
# still resolve to merged_unchanged without Seer access.
- with self.feature({"organizations:gen-ai-features": False}):
- self._call(merged=True)
+ self._call(merged=True)
assert get_event_count(mock_record, PrCloseMetricsEvent) == 1
assert (
PullRequestMetrics.objects.get(pull_request=self.pull_request).verdict
@@ -648,8 +649,9 @@ def test_missing_pr_logs_unresolved_and_does_not_emit(
assert get_event_count(mock_record, PrCloseMetricsEvent) == 0
-@with_feature(["organizations:pr-metrics", "organizations:gen-ai-features"])
+@with_feature(["organizations:pr-metrics"])
@cell_silo_test
+@override_settings(SENTRY_SELF_HOSTED=False)
class HandleWebhookForPrMetricsCooldownTest(TestCase):
"""The webhook-side scheduling of deferred emission and its cooldown claim."""
@@ -1307,9 +1309,9 @@ def test_unhandled_actions_do_not_write_activity(self) -> None:
assert not PullRequestActivity.objects.filter(pull_request=self.pr).exists()
+ @override_settings(SENTRY_SELF_HOSTED=True)
def test_activity_written_without_seer_access(self) -> None:
- with self.feature({"organizations:gen-ai-features": False}):
- self._call(action="opened")
+ self._call(action="opened")
assert PullRequestActivity.objects.filter(pull_request=self.pr).exists()
@@ -1462,9 +1464,9 @@ def test_regular_comment_has_is_review_false(self) -> None:
activity = PullRequestActivity.objects.get(pull_request=self.pr)
assert activity.payload["is_review"] is False
+ @override_settings(SENTRY_SELF_HOSTED=True)
def test_comment_written_without_seer_access(self) -> None:
- with self.feature({"organizations:gen-ai-features": False}):
- self._call()
+ self._call()
assert PullRequestActivity.objects.filter(pull_request=self.pr).exists()
@@ -1643,9 +1645,9 @@ def test_unknown_pr_number_logs_unresolved_and_does_not_raise(self) -> None:
)
assert not PullRequestActivity.objects.filter(pull_request=self.pr).exists()
+ @override_settings(SENTRY_SELF_HOSTED=True)
def test_review_written_without_seer_access(self) -> None:
- with self.feature({"organizations:gen-ai-features": False}):
- self._call()
+ self._call()
assert PullRequestActivity.objects.filter(pull_request=self.pr).exists()
@@ -1756,9 +1758,9 @@ def test_unknown_pr_number_logs_unresolved_and_does_not_raise(self) -> None:
)
assert not PullRequestActivity.objects.filter(pull_request=self.pr).exists()
+ @override_settings(SENTRY_SELF_HOSTED=True)
def test_review_comment_written_without_seer_access(self) -> None:
- with self.feature({"organizations:gen-ai-features": False}):
- self._call()
+ self._call()
assert PullRequestActivity.objects.filter(pull_request=self.pr).exists()
@@ -1864,9 +1866,9 @@ def test_unknown_pr_number_logs_unresolved_and_does_not_raise(self) -> None:
)
assert not PullRequestActivity.objects.filter(pull_request=self.pr).exists()
+ @override_settings(SENTRY_SELF_HOSTED=True)
def test_thread_event_written_without_seer_access(self) -> None:
- with self.feature({"organizations:gen-ai-features": False}):
- self._call()
+ self._call()
assert PullRequestActivity.objects.filter(pull_request=self.pr).exists()
@@ -2059,9 +2061,9 @@ def test_check_suite_flag_off_skips(self) -> None:
assert not PullRequestActivity.objects.filter(pull_request=self.pr).exists()
+ @override_settings(SENTRY_SELF_HOSTED=True)
def test_check_suite_written_without_seer_access(self) -> None:
- with self.feature({"organizations:gen-ai-features": False}):
- self._call_suite()
+ self._call_suite()
assert PullRequestActivity.objects.filter(pull_request=self.pr).exists()
@@ -2173,9 +2175,9 @@ def test_check_suite_judge_in_progress_skips(self) -> None:
assert not PullRequestActivity.objects.filter(pull_request=self.pr).exists()
-@override_settings(SENTRY_SELF_HOSTED=False)
-@with_feature(["organizations:pr-metrics", "organizations:gen-ai-features"])
+@with_feature(["organizations:pr-metrics"])
@cell_silo_test
+@override_settings(SENTRY_SELF_HOSTED=False)
class HandleWebhookForPrMetricsJudgeForwardTest(TestCase):
"""The needs-judge branch: claim the sentinel and forward."""
@@ -2310,12 +2312,12 @@ def test_untracked_pr_is_not_forwarded(
@patch("sentry.pr_metrics.tasks.forward_pr_to_seer_task.delay")
@patch("sentry.analytics.record")
+ @override_settings(SENTRY_SELF_HOSTED=True)
def test_no_seer_access_skips_judge(
self, mock_record: MagicMock, mock_delay: MagicMock
) -> None:
# Without Seer access the judge path is not eligible regardless of attribution.
- with self.feature({"organizations:gen-ai-features": False}):
- self._call()
+ self._call()
assert mock_delay.call_count == 0
assert PullRequestMetrics.objects.get(pull_request=self.pull_request).verdict is None
@@ -2340,17 +2342,17 @@ def test_ineligible_attribution_emits_merged_with_iteration(
@patch(f"{MODULE}.forward_pr_to_seer_task.delay")
@patch("sentry.analytics.record")
+ @override_settings(SENTRY_SELF_HOSTED=True)
def test_ineligible_attribution_emits_without_seer_access(
self, mock_record: MagicMock, mock_delay: MagicMock
) -> None:
# The fallback never talks to Seer, so an org's Seer-access consent gate
- # (gen-ai-features / hide_ai_features) must not block it — only the actual
- # forward-to-Seer branch, reached for judge-eligible attribution, needs it.
+ # must not block it — only the actual forward-to-Seer branch, reached for
+ # judge-eligible attribution, needs it.
PullRequestAttribution.objects.filter(pull_request=self.pull_request).update(
signal_type=PullRequestAttributionSignalType.MCP
)
- with self.feature({"organizations:gen-ai-features": False}):
- self._call()
+ self._call()
assert mock_delay.call_count == 0
assert PullRequestMetrics.objects.get(pull_request=self.pull_request).verdict == (
"merged_with_iteration"
@@ -2398,10 +2400,10 @@ def test_ineligible_attribution_stays_unemitted_when_indeterminate(
@with_feature(
[
"organizations:pr-metrics",
- "organizations:gen-ai-features",
]
)
@cell_silo_test
+@override_settings(SENTRY_SELF_HOSTED=False)
class HandleDelegatedAgentDetectionTest(TestCase):
def setUp(self) -> None:
self.project = self.create_project(organization=self.organization)
diff --git a/tests/sentry/seer/agent/test_agent_client.py b/tests/sentry/seer/agent/test_agent_client.py
index 19d58a2f30c1..7b8d5d49599c 100644
--- a/tests/sentry/seer/agent/test_agent_client.py
+++ b/tests/sentry/seer/agent/test_agent_client.py
@@ -1629,7 +1629,7 @@ def test_flush_true_dispatch_failure_marks_failed_and_raises(
assert run.seer_run_state_id is None
def test_access_gate_blocks_dispatch(self) -> None:
- # No gen-ai-features -> client construction raises before any run is created.
+ # No Seer access -> client construction raises before any run is created.
with pytest.raises(SeerPermissionError):
SeerAgentClient(self.organization, self.user)
assert not SeerRun.objects.filter(organization=self.organization).exists()
diff --git a/tests/sentry/seer/agent/test_client_utils.py b/tests/sentry/seer/agent/test_client_utils.py
index 1adaa63cd637..ab9bc1fd6e59 100644
--- a/tests/sentry/seer/agent/test_client_utils.py
+++ b/tests/sentry/seer/agent/test_client_utils.py
@@ -33,37 +33,29 @@ def setUp(self) -> None:
self.org.flags.allow_joinleave = True
self.org.save()
- def test_gen_ai_features_disabled(self) -> None:
+ @override_settings(SENTRY_SELF_HOSTED=True)
+ def test_denied_on_self_hosted(self) -> None:
result = has_seer_agent_access_with_detail(self.org, self.user)
- assert result == (False, "Feature flag not enabled")
+ assert result == (False, "Seer is not available on this installation.")
def test_hide_ai_features_option_set(self) -> None:
self.org.update_option("sentry:hide_ai_features", True)
- with self.feature("organizations:gen-ai-features"):
- result = has_seer_agent_access_with_detail(self.org, self.user)
+ result = has_seer_agent_access_with_detail(self.org, self.user)
assert result == (False, "AI features are disabled for this organization.")
def test_no_explorer_flag_enabled(self) -> None:
- with self.feature("organizations:gen-ai-features"):
- result = has_seer_agent_access_with_detail(self.org, self.user)
+ result = has_seer_agent_access_with_detail(self.org, self.user)
assert result == (False, "Feature flag not enabled")
def test_seer_explorer_flag_enabled(self) -> None:
- with self.feature(
- {"organizations:gen-ai-features": True, "organizations:seer-explorer": True}
- ):
+ with self.feature({"organizations:seer-explorer": True}):
result = has_seer_agent_access_with_detail(self.org, self.user)
assert result == (True, None)
def test_allow_joinleave_disabled(self) -> None:
self.org.flags.allow_joinleave = False
self.org.save()
- with self.feature(
- {
- "organizations:gen-ai-features": True,
- "organizations:seer-explorer": True,
- }
- ):
+ with self.feature({"organizations:seer-explorer": True}):
result = has_seer_agent_access_with_detail(self.org, self.user)
assert result == (
False,
diff --git a/tests/sentry/seer/autofix/test_autofix_agent.py b/tests/sentry/seer/autofix/test_autofix_agent.py
index a44ccccddd05..fceb73d269b6 100644
--- a/tests/sentry/seer/autofix/test_autofix_agent.py
+++ b/tests/sentry/seer/autofix/test_autofix_agent.py
@@ -1822,7 +1822,7 @@ def setUp(self):
super().setUp()
self.group = self.create_group(project=self.project)
- def _push(self, mock_post, features="organizations:gen-ai-features", **kwargs):
+ def _push(self, mock_post, features=None, **kwargs):
"""Push with a minimal run state and return the payload sent to Seer."""
mock_post.return_value = MagicMock(status=200)
state = SeerRunState(
@@ -1834,7 +1834,7 @@ def _push(self, mock_post, features="organizations:gen-ai-features", **kwargs):
metadata={"group_id": self.group.id},
)
- with self.feature(features):
+ with self.feature(features or {}):
trigger_push_changes(
group=self.group,
run_id=123,
@@ -1878,7 +1878,6 @@ def test_opens_as_draft_when_review_request_enabled(self, mock_post):
payload = self._push(
mock_post,
features={
- "organizations:gen-ai-features": True,
"organizations:autofix-pr-iteration-review-request": True,
},
)
diff --git a/tests/sentry/seer/autofix/test_autofix_utils.py b/tests/sentry/seer/autofix/test_autofix_utils.py
index ecfaf5a7414d..e4a13da0bb47 100644
--- a/tests/sentry/seer/autofix/test_autofix_utils.py
+++ b/tests/sentry/seer/autofix/test_autofix_utils.py
@@ -2,6 +2,7 @@
from unittest.mock import Mock, patch
import pytest
+from django.test import override_settings
from sentry.constants import (
SEER_AUTOMATED_RUN_STOPPING_POINT_DEFAULT,
@@ -128,6 +129,7 @@ def test_autofix_state_validate_parses_nested_structures(self) -> None:
assert state.coding_agents["agent-1"].status == CodingAgentStatus.COMPLETED
+@override_settings(SENTRY_SELF_HOSTED=False)
class TestIsIssueEligibleForSeerAutomation(TestCase):
"""Test the is_issue_eligible_for_seer_automation function."""
@@ -138,75 +140,68 @@ def setUp(self) -> None:
self.group = self.create_group(project=self.project)
def test_returns_false_for_unsupported_issue_categories(self) -> None:
- """Test returns False for unsupported issue categories like REPLAY and FEEDBACK."""
- from sentry.issues.grouptype import FeedbackGroup, ReplayRageClickType
+ """Test returns False for unsupported issue categories like FEEDBACK."""
+ from sentry.issues.grouptype import FeedbackGroup
- # Create groups with unsupported categories
- replay_group = self.create_group(project=self.project, type=ReplayRageClickType.type_id)
feedback_group = self.create_group(project=self.project, type=FeedbackGroup.type_id)
- assert is_issue_eligible_for_seer_automation(replay_group) is False
assert is_issue_eligible_for_seer_automation(feedback_group) is False
def test_returns_true_for_supported_issue_categories(self) -> None:
"""Test returns True for supported issue categories when all conditions are met."""
- with self.feature("organizations:gen-ai-features"):
- with patch("sentry.quotas.backend.check_seer_quota") as mock_budget:
- mock_budget.return_value = True
- self.project.update_option("sentry:seer_scanner_automation", True)
+ with patch("sentry.quotas.backend.check_seer_quota") as mock_budget:
+ mock_budget.return_value = True
+ self.project.update_option("sentry:seer_scanner_automation", True)
- # Test supported categories - using default error group
- result = is_issue_eligible_for_seer_automation(self.group)
+ # Test supported categories - using default error group
+ result = is_issue_eligible_for_seer_automation(self.group)
- assert result is True
+ assert result is True
- def test_returns_false_when_gen_ai_features_not_enabled(self) -> None:
- """Test returns False when organizations:gen-ai-features feature flag is not enabled."""
+ @override_settings(SENTRY_SELF_HOSTED=True)
+ def test_returns_false_when_self_hosted(self) -> None:
+ """Test returns False when Seer is not available (self-hosted)."""
result = is_issue_eligible_for_seer_automation(self.group)
assert result is False
def test_returns_false_when_ai_features_hidden(self) -> None:
"""Test returns False when sentry:hide_ai_features option is enabled."""
- with self.feature("organizations:gen-ai-features"):
- self.organization.update_option("sentry:hide_ai_features", True)
- result = is_issue_eligible_for_seer_automation(self.group)
- assert result is False
+ self.organization.update_option("sentry:hide_ai_features", True)
+ result = is_issue_eligible_for_seer_automation(self.group)
+ assert result is False
def test_returns_false_when_scanner_automation_disabled_and_not_always_trigger(self) -> None:
"""Test returns False when scanner automation is disabled and issue type doesn't always trigger."""
- with self.feature("organizations:gen-ai-features"):
- self.project.update_option("sentry:seer_scanner_automation", False)
- result = is_issue_eligible_for_seer_automation(self.group)
- assert result is False
+ self.project.update_option("sentry:seer_scanner_automation", False)
+ result = is_issue_eligible_for_seer_automation(self.group)
+ assert result is False
@patch("sentry.quotas.backend.check_seer_quota")
def test_returns_false_when_no_budget_available(self, mock_has_budget):
"""Test returns False when organization has no available budget for scanner."""
- with self.feature("organizations:gen-ai-features"):
- self.project.update_option("sentry:seer_scanner_automation", True)
- mock_has_budget.return_value = False
+ self.project.update_option("sentry:seer_scanner_automation", True)
+ mock_has_budget.return_value = False
- result = is_issue_eligible_for_seer_automation(self.group)
+ result = is_issue_eligible_for_seer_automation(self.group)
- assert result is False
- mock_has_budget.assert_called_once_with(
- org_id=self.organization.id, data_category=DataCategory.SEER_SCANNER
- )
+ assert result is False
+ mock_has_budget.assert_called_once_with(
+ org_id=self.organization.id, data_category=DataCategory.SEER_SCANNER
+ )
@patch("sentry.quotas.backend.check_seer_quota")
def test_returns_true_when_all_conditions_met(self, mock_has_budget):
"""Test returns True when all eligibility conditions are met."""
- with self.feature("organizations:gen-ai-features"):
- self.project.update_option("sentry:seer_scanner_automation", True)
+ self.project.update_option("sentry:seer_scanner_automation", True)
- mock_has_budget.return_value = True
+ mock_has_budget.return_value = True
- result = is_issue_eligible_for_seer_automation(self.group)
+ result = is_issue_eligible_for_seer_automation(self.group)
- assert result is True
- mock_has_budget.assert_called_once_with(
- org_id=self.organization.id, data_category=DataCategory.SEER_SCANNER
- )
+ assert result is True
+ mock_has_budget.assert_called_once_with(
+ org_id=self.organization.id, data_category=DataCategory.SEER_SCANNER
+ )
@patch("sentry.quotas.backend.check_seer_quota")
def test_returns_true_when_issue_type_always_triggers(
@@ -214,17 +209,16 @@ def test_returns_true_when_issue_type_always_triggers(
mock_has_budget,
):
"""Test returns True when issue type has always_trigger_seer_automation even if scanner automation is disabled."""
- with self.feature("organizations:gen-ai-features"):
- # Disable scanner automation
- self.project.update_option("sentry:seer_scanner_automation", False)
+ # Disable scanner automation
+ self.project.update_option("sentry:seer_scanner_automation", False)
- mock_has_budget.return_value = True
+ mock_has_budget.return_value = True
- # Mock the group's issue_type to always trigger
- with patch.object(self.group.issue_type, "always_trigger_seer_automation", True):
- result = is_issue_eligible_for_seer_automation(self.group)
+ # Mock the group's issue_type to always trigger
+ with patch.object(self.group.issue_type, "always_trigger_seer_automation", True):
+ result = is_issue_eligible_for_seer_automation(self.group)
- assert result is True
+ assert result is True
class TestIsSeerSeatBasedTierEnabled(TestCase):
diff --git a/tests/sentry/seer/autofix/test_issue_summary.py b/tests/sentry/seer/autofix/test_issue_summary.py
index 50fb72261bd3..4a3817e5ae33 100644
--- a/tests/sentry/seer/autofix/test_issue_summary.py
+++ b/tests/sentry/seer/autofix/test_issue_summary.py
@@ -31,7 +31,6 @@
from sentry.testutils.cases import APITestCase, SnubaTestCase, TestCase
from sentry.testutils.helpers.action_log import capture_action_log
from sentry.testutils.helpers.datetime import before_now
-from sentry.testutils.helpers.features import with_feature
from sentry.testutils.skips import requires_snuba
from sentry.types.activity import ActivityType
from sentry.utils.cache import cache
@@ -72,7 +71,6 @@ def test_post_process_kickoff_creates_system_activity(
@override_settings(SENTRY_SELF_HOSTED=False)
-@with_feature("organizations:gen-ai-features")
class IssueSummaryTest(APITestCase, SnubaTestCase, OccurrenceTestMixin):
def setUp(self) -> None:
super().setUp()
@@ -882,7 +880,7 @@ def test_stopping_point_mapping(self, score, expected):
@override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.seer.autofix.issue_summary.is_seer_seat_based_tier_enabled", return_value=True)
-@with_feature({"organizations:gen-ai-features": True})
+@override_settings(SENTRY_SELF_HOSTED=False)
class TestRunAutomationStoppingPoint(APITestCase, SnubaTestCase):
def setUp(self) -> None:
super().setUp()
@@ -950,8 +948,7 @@ def test_without_seat_based_tier(
mock_seat_based_tier,
):
mock_seat_based_tier.return_value = False
- with self.feature({"organizations:gen-ai-features": True}):
- run_automation(self.group, self.user, self.event, SeerAutomationSource.POST_PROCESS)
+ run_automation(self.group, self.user, self.event, SeerAutomationSource.POST_PROCESS)
mock_trigger.assert_called_once()
assert mock_trigger.call_args[1]["stopping_point"] == AutofixStoppingPoint.CODE_CHANGES
@@ -1032,7 +1029,7 @@ def test_upper_bound_combinations(self, fixability, user_pref, expected):
@override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.seer.autofix.issue_summary.is_seer_seat_based_tier_enabled", return_value=True)
-@with_feature({"organizations:gen-ai-features": True})
+@override_settings(SENTRY_SELF_HOSTED=False)
class TestRunAutomationWithUpperBound(APITestCase, SnubaTestCase):
def setUp(self) -> None:
super().setUp()
@@ -1107,7 +1104,7 @@ def test_fixability_limits_permissive_user_preference(
assert mock_trigger.call_args[1]["stopping_point"] == AutofixStoppingPoint.ROOT_CAUSE
-@with_feature("organizations:gen-ai-features")
+@override_settings(SENTRY_SELF_HOSTED=False)
class TestGetAndUpdateGroupFixabilityScore(APITestCase, SnubaTestCase):
def setUp(self) -> None:
super().setUp()
@@ -1249,7 +1246,6 @@ def test_no_summary_in_cache_calls_seer_without_summary(self, mock_request):
@override_settings(SENTRY_SELF_HOSTED=False)
-@with_feature("organizations:gen-ai-features")
class TestIsGroupEligibleForAutomation(APITestCase, SnubaTestCase):
def setUp(self) -> None:
super().setUp()
@@ -1268,10 +1264,10 @@ def test_returns_true_when_all_checks_pass(self, mock_fixability, mock_quota, mo
assert is_group_eligible_for_automation(self.group) is True
+ @override_settings(SENTRY_SELF_HOSTED=True)
@patch("sentry.seer.autofix.issue_summary.get_and_update_group_fixability_score")
def test_returns_false_without_seer_access(self, mock_fixability):
- with self.feature({"organizations:gen-ai-features": False}):
- assert is_group_eligible_for_automation(self.group) is False
+ assert is_group_eligible_for_automation(self.group) is False
mock_fixability.assert_not_called()
@@ -1339,7 +1335,7 @@ def test_returns_false_when_rate_limited(self, mock_fixability, mock_quota, mock
@patch("sentry.seer.autofix.issue_summary.is_seer_seat_based_tier_enabled", return_value=True)
-@with_feature({"organizations:gen-ai-features": True})
+@override_settings(SENTRY_SELF_HOSTED=False)
class TestGetAutomationStoppingPoint(TestCase):
def setUp(self) -> None:
super().setUp()
diff --git a/tests/sentry/seer/code_review/test_preflight.py b/tests/sentry/seer/code_review/test_preflight.py
index 5b3f3905a30e..a4c734c887c9 100644
--- a/tests/sentry/seer/code_review/test_preflight.py
+++ b/tests/sentry/seer/code_review/test_preflight.py
@@ -47,14 +47,14 @@ def _create_service(
# Legal AI consent tests
# -------------------------------------------------------------------------
- def test_denied_when_gen_ai_feature_flag_disabled(self) -> None:
+ @override_settings(SENTRY_SELF_HOSTED=True)
+ def test_denied_when_self_hosted(self) -> None:
service = self._create_service()
result = service.check()
assert result.allowed is False
assert result.denial_reason == PreflightDenialReason.ORG_LEGAL_AI_CONSENT_NOT_GRANTED
- @with_feature("organizations:gen-ai-features")
def test_denied_when_hide_ai_features_enabled(self) -> None:
self.organization.update_option("sentry:hide_ai_features", True)
@@ -68,7 +68,7 @@ def test_denied_when_hide_ai_features_enabled(self) -> None:
# Org feature enablement tests
# -------------------------------------------------------------------------
- @with_feature(["organizations:gen-ai-features", "organizations:code-review-beta"])
+ @with_feature(["organizations:code-review-beta"])
def test_denied_when_beta_org_has_no_repo_settings(self) -> None:
service = self._create_service()
result = service.check()
@@ -76,7 +76,7 @@ def test_denied_when_beta_org_has_no_repo_settings(self) -> None:
assert result.allowed is False
assert result.denial_reason == PreflightDenialReason.REPO_CODE_REVIEW_DISABLED
- @with_feature(["organizations:gen-ai-features", "organizations:code-review-beta"])
+ @with_feature(["organizations:code-review-beta"])
def test_allowed_when_beta_org_has_repo_settings_enabled(self) -> None:
self.create_repository_settings(
repository=self.repo,
@@ -95,7 +95,6 @@ def test_allowed_when_beta_org_has_repo_settings_enabled(self) -> None:
assert result.allowed is True
assert result.denial_reason is None
- @with_feature("organizations:gen-ai-features")
def test_denied_when_org_has_no_seer_feature_flags(self) -> None:
service = self._create_service()
result = service.check()
@@ -107,7 +106,7 @@ def test_denied_when_org_has_no_seer_feature_flags(self) -> None:
# Seer-added (legacy usage-based) org tests - not eligible for code review
# -------------------------------------------------------------------------
- @with_feature(["organizations:gen-ai-features", "organizations:seer-added"])
+ @with_feature(["organizations:seer-added"])
def test_denied_when_seer_added_only_org_not_eligible(self) -> None:
service = self._create_service()
result = service.check()
@@ -115,7 +114,7 @@ def test_denied_when_seer_added_only_org_not_eligible(self) -> None:
assert result.allowed is False
assert result.denial_reason == PreflightDenialReason.ORG_NOT_ELIGIBLE_FOR_CODE_REVIEW
- @with_feature(["organizations:gen-ai-features", "organizations:seer-added"])
+ @with_feature(["organizations:seer-added"])
def test_denied_when_seer_added_only_org_even_with_repo_settings_enabled(self) -> None:
self.create_repository_settings(
repository=self.repo,
@@ -136,7 +135,6 @@ def test_denied_when_seer_added_only_org_even_with_repo_settings_enabled(self) -
@with_feature(
[
- "organizations:gen-ai-features",
"organizations:seer-added",
"organizations:code-review-beta",
]
@@ -164,7 +162,7 @@ def test_allowed_when_seer_added_and_code_review_beta_org_has_repo_settings(self
# Seat-based org tests
# -------------------------------------------------------------------------
- @with_feature(["organizations:gen-ai-features", "organizations:seat-based-seer-enabled"])
+ @with_feature(["organizations:seat-based-seer-enabled"])
def test_denied_when_seat_based_org_has_no_repo_settings(self) -> None:
service = self._create_service()
result = service.check()
@@ -172,7 +170,7 @@ def test_denied_when_seat_based_org_has_no_repo_settings(self) -> None:
assert result.allowed is False
assert result.denial_reason == PreflightDenialReason.REPO_CODE_REVIEW_DISABLED
- @with_feature(["organizations:gen-ai-features", "organizations:seat-based-seer-enabled"])
+ @with_feature(["organizations:seat-based-seer-enabled"])
def test_denied_when_seat_based_org_has_repo_settings_disabled(self) -> None:
self.create_repository_settings(
repository=self.repo,
@@ -185,7 +183,7 @@ def test_denied_when_seat_based_org_has_repo_settings_disabled(self) -> None:
assert result.denial_reason == PreflightDenialReason.REPO_CODE_REVIEW_DISABLED
@patch("sentry.quotas.backend.check_seer_quota")
- @with_feature(["organizations:gen-ai-features", "organizations:seat-based-seer-enabled"])
+ @with_feature(["organizations:seat-based-seer-enabled"])
def test_allowed_when_seat_based_org_has_repo_settings_enabled(
self, mock_check_quota: MagicMock
) -> None:
@@ -215,7 +213,7 @@ def test_allowed_when_seat_based_org_has_repo_settings_enabled(
# -------------------------------------------------------------------------
@patch("sentry.quotas.backend.check_seer_quota")
- @with_feature(["organizations:gen-ai-features", "organizations:seat-based-seer-enabled"])
+ @with_feature(["organizations:seat-based-seer-enabled"])
def test_returns_repo_settings_when_allowed(self, mock_check_quota: MagicMock) -> None:
mock_check_quota.return_value = True
@@ -246,7 +244,6 @@ def test_returns_repo_settings_when_allowed(self, mock_check_quota: MagicMock) -
@patch("sentry.quotas.backend.check_seer_quota")
@with_feature(
[
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seat-based-seer-enabled",
]
@@ -282,7 +279,7 @@ def test_uses_repo_settings_when_has_both_code_review_beta_and_seat_based_featur
# Billing tests
# -------------------------------------------------------------------------
- @with_feature(["organizations:gen-ai-features", "organizations:seat-based-seer-enabled"])
+ @with_feature(["organizations:seat-based-seer-enabled"])
def test_denied_when_missing_integration(self) -> None:
self.create_repository_settings(
repository=self.repo,
@@ -305,7 +302,7 @@ def test_denied_when_missing_integration(self) -> None:
"sentry.seer.code_review.preflight.instance_hostname",
side_effect=InstanceHostnameError("missing"),
)
- @with_feature(["organizations:gen-ai-features", "organizations:seat-based-seer-enabled"])
+ @with_feature(["organizations:seat-based-seer-enabled"])
def test_denied_when_integration_missing_hostname(
self, mock_hostname: MagicMock, mock_capture: MagicMock
) -> None:
@@ -326,7 +323,7 @@ def test_denied_when_integration_missing_hostname(
assert result.denial_reason == PreflightDenialReason.BILLING_MISSING_CONTRIBUTOR_INFO
mock_capture.assert_called_once()
- @with_feature(["organizations:gen-ai-features", "organizations:seat-based-seer-enabled"])
+ @with_feature(["organizations:seat-based-seer-enabled"])
def test_denied_when_missing_external_identifier(self) -> None:
self.create_repository_settings(
repository=self.repo,
@@ -344,7 +341,7 @@ def test_denied_when_missing_external_identifier(self) -> None:
assert result.allowed is False
assert result.denial_reason == PreflightDenialReason.BILLING_MISSING_CONTRIBUTOR_INFO
- @with_feature(["organizations:gen-ai-features", "organizations:seat-based-seer-enabled"])
+ @with_feature(["organizations:seat-based-seer-enabled"])
def test_denied_when_contributor_does_not_exist(self) -> None:
self.create_repository_settings(
repository=self.repo,
@@ -358,7 +355,7 @@ def test_denied_when_contributor_does_not_exist(self) -> None:
assert result.denial_reason == PreflightDenialReason.ORG_CONTRIBUTOR_NOT_FOUND
@patch("sentry.quotas.backend.check_seer_quota")
- @with_feature(["organizations:gen-ai-features", "organizations:seat-based-seer-enabled"])
+ @with_feature(["organizations:seat-based-seer-enabled"])
def test_denied_when_quota_check_fails(self, mock_check_quota: MagicMock) -> None:
mock_check_quota.return_value = False
@@ -382,7 +379,6 @@ def test_denied_when_quota_check_fails(self, mock_check_quota: MagicMock) -> Non
@patch("sentry.quotas.backend.check_seer_quota")
@with_feature(
[
- "organizations:gen-ai-features",
"organizations:seat-based-seer-enabled",
"organizations:code-review-beta",
]
@@ -417,7 +413,7 @@ def test_checks_seats_when_both_code_review_beta_and_seat_based_features_are_ena
mock_check_quota.assert_called_once()
@patch("sentry.quotas.backend.check_seer_quota")
- @with_feature(["organizations:gen-ai-features", "organizations:seat-based-seer-enabled"])
+ @with_feature(["organizations:seat-based-seer-enabled"])
def test_denied_when_pr_author_is_excluded(self, mock_check_quota: MagicMock) -> None:
mock_check_quota.return_value = True
diff --git a/tests/sentry/seer/code_review/webhooks/test_merge_request.py b/tests/sentry/seer/code_review/webhooks/test_merge_request.py
index ab38411ec0e5..3860b4062f89 100644
--- a/tests/sentry/seer/code_review/webhooks/test_merge_request.py
+++ b/tests/sentry/seer/code_review/webhooks/test_merge_request.py
@@ -77,7 +77,6 @@ class _MergeRequestHandlerTestBase(GitLabTestCase):
"""
CODE_REVIEW_FEATURES = {
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -163,7 +162,6 @@ def _call_handler(self, event: dict[str, Any]) -> None:
class MergeRequestEventWebhookTest(_MergeRequestHandlerTestBase):
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -181,7 +179,6 @@ def test_open_uses_review_request_endpoint(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -217,7 +214,7 @@ def test_validation_failure_is_captured_and_review_dropped(self) -> None:
)
self.mock_seer.assert_not_called()
- @with_feature({"organizations:gen-ai-features", "organizations:code-review-beta"})
+ @with_feature({"organizations:code-review-beta"})
def test_skips_when_gitlab_flag_disabled(self) -> None:
# The GitLab MR handler is gated on organizations:seer-gitlab-support,
# independent of the other code-review flags.
@@ -231,7 +228,6 @@ def test_skips_when_gitlab_flag_disabled(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -249,7 +245,6 @@ def test_close_uses_pr_closed_endpoint(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -267,7 +262,6 @@ def test_merge_uses_pr_closed_endpoint(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -285,7 +279,6 @@ def test_update_uses_review_request_endpoint(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -302,7 +295,6 @@ def test_update_without_oldrev_is_skipped(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -320,7 +312,6 @@ def test_update_with_unrelated_changes_is_skipped(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -342,7 +333,6 @@ def test_undraft_update_uses_review_request_endpoint(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -361,7 +351,6 @@ def test_undraft_update_via_work_in_progress_uses_review_request_endpoint(self)
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -379,7 +368,6 @@ def test_undraft_update_trigger_is_ready_for_review(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -397,7 +385,6 @@ def test_undraft_update_filtered_when_ready_trigger_disabled(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -413,7 +400,6 @@ def test_skips_draft_mr(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -429,7 +415,6 @@ def test_skips_work_in_progress_mr(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -445,7 +430,6 @@ def test_close_still_sends_for_draft_mr(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -461,7 +445,6 @@ def test_skips_unsupported_action(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -477,7 +460,6 @@ def test_skips_unknown_action(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -494,7 +476,6 @@ def test_skips_missing_action(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -524,7 +505,6 @@ def test_skips_when_code_review_not_enabled(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -541,7 +521,6 @@ def test_skips_missing_last_commit(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -557,7 +536,6 @@ def test_open_filtered_when_trigger_disabled(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -573,7 +551,6 @@ def test_update_filtered_when_trigger_disabled(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -589,7 +566,6 @@ def test_close_filtered_when_no_triggers_configured(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -605,7 +581,6 @@ def test_close_sends_when_triggers_configured(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -624,7 +599,6 @@ def test_payload_contains_correct_pr_id(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -643,7 +617,6 @@ def test_payload_contains_gitlab_provider(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -664,7 +637,6 @@ def test_payload_owner_and_name_use_path_not_display_name(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -683,7 +655,6 @@ def test_payload_owner_and_name_handle_subgroups(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -702,7 +673,6 @@ def test_payload_is_private_true_for_private_project(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -721,7 +691,6 @@ def test_payload_is_private_true_for_internal_project(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -740,7 +709,6 @@ def test_payload_is_private_false_for_public_project(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -759,7 +727,6 @@ def test_payload_is_private_none_when_visibility_absent(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -778,7 +745,6 @@ def test_payload_trigger_on_ready_for_review_for_open(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -797,7 +763,6 @@ def test_payload_trigger_on_new_commit_for_update(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -816,7 +781,6 @@ def test_payload_contains_trigger_user_from_event(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -847,7 +811,6 @@ def test_open_with_gitlab_space_utc_timestamp_enqueues(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -871,7 +834,6 @@ def test_open_with_iso8601_timestamp_still_enqueues(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -899,7 +861,6 @@ def test_open_with_unparseable_timestamp_captures_and_falls_back(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -916,7 +877,6 @@ def test_duplicate_delivery_within_window_skipped(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -944,7 +904,6 @@ def test_duplicate_delivery_after_ttl_processes_again(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1039,7 +998,6 @@ def _call_handler(self, event: dict[str, Any]) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1058,7 +1016,6 @@ def test_sentry_review_comment_schedules_seer_task(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1076,7 +1033,6 @@ def test_payload_trigger_is_on_command_phrase(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1095,7 +1051,6 @@ def test_payload_contains_trigger_comment_id_and_type(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1113,7 +1068,6 @@ def test_payload_trigger_user_is_commenter(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1134,7 +1088,6 @@ def test_eyes_reaction_added_to_note(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1151,7 +1104,6 @@ def test_non_review_command_is_ignored(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1168,7 +1120,6 @@ def test_issue_note_is_ignored(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1185,7 +1136,6 @@ def test_non_create_action_is_ignored(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1202,7 +1152,6 @@ def test_sentry_review_case_insensitive(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1230,7 +1179,6 @@ def test_skips_when_feature_flag_disabled(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1255,7 +1203,6 @@ def test_skips_when_integration_is_none(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1273,7 +1220,6 @@ def test_reaction_failure_does_not_block_seer_task(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1320,7 +1266,6 @@ class MergeRequestReactionTest(_MergeRequestHandlerTestBase):
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1338,7 +1283,6 @@ def test_eyes_reaction_added_for_open_action(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1365,7 +1309,6 @@ def test_stale_hooray_reaction_deleted_before_eyes_added(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1391,7 +1334,6 @@ def test_other_users_hooray_reaction_not_deleted(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1410,7 +1352,6 @@ def test_no_reaction_for_close_action(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1429,7 +1370,6 @@ def test_no_reaction_for_merge_action(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
@@ -1449,7 +1389,6 @@ def test_reaction_failure_does_not_block_seer_task(self) -> None:
@with_feature(
{
- "organizations:gen-ai-features",
"organizations:code-review-beta",
"organizations:seer-gitlab-support",
}
diff --git a/tests/sentry/seer/code_review/webhooks/test_pull_request.py b/tests/sentry/seer/code_review/webhooks/test_pull_request.py
index 096f5aaf88b5..4ccc05f60cc4 100644
--- a/tests/sentry/seer/code_review/webhooks/test_pull_request.py
+++ b/tests/sentry/seer/code_review/webhooks/test_pull_request.py
@@ -248,7 +248,7 @@ def test_pull_request_closed_uses_pr_closed_endpoint(self) -> None:
def test_pull_request_opened_filtered_when_trigger_disabled_post_ga(self) -> None:
triggers = [CodeReviewTrigger.ON_NEW_COMMIT]
- features = {"organizations:gen-ai-features", "organizations:seat-based-seer-enabled"}
+ features = {"organizations:seat-based-seer-enabled"}
with (
self.code_review_setup(triggers=triggers, features=features),
self.tasks(),
@@ -263,7 +263,7 @@ def test_pull_request_opened_filtered_when_trigger_disabled_post_ga(self) -> Non
def test_pull_request_synchronize_filtered_when_trigger_disabled_post_ga(self) -> None:
triggers = [CodeReviewTrigger.ON_READY_FOR_REVIEW]
- features = {"organizations:gen-ai-features", "organizations:seat-based-seer-enabled"}
+ features = {"organizations:seat-based-seer-enabled"}
with (
self.code_review_setup(triggers=triggers, features=features),
self.tasks(),
@@ -278,7 +278,7 @@ def test_pull_request_synchronize_filtered_when_trigger_disabled_post_ga(self) -
def test_pull_request_ready_for_review_filtered_when_trigger_disabled_post_ga(self) -> None:
triggers = [CodeReviewTrigger.ON_NEW_COMMIT]
- features = {"organizations:gen-ai-features", "organizations:seat-based-seer-enabled"}
+ features = {"organizations:seat-based-seer-enabled"}
with (
self.code_review_setup(triggers=triggers, features=features),
self.tasks(),
@@ -301,7 +301,7 @@ def test_pull_request_closed_filtered_when_no_triggers_configured_post_ga(self)
helper skips RepositorySettings creation when triggers=[], which would cause the preflight
to deny the request before reaching the handler under test.
"""
- features = {"organizations:gen-ai-features", "organizations:seat-based-seer-enabled"}
+ features = {"organizations:seat-based-seer-enabled"}
with self.feature(features), self.tasks():
event = orjson.loads(PULL_REQUEST_OPENED_EVENT_EXAMPLE)
event["action"] = "closed"
@@ -320,7 +320,7 @@ def test_pull_request_closed_filtered_when_no_triggers_configured_post_ga(self)
def test_pull_request_closed_not_filtered_when_triggers_configured_post_ga(self) -> None:
"""Test that closed action reaches Seer when at least one trigger is configured."""
triggers: list[CodeReviewTrigger] = [CodeReviewTrigger.ON_READY_FOR_REVIEW]
- features = {"organizations:gen-ai-features", "organizations:seat-based-seer-enabled"}
+ features = {"organizations:seat-based-seer-enabled"}
with (
self.code_review_setup(triggers=triggers, features=features),
self.tasks(),
@@ -335,7 +335,7 @@ def test_pull_request_closed_not_filtered_when_triggers_configured_post_ga(self)
def test_pull_request_opened_works_when_trigger_enabled_post_ga(self) -> None:
triggers = [CodeReviewTrigger.ON_READY_FOR_REVIEW]
- features = {"organizations:gen-ai-features", "organizations:seat-based-seer-enabled"}
+ features = {"organizations:seat-based-seer-enabled"}
with (
self.code_review_setup(triggers=triggers, features=features),
self.tasks(),
@@ -350,7 +350,7 @@ def test_pull_request_opened_works_when_trigger_enabled_post_ga(self) -> None:
def test_pull_request_ready_for_review_works_when_trigger_enabled_post_ga(self) -> None:
triggers = [CodeReviewTrigger.ON_READY_FOR_REVIEW]
- features = {"organizations:gen-ai-features", "organizations:seat-based-seer-enabled"}
+ features = {"organizations:seat-based-seer-enabled"}
with (
self.code_review_setup(triggers=triggers, features=features),
self.tasks(),
diff --git a/tests/sentry/seer/endpoints/test_group_ai_autofix.py b/tests/sentry/seer/endpoints/test_group_ai_autofix.py
index d61c5c4c703e..537aad588260 100644
--- a/tests/sentry/seer/endpoints/test_group_ai_autofix.py
+++ b/tests/sentry/seer/endpoints/test_group_ai_autofix.py
@@ -60,7 +60,6 @@ def _user_context_length_calls(mock_distribution: Mock) -> list:
@override_settings(SENTRY_SELF_HOSTED=False)
-@with_feature("organizations:gen-ai-features")
class GroupAutofixEndpointTest(APITestCase, SnubaTestCase):
def _get_url(self, group_id: int) -> str:
return f"/api/0/organizations/{self.organization.slug}/issues/{group_id}/autofix/"
@@ -1487,7 +1486,7 @@ def test_open_pr_coding_disabled(self):
assert response.status_code == 403, response.data
-@with_feature("organizations:gen-ai-features")
+@override_settings(SENTRY_SELF_HOSTED=False)
class GroupAutofixConditionalGetTest(APITestCase):
def _get_url(self, group_id: int) -> str:
return f"/api/0/organizations/{self.organization.slug}/issues/{group_id}/autofix/"
diff --git a/tests/sentry/seer/endpoints/test_group_ai_summary.py b/tests/sentry/seer/endpoints/test_group_ai_summary.py
index 7dcb07ba1862..2f7cded4e72d 100644
--- a/tests/sentry/seer/endpoints/test_group_ai_summary.py
+++ b/tests/sentry/seer/endpoints/test_group_ai_summary.py
@@ -1,14 +1,15 @@
from unittest.mock import ANY, MagicMock, patch
+from django.test import override_settings
+
from sentry.seer.autofix.constants import SeerAutomationSource
from sentry.testutils.cases import APITestCase, SnubaTestCase
-from sentry.testutils.helpers.features import with_feature
from sentry.testutils.skips import requires_snuba
pytestmark = [requires_snuba]
-@with_feature("organizations:gen-ai-features")
+@override_settings(SENTRY_SELF_HOSTED=False)
class GroupAiSummaryEndpointTest(APITestCase, SnubaTestCase):
def setUp(self) -> None:
super().setUp()
diff --git a/tests/sentry/seer/endpoints/test_group_autofix_repos.py b/tests/sentry/seer/endpoints/test_group_autofix_repos.py
index 676f9ddafe66..699aa466f553 100644
--- a/tests/sentry/seer/endpoints/test_group_autofix_repos.py
+++ b/tests/sentry/seer/endpoints/test_group_autofix_repos.py
@@ -3,11 +3,9 @@
from django.test import override_settings
from sentry.testutils.cases import APITestCase, SnubaTestCase
-from sentry.testutils.helpers.features import with_feature
@override_settings(SENTRY_SELF_HOSTED=False)
-@with_feature("organizations:gen-ai-features")
class GroupAutofixReposEndpointTest(APITestCase, SnubaTestCase):
def setUp(self) -> None:
super().setUp()
diff --git a/tests/sentry/seer/endpoints/test_group_autofix_setup_check.py b/tests/sentry/seer/endpoints/test_group_autofix_setup_check.py
index 83d4accd8cfc..0a5667e0f480 100644
--- a/tests/sentry/seer/endpoints/test_group_autofix_setup_check.py
+++ b/tests/sentry/seer/endpoints/test_group_autofix_setup_check.py
@@ -1,5 +1,7 @@
from unittest.mock import MagicMock, patch
+from django.test import override_settings
+
from sentry.integrations.types import IntegrationProviderSlug
from sentry.models.repository import Repository
from sentry.seer.autofix.constants import AutofixAutomationTuningSettings
@@ -8,7 +10,6 @@
)
from sentry.silo.base import SiloMode
from sentry.testutils.cases import APITestCase, SnubaTestCase, TestCase
-from sentry.testutils.helpers.features import with_feature
from sentry.testutils.silo import assume_test_silo_mode
from sentry.utils.cache import cache
@@ -92,7 +93,7 @@ def test_unsupported_gitlab_integration(self) -> None:
assert result == "integration_missing"
-@with_feature("organizations:gen-ai-features")
+@override_settings(SENTRY_SELF_HOSTED=False)
class GroupAIAutofixEndpointSuccessTest(APITestCase, SnubaTestCase):
def setUp(self) -> None:
super().setUp()
@@ -274,7 +275,7 @@ def test_missing_integration(self) -> None:
assert response.data["seerReposLinked"] is False
-@with_feature("organizations:gen-ai-features")
+@override_settings(SENTRY_SELF_HOSTED=False)
class GroupAIAutofixSetupFreeCohortTest(APITestCase, SnubaTestCase):
"""Tests for free cohort org behavior in the /autofix/setup/ endpoint."""
diff --git a/tests/sentry/seer/endpoints/test_organization_agent_token.py b/tests/sentry/seer/endpoints/test_organization_agent_token.py
index 62ed3ad9c38c..36ce55977707 100644
--- a/tests/sentry/seer/endpoints/test_organization_agent_token.py
+++ b/tests/sentry/seer/endpoints/test_organization_agent_token.py
@@ -800,7 +800,7 @@ def test_end_to_end_read_allowed_write_denied(self) -> None:
@pytest.mark.sentry_metrics
@pytest.mark.seer_agent_token_matrix
@requires_snuba
-@override_settings(SEER_API_SHARED_SECRET=SECRET)
+@override_settings(SEER_API_SHARED_SECRET=SECRET, SENTRY_SELF_HOSTED=False)
class AgentTokenPublicGetMatrixTest(APITestCase):
"""Differential, full-stack authentication coverage for the public API.
@@ -823,6 +823,11 @@ class AgentTokenPublicGetMatrixTest(APITestCase):
def setUp(self) -> None:
super().setUp()
+ rate_limit_patcher = patch(
+ "sentry.middleware.ratelimit.get_rate_limit_value", return_value=None
+ )
+ rate_limit_patcher.start()
+ self.addCleanup(rate_limit_patcher.stop)
self.owner = self.create_user()
self.org = self.create_organization(owner=self.owner)
self.team = self.create_team(organization=self.org)
@@ -1301,7 +1306,6 @@ def _feature_flags(
"ExternalUserDetailsEndpoint": "organizations:integrations-codeowners",
"ExternalUserEndpoint": "organizations:integrations-codeowners",
"EventAttachmentDetailsEndpoint": "organizations:event-attachments",
- "GroupAutofixEndpoint": "organizations:gen-ai-features",
"GroupIntegrationDetailsEndpoint": "organizations:integrations-issue-basic",
"OrganizationEventsEndpoint": "organizations:discover-basic",
"OrganizationGroupSearchViewsEndpoint": "organizations:issue-views",
diff --git a/tests/sentry/seer/endpoints/test_organization_seer_agent_chat.py b/tests/sentry/seer/endpoints/test_organization_seer_agent_chat.py
index 2e55022b7a75..b53859e3b003 100644
--- a/tests/sentry/seer/endpoints/test_organization_seer_agent_chat.py
+++ b/tests/sentry/seer/endpoints/test_organization_seer_agent_chat.py
@@ -22,7 +22,6 @@
@with_feature("organizations:seer-explorer")
@override_settings(SENTRY_SELF_HOSTED=False)
-@with_feature("organizations:gen-ai-features")
class OrganizationSeerAgentChatEndpointTest(APITestCase):
def setUp(self) -> None:
super().setUp()
@@ -550,14 +549,10 @@ def test_new_run_denied_without_seer_explorer_flag(self) -> None:
assert response.status_code == 403
+ @override_settings(SENTRY_SELF_HOSTED=True)
def test_get_denied_without_seer_access(self) -> None:
"""GET should be denied when the org has neither seer-explorer nor base Seer access."""
- with self.feature(
- {
- "organizations:seer-explorer": False,
- "organizations:gen-ai-features": False,
- }
- ):
+ with self.feature({"organizations:seer-explorer": False}):
response = self.client.get(self.url)
assert response.status_code == 403
@@ -674,7 +669,6 @@ def test_outbox_path_flush_error_marks_failed_and_raises(
@with_feature("organizations:seer-explorer")
@override_settings(SENTRY_SELF_HOSTED=False)
-@with_feature("organizations:gen-ai-features")
class OrganizationSeerAgentChatContextEngineTest(APITestCase):
"""End-to-end tests verifying is_context_engine_enabled reaches make_agent_chat_request."""
diff --git a/tests/sentry/seer/endpoints/test_organization_seer_agent_update.py b/tests/sentry/seer/endpoints/test_organization_seer_agent_update.py
index c116aa056dbd..2166f00f766f 100644
--- a/tests/sentry/seer/endpoints/test_organization_seer_agent_update.py
+++ b/tests/sentry/seer/endpoints/test_organization_seer_agent_update.py
@@ -1,6 +1,7 @@
from unittest.mock import MagicMock, patch
import orjson
+from django.test import override_settings
from rest_framework import status
from sentry.integrations.types import ExternalProviders
@@ -11,7 +12,7 @@
@with_feature("organizations:seer-explorer")
-@with_feature("organizations:gen-ai-features")
+@override_settings(SENTRY_SELF_HOSTED=False)
class TestOrganizationSeerAgentUpdate(APITestCase):
def setUp(self) -> None:
super().setUp()
@@ -257,7 +258,7 @@ def test_explorer_update_feature_flag_disabled(self, mock_has_access: MagicMock)
@with_feature("organizations:seer-explorer")
-@with_feature("organizations:gen-ai-features")
+@override_settings(SENTRY_SELF_HOSTED=False)
class TestOrganizationSeerAgentUpdateCodingDisabled(APITestCase):
def setUp(self) -> None:
super().setUp()
@@ -305,7 +306,7 @@ def test_non_coding_payload_allowed_when_coding_disabled(
@with_feature("organizations:seer-explorer")
-@with_feature("organizations:gen-ai-features")
+@override_settings(SENTRY_SELF_HOSTED=False)
class TestOrganizationSeerAgentUpdateCommitAuthor(APITestCase):
def setUp(self) -> None:
super().setUp()
diff --git a/tests/sentry/seer/endpoints/test_organization_seer_runs.py b/tests/sentry/seer/endpoints/test_organization_seer_runs.py
index f3481585c3da..008bfafe4ff7 100644
--- a/tests/sentry/seer/endpoints/test_organization_seer_runs.py
+++ b/tests/sentry/seer/endpoints/test_organization_seer_runs.py
@@ -13,7 +13,7 @@
@override_settings(SENTRY_SELF_HOSTED=False)
@with_feature("organizations:seer-explorer")
-@with_feature("organizations:gen-ai-features")
+@override_settings(SENTRY_SELF_HOSTED=False)
class OrganizationSeerRunsEndpointTest(APITestCase):
endpoint = "sentry-api-0-organization-seer-runs"
diff --git a/tests/sentry/seer/endpoints/test_organization_seer_workflows.py b/tests/sentry/seer/endpoints/test_organization_seer_workflows.py
index dbd859b00b4f..4fe951c39b84 100644
--- a/tests/sentry/seer/endpoints/test_organization_seer_workflows.py
+++ b/tests/sentry/seer/endpoints/test_organization_seer_workflows.py
@@ -420,15 +420,14 @@ def test_history_hides_runs_outside_user_access(self) -> None:
def create_agent_workflow(
self, strategy: SeerWorkflowStrategy, feature_id: str
) -> SeerWorkflowRun:
- with self.feature("organizations:gen-ai-features"):
- return create_workflow_run(
- SeerAgentClient(self.organization, self.user),
- strategy=strategy,
- feature_id=feature_id,
- title="Test workflow",
- payload={},
- extras={"project_ids": [], "results": []},
- )
+ return create_workflow_run(
+ SeerAgentClient(self.organization, self.user),
+ strategy=strategy,
+ feature_id=feature_id,
+ title="Test workflow",
+ payload={},
+ extras={"project_ids": [], "results": []},
+ )
@override_settings(SENTRY_SELF_HOSTED=False)
@@ -514,11 +513,9 @@ def test_requires_feature_and_seer_access(self) -> None:
self.get_error_response(
self.organization.slug, strategy="duplicate_monitors", status_code=404
)
- with self.feature(
- {
- "organizations:seer-workflows-monitor-cleanup": True,
- "organizations:gen-ai-features": False,
- }
+ with (
+ override_settings(SENTRY_SELF_HOSTED=True),
+ self.feature("organizations:seer-workflows-monitor-cleanup"),
):
response = self.get_error_response(
self.organization.slug, strategy="duplicate_monitors", status_code=403
@@ -527,9 +524,7 @@ def test_requires_feature_and_seer_access(self) -> None:
limit.assert_not_called()
limit.return_value = True
- with self.feature(
- ["organizations:seer-workflows-monitor-cleanup", "organizations:gen-ai-features"]
- ):
+ with self.feature("organizations:seer-workflows-monitor-cleanup"):
self.get_error_response(
self.organization.slug, strategy="duplicate_monitors", status_code=429
)
@@ -553,9 +548,7 @@ def test_callback_for_deleted_user_marks_run_failed(self) -> None:
assert agent_run.extras["error"] == "The triggering user no longer exists."
def trigger(self):
- with self.feature(
- ["organizations:seer-workflows-monitor-cleanup", "organizations:gen-ai-features"]
- ):
+ with self.feature("organizations:seer-workflows-monitor-cleanup"):
response = self.get_success_response(
self.organization.slug, strategy="duplicate_monitors", status_code=202
)
diff --git a/tests/sentry/seer/endpoints/test_search_agent_start.py b/tests/sentry/seer/endpoints/test_search_agent_start.py
index 733ca4984205..39e0cf94592f 100644
--- a/tests/sentry/seer/endpoints/test_search_agent_start.py
+++ b/tests/sentry/seer/endpoints/test_search_agent_start.py
@@ -2,6 +2,7 @@
from unittest.mock import MagicMock, Mock, patch
import pytest
+from django.test import override_settings
from rest_framework import status
from sentry.seer.endpoints.search_agent_start import send_search_agent_start_request
@@ -106,7 +107,7 @@ def test_flag_options_are_sent_to_seer(self, mock_request: Mock) -> None:
@with_feature("organizations:gen-ai-search-agent-translate")
-@with_feature("organizations:gen-ai-features")
+@override_settings(SENTRY_SELF_HOSTED=False)
class SearchAgentStartEndpointTest(APITestCase):
def setUp(self) -> None:
super().setUp()
diff --git a/tests/sentry/seer/endpoints/test_search_agent_state.py b/tests/sentry/seer/endpoints/test_search_agent_state.py
index 55b378f0dffd..e35bc823f55f 100644
--- a/tests/sentry/seer/endpoints/test_search_agent_state.py
+++ b/tests/sentry/seer/endpoints/test_search_agent_state.py
@@ -6,8 +6,7 @@
from sentry.testutils.cases import APITestCase
-@override_settings(SENTRY_SELF_HOSTED=False)
-@override_settings(SEER_AUTOFIX_URL="https://seer.example.com")
+@override_settings(SEER_AUTOFIX_URL="https://seer.example.com", SENTRY_SELF_HOSTED=False)
class SearchAgentStateEndpointTest(APITestCase):
endpoint = "sentry-api-0-search-agent-state"
@@ -16,7 +15,6 @@ def setUp(self) -> None:
self.login_as(self.user)
self.features = {
"organizations:gen-ai-search-agent-translate": True,
- "organizations:gen-ai-features": True,
}
@patch("sentry.seer.endpoints.search_agent_state.make_search_agent_state_request")
diff --git a/tests/sentry/seer/endpoints/test_trace_explorer_ai_query.py b/tests/sentry/seer/endpoints/test_trace_explorer_ai_query.py
index f860fbeac320..c46981c1d3b3 100644
--- a/tests/sentry/seer/endpoints/test_trace_explorer_ai_query.py
+++ b/tests/sentry/seer/endpoints/test_trace_explorer_ai_query.py
@@ -1,12 +1,12 @@
from unittest.mock import patch
+from django.test import override_settings
from rest_framework import status
from sentry.testutils.cases import APITestCase
-from sentry.testutils.helpers.features import with_feature
-@with_feature("organizations:gen-ai-features")
+@override_settings(SENTRY_SELF_HOSTED=False)
class TraceExplorerAIQueryTest(APITestCase):
def setUp(self) -> None:
super().setUp()
diff --git a/tests/sentry/seer/endpoints/test_trace_explorer_ai_setup.py b/tests/sentry/seer/endpoints/test_trace_explorer_ai_setup.py
index 6fddfd355b86..29a2b4fb4478 100644
--- a/tests/sentry/seer/endpoints/test_trace_explorer_ai_setup.py
+++ b/tests/sentry/seer/endpoints/test_trace_explorer_ai_setup.py
@@ -1,7 +1,8 @@
from unittest.mock import patch
+from django.test import override_settings
+
from sentry.testutils.cases import APITestCase
-from sentry.testutils.helpers.features import with_feature
class TraceExplorerAISetupTest(APITestCase):
@@ -9,7 +10,7 @@ class TraceExplorerAISetupTest(APITestCase):
method = "post"
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.seer.endpoints.trace_explorer_ai_setup.fire_setup_request")
def test_simple(self, mock_fire_setup_request):
self.login_as(self.user)
@@ -30,7 +31,7 @@ def test_simple(self, mock_fire_setup_request):
},
)
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.seer.endpoints.trace_explorer_ai_setup.fire_setup_request")
def test_rejects_project_from_other_org(self, mock_fire_setup_request):
"""Test that requesting projects from another org returns 403"""
@@ -48,7 +49,7 @@ def test_rejects_project_from_other_org(self, mock_fire_setup_request):
assert response.data == {"detail": "You do not have permission to perform this action."}
mock_fire_setup_request.assert_not_called()
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.seer.endpoints.trace_explorer_ai_setup.fire_setup_request")
def test_rejects_nonexistent_project(self, mock_fire_setup_request):
"""Test that requesting non-existent project returns same error as inaccessible project"""
@@ -63,7 +64,7 @@ def test_rejects_nonexistent_project(self, mock_fire_setup_request):
assert response.data == {"detail": "You do not have permission to perform this action."}
mock_fire_setup_request.assert_not_called()
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.seer.endpoints.trace_explorer_ai_setup.fire_setup_request")
def test_empty_projects_still_calls_seer(self, mock_fire_setup_request):
"""Test that empty project list is handled"""
@@ -85,7 +86,8 @@ def test_empty_projects_still_calls_seer(self, mock_fire_setup_request):
},
)
- def test_requires_feature_flag(self) -> None:
+ @override_settings(SENTRY_SELF_HOSTED=True)
+ def test_denied_on_self_hosted(self) -> None:
self.login_as(self.user)
response = self.get_error_response(
@@ -96,7 +98,7 @@ def test_requires_feature_flag(self) -> None:
assert response.data == {"detail": "Organization does not have access to this feature"}
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.seer.endpoints.trace_explorer_ai_setup.fire_setup_request")
def test_invalid_project_id_returns_400(self, mock_fire_setup_request):
"""Test that non-integer project_id returns 400"""
@@ -111,7 +113,7 @@ def test_invalid_project_id_returns_400(self, mock_fire_setup_request):
assert response.data["detail"] == "Invalid project_id value"
mock_fire_setup_request.assert_not_called()
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.seer.endpoints.trace_explorer_ai_setup.fire_setup_request")
def test_negative_project_id_returns_400(self, mock_fire_setup_request):
"""Test that negative project_id (like -1 sentinel) returns 400"""
@@ -126,7 +128,7 @@ def test_negative_project_id_returns_400(self, mock_fire_setup_request):
assert response.data["detail"] == "Invalid project_id value"
mock_fire_setup_request.assert_not_called()
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
def test_requires_authentication(self) -> None:
response = self.get_error_response(
self.organization.slug,
diff --git a/tests/sentry/seer/endpoints/test_trace_explorer_ai_translate_agentic.py b/tests/sentry/seer/endpoints/test_trace_explorer_ai_translate_agentic.py
index db3a5f4f1001..c1cc2cdea0e0 100644
--- a/tests/sentry/seer/endpoints/test_trace_explorer_ai_translate_agentic.py
+++ b/tests/sentry/seer/endpoints/test_trace_explorer_ai_translate_agentic.py
@@ -1,5 +1,6 @@
from unittest.mock import MagicMock, patch
+from django.test import override_settings
from rest_framework import status
from sentry.testutils.cases import APITestCase
@@ -7,7 +8,7 @@
@with_feature("organizations:seer-explorer")
-@with_feature("organizations:gen-ai-features")
+@override_settings(SENTRY_SELF_HOSTED=False)
class SearchAgentTranslateEndpointTest(APITestCase):
def setUp(self) -> None:
super().setUp()
diff --git a/tests/sentry/seer/entrypoints/slack/test_tasks.py b/tests/sentry/seer/entrypoints/slack/test_tasks.py
index fd3a5ec2bc9c..fbe44e81e576 100644
--- a/tests/sentry/seer/entrypoints/slack/test_tasks.py
+++ b/tests/sentry/seer/entrypoints/slack/test_tasks.py
@@ -41,7 +41,6 @@
_SEER_SLACK_FEATURES = {
- "organizations:gen-ai-features": True,
"organizations:seer-explorer": True,
}
@@ -931,13 +930,13 @@ def test_unlinked_identity_records_halt(
@patch("sentry.integrations.utils.metrics.EventLifecycle.record_event")
@patch("sentry.analytics.record")
+ @override_settings(SENTRY_SELF_HOSTED=True)
def test_no_agent_access_records_halt(
self,
mock_record,
mock_lifecycle_record,
):
- with self.feature({"organizations:gen-ai-features": False}):
- process_reaction_for_slack(**self.defaults)
+ process_reaction_for_slack(**self.defaults)
assert_not_analytics_event(mock_record, SlackSeerAgentFeedback)
assert_halt_metric(mock_lifecycle_record, ProcessReactionHaltReason.NO_AGENT_ACCESS)
diff --git a/tests/sentry/seer/entrypoints/test_operator.py b/tests/sentry/seer/entrypoints/test_operator.py
index 31389abc0c5b..68bed6bf06c4 100644
--- a/tests/sentry/seer/entrypoints/test_operator.py
+++ b/tests/sentry/seer/entrypoints/test_operator.py
@@ -513,37 +513,23 @@ def test_process_autofix_updates_skips_entrypoint_without_access(
)
def test_can_trigger_autofix_returns_false_without_seer_access(self) -> None:
+ self.organization.update_option("sentry:hide_ai_features", True)
assert SeerAutofixOperator.can_trigger_autofix(group=self.group) is False
@patch("sentry.quotas.backend.check_seer_quota", return_value=True)
def test_can_trigger_autofix_returns_true_when_all_conditions_met(self, mock_quota):
- with self.feature(
- {
- "organizations:gen-ai-features": True,
- }
- ):
- assert SeerAutofixOperator.can_trigger_autofix(group=self.group) is True
+ assert SeerAutofixOperator.can_trigger_autofix(group=self.group) is True
@patch("sentry.quotas.backend.check_seer_quota", return_value=True)
def test_can_trigger_autofix_returns_false_for_ineligible_category(self, mock_quota):
from sentry.issues.grouptype import FeedbackGroup
feedback_group = self.create_group(project=self.project, type=FeedbackGroup.type_id)
- with self.feature(
- {
- "organizations:gen-ai-features": True,
- }
- ):
- assert SeerAutofixOperator.can_trigger_autofix(group=feedback_group) is False
+ assert SeerAutofixOperator.can_trigger_autofix(group=feedback_group) is False
@patch("sentry.quotas.backend.check_seer_quota", return_value=False)
def test_can_trigger_autofix_returns_false_without_quota(self, mock_quota):
- with self.feature(
- {
- "organizations:gen-ai-features": True,
- }
- ):
- assert SeerAutofixOperator.can_trigger_autofix(group=self.group) is False
+ assert SeerAutofixOperator.can_trigger_autofix(group=self.group) is False
@patch.object(SeerAutofixOperator, "has_access", return_value=True)
def test_seer_event_creates_activity_rca_completed(self, _mock_has_access):
@@ -955,7 +941,6 @@ def test_has_access_with_seer_agent(self):
with (
self.feature(
{
- "organizations:gen-ai-features": True,
"organizations:seer-explorer": True,
}
),
@@ -978,9 +963,9 @@ def test_has_access_with_seer_agent(self):
entrypoint_key=MockNoAccessEntrypoint.key,
)
+ @override_settings(SENTRY_SELF_HOSTED=True)
def test_has_access_without_seer_agent(self):
- with self.feature({"organizations:gen-ai-features": False}):
- assert not SeerAgentOperator.has_access(organization=self.organization)
+ assert not SeerAgentOperator.has_access(organization=self.organization)
class TestSeerOperatorCompletionHook(TestCase):
diff --git a/tests/sentry/seer/test_seer_setup.py b/tests/sentry/seer/test_seer_setup.py
index 728da784d028..5bd9d49d211f 100644
--- a/tests/sentry/seer/test_seer_setup.py
+++ b/tests/sentry/seer/test_seer_setup.py
@@ -6,7 +6,6 @@
is_seer_available,
)
from sentry.testutils.cases import TestCase
-from sentry.testutils.helpers.features import with_feature
class IsSeerAvailableTest(TestCase):
@@ -21,18 +20,11 @@ def test_unavailable_on_self_hosted(self) -> None:
@override_settings(SENTRY_SELF_HOSTED=False)
class HasSeerAccessTest(TestCase):
- @with_feature("organizations:gen-ai-features")
def test_allowed(self) -> None:
org = self.create_organization()
assert has_seer_access(org) is True
assert has_seer_access_with_detail(org) == (True, None)
- def test_denied_without_flag(self) -> None:
- org = self.create_organization()
- assert has_seer_access(org) is False
- assert has_seer_access_with_detail(org) == (False, "Feature flag not enabled")
-
- @with_feature("organizations:gen-ai-features")
def test_denied_when_hidden(self) -> None:
org = self.create_organization()
org.update_option("sentry:hide_ai_features", True)
@@ -42,7 +34,6 @@ def test_denied_when_hidden(self) -> None:
"AI features are disabled for this organization.",
)
- @with_feature("organizations:gen-ai-features")
@override_settings(SENTRY_SELF_HOSTED=True)
def test_denied_on_self_hosted(self) -> None:
org = self.create_organization()
diff --git a/tests/sentry/seer/workflows/test_runs.py b/tests/sentry/seer/workflows/test_runs.py
index 961482ef0963..d88ff7e90921 100644
--- a/tests/sentry/seer/workflows/test_runs.py
+++ b/tests/sentry/seer/workflows/test_runs.py
@@ -156,15 +156,14 @@ def test_dispatch_failure_is_reported_without_overwriting_a_completed_result(sel
assert status["error"] is None
def create_run(self) -> SeerRun:
- with self.feature("organizations:gen-ai-features"):
- workflow = create_workflow_run(
- SeerAgentClient(self.organization, self.user),
- strategy=SeerWorkflowStrategy.AGENTIC_TRIAGE,
- feature_id="test_workflow",
- title="Test workflow",
- payload={},
- extras={"summary": None},
- )
+ workflow = create_workflow_run(
+ SeerAgentClient(self.organization, self.user),
+ strategy=SeerWorkflowStrategy.AGENTIC_TRIAGE,
+ feature_id="test_workflow",
+ title="Test workflow",
+ payload={},
+ extras={"summary": None},
+ )
run = workflow.executions.get().seer_run
assert run is not None
return run
diff --git a/tests/sentry/tasks/seer/test_explorer_index.py b/tests/sentry/tasks/seer/test_explorer_index.py
index 2e8aaa5cd7b7..a77cad09d15a 100644
--- a/tests/sentry/tasks/seer/test_explorer_index.py
+++ b/tests/sentry/tasks/seer/test_explorer_index.py
@@ -57,7 +57,6 @@ def test_returns_projects_with_feature_flag(self) -> None:
with self.feature(
{
- "organizations:gen-ai-features": [org1.slug, org2.slug],
"organizations:seer-explorer-index": [org1.slug, org2.slug],
}
):
@@ -95,7 +94,6 @@ def test_excludes_inactive_projects(self) -> None:
with self.feature(
{
- "organizations:gen-ai-features": [org.slug],
"organizations:seer-explorer-index": [org.slug],
}
):
@@ -121,7 +119,6 @@ def test_excludes_projects_with_hide_ai_features(self) -> None:
with self.feature(
{
- "organizations:gen-ai-features": [org.slug],
"organizations:seer-explorer-index": [org.slug],
}
):
@@ -140,7 +137,6 @@ def test_excludes_projects_without_seer_acknowledgement(self) -> None:
with self.feature(
{
- "organizations:gen-ai-features": [org.slug],
"organizations:seer-explorer-index": [org.slug],
}
):
@@ -163,7 +159,6 @@ def test_excludes_projects_without_transactions(self) -> None:
with self.feature(
{
- "organizations:gen-ai-features": [org.slug],
"organizations:seer-explorer-index": [org.slug],
}
):
@@ -194,7 +189,6 @@ def test_includes_only_projects_matching_hour_shard(self) -> None:
with self.feature(
{
- "organizations:gen-ai-features": [org.slug],
"organizations:seer-explorer-index": [org.slug],
}
):
@@ -224,12 +218,7 @@ def test_excludes_projects_without_seer_billing_plan(self) -> None:
feature="seer_autofix_setup_acknowledged",
)
- with self.feature(
- {
- "organizations:gen-ai-features": [org.slug],
- }
- ):
- result = list(get_seer_explorer_enabled_projects())
+ result = list(get_seer_explorer_enabled_projects())
assert len(result) == 0
assert project.id not in [p[0] for p in result]
@@ -251,7 +240,6 @@ def test_includes_projects_with_legacy_seer_plan(self) -> None:
with self.feature(
{
- "organizations:gen-ai-features": [org.slug],
"organizations:seer-added": [org.slug],
}
):
@@ -278,7 +266,6 @@ def test_includes_projects_with_seat_based_plan(self) -> None:
with self.feature(
{
- "organizations:gen-ai-features": [org.slug],
"organizations:seat-based-seer-enabled": [org.slug],
}
):
@@ -289,6 +276,7 @@ def test_includes_projects_with_seat_based_plan(self) -> None:
assert project.id in project_ids
+@override_settings(SENTRY_SELF_HOSTED=False)
@django_db_all
class TestScheduleExplorerIndex(TestCase):
def test_skips_when_killswitch_enabled(self) -> None:
@@ -316,7 +304,6 @@ def test_schedules_projects(self) -> None:
with self.feature(
{
- "organizations:gen-ai-features": [org.slug],
"organizations:seer-explorer-index": [org.slug],
}
):
diff --git a/tests/sentry/tasks/seer/test_night_shift.py b/tests/sentry/tasks/seer/test_night_shift.py
index 92403dd56963..38802db566b9 100644
--- a/tests/sentry/tasks/seer/test_night_shift.py
+++ b/tests/sentry/tasks/seer/test_night_shift.py
@@ -224,7 +224,6 @@ def test_dispatches_eligible_orgs(self) -> None:
self.feature(
{
"organizations:seer-night-shift": [org.slug],
- "organizations:gen-ai-features": [org.slug],
"organizations:seat-based-seer-enabled": [org.slug],
}
),
@@ -254,7 +253,6 @@ def test_dispatches_with_run_options(self) -> None:
self.feature(
{
"organizations:seer-night-shift": [org.slug],
- "organizations:gen-ai-features": [org.slug],
"organizations:seat-based-seer-enabled": [org.slug],
}
),
@@ -278,7 +276,6 @@ def test_redelivery_dispatches_same_schedule_id(self) -> None:
self.feature(
{
"organizations:seer-night-shift": [org.slug],
- "organizations:gen-ai-features": [org.slug],
"organizations:seat-based-seer-enabled": [org.slug],
}
),
@@ -305,7 +302,6 @@ def test_skips_orgs_without_seat_based_seer(self) -> None:
self.feature(
{
"organizations:seer-night-shift": [org.slug],
- "organizations:gen-ai-features": [org.slug],
# seat-based-seer-enabled intentionally omitted
}
),
@@ -327,7 +323,6 @@ def test_dispatches_legacy_orgs_when_enabled(self) -> None:
self.feature(
{
"organizations:seer-night-shift": [org.slug],
- "organizations:gen-ai-features": [org.slug],
# seat-based-seer-enabled intentionally omitted
}
),
@@ -346,7 +341,6 @@ def test_skips_orgs_with_hidden_ai(self) -> None:
self.feature(
{
"organizations:seer-night-shift": [org.slug],
- "organizations:gen-ai-features": [org.slug],
"organizations:seat-based-seer-enabled": [org.slug],
}
),
@@ -364,7 +358,6 @@ def test_skips_orgs_with_code_generation_disabled(self) -> None:
self.feature(
{
"organizations:seer-night-shift": [org.slug],
- "organizations:gen-ai-features": [org.slug],
"organizations:seat-based-seer-enabled": [org.slug],
}
),
@@ -383,7 +376,6 @@ def test_skips_orgs_without_seer_project_repository(self) -> None:
self.feature(
{
"organizations:seer-night-shift": [org.slug],
- "organizations:gen-ai-features": [org.slug],
"organizations:seat-based-seer-enabled": [org.slug],
}
),
@@ -685,9 +677,7 @@ def test_no_eligible_projects(self) -> None:
org = self.create_organization()
self.create_project(organization=org)
- with (
- patch("sentry.tasks.seer.night_shift.cron.logger") as mock_logger,
- ):
+ with patch("sentry.tasks.seer.night_shift.cron.logger") as mock_logger:
run_night_shift_for_org(org.id)
info_events = [call.args[0] for call in mock_logger.info.call_args_list]
assert "night_shift.no_eligible_projects" in info_events
@@ -733,8 +723,7 @@ def test_filters_recently_skipped_groups(self) -> None:
mark_skipped(skipped_group.id)
try:
- with self.feature("organizations:gen-ai-features"):
- run_night_shift_for_org(org.id)
+ run_night_shift_for_org(org.id)
finally:
redis_clusters.get("default").delete(skip_cache_key(skipped_group.id))
@@ -891,7 +880,6 @@ def test_chunking_preserves_order_across_even_shards(self) -> None:
with (
self.options({"seer.night_shift.shard_size": 2}),
- self.feature("organizations:gen-ai-features"),
patch(
"sentry.tasks.seer.night_shift.cron.fixability_score_strategy",
return_value=scored,
@@ -916,7 +904,6 @@ def test_chunking_single_shard_when_size_exceeds_count(self) -> None:
with (
self.options({"seer.night_shift.shard_size": 10}),
- self.feature("organizations:gen-ai-features"),
patch(
"sentry.tasks.seer.night_shift.cron.fixability_score_strategy",
return_value=scored,
@@ -938,7 +925,6 @@ def test_non_positive_shard_size_clamps_to_one(self) -> None:
with (
self.options({"seer.night_shift.shard_size": 0}),
- self.feature("organizations:gen-ai-features"),
patch(
"sentry.tasks.seer.night_shift.cron.fixability_score_strategy",
return_value=scored,
@@ -959,10 +945,7 @@ def test_dispatches_candidates_to_seer_feature(self) -> None:
project, "fixable", seer_fixability_score=0.9, times_seen=5, priority=75
)
- with (
- self.feature("organizations:gen-ai-features"),
- patch("sentry.seer.night_shift.delivery.trigger_autofix_agent") as mock_autofix,
- ):
+ with patch("sentry.seer.night_shift.delivery.trigger_autofix_agent") as mock_autofix:
run_night_shift_for_org(org.id)
# Autofix is fired by Seer's pushed-back verdicts, not in-process.
@@ -1000,8 +983,7 @@ def test_payload_carries_automation_tuning_for_legacy_orgs(self) -> None:
)
self._store_event_and_update_group(project, "fixable", seer_fixability_score=0.9)
- with self.feature("organizations:gen-ai-features"):
- run_night_shift_for_org(org.id)
+ run_night_shift_for_org(org.id)
_, body = _dispatched_feature_body(org)
assert body["payload"]["candidates"][0]["automation_tuning"] == "high"
@@ -1013,7 +995,6 @@ def test_payload_omits_automation_tuning_for_seat_based_orgs(self) -> None:
self._store_event_and_update_group(project, "fixable", seer_fixability_score=0.9)
with (
- self.feature("organizations:gen-ai-features"),
patch(
"sentry.tasks.seer.night_shift.cron.is_seer_seat_based_tier_enabled",
return_value=True,
@@ -1039,8 +1020,7 @@ def test_payload_carries_per_project_automation_tuning_within_one_org(self) -> N
always, "always-fixable", seer_fixability_score=0.9
)
- with self.feature("organizations:gen-ai-features"):
- run_night_shift_for_org(org.id)
+ run_night_shift_for_org(org.id)
_, body = _dispatched_feature_body(org)
tuning_by_group_id = {
@@ -1063,7 +1043,6 @@ def test_allowed_project_slugs_gives_each_project_its_own_quota(self) -> None:
)
with (
- self.feature("organizations:gen-ai-features"),
self.options(
{
"seer.night_shift.org_tweaks": {
@@ -1099,10 +1078,7 @@ def test_shards_candidates_across_feature_runs(self) -> None:
for i in range(3)
]
- with (
- self.options({"seer.night_shift.shard_size": 2}),
- self.feature("organizations:gen-ai-features"),
- ):
+ with self.options({"seer.night_shift.shard_size": 2}):
run_night_shift_for_org(org.id)
run = SeerWorkflowRun.objects.get(organization=org)
@@ -1149,7 +1125,6 @@ def fail_second_dispatch(client, *args, **kwargs):
with (
self.options({"seer.night_shift.shard_size": 1}),
- self.feature("organizations:gen-ai-features"),
patch(
"sentry.tasks.seer.night_shift.cron.fixability_score_strategy",
return_value=scored,
@@ -1203,6 +1178,7 @@ def test_no_candidates_skips_dispatch(self) -> None:
def test_no_seer_access_keeps_shard_plan_for_resume(self) -> None:
org = self.create_organization()
+ org.update_option("sentry:hide_ai_features", True)
project = self.create_project(organization=org)
self._make_eligible(project)
self._store_event_and_update_group(
@@ -1234,7 +1210,6 @@ def test_dispatch_failure_records_error(self) -> None:
)
with (
- self.feature("organizations:gen-ai-features"),
patch(
"sentry.seer.agent.client.SeerAgentClient.start_feature_run",
side_effect=RuntimeError("boom"),
@@ -1256,8 +1231,7 @@ def test_outbox_drain_mirrors_run_against_seer(self) -> None:
project, "fixable", seer_fixability_score=0.9, times_seen=5
)
- with self.feature("organizations:gen-ai-features"):
- run_night_shift_for_org(org.id)
+ run_night_shift_for_org(org.id)
seer_run = SeerRun.objects.get(organization=org, type=SeerRunType.FEATURE_RUN)
assert seer_run.mirror_status == SeerRunMirrorStatus.PENDING
diff --git a/tests/sentry/tasks/test_llm_issue_detection.py b/tests/sentry/tasks/test_llm_issue_detection.py
index 6302b60c1f66..e9796b96c835 100644
--- a/tests/sentry/tasks/test_llm_issue_detection.py
+++ b/tests/sentry/tasks/test_llm_issue_detection.py
@@ -4,6 +4,7 @@
import pytest
from django.db.models import F
+from django.test import override_settings
from sentry.issues.grouptype import AIDetectedDBGroupType
from sentry.models.project import Project
@@ -24,7 +25,6 @@
)
from sentry.testutils.cases import APITransactionTestCase, SnubaTestCase, SpanTestCase, TestCase
from sentry.testutils.helpers.datetime import before_now
-from sentry.testutils.helpers.features import with_feature
class LLMIssueDetectionTest(TestCase):
@@ -42,7 +42,7 @@ def _budget_ok_response() -> Mock:
response.data = b'{"has_budget": true}'
return response
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.tasks.llm_issue_detection.detection.make_signed_seer_api_request")
@patch("sentry.tasks.llm_issue_detection.detection.make_issue_detection_request")
@patch(
@@ -65,7 +65,7 @@ def test_detect_llm_issues_no_transactions(
)
mock_seer_request.assert_not_called()
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.tasks.llm_issue_detection.detection.make_signed_seer_api_request")
@patch("sentry.tasks.llm_issue_detection.trace_data.Spans.run_table_query")
@patch("sentry.tasks.llm_issue_detection.detection.make_issue_detection_request")
@@ -190,7 +190,7 @@ def test_general_type_skips_occurrence_creation(self, mock_produce_occurrence):
)
assert not mock_produce_occurrence.called
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.tasks.llm_issue_detection.detection.make_signed_seer_api_request")
@patch("sentry.tasks.llm_issue_detection.detection.make_issue_detection_request")
@patch("sentry.tasks.llm_issue_detection.trace_data.Spans.run_table_query")
@@ -237,7 +237,7 @@ def test_detect_llm_issues_full_flow(
assert seer_request.organization_id == self.organization.id
assert len(seer_request.traces) == 1
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.tasks.llm_issue_detection.detection.make_signed_seer_api_request")
@patch("sentry.tasks.llm_issue_detection.detection.make_issue_detection_request")
@patch("sentry.tasks.llm_issue_detection.trace_data.Spans.run_table_query")
@@ -272,7 +272,7 @@ def test_detect_llm_issues_seer_error_logged(
assert mock_seer_request.call_count == 1
assert mock_logger_error.call_count == 1
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.tasks.llm_issue_detection.detection.make_issue_detection_request")
@patch(
"sentry.tasks.llm_issue_detection.trace_data.get_project_top_transaction_traces_for_llm_detection"
@@ -290,7 +290,7 @@ def test_check_budget_fail_open(self, mock_budget_request, mock_get_transactions
mock_get_transactions.assert_called_once()
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.tasks.llm_issue_detection.detection.make_issue_detection_request")
@patch(
"sentry.tasks.llm_issue_detection.trace_data.get_project_top_transaction_traces_for_llm_detection"
@@ -309,7 +309,7 @@ def test_check_budget_over_budget(
mock_get_transactions.assert_not_called()
mock_seer_request.assert_not_called()
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.tasks.llm_issue_detection.detection.make_issue_detection_request")
@patch(
"sentry.tasks.llm_issue_detection.trace_data.get_project_top_transaction_traces_for_llm_detection"
@@ -330,7 +330,7 @@ def test_plan_tier_forwarded_to_seer(
== f"{SEER_CHECK_BUDGET_ENDPOINT_PATH}/:organization_id"
)
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.tasks.llm_issue_detection.detection.make_issue_detection_request")
@patch(
"sentry.tasks.llm_issue_detection.trace_data.get_project_top_transaction_traces_for_llm_detection"
@@ -347,7 +347,7 @@ def test_plan_tier_defaults_to_business(
budget_url = mock_budget_request.call_args[0][1]
assert "plan_tier=business" in budget_url
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.tasks.llm_issue_detection.detection.make_signed_seer_api_request")
@patch("sentry.tasks.llm_issue_detection.detection.make_issue_detection_request")
@patch(
@@ -368,7 +368,7 @@ def test_traces_sent_per_plan_tier(
assert len(seer_request.traces) == expected
assert seer_request.plan_tier == plan_tier
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.tasks.llm_issue_detection.detection.make_signed_seer_api_request")
@patch("sentry.tasks.llm_issue_detection.detection.make_issue_detection_request")
@patch(
@@ -393,7 +393,7 @@ def test_traces_per_invocation_option_override(
class LLMIssueDetectionProjectFilterTest(TestCase):
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.tasks.llm_issue_detection.detection.make_signed_seer_api_request")
@patch(
"sentry.tasks.llm_issue_detection.trace_data.get_project_top_transaction_traces_for_llm_detection"
diff --git a/tests/sentry/tasks/test_post_process.py b/tests/sentry/tasks/test_post_process.py
index 9fee8c0e078e..98dc04bf8f6a 100644
--- a/tests/sentry/tasks/test_post_process.py
+++ b/tests/sentry/tasks/test_post_process.py
@@ -3054,7 +3054,7 @@ def test_step_is_skipped_by_fully_specified_condition(
class KickOffSeerAutomationTestMixin(BasePostProcessGroupMixin):
@patch("sentry.tasks.seer.autofix.generate_summary_and_run_automation.delay")
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
def test_kick_off_seer_automation_with_features(self, mock_generate_summary_and_run_automation):
self.project.update_option("sentry:seer_scanner_automation", True)
event = self.create_event(
@@ -3073,8 +3073,9 @@ def test_kick_off_seer_automation_with_features(self, mock_generate_summary_and_
event.group.id, trigger_path="old_seer_automation"
)
+ @override_settings(SENTRY_SELF_HOSTED=True)
@patch("sentry.tasks.seer.autofix.generate_summary_and_run_automation.delay")
- def test_kick_off_seer_automation_without_org_feature(
+ def test_kick_off_seer_automation_when_self_hosted(
self, mock_generate_summary_and_run_automation
):
self.project.update_option("sentry:seer_scanner_automation", True)
@@ -3092,7 +3093,7 @@ def test_kick_off_seer_automation_without_org_feature(
mock_generate_summary_and_run_automation.assert_not_called()
@patch("sentry.tasks.seer.autofix.generate_summary_and_run_automation.delay")
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
def test_kick_off_seer_automation_without_scanner_on(
self, mock_generate_summary_and_run_automation
):
@@ -3113,7 +3114,7 @@ def test_kick_off_seer_automation_without_scanner_on(
mock_generate_summary_and_run_automation.assert_not_called()
@patch("sentry.tasks.seer.autofix.generate_summary_and_run_automation.delay")
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
def test_kick_off_seer_automation_skips_existing_fixability_score(
self, mock_generate_summary_and_run_automation
):
@@ -3138,7 +3139,7 @@ def test_kick_off_seer_automation_skips_existing_fixability_score(
mock_generate_summary_and_run_automation.assert_not_called()
@patch("sentry.tasks.seer.autofix.generate_summary_and_run_automation.delay")
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
def test_kick_off_seer_automation_skips_existing_issue(
self, mock_generate_summary_and_run_automation
):
@@ -3162,7 +3163,7 @@ def test_kick_off_seer_automation_skips_existing_issue(
mock_generate_summary_and_run_automation.assert_not_called()
@patch("sentry.tasks.seer.autofix.generate_summary_and_run_automation.delay")
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
def test_kick_off_seer_automation_skips_with_existing_fixability_score(
self, mock_generate_summary_and_run_automation
):
@@ -3195,7 +3196,7 @@ def test_kick_off_seer_automation_skips_with_existing_fixability_score(
@patch("sentry.seer.autofix.utils.is_seer_scanner_rate_limited")
@patch("sentry.quotas.backend.check_seer_quota")
@patch("sentry.tasks.seer.autofix.generate_summary_and_run_automation.delay")
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
def test_rate_limit_only_checked_after_all_other_checks_pass(
self,
mock_generate_summary_and_run_automation,
@@ -3266,7 +3267,7 @@ def test_rate_limit_only_checked_after_all_other_checks_pass(
mock_generate_summary_and_run_automation.assert_not_called()
@patch("sentry.tasks.seer.autofix.generate_summary_and_run_automation.delay")
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
def test_kick_off_seer_automation_skips_when_lock_held(
self, mock_generate_summary_and_run_automation
):
@@ -3315,7 +3316,7 @@ def test_kick_off_seer_automation_skips_when_lock_held(
)
@patch("sentry.tasks.seer.autofix.generate_summary_and_run_automation.delay")
- @with_feature("organizations:gen-ai-features")
+ @override_settings(SENTRY_SELF_HOSTED=False)
def test_kick_off_seer_automation_with_hide_ai_features_enabled(
self, mock_generate_summary_and_run_automation
):
@@ -3403,7 +3404,7 @@ def _seat_based_post_process(self, **group_overrides):
return event
@patch("sentry.tasks.seer.autofix.generate_issue_summary_only.delay")
- @with_feature({"organizations:gen-ai-features": True})
+ @override_settings(SENTRY_SELF_HOSTED=False)
def test_seat_based_org_skips_old_issues(
self, mock_generate_summary_only, mock_seat_based_tier
):
@@ -3411,7 +3412,7 @@ def test_seat_based_org_skips_old_issues(
mock_generate_summary_only.assert_not_called()
@patch("sentry.tasks.seer.autofix.generate_issue_summary_only.delay")
- @with_feature({"organizations:gen-ai-features": True})
+ @override_settings(SENTRY_SELF_HOSTED=False)
def test_seat_based_org_skips_when_fixability_exists(
self, mock_generate_summary_only, mock_seat_based_tier
):
@@ -3422,6 +3423,7 @@ def test_seat_based_org_skips_when_fixability_exists(
class SeerAutomationHelperFunctionsTestMixin(BasePostProcessGroupMixin):
"""Unit tests for is_issue_eligible_for_seer_automation."""
+ @override_settings(SENTRY_SELF_HOSTED=False)
@patch("sentry.quotas.backend.check_seer_quota", return_value=True)
@patch("sentry.features.has", return_value=True)
def test_is_issue_eligible_for_seer_automation(self, mock_features_has, mock_has_budget):
@@ -3447,12 +3449,11 @@ def test_is_issue_eligible_for_seer_automation(self, mock_features_has, mock_has
mock_category.return_value = GroupCategory.FEEDBACK
assert is_issue_eligible_for_seer_automation(group) is False
- # Missing feature flag
- mock_features_has.return_value = False
- assert is_issue_eligible_for_seer_automation(group) is False
+ # Seer unavailable on self-hosted
+ with override_settings(SENTRY_SELF_HOSTED=True):
+ assert is_issue_eligible_for_seer_automation(group) is False
# Hide AI features enabled
- mock_features_has.return_value = True
self.organization.update_option("sentry:hide_ai_features", True)
assert is_issue_eligible_for_seer_automation(group) is False
self.organization.update_option("sentry:hide_ai_features", False)
@@ -3503,7 +3504,7 @@ class PostProcessGroupErrorTest(
):
@patch("sentry.seer.autofix.utils.is_seer_seat_based_tier_enabled", return_value=True)
@patch("sentry.tasks.seer.autofix.generate_issue_summary_only.delay")
- @with_feature({"organizations:gen-ai-features": True})
+ @override_settings(SENTRY_SELF_HOSTED=False)
def test_seat_based_org_generates_summary_for_new_issues(
self, mock_generate_summary_only, mock_seat_based_tier
):
diff --git a/tests/sentry/tasks/test_web_vitals_issue_detection.py b/tests/sentry/tasks/test_web_vitals_issue_detection.py
index 6086d7d0eaa0..3dc5dabec921 100644
--- a/tests/sentry/tasks/test_web_vitals_issue_detection.py
+++ b/tests/sentry/tasks/test_web_vitals_issue_detection.py
@@ -47,7 +47,6 @@ def test_run_detection_dispatches_sub_tasks_when_enabled(self, mock_delay):
"issue-detection.web-vitals-detection.projects-allowlist": [project.id],
}
),
- self.feature("organizations:gen-ai-features"),
):
run_web_vitals_issue_detection()
@@ -65,7 +64,6 @@ def test_run_detection_skips_when_no_github_code_mappings(self, mock_delay):
"issue-detection.web-vitals-detection.projects-allowlist": [project.id],
}
),
- self.feature("organizations:gen-ai-features"),
):
run_web_vitals_issue_detection()
@@ -81,7 +79,6 @@ def test_run_detection_skips_when_not_allowlisted(self, mock_delay):
"issue-detection.web-vitals-detection.projects-allowlist": [],
}
),
- self.feature("organizations:gen-ai-features"),
):
run_web_vitals_issue_detection()
@@ -173,7 +170,6 @@ def test_run_detection_produces_occurrences(self, mock_produce_occurrence_to_kaf
"issue-detection.web-vitals-detection.projects-allowlist": [project.id],
}
),
- self.feature("organizations:gen-ai-features"),
TaskRunner(),
):
run_web_vitals_issue_detection()
@@ -291,7 +287,6 @@ def test_run_detection_groups_rendering_vitals(self, mock_produce_occurrence_to_
"issue-detection.web-vitals-detection.projects-allowlist": [project.id],
}
),
- self.feature("organizations:gen-ai-features"),
TaskRunner(),
):
run_web_vitals_issue_detection()
@@ -377,7 +372,6 @@ def test_run_detection_does_not_produce_occurrences_for_existing_issues(
"issue-detection.web-vitals-detection.projects-allowlist": [project.id],
}
),
- self.feature("organizations:gen-ai-features"),
TaskRunner(),
):
run_web_vitals_issue_detection()
@@ -421,7 +415,6 @@ def test_run_detection_does_not_create_issue_on_insufficient_samples(
"issue-detection.web-vitals-detection.projects-allowlist": [project.id],
}
),
- self.feature("organizations:gen-ai-features"),
TaskRunner(),
):
run_web_vitals_issue_detection()
@@ -502,7 +495,6 @@ def test_run_detection_selects_trace_closest_to_p75_web_vital_value(
"issue-detection.web-vitals-detection.projects-allowlist": [project.id],
}
),
- self.feature("organizations:gen-ai-features"),
TaskRunner(),
):
run_web_vitals_issue_detection()
@@ -611,7 +603,6 @@ def test_run_detection_selects_trace_from_worst_score(self, mock_produce_occurre
"issue-detection.web-vitals-detection.projects-allowlist": [project.id],
}
),
- self.feature("organizations:gen-ai-features"),
TaskRunner(),
):
run_web_vitals_issue_detection()
@@ -644,7 +635,6 @@ def test_run_detection_does_not_run_for_project_when_user_has_disabled(
"issue-detection.web-vitals-detection.projects-allowlist": [project.id],
}
),
- self.feature("organizations:gen-ai-features"),
TaskRunner(),
):
run_web_vitals_issue_detection()