From 826390570bdbdbba05fc59b7625ed02f811f1bc6 Mon Sep 17 00:00:00 2001
From: Jana Cupova <root@localhost>
Date: Apr 21 2024 18:32:26 +0000
Subject: Increase index.py unit tests


---

diff --git a/tests/test_www/test_activesessiondelete.py b/tests/test_www/test_activesessiondelete.py
new file mode 100644
index 0000000..3f62f11
--- /dev/null
+++ b/tests/test_www/test_activesessiondelete.py
@@ -0,0 +1,41 @@
+from __future__ import absolute_import
+import unittest
+import mock
+
+from koji.server import ServerRedirect
+from .loadwebindex import webidx
+
+
+class TestActiveSessionDelete(unittest.TestCase):
+    def setUp(self):
+        self.get_server = mock.patch.object(webidx, "_getServer").start()
+        self.assert_login = mock.patch.object(webidx, "_assertLogin").start()
+        self.server = mock.MagicMock()
+        self.environ = {
+            'koji.options': {
+                'SiteName': 'test',
+                'KojiFilesURL': 'https://server.local/files',
+            },
+            'koji.currentUser': None,
+
+        }
+
+        def __get_server(env):
+            env['koji.session'] = self.server
+            return self.server
+
+        self.get_server.side_effect = __get_server
+
+    def tearDown(self):
+        mock.patch.stopall()
+
+    def test_activesessiondelete(self):
+        """Test activesessiondelete function."""
+        session_id = 1
+        self.get_server.return_value = self.server
+        self.server.logout.return_value = None
+
+        with self.assertRaises(ServerRedirect):
+            webidx.activesessiondelete(self.environ, session_id)
+        self.assertEqual(self.environ['koji.redirect'], 'activesession')
+        self.server.logout.assert_called_with(session_id=session_id)
diff --git a/tests/test_www/test_archiveinfo.py b/tests/test_www/test_archiveinfo.py
new file mode 100644
index 0000000..674273c
--- /dev/null
+++ b/tests/test_www/test_archiveinfo.py
@@ -0,0 +1,45 @@
+import mock
+import unittest
+
+from .loadwebindex import webidx
+
+
+class TestArchiveInfo(unittest.TestCase):
+    def setUp(self):
+        self.get_server = mock.patch.object(webidx, "_getServer").start()
+        self.gen_html = mock.patch.object(webidx, '_genHTML').start()
+        self.server = mock.MagicMock()
+        self.environ = {
+            'koji.options': {
+                'SiteName': 'test',
+                'KojiFilesURL': 'https://server.local/files',
+            },
+            'koji.currentUser': None
+        }
+        self.archive_id = '5'
+
+    def tearDown(self):
+        mock.patch.stopall()
+
+    def test_archiveinfo(self):
+        """Test archiveinfo function"""
+        self.server.getArchive.return_value = {'type_id': 6, 'build_id': 1, 'buildroot_id': 444,
+                                               'id': int(self.archive_id),
+                                               'filename': 'test-filename'}
+        self.server.getArchiveType.return_value = 'arch-type'
+        self.server.getBuild.return_value = {'id': 1, 'name': 'test-build'}
+        self.server.getBuildroot.return_value = {'id': 444}
+        self.server.listRPMs.return_value = [{'id': 123}, {'id': 124}]
+        self.server.listArchives.return_value = [{'id': 457}, {'id': 458}]
+
+        self.get_server.return_value = self.server
+
+        webidx.archiveinfo(self.environ, self.archive_id)
+        self.server.getArchive.assert_called_once_with(int(self.archive_id))
+        self.server.getArchiveType.assert_called_once_with(type_id=6)
+        self.server.getBuild.assert_called_once_with(1)
+        self.server.getBuildroot.assert_called_once_with(444)
+        self.server.listRPMs.assert_called_once_with(
+            imageID=int(self.archive_id), queryOpts={'limit': 1})
+        self.server.listArchives.assert_called_once_with(
+            imageID=int(self.archive_id), queryOpts={'limit': 1})
diff --git a/tests/test_www/test_buildinfo.py b/tests/test_www/test_buildinfo.py
index 981dcfa..ed590bf 100644
--- a/tests/test_www/test_buildinfo.py
+++ b/tests/test_www/test_buildinfo.py
@@ -8,6 +8,7 @@ from .loadwebindex import webidx
 class TestBuildInfo(unittest.TestCase):
     def setUp(self):
         self.get_server = mock.patch.object(webidx, "_getServer").start()
