PR#3282: Add extra of builds to listTagged call result

Merges #3282
https://pagure.io/koji/pull-request/3282

Fixes: #3110
https://pagure.io/koji/issue/3110
RFE: expose build.extra by listTagged API directly
This commit is contained in:
Tomas Kopecek 2022-05-09 14:04:36 +02:00
commit 87aa8eb17b
8 changed files with 733 additions and 151 deletions

View file

@ -1354,7 +1354,7 @@ def list_tags(build=None, package=None, perms=True, queryOpts=None, pattern=None
def readTaggedBuilds(tag, event=None, inherit=False, latest=False, package=None, owner=None, def readTaggedBuilds(tag, event=None, inherit=False, latest=False, package=None, owner=None,
type=None): type=None, extra=False):
"""Returns a list of builds for specified tag """Returns a list of builds for specified tag
:param int tag: tag ID :param int tag: tag ID
@ -1365,6 +1365,7 @@ def readTaggedBuilds(tag, event=None, inherit=False, latest=False, package=None,
:param str owner: filter on user name :param str owner: filter on user name
:param str type: restrict the list to builds of the given type. Currently the supported :param str type: restrict the list to builds of the given type. Currently the supported
types are 'maven', 'win', 'image', or any custom content generator btypes. types are 'maven', 'win', 'image', or any custom content generator btypes.
:param bool extra: Set to "True" to get the build extra info
:returns [dict]: list of buildinfo dicts :returns [dict]: list of buildinfo dicts
""" """
# build - id pkg_id version release epoch # build - id pkg_id version release epoch
@ -1437,9 +1438,15 @@ def readTaggedBuilds(tag, event=None, inherit=False, latest=False, package=None,
if owner: if owner:
clauses.append('users.name = %(owner)s') clauses.append('users.name = %(owner)s')
queryOpts = {'order': '-create_event'} # latest first queryOpts = {'order': '-create_event'} # latest first
query = QueryProcessor(columns=[x[0] for x in fields], aliases=[x[1] for x in fields], if extra:
tables=tables, joins=joins, clauses=clauses, values=locals(), fields.append(('build.extra', 'extra'))
opts=queryOpts) query = QueryProcessor(columns=[x[0] for x in fields], aliases=[x[1] for x in fields],
tables=tables, joins=joins, clauses=clauses, values=locals(),
transform=_fix_extra_field, opts=queryOpts)
else:
query = QueryProcessor(columns=[x[0] for x in fields], aliases=[x[1] for x in fields],
tables=tables, joins=joins, clauses=clauses, values=locals(),
opts=queryOpts)
builds = [] builds = []
seen = {} # used to enforce the 'latest' option seen = {} # used to enforce the 'latest' option
@ -1467,7 +1474,7 @@ def readTaggedBuilds(tag, event=None, inherit=False, latest=False, package=None,
def readTaggedRPMS(tag, package=None, arch=None, event=None, inherit=False, latest=True, def readTaggedRPMS(tag, package=None, arch=None, event=None, inherit=False, latest=True,
rpmsigs=False, owner=None, type=None): rpmsigs=False, owner=None, type=None, extra=True):
"""Returns a list of rpms and builds for specified tag """Returns a list of rpms and builds for specified tag
:param int|str tag: The tag name or ID to search :param int|str tag: The tag name or ID to search
@ -1486,6 +1493,7 @@ def readTaggedRPMS(tag, package=None, arch=None, event=None, inherit=False, late
:param str owner: Filter by build owner name :param str owner: Filter by build owner name
:param str type: Filter by build type. Supported types are 'maven', :param str type: Filter by build type. Supported types are 'maven',
'win', and 'image'. 'win', and 'image'.
:param bool extra: Set to "False" to skip the rpm extra info
:returns: a two-element list. The first element is the list of RPMs, and :returns: a two-element list. The first element is the list of RPMs, and
the second element is the list of builds. the second element is the list of builds.
""" """
@ -1513,7 +1521,6 @@ def readTaggedRPMS(tag, package=None, arch=None, event=None, inherit=False, late
('rpminfo.buildroot_id', 'buildroot_id'), ('rpminfo.buildroot_id', 'buildroot_id'),
('rpminfo.build_id', 'build_id'), ('rpminfo.build_id', 'build_id'),
('rpminfo.metadata_only', 'metadata_only'), ('rpminfo.metadata_only', 'metadata_only'),
('rpminfo.extra', 'extra'),
] ]
tables = ['rpminfo'] tables = ['rpminfo']
joins = ['tag_listing ON rpminfo.build_id = tag_listing.build_id'] joins = ['tag_listing ON rpminfo.build_id = tag_listing.build_id']
@ -1536,9 +1543,17 @@ def readTaggedRPMS(tag, package=None, arch=None, event=None, inherit=False, late
else: else:
raise koji.GenericError('Invalid type for arch option: %s' % builtins.type(arch)) raise koji.GenericError('Invalid type for arch option: %s' % builtins.type(arch))
fields, aliases = zip(*fields) if extra:
query = QueryProcessor(tables=tables, joins=joins, clauses=clauses, fields.append(('rpminfo.extra', 'extra'))
columns=fields, aliases=aliases, values=data, transform=_fix_rpm_row) query = QueryProcessor(tables=tables, joins=joins, clauses=clauses,
columns=[pair[0] for pair in fields],
aliases=[pair[1] for pair in fields],
values=data, transform=_fix_rpm_row)
else:
query = QueryProcessor(tables=tables, joins=joins, clauses=clauses,
columns=[pair[0] for pair in fields],
aliases=[pair[1] for pair in fields],
values=data)
# unique constraints ensure that each of these queries will not report # unique constraints ensure that each of these queries will not report
# duplicate rpminfo entries, BUT since we make the query multiple times, # duplicate rpminfo entries, BUT since we make the query multiple times,
@ -1573,13 +1588,20 @@ def readTaggedRPMS(tag, package=None, arch=None, event=None, inherit=False, late
return [_iter_rpms(), builds] return [_iter_rpms(), builds]
def readTaggedArchives(tag, package=None, event=None, inherit=False, latest=True, type=None): def readTaggedArchives(tag, package=None, event=None, inherit=False, latest=True, type=None,
extra=True):
"""Returns a list of archives for specified tag """Returns a list of archives for specified tag
set inherit=True to follow inheritance :param int|str tag: The tag name or ID to search
set event to query at a time in the past :param str package: Filter on a package name.
set latest=False to get all tagged archives (not just from the latest builds) :param int event: set event to query at a time in the past
set latest=N to get only the N latest tagged RPMs :param bool inherit: set to "True" to follow inheritance
:param bool|int latest: set to "False" to get all tagged archives (not just from the latest
builds), set to "N" to get only the "N" latest tagged RPMs
:param str type: Filter by build type. Supported types are 'maven' and 'win'.
:param bool extra: Set to "False" to skip the archives extra info
:returns: a two-element list. The first element is the list of archives, and
the second element is the list of builds.
If type is not None, restrict the listing to archives of the given type. Currently If type is not None, restrict the listing to archives of the given type. Currently
the supported types are 'maven' and 'win'. the supported types are 'maven' and 'win'.
@ -1608,7 +1630,6 @@ def readTaggedArchives(tag, package=None, event=None, inherit=False, latest=True
('archiveinfo.checksum', 'checksum'), ('archiveinfo.checksum', 'checksum'),
('archiveinfo.checksum_type', 'checksum_type'), ('archiveinfo.checksum_type', 'checksum_type'),
('archiveinfo.metadata_only', 'metadata_only'), ('archiveinfo.metadata_only', 'metadata_only'),
('archiveinfo.extra', 'extra'),
] ]
tables = ['archiveinfo'] tables = ['archiveinfo']
joins = ['tag_listing ON archiveinfo.build_id = tag_listing.build_id', joins = ['tag_listing ON archiveinfo.build_id = tag_listing.build_id',
@ -1633,10 +1654,16 @@ def readTaggedArchives(tag, package=None, event=None, inherit=False, latest=True
else: else:
raise koji.GenericError('unsupported archive type: %s' % type) raise koji.GenericError('unsupported archive type: %s' % type)
query = QueryProcessor(tables=tables, joins=joins, clauses=clauses, if extra:
transform=_fix_archive_row, fields.append(('archiveinfo.extra', 'extra'))
columns=[pair[0] for pair in fields], query = QueryProcessor(tables=tables, joins=joins, clauses=clauses,
aliases=[pair[1] for pair in fields]) transform=_fix_archive_row,
columns=[pair[0] for pair in fields],
aliases=[pair[1] for pair in fields])
else:
query = QueryProcessor(tables=tables, joins=joins, clauses=clauses,
columns=[pair[0] for pair in fields],
aliases=[pair[1] for pair in fields])
# unique constraints ensure that each of these queries will not report # unique constraints ensure that each of these queries will not report
# duplicate archiveinfo entries, BUT since we make the query multiple times, # duplicate archiveinfo entries, BUT since we make the query multiple times,
@ -11770,7 +11797,7 @@ class RootExports(object):
task.setPriority(priority, recurse=recurse) task.setPriority(priority, recurse=recurse)
def listTagged(self, tag, event=None, inherit=False, prefix=None, latest=False, package=None, def listTagged(self, tag, event=None, inherit=False, prefix=None, latest=False, package=None,
owner=None, type=None, strict=True): owner=None, type=None, strict=True, extra=False):
"""List builds tagged with tag. """List builds tagged with tag.
:param int|str tag: tag name or ID number :param int|str tag: tag name or ID number
@ -11785,13 +11812,14 @@ class RootExports(object):
:param str type: only builds of the given btype (such as maven or image) :param str type: only builds of the given btype (such as maven or image)
:param bool strict: If tag doesn't exist, an exception is raised, :param bool strict: If tag doesn't exist, an exception is raised,
unless strict is False in which case returns an empty list. unless strict is False in which case returns an empty list.
:param bool extra: Set to "True" to get the build extra info
""" """
# lookup tag id # lookup tag id
tag = get_tag(tag, strict=strict, event=event) tag = get_tag(tag, strict=strict, event=event)
if not tag: if not tag:
return [] return []
results = readTaggedBuilds(tag['id'], event, inherit=inherit, latest=latest, results = readTaggedBuilds(tag['id'], event, inherit=inherit, latest=latest,
package=package, owner=owner, type=type) package=package, owner=owner, type=type, extra=extra)
if prefix: if prefix:
prefix = prefix.lower() prefix = prefix.lower()
results = [build for build in results results = [build for build in results
@ -11799,7 +11827,7 @@ class RootExports(object):
return results return results
def listTaggedRPMS(self, tag, event=None, inherit=False, latest=False, package=None, arch=None, def listTaggedRPMS(self, tag, event=None, inherit=False, latest=False, package=None, arch=None,
rpmsigs=False, owner=None, type=None, strict=True): rpmsigs=False, owner=None, type=None, strict=True, extra=True):
"""List rpms and builds within tag. """List rpms and builds within tag.
:param int|str tag: tag name or ID number :param int|str tag: tag name or ID number
@ -11817,16 +11845,18 @@ class RootExports(object):
:param str type: only rpms of the given btype (such as maven or image) :param str type: only rpms of the given btype (such as maven or image)
:param bool strict: If tag doesn't exist, an exception is raised, :param bool strict: If tag doesn't exist, an exception is raised,
unless strict is False in which case returns an empty list. unless strict is False in which case returns an empty list.
:param bool extra: Set to "False" to skip the rpms extra info
""" """
# lookup tag id # lookup tag id
tag = get_tag(tag, strict=strict, event=event) tag = get_tag(tag, strict=strict, event=event)
if not tag: if not tag:
return [] return []
return readTaggedRPMS(tag['id'], event=event, inherit=inherit, latest=latest, return readTaggedRPMS(tag['id'], event=event, inherit=inherit, latest=latest,
package=package, arch=arch, rpmsigs=rpmsigs, owner=owner, type=type) package=package, arch=arch, rpmsigs=rpmsigs, owner=owner,
type=type, extra=extra)
def listTaggedArchives(self, tag, event=None, inherit=False, latest=False, package=None, def listTaggedArchives(self, tag, event=None, inherit=False, latest=False, package=None,
type=None, strict=True): type=None, strict=True, extra=True):
"""List archives and builds within a tag. """List archives and builds within a tag.
:param int|str tag: tag name or ID number :param int|str tag: tag name or ID number
@ -11839,13 +11869,14 @@ class RootExports(object):
:param str type: only archives of the given btype (such as maven or image) :param str type: only archives of the given btype (such as maven or image)
:param bool strict: If tag doesn't exist, an exception is raised, :param bool strict: If tag doesn't exist, an exception is raised,
unless strict is False in which case returns an empty list. unless strict is False in which case returns an empty list.
:param bool extra: Set to "False" to skip the archive extra info
""" """
# lookup tag id # lookup tag id
tag = get_tag(tag, strict=strict, event=event) tag = get_tag(tag, strict=strict, event=event)
if not tag: if not tag:
return [] return []
return readTaggedArchives(tag['id'], event=event, inherit=inherit, latest=latest, return readTaggedArchives(tag['id'], event=event, inherit=inherit, latest=latest,
package=package, type=type) package=package, type=type, extra=extra)
def listBuilds(self, packageID=None, userID=None, taskID=None, prefix=None, state=None, def listBuilds(self, packageID=None, userID=None, taskID=None, prefix=None, state=None,
volumeID=None, source=None, createdBefore=None, createdAfter=None, volumeID=None, source=None, createdBefore=None, createdAfter=None,

View file

@ -9,10 +9,9 @@ from . import utils
class TestCliListTagged(utils.CliTestCase): class TestCliListTagged(utils.CliTestCase):
# Show long diffs in error output...
maxDiff = None
def setUp(self): def setUp(self):
self.maxDiff = None
self.original_timezone = os.environ.get('TZ') self.original_timezone = os.environ.get('TZ')
os.environ['TZ'] = 'US/Eastern' os.environ['TZ'] = 'US/Eastern'
time.tzset() time.tzset()
@ -23,6 +22,10 @@ class TestCliListTagged(utils.CliTestCase):
""" % (self.progname, self.progname) """ % (self.progname, self.progname)
self.session = mock.MagicMock() self.session = mock.MagicMock()
self.options = mock.MagicMock(quiet=False) self.options = mock.MagicMock(quiet=False)
self.tag = 'tag'
self.pkg = 'pkg'
self.event_id = 1000
self.type = 'maven'
self.session.getTag.return_value = {'id': 1} self.session.getTag.return_value = {'id': 1}
self.session.listTaggedRPMS.return_value = [[{'id': 100, self.session.listTaggedRPMS.return_value = [[{'id': 100,
'build_id': 1, 'build_id': 1,
@ -30,28 +33,33 @@ class TestCliListTagged(utils.CliTestCase):
'version': '0.0.1', 'version': '0.0.1',
'release': '1.el6', 'release': '1.el6',
'arch': 'noarch', 'arch': 'noarch',
'sigkey': 'sigkey'}, 'sigkey': 'sigkey',
'extra': 'extra-value'},
{'id': 101, {'id': 101,
'build_id': 1, 'build_id': 1,
'name': 'rpmA', 'name': 'rpmA',
'version': '0.0.1', 'version': '0.0.1',
'release': '1.el6', 'release': '1.el6',
'arch': 'x86_64', 'arch': 'x86_64',
'sigkey': 'sigkey'} 'sigkey': 'sigkey',
'extra': None}
], [{'id': 1, ], [{'id': 1,
'name': 'packagename', 'name': 'packagename',
'version': 'version', 'version': 'version',
'release': '1.el6', 'release': '1.el6',
'nvr': 'n-v-r', 'nvr': 'n-v-r',
'tag_name': 'tag', 'tag_name': 'tag',
'owner_name': 'owner'}]] 'owner_name': 'owner',
'extra': 'extra-value-2'}]]
self.session.listTagged.return_value = [{'id': 1, self.session.listTagged.return_value = [{'id': 1,
'name': 'packagename', 'name': 'packagename',
'version': 'version', 'version': 'version',
'release': '1.el6', 'release': '1.el6',
'nvr': 'n-v-r', 'nvr': 'n-v-r',
'tag_name': 'tag', 'tag_name': 'tag',
'owner_name': 'owner'}] 'owner_name': 'owner',
'extra': 'extra-value-2'}]
self.ensure_connection_mock = mock.patch('koji_cli.commands.ensure_connection').start()
def tearDown(self): def tearDown(self):
if self.original_timezone is None: if self.original_timezone is None:
@ -63,92 +71,96 @@ class TestCliListTagged(utils.CliTestCase):
@mock.patch('sys.stdout', new_callable=six.StringIO) @mock.patch('sys.stdout', new_callable=six.StringIO)
@mock.patch('koji.util.eventFromOpts', return_value={'id': 1000, @mock.patch('koji.util.eventFromOpts', return_value={'id': 1000,
'ts': 1000000.11}) 'ts': 1000000.11})
@mock.patch('koji_cli.commands.ensure_connection') def test_list_tagged_builds(self, event_from_opts_mock, stdout):
def test_list_tagged_builds(self, ensure_connection_mock, expected = """Querying at event 1000 (Mon Jan 12 08:46:40 1970)
event_from_opts_mock, stdout): Build Tag Built by
args = ['tag', 'pkg', '--latest', '--inherit', '--event=1000'] ---------------------------------------- -------------------- ----------------
n-v-r tag owner
"""
args = [self.tag, self.pkg, '--latest', '--inherit', '--event', str(self.event_id)]
anon_handle_list_tagged(self.options, self.session, args) anon_handle_list_tagged(self.options, self.session, args)
ensure_connection_mock.assert_called_once_with(self.session, self.options) self.ensure_connection_mock.assert_called_once_with(self.session, self.options)
self.session.getTag.assert_called_once_with('tag', event=1000) self.session.getTag.assert_called_once_with(self.tag, event=self.event_id)
self.session.listTagged.assert_called_once_with('tag', self.session.listTagged.assert_called_once_with(
event=1000, self.tag, event=self.event_id, inherit=True, latest=True, package=self.pkg)
inherit=True,
latest=True,
package='pkg')
self.session.listTaggedRPMS.assert_not_called() self.session.listTaggedRPMS.assert_not_called()
self.assert_console_message(stdout, self.assert_console_message(stdout, expected)
'Querying at event 1000 (Mon Jan 12 08:46:40 1970)\n'
'Build Tag Built by\n'
'---------------------------------------- -------------------- ----------------\n'
'n-v-r tag owner\n')
@mock.patch('sys.stdout', new_callable=six.StringIO) @mock.patch('sys.stdout', new_callable=six.StringIO)
@mock.patch('koji.util.eventFromOpts', return_value=None) @mock.patch('koji.util.eventFromOpts', return_value=None)
@mock.patch('koji_cli.commands.ensure_connection') def test_list_tagged_builds_paths(self, event_from_opts_mock, stdout):
def test_list_tagged_builds_paths(self, ensure_connection_mock, expected = """Build Tag Built by
event_from_opts_mock, stdout): ---------------------------------------- -------------------- ----------------
args = ['tag', 'pkg', '--latest', '--inherit', '--paths'] /mnt/koji/packages/packagename/version/1.el6 tag owner
"""
args = [self.tag, self.pkg, '--latest', '--inherit', '--paths']
anon_handle_list_tagged(self.options, self.session, args) anon_handle_list_tagged(self.options, self.session, args)
self.assert_console_message(stdout, self.assert_console_message(stdout, expected)
'Build Tag Built by\n' self.ensure_connection_mock.assert_called_once_with(self.session, self.options)
'---------------------------------------- -------------------- ----------------\n' self.session.getTag.assert_called_once_with(self.tag, event=None)
'/mnt/koji/packages/packagename/version/1.el6 tag owner\n') self.session.listTagged.assert_called_once_with(
self.tag, inherit=True, latest=True, package=self.pkg)
self.session.listTaggedRPMS.assert_not_called()
@mock.patch('sys.stdout', new_callable=six.StringIO) @mock.patch('sys.stdout', new_callable=six.StringIO)
@mock.patch('koji.util.eventFromOpts', return_value=None) @mock.patch('koji.util.eventFromOpts', return_value=None)
@mock.patch('koji_cli.commands.ensure_connection') def test_list_tagged_rpms(self, event_from_opts_mock, stdout):
def test_list_tagged_rpms(self, ensure_connection_mock, expected = """sigkey rpmA-0.0.1-1.el6.noarch
event_from_opts_mock, stdout): sigkey rpmA-0.0.1-1.el6.x86_64
args = ['tag', 'pkg', '--latest-n=3', '--rpms', '--sigs', """
args = [self.tag, self.pkg, '--latest-n=3', '--rpms', '--sigs',
'--arch=x86_64', '--arch=noarch'] '--arch=x86_64', '--arch=noarch']
anon_handle_list_tagged(self.options, self.session, args) anon_handle_list_tagged(self.options, self.session, args)
ensure_connection_mock.assert_called_once_with(self.session, self.options) self.ensure_connection_mock.assert_called_once_with(self.session, self.options)
self.session.getTag.assert_called_once_with('tag', event=None) self.session.getTag.assert_called_once_with(self.tag, event=None)
self.session.listTaggedRPMS.assert_called_once_with('tag', self.session.listTaggedRPMS.assert_called_once_with(
package='pkg', self.tag, package=self.pkg, inherit=None, latest=3, rpmsigs=True,
inherit=None, arch=['x86_64', 'noarch'])
latest=3,
rpmsigs=True,
arch=['x86_64',
'noarch'])
self.session.listTagged.assert_not_called() self.session.listTagged.assert_not_called()
self.assert_console_message(stdout, self.assert_console_message(stdout, expected)
'sigkey rpmA-0.0.1-1.el6.noarch\n'
'sigkey rpmA-0.0.1-1.el6.x86_64\n')
@mock.patch('sys.stdout', new_callable=six.StringIO) @mock.patch('sys.stdout', new_callable=six.StringIO)
@mock.patch('koji.util.eventFromOpts', return_value=None) @mock.patch('koji.util.eventFromOpts', return_value=None)
@mock.patch('koji_cli.commands.ensure_connection') def test_list_tagged_rpms_paths(self, event_from_opts_mock, stdout):
def test_list_tagged_rpms_paths(self, ensure_connection_mock, expected = """/mnt/koji/packages/packagename/version/1.el6/noarch/rpmA-0.0.1-1.el6.noarch.rpm
event_from_opts_mock, stdout): /mnt/koji/packages/packagename/version/1.el6/x86_64/rpmA-0.0.1-1.el6.x86_64.rpm
args = ['tag', 'pkg', '--latest-n=3', '--rpms', """
args = [self.tag, self.pkg, '--latest-n=3', '--rpms', '--arch=x86_64', '--paths']
anon_handle_list_tagged(self.options, self.session, args)
self.assert_console_message(stdout, expected)
self.ensure_connection_mock.assert_called_once_with(self.session, self.options)
self.session.getTag.assert_called_once_with(self.tag, event=None)
self.session.listTaggedRPMS.assert_called_once_with(
self.tag, package=self.pkg, inherit=None, latest=3, arch=['x86_64'])
self.session.listTagged.assert_not_called()
@mock.patch('sys.stdout', new_callable=six.StringIO)
@mock.patch('koji.util.eventFromOpts', return_value=None)
def test_list_tagged_sigs_paths(self, event_from_opts_mock, stdout):
expected = ""
args = [self.tag, self.pkg, '--latest-n=3', '--rpms', '--sigs',
'--arch=x86_64', '--paths'] '--arch=x86_64', '--paths']
anon_handle_list_tagged(self.options, self.session, args) anon_handle_list_tagged(self.options, self.session, args)
self.assert_console_message(stdout, self.assert_console_message(stdout, expected)
'/mnt/koji/packages/packagename/version/1.el6/noarch/rpmA-0.0.1-1.el6.noarch.rpm\n' self.ensure_connection_mock.assert_called_once_with(self.session, self.options)
'/mnt/koji/packages/packagename/version/1.el6/x86_64/rpmA-0.0.1-1.el6.x86_64.rpm\n') self.session.getTag.assert_called_once_with(self.tag, event=None)
self.session.listTaggedRPMS.assert_called_once_with(
self.tag, package=self.pkg, inherit=None, latest=3, rpmsigs=True, arch=['x86_64'])
self.session.listTagged.assert_not_called()
@mock.patch('sys.stdout', new_callable=six.StringIO) @mock.patch('sys.stdout', new_callable=six.StringIO)
@mock.patch('koji.util.eventFromOpts', return_value=None) @mock.patch('koji.util.eventFromOpts', return_value=None)
@mock.patch('koji_cli.commands.ensure_connection') def test_list_tagged_type(self, event_from_opts_mock, stdout):
def test_list_tagged_sigs_paths(self, ensure_connection_mock, expected = """Build Tag Group Id Artifact Id Built by
event_from_opts_mock, stdout): ---------------------------------------- -------------------- -------------------- -------------------- ----------------
args = ['tag', 'pkg', '--latest-n=3', '--rpms', '--sigs', n-v-r tag group artifact owner
'--arch=x86_64', '--paths'] """
args = [self.tag, self.pkg, '--latest-n=3', '--type', self.type]
anon_handle_list_tagged(self.options, self.session, args)
self.assert_console_message(stdout, '')
@mock.patch('sys.stdout', new_callable=six.StringIO)
@mock.patch('koji.util.eventFromOpts', return_value=None)
@mock.patch('koji_cli.commands.ensure_connection')
def test_list_tagged_type(self, ensure_connection_mock,
event_from_opts_mock, stdout):
args = ['tag', 'pkg', '--latest-n=3', '--type=maven']
self.session.listTagged.return_value = [{'id': 1, self.session.listTagged.return_value = [{'id': 1,
'name': 'packagename', 'name': 'packagename',
'version': 'version', 'version': 'version',
@ -160,24 +172,21 @@ class TestCliListTagged(utils.CliTestCase):
'maven_artifact_id': 'artifact'}] 'maven_artifact_id': 'artifact'}]
anon_handle_list_tagged(self.options, self.session, args) anon_handle_list_tagged(self.options, self.session, args)
ensure_connection_mock.assert_called_once_with(self.session, self.options) self.ensure_connection_mock.assert_called_once_with(self.session, self.options)
self.session.getTag.assert_called_once_with('tag', event=None) self.session.getTag.assert_called_once_with(self.tag, event=None)
self.session.listTagged.assert_called_once_with('tag', self.session.listTagged.assert_called_once_with(
package='pkg', self.tag, package=self.pkg, inherit=None, latest=3, type=self.type)
inherit=None,
latest=3,
type='maven')
self.session.listTaggedRPMS.assert_not_called() self.session.listTaggedRPMS.assert_not_called()
self.assert_console_message(stdout, self.assert_console_message(stdout, expected)
'Build Tag Group Id Artifact Id Built by\n'
'---------------------------------------- -------------------- -------------------- -------------------- ----------------\n'
'n-v-r tag group artifact owner\n')
@mock.patch('sys.stdout', new_callable=six.StringIO) @mock.patch('sys.stdout', new_callable=six.StringIO)
@mock.patch('koji.util.eventFromOpts', return_value=None) @mock.patch('koji.util.eventFromOpts', return_value=None)
@mock.patch('koji_cli.commands.ensure_connection') def test_list_tagged_type_paths(self, event_from_opts_mock, stdout):
def test_list_tagged_type_paths(self, ensure_connection_mock, event_from_opts_mock, stdout): expected = """Build Tag Group Id Artifact Id Built by
args = ['tag', 'pkg', '--latest-n=3', '--type=maven', '--paths'] ---------------------------------------- -------------------- -------------------- -------------------- ----------------
/mnt/koji/packages/packagename/version/1.el6/maven tag group artifact owner
"""
args = [self.tag, self.pkg, '--latest-n=3', '--type', self.type, '--paths']
self.session.listTagged.return_value = [{'id': 1, self.session.listTagged.return_value = [{'id': 1,
'name': 'packagename', 'name': 'packagename',
'version': 'version', 'version': 'version',
@ -189,48 +198,49 @@ class TestCliListTagged(utils.CliTestCase):
'maven_artifact_id': 'artifact'}] 'maven_artifact_id': 'artifact'}]
anon_handle_list_tagged(self.options, self.session, args) anon_handle_list_tagged(self.options, self.session, args)
self.assert_console_message(stdout, self.assert_console_message(stdout, expected)
'Build Tag Group Id Artifact Id Built by\n' self.ensure_connection_mock.assert_called_once_with(self.session, self.options)
'---------------------------------------- -------------------- -------------------- -------------------- ----------------\n' self.session.getTag.assert_called_once_with(self.tag, event=None)
'/mnt/koji/packages/packagename/version/1.el6/maven tag group artifact owner\n') self.session.listTaggedRPMS.assert_not_called()
self.session.listTagged.assert_called_once_with(
self.tag, inherit=None, latest=3, package=self.pkg, type=self.type)
@mock.patch('koji_cli.commands.ensure_connection') def test_list_tagged_without_args(self):
@mock.patch('koji.util.eventFromOpts', return_value={'id': 1000,
'ts': 1000000.11})
def test_list_tagged_args(self, event_from_opts_mock, ensure_connection_mock):
# Case 1, no argument
expected = self.format_error_message(
"A tag name must be specified")
self.assert_system_exit( self.assert_system_exit(
anon_handle_list_tagged, anon_handle_list_tagged,
self.options, self.options, self.session, [],
self.session, stderr=self.format_error_message("A tag name must be specified"),
[], activate_session=None,
stderr=expected, exit_code=2)
activate_session=None) self.ensure_connection_mock.assert_not_called()
self.session.getTag.assert_not_called()
self.session.listTaggedRPMS.assert_not_called()
self.session.listTagged.assert_not_called()
# Case 2, arguments > 2 def test_list_tagged_more_args(self):
expected = self.format_error_message(
"Only one package name may be specified")
self.assert_system_exit( self.assert_system_exit(
anon_handle_list_tagged, anon_handle_list_tagged,
self.options, self.options, self.session, ['tag', 'pkg1', 'pkg2'],
self.session, stderr=self.format_error_message("Only one package name may be specified"),
['tag', 'pkg1', 'pkg2'], activate_session=None,
stderr=expected, exit_code=2)
activate_session=None) self.ensure_connection_mock.assert_not_called()
self.session.getTag.assert_not_called()
self.session.listTaggedRPMS.assert_not_called()
self.session.listTagged.assert_not_called()
# Case 3, no tag found def test_list_tagged_tag_not_found(self):
expected = self.format_error_message(
"No such tag: tag")
self.session.getTag.return_value = None self.session.getTag.return_value = None
self.assert_system_exit( self.assert_system_exit(
anon_handle_list_tagged, anon_handle_list_tagged,
self.options, self.options, self.session, ['tag', 'pkg1'],
self.session, stderr=self.format_error_message("No such tag: tag"),
['tag', 'pkg1'], activate_session=None,
stderr=expected, exit_code=2)
activate_session=None) self.ensure_connection_mock.assert_called_once_with(self.session, self.options)
self.session.getTag.assert_called_once_with(self.tag, event=None)
self.session.listTaggedRPMS.assert_not_called()
self.session.listTagged.assert_not_called()
def test_handle_list_tagged_help(self): def test_handle_list_tagged_help(self):
self.assert_help( self.assert_help(

View file

@ -3,6 +3,7 @@ import unittest
import koji import koji
import kojihub import kojihub
import mock import mock
import copy
QP = kojihub.QueryProcessor QP = kojihub.QueryProcessor
@ -16,6 +17,15 @@ class TestListTagged(unittest.TestCase):
self.exports = kojihub.RootExports() self.exports = kojihub.RootExports()
self.context = mock.patch('kojihub.context').start() self.context = mock.patch('kojihub.context').start()
self.cursor = mock.MagicMock() self.cursor = mock.MagicMock()
self.readTaggedBuilds = mock.patch('kojihub.readTaggedBuilds').start()
self.tag_name = 'test-tag'
self.taginfo = {'id': 1, 'name': 'tag'}
self.tagged_build = [
{'build_id': 1, 'create_event': 1172, 'creation_event_id': 1171, 'epoch': None,
'id': 1, 'name': 'test-pkg', 'nvr': 'test-pkg-2.52-1.fc35', 'owner_id': 1,
'owner_name': 'kojiuser', 'package_id': 1, 'package_name': 'test-pkg',
'release': '1.fc35', 'state': 1, 'tag_id': 1, 'tag_name': 'test-tag',
'task_id': None, 'version': '2.52', 'volume_id': 0, 'volume_name': 'DEFAULT'}]
def getQuery(self, *args, **kwargs): def getQuery(self, *args, **kwargs):
query = QP(*args, **kwargs) query = QP(*args, **kwargs)
@ -26,13 +36,38 @@ class TestListTagged(unittest.TestCase):
def test_non_exist_tag_without_strict(self): def test_non_exist_tag_without_strict(self):
self.get_tag.return_value = None self.get_tag.return_value = None
self.exports.listTagged('non-existing-tag', strict=False) self.exports.listTagged(self.tag_name, strict=False)
self.assertEqual(len(self.queries), 0) self.assertEqual(len(self.queries), 0)
def test_non_exist_tag_with_strict(self): def test_non_exist_tag_with_strict(self):
tag_name = 'non-existing-tag' error_message = "No such tagInfo: '%s'" % self.tag_name
error_message = "No such tagInfo: '%s'" % tag_name
self.get_tag.side_effect = koji.GenericError(error_message) self.get_tag.side_effect = koji.GenericError(error_message)
with self.assertRaises(koji.GenericError) as cm: with self.assertRaises(koji.GenericError) as cm:
self.exports.listTagged(tag_name) self.exports.listTagged(self.tag_name)
self.assertEqual(error_message, str(cm.exception)) self.assertEqual(error_message, str(cm.exception))
def test_list_tagged(self):
self.get_tag.return_value = self.taginfo
self.readTaggedBuilds.return_value = self.tagged_build
rv = self.exports.listTagged(self.tag_name)
self.assertEqual(rv, self.tagged_build)
def test_list_tagged_with_extra(self):
self.get_tag.return_value = self.taginfo
tagged_build = copy.deepcopy(self.tagged_build)
tagged_build[0]['extra'] = 'extra_value'
self.readTaggedBuilds.return_value = tagged_build
rv = self.exports.listTagged(self.tag_name, extra=True)
self.assertEqual(rv, tagged_build)
def test_list_tagged_with_prefix(self):
self.get_tag.return_value = self.taginfo
self.readTaggedBuilds.return_value = self.tagged_build
rv = self.exports.listTagged(self.tag_name, prefix='test')
self.assertEqual(rv, self.tagged_build)
def test_list_tagged_with_prefix_empty_result(self):
self.get_tag.return_value = self.taginfo
self.readTaggedBuilds.return_value = self.tagged_build
rv = self.exports.listTagged(self.tag_name, prefix='pkg')
self.assertEqual(rv, [])

View file

@ -3,6 +3,7 @@ import unittest
import koji import koji
import kojihub import kojihub
import mock import mock
import copy
QP = kojihub.QueryProcessor QP = kojihub.QueryProcessor
@ -16,6 +17,36 @@ class TestListTaggedArchives(unittest.TestCase):
self.exports = kojihub.RootExports() self.exports = kojihub.RootExports()
self.context = mock.patch('kojihub.context').start() self.context = mock.patch('kojihub.context').start()
self.cursor = mock.MagicMock() self.cursor = mock.MagicMock()
self.readTaggedArchives = mock.patch('kojihub.readTaggedArchives').start()
self.tag_name = 'test-tag'
self.taginfo = {'id': 1, 'name': 'tag'}
self.tagged_archives = [
[{'btype': 'maven',
'btype_id': 2,
'build_id': 1,
'checksum': '75a8f47a6626fa1934b3ca5b2c51eb4d2c3bf7c88c621d5327af0a64e08797f6',
'checksum_type': 2,
'filename': 'test-1.4.7.pom',
'id': 1,
'type_id': 3}],
[{'build_id': 1,
'epoch': 7,
'id': 1,
'name': 'kojitest-rpm',
'nvr': 'kojitest-rpm-1.1-11',
'owner_id': 1,
'owner_name': 'kojiadmin',
'package_id': 1,
'package_name': 'kojitest-rpm',
'release': '11',
'state': 1,
'tag_id': self.taginfo['id'],
'tag_name': self.tag_name,
'task_id': 3,
'version': '1.1',
'volume_id': 0,
'volume_name': 'DEFAULT'}]
]
def getQuery(self, *args, **kwargs): def getQuery(self, *args, **kwargs):
query = QP(*args, **kwargs) query = QP(*args, **kwargs)
@ -26,13 +57,26 @@ class TestListTaggedArchives(unittest.TestCase):
def test_non_exist_tag_without_strict(self): def test_non_exist_tag_without_strict(self):
self.get_tag.return_value = None self.get_tag.return_value = None
self.exports.listTagged('non-existing-tag', strict=False) self.exports.listTaggedArchives(self.tag_name, strict=False)
self.assertEqual(len(self.queries), 0) self.assertEqual(len(self.queries), 0)
def test_non_exist_tag_with_strict(self): def test_non_exist_tag_with_strict(self):
tag_name = 'non-existing-tag' error_message = "No such tagInfo: '%s'" % self.tag_name
error_message = "No such tagInfo: '%s'" % tag_name
self.get_tag.side_effect = koji.GenericError(error_message) self.get_tag.side_effect = koji.GenericError(error_message)
with self.assertRaises(koji.GenericError) as cm: with self.assertRaises(koji.GenericError) as cm:
self.exports.listTagged(tag_name) self.exports.listTaggedArchives(self.tag_name)
self.assertEqual(error_message, str(cm.exception)) self.assertEqual(error_message, str(cm.exception))
def test_list_tagged_archives_default(self):
self.get_tag.return_value = self.taginfo
tagged_archives = copy.deepcopy(self.tagged_archives)
tagged_archives[0][0]['extra'] = 'extra_value'
self.readTaggedArchives.return_value = tagged_archives
rv = self.exports.listTaggedArchives(self.tag_name)
self.assertEqual(rv, tagged_archives)
def test_list_tagged_archives_without_extra(self):
self.get_tag.return_value = self.taginfo
self.readTaggedArchives.return_value = self.tagged_archives
rv = self.exports.listTaggedArchives(self.tag_name, extra=False)
self.assertEqual(rv, self.tagged_archives)

View file

@ -3,6 +3,7 @@ import unittest
import koji import koji
import kojihub import kojihub
import mock import mock
import copy
QP = kojihub.QueryProcessor QP = kojihub.QueryProcessor
@ -16,6 +17,35 @@ class TestListTaggedRPMS(unittest.TestCase):
self.exports = kojihub.RootExports() self.exports = kojihub.RootExports()
self.context = mock.patch('kojihub.context').start() self.context = mock.patch('kojihub.context').start()
self.cursor = mock.MagicMock() self.cursor = mock.MagicMock()
self.readTaggedRPMS = mock.patch('kojihub.readTaggedRPMS').start()
self.tag_name = 'test-tag'
self.taginfo = {'id': 1, 'name': 'tag'}
self.tagged_rpms = [
[{'arch': 'src',
'build_id': 1,
'epoch': 7,
'id': 158,
'name': 'kojitest-rpm',
'release': '11',
'version': '1.1'}],
[{'build_id': 1,
'epoch': 7,
'id': 1,
'name': 'kojitest-rpm',
'nvr': 'kojitest-rpm-1.1-11',
'owner_id': 1,
'owner_name': 'kojiadmin',
'package_id': 1,
'package_name': 'kojitest-rpm',
'release': '11',
'state': 1,
'tag_id': self.taginfo['id'],
'tag_name': self.tag_name,
'task_id': 3,
'version': '1.1',
'volume_id': 0,
'volume_name': 'DEFAULT'}]
]
def getQuery(self, *args, **kwargs): def getQuery(self, *args, **kwargs):
query = QP(*args, **kwargs) query = QP(*args, **kwargs)
@ -26,13 +56,26 @@ class TestListTaggedRPMS(unittest.TestCase):
def test_non_exist_tag_without_strict(self): def test_non_exist_tag_without_strict(self):
self.get_tag.return_value = None self.get_tag.return_value = None
self.exports.listTaggedRPMS('non-existing-tag', strict=False) self.exports.listTaggedRPMS(self.tag_name, strict=False)
self.assertEqual(len(self.queries), 0) self.assertEqual(len(self.queries), 0)
def test_non_exist_tag_with_strict(self): def test_non_exist_tag_with_strict(self):
tag_name = 'non-existing-tag' error_message = "No such tagInfo: '%s'" % self.tag_name
error_message = "No such tagInfo: '%s'" % tag_name
self.get_tag.side_effect = koji.GenericError(error_message) self.get_tag.side_effect = koji.GenericError(error_message)
with self.assertRaises(koji.GenericError) as cm: with self.assertRaises(koji.GenericError) as cm:
self.exports.listTaggedRPMS(tag_name) self.exports.listTaggedRPMS(self.tag_name)
self.assertEqual(error_message, str(cm.exception)) self.assertEqual(error_message, str(cm.exception))
def test_list_tagged_archives_default(self):
self.get_tag.return_value = self.taginfo
tagged_rpms = copy.deepcopy(self.tagged_rpms)
tagged_rpms[0][0]['extra'] = 'extra_value'
self.readTaggedRPMS.return_value = tagged_rpms
rv = self.exports.listTaggedRPMS(self.tag_name)
self.assertEqual(rv, tagged_rpms)
def test_list_tagged_archives_without_extra(self):
self.get_tag.return_value = self.taginfo
self.readTaggedRPMS.return_value = self.tagged_rpms
rv = self.exports.listTaggedRPMS(self.tag_name, extra=False)
self.assertEqual(rv, self.tagged_rpms)

View file

@ -0,0 +1,127 @@
import unittest
import mock
import koji
import kojihub
import copy
QP = kojihub.QueryProcessor
class TestReadTaggedArchives(unittest.TestCase):
def getQuery(self, *args, **kwargs):
self.maxDiff = None
query = QP(*args, **kwargs)
query.execute = mock.MagicMock()
query.executeOne = mock.MagicMock()
self.queries.append(query)
return query
def setUp(self):
self.QueryProcessor = mock.patch('kojihub.QueryProcessor',
side_effect=self.getQuery).start()
self.queries = []
self.context = mock.patch('kojihub.context').start()
# It seems MagicMock will not automatically handle attributes that
# start with "assert"
self.exports = kojihub.RootExports()
self.readTaggedBuilds = mock.patch('kojihub.readTaggedBuilds').start()
self.tag_name = 'test-tag'
self.columns = ['archiveinfo.id', 'archiveinfo.type_id', 'archiveinfo.btype_id',
'btype.name', 'archiveinfo.build_id', 'archiveinfo.buildroot_id',
'archiveinfo.filename', 'archiveinfo.size', 'archiveinfo.checksum',
'archiveinfo.checksum_type', 'archiveinfo.metadata_only']
self.joins = ['tag_listing ON archiveinfo.build_id = tag_listing.build_id',
'btype ON archiveinfo.btype_id = btype.id']
self.aliases = ['id', 'type_id', 'btype_id', 'btype', 'build_id', 'buildroot_id',
'filename', 'size', 'checksum', 'checksum_type', 'metadata_only']
self.clauses = ['(active = TRUE)',
'tag_listing.tag_id = %(tagid)i']
self.tables = ['archiveinfo']
self.pkg_name = 'test_pkg'
self.build_list = [
{'build_id': 1, 'create_event': 1172, 'creation_event_id': 1171, 'epoch': None,
'id': 1, 'name': 'test-pkg', 'nvr': 'test-pkg-2.52-1.fc35', 'owner_id': 1,
'owner_name': 'kojiuser', 'package_id': 1, 'package_name': 'test-pkg',
'release': '1.fc35', 'state': 1, 'tag_id': 1, 'tag_name': 'test-tag',
'task_id': None, 'version': '2.52', 'volume_id': 0, 'volume_name': 'DEFAULT'}]
def tearDown(self):
mock.patch.stopall()
def test_get_tagged_archives_default(self):
self.readTaggedBuilds.return_value = self.build_list
kojihub.readTaggedArchives(self.tag_name)
self.assertEqual(len(self.queries), 1)
query = self.queries[0]
columns = copy.deepcopy(self.columns)
columns.append('archiveinfo.extra')
aliases = copy.deepcopy(self.aliases)
aliases.append('extra')
values = {'package': None, 'tagid': self.tag_name}
self.assertEqual(query.tables, self.tables)
self.assertEqual(query.joins, self.joins)
self.assertEqual(set(query.columns), set(columns))
self.assertEqual(set(query.aliases), set(aliases))
self.assertEqual(set(query.clauses), set(self.clauses))
self.assertEqual(query.values, values)
def test_get_tagged_archives_package_type_maven_without_extra(self):
self.readTaggedBuilds.return_value = self.build_list
kojihub.readTaggedArchives(self.tag_name, package=self.pkg_name, type='maven', extra=False)
self.assertEqual(len(self.queries), 1)
query = self.queries[0]
columns = copy.deepcopy(self.columns)
columns.extend(['maven_archives.group_id', 'maven_archives.artifact_id',
'maven_archives.version'])
aliases = copy.deepcopy(self.aliases)
aliases.extend(['maven_group_id', 'maven_artifact_id', 'maven_version'])
clauses = copy.deepcopy(self.clauses)
clauses.extend(['package.name = %(package)s'])
joins = copy.deepcopy(self.joins)
joins.extend(['build ON archiveinfo.build_id = build.id',
'package ON build.pkg_id = package.id',
'maven_archives ON archiveinfo.id = maven_archives.archive_id'])
values = {'package': self.pkg_name, 'tagid': self.tag_name}
self.assertEqual(query.tables, self.tables)
self.assertEqual(query.joins, joins)
self.assertEqual(set(query.columns), set(columns))
self.assertEqual(set(query.aliases), set(aliases))
self.assertEqual(set(query.clauses), set(clauses))
self.assertEqual(query.values, values)
def test_get_tagged_archives_package_type_win_without_extra(self):
self.readTaggedBuilds.return_value = self.build_list
kojihub.readTaggedArchives(self.tag_name, type='win', extra=False)
self.assertEqual(len(self.queries), 1)
query = self.queries[0]
columns = copy.deepcopy(self.columns)
columns.extend(['win_archives.relpath', 'win_archives.platforms', 'win_archives.flags'])
aliases = copy.deepcopy(self.aliases)
aliases.extend(['relpath', 'platforms', 'flags'])
joins = copy.deepcopy(self.joins)
joins.append('win_archives ON archiveinfo.id = win_archives.archive_id')
values = {'package': None, 'tagid': self.tag_name}
self.assertEqual(query.tables, self.tables)
self.assertEqual(query.joins, joins)
self.assertEqual(set(query.columns), set(columns))
self.assertEqual(set(query.aliases), set(aliases))
self.assertEqual(set(query.clauses), set(self.clauses))
self.assertEqual(query.values, values)
def test_get_tagged_archives_type_non_exist(self):
self.readTaggedBuilds.return_value = self.build_list
error_message = "unsupported archive type: non-exist-type"
with self.assertRaises(koji.GenericError) as cm:
kojihub.readTaggedArchives(self.tag_name, type='non-exist-type')
self.assertEqual(error_message, str(cm.exception))

View file

@ -0,0 +1,221 @@
import unittest
import mock
import koji
import kojihub
import copy
QP = kojihub.QueryProcessor
class TestReadTaggedBuilds(unittest.TestCase):
def getQuery(self, *args, **kwargs):
self.maxDiff = None
query = QP(*args, **kwargs)
query.execute = mock.MagicMock()
query.executeOne = mock.MagicMock()
self.queries.append(query)
return query
def setUp(self):
self.QueryProcessor = mock.patch('kojihub.QueryProcessor',
side_effect=self.getQuery).start()
self.queries = []
self.context = mock.patch('kojihub.context').start()
# It seems MagicMock will not automatically handle attributes that
# start with "assert"
self.exports = kojihub.RootExports()
self.readPackageList = mock.patch('kojihub.readPackageList').start()
self.lookup_name = mock.patch('kojihub.lookup_name').start()
self.tag_name = 'test-tag'
self.columns = ['tag.id', 'tag.name', 'build.id', 'build.version', 'build.release',
'build.epoch', 'build.state', 'build.completion_time', 'build.start_time',
'build.task_id', 'users.id', 'users.name', 'events.id', 'events.time',
'volume.id', 'volume.name', 'package.id', 'package.name',
'package.name || \'-\' || build.version || \'-\' || build.release',
'tag_listing.create_event']
self.fields = [('tag.id', 'tag_id'), ('tag.name', 'tag_name'), ('build.id', 'id'),
('build.id', 'build_id'), ('build.version', 'version'),
('build.release', 'release'), ('build.epoch', 'epoch'),
('build.state', 'state'), ('build.completion_time', 'completion_time'),
('build.start_time', 'start_time'), ('build.task_id', 'task_id'),
('users.id', 'owner_id'), ('users.name', 'owner_name'),
('events.id', 'creation_event_id'), ('events.time', 'creation_time'),
('volume.id', 'volume_id'), ('volume.name', 'volume_name'),
('package.id', 'package_id'), ('package.name', 'package_name'),
('package.name', 'name'),
("package.name || '-' || build.version || '-' || build.release", 'nvr'),
('tag_listing.create_event', 'create_event')]
self.joins = ['tag ON tag.id = tag_listing.tag_id',
'build ON build.id = tag_listing.build_id',
'events ON events.id = build.create_event',
'package ON package.id = build.pkg_id',
'volume ON volume.id = build.volume_id',
'users ON users.id = build.owner', ]
self.aliases = ['tag_id', 'tag_name', 'id', 'build_id', 'version', 'release', 'epoch',
'state', 'completion_time', 'start_time', 'task_id', 'owner_id',
'owner_name', 'creation_event_id', 'creation_time', 'volume_id',
'volume_name', 'package_id', 'package_name', 'name', 'nvr', 'create_event']
self.clauses = ['(tag_listing.active = TRUE)',
'tag_id = %(tagid)s',
'build.state = %(st_complete)i']
self.tables = ['tag_listing']
self.pkg_name = 'test_pkg'
self.username = 'testuser'
self.package_list = {1: {'blocked': False, 'extra_arches': '', 'package_id': 1,
'package_name': self.pkg_name, 'tag_id': 4,
'tag_name': self.tag_name},
'owner_name': self.username}
def tearDown(self):
mock.patch.stopall()
def test_get_tagged_builds_default(self):
self.readPackageList.return_value = self.package_list
kojihub.readTaggedBuilds(self.tag_name)
self.assertEqual(len(self.queries), 1)
query = self.queries[0]
values = {'clauses': self.clauses, 'event': None, 'extra': False, 'fields': self.fields,
'inherit': False, 'joins': self.joins, 'latest': False, 'owner': None,
'package': None, 'packages': self.package_list,
'queryOpts': {'order': '-create_event'}, 'st_complete': 1, 'tables': self.tables,
'tag': self.tag_name, 'tagid': self.tag_name, 'taglist': [self.tag_name],
'type': None
}
self.assertEqual(query.tables, self.tables)
self.assertEqual(query.joins, self.joins)
self.assertEqual(set(query.columns), set(self.columns))
self.assertEqual(set(query.aliases), set(self.aliases))
self.assertEqual(set(query.clauses), set(self.clauses))
self.assertEqual(query.values, values)
def test_get_tagged_builds_package_owner_type_maven_extra(self):
self.readPackageList.return_value = self.package_list
kojihub.readTaggedBuilds(self.tag_name, package=self.pkg_name, owner=self.username,
type='maven', extra=True)
self.assertEqual(len(self.queries), 1)
query = self.queries[0]
columns = copy.deepcopy(self.columns)
columns.extend(['maven_builds.group_id', 'maven_builds.artifact_id',
'maven_builds.version', 'build.extra'])
aliases = copy.deepcopy(self.aliases)
aliases.extend(['maven_group_id', 'maven_artifact_id', 'maven_version', 'extra'])
fields = copy.deepcopy(self.fields)
fields.extend([('maven_builds.group_id', 'maven_group_id'),
('maven_builds.artifact_id', 'maven_artifact_id'),
('maven_builds.version', 'maven_version'), ('build.extra', 'extra')])
clauses = copy.deepcopy(self.clauses)
clauses.extend(['package.name = %(package)s', 'users.name = %(owner)s'])
joins = copy.deepcopy(self.joins)
joins.append('maven_builds ON maven_builds.build_id = tag_listing.build_id')
values = {'clauses': clauses, 'event': None, 'extra': True, 'fields': fields,
'inherit': False, 'joins': joins, 'latest': False, 'owner': self.username,
'package': self.pkg_name, 'packages': self.package_list,
'queryOpts': {'order': '-create_event'}, 'st_complete': 1, 'tables': self.tables,
'tag': self.tag_name, 'tagid': self.tag_name, 'taglist': [self.tag_name],
'type': 'maven'}
self.assertEqual(query.tables, self.tables)
self.assertEqual(query.joins, joins)
self.assertEqual(set(query.columns), set(columns))
self.assertEqual(set(query.aliases), set(aliases))
self.assertEqual(set(query.clauses), set(clauses))
self.assertEqual(query.values, values)
def test_get_tagged_builds_type_win_latest(self):
self.readPackageList.return_value = self.package_list
kojihub.readTaggedBuilds(self.tag_name, type='win', latest=True)
self.assertEqual(len(self.queries), 1)
query = self.queries[0]
columns = copy.deepcopy(self.columns)
columns.append('win_builds.platform')
aliases = copy.deepcopy(self.aliases)
aliases.append('platform')
fields = copy.deepcopy(self.fields)
fields.append(('win_builds.platform', 'platform'))
joins = copy.deepcopy(self.joins)
joins.append('win_builds ON win_builds.build_id = tag_listing.build_id')
values = {'clauses': self.clauses, 'event': None, 'extra': False, 'fields': fields,
'inherit': False, 'joins': joins, 'latest': True, 'owner': None,
'package': None, 'packages': self.package_list,
'queryOpts': {'order': '-create_event'}, 'st_complete': 1, 'tables': self.tables,
'tag': self.tag_name, 'tagid': self.tag_name, 'taglist': [self.tag_name],
'type': 'win'}
self.assertEqual(query.tables, self.tables)
self.assertEqual(query.joins, joins)
self.assertEqual(set(query.columns), set(columns))
self.assertEqual(set(query.aliases), set(aliases))
self.assertEqual(set(query.clauses), set(self.clauses))
self.assertEqual(query.values, values)
def test_get_tagged_builds_type_image(self):
self.readPackageList.return_value = self.package_list
kojihub.readTaggedBuilds(self.tag_name, type='image')
self.assertEqual(len(self.queries), 1)
query = self.queries[0]
columns = copy.deepcopy(self.columns)
columns.append('image_builds.build_id')
aliases = copy.deepcopy(self.aliases)
aliases.append('build_id')
fields = copy.deepcopy(self.fields)
fields.append(('image_builds.build_id', 'build_id'))
joins = copy.deepcopy(self.joins)
joins.append('image_builds ON image_builds.build_id = tag_listing.build_id')
values = {'clauses': self.clauses, 'event': None, 'extra': False, 'fields': fields,
'inherit': False, 'joins': joins, 'latest': False, 'owner': None,
'package': None, 'packages': self.package_list,
'queryOpts': {'order': '-create_event'}, 'st_complete': 1, 'tables': self.tables,
'tag': self.tag_name, 'tagid': self.tag_name, 'taglist': [self.tag_name],
'type': 'image'}
self.assertEqual(query.tables, self.tables)
self.assertEqual(query.joins, joins)
self.assertEqual(set(query.columns), set(columns))
self.assertEqual(set(query.aliases), set(aliases))
self.assertEqual(set(query.clauses), set(self.clauses))
self.assertEqual(query.values, values)
def test_get_tagged_builds_type_non_exist(self):
self.readPackageList.return_value = self.package_list
self.lookup_name.return_value = None
error_message = "unsupported build type: non-exist-type"
with self.assertRaises(koji.GenericError) as cm:
kojihub.readTaggedBuilds(self.tag_name, type='non-exist-type')
self.assertEqual(error_message, str(cm.exception))
def test_get_tagged_builds_type_exists(self):
self.readPackageList.return_value = self.package_list
type = 'exist-type'
typeinfo = {'id': 11, 'name': type}
self.lookup_name.return_value = typeinfo
kojihub.readTaggedBuilds(self.tag_name, type=type)
self.assertEqual(len(self.queries), 1)
query = self.queries[0]
joins = copy.deepcopy(self.joins)
joins.append('build_types ON build.id = build_types.build_id AND btype_id = %(btype_id)s')
values = {'btype': typeinfo, 'btype_id': typeinfo['id'], 'clauses': self.clauses,
'event': None, 'extra': False, 'fields': self.fields,
'inherit': False, 'joins': joins, 'latest': False, 'owner': None,
'package': None, 'packages': self.package_list,
'queryOpts': {'order': '-create_event'}, 'st_complete': 1, 'tables': self.tables,
'tag': self.tag_name, 'tagid': self.tag_name, 'taglist': [self.tag_name],
'type': type}
self.assertEqual(query.tables, self.tables)
self.assertEqual(query.joins, joins)
self.assertEqual(set(query.columns), set(self.columns))
self.assertEqual(set(query.aliases), set(self.aliases))
self.assertEqual(set(query.clauses), set(self.clauses))
self.assertEqual(query.values, values)

View file

@ -4,17 +4,41 @@ import mock
import koji import koji
import kojihub import kojihub
import copy
QP = kojihub.QueryProcessor
class TestReadTaggedRPMS(unittest.TestCase): class TestReadTaggedRPMS(unittest.TestCase):
def getQuery(self, *args, **kwargs):
self.maxDiff = None
query = QP(*args, **kwargs)
query.execute = mock.MagicMock()
query.executeOne = mock.MagicMock()
self.queries.append(query)
return query
def setUp(self): def setUp(self):
self.QueryProcessor = mock.patch('kojihub.QueryProcessor',
side_effect=self.getQuery).start()
self.queries = []
self.context = mock.patch('kojihub.context').start() self.context = mock.patch('kojihub.context').start()
# It seems MagicMock will not automatically handle attributes that # It seems MagicMock will not automatically handle attributes that
# start with "assert" # start with "assert"
self.exports = kojihub.RootExports() self.exports = kojihub.RootExports()
self.readTaggedBuilds = mock.patch('kojihub.readTaggedBuilds').start() self.readTaggedBuilds = mock.patch('kojihub.readTaggedBuilds').start()
self.tag_name = 'test-tag' self.tag_name = 'test-tag'
self.columns = ['rpminfo.name', 'rpminfo.version', 'rpminfo.release', 'rpminfo.arch',
'rpminfo.id', 'rpminfo.epoch', 'rpminfo.payloadhash', 'rpminfo.size',
'rpminfo.buildtime', 'rpminfo.buildroot_id', 'rpminfo.build_id',
'rpminfo.metadata_only']
self.joins = ['tag_listing ON rpminfo.build_id = tag_listing.build_id']
self.aliases = ['name', 'version', 'release', 'arch', 'id', 'epoch', 'payloadhash',
'size', 'buildtime', 'buildroot_id', 'build_id', 'metadata_only']
self.clauses = ['(tag_listing.active = TRUE)',
'tag_id=%(tagid)s']
self.tables = ['rpminfo']
self.pkg_name = 'test_pkg'
self.build_list = [ self.build_list = [
{'build_id': 1, 'create_event': 1172, 'creation_event_id': 1171, 'epoch': None, {'build_id': 1, 'create_event': 1172, 'creation_event_id': 1171, 'epoch': None,
'id': 1, 'name': 'test-pkg', 'nvr': 'test-pkg-2.52-1.fc35', 'owner_id': 1, 'id': 1, 'name': 'test-pkg', 'nvr': 'test-pkg-2.52-1.fc35', 'owner_id': 1,
@ -31,3 +55,50 @@ class TestReadTaggedRPMS(unittest.TestCase):
with self.assertRaises(koji.GenericError) as cm: with self.assertRaises(koji.GenericError) as cm:
kojihub.readTaggedRPMS(self.tag_name, arch=1245) kojihub.readTaggedRPMS(self.tag_name, arch=1245)
self.assertEqual(error_message, str(cm.exception)) self.assertEqual(error_message, str(cm.exception))
def test_get_tagged_rpms_package_arch_list_without_extra(self):
self.readTaggedBuilds.return_value = self.build_list
kojihub.readTaggedRPMS(self.tag_name, package=self.pkg_name, arch=['x86_64', 'ppc'],
extra=False)
self.assertEqual(len(self.queries), 1)
query = self.queries[0]
joins = copy.deepcopy(self.joins)
joins.extend(['build ON rpminfo.build_id = build.id',
'package ON package.id = build.pkg_id'])
clauses = copy.deepcopy(self.clauses)
clauses.extend(['package.name = %(package)s', 'rpminfo.arch IN %(arch)s'])
values = {'arch': ['x86_64', 'ppc'], 'package': 'test_pkg'}
self.assertEqual(query.tables, self.tables)
self.assertEqual(set(query.columns), set(self.columns))
self.assertEqual(query.joins, joins)
self.assertEqual(set(query.aliases), set(self.aliases))
self.assertEqual(set(query.clauses), set(clauses))
self.assertEqual(query.values, values)
def test_get_tagged_rpms_rpmsigs_arch_extra(self):
self.readTaggedBuilds.return_value = self.build_list
kojihub.readTaggedRPMS(self.tag_name, rpmsigs='FD431D51', arch='x86_64')
self.assertEqual(len(self.queries), 1)
query = self.queries[0]
joins = copy.deepcopy(self.joins)
joins.extend(['LEFT OUTER JOIN rpmsigs on rpminfo.id = rpmsigs.rpm_id'])
clauses = copy.deepcopy(self.clauses)
clauses.append('rpminfo.arch = %(arch)s')
columns = copy.deepcopy(self.columns)
columns.extend(['rpmsigs.sigkey', 'rpminfo.extra'])
aliases = copy.deepcopy(self.aliases)
aliases.extend(['sigkey', 'extra'])
values = {'arch': 'x86_64'}
self.assertEqual(query.tables, self.tables)
self.assertEqual(set(query.columns), set(columns))
self.assertEqual(query.joins, joins)
self.assertEqual(set(query.aliases), set(aliases))
self.assertEqual(set(query.clauses), set(clauses))
self.assertEqual(query.values, values)