PR#4150: fix callnum lookup on hub
Merges #4150 https://pagure.io/koji/pull-request/4150 Fixes: #4149 https://pagure.io/koji/issue/4149 callnum not recorded for sessions
This commit is contained in:
commit
724dbe317f
2 changed files with 218 additions and 2 deletions
|
|
@ -76,7 +76,7 @@ class Session(object):
|
|||
self.message = ''
|
||||
self.exclusive = False
|
||||
self.lockerror = None
|
||||
self.callnum = None
|
||||
self.callnum = callnum = None
|
||||
# we look up perms, groups, and host_id on demand, see __getattr__
|
||||
self._perms = None
|
||||
self._groups = None
|
||||
|
|
@ -88,7 +88,13 @@ class Session(object):
|
|||
self.id = int(environ['HTTP_KOJI_SESSION_ID'])
|
||||
self.key = environ['HTTP_KOJI_SESSION_KEY']
|
||||
try:
|
||||
callnum = int(environ['HTTP_KOJI_CALLNUM'])
|
||||
if 'HTTP_KOJI_SESSION_CALLNUM' in environ:
|
||||
# this is the field that the koji client has sent since 1.31
|
||||
callnum = int(environ['HTTP_KOJI_SESSION_CALLNUM'])
|
||||
else:
|
||||
# before 1.35, the hub was mistakenly checking this field
|
||||
# we still accept it for backwards compatibility
|
||||
callnum = int(environ['HTTP_KOJI_CALLNUM'])
|
||||
except KeyError:
|
||||
callnum = None
|
||||
elif not context.opts['DisableURLSessions'] and args is not None:
|
||||
|
|
|
|||
210
tests/test_hub/test_client_session.py
Normal file
210
tests/test_hub/test_client_session.py
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
import io
|
||||
import mock
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import koji
|
||||
from kojihub import kojixmlrpc, db
|
||||
from koji.xmlrpcplus import Fault
|
||||
|
||||
|
||||
'''
|
||||
These tests simulate the connection between client and hub by using a fake
|
||||
python_requests session that passes directly through to the hub code.
|
||||
|
||||
The goal of these tests is to validate communication between client and hub
|
||||
'''
|
||||
|
||||
|
||||
class FakeClient(koji.ClientSession):
|
||||
|
||||
def __init__(self, baseurl, opts=None, sinfo=None, auth_method=None):
|
||||
super(FakeClient, self).__init__(baseurl, opts=opts, sinfo=sinfo, auth_method=auth_method)
|
||||
|
||||
def new_session(self):
|
||||
self.rsession = FakeReqSession(self)
|
||||
|
||||
|
||||
class FakeReqSession:
|
||||
|
||||
def __init__(self, client_session):
|
||||
self.client_session = client_session
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
def post(self, handler, **kwargs):
|
||||
headers = kwargs['headers']
|
||||
request = kwargs['data']
|
||||
data, status, rheaders = self.do_call(headers, request)
|
||||
self.last = FakeReqResult(self, data, status, rheaders)
|
||||
return self.last
|
||||
|
||||
def do_call(self, headers, request):
|
||||
_nonlocal = {}
|
||||
|
||||
def start_response(status, headers):
|
||||
_nonlocal['status'] = status
|
||||
_nonlocal['headers'] = headers
|
||||
|
||||
# set up fake env dict
|
||||
environ = {}
|
||||
environ['SCRIPT_FILENAME'] = kojixmlrpc.__file__
|
||||
environ['wsgi.url_scheme'] = 'https'
|
||||
environ['SERVER_NAME'] = 'myserver'
|
||||
environ['SERVER_PORT'] = '443'
|
||||
environ['REMOTE_ADDR'] = '127.0.0.1'
|
||||
environ['REQUEST_METHOD'] = 'POST'
|
||||
environ['CONTENT_TYPE'] = 'text/xml'
|
||||
|
||||
for k in headers:
|
||||
k2 = 'HTTP_' + k.upper().replace('-', '_')
|
||||
environ[k2] = headers[k]
|
||||
|
||||
# environ['wsgi.input'] = io.StringIO(request)
|
||||
environ['wsgi.input'] = io.BytesIO(request)
|
||||
data = kojixmlrpc.application(environ, start_response)
|
||||
data = data[0]
|
||||
return data, _nonlocal['status'], _nonlocal['headers']
|
||||
|
||||
|
||||
class FakeReqResult:
|
||||
|
||||
def __init__(self, rsession, data, status, headers):
|
||||
self.rsession = rsession
|
||||
self.data = data
|
||||
self.status = status
|
||||
self.headers = dict(headers)
|
||||
|
||||
def raise_for_status(self):
|
||||
pass
|
||||
# TODO?
|
||||
|
||||
def iter_content(self, chunk_size=1):
|
||||
yield self.data
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
# TODO?
|
||||
|
||||
|
||||
QP = db.QueryProcessor
|
||||
|
||||
|
||||
class TestClientSession(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.context = mock.MagicMock()
|
||||
self.context.session.assertLogin = mock.MagicMock()
|
||||
self.session = FakeClient('https://bad.server/')
|
||||
self.QueryProcessor = mock.patch('kojihub.auth.QueryProcessor',
|
||||
side_effect=self.getQuery).start()
|
||||
self._dml = mock.patch('kojihub.db._dml').start()
|
||||
self.connect = mock.patch('kojihub.db.connect').start()
|
||||
self.query_execute = mock.MagicMock()
|
||||
self.query_executeOne = mock.MagicMock()
|
||||
self.query_singleValue = mock.MagicMock()
|
||||
self.queries = []
|
||||
|
||||
def tearDown(self):
|
||||
mock.patch.stopall()
|
||||
|
||||
def getQuery(self, *args, **kwargs):
|
||||
query = QP(*args, **kwargs)
|
||||
query.execute = self.query_execute
|
||||
query.executeOne = self.query_executeOne
|
||||
query.singleValue = self.query_singleValue
|
||||
self.queries.append(query)
|
||||
return query
|
||||
|
||||
def test_echo(self):
|
||||
args = ['OK 123', 456, {}]
|
||||
result = self.session.echo(*args)
|
||||
self.assertEqual(result, args)
|
||||
self.assertEqual(len(self.queries), 0)
|
||||
|
||||
def test_sinfo(self):
|
||||
self.session.callnum = 1
|
||||
self.session.sinfo = {
|
||||
'session-id': 123,
|
||||
'session-key': 'MYKEY456',
|
||||
}
|
||||
session_data = {
|
||||
'expired': False,
|
||||
'renew_ts': time.time(),
|
||||
'callnum': None,
|
||||
'user_id': 1,
|
||||
'authtype': koji.AUTHTYPES['SSL'],
|
||||
'master': None,
|
||||
'exclusive': True, # avoids a third query
|
||||
}
|
||||
user_data = {'status': koji.USER_STATUS['NORMAL']}
|
||||
self.query_executeOne.side_effect = [session_data, user_data]
|
||||
|
||||
args = ['OK 123', 456, {}]
|
||||
result = self.session.echo(*args)
|
||||
self.assertEqual(result, args)
|
||||
self.assertEqual(len(self.queries), 2)
|
||||
|
||||
def test_sequence_error(self):
|
||||
self.session.callnum = 1
|
||||
self.session.sinfo = {
|
||||
'session-id': 123,
|
||||
'session-key': 'MYKEY456',
|
||||
}
|
||||
session_data = {
|
||||
'expired': False,
|
||||
'renew_ts': time.time(),
|
||||
'callnum': 2, # higher than what client reports
|
||||
'user_id': 1,
|
||||
'authtype': koji.AUTHTYPES['SSL'],
|
||||
'master': None,
|
||||
'exclusive': True, # avoids a third query
|
||||
}
|
||||
user_data = {'status': koji.USER_STATUS['NORMAL']}
|
||||
self.query_executeOne.side_effect = [session_data, user_data]
|
||||
|
||||
with self.assertRaises(koji.SequenceError):
|
||||
self.session.echo('bad')
|
||||
|
||||
def test_retry_error(self):
|
||||
self.session.callnum = 1
|
||||
self.session.sinfo = {
|
||||
'session-id': 123,
|
||||
'session-key': 'MYKEY456',
|
||||
}
|
||||
session_data = {
|
||||
'expired': False,
|
||||
'renew_ts': time.time(),
|
||||
'callnum': 1, # same as what client reports
|
||||
'user_id': 1,
|
||||
'authtype': koji.AUTHTYPES['SSL'],
|
||||
'master': None,
|
||||
'exclusive': True, # avoids a third query
|
||||
}
|
||||
user_data = {'status': koji.USER_STATUS['NORMAL']}
|
||||
self.query_executeOne.side_effect = [session_data, user_data]
|
||||
|
||||
with self.assertRaises(koji.RetryError):
|
||||
self.session.echo('bad')
|
||||
|
||||
def test_error(self):
|
||||
with self.assertRaises(koji.GenericError):
|
||||
self.session.error()
|
||||
self.assertEqual(len(self.queries), 0)
|
||||
|
||||
def test_fault(self):
|
||||
with self.assertRaises(Fault):
|
||||
self.session.fault()
|
||||
self.assertEqual(len(self.queries), 0)
|
||||
|
||||
TEST_VER_HDR = [('Koji-Version', '1.2.3')]
|
||||
|
||||
@mock.patch('kojihub.kojixmlrpc.GLOBAL_HEADERS', new=TEST_VER_HDR)
|
||||
def test_hub_version(self):
|
||||
self.session.echo('test')
|
||||
self.assertEqual(self.session.hub_version_str, '1.2.3')
|
||||
self.assertEqual(self.session.hub_version, (1, 2, 3))
|
||||
|
||||
|
||||
# the end
|
||||
Loading…
Add table
Add a link
Reference in a new issue