+        self.server = mock.MagicMock()
         self.environ = {
             'koji.options': {
                 'SiteName': 'test',
@@ -22,10 +23,9 @@ class TestBuildInfo(unittest.TestCase):
 
     def test_buildinfo_exception(self):
         """Test taskinfo function raises exception"""
-        server = mock.MagicMock()
-        server.getBuild.side_effect = koji.GenericError
-        self.get_server.return_value = server
+        self.server.getBuild.side_effect = koji.GenericError
+        self.get_server.return_value = self.server
 
         with self.assertRaises(koji.GenericError) as cm:
             webidx.buildinfo(self.environ, self.build_id)
-        self.assertEqual(str(cm.exception), 'No such build ID: %s' % self.build_id)
+        self.assertEqual(str(cm.exception), f'No such build ID: {self.build_id}')
diff --git a/tests/test_www/test_buildrootinfo.py b/tests/test_www/test_buildrootinfo.py
index 47634a2..7e4c212 100644
--- a/tests/test_www/test_buildrootinfo.py
+++ b/tests/test_www/test_buildrootinfo.py
@@ -8,6 +8,7 @@ from .loadwebindex import webidx
 class TestBuildrootInfo(unittest.TestCase):
     def setUp(self):
         self.get_server = mock.patch.object(webidx, "_getServer").start()
+        self.gen_html = mock.patch.object(webidx, '_genHTML').start()
         self.assert_login = mock.patch.object(webidx, "_assertLogin").start()
         self.server = mock.MagicMock()
         self.environ = {
@@ -30,3 +31,23 @@ class TestBuildrootInfo(unittest.TestCase):
         with self.assertRaises(koji.GenericError) as cm:
             webidx.buildrootinfo(self.environ, self.buildroot_id)
         self.assertEqual(str(cm.exception), f'unknown buildroot ID: {self.buildroot_id}')
+
+    def test_buildrootinfo_valid(self):
+        """Test buildrootinfo function."""
+        self.get_server.return_value = self.server
+        self.server.getBuildroot.return_value = {'br_type': 55, 'cg_name': 'test-test',
+                                                 'id': int(self.buildroot_id)}
+        webidx.buildrootinfo(self.environ, self.buildroot_id)
+        self.server.getBuildroot.assert_called_once_with(int(self.buildroot_id))
+        self.server.getTaskInfo.assert_not_called()
+
+    def test_buildrootinfo_valid_2(self):
+        """Test buildrootinfo function."""
+        self.get_server.return_value = self.server
+        self.server.getBuildroot.return_value = {'br_type': 0, 'task_id': 345,
+                                                 'tag_name': 'test-tag', 'repo_id': 999,
+                                                 'id': int(self.buildroot_id)}
+        self.server.getTaskInfo.return_value = {'id': 345}
+        webidx.buildrootinfo(self.environ, self.buildroot_id)
+        self.server.getBuildroot.assert_called_once_with(int(self.buildroot_id))
+        self.server.getTaskInfo.assert_called_once_with(345, request=True)
diff --git a/tests/test_www/test_buildtargetcreate.py b/tests/test_www/test_buildtargetcreate.py
index d3502e0..74057d1 100644
--- a/tests/test_www/test_buildtargetcreate.py
+++ b/tests/test_www/test_buildtargetcreate.py
@@ -12,6 +12,7 @@ class TestBuildTargetCreate(unittest.TestCase):
     def setUp(self):
         self.get_server = mock.patch.object(webidx, "_getServer").start()
         self.assert_login = mock.patch.object(webidx, "_assertLogin").start()
+        self.gen_html = mock.patch.object(webidx, '_genHTML').start()
         self.server = mock.MagicMock()
         self.buildtarget_id = '1'
         self.buildtag_id = '11'
@@ -54,6 +55,10 @@ class TestBuildTargetCreate(unittest.TestCase):
 
         with self.assertRaises(koji.GenericError) as cm:
             webidx.buildtargetcreate(self.environ)
+        self.server.createBuildTarget.assert_called_with('testname', int(self.buildtag_id),
+                                                         int(self.desttag_id))
+        self.server.getBuildTarget.assert_called_with('testname')
+        self.server.listTags.assert_not_called()
         self.assertEqual(str(cm.exception), 'error creating build target "testname"')
 
     def test_buildtargetcreate_add_case_valid(self):
@@ -74,6 +79,8 @@ class TestBuildTargetCreate(unittest.TestCase):
             webidx.buildtargetcreate(self.environ)
         self.server.createBuildTarget.assert_called_with('testname', int(self.buildtag_id),
                                                          int(self.desttag_id))
+        self.server.getBuildTarget.assert_called_with('testname')
+        self.server.listTags.assert_not_called()
         self.assertEqual(self.environ['koji.redirect'], 'buildtargetinfo?targetID=1')
 
     def test_buildtargetcreate_cancel_case(self):
@@ -87,9 +94,28 @@ class TestBuildTargetCreate(unittest.TestCase):
             return self.server
 
         self.get_server.side_effect = __get_server
-        self.server.getBuildTarget.return_value = {'id': self.buildtarget_id}
 
         with self.assertRaises(ServerRedirect):
             webidx.buildtargetcreate(self.environ)
         self.server.createBuildTarget.assert_not_called()
+        self.server.getBuildTarget.assert_not_called()
+        self.server.listTags.assert_not_called()
         self.assertEqual(self.environ['koji.redirect'], 'buildtargets')
+
+    def test_buildtargetcreate_another_case(self):
+        """Test buildtargetcreate function valid case (another)."""
+        urlencode_data = b"another=True"
+        fs = self.get_fs(urlencode_data)
+
+        def __get_server(env):
+            env['koji.session'] = self.server
+            env['koji.form'] = fs
+            return self.server
+
+        self.get_server.side_effect = __get_server
+        self.server.listTags.return_value = [{'id': 1, 'name': 'test-tag-1'},
+                                             {'id': 2, 'name': 'test-tag-2'}]
+        webidx.buildtargetcreate(self.environ)
+        self.server.createBuildTarget.assert_not_called()
+        self.server.getBuildTarget.assert_not_called()
+        self.server.listTags.assert_called_with()
diff --git a/tests/test_www/test_buildtargetdelete.py b/tests/test_www/test_buildtargetdelete.py
index 0c9e636..e48eb19 100644
--- a/tests/test_www/test_buildtargetdelete.py
+++ b/tests/test_www/test_buildtargetdelete.py
@@ -39,7 +39,7 @@ class TestBuildTargetDelete(unittest.TestCase):
         with self.assertRaises(koji.GenericError) as cm:
             webidx.buildtargetdelete(self.environ, self.buildtarget_id)
         self.session.deleteBuildTarget.assert_not_called()
-        self.assertEqual(str(cm.exception), 'No such build target: %s' % self.buildtarget_id)
+        self.assertEqual(str(cm.exception), f'No such build target: {self.buildtarget_id}')
 
     def test_buildtargetdelete_normal_case(self):
         """Test taskinfo function"""
diff --git a/tests/test_www/test_buildtargetedit.py b/tests/test_www/test_buildtargetedit.py
index b8087f0..9713d57 100644
--- a/tests/test_www/test_buildtargetedit.py
+++ b/tests/test_www/test_buildtargetedit.py
@@ -12,6 +12,7 @@ class TestBuildTargetEdit(unittest.TestCase):
     def setUp(self):
         self.get_server = mock.patch.object(webidx, "_getServer").start()
         self.assert_login = mock.patch.object(webidx, "_assertLogin").start()
+        self.gen_html = mock.patch.object(webidx, '_genHTML').start()
         self.server = mock.MagicMock()
         self.buildtarget_id = '1'
         self.buildtag_id = '11'
@@ -55,6 +56,10 @@ class TestBuildTargetEdit(unittest.TestCase):
         with self.assertRaises(koji.GenericError) as cm:
             webidx.buildtargetedit(self.environ, self.buildtarget_id)
         self.assertEqual(str(cm.exception), f'No such build target: {self.buildtarget_id}')
+        self.server.getBuildTarget.assert_called_with(int(self.buildtarget_id))
+        self.server.getTag.assert_not_called()
+        self.server.editBuildTarget.assert_not_called()
+        self.server.listTags.assert_not_called()
 
     def test_buildtargetedit_exception_build_tag(self):
         """Test buildtargetedit function raises exception when build tag not exists."""
@@ -74,6 +79,10 @@ class TestBuildTargetEdit(unittest.TestCase):
         with self.assertRaises(koji.GenericError) as cm:
             webidx.buildtargetedit(self.environ, self.buildtarget_id)
         self.assertEqual(str(cm.exception), f'No such tag ID: {self.buildtag_id}')
+        self.server.getBuildTarget.assert_called_with(int(self.buildtarget_id))
+        self.server.getTag.assert_called_with(int(self.buildtag_id))
+        self.server.editBuildTarget.assert_not_called()
+        self.server.listTags.assert_not_called()
 
     def test_buildtargetedit_exception_dest_tag(self):
         """Test buildtargetedit function raises exception when destination tag not exists."""
@@ -93,6 +102,11 @@ class TestBuildTargetEdit(unittest.TestCase):
         with self.assertRaises(koji.GenericError) as cm:
             webidx.buildtargetedit(self.environ, self.buildtarget_id)
         self.assertEqual(str(cm.exception), f'No such tag ID: {self.desttag_id}')
+        self.server.getBuildTarget.assert_called_with(int(self.buildtarget_id))
+        self.server.getTag.assert_has_calls([mock.call(int(self.buildtag_id)),
+                                             mock.call(int(self.desttag_id))])
+        self.server.editBuildTarget.assert_not_called()
+        self.server.listTags.assert_not_called()
 
     def test_buildtargetedit_save_case_valid(self):
         """Test buildtargetedit function valid case (save)."""
@@ -116,6 +130,12 @@ class TestBuildTargetEdit(unittest.TestCase):
             webidx.buildtargetedit(self.environ, self.buildtarget_id)
         self.assertEqual(self.environ['koji.redirect'],
                          f'buildtargetinfo?targetID={self.buildtarget_id}')
+        self.server.getBuildTarget.assert_called_with(int(self.buildtarget_id))
+        self.server.getTag.assert_has_calls([mock.call(int(self.buildtag_id)),
+                                             mock.call(int(self.desttag_id))])
+        self.server.editBuildTarget.assert_called_with(int(self.buildtarget_id), 'testname',
+                                                       int(self.buildtag_id), int(self.desttag_id))
+        self.server.listTags.assert_not_called()
 
     def test_buildtargetedit_cancel_case(self):
         """Test buildtargetedit function valid case (cancel)."""
@@ -132,8 +152,29 @@ class TestBuildTargetEdit(unittest.TestCase):
 
         with self.assertRaises(ServerRedirect):
             webidx.buildtargetedit(self.environ, self.buildtarget_id)
+        self.assertEqual(self.environ['koji.redirect'],
+                         f'buildtargetinfo?targetID={self.buildtarget_id}')
         self.server.getBuildTarget.assert_called_with(int(self.buildtarget_id))
         self.server.getTag.assert_not_called()
         self.server.editBuildTarget.assert_not_called()
-        self.assertEqual(self.environ['koji.redirect'],
-                         f'buildtargetinfo?targetID={self.buildtarget_id}')
+        self.server.listTags.assert_not_called()
+
+    def test_buildtargetedit_another_case(self):
+        """Test buildtargetedit function valid case (another)."""
+        urlencode_data = b"another=True"
+        fs = self.get_fs(urlencode_data)
+        self.server.getBuildTarget.return_value = {'id': int(self.buildtarget_id)}
+
+        def __get_server(env):
+            env['koji.session'] = self.server
+            env['koji.form'] = fs
+            return self.server
+
+        self.get_server.side_effect = __get_server
+        self.server.listTags.return_value = [{'id': 1, 'name': 'test-tag-1'},
+                                             {'id': 2, 'name': 'test-tag-2'}]
+        webidx.buildtargetedit(self.environ, self.buildtarget_id)
+        self.server.getBuildTarget.assert_called_with(int(self.buildtarget_id))
+        self.server.getTag.assert_not_called()
+        self.server.editBuildTarget.assert_not_called()
+        self.server.listTags.assert_called_with()
diff --git a/tests/test_www/test_buildtargetinfo.py b/tests/test_www/test_buildtargetinfo.py
index 0fb6908..668b7e9 100644
--- a/tests/test_www/test_buildtargetinfo.py
+++ b/tests/test_www/test_buildtargetinfo.py
@@ -8,6 +8,8 @@ from .loadwebindex import webidx
 class TestBuildTargetInfo(unittest.TestCase):
     def setUp(self):
         self.get_server = mock.patch.object(webidx, "_getServer").start()
+        self.gen_html = mock.patch.object(webidx, '_genHTML').start()
+        self.server = mock.MagicMock()
         self.environ = {
             'koji.options': {
                 'SiteName': 'test',
@@ -22,21 +24,47 @@ class TestBuildTargetInfo(unittest.TestCase):
         mock.patch.stopall()
 
     def test_buildtargetinfo_exception_id(self):
-        """Test taskinfo function raises exception"""
-        server = mock.MagicMock()
-        server.getBuildTarget.return_value = None
-        self.get_server.return_value = server
+        """Test buildtargetinfo function raises exception"""
+        self.server.getBuildTarget.return_value = None
+        self.get_server.return_value = self.server
 
         with self.assertRaises(koji.GenericError) as cm:
             webidx.buildtargetinfo(self.environ, targetID=self.buildtarget_id)
-        self.assertEqual(str(cm.exception), 'No such build target: %s' % self.buildtarget_id)
+        self.assertEqual(str(cm.exception), f'No such build target: {self.buildtarget_id}')
 
     def test_buildtargetinfo_exception_name(self):
-        """Test taskinfo function raises exception"""
-        server = mock.MagicMock()
-        server.getBuildTarget.return_value = None
-        self.get_server.return_value = server
+        """Test buildtargetinfo function raises exception"""
+        self.server.getBuildTarget.return_value = None
+        self.get_server.return_value = self.server
 
         with self.assertRaises(koji.GenericError) as cm:
             webidx.buildtargetinfo(self.environ, name=self.buildtarget_name)
-        self.assertEqual(str(cm.exception), 'No such build target: %s' % self.buildtarget_name)
+        self.assertEqual(str(cm.exception), f'No such build target: {self.buildtarget_name}')
+
+    def test_buildtargetinfo_without_current_user(self):
+        """Test buildtargetinfo function valid without current user"""
+        self.server.getBuildTarget.return_value = {'name': 'test-build-target',
+                                                   'build_tag': 'test-build-tag',
+                                                   'dest_tag': 'test-dest-tag'}
+        self.server.getTag.side_effect = [{'id': 123, 'name': 'test-build-tag'},
+                                          {'id': 234, 'name': 'test-dest-tag'}]
+        self.get_server.return_value = self.server
+        webidx.buildtargetinfo(self.environ, name=self.buildtarget_name)
+
+    def test_buildtargetinfo_with_current_user(self):
+        """Test buildtargetinfo function valid with current user"""
+        environ = {
+            'koji.options': {
+                'SiteName': 'test',
+                'KojiFilesURL': 'https://server.local/files',
+            },
+            'koji.currentUser': {'id': 5}
+        }
+        self.server.getBuildTarget.return_value = {'name': 'test-build-target',
+                                                   'build_tag': 'test-build-tag',
+                                                   'dest_tag': 'test-dest-tag'}
+        self.server.getTag.side_effect = [{'id': 123, 'name': 'test-build-tag'},
+                                          {'id': 234, 'name': 'test-dest-tag'}]
+        self.server.getUserPerms.return_value = ['perm-1', 'perm-2']
+        self.get_server.return_value = self.server
+        webidx.buildtargetinfo(environ, name=self.buildtarget_name)
diff --git a/tests/test_www/test_channelinfo.py b/tests/test_www/test_channelinfo.py
index 742418f..b7db290 100644
--- a/tests/test_www/test_channelinfo.py
+++ b/tests/test_www/test_channelinfo.py
@@ -8,6 +8,8 @@ from .loadwebindex import webidx
 class TestChannelInfo(unittest.TestCase):
     def setUp(self):
         self.get_server = mock.patch.object(webidx, "_getServer").start()
+        self.gen_html = mock.patch.object(webidx, '_genHTML').start()
+        self.server = mock.MagicMock()
         self.environ = {
             'koji.options': {
                 'SiteName': 'test',
@@ -21,11 +23,30 @@ class TestChannelInfo(unittest.TestCase):
         mock.patch.stopall()
 
     def test_channelinfo_exception(self):
-        """Test taskinfo function raises exception"""
-        server = mock.MagicMock()
-        self.get_server.return_value = server
-        server.getChannel.return_value = None
+        """Test channelinfo function raises exception"""
+        self.get_server.return_value = self.server
+        self.server.getChannel.return_value = None
 
         with self.assertRaises(koji.GenericError) as cm:
             webidx.channelinfo(self.environ, self.channel_id)
-        self.assertEqual(str(cm.exception), 'No such channel ID: %s' % self.channel_id)
+        self.assertEqual(str(cm.exception), f'No such channel ID: {self.channel_id}')
+        self.server.getChannel.assert_called_once_with(int(self.channel_id))
+        self.server.listTasks.assert_not_called()
+        self.server.listHosts.assert_not_called()
+
+    def test_channel_info_valid(self):
+        """Test channelinfo function valid"""
+        self.get_server.return_value = self.server
+        self.server.getChannel.return_value = {'name': 'test-channel', 'id': self.channel_id}
+        self.server.listTasks.return_value = 5
+        self.server.listHosts.return_value = [
+            {'id': 1, 'name': 'test-host-1', 'enabled': True, 'ready': True},
+            {'id': 2, 'name': 'test-host-2', 'enabled': False, 'ready': False},
+            {'id': 3, 'name': 'test-host-3', 'enabled': True, 'ready': False}]
+
+        webidx.channelinfo(self.environ, self.channel_id)
+        self.server.getChannel.assert_called_once_with(int(self.channel_id))
+        self.server.listTasks.assert_called_once_with(
+            opts={'channel_id': int(self.channel_id), 'state': [0, 1, 4]},
+            queryOpts={'countOnly': True})
+        self.server.listHosts.assert_called_once_with(channelID=int(self.channel_id))
diff --git a/tests/test_www/test_externalrepoinfo.py b/tests/test_www/test_externalrepoinfo.py
new file mode 100644
index 0000000..60c9624
--- /dev/null
+++ b/tests/test_www/test_externalrepoinfo.py
@@ -0,0 +1,31 @@
+import mock
+import unittest
+
+from .loadwebindex import webidx
+
+
+class TestExternalRepoInfo(unittest.TestCase):
+    def setUp(self):
+        self.get_server = mock.patch.object(webidx, "_getServer").start()
+        self.gen_html = mock.patch.object(webidx, '_genHTML').start()
+        self.server = mock.MagicMock()
+        self.environ = {
+            'koji.options': {
+                'SiteName': 'test',
+                'KojiFilesURL': 'https://server.local/files',
+            },
+            'koji.currentUser': None
+        }
+        self.extrepo_id = '111'
+
+    def tearDown(self):
+        mock.patch.stopall()
+
+    def test_externalrepoinfo_valid(self):
+        """Test externalrepoinfo function."""
+        self.get_server.return_value = self.server
+        self.server.getExternalRepo.return_value = {'name': 'ext-repo', 'id': 111}
+        self.server.getTagExternalRepos.return_value = {'id': 1, 'name': 'test-tag'}
+        webidx.externalrepoinfo(self.environ, self.extrepo_id)
+        self.server.getExternalRepo.assert_called_once_with(int(self.extrepo_id), strict=True)
+        self.server.getTagExternalRepos.assert_called_once_with(repo_info=int(self.extrepo_id))
diff --git a/tests/test_www/test_fileinfo.py b/tests/test_www/test_fileinfo.py
index c950cda..4630f27 100644
--- a/tests/test_www/test_fileinfo.py
+++ b/tests/test_www/test_fileinfo.py
@@ -8,7 +8,8 @@ from .loadwebindex import webidx
 class TestFileInfo(unittest.TestCase):
     def setUp(self):
         self.get_server = mock.patch.object(webidx, "_getServer").start()
-
+        self.gen_html = mock.patch.object(webidx, '_genHTML').start()
+        self.server = mock.MagicMock()
         self.environ = {
             'koji.options': {
                 'SiteName': 'test',
@@ -24,61 +25,94 @@ class TestFileInfo(unittest.TestCase):
         mock.patch.stopall()
 
     def test_fileinfo_exception_rpm(self):
-        """Test taskinfo function raises exception"""
-        server = mock.MagicMock()
-        server.getRPM.return_value = None
-
-        self.get_server.return_value = server
+        """Test fileinfo function raises exception"""
+        self.server.getRPM.return_value = None
+        self.get_server.return_value = self.server
 
         with self.assertRaises(koji.GenericError) as cm:
             webidx.fileinfo(self.environ, self.filename, rpmID=self.rpm_id)
         self.assertEqual(
-            str(cm.exception), 'No such RPM ID: %s' % self.rpm_id)
+            str(cm.exception), f'No such RPM ID: {self.rpm_id}')
+        self.server.getRPM.assert_called_once_with(int(self.rpm_id))
+        self.server.getRPMFile.assert_not_called()
+        self.server.getArchive.assert_not_called()
+        self.server.getArchiveFile.assert_not_called()
 
     def test_fileinfo_exception_archive(self):
-        """Test taskinfo function raises exception"""
-        server = mock.MagicMock()
-        server.getArchive.return_value = None
-
-        self.get_server.return_value = server
+        """Test fileinfo function raises exception"""
+        self.server.getArchive.return_value = None
+        self.get_server.return_value = self.server
 
         with self.assertRaises(koji.GenericError) as cm:
             webidx.fileinfo(self.environ, self.filename, archiveID=self.archive_id)
         self.assertEqual(
-            str(cm.exception), 'No such archive ID: %s' % self.archive_id)
+            str(cm.exception), f'No such archive ID: {self.archive_id}')
+        self.server.getRPM.assert_not_called()
+        self.server.getRPMFile.assert_not_called()
+        self.server.getArchive.assert_called_once_with(int(self.archive_id))
+        self.server.getArchiveFile.assert_not_called()
 
     def test_fileinfo_exception_rpm_file(self):
-        """Test taskinfo function raises exception"""
-        server = mock.MagicMock()
-        server.getRPMFile.return_value = None
-        server.getRPM.return_value = {'id': 123}
-
-        self.get_server.return_value = server
+        """Test fileinfo function raises exception"""
+        self.server.getRPMFile.return_value = None
+        self.server.getRPM.return_value = {'id': self.rpm_id}
+        self.get_server.return_value = self.server
 
         with self.assertRaises(koji.GenericError) as cm:
             webidx.fileinfo(self.environ, self.filename, rpmID=self.rpm_id)
-        self.assertEqual(
-            str(cm.exception), 'no file %s in RPM %i' % (self.filename, int(self.rpm_id)))
+        self.assertEqual(str(cm.exception), f'no file {self.filename} in RPM {self.rpm_id}')
+        self.server.getRPM.assert_called_once_with(int(self.rpm_id))
+        self.server.getRPMFile.assert_called_once_with(self.rpm_id, self.filename)
+        self.server.getArchive.assert_not_called()
+        self.server.getArchiveFile.assert_not_called()
 
     def test_fileinfo_exception_archive_file(self):
-        """Test taskinfo function raises exception"""
-        server = mock.MagicMock()
-        server.getArchiveFile.return_value = None
-        server.getArchive.return_value = {'id': 123}
-
-        self.get_server.return_value = server
+        """Test fileinfo function raises exception"""
+        self.server.getArchiveFile.return_value = None
+        self.server.getArchive.return_value = {'id': self.archive_id}
+        self.get_server.return_value = self.server
 
         with self.assertRaises(koji.GenericError) as cm:
             webidx.fileinfo(self.environ, self.filename, archiveID=self.archive_id)
-        self.assertEqual(
-            str(cm.exception), 'no file %s in archive %i' % (self.filename, int(self.archive_id)))
+        self.assertEqual(str(cm.exception),
+                         f'no file {self.filename} in archive {self.archive_id}')
+        self.server.getRPM.assert_not_called()
+        self.server.getRPMFile.assert_not_called()
+        self.server.getArchive.assert_called_once_with(int(self.archive_id))
+        self.server.getArchiveFile.assert_called_once_with(self.archive_id, self.filename)
 
     def test_fileinfo_exception(self):
-        """Test taskinfo function raises exception"""
-        server = mock.MagicMock()
-
-        self.get_server.return_value = server
+        """Test fileinfo function raises exception"""
+        self.get_server.return_value = self.server
 
         with self.assertRaises(koji.GenericError) as cm:
             webidx.fileinfo(self.environ, self.filename)
         self.assertEqual(str(cm.exception), 'either rpmID or archiveID must be specified')
+        self.server.getRPM.assert_not_called()
+        self.server.getRPMFile.assert_not_called()
+        self.server.getArchive.assert_not_called()
+        self.server.getArchiveFile.assert_not_called()
+
+    def test_fileinfo_archive_file_valid(self):
+        """Test fileinfo function valid"""
+        self.server.getArchiveFile.return_value = {'name': self.filename}
+        self.server.getArchive.return_value = {'id': self.archive_id}
+        self.get_server.return_value = self.server
+
+        webidx.fileinfo(self.environ, self.filename, archiveID=self.archive_id)
+        self.server.getRPM.assert_not_called()
+        self.server.getRPMFile.assert_not_called()
+        self.server.getArchive.assert_called_once_with(int(self.archive_id))
+        self.server.getArchiveFile.assert_called_once_with(self.archive_id, self.filename)
+
+    def test_fileinfo_rpm_file_valid(self):
+        """Test fileinfo function valid"""
+        self.server.getRPMFile.return_value = {'name': f'{self.filename}.rpm'}
+        self.server.getRPM.return_value = {'id': self.rpm_id}
+        self.get_server.return_value = self.server
+
+        webidx.fileinfo(self.environ, self.filename, rpmID=self.rpm_id)
+        self.server.getRPM.assert_called_once_with(int(self.rpm_id))
+        self.server.getRPMFile.assert_called_once_with(self.rpm_id, self.filename)
+        self.server.getArchive.assert_not_called()
+        self.server.getArchiveFile.assert_not_called()
diff --git a/tests/test_www/test_hostedit.py b/tests/test_www/test_hostedit.py
index 32d4e1f..a55a7b1 100644
--- a/tests/test_www/test_hostedit.py
+++ b/tests/test_www/test_hostedit.py
@@ -12,6 +12,7 @@ class TestHostEdit(unittest.TestCase):
     def setUp(self):
         self.get_server = mock.patch.object(webidx, "_getServer").start()
         self.assert_login = mock.patch.object(webidx, "_assertLogin").start()
+        self.gen_html = mock.patch.object(webidx, '_genHTML').start()
         self.server = mock.MagicMock()
         self.environ = {
             'koji.options': {
@@ -45,6 +46,12 @@ class TestHostEdit(unittest.TestCase):
         with self.assertRaises(koji.GenericError) as cm:
             webidx.hostedit(self.environ, self.host_id)
         self.assertEqual(str(cm.exception), f'no host with ID: {self.host_id}')
+        self.server.getHost.assert_called_with(int(self.host_id))
+        self.server.editHost.assert_not_called()
+        self.server.listChannels.assert_not_called()
+        self.server.removeHostFromChannel.assert_not_called()
+        self.server.addHostToChannel.assert_not_called()
+        self.server.listChannels.assert_not_called()
 
     def test_hostedit_save_case_valid(self):
         """Test hostedit function valid case (save)."""
@@ -66,6 +73,7 @@ class TestHostEdit(unittest.TestCase):
 
         with self.assertRaises(ServerRedirect):
             webidx.hostedit(self.environ, self.host_id)
+        self.assertEqual(self.environ['koji.redirect'], f'hostinfo?hostID={self.host_id}')
         self.server.getHost.assert_called_with(int(self.host_id))
         self.server.editHost.assert_called_with(int(self.host_id), arches='x86_64', capacity=1.0,
                                                 description='test-desc', comment='test-comment')
@@ -73,7 +81,6 @@ class TestHostEdit(unittest.TestCase):
         self.server.removeHostFromChannel.assert_called_with(
             self.host_info['name'], 'test-channel')
         self.server.addHostToChannel.assert_called_with(self.host_info['name'], 'default')
-        self.assertEqual(self.environ['koji.redirect'], f'hostinfo?hostID={self.host_id}')
 
     def test_hostedit_cancel_case_valid(self):
         """Test hostedit function valid case (cancel)."""
@@ -90,9 +97,38 @@ class TestHostEdit(unittest.TestCase):
 
         with self.assertRaises(ServerRedirect):
             webidx.hostedit(self.environ, self.host_id)
+        self.assertEqual(self.environ['koji.redirect'], f'hostinfo?hostID={self.host_id}')
         self.server.getHost.assert_called_with(int(self.host_id))
         self.server.editHost.assert_not_called()
         self.server.listChannels.assert_not_called()
         self.server.removeHostFromChannel.assert_not_called()
         self.server.addHostToChannel.assert_not_called()
-        self.assertEqual(self.environ['koji.redirect'], f'hostinfo?hostID={self.host_id}')
+        self.server.listChannels.assert_not_called()
+
+    def test_hostedit_another_case(self):
+        """Test hostedit function valid case (another)."""
+        urlencode_data = b"another=True"
+        fs = self.get_fs(urlencode_data)
+
+        def __get_server(env):
+            env['koji.session'] = self.server
+            env['koji.form'] = fs
+            return self.server
+
+        self.get_server.side_effect = __get_server
+        self.server.getHost.return_value = self.host_info
+        self.server.listChannels.side_effect = [
+            [{'id': 1, 'name': 'test-channel-1'},
+             {'id': 2, 'name': 'test-channel-2'},
+             {'id': 3, 'name': 'test-channel-3'}],
+            [{'id': 1, 'name': 'test-channel-1'},
+             {'id': 3, 'name': 'test-channel-3'}]
+        ]
+
+        webidx.hostedit(self.environ, self.host_id)
+        self.server.getHost.assert_called_with(int(self.host_id))
+        self.server.editHost.assert_not_called()
+        self.server.removeHostFromChannel.assert_not_called()
+        self.server.addHostToChannel.assert_not_called()
+        self.server.listChannels.assert_has_calls(([mock.call(),
+                                                    mock.call(hostID=int(self.host_id))]))
diff --git a/tests/test_www/test_hostinfo.py b/tests/test_www/test_hostinfo.py
index ff1bde5..b46e9a5 100644
--- a/tests/test_www/test_hostinfo.py
+++ b/tests/test_www/test_hostinfo.py
@@ -8,7 +8,8 @@ from .loadwebindex import webidx
 class TestHostInfo(unittest.TestCase):
     def setUp(self):
         self.get_server = mock.patch.object(webidx, "_getServer").start()
-
+        self.gen_html = mock.patch.object(webidx, '_genHTML').start()
+        self.server = mock.MagicMock()
         self.environ = {
             'koji.options': {
                 'SiteName': 'test',
@@ -24,34 +25,69 @@ class TestHostInfo(unittest.TestCase):
 
     def test_hostinfo_exception_host_option(self):
         """Test taskinfo function raises exception"""
-        server = mock.MagicMock()
-        server.getHost.return_value = None
-
-        self.get_server.return_value = server
+        self.server.getHost.return_value = None
+        self.get_server.return_value = self.server
 
         with self.assertRaises(koji.GenericError) as cm:
             webidx.hostinfo(self.environ, hostID=self.host_id)
-        self.assertEqual(
-            str(cm.exception), 'No such host ID: %s' % self.host_id)
+        self.assertEqual(str(cm.exception), f'No such host ID: {self.host_id}')
 
     def test_hostinfo_exception_user_option(self):
         """Test taskinfo function raises exception"""
-        server = mock.MagicMock()
-        server.listHosts.return_value = []
-
-        self.get_server.return_value = server
+        self.server.listHosts.return_value = []
+        self.get_server.return_value = self.server
 
         with self.assertRaises(koji.GenericError) as cm:
             webidx.hostinfo(self.environ, userID=self.user_id)
-        self.assertEqual(
-            str(cm.exception), 'No such host for user ID: %s' % self.user_id)
+        self.assertEqual(str(cm.exception), f'No such host for user ID: {self.user_id}')
 
     def test_hostinfo_exception(self):
-        """Test taskinfo function raises exception"""
-        server = mock.MagicMock()
-
-        self.get_server.return_value = server
+        """Test hostinfo function raises exception"""
+        self.get_server.return_value = self.server
 
         with self.assertRaises(koji.GenericError) as cm:
             webidx.hostinfo(self.environ)
         self.assertEqual(str(cm.exception), 'hostID or userID must be provided')
+
+    def test_hostinfo_valid(self):
+        """Test hostinfo function"""
+        self.get_server.return_value = self.server
+        self.server.listHosts.return_value = [
+            {'id': 11, 'name': 'test-host', 'update_ts': 1234567890}]
+        self.server.listChannels.return_value = [
+            {'id': 123, 'name': 'test-channel-123', 'enabled': True},
+            {'id': 456, 'name': 'test-channel-456', 'enabled': False}]
+        self.server.listBuildroots.return_value = [{'id': 111, 'create_event_time': 11111111},
+                                                   {'id': 222, 'create_event_time': 22222222}]
+        webidx.hostinfo(self.environ, userID=self.user_id)
+        self.server.getHost.assert_not_called()
+        self.server.listHosts.assert_called_once_with(userID=int(self.user_id))
+        self.server.listChannels.assert_called_once_with(int(self.host_id))
+        self.server.listBuildroots.assert_called_once_with(
+            hostID=int(self.host_id), state=[0, 1, 2])
+        self.server.getUserPerms.assert_not_called()
+
+    def test_hostinfo_valid_2(self):
+        """Test hostinfo function"""
+        environ = {
+            'koji.options': {
+                'SiteName': 'test',
+                'KojiFilesURL': 'https://server.local/files',
+            },
+            'koji.currentUser': {'id': 5}
+        }
+        self.get_server.return_value = self.server
+        self.server.getHost.return_value = {'id': 11, 'name': 'test-host', 'update_ts': 1234567890}
+        self.server.listChannels.return_value = [
+            {'id': 123, 'name': 'test-channel-123', 'enabled': True},
+            {'id': 456, 'name': 'test-channel-456', 'enabled': False}]
+        self.server.listBuildroots.return_value = [{'id': 111, 'create_event_time': 11111111},
+                                                   {'id': 222, 'create_event_time': 22222222}]
+        self.server.getUserPerms.return_value = ['perm-1', 'perm-2']
+        webidx.hostinfo(environ, hostID=self.host_id)
+        self.server.getHost.assert_called_once_with(int(self.host_id))
+        self.server.listHosts.assert_not_called()
+        self.server.listChannels.assert_called_once_with(int(self.host_id))
+        self.server.listBuildroots.assert_called_once_with(
+            hostID=int(self.host_id), state=[0, 1, 2])
+        self.server.getUserPerms.assert_called_once_with(5)
diff --git a/tests/test_www/test_notificationcreate.py b/tests/test_www/test_notificationcreate.py
index b2cdf62..b3c737d 100644
--- a/tests/test_www/test_notificationcreate.py
+++ b/tests/test_www/test_notificationcreate.py
@@ -11,6 +11,7 @@ from koji.server import ServerRedirect
 class TestNotificationCreate(unittest.TestCase):
     def setUp(self):
         self.get_server = mock.patch.object(webidx, "_getServer").start()
+        self.gen_html = mock.patch.object(webidx, '_genHTML').start()
         self.assert_login = mock.patch.object(webidx, "_assertLogin").start()
         self.server = mock.MagicMock()
         self.buildtag_id = '11'
@@ -59,6 +60,9 @@ class TestNotificationCreate(unittest.TestCase):
         with self.assertRaises(koji.GenericError) as cm:
             webidx.notificationcreate(environ)
         self.assertEqual(str(cm.exception), 'not logged-in')
+        self.server.createNotification.assert_not_called()
+        self.server.listPackagesSimple.assert_not_called()
+        self.server.listTags.assert_not_called()
 
     def test_notificationcreate_add_case_int(self):
         """Test notificationcreate function valid case (add)"""
@@ -75,9 +79,11 @@ class TestNotificationCreate(unittest.TestCase):
 
         with self.assertRaises(ServerRedirect):
             webidx.notificationcreate(self.environ)
+        self.assertEqual(self.environ['koji.redirect'], 'index')
         self.server.createNotification.assert_called_with('1', int(self.pkg_id),
                                                           int(self.buildtag_id), True)
-        self.assertEqual(self.environ['koji.redirect'], 'index')
+        self.server.listPackagesSimple.assert_not_called()
+        self.server.listTags.assert_not_called()
 
     def test_notificationcreate_add_case_all(self):
         """Test notificationcreate function valid case (add)"""
@@ -94,8 +100,10 @@ class TestNotificationCreate(unittest.TestCase):
 
         with self.assertRaises(ServerRedirect):
             webidx.notificationcreate(self.environ)
-        self.server.createNotification.assert_called_with('1', None, None, False)
         self.assertEqual(self.environ['koji.redirect'], 'index')
+        self.server.createNotification.assert_called_with('1', None, None, False)
+        self.server.listPackagesSimple.assert_not_called()
+        self.server.listTags.assert_not_called()
 
     def test_notificationcreate_cancel_case(self):
         """Test notificationcreate function valid case (cancel)."""
@@ -111,5 +119,24 @@ class TestNotificationCreate(unittest.TestCase):
 
         with self.assertRaises(ServerRedirect):
             webidx.notificationcreate(self.environ)
-        self.server.createNotification.assert_not_called()
         self.assertEqual(self.environ['koji.redirect'], 'index')
+        self.server.createNotification.assert_not_called()
+        self.server.listPackagesSimple.assert_not_called()
+        self.server.listTags.assert_not_called()
+
+    def test_notificationcreate_another_case(self):
+        """Test notificationcreate function valid case (another)."""
+        urlencode_data = b"another=True"
+        fs = self.get_fs(urlencode_data)
+
+        def __get_server(env):
+            env['koji.session'] = self.server
+            env['koji.form'] = fs
+            return self.server
+
+        self.get_server.side_effect = __get_server
+
+        webidx.notificationcreate(self.environ)
+        self.server.createNotification.assert_not_called()
+        self.server.listPackagesSimple.assert_called_once_with(queryOpts={'order': 'package_name'})
+        self.server.listTags.assert_called_once_with(queryOpts={'order': 'name'})
diff --git a/tests/test_www/test_notificationedit.py b/tests/test_www/test_notificationedit.py
index 88d678f..b89531d 100644
--- a/tests/test_www/test_notificationedit.py
+++ b/tests/test_www/test_notificationedit.py
@@ -11,6 +11,7 @@ from koji.server import ServerRedirect
 class TestNotificationEdit(unittest.TestCase):
     def setUp(self):
         self.get_server = mock.patch.object(webidx, "_getServer").start()
+        self.gen_html = mock.patch.object(webidx, '_genHTML').start()
         self.assert_login = mock.patch.object(webidx, "_assertLogin").start()
         self.server = mock.MagicMock()
         self.environ = {
@@ -46,6 +47,10 @@ class TestNotificationEdit(unittest.TestCase):
         with self.assertRaises(koji.GenericError) as cm:
             webidx.notificationedit(self.environ, self.notification_id)
         self.assertEqual(str(cm.exception), f'no notification with ID: {self.notification_id}')
+        self.server.getBuildNotification.assert_called_once_with(int(self.notification_id))
+        self.server.updateNotification.assert_not_called()
+        self.server.listPackagesSimple.assert_not_called()
+        self.server.listTags.assert_not_called()
 
     def test_notificationedit_save_case_int(self):
         """Test notificationedit function valid case (save)."""
@@ -63,9 +68,12 @@ class TestNotificationEdit(unittest.TestCase):
 
         with self.assertRaises(ServerRedirect):
             webidx.notificationedit(self.environ, self.notification_id)
-        self.server.updateNotification.assert_called_with(self.notification_id, self.pkg_id,
-                                                          self.tag_id, True)
         self.assertEqual(self.environ['koji.redirect'], 'index')
+        self.server.getBuildNotification.assert_called_once_with(int(self.notification_id))
+        self.server.updateNotification.assert_called_once_with(self.notification_id, self.pkg_id,
+                                                               self.tag_id, True)
+        self.server.listPackagesSimple.assert_not_called()
+        self.server.listTags.assert_not_called()
 
     def test_notificationedit_save_case_all(self):
         """Test notificationedit function valid case (all)."""
@@ -83,8 +91,12 @@ class TestNotificationEdit(unittest.TestCase):
 
         with self.assertRaises(ServerRedirect):
             webidx.notificationedit(self.environ, self.notification_id)
-        self.server.updateNotification.assert_called_with(self.notification_id, None, None, False)
         self.assertEqual(self.environ['koji.redirect'], 'index')
+        self.server.getBuildNotification.assert_called_once_with(int(self.notification_id))
+        self.server.updateNotification.assert_called_once_with(
+            self.notification_id, None, None, False)
+        self.server.listPackagesSimple.assert_not_called()
+        self.server.listTags.assert_not_called()
 
     def test_notificationedit_cancel_case(self):
         """Test notificationedit function valid case (cancel)."""
@@ -101,5 +113,27 @@ class TestNotificationEdit(unittest.TestCase):
 
         with self.assertRaises(ServerRedirect):
             webidx.notificationedit(self.environ, self.notification_id)
-        self.server.updateNotification.assert_not_called()
         self.assertEqual(self.environ['koji.redirect'], 'index')
+        self.server.getBuildNotification.assert_called_once_with(int(self.notification_id))
+        self.server.updateNotification.assert_not_called()
+        self.server.listPackagesSimple.assert_not_called()
+        self.server.listTags.assert_not_called()
+
+    def test_notificationedit_another_case(self):
+        """Test notificationedit function valid case (another)."""
+        urlencode_data = b"another=True"
+        fs = self.get_fs(urlencode_data)
+
+        def __get_server(env):
+            env['koji.session'] = self.server
+            env['koji.form'] = fs
+            return self.server
+
+        self.server.getBuildNotification.return_value = {'id': self.notification_id}
+        self.get_server.side_effect = __get_server
+
+        webidx.notificationedit(self.environ, self.notification_id)
+        self.server.getBuildNotification.assert_called_once_with(int(self.notification_id))
+        self.server.updateNotification.assert_not_called()
+        self.server.listPackagesSimple.assert_called_once_with(queryOpts={'order': 'package_name'})
+        self.server.listTags.assert_called_once_with(queryOpts={'order': 'name'})
diff --git a/tests/test_www/test_packageinfo.py b/tests/test_www/test_packageinfo.py
index d59e13b..e9194bf 100644
--- a/tests/test_www/test_packageinfo.py
+++ b/tests/test_www/test_packageinfo.py
@@ -8,6 +8,8 @@ from .loadwebindex import webidx
 class TestPackageInfo(unittest.TestCase):
     def setUp(self):
         self.get_server = mock.patch.object(webidx, "_getServer").start()
+        self.gen_html = mock.patch.object(webidx, '_genHTML').start()
+        self.server = mock.MagicMock()
 
         self.environ = {
             'koji.options': {
@@ -23,12 +25,19 @@ class TestPackageInfo(unittest.TestCase):
 
     def test_packageinfo_exception(self):
         """Test taskinfo function raises exception"""
-        server = mock.MagicMock()
-        server.getPackage.return_value = None
+        self.server.getPackage.return_value = None
 
-        self.get_server.return_value = server
+        self.get_server.return_value = self.server
 
         with self.assertRaises(koji.GenericError) as cm:
             webidx.packageinfo(self.environ, self.package_id)
-        self.assertEqual(
-            str(cm.exception), 'No such package ID: %s' % self.package_id)
+        self.assertEqual(str(cm.exception), f'No such package ID: {self.package_id}')
+        self.server.getPackage.assert_called_once_with(int(self.package_id))
+
+    def test_packageinfo_valid(self):
+        """Test taskinfo function"""
+        self.server.getPackage.return_value = {'id': self.package_id, 'name': 'test-pkg'}
+
+        self.get_server.return_value = self.server
+        webidx.packageinfo(self.environ, self.package_id)
+        self.server.getPackage.assert_called_once_with(int(self.package_id))
diff --git a/tests/test_www/test_repoinfo.py b/tests/test_www/test_repoinfo.py
new file mode 100644
index 0000000..5d45003
--- /dev/null
+++ b/tests/test_www/test_repoinfo.py
@@ -0,0 +1,46 @@
+import mock
+import unittest
+
+from .loadwebindex import webidx
+
+
+class TestRepoInfo(unittest.TestCase):
+    def setUp(self):
+        self.get_server = mock.patch.object(webidx, "_getServer").start()
+        self.gen_html = mock.patch.object(webidx, '_genHTML').start()
+        self.server = mock.MagicMock()
+        self.environ = {
+            'koji.options': {
+                'SiteName': 'test',
+                'KojiFilesURL': 'https://server.local/files',
+            },
+            'koji.currentUser': None
+        }
+        self.repo_id = '5'
+
+    def tearDown(self):
+        mock.patch.stopall()
+
+    def test_repoinfo_dist(self):
+        """Test repoinfo function - dist repo"""
+        self.get_server.return_value = self.server
+
+        self.server.repoInfo.return_value = {'dist': True, 'id': int(self.repo_id),
+                                             'tag_name': 'test-tag'}
+        self.server.listBuildroots.return_value = []
+
+        webidx.repoinfo(self.environ, self.repo_id)
+        self.server.repoInfo.assert_called_once_with(int(self.repo_id), strict=False)
+        self.server.listBuildroots.assert_called_once_with(repoID=int(self.repo_id))
+
+    def test_repoinfo_not_dist(self):
+        """Test repoinfo function - not dist repo"""
+        self.get_server.return_value = self.server
+
+        self.server.repoInfo.return_value = {'dist': False, 'id': int(self.repo_id),
+                                             'tag_name': 'test-tag'}
+        self.server.listBuildroots.return_value = [{'id': 1, 'repo_id': int(self.repo_id)}]
+
+        webidx.repoinfo(self.environ, self.repo_id)
+        self.server.repoInfo.assert_called_once_with(int(self.repo_id), strict=False)
+        self.server.listBuildroots.assert_called_once_with(repoID=int(self.repo_id))
diff --git a/tests/test_www/test_rpminfo.py b/tests/test_www/test_rpminfo.py
index dc4495e..3007627 100644
--- a/tests/test_www/test_rpminfo.py
+++ b/tests/test_www/test_rpminfo.py
@@ -8,7 +8,8 @@ from .loadwebindex import webidx
 class TestRpmInfo(unittest.TestCase):
     def setUp(self):
         self.get_server = mock.patch.object(webidx, "_getServer").start()
-
+        self.gen_html = mock.patch.object(webidx, '_genHTML').start()
+        self.server = mock.MagicMock()
         self.environ = {
             'koji.options': {
                 'SiteName': 'test',
@@ -16,19 +17,48 @@ class TestRpmInfo(unittest.TestCase):
             },
             'koji.currentUser': None
         }
-        self.build_id = '1'
+        self.rpm_id = '5'
 
     def tearDown(self):
         mock.patch.stopall()
 
     def test_rpminfo_exception(self):
         """Test taskinfo function raises exception"""
-        server = mock.MagicMock()
-        server.getRPM.side_effect = koji.GenericError
+        self.server.getRPM.side_effect = koji.GenericError
 
-        self.get_server.return_value = server
+        self.get_server.return_value = self.server
 
         with self.assertRaises(koji.GenericError) as cm:
-            webidx.rpminfo(self.environ, self.build_id)
-        self.assertEqual(
-            str(cm.exception), 'No such RPM ID: %s' % self.build_id)
+            webidx.rpminfo(self.environ, self.rpm_id)
+        self.assertEqual(str(cm.exception), f'No such RPM ID: {self.rpm_id}')
+        self.server.getRPM.assert_called_once_with(int(self.rpm_id), strict=True)
+        self.server.getBuild.assert_not_called()
+        self.server.getBuildroot.assert_not_called()
+        self.server.getRPMDeps.assert_not_called()
+        self.server.getRPMHeaders.assert_not_called()
+
+    def test_rpminfo_valid(self):
+        """Test taskinfo function"""
+        rpm_headers = ['summary', 'description', 'license', 'disturl', 'vcs']
+        self.server.getRPM.return_value = {'id': int(self.rpm_id), 'build_id': 1,
+                                           'buildroot_id': 444, 'name': 'test-name',
+                                           'version': 123, 'release': 5, 'epoch': 2,
+                                           'arch': 'test-arch', 'suffix': 'suf',
+                                           'external_repo_id': 0}
+        self.server.getBuild.return_value = {'id': 1, 'name': 'test-build'}
+        self.server.getBuildroot.return_value = {'id': 444}
+        self.server.getRPMDeps.return_value = [{'type': 'requires'}, {'type': 'suggests'}]
+        self.server.getRPMHeaders.return_value = {'summary': 'test_summary',
+                                                  'description': 'test-description',
+                                                  'license': 'test-license',
+                                                  'disturl': 'test-disturl',
+                                                  'vcs': 'test-vcs'}
+
+        self.get_server.return_value = self.server
+
+        webidx.rpminfo(self.environ, self.rpm_id)
+        self.server.getRPM.assert_called_once_with(int(self.rpm_id), strict=True)
+        self.server.getBuild.assert_called_once_with(1)
+        self.server.getBuildroot.assert_called_once_with(444)
+        self.server.getRPMDeps.assert_called_once_with(int(self.rpm_id))
+        self.server.getRPMHeaders.assert_called_once_with(int(self.rpm_id), headers=rpm_headers)
diff --git a/tests/test_www/test_tagcreate.py b/tests/test_www/test_tagcreate.py
index 842dcd1..34fd537 100644
--- a/tests/test_www/test_tagcreate.py
+++ b/tests/test_www/test_tagcreate.py
@@ -10,6 +10,7 @@ from koji.server import ServerRedirect
 class TestTagCreate(unittest.TestCase):
     def setUp(self):
         self.get_server = mock.patch.object(webidx, "_getServer").start()
+        self.gen_html = mock.patch.object(webidx, '_genHTML').start()
         self.assert_login = mock.patch.object(webidx, "_assertLogin").start()
         self.server = mock.MagicMock()
         self.environ = {
@@ -50,10 +51,12 @@ class TestTagCreate(unittest.TestCase):
 
         with self.assertRaises(ServerRedirect):
             webidx.tagcreate(self.environ)
+        self.assertEqual(self.environ['koji.redirect'], 'taginfo?tagID=1')
+        self.server.mavenEnabled.assert_called_once_with()
         self.server.createTag.assert_called_with('testname', arches='x86_64', locked=True,
                                                  perm=1, maven_support=True,
                                                  maven_include_all=True)
-        self.assertEqual(self.environ['koji.redirect'], 'taginfo?tagID=1')
+        self.server.getAllPerms.assert_not_called()
 
     def test_tagcreate_cancel_case_valid(self):
         """Test tagcreate function valid cases (cancel)."""
@@ -69,5 +72,28 @@ class TestTagCreate(unittest.TestCase):
 
         with self.assertRaises(ServerRedirect):
             webidx.tagcreate(self.environ)
-        self.server.createTag.assert_not_called()
         self.assertEqual(self.environ['koji.redirect'], 'tags')
+        self.server.mavenEnabled.assert_called_once_with()
+        self.server.createTag.assert_not_called()
+        self.server.getAllPerms.assert_not_called()
+
+    def test_tagedit_another_case_valid(self):
+        """Test tagedit function valid case (another)."""
+        urlencode_data = b"another=True"
+        fs = self.get_fs(urlencode_data)
+
+        def __get_server(env):
+            env['koji.session'] = self.server
+            env['koji.form'] = fs
+            return self.server
+
+        self.get_server.side_effect = __get_server
+        self.server.mavenEnabled.return_value = True
+        self.server.getAllPerms.return_value = [{'id': 1, 'name': 'test-perm-1'},
+                                                {'id': 2, 'name': 'test-perm-2'},
+                                                {'id': 3, 'name': 'test-perm-3'}]
+
+        webidx.tagcreate(self.environ)
+        self.server.mavenEnabled.assert_called_once_with()
+        self.server.createTag.assert_not_called()
+        self.server.getAllPerms.assert_called_once_with()
diff --git a/tests/test_www/test_tagedit.py b/tests/test_www/test_tagedit.py
index 774eeea..b9418d9 100644
--- a/tests/test_www/test_tagedit.py
+++ b/tests/test_www/test_tagedit.py
@@ -11,6 +11,7 @@ from koji.server import ServerRedirect
 class TestTagEdit(unittest.TestCase):
     def setUp(self):
         self.get_server = mock.patch.object(webidx, "_getServer").start()
+        self.gen_html = mock.patch.object(webidx, '_genHTML').start()
         self.assert_login = mock.patch.object(webidx, "_assertLogin").start()
         self.server = mock.MagicMock()
         self.environ = {
@@ -44,6 +45,10 @@ class TestTagEdit(unittest.TestCase):
         with self.assertRaises(koji.GenericError) as cm:
             webidx.tagedit(self.environ, self.tag_id)
         self.assertEqual(str(cm.exception), f'no tag with ID: {self.tag_id}')
+        self.server.mavenEnabled.assert_called_once_with()
+        self.server.getTag.assert_called_once_with(int(self.tag_id))
+        self.server.getAllPerms.assert_not_called()
+        self.server.editTag2.assert_not_called()
 
     def test_tagedit_add_case_valid(self):
         """Test tagedit function valid case (save)."""
@@ -62,10 +67,13 @@ class TestTagEdit(unittest.TestCase):
 
         with self.assertRaises(ServerRedirect):
             webidx.tagedit(self.environ, self.tag_id)
+        self.assertEqual(self.environ['koji.redirect'], f'taginfo?tagID={self.tag_id}')
+        self.server.mavenEnabled.assert_called_once_with()
+        self.server.getTag.assert_called_once_with(int(self.tag_id))
+        self.server.getAllPerms.assert_not_called()
         self.server.editTag2.assert_called_with(1, arches='x86_64', locked=True, perm=1,
                                                 maven_support=True, maven_include_all=True,
                                                 name='testname')
-        self.assertEqual(self.environ['koji.redirect'], f'taginfo?tagID={self.tag_id}')
 
     def test_tagedit_cancel_case_valid(self):
         """Test tagedit function valid case (cancel)."""
@@ -84,3 +92,30 @@ class TestTagEdit(unittest.TestCase):
             webidx.tagedit(self.environ, self.tag_id)
         self.server.editTag2.assert_not_called()
         self.assertEqual(self.environ['koji.redirect'], f'taginfo?tagID={self.tag_id}')
+        self.server.mavenEnabled.assert_called_once_with()
+        self.server.getTag.assert_called_once_with(int(self.tag_id))
+        self.server.getAllPerms.assert_not_called()
+        self.server.editTag2.assert_not_called()
+
+    def test_tagedit_another_case_valid(self):
+        """Test tagedit function valid case (another)."""
+        urlencode_data = b"another=True"
+        fs = self.get_fs(urlencode_data)
+
+        def __get_server(env):
+            env['koji.session'] = self.server
+            env['koji.form'] = fs
+            return self.server
+
+        self.get_server.side_effect = __get_server
+        self.server.mavenEnabled.return_value = True
+        self.server.getTag.return_value = {'id': int(self.tag_id)}
+        self.server.getAllPerms.return_value = [{'id': 1, 'name': 'test-perm-1'},
+                                                {'id': 2, 'name': 'test-perm-2'},
+                                                {'id': 3, 'name': 'test-perm-3'}]
+
+        webidx.tagedit(self.environ, self.tag_id)
+        self.server.mavenEnabled.assert_called_once_with()
+        self.server.getTag.assert_called_once_with(int(self.tag_id))
+        self.server.getAllPerms.assert_called_once_with()
+        self.server.editTag2.assert_not_called()
diff --git a/tests/test_www/test_tagparent.py b/tests/test_www/test_tagparent.py
new file mode 100644
index 0000000..f89a307
--- /dev/null
+++ b/tests/test_www/test_tagparent.py
@@ -0,0 +1,222 @@
+from __future__ import absolute_import
+import unittest
+import mock
+import cgi
+
+import koji
+from io import BytesIO
+from koji.server import ServerRedirect
+from .loadwebindex import webidx
+
+
+class TestActiveSessionDelete(unittest.TestCase):
+    def setUp(self):
+        self.get_server = mock.patch.object(webidx, "_getServer").start()
+        self.assert_login = mock.patch.object(webidx, "_assertLogin").start()
+        self.gen_html = mock.patch.object(webidx, '_genHTML').start()
+        self.server = mock.MagicMock()
+        self.environ = {
+            'koji.options': {
+                'SiteName': 'test',
+                'KojiFilesURL': 'https://server.local/files',
+            },
+            'koji.currentUser': None,
+
+        }
+
+        def __get_server(env):
+            env['koji.session'] = self.server
+            return self.server
+
+        self.get_server.side_effect = __get_server
+
+    def tearDown(self):
+        mock.patch.stopall()
+
+    def get_fs(self, urlencode_data):
+        urlencode_environ = {
+            'CONTENT_LENGTH': str(len(urlencode_data)),
+            'CONTENT_TYPE': 'application/x-www-form-urlencoded',
+            'QUERY_STRING': '',
+            'REQUEST_METHOD': 'POST',
+        }
+        data = BytesIO(urlencode_data)
+        data.seek(0)
+        return cgi.FieldStorage(fp=data, environ=urlencode_environ)
+
+    def test_tagparent_remove(self):
+        """Test tagparent function with remove action."""
+        tag_id = 456
+        parent_id = 123
+        action = 'remove'
+        data = [{'parent_id': parent_id}]
+        self.get_server.return_value = self.server
+        self.server.getTag.side_effect = [{'id': tag_id}, {'id': parent_id}]
+        self.server.getInheritanceData.return_value = data
+        self.server.setInheritanceData.return_value = None
+
+        with self.assertRaises(ServerRedirect):
+            webidx.tagparent(self.environ, tag_id, parent_id, action)
+        self.assertEqual(self.environ['koji.redirect'], f'taginfo?tagID={tag_id}')
+        self.server.getTag.assert_has_calls([mock.call(tag_id, strict=True),
+                                             mock.call(parent_id, strict=True)])
+        self.server.getInheritanceData.assert_called_once_with(tag_id)
+        self.server.setInheritanceData.assert_called_once_with(tag_id, data)
+
+    def test_tagparent_remove_tag_not_parent(self):
+        """Test tagparent function with remove action."""
+        tag_id = 456
+        parent_id = 123
+        action = 'remove'
+        data = [{'parent_id': 111}]
+        self.get_server.return_value = self.server
+        self.server.getTag.side_effect = [{'id': tag_id}, {'id': parent_id}]
+        self.server.getInheritanceData.return_value = data
+
+        with self.assertRaises(koji.GenericError) as cm:
+            webidx.tagparent(self.environ, tag_id, parent_id, action)
+        self.assertEqual(str(cm.exception), f'tag {parent_id} is not a parent of tag {tag_id}')
+        self.server.getTag.assert_has_calls([mock.call(tag_id, strict=True),
+                                             mock.call(parent_id, strict=True)])
+        self.server.getInheritanceData.assert_called_once_with(tag_id)
+        self.server.setInheritanceData.assert_not_called()
+
+    def test_tagparent_wrong_action(self):
+        """Test tagparent function with remove action."""
+        tag_id = 456
+        parent_id = 123
+        action = 'action'
+        self.get_server.return_value = self.server
+        self.server.getTag.side_effect = [{'id': tag_id}, {'id': parent_id}]
+
+        with self.assertRaises(koji.GenericError) as cm:
+            webidx.tagparent(self.environ, tag_id, parent_id, action)
+        self.assertEqual(str(cm.exception), f'unknown action: {action}')
+        self.server.getTag.assert_has_calls([mock.call(tag_id, strict=True),
+                                             mock.call(parent_id, strict=True)])
+        self.server.getInheritanceData.assert_not_called()
+        self.server.setInheritanceData.assert_not_called()
+
+    def test_tagparent_action_add(self):
+        """Test tagparent function with add action."""
+        tag_id = 456
+        parent_id = 123
+        action = 'add'
+        data = [{'parent_id': 111}]
+        urlencode_data = (b"add=true&priority=10&maxdepth=5&pkg_filter=pkg_filter&"
+                          b"intransitive=true&noconfig=false")
+        fs = self.get_fs(urlencode_data)
+
+        def __get_server(env):
+            env['koji.session'] = self.server
+            env['koji.form'] = fs
+            return self.server
+
+        self.get_server.side_effect = __get_server
+        self.server.getTag.side_effect = [{'id': tag_id}, {'id': parent_id}]
+        self.server.getInheritanceData.return_value = data
+        self.server.setInheritanceData.return_value = None
+
+        with self.assertRaises(ServerRedirect):
+            webidx.tagparent(self.environ, tag_id, parent_id, action)
+        self.assertEqual(self.environ['koji.redirect'], f'taginfo?tagID={tag_id}')
+        self.server.getTag.assert_has_calls([mock.call(tag_id, strict=True),
+                                             mock.call(parent_id, strict=True)])
+        self.server.getInheritanceData.assert_called_once_with(tag_id)
+        self.server.setInheritanceData.assert_called_once_with(tag_id, data)
+
+    def test_tagparent_action_add_form_cancel(self):
+        """Test tagparent function with cancel action."""
+        tag_id = 456
+        parent_id = 123
+        action = 'add'
+        urlencode_data = b"cancel=true"
+        fs = self.get_fs(urlencode_data)
+
+        def __get_server(env):
+            env['koji.session'] = self.server
+            env['koji.form'] = fs
+            return self.server
+
+        self.get_server.side_effect = __get_server
+        self.server.getTag.side_effect = [{'id': tag_id}, {'id': parent_id}]
+
+        with self.assertRaises(ServerRedirect):
+            webidx.tagparent(self.environ, tag_id, parent_id, action)
+        self.assertEqual(self.environ['koji.redirect'], f'taginfo?tagID={tag_id}')
+        self.server.getTag.assert_has_calls([mock.call(tag_id, strict=True),
+                                             mock.call(parent_id, strict=True)])
+
+    def test_tagparent_action_edit_inheritance_0(self):
+        """Test tagparent function with edit action."""
+        tag_id = 456
+        parent_id = 123
+        action = 'add'
+        data = [{'parent_id': 111, 'priority': 1}]
+
+        urlencode_data = b"edit=true"
+        fs = self.get_fs(urlencode_data)
+
+        def __get_server(env):
+            env['koji.session'] = self.server
+            env['koji.form'] = fs
+            return self.server
+
+        self.get_server.side_effect = __get_server
+        self.server.getTag.side_effect = [{'id': tag_id}, {'id': parent_id}]
+        self.server.getInheritanceData.return_value = data
+        webidx.tagparent(self.environ, tag_id, parent_id, action)
+        self.server.getTag.assert_has_calls([mock.call(tag_id, strict=True),
+                                             mock.call(parent_id, strict=True)])
+        self.server.getInheritanceData.assert_called_once_with(tag_id)
+
+    def test_tagparent_action_edit_inheritance_1(self):
+        """Test tagparent function with edit action."""
+        tag_id = 456
+        parent_id = 123
+        action = 'add'
+        data = [{'parent_id': 123, 'priority': 1}]
+
+        urlencode_data = b"edit=true"
+        fs = self.get_fs(urlencode_data)
+
+        def __get_server(env):
+            env['koji.session'] = self.server
+            env['koji.form'] = fs
+            return self.server
+
+        self.get_server.side_effect = __get_server
+        self.server.getTag.side_effect = [{'id': tag_id}, {'id': parent_id}]
+        self.server.getInheritanceData.return_value = data
+        webidx.tagparent(self.environ, tag_id, parent_id, action)
+        self.server.getTag.assert_has_calls([mock.call(tag_id, strict=True),
+                                             mock.call(parent_id, strict=True)])
+        self.server.getInheritanceData.assert_called_once_with(tag_id)
+
+    def test_tagparent_action_edit_inheritance_more(self):
+        """Test tagparent function with edit action."""
+        tag_id = 456
+        parent_id = 123
+        action = 'add'
+        data = [{'parent_id': 123, 'priority': 1},
+                {'parent_id': 123, 'priority': 2},
+                {'parent_id': 123, 'priority': 3}]
+
+        urlencode_data = b"edit=true"
+        fs = self.get_fs(urlencode_data)
+
+        def __get_server(env):
+            env['koji.session'] = self.server
+            env['koji.form'] = fs
+            return self.server
+
+        self.get_server.side_effect = __get_server
+        self.server.getTag.side_effect = [{'id': tag_id}, {'id': parent_id}]
+        self.server.getInheritanceData.return_value = data
+        with self.assertRaises(koji.GenericError) as cm:
+            webidx.tagparent(self.environ, tag_id, parent_id, action)
+        self.assertEqual(str(cm.exception),
+                         f'tag {tag_id} has tag {parent_id} listed as a parent more than once')
+        self.server.getTag.assert_has_calls([mock.call(tag_id, strict=True),
+                                             mock.call(parent_id, strict=True)])
+        self.server.getInheritanceData.assert_called_once_with(tag_id)
diff --git a/tests/test_www/test_taskinfo.py b/tests/test_www/test_taskinfo.py
index 9b06ed8..e18dad2 100644
--- a/tests/test_www/test_taskinfo.py
+++ b/tests/test_www/test_taskinfo.py
@@ -14,6 +14,7 @@ class TestTaskInfo(unittest.TestCase):
         self.get_server = mock.patch.object(webidx, "_getServer").start()
         self.task_label = mock.patch('koji.taskLabel').start()
         self.gen_html = mock.patch.object(webidx, '_genHTML').start()
+        self.server = mock.MagicMock()
 
         self.environ = {
             'koji.options': {
@@ -50,31 +51,30 @@ class TestTaskInfo(unittest.TestCase):
         self.parent_task['parent'] = None
 
     def __get_server(self, task=None):
-        server = mock.MagicMock()
-        server.getTaskInfo.side_effect = [
+        self.server.getTaskInfo.side_effect = [
             task if task else self.task,
             self.parent_task
         ]
 
-        server.getTaskDescendents.return_value = {
+        self.server.getTaskDescendents.return_value = {
             str(self.task_id): []
         }
 
-        server.getChannel.return_value = {
+        self.server.getChannel.return_value = {
             'name': 'TestChannel'
         }
 
-        server.getHost.return_value = {
+        self.server.getHost.return_value = {
             'name': 'TestHost'
         }
 
-        server.getUser.return_value = {
+        self.server.getUser.return_value = {
             'name': 'Tester'
         }
 
-        server.getUserPerms.return_value = ['admin']
+        self.server.getUserPerms.return_value = ['admin']
 
-        server.listBuilds.return_value = [
+        self.server.listBuilds.return_value = [
             {
                 "package_name": "less",
                 "extra": "None",
@@ -95,14 +95,14 @@ class TestTaskInfo(unittest.TestCase):
             },
         ]
 
-        server.listBuildroots.return_value = {
+        self.server.listBuildroots.return_value = {
             "arch": "x86_64",
             "host_name": "builder",
             "task_id": self.task_id,
             "id": 1
         }
 
-        server.getTag.return_value = {
+        self.server.getTag.return_value = {
             "id": 2,
             "arches": "x86_64",
             "name": "fedora-build",
@@ -110,7 +110,7 @@ class TestTaskInfo(unittest.TestCase):
             "perm": None
         }
 
-        server.getTaskResult.return_value = {
+        self.server.getTaskResult.return_value = {
             "brootid": 1,
             "srpms": [
                 "tasks/8/8/less-487-3.fc26.src.rpm"
@@ -129,7 +129,7 @@ class TestTaskInfo(unittest.TestCase):
             ]
         }
 
-        server.listTaskOutput.return_value = {
+        self.server.listTaskOutput.return_value = {
             "root.log": ["DEFAULT"],
             "hw_info.log": ["DEFAULT"],
             "less-debuginfo-487-3.fc26.x86_64.rpm": ["DEFAULT"],
@@ -140,22 +140,20 @@ class TestTaskInfo(unittest.TestCase):
             "less-487-3.fc26.x86_64.rpm": ["DEFAULT"],
             "installed_pkgs.log": ["DEFAULT"]
         }
-        return server
+        return self.server
 
     def tearDown(self):
         mock.patch.stopall()
 
     def test_taskinfo_exception(self):
         """Test taskinfo function raises exception"""
-        server = mock.MagicMock()
-        server.getTaskInfo.return_value = None
+        self.server.getTaskInfo.return_value = None
 
-        self.get_server.return_value = server
+        self.get_server.return_value = self.server
 
         with self.assertRaises(koji.GenericError) as cm:
             webidx.taskinfo(self.environ, self.task_id)
-        self.assertEqual(
-            str(cm.exception), 'No such task ID: %s' % self.task_id)
+        self.assertEqual(str(cm.exception), f'No such task ID: {self.task_id}')
 
     def test_taskinfo_getTaskResult_exception(self):
         """Test taskinfo function with exception raised from getTaskResult"""
@@ -322,7 +320,7 @@ class TestTaskInfo(unittest.TestCase):
         self.get_server.return_value = server
         server.getBuildTarget.return_value = 'tgt'
         webidx.taskinfo(self.environ, self.task_id)
-        self.assertEqual(self.environ['koji.values']['params'], 
+        self.assertEqual(self.environ['koji.values']['params'],
                          {'build': '', 'build_target': 'tgt', 'spec_url': '', 'task': {'id': 999}})
 
         # case 7. restartVerify
diff --git a/tests/test_www/test_userinfo.py b/tests/test_www/test_userinfo.py
new file mode 100644
index 0000000..5d95133
--- /dev/null
+++ b/tests/test_www/test_userinfo.py
@@ -0,0 +1,35 @@
+import mock
+import unittest
+
+from .loadwebindex import webidx
+
+
+class TestUserInfo(unittest.TestCase):
+    def setUp(self):
+        self.get_server = mock.patch.object(webidx, "_getServer").start()
+        self.gen_html = mock.patch.object(webidx, '_genHTML').start()
+        self.server = mock.MagicMock()
+        self.environ = {
+            'koji.options': {
+                'SiteName': 'test',
+                'KojiFilesURL': 'https://server.local/files',
+            },
+            'koji.currentUser': None
+        }
+        self.user_id = '5'
+
+    def tearDown(self):
+        mock.patch.stopall()
+
+    def test_userinfo(self):
+        """Test userinfo function"""
+        self.get_server.return_value = self.server
+
+        self.server.getUser.return_value = {'id': int(self.user_id), 'name': 'test-user'}
+        self.server.listTasks.return_value = 123
+        self.server.countAndFilterResults.return_value = [123, 'result']
+
+        webidx.userinfo(self.environ, self.user_id)
+        self.server.getUser.assert_called_once_with(int(self.user_id), strict=True)
+        self.server.listTasks.assert_called_once_with(
+            opts={'owner': int(self.user_id), 'parent': None}, queryOpts={'countOnly': True})

