|
@@ -13,6 +13,7 @@ from sentry.models.repository import Repository
|
|
|
from sentry.shared_integrations.exceptions.base import ApiError
|
|
|
from sentry.tasks.derive_code_mappings import derive_code_mappings, identify_stacktrace_paths
|
|
|
from sentry.testutils import TestCase
|
|
|
+from sentry.testutils.helpers import apply_feature_flag_on_cls, with_feature
|
|
|
from sentry.utils.locking import UnableToAcquireLock
|
|
|
|
|
|
|
|
@@ -34,6 +35,7 @@ class BaseDeriveCodeMappings(TestCase):
|
|
|
return self.store_event(data=test_data, project_id=self.project.id).data
|
|
|
|
|
|
|
|
|
+@apply_feature_flag_on_cls("organizations:derive-code-mappings")
|
|
|
class TestTaskBehavior(BaseDeriveCodeMappings):
|
|
|
"""Test task behavior that is not language specific."""
|
|
|
|
|
@@ -122,6 +124,7 @@ class TestJavascriptDeriveCodeMappings(BaseDeriveCodeMappings):
|
|
|
assert stacktrace_paths == []
|
|
|
|
|
|
@responses.activate
|
|
|
+ @with_feature("organizations:derive-code-mappings")
|
|
|
def test_derive_code_mappings_starts_with_period_slash(self):
|
|
|
repo_name = "foo/bar"
|
|
|
with patch(
|
|
@@ -141,6 +144,7 @@ class TestJavascriptDeriveCodeMappings(BaseDeriveCodeMappings):
|
|
|
assert code_mapping.repository.name == repo_name
|
|
|
|
|
|
@responses.activate
|
|
|
+ @with_feature("organizations:derive-code-mappings")
|
|
|
def test_derive_code_mappings_starts_with_period_slash_no_containing_directory(self):
|
|
|
repo_name = "foo/bar"
|
|
|
with patch(
|
|
@@ -160,6 +164,7 @@ class TestJavascriptDeriveCodeMappings(BaseDeriveCodeMappings):
|
|
|
assert code_mapping.repository.name == repo_name
|
|
|
|
|
|
@responses.activate
|
|
|
+ @with_feature("organizations:derive-code-mappings")
|
|
|
def test_derive_code_mappings_one_to_one_match(self):
|
|
|
repo_name = "foo/bar"
|
|
|
with patch(
|
|
@@ -198,6 +203,7 @@ class TestRubyDeriveCodeMappings(BaseDeriveCodeMappings):
|
|
|
assert set(stacktrace_paths) == {"some/path/test.rb", "lib/tasks/crontask.rake"}
|
|
|
|
|
|
@responses.activate
|
|
|
+ @with_feature("organizations:derive-code-mappings")
|
|
|
def test_derive_code_mappings_rb(self):
|
|
|
repo_name = "foo/bar"
|
|
|
with patch(
|
|
@@ -213,6 +219,7 @@ class TestRubyDeriveCodeMappings(BaseDeriveCodeMappings):
|
|
|
assert code_mapping.repository.name == repo_name
|
|
|
|
|
|
@responses.activate
|
|
|
+ @with_feature("organizations:derive-code-mappings")
|
|
|
def test_derive_code_mappings_rake(self):
|
|
|
repo_name = "foo/bar"
|
|
|
with patch(
|
|
@@ -258,6 +265,7 @@ class TestNodeDeriveCodeMappings(BaseDeriveCodeMappings):
|
|
|
}
|
|
|
|
|
|
@responses.activate
|
|
|
+ @with_feature("organizations:derive-code-mappings")
|
|
|
def test_derive_code_mappings_starts_with_app(self):
|
|
|
repo_name = "foo/bar"
|
|
|
with patch(
|
|
@@ -273,6 +281,7 @@ class TestNodeDeriveCodeMappings(BaseDeriveCodeMappings):
|
|
|
assert code_mapping.repository.name == repo_name
|
|
|
|
|
|
@responses.activate
|
|
|
+ @with_feature("organizations:derive-code-mappings")
|
|
|
def test_derive_code_mappings_starts_with_multiple_dot_dot_slash(self):
|
|
|
repo_name = "foo/bar"
|
|
|
with patch(
|
|
@@ -288,6 +297,7 @@ class TestNodeDeriveCodeMappings(BaseDeriveCodeMappings):
|
|
|
assert code_mapping.repository.name == repo_name
|
|
|
|
|
|
@responses.activate
|
|
|
+ @with_feature("organizations:derive-code-mappings")
|
|
|
def test_derive_code_mappings_starts_with_app_dot_dot_slash(self):
|
|
|
repo_name = "foo/bar"
|
|
|
with patch(
|
|
@@ -338,6 +348,23 @@ class TestPythonDeriveCodeMappings(BaseDeriveCodeMappings):
|
|
|
"sentry/tasks.py",
|
|
|
]
|
|
|
|
|
|
+ @with_feature({"organizations:derive-code-mappings": False})
|
|
|
+ def test_feature_off(self):
|
|
|
+ event = self.store_event(data=self.test_data, project_id=self.project.id)
|
|
|
+
|
|
|
+ assert not RepositoryProjectPathConfig.objects.filter(project_id=self.project.id).exists()
|
|
|
+
|
|
|
+ with patch(
|
|
|
+ "sentry.tasks.derive_code_mappings.identify_stacktrace_paths",
|
|
|
+ return_value={
|
|
|
+ self.project: ["sentry/models/release.py", "sentry/tasks.py"],
|
|
|
+ },
|
|
|
+ ) as mock_identify_stacktraces, self.tasks():
|
|
|
+ derive_code_mappings(self.project.id, event.data)
|
|
|
+
|
|
|
+ assert mock_identify_stacktraces.call_count == 0
|
|
|
+ assert not RepositoryProjectPathConfig.objects.filter(project_id=self.project.id).exists()
|
|
|
+
|
|
|
@patch("sentry.integrations.github.GitHubIntegration.get_trees_for_org")
|
|
|
@patch(
|
|
|
"sentry.integrations.utils.code_mapping.CodeMappingTreesHelper.generate_code_mappings",
|
|
@@ -349,6 +376,7 @@ class TestPythonDeriveCodeMappings(BaseDeriveCodeMappings):
|
|
|
)
|
|
|
],
|
|
|
)
|
|
|
+ @with_feature("organizations:derive-code-mappings")
|
|
|
def test_derive_code_mappings_single_project(
|
|
|
self, mock_generate_code_mappings, mock_get_trees_for_org
|
|
|
):
|
|
@@ -386,6 +414,7 @@ class TestPythonDeriveCodeMappings(BaseDeriveCodeMappings):
|
|
|
],
|
|
|
)
|
|
|
@patch("sentry.tasks.derive_code_mappings.logger")
|
|
|
+ @with_feature("organizations:derive-code-mappings")
|
|
|
def test_derive_code_mappings_duplicates(
|
|
|
self, mock_logger, mock_generate_code_mappings, mock_get_trees_for_org
|
|
|
):
|
|
@@ -422,7 +451,43 @@ class TestPythonDeriveCodeMappings(BaseDeriveCodeMappings):
|
|
|
assert code_mapping.first().automatically_generated is False
|
|
|
assert mock_logger.info.call_count == 1
|
|
|
|
|
|
+ @patch("sentry.integrations.github.GitHubIntegration.get_trees_for_org")
|
|
|
+ @patch(
|
|
|
+ "sentry.integrations.utils.code_mapping.CodeMappingTreesHelper.generate_code_mappings",
|
|
|
+ return_value=[
|
|
|
+ CodeMapping(
|
|
|
+ repo=Repo(name="repo", branch="master"),
|
|
|
+ stacktrace_root="sentry/models",
|
|
|
+ source_path="src/sentry/models",
|
|
|
+ )
|
|
|
+ ],
|
|
|
+ )
|
|
|
+ @patch("sentry.tasks.derive_code_mappings.logger")
|
|
|
+ @with_feature("organizations:derive-code-mappings")
|
|
|
+ def test_derive_code_mappings_dry_run(
|
|
|
+ self, mock_logger, mock_generate_code_mappings, mock_get_trees_for_org
|
|
|
+ ):
|
|
|
+
|
|
|
+ event = self.store_event(data=self.test_data, project_id=self.project.id)
|
|
|
+
|
|
|
+ assert not RepositoryProjectPathConfig.objects.filter(project_id=self.project.id).exists()
|
|
|
+
|
|
|
+ with patch(
|
|
|
+ "sentry.tasks.derive_code_mappings.identify_stacktrace_paths",
|
|
|
+ return_value=["sentry/models/release.py", "sentry/tasks.py"],
|
|
|
+ ) as mock_identify_stacktraces, self.tasks():
|
|
|
+ derive_code_mappings(self.project.id, event.data, dry_run=True)
|
|
|
+
|
|
|
+ assert mock_logger.info.call_count == 1
|
|
|
+ assert mock_identify_stacktraces.call_count == 1
|
|
|
+ assert mock_get_trees_for_org.call_count == 1
|
|
|
+ assert mock_generate_code_mappings.call_count == 1
|
|
|
+
|
|
|
+ # We should not create the code mapping for dry runs
|
|
|
+ assert not RepositoryProjectPathConfig.objects.filter(project_id=self.project.id).exists()
|
|
|
+
|
|
|
@responses.activate
|
|
|
+ @with_feature("organizations:derive-code-mappings")
|
|
|
def test_derive_code_mappings_stack_and_source_root_do_not_match(self):
|
|
|
repo_name = "foo/bar"
|
|
|
with patch(
|
|
@@ -438,6 +503,7 @@ class TestPythonDeriveCodeMappings(BaseDeriveCodeMappings):
|
|
|
assert code_mapping.source_root == "src/sentry/"
|
|
|
|
|
|
@responses.activate
|
|
|
+ @with_feature("organizations:derive-code-mappings")
|
|
|
def test_derive_code_mappings_no_normalization(self):
|
|
|
repo_name = "foo/bar"
|
|
|
with patch(
|