From 280868948fd011f129d683467999cc8dce3ed0ac Mon Sep 17 00:00:00 2001 From: Yashna Parikh Date: Tue, 6 Oct 2026 18:14:50 -0400 Subject: [PATCH 1/5] Fix False positive --- src/core/src/package_managers/YumPackageManager.py | 3 +-- src/core/tests/Test_YumPackageManager.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/core/src/package_managers/YumPackageManager.py b/src/core/src/package_managers/YumPackageManager.py index 27cbf846..8d6b46ca 100644 --- a/src/core/src/package_managers/YumPackageManager.py +++ b/src/core/src/package_managers/YumPackageManager.py @@ -950,8 +950,7 @@ def try_mitigate_issues_if_any(self, command, code, out, raise_on_exception=True self.log_error_mitigation_failure(out, raise_on_exception) return code, out - if "Error" in out or "Errno" in out: - + if ("Error" in out or "Errno" in out) and "perl-Errno" not in out: # Preemptively exit the retry loop if the same error string is repeating in the call stack. # This implies that self.check_known_issues_and_attempt_fix may have failed to mitigate the error. if out in seen_errors: diff --git a/src/core/tests/Test_YumPackageManager.py b/src/core/tests/Test_YumPackageManager.py index 44d0027a..43a4fc80 100644 --- a/src/core/tests/Test_YumPackageManager.py +++ b/src/core/tests/Test_YumPackageManager.py @@ -638,6 +638,19 @@ def test_auto_issue_mitigation_when_retries_are_exhausted_raise_exception_disabl self.assertEqual(out, expected_out) self.assertTrue(code >= 0) + def test_auto_issue_mitigation_skips_perl_errno_false_positive(self): + # "perl-Errno" contains the substring "Errno" but is not an actual error, so mitigation is skipped and output is returned unchanged. + expected_out = "Installing: perl-Errno-1.28-422.el8.x86_64" + + package_manager = self.container.get('package_manager') + self.assertTrue(package_manager) + + package_manager.check_known_issues_and_attempt_fix = lambda output: self.fail("perl-Errno must not enter error mitigation") + code, out = package_manager.try_mitigate_issues_if_any('testcmd', 0, expected_out) + + self.assertEqual(out, expected_out) + self.assertEqual(code, 0) + def test_disable_auto_os_updates_with_uninstalled_services(self): # no services are installed on the machine. expected o/p: function will complete successfully. Backup file will be created with default values, no auto OS update configuration settings will be updated as there are none self.runtime.set_legacy_test_type('SadPath') From 0bec50ff62be682ef1663e3c27dcc80b0fc491d4 Mon Sep 17 00:00:00 2001 From: Yashna Parikh Date: Tue, 6 Oct 2026 18:31:45 -0400 Subject: [PATCH 2/5] Address copilot suggestion --- .../src/package_managers/YumPackageManager.py | 5 ++++- src/core/tests/Test_YumPackageManager.py | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/core/src/package_managers/YumPackageManager.py b/src/core/src/package_managers/YumPackageManager.py index 8d6b46ca..ff5c5043 100644 --- a/src/core/src/package_managers/YumPackageManager.py +++ b/src/core/src/package_managers/YumPackageManager.py @@ -950,7 +950,10 @@ def try_mitigate_issues_if_any(self, command, code, out, raise_on_exception=True self.log_error_mitigation_failure(out, raise_on_exception) return code, out - if ("Error" in out or "Errno" in out) and "perl-Errno" not in out: + # Strip the benign "perl-Errno" package token so its "Errno" substring is not misread as an error marker, + # while still detecting any genuine errors present elsewhere in the output. + out_without_perl_errno = out.replace("perl-Errno", "") + if "Error" in out_without_perl_errno or "Errno" in out_without_perl_errno: # Preemptively exit the retry loop if the same error string is repeating in the call stack. # This implies that self.check_known_issues_and_attempt_fix may have failed to mitigate the error. if out in seen_errors: diff --git a/src/core/tests/Test_YumPackageManager.py b/src/core/tests/Test_YumPackageManager.py index 43a4fc80..4fef2218 100644 --- a/src/core/tests/Test_YumPackageManager.py +++ b/src/core/tests/Test_YumPackageManager.py @@ -651,6 +651,23 @@ def test_auto_issue_mitigation_skips_perl_errno_false_positive(self): self.assertEqual(out, expected_out) self.assertEqual(code, 0) + def test_auto_issue_mitigation_detects_real_error_alongside_perl_errno(self): + # A genuine error must still be mitigated even when a benign "perl-Errno" package line is present in the same output. + expected_out = "Installing: perl-Errno-1.28-422.el8.x86_64. Error: Failed to download metadata for repo 'rhui-rhel-8-for-x86_64-baseos-rhui-rpms'" + + package_manager = self.container.get('package_manager') + self.assertTrue(package_manager) + + mitigation_attempted = {'called': False} + def record_call(output): + mitigation_attempted['called'] = True + return False + package_manager.check_known_issues_and_attempt_fix = record_call + + package_manager.try_mitigate_issues_if_any('testcmd', 0, expected_out) + + self.assertTrue(mitigation_attempted['called']) + def test_disable_auto_os_updates_with_uninstalled_services(self): # no services are installed on the machine. expected o/p: function will complete successfully. Backup file will be created with default values, no auto OS update configuration settings will be updated as there are none self.runtime.set_legacy_test_type('SadPath') From 2a9657d50b242473e56bd45eaae5634371c05f1e Mon Sep 17 00:00:00 2001 From: Yashna Parikh Date: Wed, 7 Oct 2026 12:08:40 -0400 Subject: [PATCH 3/5] Address code review --- src/core/tests/Test_YumPackageManager.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/core/tests/Test_YumPackageManager.py b/src/core/tests/Test_YumPackageManager.py index 4fef2218..ff0ac494 100644 --- a/src/core/tests/Test_YumPackageManager.py +++ b/src/core/tests/Test_YumPackageManager.py @@ -49,6 +49,10 @@ def mock_linux7_distribution_to_return_redhat(self): def mock_linux8_distribution_to_return_redhat(self): return ['Red Hat Enterprise Linux Server', '8', 'Ootpa'] + + def mock_check_known_issues_and_attempt_fix_record_call(self, output): + self.check_known_issues_and_attempt_fix_called = True + return False #endregion Mocks # region Utility Functions @@ -645,9 +649,15 @@ def test_auto_issue_mitigation_skips_perl_errno_false_positive(self): package_manager = self.container.get('package_manager') self.assertTrue(package_manager) - package_manager.check_known_issues_and_attempt_fix = lambda output: self.fail("perl-Errno must not enter error mitigation") + self.check_known_issues_and_attempt_fix_called = False + backup_check_known_issues_and_attempt_fix = package_manager.check_known_issues_and_attempt_fix + package_manager.check_known_issues_and_attempt_fix = self.mock_check_known_issues_and_attempt_fix_record_call + code, out = package_manager.try_mitigate_issues_if_any('testcmd', 0, expected_out) + package_manager.check_known_issues_and_attempt_fix = backup_check_known_issues_and_attempt_fix + + self.assertFalse(self.check_known_issues_and_attempt_fix_called) self.assertEqual(out, expected_out) self.assertEqual(code, 0) @@ -658,15 +668,15 @@ def test_auto_issue_mitigation_detects_real_error_alongside_perl_errno(self): package_manager = self.container.get('package_manager') self.assertTrue(package_manager) - mitigation_attempted = {'called': False} - def record_call(output): - mitigation_attempted['called'] = True - return False - package_manager.check_known_issues_and_attempt_fix = record_call + self.check_known_issues_and_attempt_fix_called = False + backup_check_known_issues_and_attempt_fix = package_manager.check_known_issues_and_attempt_fix + package_manager.check_known_issues_and_attempt_fix = self.mock_check_known_issues_and_attempt_fix_record_call package_manager.try_mitigate_issues_if_any('testcmd', 0, expected_out) - self.assertTrue(mitigation_attempted['called']) + package_manager.check_known_issues_and_attempt_fix = backup_check_known_issues_and_attempt_fix + + self.assertTrue(self.check_known_issues_and_attempt_fix_called) def test_disable_auto_os_updates_with_uninstalled_services(self): # no services are installed on the machine. expected o/p: function will complete successfully. Backup file will be created with default values, no auto OS update configuration settings will be updated as there are none From 510c54d54322ac48c4d13aade8ad37ac617f3644 Mon Sep 17 00:00:00 2001 From: Yashna Parikh Date: Thu, 8 Oct 2026 13:10:38 -0400 Subject: [PATCH 4/5] Fix UT --- src/core/tests/Test_YumPackageManager.py | 33 ++++++++---------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/src/core/tests/Test_YumPackageManager.py b/src/core/tests/Test_YumPackageManager.py index ff0ac494..e5044f01 100644 --- a/src/core/tests/Test_YumPackageManager.py +++ b/src/core/tests/Test_YumPackageManager.py @@ -49,10 +49,6 @@ def mock_linux7_distribution_to_return_redhat(self): def mock_linux8_distribution_to_return_redhat(self): return ['Red Hat Enterprise Linux Server', '8', 'Ootpa'] - - def mock_check_known_issues_and_attempt_fix_record_call(self, output): - self.check_known_issues_and_attempt_fix_called = True - return False #endregion Mocks # region Utility Functions @@ -643,40 +639,33 @@ def test_auto_issue_mitigation_when_retries_are_exhausted_raise_exception_disabl self.assertTrue(code >= 0) def test_auto_issue_mitigation_skips_perl_errno_false_positive(self): - # "perl-Errno" contains the substring "Errno" but is not an actual error, so mitigation is skipped and output is returned unchanged. + # "perl-Errno" contains the substring "Errno" but is not an actual error, so no mitigation is triggered and the output is returned unchanged. expected_out = "Installing: perl-Errno-1.28-422.el8.x86_64" package_manager = self.container.get('package_manager') self.assertTrue(package_manager) - self.check_known_issues_and_attempt_fix_called = False - backup_check_known_issues_and_attempt_fix = package_manager.check_known_issues_and_attempt_fix - package_manager.check_known_issues_and_attempt_fix = self.mock_check_known_issues_and_attempt_fix_record_call - code, out = package_manager.try_mitigate_issues_if_any('testcmd', 0, expected_out) - package_manager.check_known_issues_and_attempt_fix = backup_check_known_issues_and_attempt_fix - - self.assertFalse(self.check_known_issues_and_attempt_fix_called) self.assertEqual(out, expected_out) self.assertEqual(code, 0) def test_auto_issue_mitigation_detects_real_error_alongside_perl_errno(self): - # A genuine error must still be mitigated even when a benign "perl-Errno" package line is present in the same output. - expected_out = "Installing: perl-Errno-1.28-422.el8.x86_64. Error: Failed to download metadata for repo 'rhui-rhel-8-for-x86_64-baseos-rhui-rpms'" + # A benign "perl-Errno" package line must not mask a genuine error in the same output + download_metadata_error = "Error: Failed to download metadata for repo 'rhui-rhel-8-for-x86_64-baseos-rhui-rpms': Cannot download repomd.xml: Cannot download repodata/repomd.xml: All mirrors were tried" + out_with_perl_errno = "Installing: perl-Errno-1.28-422.el8.x86_64. " + download_metadata_error + self.runtime.set_legacy_test_type('IssueMitigationRetryExitAfterMultipleAttempts') package_manager = self.container.get('package_manager') self.assertTrue(package_manager) - self.check_known_issues_and_attempt_fix_called = False - backup_check_known_issues_and_attempt_fix = package_manager.check_known_issues_and_attempt_fix - package_manager.check_known_issues_and_attempt_fix = self.mock_check_known_issues_and_attempt_fix_record_call + captured_output, original_stdout = self.__capture_std_io() + code, out = package_manager.try_mitigate_issues_if_any('testcmd', 0, out_with_perl_errno, raise_on_exception = False) + sys.stdout = original_stdout - package_manager.try_mitigate_issues_if_any('testcmd', 0, expected_out) - - package_manager.check_known_issues_and_attempt_fix = backup_check_known_issues_and_attempt_fix - - self.assertTrue(self.check_known_issues_and_attempt_fix_called) + # The genuine error entered the mitigation path and was matched against the known errors list. + self.__assert_std_io(captured_output, expected_output="[YPM] Found a match within known errors list, attempting a fix...") + self.assertTrue(code >= 0) def test_disable_auto_os_updates_with_uninstalled_services(self): # no services are installed on the machine. expected o/p: function will complete successfully. Backup file will be created with default values, no auto OS update configuration settings will be updated as there are none From 8851d54028ce99b830f3b1dcfddf9340fbae397a Mon Sep 17 00:00:00 2001 From: yashnap Date: Thu, 8 Oct 2026 13:55:58 -0400 Subject: [PATCH 5/5] Copilot suggestion Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> --- src/core/tests/Test_YumPackageManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/tests/Test_YumPackageManager.py b/src/core/tests/Test_YumPackageManager.py index e5044f01..a16997cd 100644 --- a/src/core/tests/Test_YumPackageManager.py +++ b/src/core/tests/Test_YumPackageManager.py @@ -665,7 +665,7 @@ def test_auto_issue_mitigation_detects_real_error_alongside_perl_errno(self): # The genuine error entered the mitigation path and was matched against the known errors list. self.__assert_std_io(captured_output, expected_output="[YPM] Found a match within known errors list, attempting a fix...") - self.assertTrue(code >= 0) + self.assertGreaterEqual(code, 0) def test_disable_auto_os_updates_with_uninstalled_services(self): # no services are installed on the machine. expected o/p: function will complete successfully. Backup file will be created with default values, no auto OS update configuration settings will be updated as there are none