From 8d0ef68bfa9c93d3016c3a7da9538794b50ea404 Mon Sep 17 00:00:00 2001
From: Tomas Kopecek <tkopecek@redhat.com>
Date: Aug 28 2024 13:54:17 +0000
Subject: Basic tests for kiwi plugin


Related: https://pagure.io/koji/issue/4179

---

diff --git a/tests/test_builder/test_taskparams.py b/tests/test_builder/test_taskparams.py
index a21a103..701df02 100644
--- a/tests/test_builder/test_taskparams.py
+++ b/tests/test_builder/test_taskparams.py
@@ -63,7 +63,8 @@ class TestParseTaskParams(unittest.TestCase):
             # match
             self.assertIn(list(spec), koji.tasks.LEGACY_SIGNATURES[method])
 
-        external = ['runroot', 'saveFailedTree', 'vmExec', 'winbuild']
+        external = ['runroot', 'saveFailedTree', 'vmExec', 'winbuild',
+                    'kiwiBuild', 'createKiwiImage']
         missing = [m for m in missing if m not in external]
         if missing:
             raise Exception('Unable to test legacy signatures. Missing: '
diff --git a/tests/test_plugins/test_kiwi_builder.py b/tests/test_plugins/test_kiwi_builder.py
new file mode 100644
index 0000000..2278ece
--- /dev/null
+++ b/tests/test_plugins/test_kiwi_builder.py
@@ -0,0 +1,228 @@
+import mock
+import tempfile
+import unittest
+
+import koji
+# inject builder data
+from tests.test_builder.loadkojid import kojid
+import __main__
+__main__.BuildRoot = kojid.BuildRoot
+__main__.BaseBuildTask = kojid.BaseBuildTask
+__main__.BuildImageTask = kojid.BuildImageTask
+__main__.SCM = kojid.SCM
+
+from plugins.builder import kiwi
+
+
+class TestKiwiBuildTask(unittest.TestCase):
+    def SCM(self, *args, **kwargs):
+        scm = mock.MagicMock()
+        scm.assert_allowed = mock.MagicMock()
+        self.scm.append(scm)
+        return scm
+
+    def setUp(self):
+        self.scm = []
+        self.session = mock.MagicMock()
+        self.options = mock.MagicMock()
+        self.options.allowed_scms = 'allowed_scms'
+        self.options.allowed_scms_use_config = False
+        self.options.allowed_scms_use_policy = True
+        kiwi.SCM = mock.MagicMock(side_effect=self.SCM)
+        koji.ensuredir = mock.MagicMock()
+        self.task = kiwi.KiwiBuildTask(123, 'kiwiBuild', {}, self.session, self.options)
+
+    def test_get_nrvp_invalid_xml(self):
+        # missing file
+        with self.assertRaises(koji.GenericError):
+            self.task.get_nvrp('/dev/null/non_existent_path')
+
+        # empty file
+        with tempfile.NamedTemporaryFile(delete_on_close=False) as fp:
+            fp.write(b'')
+            fp.close()
+            with self.assertRaises(koji.GenericError):
+                self.task.get_nvrp(fp.name)
+
+        # empty xml
+        with tempfile.NamedTemporaryFile(delete_on_close=False) as fp:
+            fp.write(b'<?xml version="1.0"?><test></test>')
+            fp.close()
+            with self.assertRaises(koji.GenericError):
+                self.task.get_nvrp(fp.name)
+
+    def test_get_nrvp_correct(self):
+        # minimal correct xml
+        with tempfile.NamedTemporaryFile(delete_on_close=False) as fp:
+            fp.write(b'''<?xml version="1.0" encoding="utf-8"?>
+                    <image schemaversion="7.4" name="Fedora-34.0_disk">
+                        <profiles>
+                            <profile name="Base" description="Base System"
+                                     import="true" image="true"/>
+                        </profiles>
+                        <preferences>
+                            <version>1.0.0</version>
+                        </preferences>
+                    </image>
+            ''')
+            fp.close()
+            name, version, profile = self.task.get_nvrp(fp.name)
+            self.assertEqual(name, 'Fedora-34.0_disk')
+            self.assertEqual(version, '1.0.0')
+            self.assertEqual(profile, 'Base')
+
+    def test_handler_correct(self, arches=None):
+        if arches is None:
+            arches = ['arch1', 'arch2']
+        self.session.getBuildTarget.return_value = {
+            'id': 1,
+            'name': 'target',
+            'build_tag': 123,
+            'build_tag_name': 'build_tag',
+            'dest_tag': 321,
+            'dest_tag_name': 'dest_tag'
+        }
+        self.task.getRepo = mock.MagicMock()
+        self.task.getRepo.return_value = {
+            'create_event': 59911321,
+            'create_ts': 1724763098.15199,
+            'creation_time': '2024-08-27 12:51:38.151991',
+            'dist': False,
+            'id': 8561958,
+            'state': 1,
+            'task_id': 63698179
+        }
+        self.session.getBuildConfig.return_value = {
+            'arches': 'arch1 arch2',
+            'id': 1234,
+            'extra': {},
+        }
+        self.task.run_callbacks = mock.MagicMock()
+        self.task.initImageBuild = mock.MagicMock()
+        self.task.initImageBuild.return_value = {
+            'id': 98,
+            'name': 'image_name',
+            'version': 'image_version',
+            'release': 'image_release',
+        }
+        self.task.get_nvrp = mock.MagicMock()
+        self.task.get_nvrp.return_value = 'name', 'version', 'profile'
+        self.task.wait = mock.MagicMock()
+        self.task.wait.return_value = {
+            1: 'correct result'
+        }
+        self.session.getChannel.return_value = {'name': 'channel_id'}
+        self.session.getTaskInfo.return_value = {
+            'owner': 'owner_id',
+            'channel_id': 'channel_id',
+        }
+        self.session.getNextRelease.return_value = 'next_release'
+
+        result = self.task.handler('target', arches,
+                                   'git://desc.server/repo#fragment', 'desc_path')
+
+        # test scm.assert_allowed inputs
+        scm = self.scm[0]
+        scm.assert_allowed.assert_called_once_with(
+            allowed=self.options.allowed_scms,
+            session=self.session,
+            by_config=self.options.allowed_scms_use_config,
+            by_policy=self.options.allowed_scms_use_policy,
+            policy_data={
+                'user_id': 'owner_id',
+                'channel': 'channel_id',
+                'scratch': False,
+            })
+
+        self.task.run_callbacks.assert_has_calls([
+            mock.call(
+                'preSCMCheckout',
+                scminfo=scm.get_info(),
+                build_tag=123,
+                scratch=False,
+            ),
+            mock.call(
+                'postSCMCheckout',
+                scminfo=scm.get_info(),
+                build_tag=123,
+                scratch=False,
+                srcdir=scm.checkout()
+            )
+        ])
+
+        # subtasks
+        self.session.host.subtask.assert_has_calls([
+            mock.call(
+                method='createKiwiImage',
+                arglist=[
+                    'name-profile',
+                    'version',
+                    'image_release',
+                    'arch1',
+                    self.session.getBuildTarget.return_value,
+                    123,
+                    self.task.getRepo.return_value,
+                    'git://desc.server/repo#fragment',
+                    'desc_path',
+                    {
+                        'scratch': False,
+                        'optional_arches': [],
+                    }
+                ],
+                label='arch1',
+                parent=123,
+                arch='arch1',
+            ),
+            mock.call(
+                method='createKiwiImage',
+                arglist=[
+                    'name-profile',
+                    'version',
+                    'image_release',
+                    'arch2',
+                    self.session.getBuildTarget.return_value,
+                    123,
+                    self.task.getRepo.return_value,
+                    'git://desc.server/repo#fragment',
+                    'desc_path',
+                    {
+                        'scratch': False,
+                        'optional_arches': []
+                    }
+                ],
+                label='arch2',
+                parent=123,
+                arch='arch2'
+            ),
+            mock.call(
+                method='tagBuild',
+                arglist=[
+                    321,
+                    self.task.initImageBuild()['id'],
+                    False,
+                    None,
+                    True
+                ],
+                label='tag',
+                parent=123,
+                arch='noarch'
+            )
+        ])
+
+        self.assertEqual(result,
+                         'image build results in: /mnt/koji/packages/'
+                         'image_name/image_version/image_release/images')
+
+    def test_handler_incompatible_archs(self):
+        # arches must be preset in the buildroot
+        with self.assertRaises(koji.BuildError):
+            self.test_handler_correct(arches=['arch3', 'arch4'])
+
+    def test_no_arches(self):
+        # exactly same as correct variant. No arches are supplied,
+        # they should be taken from build_config
+        self.test_handler_correct(arches=[])
+
+
+class TestKiwiCreateImageTask(unittest.TestCase):
+    pass
diff --git a/tests/test_plugins/test_kiwi_cli.py b/tests/test_plugins/test_kiwi_cli.py
new file mode 100644
index 0000000..f50769a
--- /dev/null
+++ b/tests/test_plugins/test_kiwi_cli.py
@@ -0,0 +1,137 @@
+import mock
+import io
+import os
+import sys
+
+
+sys.path = [os.path.join(os.path.dirname(__file__), '..')] + sys.path
+from test_cli import utils
+
+import plugins.cli.kiwi as kiwi
+
+class TestAddChannel(utils.CliTestCase):
+    def setUp(self):
+        self.maxDiff = None
+        self.task_id = 1
+        self.options = mock.MagicMock()
+        self.session = mock.MagicMock()
+        self.session.hub_version = (1, 35, 0)
+        #self.activate_session_mock = mock.patch('koji_cli.lib.activate_session').start()
+        self.error_format = """Usage: %s add-channel [options] <channel_name>
+(Specify the --help global option for a list of other help options)
+
+%s: error: {message}
+""" % (self.progname, self.progname)
+        self.target = 'target'
+        self.description_scm = 'git://scm'
+        self.description_path = 'path/to/file.kiwi'
+
+    def tearDown(self):
+        mock.patch.stopall()
+
+    @mock.patch('sys.stderr', new_callable=io.StringIO)
+    @mock.patch('sys.stdout', new_callable=io.StringIO)
+    def test_handle_kiwi_build_old_hub(self, stdout, stderr):
+        # missing use_buildroot_repo option
+        kiwi.watch_tasks = mock.MagicMock()
+        self.session.hub_version = (1, 34, 1)
+        self.session.kiwiBuild.return_value = self.task_id
+        rv = kiwi.handle_kiwi_build(self.options, self.session,
+                                    [
+                                      self.target,
+                                      self.description_scm,
+                                      self.description_path
+                                    ])
+        actual = stderr.getvalue()
+        expected = 'hub version is < 1.35, buildroot repo is always used in addition to specified repos\n'
+        self.assertMultiLineEqual(actual, expected)
+        self.assertMultiLineEqual(stdout.getvalue(), '')
+        self.session.kiwiBuild.assert_called_once_with(
+            arches=[],
+            target=self.target,
+            desc_url=self.description_scm,
+            desc_path=self.description_path,
+        )
+        self.assertNotEqual(rv, 1)
+
+    @mock.patch('sys.stderr', new_callable=io.StringIO)
+    @mock.patch('sys.stdout', new_callable=io.StringIO)
+    def test_handle_kiwi_build_newd_hub(self, stdout, stderr):
+        # introduced use_buildroot_repo option
+        kiwi.watch_tasks = mock.MagicMock()
+        self.session.hub_version = (1, 35, 0)
+        self.session.kiwiBuild.return_value = self.task_id
+        rv = kiwi.handle_kiwi_build(self.options, self.session,
+                                    [
+                                      self.target,
+                                      self.description_scm,
+                                      self.description_path
+                                    ])
+        actual = stderr.getvalue()
+        expected = 'no repos given, using buildroot repo\n'
+        self.assertMultiLineEqual(actual, expected)
+        self.assertMultiLineEqual(stdout.getvalue(), '')
+        self.session.kiwiBuild.assert_called_once_with(
+            arches=[],
+            target=self.target,
+            desc_url=self.description_scm,
+            desc_path=self.description_path,
+            use_buildroot_repo=True,
+        )
+        self.assertNotEqual(rv, 1)
+
+    @mock.patch('sys.stderr', new_callable=io.StringIO)
+    @mock.patch('sys.stdout', new_callable=io.StringIO)
+    def test_handle_kiwi_all_options(self, stdout, stderr):
+        # introduced use_buildroot_repo option
+        kiwi.watch_tasks = mock.MagicMock()
+        self.session.hub_version = (1, 35, 0)
+        self.session.kiwiBuild.return_value = self.task_id
+        rv = kiwi.handle_kiwi_build(self.options, self.session,
+                                    [
+                                      self.target,
+                                      self.description_scm,
+                                      self.description_path,
+                                      '--scratch',
+                                      '--version=test_version',
+                                      '--release=test_release',
+                                      '--repo=https://test_repo_1',
+                                      '--repo=https://test_repo_2',
+                                      '--buildroot-repo',
+                                      '--repo-releasever=releasever',
+                                      '--noprogress',
+                                      '--kiwi-profile=profile.kiwi',
+                                      '--type=build_type',
+                                      '--type-attr=type_attr',
+                                      '--result-bundle-name-format=name_format',
+                                      '--make-prep',
+                                      '--can-fail=x86_64',
+                                      '--arch=x86_64',
+                                      '--arch=s390x',
+                                      '--nowait',
+                                    ])
+        actual = stderr.getvalue()
+        expected = ''
+        self.assertMultiLineEqual(actual, expected)
+        self.assertMultiLineEqual(stdout.getvalue(), '')
+        self.session.kiwiBuild.assert_called_once_with(
+            arches=['x86_64', 's390x'],
+            target=self.target,
+            desc_url=self.description_scm,
+            desc_path=self.description_path,
+            scratch=True,
+            optional_arches=['x86_64'],
+            profile='profile.kiwi',
+            version='test_version',
+            release='test_release',
+            make_prep=True,
+            type='build_type',
+            type_attr=['type_attr'],
+            result_bundle_name_format='name_format',
+            repos=['https://test_repo_1', 'https://test_repo_2'],
+            repo_releasever='releasever',
+            use_buildroot_repo=True,
+        )
+        self.assertNotEqual(rv, 1)
+
+
diff --git a/tests/test_plugins/test_kiwi_hub.py b/tests/test_plugins/test_kiwi_hub.py
new file mode 100644
index 0000000..cd5c7f6
--- /dev/null
+++ b/tests/test_plugins/test_kiwi_hub.py
@@ -0,0 +1,97 @@
+import mock
+import unittest
+
+import koji
+import kojihub
+from plugins.hub import kiwi
+
+class TestKiwiHub(unittest.TestCase):
+    def setUp(self):
+        self.context = mock.patch('plugins.hub.kiwi.context').start()
+        self.context.session.assertPerm = mock.MagicMock()
+        kojihub.get_build_target = mock.MagicMock()
+        kojihub.get_build_target.return_value = {'id': 1, 'name': 'target'}
+        kojihub.make_task = mock.MagicMock()
+        kojihub.make_task.return_value = 1
+
+    def tearDown(self):
+        mock.patch.stopall()
+
+    def test_kiwi_basic(self):
+        kiwi.kiwiBuild('target', ['arch1', 'arch2'], 'desc_url', 'desc_path')
+        kojihub.get_build_target.assert_called_once_with('target', strict=True)
+        kojihub.make_task.assert_called_once_with(
+            'kiwiBuild',
+            [
+                'target',
+                ['arch1', 'arch2'],
+                'desc_url',
+                'desc_path',
+                {'use_buildroot_repo': True}
+            ],
+            channel='image'
+        )
+
+    def test_nonexistent_target(self):
+        kojihub.get_build_target.side_effect = koji.GenericError
+        with self.assertRaises(koji.GenericError):
+            kiwi.kiwiBuild('target', ['arch1', 'arch2'], 'desc_url', 'desc_path')
+        kojihub.get_build_target.assert_called_once_with('target', strict=True)
+
+    def test_invalid_arches(self):
+        for arch_set in ['arch1,arch2', 'ěšč']:
+            with self.assertRaises(koji.GenericError):
+                kiwi.kiwiBuild('target', arch_set, 'desc_url', 'desc_path')
+
+        with self.assertRaises(TypeError):
+            kiwi.kiwiBuild('target', ["arch1", None], 'desc_url', 'desc_path')
+
+        with self.assertRaises(AttributeError):
+            kiwi.kiwiBuild('target', None, 'desc_url', 'desc_path')
+
+    def test_all_options(self):
+        kiwi.kiwiBuild(
+            'target',
+            ['arch1', 'arch2'],
+            'desc_url',
+            'desc_path',
+            optional_arches=['arch1'],
+            profile='profile',
+            scratch=True,
+            priority=10,
+            make_prep=True,
+            repos=['repo1', 'repo2'],
+            release='release',
+            type='type',
+            type_attr=['type_attr'],
+            result_bundle_name_format='name_format',
+            use_buildroot_repo=True,
+            version='version',
+            repo_releasever='relver'
+        )
+        kojihub.make_task.assert_called_once_with(
+            'kiwiBuild',
+            [
+                'target',
+                ['arch1', 'arch2'],
+                'desc_url',
+                'desc_path',
+                {
+                    'scratch': True,
+                    'profile': 'profile',
+                    'version': 'version',
+                    'release': 'release',
+                    'optional_arches': ['arch1'],
+                    'repos': ['repo1', 'repo2'],
+                    'repo_releasever': 'relver',
+                    'make_prep': True,
+                    'type': 'type',
+                    'use_buildroot_repo': True,
+                    'type_attr': ['type_attr'],
+                    'result_bundle_name_format': 'name_format'
+                },
+            ],
+            channel='image',
+            priority=koji.PRIO_DEFAULT + 10,
+        )
+

