From 375091db332b56f74e61a718aad436bc437dcc8d Mon Sep 17 00:00:00 2001
From: Jana Cupova <root@localhost>
Date: Jun 01 2022 06:20:40 +0000
Subject: Increase www unit tests


---

diff --git a/tests/test_www/test_buildrootinfo.py b/tests/test_www/test_buildrootinfo.py
new file mode 100644
index 0000000..47634a2
--- /dev/null
+++ b/tests/test_www/test_buildrootinfo.py
@@ -0,0 +1,32 @@
+import unittest
+import koji
+
+import mock
+from .loadwebindex import webidx
+
+
+class TestBuildrootInfo(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
+        }
+        self.buildroot_id = '1'
+
+    def tearDown(self):
+        mock.patch.stopall()
+
+    def test_buildrootinfo_exception(self):
+        """Test buildrootinfo function raises exception when buildroot ID is unknown."""
+        self.get_server.return_value = self.server
+        self.server.getBuildroot.return_value = None
+
+        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}')
diff --git a/tests/test_www/test_buildtargetcreate.py b/tests/test_www/test_buildtargetcreate.py
new file mode 100644
index 0000000..d3502e0
--- /dev/null
+++ b/tests/test_www/test_buildtargetcreate.py
@@ -0,0 +1,95 @@
+import mock
+import unittest
+import cgi
+
+import koji
+from io import BytesIO
+from .loadwebindex import webidx
+from koji.server import ServerRedirect
+
+
+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.server = mock.MagicMock()
+        self.buildtarget_id = '1'
+        self.buildtag_id = '11'
+        self.desttag_id = '99'
+        self.environ = {
+            'koji.options': {
+                'SiteName': 'test',
+                'KojiFilesURL': 'https://server.local/files',
+            },
+            'koji.currentUser': None,
+        }
+
+    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_buildtargetcreate_add_case_exception(self):
+        """Test buildtargetcreate function raises exception when buildtarget is not created."""
+        urlencode_data = b"add=True&name=testname&buildTag=11&destTag=99"
+        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.createBuildTarget.return_value = 1
+        self.server.getBuildTarget.return_value = None
+
+        with self.assertRaises(koji.GenericError) as cm:
+            webidx.buildtargetcreate(self.environ)
+        self.assertEqual(str(cm.exception), 'error creating build target "testname"')
+
+    def test_buildtargetcreate_add_case_valid(self):
+        """Test buildtargetcreate function valid case (add)."""
+        urlencode_data = b"add=True&name=testname&buildTag=11&destTag=99"
+        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.createBuildTarget.return_value = 1
+        self.server.getBuildTarget.return_value = {'id': 1}
+
+        with self.assertRaises(ServerRedirect):
+            webidx.buildtargetcreate(self.environ)
+        self.server.createBuildTarget.assert_called_with('testname', int(self.buildtag_id),
+                                                         int(self.desttag_id))
+        self.assertEqual(self.environ['koji.redirect'], 'buildtargetinfo?targetID=1')
+
+    def test_buildtargetcreate_cancel_case(self):
+        """Test buildtargetcreate function valid case (cancel)."""
+        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.getBuildTarget.return_value = {'id': self.buildtarget_id}
+
+        with self.assertRaises(ServerRedirect):
+            webidx.buildtargetcreate(self.environ)
+        self.server.createBuildTarget.assert_not_called()
+        self.assertEqual(self.environ['koji.redirect'], 'buildtargets')
diff --git a/tests/test_www/test_buildtargetedit.py b/tests/test_www/test_buildtargetedit.py
index 5d6d315..b8087f0 100644
--- a/tests/test_www/test_buildtargetedit.py
+++ b/tests/test_www/test_buildtargetedit.py
@@ -5,6 +5,7 @@ import cgi
 import koji
 from io import BytesIO
 from .loadwebindex import webidx
+from koji.server import ServerRedirect
 
 
 class TestBuildTargetEdit(unittest.TestCase):
