This fixes issues with test_pages that I found while checking coverage. The environ dict was recycled across each call in the loop, but those calls can modify it, leaking data from one pass to another. Also, the mocking for _getServer was not placing the session in the environ, which was causing the tests to miss some cases.
225 lines
8.2 KiB
Python
225 lines
8.2 KiB
Python
from __future__ import absolute_import
|
|
import inspect
|
|
import json
|
|
try:
|
|
from unittest import mock
|
|
except ImportError:
|
|
import mock
|
|
import os
|
|
import six
|
|
import sys
|
|
import unittest
|
|
|
|
from six.moves import StringIO
|
|
|
|
import koji
|
|
from koji_cli.lib import watch_tasks
|
|
from koji_cli.commands import anon_handle_watch_task
|
|
from koji.util import dslice
|
|
from ..test_cli.fakeclient import FakeClientSession, RecordingClientSession, encode_data
|
|
from .loadwebindex import webidx
|
|
from kojiweb.util import FieldStorageCompat
|
|
|
|
|
|
class TestPages(unittest.TestCase):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
# recording session used across tests in recording mode
|
|
cls.cfile = os.path.dirname(__file__) + f'/data/pages_calls.json'
|
|
cls.cfile2 = os.path.dirname(__file__) + f'/data/pages_calls_updates.json'
|
|
cls.recording = False
|
|
cls.updating = False
|
|
cls.rsession = RecordingClientSession('http://localhost/kojihub', {})
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
if cls.recording:
|
|
# save recorded calls
|
|
cls.rsession.dump(cls.cfile)
|
|
elif cls.updating:
|
|
cls.rsession.dump(cls.cfile2)
|
|
|
|
def setUp(self):
|
|
self.environ = {
|
|
'koji.options': {
|
|
'SiteName': 'test',
|
|
'KojiFilesURL': 'http://server.local/files',
|
|
'KojiHubURL': 'http://server.local/kojihub',
|
|
'KojiGreeting': 'Welcome to Koji Web',
|
|
'LoginDisabled': True,
|
|
'Tasks': [],
|
|
'ToplevelTasks': [],
|
|
'ParentTasks': [],
|
|
'MBS_WEB_URL': None,
|
|
},
|
|
'koji.currentUser': None,
|
|
'SCRIPT_FILENAME': webidx.__file__, # close enough
|
|
'SCRIPT_NAME': '',
|
|
'SERVER_PORT': '80',
|
|
'SERVER_NAME': 'server.local',
|
|
'wsgi.url_scheme': 'http',
|
|
'koji.headers': [],
|
|
}
|
|
self.get_server = mock.patch.object(webidx, "_getServer").start()
|
|
self._assertLogin = mock.patch.object(webidx, "_assertLogin").start()
|
|
self.server = None # set up setup_server
|
|
|
|
# mock time so that call args are reproduced
|
|
self.time = mock.patch('time.time').start()
|
|
self.time.return_value = 1735707600.0
|
|
|
|
def __get_server(env):
|
|
# this is replacing the call to _getServer
|
|
env['koji.session'] = self.server
|
|
return self.server
|
|
|
|
self.get_server.side_effect = __get_server
|
|
self.setup_server()
|
|
|
|
def setup_server(self):
|
|
if self.recording:
|
|
self.server = self.rsession
|
|
else:
|
|
self.server = FakeClientSession('SERVER', {})
|
|
self.server.load(self.cfile)
|
|
if self.updating:
|
|
self.server._missing_rsession = self.rsession
|
|
return self.server
|
|
|
|
def tearDown(self):
|
|
mock.patch.stopall()
|
|
|
|
# Show long diffs in error output...
|
|
maxDiff = None
|
|
|
|
CALLS = [
|
|
['index', ''],
|
|
['packages', ''],
|
|
['packages', 'prefix=m&order=package_name&inherited=1&blocked=1'],
|
|
['packages', 'start=50&order=package_name&inherited=1&blocked=1'],
|
|
['builds', ''],
|
|
['builds', 'start=50&order=-build_id'],
|
|
['builds', 'prefix=d&order=-build_id'],
|
|
['builds', 'state=4&prefix=d&order=-build_id'],
|
|
['builds', 'type=image&prefix=d&order=-build_id'],
|
|
['tasks', ''],
|
|
['tasks', 'state=all&view=tree&order=-id&method=all'],
|
|
['tasks', 'state=failed&view=tree&order=-id&method=all'],
|
|
['tasks', 'view=flat&state=failed&order=-id&method=all'],
|
|
['tasks', 'method=buildArch&view=flat&state=failed&order=-id'],
|
|
['tasks', 'owner=mikem&view=flat&state=failed&order=-id&method=buildArch'],
|
|
['tags', ''],
|
|
['tags', 'start=50&order=name'],
|
|
['buildtargets', ''],
|
|
['buildtargets', 'order=-id'],
|
|
['users', ''],
|
|
['users', 'prefix=m&order=name'],
|
|
['hosts', ''],
|
|
['hosts', 'ready=yes&channel=all&state=all&order=name&arch=all'],
|
|
['hosts', 'channel=appliance&ready=all&state=all&order=name&arch=all'],
|
|
['hosts', 'arch=x86_64&channel=appliance&ready=all&state=all&order=name'],
|
|
['reports', ''],
|
|
['packagesbyuser', ''],
|
|
['buildsbyuser', ''],
|
|
['rpmsbyhost', ''],
|
|
['tasksbyuser', ''],
|
|
['tasksbyhost', ''],
|
|
['buildsbystatus', ''],
|
|
['buildsbytarget', ''],
|
|
['clusterhealth', ''],
|
|
['search', ''],
|
|
['search', 'terms=k*&type=package&match=glob'],
|
|
['api', ''],
|
|
['userinfo', 'userID=1'],
|
|
['activesession', ''],
|
|
['archiveinfo', 'archiveID=202'],
|
|
['buildinfo', 'buildID=628'],
|
|
['rpminfo', 'rpmID=6608'],
|
|
['buildrootinfo', 'buildrootID=966'],
|
|
['buildinfo', 'buildID=574'],
|
|
['buildrootinfo', 'buildrootID=934'],
|
|
['repoinfo', 'repoID=2580'],
|
|
['buildroots', 'repoID=2580'],
|
|
['buildroots', 'state=3&repoID=2580&order=id'],
|
|
['buildtargetedit', 'targetID=107&a='],
|
|
['buildtargetinfo', 'targetID=107'],
|
|
['taskinfo', 'taskID=14330'],
|
|
['channelinfo', 'channelID=1'],
|
|
#['channelinfo', 'channelID=MISSING'],
|
|
['taginfo', 'tagID=798'],
|
|
['externalrepoinfo', 'extrepoID=1'],
|
|
['fileinfo', 'rpmID=6608&filename=/etc/koji.conf'],
|
|
['hostinfo', 'hostID=1'],
|
|
['hostedit', 'hostID=1&a='],
|
|
['notificationcreate', 'a='],
|
|
['notificationedit', 'notificationID=1&a='],
|
|
['packageinfo', 'packageID=306'],
|
|
['recentbuilds', ''],
|
|
['recentbuilds', 'package=1'],
|
|
['repoinfo', 'repoID=88'],
|
|
['rpminfo', 'rpmID=6608'],
|
|
['rpmlist', 'buildrootID=657&type=component'],
|
|
['taginfo', 'tagID=2'],
|
|
['tagedit', 'tagID=2&a='],
|
|
['tagparent', 'tagID=2&parentID=1&action=edit&a='],
|
|
['taginfo', 'tagID=2090'],
|
|
['userinfo', 'userID=1'],
|
|
['taskinfo', 'taskID=1'],
|
|
['archivelist', 'buildrootID=363&type=built'],
|
|
['buildinfo', 'buildID=422'],
|
|
['archiveinfo', 'archiveID=130'],
|
|
['archivelist', 'buildrootID=345&type=built'],
|
|
['buildinfo', 'buildID=612'],
|
|
['buildroots', ''],
|
|
['buildroots', 'start=50&order=id'],
|
|
#['builds', 'start=50&order=id'],
|
|
['reporequests', ''],
|
|
['reporequests', 'active=all&order=tag_name'],
|
|
['reporequests', 'active=false&order=-id'],
|
|
['reporequests', 'tag=1&active=all'],
|
|
['reporequest', 'reqID=127'],
|
|
['reporequest', 'reqID=128'],
|
|
['reporequest', 'reqID=132'],
|
|
['reporequest', 'reqID=133'],
|
|
['reporequest', 'reqID=134'],
|
|
]
|
|
|
|
def prep_handler(self, method, query):
|
|
"""Takes method name and query string, returns handler and data"""
|
|
# based loosely on publisher prep_handler
|
|
environ = self.environ.copy()
|
|
environ['QUERY_STRING'] = query
|
|
environ['koji.method'] = method
|
|
environ['SCRIPT_NAME'] = method
|
|
handler = getattr(webidx, method)
|
|
fs = FieldStorageCompat(environ)
|
|
environ['koji.form'] = fs
|
|
# even though we have curated urls, we need to filter args for some cases, e.g. search
|
|
args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = \
|
|
inspect.getfullargspec(handler)
|
|
if not varkw:
|
|
data = dslice(fs.data, args, strict=False)
|
|
else:
|
|
data = fs.data.copy()
|
|
return handler, data, environ
|
|
|
|
def test_web_handlers(self):
|
|
"""Test a bunch of web handlers"""
|
|
for method, query in self.CALLS:
|
|
handler, data, environ = self.prep_handler(method, query)
|
|
|
|
result = handler(environ, **data)
|
|
|
|
# result should be a string containing the rendered template
|
|
self.assertIsInstance(result, str)
|
|
# none of these should return the error template
|
|
self.assertNotIn(r'<h4>Error</h4>', result)
|
|
# all except recentbuilds (rss) should render the header and footer
|
|
if method != 'recentbuilds':
|
|
self.assertIn(r'<div id="header">', result)
|
|
self.assertIn(r'<div id="mainNav">', result)
|
|
self.assertIn(r'<p id="footer">', result)
|
|
|
|
|
|
# the end
|