|
@@ -1,6 +1,7 @@
|
|
|
from typing import Any, Mapping
|
|
|
from unittest import mock
|
|
|
|
|
|
+from sentry.api.endpoints.project_stacktrace_link import ProjectStacktraceLinkEndpoint
|
|
|
from sentry.integrations.example.integration import ExampleIntegration
|
|
|
from sentry.models import Integration, OrganizationIntegration
|
|
|
from sentry.testutils import APITestCase
|
|
@@ -36,8 +37,7 @@ def serialized_integration(integration: Integration) -> Mapping[str, Any]:
|
|
|
}
|
|
|
|
|
|
|
|
|
-@region_silo_test
|
|
|
-class ProjectStacktraceLinkTest(APITestCase):
|
|
|
+class BaseProjectStacktraceLink(APITestCase):
|
|
|
endpoint = "sentry-api-0-project-stacktrace-link"
|
|
|
|
|
|
def setUp(self):
|
|
@@ -53,6 +53,29 @@ class ProjectStacktraceLinkTest(APITestCase):
|
|
|
self.repo.provider = "example"
|
|
|
self.repo.save()
|
|
|
|
|
|
+ self.login_as(self.user)
|
|
|
+
|
|
|
+ def expected_configurations(self, code_mapping) -> Mapping[str, Any]:
|
|
|
+ return {
|
|
|
+ "defaultBranch": "master",
|
|
|
+ "id": str(code_mapping.id),
|
|
|
+ "integrationId": str(self.integration.id),
|
|
|
+ "projectId": str(self.project.id),
|
|
|
+ "projectSlug": self.project.slug,
|
|
|
+ "provider": serialized_provider(),
|
|
|
+ "repoId": str(self.repo.id),
|
|
|
+ "repoName": self.repo.name,
|
|
|
+ "sourceRoot": code_mapping.source_root,
|
|
|
+ "stackRoot": code_mapping.stack_root,
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+@region_silo_test
|
|
|
+class ProjectStacktraceLinkTest(BaseProjectStacktraceLink):
|
|
|
+ endpoint = "sentry-api-0-project-stacktrace-link"
|
|
|
+
|
|
|
+ def setUp(self):
|
|
|
+ BaseProjectStacktraceLink.setUp(self)
|
|
|
self.code_mapping1 = self.create_code_mapping(
|
|
|
organization_integration=self.oi,
|
|
|
project=self.project,
|
|
@@ -70,21 +93,6 @@ class ProjectStacktraceLinkTest(APITestCase):
|
|
|
)
|
|
|
|
|
|
self.filepath = "usr/src/getsentry/src/sentry/src/sentry/utils/safe.py"
|
|
|
- self.login_as(self.user)
|
|
|
-
|
|
|
- def expected_configurations(self, code_mapping) -> Mapping[str, Any]:
|
|
|
- return {
|
|
|
- "defaultBranch": "master",
|
|
|
- "id": str(code_mapping.id),
|
|
|
- "integrationId": str(self.integration.id),
|
|
|
- "projectId": str(self.project.id),
|
|
|
- "projectSlug": self.project.slug,
|
|
|
- "provider": serialized_provider(),
|
|
|
- "repoId": str(self.repo.id),
|
|
|
- "repoName": self.repo.name,
|
|
|
- "sourceRoot": code_mapping.source_root,
|
|
|
- "stackRoot": code_mapping.stack_root,
|
|
|
- }
|
|
|
|
|
|
def test_no_filepath(self):
|
|
|
"""The file query search is missing"""
|
|
@@ -218,22 +226,9 @@ class ProjectStacktraceLinkTest(APITestCase):
|
|
|
|
|
|
|
|
|
@region_silo_test
|
|
|
-class ProjectStacktraceLinkTestMobile(APITestCase):
|
|
|
- endpoint = "sentry-api-0-project-stacktrace-link"
|
|
|
-
|
|
|
+class ProjectStacktraceLinkTestMobile(BaseProjectStacktraceLink):
|
|
|
def setUp(self):
|
|
|
- self.integration = Integration.objects.create(provider="example", name="Example")
|
|
|
- self.integration.add_organization(self.organization, self.user)
|
|
|
- self.oi = OrganizationIntegration.objects.get(integration_id=self.integration.id)
|
|
|
-
|
|
|
- self.repo = self.create_repo(
|
|
|
- project=self.project,
|
|
|
- name="getsentry/sentry",
|
|
|
- )
|
|
|
- self.repo.integration_id = self.integration.id
|
|
|
- self.repo.provider = "example"
|
|
|
- self.repo.save()
|
|
|
-
|
|
|
+ BaseProjectStacktraceLink.setUp(self)
|
|
|
self.code_mapping1 = self.create_code_mapping(
|
|
|
organization_integration=self.oi,
|
|
|
project=self.project,
|
|
@@ -242,22 +237,6 @@ class ProjectStacktraceLinkTestMobile(APITestCase):
|
|
|
source_root="",
|
|
|
)
|
|
|
|
|
|
- self.login_as(self.user)
|
|
|
-
|
|
|
- def expected_configurations(self, code_mapping) -> Mapping[str, Any]:
|
|
|
- return {
|
|
|
- "defaultBranch": "master",
|
|
|
- "id": str(code_mapping.id),
|
|
|
- "integrationId": str(self.integration.id),
|
|
|
- "projectId": str(self.project.id),
|
|
|
- "projectSlug": self.project.slug,
|
|
|
- "provider": serialized_provider(),
|
|
|
- "repoId": str(self.repo.id),
|
|
|
- "repoName": self.repo.name,
|
|
|
- "sourceRoot": code_mapping.source_root,
|
|
|
- "stackRoot": code_mapping.stack_root,
|
|
|
- }
|
|
|
-
|
|
|
@mock.patch.object(
|
|
|
ExampleIntegration,
|
|
|
"get_stacktrace_link",
|
|
@@ -317,3 +296,94 @@ class ProjectStacktraceLinkTestMobile(APITestCase):
|
|
|
)
|
|
|
assert response.data["config"] == self.expected_configurations(self.code_mapping1)
|
|
|
assert response.data["sourceUrl"] == f"{example_base_url}/{file_path}"
|
|
|
+
|
|
|
+
|
|
|
+class ProjectStacktraceLinkTestMultipleMatches(BaseProjectStacktraceLink):
|
|
|
+ def setUp(self):
|
|
|
+ BaseProjectStacktraceLink.setUp(self)
|
|
|
+ # Created by the user, not well defined stack root
|
|
|
+ self.code_mapping1 = self.create_code_mapping(
|
|
|
+ organization_integration=self.oi,
|
|
|
+ project=self.project,
|
|
|
+ repo=self.repo,
|
|
|
+ stack_root="",
|
|
|
+ source_root="",
|
|
|
+ automatically_generated=False,
|
|
|
+ )
|
|
|
+ # Created by automation, not as well defined stack root
|
|
|
+ self.code_mapping2 = self.create_code_mapping(
|
|
|
+ organization_integration=self.oi,
|
|
|
+ project=self.project,
|
|
|
+ repo=self.repo,
|
|
|
+ stack_root="usr/src/getsentry/src/",
|
|
|
+ source_root="",
|
|
|
+ automatically_generated=True,
|
|
|
+ )
|
|
|
+ # Created by the user, well defined stack root
|
|
|
+ self.code_mapping3 = self.create_code_mapping(
|
|
|
+ organization_integration=self.oi,
|
|
|
+ project=self.project,
|
|
|
+ repo=self.repo,
|
|
|
+ stack_root="usr/src/getsentry/",
|
|
|
+ source_root="",
|
|
|
+ automatically_generated=False,
|
|
|
+ )
|
|
|
+ # Created by the user, not as well defined stack root
|
|
|
+ self.code_mapping4 = self.create_code_mapping(
|
|
|
+ organization_integration=self.oi,
|
|
|
+ project=self.project,
|
|
|
+ repo=self.repo,
|
|
|
+ stack_root="usr/src/",
|
|
|
+ source_root="",
|
|
|
+ automatically_generated=False,
|
|
|
+ )
|
|
|
+ # Created by automation, well defined stack root
|
|
|
+ self.code_mapping5 = self.create_code_mapping(
|
|
|
+ organization_integration=self.oi,
|
|
|
+ project=self.project,
|
|
|
+ repo=self.repo,
|
|
|
+ stack_root="usr/src/getsentry/src/sentry/",
|
|
|
+ source_root="",
|
|
|
+ automatically_generated=True,
|
|
|
+ )
|
|
|
+ self.code_mappings = [
|
|
|
+ self.code_mapping1,
|
|
|
+ self.code_mapping2,
|
|
|
+ self.code_mapping3,
|
|
|
+ self.code_mapping4,
|
|
|
+ self.code_mapping5,
|
|
|
+ ]
|
|
|
+
|
|
|
+ self.filepath = "usr/src/getsentry/src/sentry/src/sentry/utils/safe.py"
|
|
|
+
|
|
|
+ def test_test_multiple_code_mapping_matches_order(self):
|
|
|
+ project_stacktrace_link_endpoint = ProjectStacktraceLinkEndpoint()
|
|
|
+
|
|
|
+ configs = self.code_mappings
|
|
|
+ # Expected configs: stack_root, automatically_generated
|
|
|
+ expected_config_order = [
|
|
|
+ self.code_mapping3, # "usr/src/getsentry/", False
|
|
|
+ self.code_mapping4, # "usr/src/", False
|
|
|
+ self.code_mapping1, # "", False
|
|
|
+ self.code_mapping5, # "usr/src/getsentry/src/sentry/", True
|
|
|
+ self.code_mapping2, # "usr/src/getsentry/src/", True
|
|
|
+ ]
|
|
|
+ sorted_configs = project_stacktrace_link_endpoint.sort_code_mapping_configs(configs)
|
|
|
+ assert sorted_configs == expected_config_order
|
|
|
+
|
|
|
+ def test_multiple_code_mapping_matches(self):
|
|
|
+ with mock.patch.object(
|
|
|
+ ExampleIntegration,
|
|
|
+ "get_stacktrace_link",
|
|
|
+ return_value="https://github.com/usr/src/getsentry/src/sentry/src/sentry/utils/safe.py",
|
|
|
+ ):
|
|
|
+ response = self.get_success_response(
|
|
|
+ self.organization.slug, self.project.slug, qs_params={"file": self.filepath}
|
|
|
+ )
|
|
|
+ # Assert that the code mapping that is user generated and has the most defined stack
|
|
|
+ # trace of the user generated code mappings is chosen
|
|
|
+ assert response.data["config"] == self.expected_configurations(self.code_mapping3)
|
|
|
+ assert (
|
|
|
+ response.data["sourceUrl"]
|
|
|
+ == "https://github.com/usr/src/getsentry/src/sentry/src/sentry/utils/safe.py"
|
|
|
+ )
|