@@ -23,7 +24,10 @@ class TestBuildTargetEdit(unittest.TestCase):
             'koji.currentUser': None,
         }
 
-        urlencode_data = b"save=True&name=testname&buildTag=11&destTag=99"
+    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',
@@ -32,43 +36,104 @@ class TestBuildTargetEdit(unittest.TestCase):
         }
         data = BytesIO(urlencode_data)
         data.seek(0)
-        self.fs = cgi.FieldStorage(fp=data, environ=urlencode_environ)
+        return cgi.FieldStorage(fp=data, environ=urlencode_environ)
+
+    def test_buildtargetedit_exception(self):
+        """Test buildtargetedit function raises exception when build target not exists."""
+        urlencode_data = b"save=True&name=testname&buildTag=11&destTag=99"
+        fs = self.get_fs(urlencode_data)
 
         def __get_server(env):
             env['koji.session'] = self.server
-            env['koji.form'] = self.fs
+            env['koji.form'] = fs
             return self.server
 
         self.get_server.side_effect = __get_server
-
-    def tearDown(self):
-        mock.patch.stopall()
-
-    def test_buildtargetedit_exception(self):
-        """Test taskinfo function raises exception"""
         self.server.getBuildTarget.return_value = None
         self.get_server.return_value = self.server
 
         with self.assertRaises(koji.GenericError) as cm:
             webidx.buildtargetedit(self.environ, 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_buildtargetedit_exception_build_tag(self):
-        """Test taskinfo function raises exception"""
-        self.server.getBuildTarget.return_value = {'id': 1}
+        """Test buildtargetedit function raises exception when build tag not exists."""
+        urlencode_data = b"save=True&name=testname&buildTag=11&destTag=99"
+        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.getBuildTarget.return_value = {'id': int(self.buildtarget_id)}
         self.server.getTag.return_value = None
         self.get_server.return_value = self.server
 
         with self.assertRaises(koji.GenericError) as cm:
             webidx.buildtargetedit(self.environ, self.buildtarget_id)
-        self.assertEqual(str(cm.exception), 'No such tag ID: %s' % self.buildtag_id)
+        self.assertEqual(str(cm.exception), f'No such tag ID: {self.buildtag_id}')
 
     def test_buildtargetedit_exception_dest_tag(self):
-        """Test taskinfo function raises exception"""
-        self.server.getBuildTarget.return_value = {'id': 1}
-        self.server.getTag.side_effect = [{'id': 11}, None]
+        """Test buildtargetedit function raises exception when destination tag not exists."""
+        urlencode_data = b"save=True&name=testname&buildTag=11&destTag=99"
+        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.getBuildTarget.return_value = {'id': int(self.buildtarget_id)}
+        self.server.getTag.side_effect = [{'id': int(self.buildtag_id)}, None]
         self.get_server.return_value = self.server
 
         with self.assertRaises(koji.GenericError) as cm:
             webidx.buildtargetedit(self.environ, self.buildtarget_id)
-        self.assertEqual(str(cm.exception), 'No such tag ID: %s' % self.desttag_id)
+        self.assertEqual(str(cm.exception), f'No such tag ID: {self.desttag_id}')
+
+    def test_buildtargetedit_save_case_valid(self):
+        """Test buildtargetedit function valid case (save)."""
+        urlencode_data = b"save=True&name=testname&buildTag=11&destTag=99"
+        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.getBuildTarget.return_value = {'id': int(self.buildtarget_id)}
+        self.server.getTag.side_effect = [{'id': int(self.buildtag_id)},
+                                          {'id': int(self.desttag_id)}]
+        self.server.editBuildTarget.return_value = None
+        self.get_server.return_value = self.server
+
+        with self.assertRaises(ServerRedirect):
+            webidx.buildtargetedit(self.environ, self.buildtarget_id)
+        self.assertEqual(self.environ['koji.redirect'],
+                         f'buildtargetinfo?targetID={self.buildtarget_id}')
+
+    def test_buildtargetedit_cancel_case(self):
+        """Test buildtargetedit function valid case (cancel)."""
+        self.server.getBuildTarget.return_value = {'id': int(self.buildtarget_id)}
+        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
+
+        with self.assertRaises(ServerRedirect):
+            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.assertEqual(self.environ['koji.redirect'],
+                         f'buildtargetinfo?targetID={self.buildtarget_id}')
diff --git a/tests/test_www/test_cancelbuild.py b/tests/test_www/test_cancelbuild.py
new file mode 100644
index 0000000..ea96a08
--- /dev/null
+++ b/tests/test_www/test_cancelbuild.py
@@ -0,0 +1,55 @@
+import unittest
+import koji
+
+import mock
+from .loadwebindex import webidx
+from koji.server import ServerRedirect
+
+
+class TestCancelBuild(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
+        }
+        self.build_id = '1'
+
+    def tearDown(self):
+        mock.patch.stopall()
+
+    def test_cancelbuild_exception_unknown_build(self):
+        """Test cancelbuild function raises exception when build ID is unknown."""
+        self.get_server.return_value = self.server
+        self.server.getBuild.return_value = None
+
+        with self.assertRaises(koji.GenericError) as cm:
+            webidx.cancelbuild(self.environ, self.build_id)
+        self.assertEqual(str(cm.exception), f'unknown build ID: {self.build_id}')
+
+    def test_cancelbuild_unable_cancel(self):
+        """Test cancelbuild function raises exception when unable to cancel build."""
+        self.get_server.return_value = self.server
+        self.server.getBuild.return_value = {'id': int(self.build_id)}
+        self.server.cancelBuild.return_value = None
+
+        with self.assertRaises(koji.GenericError) as cm:
+            webidx.cancelbuild(self.environ, self.build_id)
+        self.assertEqual(str(cm.exception), 'unable to cancel build')
+
+    def test_cancelbuild_valid(self):
+        """Test cancelbuild function valid case."""
+        self.server.getBuild.return_value = {'id': int(self.build_id)}
+        self.server.cancelBuild.return_value = int(self.build_id)
+        self.get_server.return_value = self.server
+
+        with self.assertRaises(ServerRedirect):
+            webidx.cancelbuild(self.environ, self.build_id)
+        self.server.getBuild.assert_called_with(int(self.build_id))
+        self.server.cancelBuild.assert_called_with(int(self.build_id))
+        self.assertEqual(self.environ['koji.redirect'], f'buildinfo?buildID={self.build_id}')
diff --git a/tests/test_www/test_canceltask.py b/tests/test_www/test_canceltask.py
new file mode 100644
index 0000000..49c90ea
--- /dev/null
+++ b/tests/test_www/test_canceltask.py
@@ -0,0 +1,33 @@
+import unittest
+
+import mock
+from .loadwebindex import webidx
+from koji.server import ServerRedirect
+
+
+class TestCancelTask(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
+        }
+        self.task_id = '1'
+
+    def tearDown(self):
+        mock.patch.stopall()
+
+    def test_canceltask_valid(self):
+        """Test canceltask function valid case."""
+        self.server.cancelTask.return_value = None
+        self.get_server.return_value = self.server
+
+        with self.assertRaises(ServerRedirect):
+            webidx.canceltask(self.environ, self.task_id)
+        self.server.cancelTask.assert_called_with(int(self.task_id))
+        self.assertEqual(self.environ['koji.redirect'], f'taskinfo?taskID={self.task_id}')
diff --git a/tests/test_www/test_disablehost.py b/tests/test_www/test_disablehost.py
new file mode 100644
index 0000000..d100891
--- /dev/null
+++ b/tests/test_www/test_disablehost.py
@@ -0,0 +1,35 @@
+import unittest
+
+import mock
+from .loadwebindex import webidx
+from koji.server import ServerRedirect
+
+
+class TestDisableHost(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
+        }
+        self.host_id = '1'
+        self.host_info = {'id': int(self.host_id), 'name': 'test-host'}
+
+    def tearDown(self):
+        mock.patch.stopall()
+
+    def test_disablehost_valid(self):
+        """Test disablehost function valid case."""
+        self.server.getHost.return_value = self.host_info
+        self.server.disableHost.return_value = None
+        self.get_server.return_value = self.server
+
+        with self.assertRaises(ServerRedirect):
+            webidx.disablehost(self.environ, str(self.host_id))
+        self.server.disableHost.assert_called_with(self.host_info['name'])
+        self.assertEqual(self.environ['koji.redirect'], f'hostinfo?hostID={self.host_id}')
diff --git a/tests/test_www/test_enablehost.py b/tests/test_www/test_enablehost.py
new file mode 100644
index 0000000..4501306
--- /dev/null
+++ b/tests/test_www/test_enablehost.py
@@ -0,0 +1,35 @@
+import unittest
+
+import mock
+from .loadwebindex import webidx
+from koji.server import ServerRedirect
+
+
+class TestEnableHost(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
+        }
+        self.host_id = '1'
+        self.host_info = {'id': int(self.host_id), 'name': 'test-host'}
+
+    def tearDown(self):
+        mock.patch.stopall()
+
+    def test_enablehost_valid(self):
+        """Test enablehost function valid case."""
+        self.server.getHost.return_value = self.host_info
+        self.server.enableHost.return_value = None
+        self.get_server.return_value = self.server
+
+        with self.assertRaises(ServerRedirect):
+            webidx.enablehost(self.environ, self.host_id)
+        self.server.enableHost.assert_called_with(self.host_info['name'])
+        self.assertEqual(self.environ['koji.redirect'], f'hostinfo?hostID={self.host_id}')
diff --git a/tests/test_www/test_freetask.py b/tests/test_www/test_freetask.py
new file mode 100644
index 0000000..68124b3
--- /dev/null
+++ b/tests/test_www/test_freetask.py
@@ -0,0 +1,33 @@
+import unittest
+
+import mock
+from .loadwebindex import webidx
+from koji.server import ServerRedirect
+
+
+class TestFreeTask(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
+        }
+        self.task_id = '1'
+
+    def tearDown(self):
+        mock.patch.stopall()
+
+    def test_freetask_valid(self):
+        """Test freetask function valid case."""
+        self.server.freeTask.return_value = None
+        self.get_server.return_value = self.server
+
+        with self.assertRaises(ServerRedirect):
+            webidx.freetask(self.environ, self.task_id)
+        self.server.freeTask.assert_called_with(int(self.task_id))
+        self.assertEqual(self.environ['koji.redirect'], f'taskinfo?taskID={self.task_id}')
diff --git a/tests/test_www/test_hostedit.py b/tests/test_www/test_hostedit.py
new file mode 100644
index 0000000..32d4e1f
--- /dev/null
+++ b/tests/test_www/test_hostedit.py
@@ -0,0 +1,98 @@
+import unittest
+import koji
+import cgi
+
+import mock
+from io import BytesIO
+from .loadwebindex import webidx
+from koji.server import ServerRedirect
+
+
+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.server = mock.MagicMock()
+        self.environ = {
+            'koji.options': {
+                'SiteName': 'test',
+                'KojiFilesURL': 'https://server.local/files',
+            },
+            'koji.currentUser': None
+        }
+        self.host_id = '1'
+        self.host_info = {'id': int(self.host_id), 'name': 'test-host', 'enabled': True}
+
+    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_hostedit_exception_host_not_existing(self):
+        """Test hostedit function raises exception when host ID not exists."""
+        self.get_server.return_value = self.server
+        self.server.getHost.return_value = None
+
+        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}')
+
+    def test_hostedit_save_case_valid(self):
+        """Test hostedit function valid case (save)."""
+        urlencode_data = b"save=True&arches=x86_64&capacity=1.0&description=test-desc&" \
+                         b"comment=test-comment&enable=True&channels=default"
+        fs = self.get_fs(urlencode_data)
+        self.server.getHost.return_value = self.host_info
+        self.server.editHost.return_value = True
+        self.server.listChannels.return_value = [{'id': 2, 'name': 'test-channel'}]
+        self.server.removeHostFromChannel.return_value = None
+        self.server.addHostToChannel.return_value = None
+
+        def __get_server(env):
+            env['koji.session'] = self.server
+            env['koji.form'] = fs
+            return self.server
+
+        self.get_server.side_effect = __get_server
+
+        with self.assertRaises(ServerRedirect):
+            webidx.hostedit(self.environ, 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')
+        self.server.listChannels.assert_called_with(hostID=int(self.host_id))
+        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)."""
+        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.getHost.return_value = self.host_info
+
+        with self.assertRaises(ServerRedirect):
+            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.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}')
diff --git a/tests/test_www/test_notificationcreate.py b/tests/test_www/test_notificationcreate.py
new file mode 100644
index 0000000..b2cdf62
--- /dev/null
+++ b/tests/test_www/test_notificationcreate.py
@@ -0,0 +1,115 @@
+import mock
+import unittest
+import cgi
+
+import koji
+from io import BytesIO
+from .loadwebindex import webidx
+from koji.server import ServerRedirect
+
+
+class TestNotificationCreate(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.buildtag_id = '11'
+        self.pkg_id = '2'
+        self.environ = {
+            'koji.options': {
+                'SiteName': 'test',
+                'KojiFilesURL': 'https://server.local/files',
+            },
+            'koji.currentUser': {'id': '1'},
+        }
+
+    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_notificationcreate_add_case_not_logged(self):
+        """Test notificationcreate function raises exception when user is not logged."""
+        environ = {
+            'koji.options': {
+                'SiteName': 'test',
+                'KojiFilesURL': 'https://server.local/files',
+            },
+            'koji.currentUser': None,
+        }
+        urlencode_data = b"add=True&package=2&tag=11"
+        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
+
+        with self.assertRaises(koji.GenericError) as cm:
+            webidx.notificationcreate(environ)
+        self.assertEqual(str(cm.exception), 'not logged-in')
+
+    def test_notificationcreate_add_case_int(self):
+        """Test notificationcreate function valid case (add)"""
+        urlencode_data = b"add=True&package=2&tag=11&success_only=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.createNotification.return_value = 11
+
+        with self.assertRaises(ServerRedirect):
+            webidx.notificationcreate(self.environ)
+        self.server.createNotification.assert_called_with('1', int(self.pkg_id),
+                                                          int(self.buildtag_id), True)
+        self.assertEqual(self.environ['koji.redirect'], 'index')
+
+    def test_notificationcreate_add_case_all(self):
+        """Test notificationcreate function valid case (add)"""
+        urlencode_data = b"add=True&package=all&tag=all"
+        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.createNotification.return_value = 11
+
+        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')
+
+    def test_notificationcreate_cancel_case(self):
+        """Test notificationcreate function valid case (cancel)."""
+        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
+
+        with self.assertRaises(ServerRedirect):
+            webidx.notificationcreate(self.environ)
+        self.server.createNotification.assert_not_called()
+        self.assertEqual(self.environ['koji.redirect'], 'index')
diff --git a/tests/test_www/test_notificationdelete.py b/tests/test_www/test_notificationdelete.py
new file mode 100644
index 0000000..dc462fe
--- /dev/null
+++ b/tests/test_www/test_notificationdelete.py
@@ -0,0 +1,43 @@
+import unittest
+import koji
+
+import mock
+from .loadwebindex import webidx
+from koji.server import ServerRedirect
+
+
+class TestNotificationDelete(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
+        }
+        self.notification_id = '1'
+
+    def tearDown(self):
+        mock.patch.stopall()
+
+    def test_notificationdelete_exception(self):
+        """Test notificationdelete function raises exception when notification ID not exists."""
+        self.server.getBuildNotification.return_value = None
+        self.get_server.return_value = self.server
+
+        with self.assertRaises(koji.GenericError) as cm:
+            webidx.notificationdelete(self.environ, self.notification_id)
+        self.assertEqual(str(cm.exception), f'no notification with ID: {self.notification_id}')
+
+    def test_notificationdelete_valid(self):
+        """Test notificationdelete function valid case."""
+        self.server.getBuildNotification.return_value = {'id': self.notification_id}
+        self.get_server.return_value = self.server
+
+        with self.assertRaises(ServerRedirect):
+            webidx.notificationdelete(self.environ, self.notification_id)
+        self.server.deleteNotification.assert_called_with(self.notification_id)
+        self.assertEqual(self.environ['koji.redirect'], 'index')
diff --git a/tests/test_www/test_notificationedit.py b/tests/test_www/test_notificationedit.py
new file mode 100644
index 0000000..88d678f
--- /dev/null
+++ b/tests/test_www/test_notificationedit.py
@@ -0,0 +1,105 @@
+import unittest
+import koji
+import cgi
+
+import mock
+from io import BytesIO
+from .loadwebindex import webidx
+from koji.server import ServerRedirect
+
+
+class TestNotificationEdit(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
+        }
+        self.notification_id = '1'
+        self.pkg_id = 2
+        self.tag_id = 11
+
+    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_notificationedit_exception(self):
+        """Test notificationedit function raises exception when notification ID is not exists."""
+        self.server.getBuildNotification.return_value = None
+        self.get_server.return_value = self.server
+
+        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}')
+
+    def test_notificationedit_save_case_int(self):
+        """Test notificationedit function valid case (save)."""
+        urlencode_data = b"save=True&package=2&tag=11&success_only=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.getBuildNotification.return_value = {'id': self.notification_id}
+        self.server.updateNotification.return_value = 1
+
+        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')
+
+    def test_notificationedit_save_case_all(self):
+        """Test notificationedit function valid case (all)."""
+        urlencode_data = b"save=True&package=all&tag=all"
+        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
+        self.server.updateNotification.return_value = 1
+
+        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')
+
+    def test_notificationedit_cancel_case(self):
+        """Test notificationedit function valid case (cancel)."""
+        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.server.getBuildNotification.return_value = {'id': self.notification_id}
+        self.get_server.side_effect = __get_server
+
+        with self.assertRaises(ServerRedirect):
+            webidx.notificationedit(self.environ, self.notification_id)
+        self.server.updateNotification.assert_not_called()
+        self.assertEqual(self.environ['koji.redirect'], 'index')
diff --git a/tests/test_www/test_resubmittask.py b/tests/test_www/test_resubmittask.py
new file mode 100644
index 0000000..f693179
--- /dev/null
+++ b/tests/test_www/test_resubmittask.py
@@ -0,0 +1,33 @@
+import unittest
+
+import mock
+from .loadwebindex import webidx
+from koji.server import ServerRedirect
+
+
+class TestResubmitTask(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
+        }
+        self.task_id = 1
+
+    def tearDown(self):
+        mock.patch.stopall()
+
+    def test_resubmittask_valid(self):
+        """Test resubmittask function valid case."""
+        self.server.resubmitTask.return_value = 2
+        self.get_server.return_value = self.server
+
+        with self.assertRaises(ServerRedirect):
+            webidx.resubmittask(self.environ, str(self.task_id))
+        self.server.resubmitTask.assert_called_with(self.task_id)
+        self.assertEqual(self.environ['koji.redirect'], 'taskinfo?taskID=2')
diff --git a/tests/test_www/test_tagcreate.py b/tests/test_www/test_tagcreate.py
new file mode 100644
index 0000000..842dcd1
--- /dev/null
+++ b/tests/test_www/test_tagcreate.py
@@ -0,0 +1,73 @@
+import unittest
+import cgi
+
+import mock
+from io import BytesIO
+from .loadwebindex import webidx
+from koji.server import ServerRedirect
+
+
+class TestTagCreate(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 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_tagcreate_add_case_valid(self):
+        """Test tagcreate function valid case (add)"""
+        urlencode_data = b"add=True&name=testname&arches=x86_64&locked=True&permission=1" \
+                         b"&maven_support=True&maven_include_all=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.createTag.return_value = 1
+
+        with self.assertRaises(ServerRedirect):
+            webidx.tagcreate(self.environ)
+        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')
+
+    def test_tagcreate_cancel_case_valid(self):
+        """Test tagcreate function valid cases (cancel)."""
+        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
+
+        with self.assertRaises(ServerRedirect):
+            webidx.tagcreate(self.environ)
+        self.server.createTag.assert_not_called()
+        self.assertEqual(self.environ['koji.redirect'], 'tags')
diff --git a/tests/test_www/test_tagdelete.py b/tests/test_www/test_tagdelete.py
new file mode 100644
index 0000000..1357af3
--- /dev/null
+++ b/tests/test_www/test_tagdelete.py
@@ -0,0 +1,44 @@
+import mock
+import unittest
+
+import koji
+from .loadwebindex import webidx
+from koji.server import ServerRedirect
+
+
+class TestTagDelete(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
+        }
+        self.tag_id = '1'
+
+    def tearDown(self):
+        mock.patch.stopall()
+
+    def test_tagdelete_exception(self):
+        """Test tagdelete function raises exception when tag not exists."""
+        self.get_server.return_value = self.server
+        self.server.getTag.return_value = None
+
+        with self.assertRaises(koji.GenericError) as cm:
+            webidx.tagdelete(self.environ, self.tag_id)
+        self.assertEqual(str(cm.exception), f'no tag with ID: {self.tag_id}')
+
+    def test_tagdelete_valid(self):
+        """Test taskinfo function valid case."""
+        self.server.getTag.return_value = {'id': int(self.tag_id)}
+        self.server.deleteTag.return_value = None
+        self.get_server.return_value = self.server
+
+        with self.assertRaises(ServerRedirect):
+            webidx.tagdelete(self.environ, self.tag_id)
+        self.server.deleteTag.assert_called_with(int(self.tag_id))
+        self.assertEqual(self.environ['koji.redirect'], 'tags')
diff --git a/tests/test_www/test_tagedit.py b/tests/test_www/test_tagedit.py
new file mode 100644
index 0000000..774eeea
--- /dev/null
+++ b/tests/test_www/test_tagedit.py
@@ -0,0 +1,86 @@
+import unittest
+import koji
+import cgi
+
+import mock
+from io import BytesIO
+from .loadwebindex import webidx
+from koji.server import ServerRedirect
+
+
+class TestTagEdit(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
+        }
+        self.tag_id = '1'
+
+    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_tagedit_exception(self):
+        """Test tagedit function raises exception when tag ID not exists."""
+        self.get_server.return_value = self.server
+        self.server.getTag.return_value = None
+
+        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}')
+
+    def test_tagedit_add_case_valid(self):
+        """Test tagedit function valid case (save)."""
+        urlencode_data = b"save=True&name=testname&arches=x86_64&locked=True&permission=1" \
+                         b"&maven_support=True&maven_include_all=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.editTag2.return_value = None
+        self.server.getTag.return_value = {'id': int(self.tag_id)}
+
+        with self.assertRaises(ServerRedirect):
+            webidx.tagedit(self.environ, self.tag_id)
+        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)."""
+        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.return_value = {'id': int(self.tag_id)}
+
+        with self.assertRaises(ServerRedirect):
+            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}')

