#4150 fix callnum lookup on hub
Merged 8 months ago by mikem. Opened 2 years ago by mikem.
mikem/koji fix-callnum  into  master

file modified
+8 -2
@@ -76,7 +76,7 @@ 

          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 @@ 

              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:

@@ -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

1 new commit added

  • test_client_session.py
2 years ago

Metadata Update from @tkopecek:
- Pull-request tagged with: testing-basic

9 months ago

Unit test doesn't work for me. Is it for you (rebased on master)?

Unit test doesn't work for me. Is it for you (rebased on master)?

Unit tests pass for me here. What failure are you getting, in what env?

Many tests from test_client_session ends with koji.ServerOffline: server startup error (F42, LANG=C make test3

Metadata Update from @mfilip:
- Pull-request tagged with: testing-done

9 months ago

rebased onto 45c290d

8 months ago

rebased, no conflicts

1 new commit added

  • fix unit tests
8 months ago

Fixed the unit test issue. I wasn't seeing it locally because I had a local db running.

Commit 724dbe3 fixes this pull-request

Pull-Request has been merged by mikem

8 months ago