from __future__ import absolute_import import mock import koji from koji.xmlrpcplus import Fault class BaseFakeClientSession(koji.ClientSession): def __init__(self, *a, **kw): super(BaseFakeClientSession, self).__init__(*a, **kw) def multiCall(self, strict=False): if not self.multicall: raise Exception("not in multicall") ret = [] self.multicall = False calls = self._calls self._calls = [] for call in calls: method = call['methodName'] args, kwargs = koji.decode_args(call['params']) try: result = self._callMethod(method, args, kwargs) ret.append(result) except Fault as fault: if strict: raise else: ret.append({'faultCode': fault.faultCode, 'faultString': fault.faultString}) return ret class FakeClientSession(BaseFakeClientSession): def __init__(self, *a, **kw): super(FakeClientSession, self).__init__(*a, **kw) self._calldata = {} self._offsets = {} def load_calls(self, data): """Load call data Data should be a list of dictionaries with keys: - method - args - kwargs - result (for successful calls) - fault (for errors) That represent call data, e.g. as generated by RecordingClientSession """ for call in data: key = self._munge([call['method'], call['args'], call['kwargs']]) self._calldata.setdefault(key, []).append(call) def _callMethod(self, name, args, kwargs=None, retry=True): if self.multicall: return super(FakeClientSession, self)._callMethod(name, args, kwargs, retry) key = self._munge([name, args, kwargs]) # we may have a series of calls for each key calls = self._calldata.get(key) ofs = self._offsets.get(key, 0) call = calls[ofs] ofs += 1 if ofs < len(calls): # don't go past the end self._offsets[key] = ofs if call: if 'fault' in call: fault = Fault(call['fault']['faultCode'], call['fault']['faultString']) raise koji.convertFault(fault) else: return call['result'] else: return mock.MagicMock() def _munge(self, data): def callback(value): if isinstance(value, list): return tuple(value) elif isinstance(value, dict): keys = sorted(value.keys()) return tuple([(k, value[k]) for k in keys]) else: return value walker = koji.util.DataWalker(data, callback) return walker.walk() class RecordingClientSession(BaseFakeClientSession): def __init__(self, *a, **kw): super(RecordingClientSession, self).__init__(*a, **kw) self._calldata = [] def get_calls(self): return self._calldata def _callMethod(self, name, args, kwargs=None, retry=True): if self.multicall: return super(RecordingClientSession, self)._callMethod(name, args, kwargs, retry) call = { 'method': name, 'args': args, 'kwargs': kwargs, } self._calldata.append(call) try: result = super(RecordingClientSession, self)._callMethod(name, args, kwargs, retry) call['result'] = result return result except Fault as fault: err = {'faultCode': fault.faultCode, 'faultString': fault.faultString} call['fault'] = err raise except koji.GenericError as e: err = {'faultCode': e.faultCode, 'faultString': str(e)} call['fault'] = err raise