1313from sentry .shared_integrations .exceptions .base import ApiError
1414from sentry .tasks .derive_code_mappings import derive_code_mappings , identify_stacktrace_paths
1515from sentry .testutils import TestCase
16+ from sentry .testutils .helpers import apply_feature_flag_on_cls , with_feature
1617from sentry .utils .locking import UnableToAcquireLock
1718
1819
@@ -34,6 +35,7 @@ def generate_data(self, frames: List[Dict[str, Union[str, bool]]], platform: str
3435 return self .store_event (data = test_data , project_id = self .project .id ).data
3536
3637
38+ @apply_feature_flag_on_cls ("organizations:derive-code-mappings" )
3739class TestTaskBehavior (BaseDeriveCodeMappings ):
3840 """Test task behavior that is not language specific."""
3941
@@ -122,6 +124,7 @@ def test_find_stacktrace_paths_bad_data(self):
122124 assert stacktrace_paths == []
123125
124126 @responses .activate
127+ @with_feature ("organizations:derive-code-mappings" )
125128 def test_derive_code_mappings_starts_with_period_slash (self ):
126129 repo_name = "foo/bar"
127130 with patch (
@@ -141,6 +144,7 @@ def test_derive_code_mappings_starts_with_period_slash(self):
141144 assert code_mapping .repository .name == repo_name
142145
143146 @responses .activate
147+ @with_feature ("organizations:derive-code-mappings" )
144148 def test_derive_code_mappings_starts_with_period_slash_no_containing_directory (self ):
145149 repo_name = "foo/bar"
146150 with patch (
@@ -160,6 +164,7 @@ def test_derive_code_mappings_starts_with_period_slash_no_containing_directory(s
160164 assert code_mapping .repository .name == repo_name
161165
162166 @responses .activate
167+ @with_feature ("organizations:derive-code-mappings" )
163168 def test_derive_code_mappings_one_to_one_match (self ):
164169 repo_name = "foo/bar"
165170 with patch (
@@ -198,6 +203,7 @@ def test_find_stacktrace_paths_single_project(self):
198203 assert set (stacktrace_paths ) == {"some/path/test.rb" , "lib/tasks/crontask.rake" }
199204
200205 @responses .activate
206+ @with_feature ("organizations:derive-code-mappings" )
201207 def test_derive_code_mappings_rb (self ):
202208 repo_name = "foo/bar"
203209 with patch (
@@ -213,6 +219,7 @@ def test_derive_code_mappings_rb(self):
213219 assert code_mapping .repository .name == repo_name
214220
215221 @responses .activate
222+ @with_feature ("organizations:derive-code-mappings" )
216223 def test_derive_code_mappings_rake (self ):
217224 repo_name = "foo/bar"
218225 with patch (
@@ -258,6 +265,7 @@ def test_find_stacktrace_paths_single_project(self):
258265 }
259266
260267 @responses .activate
268+ @with_feature ("organizations:derive-code-mappings" )
261269 def test_derive_code_mappings_starts_with_app (self ):
262270 repo_name = "foo/bar"
263271 with patch (
@@ -273,6 +281,7 @@ def test_derive_code_mappings_starts_with_app(self):
273281 assert code_mapping .repository .name == repo_name
274282
275283 @responses .activate
284+ @with_feature ("organizations:derive-code-mappings" )
276285 def test_derive_code_mappings_starts_with_multiple_dot_dot_slash (self ):
277286 repo_name = "foo/bar"
278287 with patch (
@@ -288,6 +297,7 @@ def test_derive_code_mappings_starts_with_multiple_dot_dot_slash(self):
288297 assert code_mapping .repository .name == repo_name
289298
290299 @responses .activate
300+ @with_feature ("organizations:derive-code-mappings" )
291301 def test_derive_code_mappings_starts_with_app_dot_dot_slash (self ):
292302 repo_name = "foo/bar"
293303 with patch (
@@ -338,6 +348,23 @@ def test_handle_duplicate_filenames_in_stacktrace(self):
338348 "sentry/tasks.py" ,
339349 ]
340350
351+ @with_feature ({"organizations:derive-code-mappings" : False })
352+ def test_feature_off (self ):
353+ event = self .store_event (data = self .test_data , project_id = self .project .id )
354+
355+ assert not RepositoryProjectPathConfig .objects .filter (project_id = self .project .id ).exists ()
356+
357+ with patch (
358+ "sentry.tasks.derive_code_mappings.identify_stacktrace_paths" ,
359+ return_value = {
360+ self .project : ["sentry/models/release.py" , "sentry/tasks.py" ],
361+ },
362+ ) as mock_identify_stacktraces , self .tasks ():
363+ derive_code_mappings (self .project .id , event .data )
364+
365+ assert mock_identify_stacktraces .call_count == 0
366+ assert not RepositoryProjectPathConfig .objects .filter (project_id = self .project .id ).exists ()
367+
341368 @patch ("sentry.integrations.github.GitHubIntegration.get_trees_for_org" )
342369 @patch (
343370 "sentry.integrations.utils.code_mapping.CodeMappingTreesHelper.generate_code_mappings" ,
@@ -349,6 +376,7 @@ def test_handle_duplicate_filenames_in_stacktrace(self):
349376 )
350377 ],
351378 )
379+ @with_feature ("organizations:derive-code-mappings" )
352380 def test_derive_code_mappings_single_project (
353381 self , mock_generate_code_mappings , mock_get_trees_for_org
354382 ):
@@ -386,6 +414,7 @@ def test_skips_not_supported_platforms(self):
386414 ],
387415 )
388416 @patch ("sentry.tasks.derive_code_mappings.logger" )
417+ @with_feature ("organizations:derive-code-mappings" )
389418 def test_derive_code_mappings_duplicates (
390419 self , mock_logger , mock_generate_code_mappings , mock_get_trees_for_org
391420 ):
@@ -422,7 +451,43 @@ def test_derive_code_mappings_duplicates(
422451 assert code_mapping .first ().automatically_generated is False
423452 assert mock_logger .info .call_count == 1
424453
454+ @patch ("sentry.integrations.github.GitHubIntegration.get_trees_for_org" )
455+ @patch (
456+ "sentry.integrations.utils.code_mapping.CodeMappingTreesHelper.generate_code_mappings" ,
457+ return_value = [
458+ CodeMapping (
459+ repo = Repo (name = "repo" , branch = "master" ),
460+ stacktrace_root = "sentry/models" ,
461+ source_path = "src/sentry/models" ,
462+ )
463+ ],
464+ )
465+ @patch ("sentry.tasks.derive_code_mappings.logger" )
466+ @with_feature ("organizations:derive-code-mappings" )
467+ def test_derive_code_mappings_dry_run (
468+ self , mock_logger , mock_generate_code_mappings , mock_get_trees_for_org
469+ ):
470+
471+ event = self .store_event (data = self .test_data , project_id = self .project .id )
472+
473+ assert not RepositoryProjectPathConfig .objects .filter (project_id = self .project .id ).exists ()
474+
475+ with patch (
476+ "sentry.tasks.derive_code_mappings.identify_stacktrace_paths" ,
477+ return_value = ["sentry/models/release.py" , "sentry/tasks.py" ],
478+ ) as mock_identify_stacktraces , self .tasks ():
479+ derive_code_mappings (self .project .id , event .data , dry_run = True )
480+
481+ assert mock_logger .info .call_count == 1
482+ assert mock_identify_stacktraces .call_count == 1
483+ assert mock_get_trees_for_org .call_count == 1
484+ assert mock_generate_code_mappings .call_count == 1
485+
486+ # We should not create the code mapping for dry runs
487+ assert not RepositoryProjectPathConfig .objects .filter (project_id = self .project .id ).exists ()
488+
425489 @responses .activate
490+ @with_feature ("organizations:derive-code-mappings" )
426491 def test_derive_code_mappings_stack_and_source_root_do_not_match (self ):
427492 repo_name = "foo/bar"
428493 with patch (
@@ -438,6 +503,7 @@ def test_derive_code_mappings_stack_and_source_root_do_not_match(self):
438503 assert code_mapping .source_root == "src/sentry/"
439504
440505 @responses .activate
506+ @with_feature ("organizations:derive-code-mappings" )
441507 def test_derive_code_mappings_no_normalization (self ):
442508 repo_name = "foo/bar"
443509 with patch (
0 commit comments