From c44c5285cd93a6d045562d45654f9b6d36567949 Mon Sep 17 00:00:00 2001 From: Mike McLean Date: Apr 16 2026 21:42:31 +0000 Subject: [PATCH 1/5] unit test fixes for rhel8 compat --- diff --git a/devtools/check-api b/devtools/check-api index d185358..a85973a 100755 --- a/devtools/check-api +++ b/devtools/check-api @@ -9,7 +9,7 @@ from collections import OrderedDict # import koji code from our checkout if os.path.exists(__file__): - kojitop = os.path.dirname(os.path.dirname(__file__)) + kojitop = os.path.normpath(os.path.dirname(__file__) + '/..') else: # e.g. kojitop = os.getcwd() diff --git a/tests/test_cli/test_import_cg.py b/tests/test_cli/test_import_cg.py index b229e10..f163de3 100644 --- a/tests/test_cli/test_import_cg.py +++ b/tests/test_cli/test_import_cg.py @@ -21,7 +21,8 @@ class TestImportCG(utils.CliTestCase): self.session = mock.MagicMock() self.workdir = tempfile.mkdtemp() self.outdir = self.workdir + '/output' - self.unique_path_mock = mock.patch('koji_cli.commands.unique_path').start() + self.unique_path_mock = mock.patch('koji_cli.commands.unique_path', + return_value='cli-import/12345.foobar').start() self.running_in_bg = mock.patch('koji_cli.commands._running_in_bg').start() self.running_in_bg.return_value = False self.linked_upload = mock.patch('koji_cli.commands.linked_upload').start() @@ -82,10 +83,10 @@ class TestImportCG(utils.CliTestCase): handle_import_cg(self.options, self.session, arguments) self.assertEqual(len(self.session.uploadWrapper.mock_calls), len(metadata['output'])) - kwargs = self.session.uploadWrapper.call_args.kwargs + kwargs = self.session.uploadWrapper.call_args[1] self.assertEqual(kwargs['callback'], _progress_callback) self.session.CGImport.assert_called_once() - args = self.session.CGImport.call_args.args + args = self.session.CGImport.call_args[0] self.assertEqual(args[0], metadata) self.linked_upload.assert_not_called() @@ -172,7 +173,7 @@ class TestImportCG(utils.CliTestCase): self.session.uploadWrapper.assert_not_called() self.linked_upload.assert_not_called() self.session.CGImport.assert_called_once() - args = self.session.CGImport.call_args.args + args = self.session.CGImport.call_args[0] self.assertEqual(args[0], metadata) def test_handle_import_cg_draft(self): @@ -189,7 +190,7 @@ class TestImportCG(utils.CliTestCase): self.assertEqual(len(self.session.uploadWrapper.mock_calls), len(metadata['output'])) self.session.CGImport.assert_called_once() - args = self.session.CGImport.call_args.args + args = self.session.CGImport.call_args[0] self.assertEqual(args[0], metadata) def test_handle_import_cg_reserve(self): @@ -206,7 +207,7 @@ class TestImportCG(utils.CliTestCase): self.assertEqual(len(self.session.uploadWrapper.mock_calls), len(metadata['output'])) self.session.CGImport.assert_called_once() - args = self.session.CGImport.call_args.args + args = self.session.CGImport.call_args[0] self.assertEqual(args[0], expect) def test_handle_import_cg_linked(self): @@ -220,7 +221,7 @@ class TestImportCG(utils.CliTestCase): self.session.uploadWrapper.assert_not_called() self.assertEqual(len(self.linked_upload.mock_calls), len(metadata['output'])) self.session.CGImport.assert_called_once() - args = self.session.CGImport.call_args.args + args = self.session.CGImport.call_args[0] self.assertEqual(args[0], metadata) def test_handle_import_cg_noprogress(self): @@ -232,10 +233,10 @@ class TestImportCG(utils.CliTestCase): handle_import_cg(self.options, self.session, arguments) self.assertEqual(len(self.session.uploadWrapper.mock_calls), len(metadata['output'])) - kwargs = self.session.uploadWrapper.call_args.kwargs + kwargs = self.session.uploadWrapper.call_args[1] self.assertEqual(kwargs['callback'], None) self.session.CGImport.assert_called_once() - args = self.session.CGImport.call_args.args + args = self.session.CGImport.call_args[0] self.assertEqual(args[0], metadata) self.linked_upload.assert_not_called() diff --git a/tests/test_hub/test_repo_requests.py b/tests/test_hub/test_repo_requests.py index 3192af6..3f9562d 100644 --- a/tests/test_hub/test_repo_requests.py +++ b/tests/test_hub/test_repo_requests.py @@ -599,8 +599,8 @@ class TestQueueTask(BaseTest): repos.repo_queue_task(req) self.make_task.assert_called_once() - method, args = self.make_task.call_args.args - taskopts = self.make_task.call_args.kwargs + method, args = self.make_task.call_args[0] + taskopts = self.make_task.call_args[1] self.assertEqual(method, 'newRepo') self.assertEqual(taskopts['channel'], 'createrepo') params = koji.tasks.parse_task_params('newRepo', args) @@ -633,7 +633,7 @@ class TestUpdateEndEvents(BaseTest): self.tag_last_change_event.assert_not_called() expect = [{'id': 1, 'end_event': 1001}] self.BulkUpdateProcessor.assert_called_once() - updates = self.BulkUpdateProcessor.call_args.kwargs['data'] + updates = self.BulkUpdateProcessor.call_args[1]['data'] self.assertEqual(updates, expect) def test_event_cache(self): @@ -662,7 +662,7 @@ class TestUpdateEndEvents(BaseTest): self.assertEqual(self.tag_first_change_event.mock_calls, expect_calls) expect_updates = [{'id': 4, 'end_event': 1000}] self.BulkUpdateProcessor.assert_called_once() - updates = self.BulkUpdateProcessor.call_args.kwargs['data'] + updates = self.BulkUpdateProcessor.call_args[1]['data'] self.assertEqual(updates, expect_updates) @@ -799,14 +799,14 @@ class TestGetRepo(BaseTest): repos.get_repo('TAGID', at_event=101010) self.RepoQuery.assert_called_once() - clauses, fields, qopts = self.RepoQuery.call_args.args + clauses, fields, qopts = self.RepoQuery.call_args[0] self.assertIn(['create_event', '=', 101010], clauses) def test_get_repo_min_event(self): repos.get_repo('TAGID', min_event=101010) self.RepoQuery.assert_called_once() - clauses, fields, qopts = self.RepoQuery.call_args.args + clauses, fields, qopts = self.RepoQuery.call_args[0] self.assertIn(['create_event', '>=', 101010], clauses) @@ -996,12 +996,12 @@ class TestRequestRepo(BaseTest): # check all the calls made with the value self.get_repo.assert_called_once() - ev = self.get_repo.call_args.kwargs['min_event'] + ev = self.get_repo.call_args[1]['min_event'] self.assertEqual(ev, last) clauses = self.RepoQueueQuery.call_args_list[0].args[0] self.assertIn(['min_event', '>=', last], clauses) self.InsertProcessor.assert_called_once() - data = self.InsertProcessor.call_args.kwargs['data'] + data = self.InsertProcessor.call_args[1]['data'] self.assertEqual(data['min_event'], last) def test_request_priority(self): @@ -1015,7 +1015,7 @@ class TestRequestRepo(BaseTest): # check all the calls made with the value self.InsertProcessor.assert_called_once() - data = self.InsertProcessor.call_args.kwargs['data'] + data = self.InsertProcessor.call_args[1]['data'] self.assertEqual(data['min_event'], ev + 10) # tag last change self.assertEqual(data['priority'], 25) # default + 5 @@ -1106,12 +1106,12 @@ class TestRequestRepo(BaseTest): # check all the calls made with the value self.get_repo.assert_called_once() - ev = self.get_repo.call_args.kwargs['min_event'] + ev = self.get_repo.call_args[1]['min_event'] self.assertEqual(ev, last) clauses = self.RepoQueueQuery.call_args_list[0].args[0] self.assertIn(['min_event', '>=', last], clauses) self.InsertProcessor.assert_called_once() - data = self.InsertProcessor.call_args.kwargs['data'] + data = self.InsertProcessor.call_args[1]['data'] self.assertEqual(data['min_event'], last) @@ -1253,7 +1253,7 @@ class TestDefaultMinEvent(BaseTest): # we should report the ts from the last event, minus lag self.assertEqual(ev, 9999) self.getLastEvent.assert_called_once() - base_ts = self.getLastEvent.call_args.kwargs['before'] + base_ts = self.getLastEvent.call_args[1]['before'] self.assertEqual(base_ts, now - 3600) def test_tag_older(self): @@ -1269,7 +1269,7 @@ class TestDefaultMinEvent(BaseTest): # we should report the ts for the tag, since it is older self.assertEqual(ev, 9900) self.getLastEvent.assert_called_once() - base_ts = self.getLastEvent.call_args.kwargs['before'] + base_ts = self.getLastEvent.call_args[1]['before'] self.assertEqual(base_ts, now - 3600) def test_lag_override(self): @@ -1285,7 +1285,7 @@ class TestDefaultMinEvent(BaseTest): # we should report the ts from the last event, minus lag self.assertEqual(ev, 9999) self.getLastEvent.assert_called_once() - base_ts = self.getLastEvent.call_args.kwargs['before'] + base_ts = self.getLastEvent.call_args[1]['before'] self.assertEqual(base_ts, now - 1800) def test_lag_override_invalid(self): @@ -1301,7 +1301,7 @@ class TestDefaultMinEvent(BaseTest): # we should report the ts from the last event, minus lag self.assertEqual(ev, 9999) self.getLastEvent.assert_called_once() - base_ts = self.getLastEvent.call_args.kwargs['before'] + base_ts = self.getLastEvent.call_args[1]['before'] self.assertEqual(base_ts, now - 3600) def test_no_last_event(self): @@ -1405,32 +1405,32 @@ class TestExports(BaseTest): with self.assertRaises(koji.ActionNotAllowed): self.exports.setRequestPriority(99, 1) self.set_request_priority.assert_not_called() - self.assertEqual(self.assertPerm.call_args.args, ('admin',)) + self.assertEqual(self.assertPerm.call_args[0], ('admin',)) with self.assertRaises(koji.ActionNotAllowed): self.exports.setState(99, 1) self.repo_set_state.assert_not_called() - self.assertEqual(self.assertPerm.call_args.args, ('repo',)) + self.assertEqual(self.assertPerm.call_args[0], ('repo',)) with self.assertRaises(koji.ActionNotAllowed): self.exports.setExternalRepoData(99, 1) self.set_external_repo_data.assert_not_called() - self.assertEqual(self.assertPerm.call_args.args, ('repo',)) + self.assertEqual(self.assertPerm.call_args[0], ('repo',)) with self.assertRaises(koji.ActionNotAllowed): self.exports.autoRequests() self.do_auto_requests.assert_not_called() - self.assertEqual(self.assertPerm.call_args.args, ('repo',)) + self.assertEqual(self.assertPerm.call_args[0], ('repo',)) with self.assertRaises(koji.ActionNotAllowed): self.exports.checkQueue() self.check_repo_queue.assert_not_called() - self.assertEqual(self.assertPerm.call_args.args, ('repo',)) + self.assertEqual(self.assertPerm.call_args[0], ('repo',)) with self.assertRaises(koji.ActionNotAllowed): self.exports.updateEndEvents() self.update_end_events.assert_not_called() - self.assertEqual(self.assertPerm.call_args.args, ('repo',)) + self.assertEqual(self.assertPerm.call_args[0], ('repo',)) def test_with_perm(self): # assertPerm does not raise diff --git a/tests/test_hub/test_wrapper_rpm.py b/tests/test_hub/test_wrapper_rpm.py index d3e2937..0c782f7 100644 --- a/tests/test_hub/test_wrapper_rpm.py +++ b/tests/test_hub/test_wrapper_rpm.py @@ -102,7 +102,7 @@ class TestWrapperRPM(unittest.TestCase): self.exports.wrapperRPM(self.build, self.url, self.target, opts=opts) self.make_task.assert_called_once() - taskargs = self.make_task.call_args.args[1] + taskargs = self.make_task.call_args[0][1] expect_opts = {'scratch': True} # repo_id filtered out self.assertEqual(taskargs[4], expect_opts) diff --git a/tests/test_lib/test_utils.py b/tests/test_lib/test_utils.py index 18fdcf1..6d3ae4c 100644 --- a/tests/test_lib/test_utils.py +++ b/tests/test_lib/test_utils.py @@ -1521,7 +1521,7 @@ class TestRmtree(unittest.TestCase): self.assertEqual(rmtree_nofork.call_args[0][0], path) _exit.assert_called_once() if mock.__package__ == 'unittest': - logger = rmtree_nofork.call_args.kwargs['logger'] + logger = rmtree_nofork.call_args[1]['logger'] else: logger = rmtree_nofork.call_args[1]['logger'] From e56791db39c71695d57ceb70afa2faa7bca7da45 Mon Sep 17 00:00:00 2001 From: Mike McLean Date: Apr 16 2026 21:42:31 +0000 Subject: [PATCH 2/5] adjust pathinfo mocking --- diff --git a/tests/test_hub/test_import_image_internal.py b/tests/test_hub/test_import_image_internal.py index ec1c6c3..61ca234 100644 --- a/tests/test_hub/test_import_image_internal.py +++ b/tests/test_hub/test_import_image_internal.py @@ -4,6 +4,7 @@ import shutil import tempfile import unittest +import koji import kojihub @@ -16,9 +17,9 @@ class TestImportImageInternal(unittest.TestCase): self.Task.return_value.assertHost = mock.MagicMock() self.get_build = mock.patch('kojihub.kojihub.get_build').start() self.get_archive_type = mock.patch('kojihub.kojihub.get_archive_type').start() - self.path_work = mock.patch('koji.pathinfo.work').start() + self.pathinfo = koji.PathInfo(self.tempdir) + mock.patch('koji.pathinfo', new=self.pathinfo).start() self.import_archive = mock.patch('kojihub.kojihub.import_archive').start() - self.build = mock.patch('koji.pathinfo.build').start() self.get_rpm = mock.patch('kojihub.kojihub.get_rpm').start() def tearDown(self): @@ -45,8 +46,7 @@ class TestImportImageInternal(unittest.TestCase): 'release': 'release', } self.get_archive_type.return_value = 4 - self.path_work.return_value = self.tempdir - os.makedirs(self.tempdir + "/tasks/1/1") + os.makedirs(self.tempdir + "/work/tasks/1/1") kojihub.importImageInternal( task_id=1, build_info=self.get_build.return_value, imgdata=imgdata) @@ -85,13 +85,11 @@ class TestImportImageInternal(unittest.TestCase): self.get_build.return_value = build_info self.get_rpm.return_value = rpm self.get_archive_type.return_value = 4 - self.path_work.return_value = self.tempdir - self.build.return_value = self.tempdir self.import_archive.return_value = { 'id': 9, 'filename': self.tempdir + '/foo.archive', } - workdir = self.tempdir + "/tasks/1/1" + workdir = self.tempdir + "/work/tasks/1/1" os.makedirs(workdir) # Create a log file to exercise that code path with open(workdir + '/foo.log', 'w'): @@ -102,7 +100,7 @@ class TestImportImageInternal(unittest.TestCase): # Check that the log symlink made it to where it was supposed to. dest = os.readlink(workdir + '/foo.log') dest = os.path.abspath(os.path.join(workdir, dest)) - self.assertEqual(dest, self.tempdir + '/data/logs/image/foo.log') + self.assertEqual(dest, self.tempdir + '/packages/name/version/release/data/logs/image/foo.log') # And.. check all the sql statements self.assertEqual(len(cursor.execute.mock_calls), 1) @@ -135,13 +133,11 @@ class TestImportImageInternal(unittest.TestCase): self.context_db.session.host_id = 42 self.get_build.return_value = build_info self.get_archive_type.return_value = 4 - self.path_work.return_value = self.tempdir - self.build.return_value = self.tempdir self.import_archive.return_value = { 'id': 9, 'filename': self.tempdir + '/foo.archive', } - workdir = self.tempdir + "/tasks/1/1" + workdir = self.tempdir + "/work/tasks/1/1" os.makedirs(workdir) # Create a log file to exercise that code path with open(workdir + '/foo.log', 'w'): @@ -149,8 +145,8 @@ class TestImportImageInternal(unittest.TestCase): # Create a same named log file already present # This should force the function to add arch to the final path - os.makedirs(self.tempdir + '/data/logs/image') - with open(self.tempdir + '/data/logs/image/foo.log', 'w'): + os.makedirs(self.tempdir + '/packages/name/version/release/data/logs/image') + with open(self.tempdir + '/packages/name/version/release/data/logs/image/foo.log', 'w'): pass kojihub.importImageInternal(task_id=1, build_info=build_info, imgdata=imgdata) @@ -158,7 +154,7 @@ class TestImportImageInternal(unittest.TestCase): # Check that the log symlink made it to where it was supposed to. dest = os.readlink(workdir + '/foo.log') dest = os.path.abspath(os.path.join(workdir, dest)) - self.assertEqual(dest, self.tempdir + '/data/logs/image/x86_64/foo.log') + self.assertEqual(dest, self.tempdir + '/packages/name/version/release/data/logs/image/x86_64/foo.log') def test_with_livemedia_task(self): taskinfo = {'id': 101010, 'method': 'livemedia'} @@ -182,13 +178,11 @@ class TestImportImageInternal(unittest.TestCase): self.context_db.session.host_id = 42 self.get_build.return_value = build_info self.get_archive_type.return_value = 4 - self.path_work.return_value = self.tempdir - self.build.return_value = self.tempdir self.import_archive.return_value = { 'id': 9, 'filename': self.tempdir + '/foo.archive', } - workdir = self.tempdir + "/tasks/1/1" + workdir = self.tempdir + "/work/tasks/1/1" os.makedirs(workdir) # Create a log file to exercise that code path with open(workdir + '/foo.log', 'w'): @@ -199,7 +193,7 @@ class TestImportImageInternal(unittest.TestCase): # Check that the log symlink made it to where it was supposed to. dest = os.readlink(workdir + '/foo.log') dest = os.path.abspath(os.path.join(workdir, dest)) - self.assertEqual(dest, self.tempdir + '/data/logs/image/x86_64/foo.log') + self.assertEqual(dest, self.tempdir + '/packages/name/version/release/data/logs/image/x86_64/foo.log') # the end From b7bc522f5909a95e1bfcfd2d730923afd4b586f7 Mon Sep 17 00:00:00 2001 From: Mike McLean Date: Apr 16 2026 21:42:31 +0000 Subject: [PATCH 3/5] more compat fix for mocks --- diff --git a/tests/test_hub/test_repo_requests.py b/tests/test_hub/test_repo_requests.py index 3f9562d..fa21b5f 100644 --- a/tests/test_hub/test_repo_requests.py +++ b/tests/test_hub/test_repo_requests.py @@ -998,7 +998,7 @@ class TestRequestRepo(BaseTest): self.get_repo.assert_called_once() ev = self.get_repo.call_args[1]['min_event'] self.assertEqual(ev, last) - clauses = self.RepoQueueQuery.call_args_list[0].args[0] + clauses = self.RepoQueueQuery.call_args_list[0][0][0] self.assertIn(['min_event', '>=', last], clauses) self.InsertProcessor.assert_called_once() data = self.InsertProcessor.call_args[1]['data'] @@ -1108,7 +1108,7 @@ class TestRequestRepo(BaseTest): self.get_repo.assert_called_once() ev = self.get_repo.call_args[1]['min_event'] self.assertEqual(ev, last) - clauses = self.RepoQueueQuery.call_args_list[0].args[0] + clauses = self.RepoQueueQuery.call_args_list[0][0][0] self.assertIn(['min_event', '>=', last], clauses) self.InsertProcessor.assert_called_once() data = self.InsertProcessor.call_args[1]['data'] From 654c519799c2a0f44f33f31e59632ce9f71e8964 Mon Sep 17 00:00:00 2001 From: Mike McLean Date: Apr 16 2026 21:42:31 +0000 Subject: [PATCH 4/5] path.join doesn't like mocks on el8 --- diff --git a/tests/test_plugins/test_kiwi_builder.py b/tests/test_plugins/test_kiwi_builder.py index 7c7b4fc..0a0c7d2 100644 --- a/tests/test_plugins/test_kiwi_builder.py +++ b/tests/test_plugins/test_kiwi_builder.py @@ -19,6 +19,7 @@ class TestKiwiBuildTask(unittest.TestCase): def SCM(self, *args, **kwargs): scm = mock.MagicMock() scm.assert_allowed = mock.MagicMock() + scm.checkout.return_value = '/tmp/NO-SUCH-PATH' self.scm.append(scm) return scm From 796d54fdb7395788e3a8f10f2aba3d69828e8d9c Mon Sep 17 00:00:00 2001 From: Mike McLean Date: Apr 16 2026 21:42:31 +0000 Subject: [PATCH 5/5] skip api test on older pythons --- diff --git a/tests/test_api/test_api.py b/tests/test_api/test_api.py index ba561b6..c4d8777 100644 --- a/tests/test_api/test_api.py +++ b/tests/test_api/test_api.py @@ -1,4 +1,5 @@ import os.path +import sys import subprocess import unittest @@ -11,6 +12,8 @@ class TestAPICompat(unittest.TestCase): # base api data, as generated by dump-api BASE = os.path.relpath(os.path.dirname(__file__) + '/data/api.json') + # this test can be sensitive to wide python version gaps + @unittest.skipIf(sys.version_info[:2] < (3, 9), "Skipped on legacy platforms") def test_api_compat(self): cmd = [self.SCRIPT, self.BASE] proc = subprocess.Popen(cmd, stderr=subprocess.PIPE)