From eea501a846532c7e807f69f7135d73f03cfce38c Mon Sep 17 00:00:00 2001 From: Jana Librova Date: Jun 03 2025 11:48:02 +0000 Subject: Add check if int/str options are empty in listBuilds Fixes: https://pagure.io/koji/issue/4402 --- diff --git a/kojihub/kojihub.py b/kojihub/kojihub.py index 24f8eda..13b8fd2 100644 --- a/kojihub/kojihub.py +++ b/kojihub/kojihub.py @@ -12595,51 +12595,81 @@ class RootExports(object): ] clauses = [] if packageID is not None: + if packageID == '': + raise koji.ParameterError('packageID option is empty') + # packageID could be ID or name packageID = get_package_id(packageID) if not packageID: return [] clauses.append('package.id = %(packageID)i') if userID is not None: + if userID == '': + raise koji.ParameterError('userID option is empty') + # userID could be ID or name userinfo = get_user(userID) if not userinfo: return [] userID = userinfo['id'] clauses.append('users.id = %(userID)i') if volumeID is not None: + if volumeID == '': + raise koji.ParameterError('volumeID option is empty') clauses.append('volume.id = %(volumeID)i') if taskID is not None: + if taskID == '': + raise koji.ParameterError('taskID option is empty') if taskID == -1: clauses.append('build.task_id IS NOT NULL') else: clauses.append('build.task_id = %(taskID)i') if source is not None: + if source == '': + raise koji.ParameterError('source option is empty') source = self._prepareSearchTerms(source, 'glob') clauses.append('build.source ilike %(source)s') - if prefix: + if prefix is not None: + if prefix == '': + raise koji.ParameterError('prefix option is empty') clauses.append("package.name ilike %(prefix)s || '%%'") - if pattern: + if pattern is not None: + if pattern == '': + raise koji.ParameterError('pattern option is empty') pattern = self._prepareSearchTerms(pattern, 'glob') clauses.append("package.name || '-' || build.version || '-' || build.release" " ilike %(pattern)s") if state is not None: + if state == '': + raise koji.ParameterError('state option is empty') + clauses.append('build.state = %(state)i') + if createdBefore is not None: + if createdBefore == '': + raise koji.ParameterError('createdBefore option is empty') clauses.append('build.state = %(state)i') - if createdBefore: if not isinstance(createdBefore, str): createdBefore = convert_timestamp(createdBefore) clauses.append('events.time < %(createdBefore)s') - if createdAfter: + if createdAfter is not None: + if createdAfter == '': + raise koji.ParameterError('createdAfter option is empty') if not isinstance(createdAfter, str): createdAfter = convert_timestamp(createdAfter) clauses.append('events.time > %(createdAfter)s') - if completeBefore: + if completeBefore is not None: + if completeBefore == '': + raise koji.ParameterError('completeBefore option is empty') if not isinstance(completeBefore, str): completeBefore = convert_timestamp(completeBefore) clauses.append('build.completion_time < %(completeBefore)s') - if completeAfter: + if completeAfter is not None: + if completeAfter == '': + raise koji.ParameterError('completeAfter option is empty') if not isinstance(completeAfter, str): completeAfter = convert_timestamp(completeAfter) clauses.append('build.completion_time > %(completeAfter)s') - if cgID: + if cgID is not None: + if cgID == '': + raise koji.ParameterError('cgID option is empty') + # cgID could be ID or name cgID = lookup_name('content_generator', cgID, strict=False) if not cgID: return [] @@ -12672,6 +12702,9 @@ class RootExports(object): joins.append('image_builds ON build.id = image_builds.build_id') fields.append(('image_builds.build_id', 'build_id')) else: + if type == '': + raise koji.ParameterError('type option is empty') + # type could be ID or name btype = lookup_name('btype', type, strict=False) if not btype: raise koji.GenericError('unsupported build type: %s' % type) diff --git a/tests/test_hub/test_list_builds.py b/tests/test_hub/test_list_builds.py index 4fded65..98a861c 100644 --- a/tests/test_hub/test_list_builds.py +++ b/tests/test_hub/test_list_builds.py @@ -2,6 +2,7 @@ import unittest from unittest import mock +import koji import kojihub QP = kojihub.QueryProcessor @@ -18,7 +19,7 @@ class TestListBuilds(unittest.TestCase): def setUp(self): # defaults - self.tables= ['build'] + self.tables = ['build'] # note: QueryProcessor reports these sorted by alias self.columns = [ 'build.id', @@ -114,7 +115,7 @@ class TestListBuilds(unittest.TestCase): self.get_user.return_value = None rv = self.exports.listBuilds(userID=user) self.assertEqual(rv, []) - + def test_draft(self): package = 'test-package' package_id = 1 @@ -128,3 +129,87 @@ class TestListBuilds(unittest.TestCase): self.assertEqual(qp.columns, self.columns) self.assertEqual(qp.clauses, ['draft IS TRUE'] + self.clauses) self.assertEqual(qp.joins, self.joins) + + def test_packageID_is_empty(self): + with self.assertRaises(koji.ParameterError) as cm: + self.exports.listBuilds(packageID='') + self.assertEqual(cm.exception.args[0], 'packageID option is empty') + self.assertEqual(len(self.queries), 0) + + def test_userID_is_empty(self): + with self.assertRaises(koji.ParameterError) as cm: + self.exports.listBuilds(userID='') + self.assertEqual(cm.exception.args[0], 'userID option is empty') + self.assertEqual(len(self.queries), 0) + + def test_volumeID_is_empty(self): + with self.assertRaises(koji.ParameterError) as cm: + self.exports.listBuilds(volumeID='') + self.assertEqual(cm.exception.args[0], 'volumeID option is empty') + self.assertEqual(len(self.queries), 0) + + def test_taskID_is_empty(self): + with self.assertRaises(koji.ParameterError) as cm: + self.exports.listBuilds(taskID='') + self.assertEqual(cm.exception.args[0], 'taskID option is empty') + self.assertEqual(len(self.queries), 0) + + def test_source_is_empty(self): + with self.assertRaises(koji.ParameterError) as cm: + self.exports.listBuilds(source='') + self.assertEqual(cm.exception.args[0], 'source option is empty') + self.assertEqual(len(self.queries), 0) + + def test_prefix_is_empty(self): + with self.assertRaises(koji.ParameterError) as cm: + self.exports.listBuilds(prefix='') + self.assertEqual(cm.exception.args[0], 'prefix option is empty') + self.assertEqual(len(self.queries), 0) + + def test_pattern_is_empty(self): + with self.assertRaises(koji.ParameterError) as cm: + self.exports.listBuilds(pattern='') + self.assertEqual(cm.exception.args[0], 'pattern option is empty') + self.assertEqual(len(self.queries), 0) + + def test_state_is_empty(self): + with self.assertRaises(koji.ParameterError) as cm: + self.exports.listBuilds(state='') + self.assertEqual(cm.exception.args[0], 'state option is empty') + self.assertEqual(len(self.queries), 0) + + def test_createdBefore_is_empty(self): + with self.assertRaises(koji.ParameterError) as cm: + self.exports.listBuilds(createdBefore='') + self.assertEqual(cm.exception.args[0], 'createdBefore option is empty') + self.assertEqual(len(self.queries), 0) + + def test_createdAfter_is_empty(self): + with self.assertRaises(koji.ParameterError) as cm: + self.exports.listBuilds(createdAfter='') + self.assertEqual(cm.exception.args[0], 'createdAfter option is empty') + self.assertEqual(len(self.queries), 0) + + def test_completeBefore_is_empty(self): + with self.assertRaises(koji.ParameterError) as cm: + self.exports.listBuilds(completeBefore='') + self.assertEqual(cm.exception.args[0], 'completeBefore option is empty') + self.assertEqual(len(self.queries), 0) + + def test_completeAfter_is_empty(self): + with self.assertRaises(koji.ParameterError) as cm: + self.exports.listBuilds(completeAfter='') + self.assertEqual(cm.exception.args[0], 'completeAfter option is empty') + self.assertEqual(len(self.queries), 0) + + def test_cgID_is_empty(self): + with self.assertRaises(koji.ParameterError) as cm: + self.exports.listBuilds(cgID='') + self.assertEqual(cm.exception.args[0], 'cgID option is empty') + self.assertEqual(len(self.queries), 0) + + def test_type_is_empty(self): + with self.assertRaises(koji.ParameterError) as cm: + self.exports.listBuilds(type='') + self.assertEqual(cm.exception.args[0], 'type option is empty') + self.assertEqual(len(self.queries), 0)