@@ -132,7 +132,7 @@ def setUp(self) -> None:
132
132
lineno = 30 ,
133
133
commit = CommitInfo (
134
134
commitId = "commit-id-old" ,
135
- committedDate = datetime .now (tz = datetime_timezone .utc ) - timedelta (days = 62 ),
135
+ committedDate = datetime .now (tz = datetime_timezone .utc ) - timedelta (days = 70 ),
136
136
commitMessage = "old commit message" ,
137
137
commitAuthorName = None ,
138
138
commitAuthorEmail = "old@localhost" ,
@@ -658,66 +658,122 @@ def test_failure_no_blames(
658
658
},
659
659
)
660
660
661
- @patch ("sentry.integrations.utils.commit_context.logger.info" )
662
661
@patch ("sentry.tasks.groupowner.process_suspect_commits.delay" )
663
662
@patch ("sentry.analytics.record" )
664
663
@patch (
665
664
"sentry.integrations.github.integration.GitHubIntegration.get_commit_context_all_frames" ,
666
665
)
667
- def test_failure_old_blame (
668
- self , mock_get_commit_context , mock_record , mock_process_suspect_commits , mock_logger_info
666
+ def test_time_threshold_filtering (
667
+ self , mock_get_commit_context , mock_record , mock_process_suspect_commits
669
668
):
670
669
"""
671
- A failure case where the only blame returned is past the time threshold. We bail out and fall back
672
- to the release-based suspect commits.
670
+ A hacky way to run a parameterized test in TestCase.
671
+ Tests the logic that filters commits by age relative to issue first_seen .
673
672
"""
674
- mock_get_commit_context .return_value = [self .blame_too_old ]
675
- with self .tasks ():
676
- assert not GroupOwner .objects .filter (group = self .event .group ).exists ()
677
- event_frames = get_frame_paths (self .event )
678
- process_commit_context (
679
- event_id = self .event .event_id ,
680
- event_platform = self .event .platform ,
681
- event_frames = event_frames ,
682
- group_id = self .event .group_id ,
683
- project_id = self .event .project_id ,
684
- sdk_name = "sentry.python" ,
685
- )
673
+ # Test cases: each tuple contains (test_case_name, blames_setup, group_first_seen_days_ago, expected_group_owner_exists, expected_commit_id)
674
+ test_cases = [
675
+ # All commits too young
676
+ ("all_commits_too_young" , ["blame_recent" ], 3 , False , None ),
677
+ # Early exit on too old
678
+ ("early_exit_too_old" , ["blame_too_old" , "blame_existing_commit" ], 10 , False , None ),
679
+ # All outside threshold
680
+ ("mixed_young_then_old" , ["blame_recent" , "blame_too_old" ], 10 , False , None ),
681
+ # Skip young, find valid
682
+ (
683
+ "skip_young_find_valid" ,
684
+ ["blame_recent" , "blame_existing_commit" ],
685
+ 5 ,
686
+ True ,
687
+ "existing-commit" ,
688
+ ),
689
+ # All valid, picks most recent
690
+ (
691
+ "all_valid_picks_most_recent" ,
692
+ ["blame_existing_commit" , "blame_no_existing_commit" ],
693
+ 5 ,
694
+ True ,
695
+ "existing-commit" ,
696
+ ),
697
+ # All commits too old
698
+ ("only_old_commits" , ["blame_too_old" ], 10 , False , None ),
699
+ # Empty blames
700
+ ("empty_blames" , [], 10 , False , None ),
701
+ ]
686
702
687
- assert not GroupOwner .objects .filter (group = self .event .group ).exists ()
688
- mock_process_suspect_commits .assert_called_once_with (
689
- event_id = self .event .event_id ,
690
- event_platform = self .event .platform ,
691
- event_frames = event_frames ,
692
- group_id = self .event .group_id ,
693
- project_id = self .event .project_id ,
694
- sdk_name = "sentry.python" ,
703
+ existing_commit = self .create_commit (
704
+ project = self .project ,
705
+ repo = self .repo ,
706
+ author = self .commit_author ,
707
+ key = "existing-commit" ,
695
708
)
709
+ existing_commit .update (message = "" )
710
+
711
+ # Map blame names to actual blame objects
712
+ blame_mapping = {
713
+ "blame_recent" : self .blame_recent ,
714
+ "blame_too_old" : self .blame_too_old ,
715
+ "blame_existing_commit" : self .blame_existing_commit ,
716
+ "blame_no_existing_commit" : self .blame_no_existing_commit ,
717
+ }
696
718
697
- assert_any_analytics_event (
698
- mock_record ,
699
- IntegrationsFailedToFetchCommitContextAllFrames (
700
- organization_id = self .organization .id ,
701
- project_id = self .project .id ,
702
- group_id = self .event .group_id ,
703
- event_id = self .event .event_id ,
704
- num_frames = 1 ,
705
- num_successfully_mapped_frames = 1 ,
706
- reason = "commit_too_old" ,
707
- ),
708
- )
719
+ for (
720
+ case_name ,
721
+ blames_setup ,
722
+ group_first_seen_days_ago ,
723
+ expected_group_owner_exists ,
724
+ expected_commit_id ,
725
+ ) in test_cases :
726
+ with self .subTest (case = case_name ):
727
+ # Reset mocks for each test case
728
+ mock_get_commit_context .reset_mock ()
729
+ mock_record .reset_mock ()
730
+
731
+ # Clean up any existing GroupOwners from previous test cases
732
+ GroupOwner .objects .filter (group = self .event .group ).delete ()
733
+
734
+ # Setup group first_seen
735
+ group_first_seen = datetime .now (tz = datetime_timezone .utc ) - timedelta (
736
+ days = group_first_seen_days_ago
737
+ )
738
+ self .event .group .first_seen = group_first_seen
739
+ self .event .group .save ()
740
+
741
+ # Build the mock return value
742
+ mock_blames = [blame_mapping [blame_name ] for blame_name in blames_setup ]
743
+ mock_get_commit_context .return_value = mock_blames
744
+
745
+ with self .tasks ():
746
+ assert not GroupOwner .objects .filter (group = self .event .group ).exists ()
747
+ event_frames = get_frame_paths (self .event )
748
+
749
+ with self .options ({"issues.suspect-commit-strategy" : True }):
750
+ process_commit_context (
751
+ event_id = self .event .event_id ,
752
+ event_platform = self .event .platform ,
753
+ event_frames = event_frames ,
754
+ group_id = self .event .group_id ,
755
+ project_id = self .event .project_id ,
756
+ sdk_name = "sentry.python" ,
757
+ )
758
+
759
+ # Assert GroupOwner existence
760
+ group_owners = GroupOwner .objects .filter (group = self .event .group )
761
+ actual_exists = group_owners .exists ()
762
+ self .assertEqual (
763
+ actual_exists ,
764
+ expected_group_owner_exists ,
765
+ f"GroupOwner existence assertion failed for case: { case_name } " ,
766
+ )
709
767
710
- mock_logger_info .assert_any_call (
711
- "process_commit_context_all_frames.find_commit_context_failed" ,
712
- extra = {
713
- "organization" : self .organization .id ,
714
- "group" : self .event .group_id ,
715
- "event" : self .event .event_id ,
716
- "project_id" : self .project .id ,
717
- "reason" : "commit_too_old" ,
718
- "num_frames" : 1 ,
719
- },
720
- )
768
+ # Assert correct commit selected
769
+ if expected_group_owner_exists and expected_commit_id :
770
+ created_commit = Commit .objects .get (key = expected_commit_id )
771
+ group_owner = group_owners .first ()
772
+ self .assertEqual (
773
+ group_owner .context ["commitId" ],
774
+ created_commit .id ,
775
+ f"Wrong commit selected for case: { case_name } " ,
776
+ )
721
777
722
778
@patch ("sentry.tasks.groupowner.process_suspect_commits.delay" )
723
779
@patch (
0 commit comments